sendmail: update from maintainer
authorDenis Vlasenko <vda.linux@googlemail.com>
Tue, 26 Feb 2008 21:13:17 +0000 (21:13 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Tue, 26 Feb 2008 21:13:17 +0000 (21:13 -0000)
include/usage.h
networking/sendmail.c

index 6b54a1266b746f9e30b7b10f33cdfd39c2d1cf4a..e9770cea9392daa848e76280af9a36b2760cffe9 100644 (file)
@@ -1026,17 +1026,19 @@ USE_FEATURE_BRCTL_FANCY("\n" \
        "       -v              Give fdisk version"
 
 #define fetchmail_trivial_usage \
-       "[-C dir] [-w timeout] [-U user] -P password [-X] [-t] [-z] server[:port]"
+       "[-w timeout] [-U user] -P password [-X] [-t] [-z] server[:port] maildir [prog]"
 #define fetchmail_full_usage \
        "Fetch content of remote mailbox to local Maildir." \
        "\n\nOptions:\n" \
-       "       -C dir          Set Maildir location\n" \
        "       -w timeout      Set timeout on network operations\n" \
        "       -U username     Authenticate with specified username\n" \
        "       -P password     Authenticate with specified password\n" \
        "       -X              Use openssl connection helper for secured servers\n" \
        "       -t              Get only headers\n" \
-       "       -z              Delete messages on server"
+       "       -z              Delete messages on server" \
+USE_FEATURE_FETCHMAIL_FILTER("\n" \
+       "        prog           Run prog <message file> on message delivery" \
+)
 
 #define findfs_trivial_usage \
        "LABEL=label or UUID=uuid"
@@ -3258,18 +3260,16 @@ USE_FEATURE_RUN_PARTS_FANCY("\n -l      Prints names of all matching files even when
 #define selinuxenabled_full_usage
 
 #define sendmail_trivial_usage \
-       "[-C dir] [-w timeout] [-U user] [-P password] [-X]\n" \
-       "-t to [-t to] -f from [-n] [-s subject] [-c charset] server[:port] [body] [attachment ...]"
+       "[-w timeout] [-U user] [-P password] [-X]\n" \
+       "-t to [-t to]... [-n] [-s subject] [-c charset] server[:port] from [body] [attachment ...]"
 #define sendmail_full_usage \
        "Send an email with optional attachments." \
        "\n\nOptions:\n" \
-       "       -C dir          Change current directory to dir\n" \
        "       -w timeout      Set timeout on network operations\n" \
        "       -U username     Authenticate with specified username\n" \
        "       -P password     Authenticate with specified password\n" \
+       "       -t address      Recipient(s). May be multiple\n" \
        "       -X              Use openssl connection helper for secured servers\n" \
-       "       -t to           Recipient email. May be multiple\n" \
-       "       -f from         Sender address\n" \
        "       -n              Request delivery notification to sender\n" \
        "       -s subject      Subject\n" \
        "       -c charset      Assumed charset for body and subject [utf-8]"
index b2fbc5a7af1bea863c4b4da221288b77144285f7..adc6c76264ad3baac05cc0e59ca4b3ac5af3ce4a 100644 (file)
@@ -27,9 +27,13 @@ static void uuencode(char *fname, const char *text)
                if (NOT_LONE_DASH(fname))
                        fd = xopen(fname, O_RDONLY);
                src_buf = bb_common_bufsiz1;
-       } else {
+       // N.B. strlen(NULL) segfaults!
+       } else if (text) {
+               // though we do not call uuencode(NULL, NULL) explicitly
+               // still we do not want to break things suddenly
                len = strlen(text);
-       }
+       } else
+               return;
 
        fflush(stdout); // sync stdio and unistd output
        while (1) {
@@ -59,7 +63,38 @@ static void uuencode(char *fname, const char *text)
                close(fd);
 }
 
-static pid_t helper_pid;
+static const char *const init_xargs[9] = {
+       "openssl", "s_client", "-quiet", "-connect",
+       NULL, "-tls1", "-starttls", "smtp"
+};
+
+struct globals {
+       pid_t helper_pid;
+       unsigned timeout;
+       // arguments for SSL connection helper
+       const char *xargs[9];
+#if ENABLE_FEATURE_FETCHMAIL_FILTER
+       // arguments for postprocess helper
+       const char *fargs[3];
+#endif
+};
+#define G (*ptr_to_globals)
+#define helper_pid      (G.helper_pid)
+#define timeout         (G.timeout   )
+#define xargs           (G.xargs     )
+#define fargs           (G.fargs     )
+#define INIT_G() do { \
+       PTR_TO_GLOBALS = xzalloc(sizeof(G)); \
+       memcpy(xargs, init_xargs, sizeof(init_xargs)); \
+       fargs[0] = "utf-8"; \
+} while (0)
+
+#define opt_connect      (xargs[4])
+#define opt_after_connect (xargs[5])
+#if ENABLE_FEATURE_FETCHMAIL_FILTER
+#define opt_charset      (fargs[0])
+#define opt_subject      (fargs[1])
+#endif
 
 static void kill_helper(void)
 {
@@ -71,7 +106,7 @@ static void kill_helper(void)
 // generic signal handler
 static void signal_handler(int signo)
 {
-       int err;
+#define err signo
 
        if (SIGALRM == signo) {
                kill_helper();
@@ -107,20 +142,19 @@ static void launch_helper(const char **argv)
                                close(pipes[i]);
        if (!helper_pid) {
                // child - try to execute connection helper
-               BB_EXECVP(argv[0], (char **)argv);
+//             close(STDERR_FILENO);
+               BB_EXECVP(*argv, (char **)argv);
                _exit(127);
        }
        // parent - check whether child is alive
-       bb_signals_recursive(0
-                       + (1 << SIGCHLD)
-                       + (1 << SIGALRM)
-                       , signal_handler);
+       bb_signals(0
+               + (1 << SIGCHLD)
+               + (1 << SIGALRM)
+               , signal_handler);
        signal_handler(SIGCHLD);
        // child seems OK -> parent goes on
 }
 
-static unsigned timeout;
-
 static char *command(const char *fmt, const char *param)
 {
        char *msg = (char *)fmt;
@@ -145,7 +179,9 @@ static int smtp_checkp(const char *fmt, const char *param, int code)
        // if code != -1 then checks whether the number equals the code
        // if not equal -> die saying msg
 #if ENABLE_FEATURE_SENDMAIL_EHLO
-       while ((answer = xmalloc_getline(stdin)) && '-' == answer[3]) ;
+       while ((answer = xmalloc_getline(stdin)) != NULL)
+               if (strlen(answer) <= 3 || '-' != answer[3])
+                       break;
 #else
        answer = xmalloc_getline(stdin);
 #endif
@@ -169,15 +205,31 @@ static int smtp_check(const char *fmt, int code)
        return smtp_checkp(fmt, NULL, code);
 }
 
+// strip argument of bad chars
+static char *sane(char *str)
+{
+       char *s = str;
+       char *p = s;
+       while (*s) {
+               if (isalnum(*s) || '_' == *s || '-' == *s || '.' == *s || '@' == *s) {
+                       *p++ = *s;
+               }
+               s++;
+       }
+       *p = '\0';
+       return str;
+}
+
+#if ENABLE_FETCHMAIL
 static void pop3_checkr(const char *fmt, const char *param, char **ret)
 {
        char *msg = command(fmt, param);
        char *answer = xmalloc_getline(stdin);
-       if (answer && '+' == answer[0]) {
+       if (answer && '+' == *answer) {
                alarm(0);
                if (ret)
-                       *ret = answer;
-               else
+                       *ret = answer+4; // skip "+OK "
+               else if (ENABLE_FEATURE_CLEAN_UP)
                        free(answer);
                return;
        }
@@ -190,28 +242,16 @@ static void pop3_check(const char *fmt, const char *param)
        pop3_checkr(fmt, param, NULL);
 }
 
-// strip argument of bad chars
-static char *sane(char *str)
-{
-       char *s = str;
-       char *p = s;
-       while (*s) {
-               if (isalnum(*s) || '_' == *s || '-' == *s || '.' == *s || '@' == *s) {
-                       *p++ = *s;
-               }
-               s++;
-       }
-       *p = '\0';
-       return str;
-}
-
-static void pop3_message(int fd)
+static void pop3_message(const char *filename)
 {
+       int fd;
        char *answer;
-       // read stdin, copy to file fd
+       // create and open file filename
+       // read stdin, copy to created file
+       fd = xopen(filename, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL);
        while ((answer = xmalloc_fgets_str(stdin, "\r\n"))) {
                char *s = answer;
-               if ('.' == answer[0]) {
+               if ('.' == *answer) {
                        if ('.' == answer[1])
                                s++;
                        else if ('\r' == answer[1] && '\n' == answer[2] && '\0' == answer[3])
@@ -222,64 +262,56 @@ static void pop3_message(int fd)
        }
        close(fd);
 }
-
-static const char *args[] = {
-       "openssl", "s_client", "-quiet", "-connect", NULL, "-tls1", "-starttls", "smtp", NULL
-};
-#define opt_connect    args[4]
-#define opt_after_connect args[5]
+#endif
 
 int sendgetmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int sendgetmail_main(int argc, char **argv)
 {
-       llist_t *recipients = NULL;
-       char *from;
-       const char *subject;
-       char *charset = (char *)"utf-8";
+       llist_t *opt_recipients = NULL;
+#if !ENABLE_FEATURE_FETCHMAIL_FILTER
+       char *opt_subject;
+       char *opt_charset = (char *)"utf-8";
+#endif
 
        const char *opt_user;
        const char *opt_pass;
-       const char *opt_timeout;
-       const char *opt_chdir;
 
        enum {
-               OPT_C = 1 << 0,         // chdir
-               OPT_w = 1 << 1,         // network timeout
-               OPT_U = 1 << 2,         // user
-               OPT_P = 1 << 3,         // password
-               OPT_X = 1 << 4,         // use openssl connection helper
+               OPT_w = 1 << 0,         // network timeout
+               OPT_U = 1 << 1,         // user
+               OPT_P = 1 << 2,         // password
+               OPT_X = 1 << 3,         // connect using openssl s_client helper
 
-               OPTS_t = 1 << 5,        // sendmail "to"
-               OPTF_t = 1 << 5,        // fetchmail "TOP"
+               OPTS_n = 1 << 4,        // sendmail: request notification
+               OPTF_t = 1 << 4,        // fetchmail: use "TOP" not "RETR"
 
-               OPTS_f = 1 << 6,        // sendmail "from"
-               OPTF_z = 1 << 6,        // fetchmail "delete"
+               OPTS_s = 1 << 5,        // sendmail: subject
+               OPTF_z = 1 << 5,        // fetchmail: delete from server
 
-               OPTS_n = 1 << 7,        // notification
-               OPTS_s = 1 << 8,        // subject given
-               OPTS_c = 1 << 9,        // charset for subject and body
+               OPTS_c = 1 << 6,        // sendmail: assumed charset
+               OPTS_t = 1 << 7,        // sendmail: recipient(s)
        };
 
        const char *options;
        unsigned opts;
 
-       // SENDMAIL
-       if ('s' == applet_name[0]) {
-               // save initial stdin
+       INIT_G();
+
+       if (!ENABLE_FETCHMAIL || 's' == applet_name[0]) {
+               // SENDMAIL
+               // save initial stdin (body or attachements can be piped!)
                xdup2(STDIN_FILENO, INITIAL_STDIN_FILENO);
-               // -f must be specified
-               // -t may be multiple
-               opt_complementary = "-1:f:t::";
-               options = "C:w:U:P:X" "t:f:ns:c:";
-       // FETCHMAIL
+               opt_complementary = "-2:w+:t:t::"; // count(-t) > 0
+               options = "w:U:P:X" "ns:c:t:";
        } else {
+               // FETCHMAIL
                opt_after_connect = NULL;
-               opt_complementary = "=1:P";
-               options = "C:w:U:P:X" "tz";
+               opt_complementary = "-2:w+:P";
+               options = "w:U:P:X" "tz";
        }
        opts = getopt32(argv, options,
-               &opt_chdir, &opt_timeout, &opt_user, &opt_pass,
-               &recipients, &from, &subject, &charset
+               &timeout, &opt_user, &opt_pass,
+               &opt_subject, &opt_charset, &opt_recipients
        );
 
        //argc -= optind;
@@ -288,16 +320,9 @@ int sendgetmail_main(int argc, char **argv)
        // first argument is remote server[:port]
        opt_connect = *argv++;
 
-       if (opts & OPT_w)
-               timeout = xatou(opt_timeout);
-
-       // chdir
-       if (opts & OPT_C)
-               xchdir(opt_chdir);
-
        // connect to server
        if (opts & OPT_X) {
-               launch_helper(args);
+               launch_helper(xargs);
        } else {
                // no connection helper provided -> make plain connect
                int fd = create_and_connect_stream_or_die(opt_connect, 0);
@@ -305,24 +330,30 @@ int sendgetmail_main(int argc, char **argv)
                xdup2(STDIN_FILENO, STDOUT_FILENO);
        }
 
-       // randomize
-       srand(time(NULL));
-
-       // SENDMAIL
-       if (recipients) {
+#if ENABLE_FETCHMAIL
+       if (opt_after_connect)
+#endif
+       {
+               // SENDMAIL
+               char *opt_from;
                int code;
                char *boundary;
+               const char *fmt;
+               const char *p;
+               char *q;
 
                // wait for initial OK on plain connect
                if (!(opts & OPT_X))
                        smtp_check(NULL, 220);
 
-               sane(from);
+               // get specified sender
+               opt_from = sane(*argv++);
+
                // introduce to server
                // should we respect modern (but useless here) EHLO?
                // or should they respect we wanna be tiny?!
-               if (!ENABLE_FEATURE_SENDMAIL_EHLO || 250 != smtp_checkp("EHLO %s", from, -1)) {
-                       smtp_checkp("HELO %s", from, 250);
+               if (!ENABLE_FEATURE_SENDMAIL_EHLO || 250 != smtp_checkp("EHLO %s", opt_from, -1)) {
+                       smtp_checkp("HELO %s", opt_from, 250);
                }
 
                // set sender
@@ -330,13 +361,13 @@ int sendgetmail_main(int argc, char **argv)
                // no authentication is possible
                code = (opts & OPT_P) ? -1 : 250;
                // first try softly without authentication
-               while (250 != smtp_checkp("MAIL FROM:<%s>", from, code)) {
+               while (250 != smtp_checkp("MAIL FROM:<%s>", opt_from, code)) {
                        // MAIL FROM failed -> authentication needed
                        // do we have username?
                        if (!(opts & OPT_U)) {
                                // no! fetch it from "from" option
                                //opts |= OPT_U;
-                               opt_user = xstrdup(from);
+                               opt_user = xstrdup(opt_from);
                                *strchrnul(opt_user, '@') = '\0';
                        }
                        // now it seems we have username
@@ -351,30 +382,35 @@ int sendgetmail_main(int argc, char **argv)
                        // but now die on failure
                        code = 250;
                }
+
                // set recipients
-               for (llist_t *to = recipients; to; to = to->link) {
+               for (llist_t *to = opt_recipients; to; to = to->link) {
                        smtp_checkp("RCPT TO:<%s>", sane(to->data), 250);
                }
 
                // now put message
                smtp_check("DATA", 354);
                // put address headers
-               printf("From: %s\r\n", from);
-               for (llist_t *to = recipients; to; to = to->link) {
+               printf("From: %s\r\n", opt_from);
+               for (llist_t *to = opt_recipients; to; to = to->link) {
                        printf("To: %s\r\n", to->data);
                }
                // put encoded subject
                if (opts & OPTS_c)
-                       sane(charset);
+                       sane((char *)opt_charset);
                if (opts & OPTS_s) {
-                       printf("Subject: =?%s?B?", charset);
-                       uuencode(NULL, subject);
+                       printf("Subject: =?%s?B?", opt_charset);
+                       uuencode(NULL, opt_subject);
                        printf("?=\r\n");
                }
                // put notification
                if (opts & OPTS_n)
-                       printf("Disposition-Notification-To: %s\r\n", from);
+                       printf("Disposition-Notification-To: %s\r\n", opt_from);
                // put common headers and body start
+               // randomize
+#if ENABLE_FEATURE_SENDMAIL_BLOATY
+               srand(time(NULL));
+#endif
                boundary = xasprintf("%d-%d-%d", rand(), rand(), rand());
                printf(
                        USE_FEATURE_SENDMAIL_BLOATY("X-Mailer: busybox " BB_VER " sendmail\r\n")
@@ -386,16 +422,16 @@ int sendgetmail_main(int argc, char **argv)
                        , boundary
                );
                // put body + attachment(s)
-       {
-               const char *fmt =
+
+               fmt =
                        "\r\n--%s\r\n"
                        "%stext/plain; charset=%s\r\n"
                        "%s%s\r\n"
                        "%s"
                ;
-               const char *p = charset;
-               char *q = (char *)"";
-               while (argv[0]) {
+               p = opt_charset;
+               q = (char *)"";
+               while (*argv) {
                        printf(
                                fmt
                                , boundary
@@ -414,9 +450,9 @@ int sendgetmail_main(int argc, char **argv)
                        ;
                        uuencode(*argv, NULL);
                        if (*(++argv))
-                               q = bb_get_last_path_component_strip(argv[0]);
+                               q = bb_get_last_path_component_strip(*argv);
                }
-       }
+
                // put terminator
                printf("\r\n--%s--\r\n" "\r\n", boundary);
                if (ENABLE_FEATURE_CLEAN_UP)
@@ -426,11 +462,25 @@ int sendgetmail_main(int argc, char **argv)
                smtp_check(".", 250);
                smtp_check("QUIT", 221);
 
-       // FETCHMAIL
+#if ENABLE_FETCHMAIL
        } else {
-               // authenticate
+               // FETCHMAIL
                char *buf;
                unsigned nmsg;
+               char *hostname;
+               pid_t pid;
+               // cache fetch command
+               const char *retr = (opts & OPTF_t) ? "TOP %u 0" : "RETR %u";
+
+               // goto maildir
+               xchdir(*argv++);
+
+#if ENABLE_FEATURE_FETCHMAIL_FILTER
+               // cache postprocess program
+               *fargs = *argv;
+#endif
+               
+               // authenticate
                if (!(opts & OPT_U)) {
                        //opts |= OPT_U;
                        opt_user = getenv("USER");
@@ -438,25 +488,29 @@ int sendgetmail_main(int argc, char **argv)
 #if ENABLE_FEATURE_FETCHMAIL_APOP
                pop3_checkr(NULL, NULL, &buf);
                // server supports APOP?
-               if ('<' == buf[4]) {
+               if ('<' == *buf) {
                        md5_ctx_t md5;
-                       uint8_t hex[16*2 + 1];
                        // yes. compose <stamp><password>
                        char *s = strchr(buf, '>');
                        if (s)
                                strcpy(s+1, opt_pass);
-                       s = buf+4;
+                       s = buf;
                        // get md5 sum of <stamp><password>
                        md5_begin(&md5);
                        md5_hash(s, strlen(s), &md5);
                        md5_end(s, &md5);
-                       bin2hex(hex, s, 16);
+                       // NOTE: md5 struct contains enough space
+                       // so we reuse md5 space instead of xzalloc(16*2+1)
+#define md5_hex ((uint8_t *)&md5)
+//                     uint8_t *md5_hex = (uint8_t *)&md5;
+                       *bin2hex(md5_hex, s, 16) = '\0';
                        // APOP
-                       s = xasprintf("%s %s", opt_user, hex);
+                       s = xasprintf("%s %s", opt_user, md5_hex);
+#undef md5_hex
                        pop3_check("APOP %s", s);
                        if (ENABLE_FEATURE_CLEAN_UP) {
                                free(s);
-                               free(buf);
+                               free(buf-4); // buf is "+OK " away from malloc'ed string
                        }
                } else {
 #else
@@ -472,60 +526,64 @@ int sendgetmail_main(int argc, char **argv)
                // get statistics
                pop3_checkr("STAT", NULL, &buf);
 
+               // prepare message filename suffix
+               hostname = xzalloc(MAXHOSTNAMELEN+1);
+               gethostname(hostname, MAXHOSTNAMELEN);
+               pid = getpid();
+
                // get number of messages
-               nmsg = atoi(buf+4);
+               // NOTE: we don't use xatou(buf) since buf is "nmsg nbytes"
+               // we only need nmsg and atoi is just exactly what we need
+               // if atoi fails to convert buf into number it returns 0
+               // in this case the following loop simply will not be executed 
+               nmsg = atoi(buf);
                if (ENABLE_FEATURE_CLEAN_UP)
-                       free(buf);
-
-               // lock maildir
-               ////USE_FEATURE_CLEAN_UP(close)(xopen(".lock", O_CREAT | O_WRONLY | O_TRUNC | O_EXCL));
-               
-               // make tempnam(dir, salt) respect dir argument
-               unsetenv("TMPDIR");
-
-               // TODO: piping through external filter argv... if *argv
+                       free(buf-4); // buf is "+OK " away from malloc'ed string
 
-               // cache fetch command
-       {
-               const char *retr = (opts & OPTF_t) ? "TOP %u 0" : "RETR %u";
                // loop through messages
                for (; nmsg; nmsg--) {
-                       int fd;
-                       char tmp_name[sizeof("tmp/XXXXXX")];
-                       char new_name[sizeof("new/XXXXXX")];
-
+                       char *filename = xasprintf("tmp/%llu.%u.%s", monotonic_us(), pid, hostname);
+                       char *target;
+#if ENABLE_FEATURE_FETCHMAIL_FILTER
+               int rc;
+#endif
                        // retrieve message in ./tmp
-                       strcpy(tmp_name, "tmp/XXXXXX");
-                       fd = mkstemp(tmp_name);
-                       if (fd < 0)
-                               bb_perror_msg_and_die("cannot create unique file");
                        pop3_check(retr, (const char *)nmsg);
-                       pop3_message(fd); // NB: closes fd
-
-                       // move file to ./new atomically
-                       strncpy(new_name, "new", 3);
-                       strcpy(new_name + 3, tmp_name + 3);
-                       if (rename(tmp_name, new_name) < 0) {
-                               // rats! such file exists! try to make unique name
-                               strcpy(new_name + 3, "tmp/XXXXXX" + 3);
-                               fd = mkstemp(new_name);
-                               if (fd < 0)
-                                       bb_perror_msg_and_die("cannot create unique file");
-                               close(fd);
-                               xrename(tmp_name, new_name);
-                       }
-
+                       pop3_message(filename);
                        // delete message from server
                        if (opts & OPTF_z)
                                pop3_check("DELE %u", (const char*)nmsg);
+
+#if ENABLE_FEATURE_FETCHMAIL_FILTER
+                       // run postprocessing program
+                       if (*fargs) {
+                               fargs[1] = filename;
+                               rc = wait4pid(spawn((char **)fargs));
+                               if (99 == rc)
+                                       break;
+                               if (1 == rc)
+                                       goto skip;
+                       }
+#endif
+                       // atomically move message to ./new
+                       target = xstrdup(filename);
+                       strncpy(target, "new", 3);
+                       // ... or just stop receiving on error
+                       if (rename_or_warn(filename, target))
+                               break;
+                       free(target);
+#if ENABLE_FEATURE_FETCHMAIL_FILTER
+ skip:
+#endif
+                       free(filename);
                }
-       }
+
+               if (ENABLE_FEATURE_CLEAN_UP)
+                       free(hostname);
 
                // Bye
                pop3_check("QUIT", NULL);
-
-               // unlock maildir
-               ////unlink(".lock");
+#endif // ENABLE_FETCHMAIL
        }
 
        return 0;