sendmail: fixes by Vladimir Dronnikov <dronnikov at gmail.com>
authorDenis Vlasenko <vda.linux@googlemail.com>
Sat, 2 Feb 2008 17:54:35 +0000 (17:54 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Sat, 2 Feb 2008 17:54:35 +0000 (17:54 -0000)
include/usage.h
networking/sendmail.c

index 0ef17b5209a0edc5c7100583aceb3b1ffdb772a7..989ed9134ab154699cdf2312972dae9652740957 100644 (file)
@@ -3135,26 +3135,28 @@ USE_FEATURE_RUN_PARTS_FANCY("\n -l      Prints names of all matching files even when
 #define selinuxenabled_full_usage
 
 #define sendmail_trivial_usage \
-       "[-d] {-t to}+ [-f from] [-n[notify]] [-s subject] [-b file]*\n" \
-       "[-a attachment]* [-c charset] [-w timeout] [-h server] [-p port] [-U user] [-P password]"
+       "{-t to}+ {-f from} [-n[notify]] [-s subject] [-b file]*\n" \
+       "[-a attachment]* [-c charset]" \
+       USE_FEATURE_SENDMAIL_NETWORK("\n" \
+       " [-d] [-w timeout] [-h server] [-p port] [-U user] [-P password]" \
+       )
 #define sendmail_full_usage \
-       "Send an email from to with subject and optional attachments.\n" \
-       "Body is read from stdin or from optional files" \
+       "Send an email <from> <to> with <subject> and optional attachments." \
        "\n\nArguments:\n" \
-       "       -d              Just dump composed message\n" \
        "       -t to           Recipient email. May be multiple\n" \
-       "       -f from         Sender email\n" \
+       "       -f from         Sender address\n" \
        "       -n[notify]      Optional notification address. If just -n given then notifies the sender\n" \
        "       -s subject      Optional subject\n" \
        "       -b filename     Optional body content file. May be multiple\n" \
        "       -a filename     Optional file attachment. May be multiple\n" \
        "       -c charset      Assumed charset for body and subject [koi8-r]" \
        USE_FEATURE_SENDMAIL_NETWORK("\n" \
+       "       -d              Just dump composed message\n" \
        "       -w timeout      Set timeout on network operations\n" \
        "       -h server       Optional mail server name or IP [127.0.0.1]\n" \
        "       -p port         Optional mail server port [25]\n" \
        "       -U username     Authenticate using AUTH LOGIN with specified username\n" \
-       "       -P password     Authenticate using AUTH LOGIN with specified password"\
+       "       -P password     Authenticate using AUTH LOGIN with specified password" \
        )
 
 #define seq_trivial_usage \
index 2ea10b124049056637d088e1e567d525e4a7eacd..fa1abc7ff794797a1ed770db457bd2538cd55d15 100644 (file)
@@ -16,45 +16,46 @@ enum {
        DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3),
 };
 
-static void uuencode(const char *fname)
+static void uuencode(char *fname, const char *text)
 {
+#define src_buf text
        int fd;
-       char src_buf[SRC_BUF_SIZE];
-       char dst_buf[1 + DST_BUF_SIZE + 1];
+#define len fd
+       char dst_buf[DST_BUF_SIZE + 1];
 
-       fd = xopen(fname, O_RDONLY);
-       fflush(stdout);
-       dst_buf[0] = '\n';
+       if (fname) {
+               fd = xopen(fname, O_RDONLY);
+               src_buf = bb_common_bufsiz1;
+       } else {
+               len = strlen(text);
+       }
+
+       fflush(stdout); // sync stdio and unistd output
        while (1) {
-               size_t size = full_read(fd, src_buf, SRC_BUF_SIZE);
+               size_t size;
+               if (fname) {
+                       size = full_read(fd, (char *)src_buf, SRC_BUF_SIZE);
+                       if ((ssize_t)size < 0)
+                               bb_perror_msg_and_die(bb_msg_read_error);
+               } else {
+                       size = len;
+                       if (len > SRC_BUF_SIZE)
+                               size = SRC_BUF_SIZE;
+               }
                if (!size)
                        break;
-               if ((ssize_t)size < 0)
-                       bb_perror_msg_and_die(bb_msg_read_error);
-               /* Encode the buffer we just read in */
-               bb_uuencode(dst_buf + 1, src_buf, size, bb_uuenc_tbl_base64);
-               xwrite(STDOUT_FILENO, dst_buf, 1 + 4 * ((size + 2) / 3));
-       }
-       close(fd);
-}
-
-// "inline" version
-// encodes content of given buffer instead of fd
-// used to encode subject and authentication terms
-static void uuencode_inline(const char *src_buf)
-{
-       size_t len;
-       char dst_buf[DST_BUF_SIZE + 1];
-
-       len = strlen(src_buf);
-       fflush(stdout);
-       while (len > 0) {
-               size_t chunk = (len <= SRC_BUF_SIZE) ? len : SRC_BUF_SIZE;
-               bb_uuencode(dst_buf, src_buf, chunk, bb_uuenc_tbl_base64);
-               xwrite(STDOUT_FILENO, dst_buf, 4 * ((chunk + 2) / 3));
-               src_buf += chunk;
-               len -= chunk;
+               // Encode the buffer we just read in
+               bb_uuencode(dst_buf, src_buf, size, bb_uuenc_tbl_base64);
+               if (fname) {
+                       xwrite(STDOUT_FILENO, "\n", 1);
+               } else {
+                       src_buf += size;
+                       len -= size;
+               }
+               xwrite(STDOUT_FILENO, dst_buf, 4 * ((size + 2) / 3));
        }
+       if (ENABLE_FEATURE_CLEAN_UP && fname)
+               close(fd);
 }
 
 #if ENABLE_FEATURE_SENDMAIL_NETWORK
@@ -72,7 +73,7 @@ static void signal_handler(int signo)
                        bb_error_msg_and_die("child exited (%d)", WEXITSTATUS(err));
 }
 
-static pid_t helper_pid = -1;
+static pid_t helper_pid;
 
 // read stdin, parses first bytes to a number, i.e. server response
 // if code = -1 then just return this number
@@ -92,7 +93,7 @@ static int check(int code, const char *errmsg)
                        return n;
                }
        }
-       // TODO!!!: is there more elegant way to terminate child on program failure?
+       // TODO: is there more elegant way to terminate child on program failure?
        if (helper_pid > 0)
                kill(helper_pid, SIGTERM);
        if (!answer)
@@ -104,9 +105,24 @@ static int check(int code, const char *errmsg)
 
 static int puts_and_check(const char *msg, int code, const char *errmsg)
 {
-       puts(msg);
+       printf("%s\r\n", msg);
        return check(code, errmsg);
 }
+
+// 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;
+}
 #endif
 
 int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -115,10 +131,10 @@ int sendmail_main(int argc, char **argv)
        llist_t *recipients = NULL;
        llist_t *bodies = NULL;
        llist_t *attachments = NULL;
-       const char *from;
-       const char *notify;
+       char *from;
+       char *notify = NULL;
        const char *subject;
-       const char *charset = "utf-8";
+       char *charset = (char*)"utf-8";
 #if ENABLE_FEATURE_SENDMAIL_NETWORK
        const char *wsecs = "10";
        const char *server = "127.0.0.1";
@@ -133,6 +149,7 @@ int sendmail_main(int argc, char **argv)
                OPT_f = 1 << 0,         // sender
                OPT_n = 1 << 2,         // notification
                OPT_s = 1 << 3,         // subject given
+               OPT_c = 1 << 6,         // charset
                OPT_d = 1 << 7,         // dry run - no networking
                OPT_w = 1 << 8,         // network timeout
                OPT_h = 1 << 9,         // server
@@ -152,9 +169,10 @@ int sendmail_main(int argc, char **argv)
        //argc -= optind;
        argv += optind;
 
-//printf("OPTS[%4x]\n", opts);
-
-       // TODO!!!: strip recipients and sender from <>
+       // sanitize user input
+       sane(from);
+       if (opts & OPT_c)
+               sane(charset);
 
        // establish connection
 #if ENABLE_FEATURE_SENDMAIL_NETWORK
@@ -167,24 +185,24 @@ int sendmail_main(int argc, char **argv)
                                bb_error_msg_and_die("no password");
                        }
                }
-//printf("OPTS[%4x][%s][%s]\n", opts, opt_user, opt_pass);
-//exit(0);
                // set chat timeout
                alarm(timeout);
                // connect to server
                if (argv[0]) {
                        // if connection helper given
                        // setup vanilla unidirectional pipes interchange
+                       int idx;
                        int pipes[4];
                        xpipe(pipes);
                        xpipe(pipes+2);
                        helper_pid = vfork();
                        if (helper_pid < 0)
                                bb_perror_msg_and_die("vfork");
-                       xdup2(pipes[(helper_pid)?0:2], STDIN_FILENO);
-                       xdup2(pipes[(helper_pid)?3:1], STDOUT_FILENO);
+                       idx = (!helper_pid)*2;
+                       xdup2(pipes[idx], STDIN_FILENO);
+                       xdup2(pipes[3-idx], STDOUT_FILENO);
                        if (ENABLE_FEATURE_CLEAN_UP)
-                               for (int i = 4; --i >= 0; )
+                               for (int i = 4; --i >= 0;)
                                        if (pipes[i] > STDOUT_FILENO)
                                                close(pipes[i]);
                        // replace child with connection helper
@@ -210,10 +228,12 @@ int sendmail_main(int argc, char **argv)
                        check(220, "INIT");
                }
                // mail user specified? try modern AUTHentication
-               if (opt_user && (334 == puts_and_check("auth login", -1, "auth login"))) {
-                       uuencode_inline(opt_user);
+               if ((opts & OPT_U)
+                && (334 == puts_and_check("auth login", -1, "auth login"))
+               ) {
+                       uuencode(NULL, opt_user);
                        puts_and_check("", 334, "AUTH");
-                       uuencode_inline(opt_pass);
+                       uuencode(NULL, opt_pass);
                        puts_and_check("", 235, "AUTH");
                // no mail user specified or modern AUTHentication is not supported?
                } else {
@@ -224,15 +244,15 @@ int sendmail_main(int argc, char **argv)
                                domain++;
                        else
                                domain = "local";
-                       printf("helo %s\n", domain);
+                       printf("helo %s\r\n", domain);
                        check(250, "HELO");
                }
 
                // set addresses
-               printf("mail from:<%s>\n", from);
+               printf("mail from:<%s>\r\n", from);
                check(250, "MAIL FROM");
                for (llist_t *to = recipients; to; to = to->link) {
-                       printf("rcpt to:<%s>\n", to->data);
+                       printf("rcpt to:<%s>\r\n", sane(to->data));
                        check(250, "RCPT TO");
                }
                puts_and_check("data", 354, "DATA");
@@ -243,60 +263,64 @@ int sendmail_main(int argc, char **argv)
 
        // now put message
        // put address headers
-       printf("From: %s\n", from);
+       printf("From: %s\r\n", from);
        for (llist_t *to = recipients; to; to = to->link) {
-               printf("To: %s\n", to->data);
+               printf("To: %s\r\n", sane(to->data));
        }
        // put encoded subject
        if (opts & OPT_s) {
                printf("Subject: =?%s?B?", charset);
-               uuencode_inline(subject);
-               puts("?=");
+               uuencode(NULL, subject);
+               puts("?=\r");
        }
        // put notification
        if (opts & OPT_n) {
-               const char *s = notify;
-               if (!s[0])
-                       s = from; // notify sender by default
-               printf("Disposition-Notification-To: %s\n", s);
+               // -n without parameter?
+               if (!notify)
+                       notify = from; // notify sender by default
+               printf("Disposition-Notification-To: %s\r\n", sane(notify));
        }
        // put common headers and body start
        //srand(?);
        boundary = xasprintf("%d-%d-%d", rand(), rand(), rand());
        printf(
-               "X-Mailer: busybox " BB_VER " sendmail\n"
-               "X-Priority: 3\n"
-               "Message-ID: <%s>\n"
-               "Mime-Version: 1.0\n"
-               "Content-Type: multipart/mixed; boundary=\"%s\"\n"
-               "\n"
-               "--%s\n"
-               "Content-Type: text/plain; charset=%s\n"
-               "%s\n%s"
-               , boundary, boundary, boundary, charset
+               "X-Mailer: busybox " BB_VER " sendmail\r\n"
+               "Message-ID: <%s>\r\n"
+               "Mime-Version: 1.0\r\n"
+               "%smultipart/mixed; boundary=\"%s\"\r\n"
+               "\r\n"
+               "--%s\r\n"
+               "%stext/plain; charset=%s\r\n"
+               "%s\r\n%s"
+               , boundary
+               , "Content-Type: "
+               , boundary, boundary
+               , "Content-Type: "
+               , charset
                , "Content-Disposition: inline"
-               , "Content-Transfer-Encoding: base64\n"
+               , "Content-Transfer-Encoding: base64\r\n"
        );
        // put body(ies)
        for (llist_t *f = bodies; f; f = f->link) {
-               uuencode(f->data);
+               uuencode(f->data, NULL);
        }
        // put attachment(s)
        for (llist_t *f = attachments; f; f = f->link) {
                printf(
-                       "\n--%s\n"
-                       "Content-Type: application/octet-stream\n"
-                       "%s; filename=\"%s\"\n"
+                       "\r\n--%s\r\n"
+                       "%sapplication/octet-stream\r\n"
+                       "%s; filename=\"%s\"\r\n"
                        "%s"
                        , boundary
+                       , "Content-Type: "
                        , "Content-Disposition: inline"
                        , bb_get_last_path_component_strip(f->data)
-                       , "Content-Transfer-Encoding: base64\n"
+                       , "Content-Transfer-Encoding: base64\r\n"
                );
-               uuencode(f->data);
+               uuencode(f->data, NULL);
        }
        // put terminator
-       printf("\n--%s--\n\n", boundary);
+       printf("\r\n--%s--\r\n\r\n", boundary);
        if (ENABLE_FEATURE_CLEAN_UP)
                free(boundary);