FreeBSD Bugzilla – Attachment 205286 Details for
Bug 210537
[patch] [feature request] set MIME type in cron-generated e-mails
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Refresh
cron-mime2.patch (text/plain), 14.26 KB, created by
Mikhail Teterin
on 2019-06-22 20:09:42 UTC
(
hide
)
Description:
Refresh
Filename:
MIME Type:
Creator:
Mikhail Teterin
Created:
2019-06-22 20:09:42 UTC
Size:
14.26 KB
patch
obsolete
>Index: Makefile >=================================================================== >--- Makefile (revision 349287) >+++ Makefile (working copy) >@@ -1,5 +1,7 @@ > # $FreeBSD$ > >+.include <src.opts.mk> >+ > PROG= cron > MAN= cron.8 > SRCS= cron.c database.c do_command.c job.c user.c popen.c >@@ -6,8 +8,13 @@ > > CFLAGS+= -DLOGIN_CAP -DPAM > >-LIBADD= cron pam util >+LIBADD= cron pam util magic >+.if ${MK_OPENSSL} == "no" >+CFLAGS+= -DNO_OPENSSL >+.else >+LIBADD+=crypto >+.endif > >-WARNS?= 2 >+WARNS?= 5 > > .include <bsd.prog.mk> >Index: cron.8 >=================================================================== >--- cron.8 (revision 349287) >+++ cron.8 (working copy) >@@ -28,6 +28,7 @@ > .Op Fl j Ar jitter > .Op Fl J Ar rootjitter > .Op Fl m Ar mailto >+.Op Fl M > .Op Fl n > .Op Fl s > .Op Fl o >@@ -123,6 +124,18 @@ > The same as > .Fl j > except that it will affect jobs run by the superuser only. >+.It Fl M >+When preparing an e-mail with a job's output (if any), try to guess the >+Content-Type of the message. This uses >+.Lb libmagic >+-- the same mechanism, as that used by >+.Nm file. >+.Pp >+Note, only the initial portion of the output is analyzed (typicaly >+-- up to 1024 characters), which could sometimes lead to incorrect results. >+For example, if the text contains its first non-ASCII character beyond >+the initial portion, the charset part of the content-type may be incorrectly >+set to "us-ascii". > .It Fl m Ar mailto > Overrides the default recipient for > .Nm >@@ -168,9 +181,14 @@ > The second case is for the jobs that run less frequently. > They are executed exactly once, they are not skipped nor > executed twice (unless cron is restarted or the user's >-.Xr crontab 5 >+.Xr crontab 5 , >+.Xr file 1 , >+.Xr libmagic 3 > is changed during such a time interval). >-If an interval disappears >+.An Paul Vixie (original author) Aq paul@vix.com >+.An Dmitry Morozovsky (jitter additions) >+.An Sergey Babkin (time-zone changing code) Aq babkin@FreeBSD.org >+.An Mikhail Teterin (Content-Type setting) Aq mi@aldan.algebra.com > due to the GMT offset change, such jobs are > executed at the same absolute point of time as they would be in the > old time zone. >Index: cron.c >=================================================================== >--- cron.c (revision 349287) >+++ cron.c (working copy) >@@ -31,6 +31,7 @@ > #else > # include <time.h> > #endif >+#include <magic.h> > > > static void usage(void), >@@ -57,10 +58,10 @@ > static void > usage() { > #if DEBUGGING >- char **dflags; >+ const char **dflags; > #endif > >- fprintf(stderr, "usage: cron [-j jitter] [-J rootjitter] " >+ fprintf(stderr, "usage: cron [-j jitter] [-J rootjitter] [-m]" > "[-m mailto] [-n] [-s] [-o] [-x debugflag[,...]]\n"); > #if DEBUGGING > fprintf(stderr, "\ndebugflags: "); >@@ -479,7 +480,7 @@ > > #ifdef USE_SIGCHLD > static void >-sigchld_handler(int x) >+sigchld_handler(int x __unused) > { > WAIT_T waiter; > PID_T pid; >@@ -511,11 +512,12 @@ > > > static void >-sighup_handler(int x) >+sighup_handler(int x __unused) > { > log_close(); > } > >+extern magic_t Magic; > > static void > parse_args(argc, argv) >@@ -525,7 +527,7 @@ > int argch; > char *endp; > >- while ((argch = getopt(argc, argv, "j:J:m:nosx:")) != -1) { >+ while ((argch = getopt(argc, argv, "j:J:m:Mnosx:")) != -1) { > switch (argch) { > case 'j': > Jitter = strtoul(optarg, &endp, 10); >@@ -542,6 +544,19 @@ > case 'm': > defmailto = optarg; > break; >+ case 'M': >+ Magic = magic_open(MAGIC_MIME|MAGIC_ERROR); >+ if (Magic == NULL) { >+ warnx("failed to allocate the magic structure"); >+ break; >+ } >+ if (!magic_load(Magic, NULL)) >+ break; >+ warnx("failed to load magic DB: %s", >+ magic_error(Magic)); >+ magic_close(Magic); >+ Magic = NULL; >+ break; > case 'n': > dont_daemonize = 1; > break; >Index: cron.h >=================================================================== >--- cron.h (revision 349287) >+++ cron.h (working copy) >@@ -228,36 +228,36 @@ > unget_char(int, FILE *), > free_entry(entry *), > skip_comments(FILE *), >- log_it(char *, int, char *, const char *), >+ log_it(const char *, int, const char *, const char *), > log_close(void); > > int job_runqueue(void), > set_debug_flags(char *), > get_char(FILE *), >- get_string(char *, int, FILE *, char *), >+ get_string(char *, int, FILE *, const char *), > swap_uids(void), > swap_uids_back(void), > load_env(char *, FILE *), > cron_pclose(FILE *), >- strcmp_until(char *, char *, int), >+ strcmp_until(const char *, const char *, int), > allowed(char *), > strdtb(char *); > >-char *env_get(char *, char **), >+char *env_get(const char *, char **), > *arpadate(time_t *), > *mkprints(unsigned char *, unsigned int), >- *first_word(char *, char *), >+ *first_word(char *, const char *), > **env_init(void), > **env_copy(char **), >- **env_set(char **, char *); >+ **env_set(char **, const char *); > >-user *load_user(int, struct passwd *, char *), >- *find_user(cron_db *, char *); >+user *load_user(int, struct passwd *, const char *), >+ *find_user(const cron_db *, const char *); > >-entry *load_entry(FILE *, void (*)(char *), >+entry *load_entry(FILE *, void (*)(const char *), > struct passwd *, char **); > >-FILE *cron_popen(char *, char *, entry *); >+FILE *cron_popen(char *, const char *, const entry *); > > > /* in the C tradition, we only create >@@ -267,24 +267,24 @@ > > #ifdef MAIN_PROGRAM > # if !defined(LINT) && !defined(lint) >-char *copyright[] = { >+const char *copyright[] = { > "@(#) Copyright 1988,1989,1990,1993,1994 by Paul Vixie", > "@(#) All rights reserved" > }; > # endif > >-char *MonthNames[] = { >+const char *MonthNames[] = { > "Jan", "Feb", "Mar", "Apr", "May", "Jun", > "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", > NULL > }; > >-char *DowNames[] = { >+const char *DowNames[] = { > "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", > NULL > }; > >-char *ProgramName, >+const char *ProgramName, > *defmailto; > int LineNumber; > unsigned Jitter, >@@ -293,7 +293,7 @@ > > # if DEBUGGING > int DebugFlags; >-char *DebugFlagNames[] = { /* sync with #defines */ >+const char *DebugFlagNames[] = { /* sync with #defines */ > "ext", "sch", "proc", "pars", "load", "misc", "test", "bit", > NULL /* NULL must be last element */ > }; >Index: database.c >=================================================================== >--- database.c (revision 349287) >+++ database.c (working copy) >@@ -33,7 +33,7 @@ > #define TMAX(a,b) ((a)>(b)?(a):(b)) > > >-static void process_crontab(char *, char *, char *, >+static void process_crontab(const char *, const char *, const char *, > struct stat *, > cron_db *, cron_db *); > >@@ -53,10 +53,11 @@ > const char *name; > struct stat st; > } syscrontabs [] = { >- { SYSCRONTABS }, >- { LOCALSYSCRONTABS } >+ { .name = SYSCRONTABS }, >+ { .name = LOCALSYSCRONTABS } > }; >- int i, ret; >+ size_t i; >+ int ret; > > Debug(DLOAD, ("[%d] load_database()\n", getpid())) > >@@ -234,10 +235,9 @@ > > user * > find_user(db, name) >- cron_db *db; >- char *name; >+ const cron_db *db; >+ const char *name; > { >- char *env_get(); > user *u; > > for (u = db->head; u != NULL; u = u->next) >@@ -249,9 +249,9 @@ > > static void > process_crontab(uname, fname, tabname, statbuf, new_db, old_db) >- char *uname; >- char *fname; >- char *tabname; >+ const char *uname; >+ const char *fname; >+ const char *tabname; > struct stat *statbuf; > cron_db *new_db; > cron_db *old_db; >Index: do_command.c >=================================================================== >--- do_command.c (revision 349287) >+++ do_command.c (working copy) >@@ -26,6 +26,10 @@ > #if defined(sequent) > # include <sys/universe.h> > #endif >+#ifndef NO_OPENSSL >+# include <openssl/bio.h> >+# include <openssl/evp.h> >+#endif > #if defined(SYSLOG) > # include <syslog.h> > #endif >@@ -36,10 +40,15 @@ > # include <security/pam_appl.h> > # include <security/openpam.h> > #endif >+#include <magic.h> >+#include <inttypes.h> > >+magic_t Magic = NULL; /* Try to set Content-Type in generated e-mail? */ > >-static void child_process(entry *, user *), >- do_univ(user *); >+static void child_process(const entry *, const user *); >+#if defined(sequent) >+static void do_univ(const user *); >+#endif > > > void >@@ -85,16 +94,54 @@ > Debug(DPROC, ("[%d] main process returning to work\n", getpid())) > } > >+#ifndef NO_OPENSSL >+static size_t >+mimefwrite(const void * restrict ptr, size_t size, size_t nmemb, >+ FILE * restrict stream) >+{ >+ static BIO *base64; >+ int bytes; > >+ Debug(DPROC, ("[%d] PROCESSING binary output %p, %zd, %zd, %p\n", >+ getpid(), ptr, size, nmemb, stream)); >+ >+ if (ptr == NULL) { >+ if (base64 == NULL) >+ return 0; >+ >+ Debug(DPROC, ("[%d] CLOSING base64 output %p\n", >+ getpid(), stream)); >+ >+ BIO_set_close(base64, BIO_NOCLOSE); >+ BIO_free_all(base64); >+ base64 = NULL; >+ return 0; >+ } >+ >+ if (base64 == NULL) { >+ BIO *b64 = BIO_new(BIO_f_base64()); >+ >+ base64 = BIO_new_fp(stream, BIO_NOCLOSE); >+ base64 = BIO_push(b64, base64); >+ } >+ >+ bytes = BIO_write(base64, ptr, size * nmemb); >+ >+ return bytes / size; >+} >+#endif >+ > static void > child_process(e, u) >- entry *e; >- user *u; >+ const entry *e; >+ const user *u; > { > int stdin_pipe[2], stdout_pipe[2]; > register char *input_data; > char *usernm, *mailto, *mailfrom; > int children = 0; >+ size_t (*writer)(const void * restrict, size_t, size_t, >+ FILE * restrict) = fwrite; > # if defined(LOGIN_CAP) > struct passwd *pwd; > login_cap_t *lc; >@@ -275,11 +322,13 @@ > close(stdin_pipe[READ_PIPE]); > close(stdout_pipe[WRITE_PIPE]); > >+#if defined(sequent) > /* set our login universe. Do this in the grandchild > * so that the child can invoke /usr/lib/sendmail > * without surprises. > */ > do_univ(u); >+#endif > > # if defined(LOGIN_CAP) > /* Set user's entire context, but skip the environment >@@ -453,7 +502,8 @@ > > /*local*/{ > register FILE *in = fdopen(stdout_pipe[READ_PIPE], "r"); >- register int ch; >+ char buf[BUFSIZ]; >+ size_t bufsize; > > if (in == NULL) { > warn("fdopen failed in child"); >@@ -460,15 +510,22 @@ > _exit(ERROR_EXIT); > } > >- ch = getc(in); >- if (ch != EOF) { >+ /* >+ * Read a little bit of the job's output -- enough to >+ * determine the * Content-Type of it... >+ */ >+ bufsize = fread(buf, sizeof(char), >+ sizeof(buf)/sizeof(char), in); >+ >+ if (bufsize > 0) { > register FILE *mail; >- register int bytes = 1; >+ off_t bytes = 0; > int status = 0; > > Debug(DPROC|DEXT, >- ("[%d] got data (%x:%c) from grandchild\n", >- getpid(), ch, ch)) >+ ("[%d] got data (%x:%c) from grandchild, " >+ "%zd chars\n", >+ getpid(), buf[0], buf[0], bufsize)) > > /* get name of recipient. this is MAILTO if set to a > * valid local username; USER otherwise. >@@ -494,6 +551,7 @@ > register char **env; > auto char mailcmd[MAX_COMMAND]; > auto char hostname[MAXHOSTNAMELEN]; >+ const char *mimetype; > > if (gethostname(hostname, MAXHOSTNAMELEN) == -1) > hostname[0] = '\0'; >@@ -511,6 +569,38 @@ > fprintf(mail, "From: Cron Daemon <%s>\n", > mailfrom); > fprintf(mail, "To: %s\n", mailto); >+ >+ if (Magic) { >+ mimetype = magic_buffer(Magic, >+ buf, bufsize*sizeof(char)); >+ if (mimetype == NULL) { >+ fprintf(mail, >+ "X-Cron-MagicError: %s\n", >+ magic_error(Magic)); >+ } else { >+ const char *encoding = "8bit"; >+#ifndef NO_OPENSSL >+ /* >+ * If available, use OpenSSL's >+ * base64 functinality to >+ * convert binary body: >+ */ >+ if (strstr(mimetype, >+ "charset=binary")) { >+ encoding = "base64"; >+ writer = mimefwrite; >+ } >+#endif >+ >+ fprintf(mail, >+ "Mime-Version: 1.0\n" >+ "Content-Type: %s\n" >+ "Content-Transfer-" >+ "Encoding: %s\n", >+ mimetype, encoding); >+ } >+ } >+ > fprintf(mail, "Subject: Cron <%s@%s> %s\n", > usernm, first_word(hostname, "."), > e->cmd); >@@ -522,10 +612,6 @@ > fprintf(mail, "X-Cron-Env: <%s>\n", > *env); > fprintf(mail, "\n"); >- >- /* this was the first char from the pipe >- */ >- putc(ch, mail); > } > > /* we have to read the input pipe no matter whether >@@ -533,12 +619,17 @@ > * mail pipe if we ARE mailing. > */ > >- while (EOF != (ch = getc(in))) { >- bytes++; >- if (mailto) >- putc(ch, mail); >- } >+ do { >+ if (mailto) { >+ bytes += sizeof(char) * >+ writer(buf, sizeof(char), >+ bufsize, mail); >+ } > >+ bufsize = fread(buf, sizeof(char), >+ sizeof(buf)/sizeof(char), in); >+ } while (bufsize != 0); >+ > /* only close pipe if we opened it -- i.e., we're > * mailing... > */ >@@ -546,6 +637,14 @@ > if (mailto) { > Debug(DPROC, ("[%d] closing pipe to mail\n", > getpid())) >+#ifndef NO_OPENSSL >+ /* >+ * If we were using mimefwrite, give it a >+ * chance to properly close things down: >+ */ >+ if (writer == mimefwrite) >+ mimefwrite(NULL, 0, 0, mail); >+#endif > /* Note: the pclose will probably see > * the termination of the grandchild > * in addition to the mail process, since >@@ -560,11 +659,9 @@ > * what's going on. > */ > if (mailto && status) { >- char buf[MAX_TEMPSTR]; >- > snprintf(buf, sizeof(buf), >- "mailed %d byte%s of output but got status 0x%04x\n", >- bytes, (bytes==1)?"":"s", >+ "mailed %jd byte%s of output but got status 0x%04x\n", >+ (intmax_t)bytes, (bytes==1)?"":"s", > status); > log_it(usernm, getpid(), "MAIL", buf); > } >@@ -600,11 +697,11 @@ > } > > >+#if defined(sequent) > static void > do_univ(u) >- user *u; >+ const user *u; > { >-#if defined(sequent) > /* Dynix (Sequent) hack to put the user associated with > * the passed user structure into the ATT universe if > * necessary. We have to dig the gecos info out of >@@ -634,5 +731,5 @@ > return; > > (void) universe(U_ATT); >+} > #endif >-} >Index: popen.c >=================================================================== >--- popen.c (revision 349287) >+++ popen.c (working copy) >@@ -56,8 +56,9 @@ > > FILE * > cron_popen(program, type, e) >- char *program, *type; >- entry *e; >+ char *program; >+ const char *type; >+ const entry *e; > { > register char *cp; > FILE *iop; >Index: user.c >=================================================================== >--- user.c (revision 349287) >+++ user.c (working copy) >@@ -44,7 +44,7 @@ > > static void > log_error(msg) >- char *msg; >+ const char *msg; > { > log_it(User_name, getpid(), "PARSE", msg); > } >@@ -53,7 +53,7 @@ > load_user(crontab_fd, pw, name) > int crontab_fd; > struct passwd *pw; /* NULL implies syscrontab */ >- char *name; >+ const char *name; > { > char envstr[MAX_ENVSTR]; > FILE *file;
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 210537
:
172959
|
184654
|
188784
| 205286