sendmail: update by Vladimir
authorDenis Vlasenko <vda.linux@googlemail.com>
Sun, 28 Sep 2008 13:50:57 +0000 (13:50 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Sun, 28 Sep 2008 13:50:57 +0000 (13:50 -0000)
include/applets.h
include/usage.h
networking/Config.in
networking/sendmail.c

index 90b9417660edd7082de0955e54cd5798b6c5505a..35649d3df5d9b54c8b51cac014fe6106b4ac314d 100644 (file)
@@ -149,7 +149,7 @@ USE_FBSPLASH(APPLET(fbsplash, _BB_DIR_SBIN, _BB_SUID_NEVER))
 USE_FDFLUSH(APPLET_ODDNAME(fdflush, freeramdisk, _BB_DIR_BIN, _BB_SUID_NEVER, fdflush))
 USE_FDFORMAT(APPLET(fdformat, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
 USE_FDISK(APPLET(fdisk, _BB_DIR_SBIN, _BB_SUID_NEVER))
-USE_FETCHMAIL(APPLET_ODDNAME(fetchmail, sendgetmail, _BB_DIR_USR_BIN, _BB_SUID_NEVER, fetchmail))
+//USE_FETCHMAIL(APPLET(fetchmail, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
 USE_FEATURE_GREP_FGREP_ALIAS(APPLET_ODDNAME(fgrep, grep, _BB_DIR_BIN, _BB_SUID_NEVER, fgrep))
 USE_FIND(APPLET_NOEXEC(find, find, _BB_DIR_USR_BIN, _BB_SUID_NEVER, find))
 USE_FINDFS(APPLET(findfs, _BB_DIR_SBIN, _BB_SUID_MAYBE))
@@ -235,6 +235,7 @@ USE_LSATTR(APPLET(lsattr, _BB_DIR_BIN, _BB_SUID_NEVER))
 USE_LSMOD(APPLET(lsmod, _BB_DIR_SBIN, _BB_SUID_NEVER))
 USE_MODPROBE_SMALL(APPLET_ODDNAME(lsmod, modprobe, _BB_DIR_SBIN, _BB_SUID_NEVER, modprobe))
 USE_UNLZMA(APPLET_ODDNAME(lzmacat, unlzma, _BB_DIR_USR_BIN, _BB_SUID_NEVER, lzmacat))
+USE_FEATURE_SENDMAIL_MAILX(APPLET_ODDNAME(mail, sendmail, _BB_DIR_USR_BIN, _BB_SUID_NEVER, sendmail))
 USE_MAKEDEVS(APPLET(makedevs, _BB_DIR_SBIN, _BB_SUID_NEVER))
 USE_MAN(APPLET(man, _BB_DIR_SBIN, _BB_SUID_NEVER))
 USE_MATCHPATHCON(APPLET(matchpathcon, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
@@ -313,7 +314,7 @@ USE_RX(APPLET(rx, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
 USE_SCRIPT(APPLET(script, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
 USE_SED(APPLET(sed, _BB_DIR_BIN, _BB_SUID_NEVER))
 USE_SELINUXENABLED(APPLET(selinuxenabled, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
-USE_SENDMAIL(APPLET_ODDNAME(sendmail, sendgetmail, _BB_DIR_USR_SBIN, _BB_SUID_NEVER, sendmail))
+USE_SENDMAIL(APPLET(sendmail, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
 USE_SEQ(APPLET_NOFORK(seq, seq, _BB_DIR_USR_BIN, _BB_SUID_NEVER, seq))
 USE_SESTATUS(APPLET(sestatus, _BB_DIR_USR_SBIN, _BB_SUID_NEVER))
 USE_SETARCH(APPLET(setarch, _BB_DIR_BIN, _BB_SUID_NEVER))
index a5234e0536dc823554ce44bc1aa5685da2375a3b..fa7ac3bf7cbdda988f9cc405a6e6a129acdf780f 100644 (file)
 #define sendmail_trivial_usage \
        "[-w timeout] [-H [user:pass@]server[:port]] [-S]\n" \
        "[-N type] [-f sender] [-F fullname] " \
-       USE_FEATURE_SENDMAIL_MAILX("[-s subject] [-c charset] [-a attach]... ") "[-t] [rcpt]..."
+       USE_FEATURE_SENDMAIL_MAILX("[-s subject] [-c cc-rcpt]... [-j charset] [-a attach]... [-e err-rcpt] ") "[-t] [rcpt]..."
 #define sendmail_full_usage "\n\n" \
        "Send an email\n" \
      "\nOptions:" \
      "\n       -F fullname     Sender full name. Overrides $NAME" \
        USE_FEATURE_SENDMAIL_MAILX( \
      "\n       -s subject      Subject" \
-     "\n       -c charset      Assume charset for body and subject (" CONFIG_FEATURE_SENDMAIL_CHARSET ")" \
+     "\n       -c rcpt         Cc: recipient. May be multiple" \
+     "\n       -j charset      Assume charset for body and subject (" CONFIG_FEATURE_SENDMAIL_CHARSET ")" \
      "\n       -a file         File to attach. May be multiple" \
-       )
+     "\n       -e rcpt         Errors-To: recipient" \
+       )
      "\n       -t              Read recipients and subject from body" \
      "\n" \
      "\nOther options are silently ignored; -oi is implied" \
index 1984297a6316a5508f4ea20e0f642350cf642720..9aa14220fd83e675cecf77fb95a44f57dbdaea6d 100644 (file)
@@ -694,6 +694,13 @@ config FEATURE_SENDMAIL_MAILX
        help
          Allow to specify subject, attachments and their charset.
 
+config FEATURE_SENDMAIL_MAILXX
+       bool "Allow to specify Cc: addresses and some additional headers"
+       default n
+       depends on FEATURE_SENDMAIL_MAILX
+       help
+         Allow to specify Cc: addresses and some additional headers: Errors-To:.
+
 config FEATURE_SENDMAIL_SSL
        bool "Allow to communicate via SSL/TLS"
        default y
index 6d00026e27121edd62b506891cdb73ab65ce73ad..ef6e03be6119e5903d9b9c5990ac5bcdea75488a 100644 (file)
@@ -1,6 +1,6 @@
 /* vi: set sw=4 ts=4: */
 /*
- * bare bones sendmail/fetchmail
+ * bare bones sendmail
  *
  * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
  *
@@ -14,15 +14,12 @@ struct globals {
        FILE *fp0; // initial stdin
        // arguments for SSL connection helper
        const char *xargs[9];
-       // arguments for postprocess helper
-       const char *fargs[3];
 };
 #define G (*ptr_to_globals)
 #define helper_pid      (G.helper_pid)
 #define timeout         (G.timeout   )
 #define fp0             (G.fp0       )
 #define xargs           (G.xargs     )
-#define fargs           (G.fargs     )
 #define INIT_G() do { \
        SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
        xargs[0] = "openssl"; \
@@ -33,13 +30,9 @@ struct globals {
        xargs[5] = "-tls1"; \
        xargs[6] = "-starttls"; \
        xargs[7] = "smtp"; \
-       fargs[0] = CONFIG_FEATURE_SENDMAIL_CHARSET; \
 } while (0)
 
-#define opt_connect       (xargs[4])
-#define opt_after_connect (xargs[5])
-#define opt_charset       (fargs[0])
-#define opt_subject       (fargs[1])
+#define opt_connect (xargs[4])
 
 static void uuencode(char *fname, const char *text)
 {
@@ -112,8 +105,12 @@ static void signal_handler(int signo)
 
        // SIGCHLD. reap zombies
        if (wait_any_nohang(&err) > 0)
-               if (WIFEXITED(err) && WEXITSTATUS(err))
-                       bb_error_msg_and_die("child exited (%d)", WEXITSTATUS(err));
+               if (WIFEXITED(err)) {
+//                     if (WEXITSTATUS(err))
+                               bb_error_msg_and_die("child exited (%d)", WEXITSTATUS(err));
+//                     else
+//                             kill(0, SIGCONT);
+               }
 #undef err
 }
 
@@ -178,10 +175,8 @@ static int smtp_checkp(const char *fmt, const char *param, int code)
        while ((answer = xmalloc_fgetline(stdin)) != NULL)
                if (strlen(answer) <= 3 || '-' != answer[3])
                        break;
-//bb_error_msg("FMT[%s]ANS[%s]", fmt, answer);
        if (answer) {
                int n = atoi(answer);
-//bb_error_msg("FMT[%s]COD[%d][%d]", fmt, n, code);
                alarm(0);
                free(answer);
                if (-1 == code || n == code)
@@ -211,50 +206,6 @@ static char *sane(char *str)
        return str;
 }
 
-#if ENABLE_FETCHMAIL
-static void pop3_checkr(const char *fmt, const char *param, char **ret)
-{
-       const char *msg = command(fmt, param);
-       char *answer = xmalloc_fgetline(stdin);
-       if (answer && '+' == *answer) {
-               alarm(0);
-               if (ret)
-                       *ret = answer+4; // skip "+OK "
-               else if (ENABLE_FEATURE_CLEAN_UP)
-                       free(answer);
-               return;
-       }
-       kill_helper();
-       bb_error_msg_and_die("%s failed", msg);
-}
-
-static inline void pop3_check(const char *fmt, const char *param)
-{
-       pop3_checkr(fmt, param, NULL);
-}
-
-static void pop3_message(const char *filename)
-{
-       int fd;
-       char *answer;
-       // 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")) != NULL) {
-               char *s = answer;
-               if ('.' == *answer) {
-                       if ('.' == answer[1])
-                               s++;
-                       else if ('\r' == answer[1] && '\n' == answer[2] && '\0' == answer[3])
-                               break;
-               }
-               xwrite(fd, s, strlen(s));
-               free(answer);
-       }
-       close(fd);
-}
-#endif
-
 // NB: parse_url can modify url[] (despite const), but only if '@' is there
 static const char *parse_url(const char *url, const char **user, const char **pass)
 {
@@ -280,64 +231,61 @@ static void rcptto(const char *s)
        smtp_checkp("RCPT TO:<%s>", s, 250);
 }
 
-int sendgetmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
-int sendgetmail_main(int argc UNUSED_PARAM, char **argv)
+int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int sendmail_main(int argc UNUSED_PARAM, char **argv)
 {
 #if ENABLE_FEATURE_SENDMAIL_MAILX
        llist_t *opt_attachments = NULL;
+       const char *opt_subject;
+       const char *opt_charset = CONFIG_FEATURE_SENDMAIL_CHARSET;
+#if ENABLE_FEATURE_SENDMAIL_MAILXX
+       llist_t *opt_carboncopies = NULL;
+       char *opt_errors_to;
+#endif
 #endif
        char *opt_from, *opt_fullname;
        const char *opt_user;
        const char *opt_pass;
+       int code;
+       char *boundary;
+       llist_t *l;
+       llist_t *headers = NULL;
+       char *domain = sane(safe_getdomainname());
+       unsigned opts;
 
        enum {
                OPT_w = 1 << 0,         // network timeout
-
                OPT_H = 1 << 1,         // [user:password@]server[:port]
                OPT_S = 1 << 2,         // connect using openssl s_client helper
-
-               OPTS_t = 1 << 3,        // sendmail: read message for recipients
-               OPTF_t = 1 << 3,        // fetchmail: use "TOP" not "RETR"
-
-               OPTS_N = 1 << 4,        // sendmail: request notification
-               OPTF_z = 1 << 4,        // fetchmail: delete from server
-
-               OPTS_f = 1 << 5,        // sendmail: sender address
-               OPTS_F = 1 << 6,        // sendmail: sender name, overrides $NAME
-
-               OPTS_s = 1 << 7,        // sendmail: subject
-               OPTS_c = 1 << 8,        // sendmail: assumed charset
-               OPTS_a = 1 << 9,        // sendmail: attachment(s)
+               OPT_t = 1 << 3,         // read message for recipients
+               OPT_N = 1 << 4,         // request notification
+               OPT_f = 1 << 5,         // sender address
+               OPT_F = 1 << 6,         // sender name, overrides $NAME
+               OPT_s = 1 << 7,         // subject
+               OPT_j = 1 << 8,         // assumed charset
+               OPT_a = 1 << 9,         // attachment(s)
+               OPT_c = 1 << 10,        // carbon copy
+               OPT_e = 1 << 11,        // errors-to address
        };
-       const char *options;
-       unsigned opts;
 
        // init global variables
        INIT_G();
 
-       // parse options, different option sets for sendmail and fetchmail
-       // N.B. opt_after_connect hereafter is NULL if we are called as fetchmail
-       // and is NOT NULL if we are called as sendmail
-       if (!ENABLE_FETCHMAIL || 's' == applet_name[0]) {
-               // SENDMAIL
-               // save initial stdin since body is piped!
-               xdup2(STDIN_FILENO, 3);
-               fp0 = fdopen(3, "r");
-               opt_complementary = "w+:a::";
-               options = "w:H:St" "N:f:F:" USE_FEATURE_SENDMAIL_MAILX("s:c:a:")
-               "X:V:vq:R:O:o:nmL:Iih:GC:B:b:A:"; // postfix compat only, ignored
-       } else {
-               // FETCHMAIL
-               opt_after_connect = NULL;
-               opt_complementary = "-1:w+";
-               options = "w:H:St" "z";
-       }
-       opts = getopt32(argv, options,
+       // save initial stdin since body is piped!
+       xdup2(STDIN_FILENO, 3);
+       fp0 = fdopen(3, "r");
+
+       // parse options
+       opt_complementary = "w+:a::" USE_FEATURE_SENDMAIL_MAILXX("c::");
+       opts = getopt32(argv,
+               "w:H:St" "N:f:F:" USE_FEATURE_SENDMAIL_MAILX("s:j:a:") USE_FEATURE_SENDMAIL_MAILXX("c:e:")
+               "X:V:vq:R:O:o:nmL:Iih:GC:B:b:A:" // postfix compat only, ignored
+               // r:Q:p:M:Dd are candidates from another man page. TODO?
+               "46E", // ssmtp introduces another quirks. TODO?: -a[upm] (user, pass, method) to be supported
                &timeout /* -w */, &opt_connect /* -H */,
                NULL, &opt_from, &opt_fullname,
-#if ENABLE_FEATURE_SENDMAIL_MAILX
-               &opt_subject, &opt_charset, &opt_attachments,
-#endif
+               USE_FEATURE_SENDMAIL_MAILX(&opt_subject, &opt_charset, &opt_attachments,)
+               USE_FEATURE_SENDMAIL_MAILXX(&opt_carboncopies, &opt_errors_to,)
                NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
        );
        //argc -= optind;
@@ -354,7 +302,6 @@ int sendgetmail_main(int argc UNUSED_PARAM, char **argv)
        // NB: parse_url modifies opt_connect[] ONLY if '@' is there.
        // Thus "127.0.0.1" won't be modified, an is ok that it is RO.
        opt_connect = parse_url(opt_connect, &opt_user, &opt_pass);
-//     bb_error_msg("H[%s] U[%s] P[%s]", opt_connect, opt_user, opt_pass);
 
        // username must be defined!
        if (!opt_user) {
@@ -376,347 +323,233 @@ int sendgetmail_main(int argc UNUSED_PARAM, char **argv)
                xdup2(STDIN_FILENO, STDOUT_FILENO);
        }
 
-       // are we sendmail?
-       if (!ENABLE_FETCHMAIL || opt_after_connect)
-/***************************************************
- * SENDMAIL
- ***************************************************/
-       {
-               int code;
-               char *boundary;
-               llist_t *l;
-               llist_t *headers = NULL;
-               char *domain = sane(safe_getdomainname());
-
-               // got no sender address? -> use username as a resort
-               if (!(opts & OPTS_f)) {
-                       opt_from = xasprintf("%s@%s", opt_user, domain);
-               }
+       // got no sender address? -> use username as a resort
+       if (!(opts & OPT_f)) {
+               opt_from = xasprintf("%s@%s", opt_user, domain);
+       }
 
-               // introduce to server
+       // introduce to server
 
-               // we didn't use SSL helper? ->
-               if (!(opts & OPT_S)) {
-                       // ... wait for initial server OK
-                       smtp_check(NULL, 220);
-               }
+       // we didn't use SSL helper? ->
+       if (!(opts & OPT_S)) {
+               // ... wait for initial server OK
+               smtp_check(NULL, 220);
+       }
 
-               // we should start with modern EHLO
-               if (250 != smtp_checkp("EHLO %s", domain, -1)) {
-                       smtp_checkp("HELO %s", domain, 250);
+       // we should start with modern EHLO
+       if (250 != smtp_checkp("EHLO %s", domain, -1)) {
+               smtp_checkp("HELO %s", domain, 250);
+       }
+       if (ENABLE_FEATURE_CLEAN_UP)
+               free(domain);
+
+       // set sender
+       // NOTE: if password has not been specified
+       // then no authentication is possible
+       code = (opt_pass ? -1 : 250);
+       // first try softly without authentication
+       while (250 != smtp_checkp("MAIL FROM:<%s>", opt_from, code)) {
+               // MAIL FROM failed -> authentication needed
+               if (334 == smtp_check("AUTH LOGIN", -1)) {
+                       uuencode(NULL, opt_user); // opt_user != NULL
+                       smtp_check("", 334);
+                       uuencode(NULL, opt_pass);
+                       smtp_check("", 235);
                }
-               if (ENABLE_FEATURE_CLEAN_UP)
-                       free(domain);
-
-               // set sender
-               // NOTE: if password has not been specified
-               // then no authentication is possible
-               code = (opt_pass ? -1 : 250);
-               // first try softly without authentication
-               while (250 != smtp_checkp("MAIL FROM:<%s>", opt_from, code)) {
-                       // MAIL FROM failed -> authentication needed
-                       if (334 == smtp_check("AUTH LOGIN", -1)) {
-                               uuencode(NULL, opt_user); // opt_user != NULL
-                               smtp_check("", 334);
-                               uuencode(NULL, opt_pass);
-                               smtp_check("", 235);
-                       }
-                       // authenticated OK? -> retry to set sender
-                       // but this time die on failure!
-                       code = 250;
+               // authenticated OK? -> retry to set sender
+               // but this time die on failure!
+               code = 250;
+       }
+
+       // recipients specified as arguments
+       while (*argv) {
+               char *s = sane(*argv);
+               // loose test on email address validity
+               if (strchr(s, '@')) {
+                       rcptto(s);
+                       llist_add_to_end(&headers, xasprintf("To: %s", s));
                }
+               argv++;
+       }
 
-               // recipients specified as arguments
-               while (*argv) {
-                       // loose test on email address validity
-                       if (strchr(sane(*argv), '@')) {
-                               rcptto(sane(*argv));
-                               llist_add_to_end(&headers, xasprintf("To: %s", *argv));
-                       }
-                       argv++;
+#if ENABLE_FEATURE_SENDMAIL_MAILXX
+       // carbon copies recipients specified as -c options
+       for (l = opt_carboncopies; l; l = l->link) {
+               char *s = sane(l->data);
+               // loose test on email address validity
+               if (strchr(s, '@')) {
+                       rcptto(s);
+                       // TODO: do we ever need to mangle the message?
+                       //llist_add_to_end(&headers, xasprintf("Cc: %s", s));
                }
+       }
+#endif
 
-               // if -t specified or no recipients specified -> read recipients from message
-               // i.e. scan stdin for To:, Cc:, Bcc: lines ...
-               // ... and then use the rest of stdin as message body
-               // N.B. subject read from body can be further overrided with one specified on command line.
-               // recipients are merged. Bcc: lines are deleted
-               // N.B. other headers are collected and will be dumped verbatim
-               if (opts & OPTS_t || !headers) {
-                       // fetch recipients and (optionally) subject
-                       char *s;
-                       while ((s = xmalloc_fgetline(fp0)) != NULL) {
-                               if (0 == strncasecmp("To: ", s, 4) || 0 == strncasecmp("Cc: ", s, 4)) {
-                                       rcptto(sane(s+4));
-                                       llist_add_to_end(&headers, s);
-                               } else if (0 == strncasecmp("Bcc: ", s, 5)) {
-                                       rcptto(sane(s+5));
-                                       free(s);
-                                       // N.B. Bcc vanishes from headers!
-                               } else if (0 == strncmp("Subject: ", s, 9)) {
-                                       // we read subject -> use it verbatim unless it is specified
-                                       // on command line
-                                       if (!(opts & OPTS_s))
-                                               llist_add_to_end(&headers, s);
-                                       else
-                                               free(s);
-                               } else if (s[0]) {
-                                       // misc header
+       // if -t specified or no recipients specified -> read recipients from message
+       // i.e. scan stdin for To:, Cc:, Bcc: lines ...
+       // ... and then use the rest of stdin as message body
+       // N.B. subject read from body can be further overrided with one specified on command line.
+       // recipients are merged. Bcc: lines are deleted
+       // N.B. other headers are collected and will be dumped verbatim
+       if (opts & OPT_t || !headers) {
+               // fetch recipients and (optionally) subject
+               char *s;
+               while ((s = xmalloc_fgetline(fp0)) != NULL) {
+                       if (0 == strncasecmp("To: ", s, 4) || 0 == strncasecmp("Cc: ", s, 4)) {
+                               rcptto(sane(s+4));
+                               llist_add_to_end(&headers, s);
+                       } else if (0 == strncasecmp("Bcc: ", s, 5)) {
+                               rcptto(sane(s+5));
+                               free(s);
+                               // N.B. Bcc vanishes from headers!
+                       } else if (0 == strncmp("Subject: ", s, 9)) {
+                               // we read subject -> use it verbatim unless it is specified
+                               // on command line
+                               if (!(opts & OPT_s))
                                        llist_add_to_end(&headers, s);
-                               } else {
+                               else
                                        free(s);
-                                       break; // stop on the first empty line
-                               }
+                       } else if (s[0]) {
+                               // misc header
+                               llist_add_to_end(&headers, s);
+                       } else {
+                               free(s);
+                               break; // stop on the first empty line
                        }
                }
+       }
 
-               // enter "put message" mode
-               smtp_check("DATA", 354);
+       // enter "put message" mode
+       smtp_check("DATA", 354);
 
-               // put headers we could have preread with -t
-               for (l = headers; l; l = l->link) {
-                       printf("%s\r\n", l->data);
-                       if (ENABLE_FEATURE_CLEAN_UP)
-                               free(l->data);
-               }
+       // put headers we could have preread with -t
+       for (l = headers; l; l = l->link) {
+               printf("%s\r\n", l->data);
+               if (ENABLE_FEATURE_CLEAN_UP)
+                       free(l->data);
+       }
 
-               // put (possibly encoded) subject
-               if (opts & OPTS_c)
-                       sane((char *)opt_charset);
-               if (opts & OPTS_s) {
-                       printf("Subject: ");
-                       if (opts & OPTS_c) {
-                               printf("=?%s?B?", opt_charset);
-                               uuencode(NULL, opt_subject);
-                               printf("?=");
-                       } else {
-                               printf("%s", opt_subject);
-                       }
-                       printf("\r\n");
+       // put (possibly encoded) subject
+       if (opts & OPT_j)
+               sane((char *)opt_charset);
+       if (opts & OPT_s) {
+               printf("Subject: ");
+               if (opts & OPT_j) {
+                       printf("=?%s?B?", opt_charset);
+                       uuencode(NULL, opt_subject);
+                       printf("?=");
+               } else {
+                       printf("%s", opt_subject);
                }
+               printf("\r\n");
+       }
 
-               // put sender name, $NAME is the default
-               if (!(opts & OPTS_F))
-                       opt_fullname = getenv("NAME");
-               if (opt_fullname)
-                       printf("From: \"%s\" <%s>\r\n", opt_fullname, opt_from);
+       // put sender name, $NAME is the default
+       if (!(opts & OPT_F))
+               opt_fullname = getenv("NAME");
+       if (opt_fullname)
+               printf("From: \"%s\" <%s>\r\n", opt_fullname, opt_from);
 
-               // put notification
-               if (opts & OPTS_N)
-                       printf("Disposition-Notification-To: %s\r\n", opt_from);
+       // put notification
+       if (opts & OPT_N)
+               printf("Disposition-Notification-To: %s\r\n", opt_from);
 
-               // make a random string -- it will delimit message parts
-               srand(monotonic_us());
-               boundary = xasprintf("%d-%d-%d", rand(), rand(), rand());
+#if ENABLE_FEATURE_SENDMAIL_MAILXX
+       // put errors recipient
+       if (opts & OPT_e)
+               printf("Errors-To: %s\r\n", opt_errors_to);
+#endif
 
-               // put common headers
-               // TODO: do we really need this?
-//             printf("Message-ID: <%s>\r\n", boundary);
+       // make a random string -- it will delimit message parts
+       srand(monotonic_us());
+       boundary = xasprintf("%d-%d-%d", rand(), rand(), rand());
 
-#if ENABLE_FEATURE_SENDMAIL_MAILX
-               // have attachments? -> compose multipart MIME
-               if (opt_attachments) {
-                       const char *fmt;
-                       const char *p;
-                       char *q;
+       // put common headers
+       // TODO: do we really need this?
+//     printf("Message-ID: <%s>\r\n", boundary);
 
+#if ENABLE_FEATURE_SENDMAIL_MAILX
+       // have attachments? -> compose multipart MIME
+       if (opt_attachments) {
+               const char *fmt;
+               const char *p;
+               char *q;
+
+               printf(
+                       "Mime-Version: 1.0\r\n"
+                       "%smultipart/mixed; boundary=\"%s\"\r\n"
+                       , "Content-Type: "
+                       , boundary
+               );
+
+               // body is pseudo attachment read from stdin in first turn
+               llist_add_to(&opt_attachments, (char *)"-");
+
+               // put body + attachment(s)
+               // N.B. all these weird things just to be tiny
+               // by reusing string patterns!
+               fmt =
+                       "\r\n--%s\r\n"
+                       "%stext/plain; charset=%s\r\n"
+                       "%s%s\r\n"
+                       "%s"
+               ;
+               p = opt_charset;
+               q = (char *)"";
+               l = opt_attachments;
+               while (l) {
                        printf(
-                               "Mime-Version: 1.0\r\n"
-                               "%smultipart/mixed; boundary=\"%s\"\r\n"
-                               , "Content-Type: "
+                               fmt
                                , boundary
+                               , "Content-Type: "
+                               , p
+                               , "Content-Disposition: inline"
+                               , q
+                               , "Content-Transfer-Encoding: base64\r\n"
                        );
-
-                       // body is pseudo attachment read from stdin in first turn
-                       llist_add_to(&opt_attachments, (char *)"-");
-
-                       // put body + attachment(s)
-                       // N.B. all these weird things just to be tiny
-                       // by reusing string patterns!
+                       p = "";
                        fmt =
                                "\r\n--%s\r\n"
-                               "%stext/plain; charset=%s\r\n"
-                               "%s%s\r\n"
+                               "%sapplication/octet-stream%s\r\n"
+                               "%s; filename=\"%s\"\r\n"
                                "%s"
                        ;
-                       p = opt_charset;
-                       q = (char *)"";
-                       l = opt_attachments;
-                       while (l) {
-                               printf(
-                                       fmt
-                                       , boundary
-                                       , "Content-Type: "
-                                       , p
-                                       , "Content-Disposition: inline"
-                                       , q
-                                       , "Content-Transfer-Encoding: base64\r\n"
-                               );
-                               p = "";
-                               fmt =
-                                       "\r\n--%s\r\n"
-                                       "%sapplication/octet-stream%s\r\n"
-                                       "%s; filename=\"%s\"\r\n"
-                                       "%s"
-                               ;
-                               uuencode(l->data, NULL);
-                               l = l->link;
-                               if (l)
-                                       q = bb_get_last_path_component_strip(l->data);
-                       }
+                       uuencode(l->data, NULL);
+                       l = l->link;
+                       if (l)
+                               q = bb_get_last_path_component_strip(l->data);
+               }
 
-                       // put message terminator
-                       printf("\r\n--%s--\r\n" "\r\n", boundary);
+               // put message terminator
+               printf("\r\n--%s--\r\n" "\r\n", boundary);
 
-               // no attachments? -> just dump message
-               } else
+       // no attachments? -> just dump message
+       } else
 #endif
-               {
-                       char *s;
-                       // terminate headers
-                       printf("\r\n");
-                       // put plain text respecting leading dots
-                       while ((s = xmalloc_fgetline(fp0)) != NULL) {
-                               // escape leading dots
-                               // N.B. this feature is implied even if no -i switch given
-                               // N.B. we need to escape the leading dot regardless of
-                               // whether it is single or not character on the line
-                               if (/*(opts & OPTS_i) && */ '.' == s[0] /*&& '\0' == s[1] */)
-                                       printf(".");
-                               // dump read line
-                               printf("%s\r\n", s);
-                       }
+       {
+               char *s;
+               // terminate headers
+               printf("\r\n");
+               // put plain text respecting leading dots
+               while ((s = xmalloc_fgetline(fp0)) != NULL) {
+                       // escape leading dots
+                       // N.B. this feature is implied even if no -i (-oi) switch given
+                       // N.B. we need to escape the leading dot regardless of
+                       // whether it is single or not character on the line
+                       if ('.' == s[0] /*&& '\0' == s[1] */)
+                               printf(".");
+                       // dump read line
+                       printf("%s\r\n", s);
                }
-
-               // leave "put message" mode
-               smtp_check(".", 250);
-               // ... and say goodbye
-               smtp_check("QUIT", 221);
-               // cleanup
-               if (ENABLE_FEATURE_CLEAN_UP)
-                       fclose(fp0);
        }
-#if ENABLE_FETCHMAIL
-/***************************************************
- * FETCHMAIL
- ***************************************************/
-       else {
-               char *buf;
-               unsigned nmsg;
-               char *hostname;
-               pid_t pid;
-
-               // cache fetch command:
-               // TOP will return only the headers
-               // RETR will dump the whole message
-               const char *retr = (opts & OPTF_t) ? "TOP %u 0" : "RETR %u";
-
-               // goto maildir
-               xchdir(*argv++);
-
-               // cache postprocess program
-               *fargs = *argv;
-
-               // authenticate
-
-               // password is mandatory
-               if (!opt_pass) {
-                       bb_error_msg_and_die("no password");
-               }
 
-               // get server greeting
-               pop3_checkr(NULL, NULL, &buf);
-
-               // server supports APOP?
-               if ('<' == *buf) {
-                       md5_ctx_t md5;
-                       // yes! compose <stamp><password>
-                       char *s = strchr(buf, '>');
-                       if (s)
-                               strcpy(s+1, opt_pass);
-                       s = buf;
-                       // get md5 sum of <stamp><password>
-                       md5_begin(&md5);
-                       md5_hash(s, strlen(s), &md5);
-                       md5_end(s, &md5);
-                       // 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((char *)md5_hex, s, 16) = '\0';
-                       // APOP
-                       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-4); // buf is "+OK " away from malloc'ed string
-                       }
-               // server ignores APOP -> use simple text authentication
-               } else {
-                       // USER
-                       pop3_check("USER %s", opt_user);
-                       // PASS
-                       pop3_check("PASS %s", opt_pass);
-               }
-
-               // get mailbox statistics
-               pop3_checkr("STAT", NULL, &buf);
-
-               // prepare message filename suffix
-               hostname = safe_gethostname();
-               pid = getpid();
-
-               // get messages counter
-               // 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-4); // buf is "+OK " away from malloc'ed string
-
-               // loop through messages
-               for (; nmsg; nmsg--) {
-
-                       // generate unique filename
-                       char *filename = xasprintf("tmp/%llu.%u.%s",
-                                       monotonic_us(), (unsigned)pid, hostname);
-                       char *target;
-                       int rc;
-
-                       // retrieve message in ./tmp/
-                       pop3_check(retr, (const char *)(ptrdiff_t)nmsg);
-                       pop3_message(filename);
-                       // delete message from server
-                       if (opts & OPTF_z)
-                               pop3_check("DELE %u", (const char*)(ptrdiff_t)nmsg);
-
-                       // run postprocessing program
-                       if (*fargs) {
-                               fargs[1] = filename;
-                               rc = wait4pid(spawn((char **)fargs));
-                               if (99 == rc)
-                                       break;
-                               if (1 == rc)
-                                       goto skip;
-                       }
-
-                       // 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);
- skip:
-                       free(filename);
-               }
-
-               // Bye
-               pop3_check("QUIT", NULL);
-       }
-#endif // ENABLE_FETCHMAIL
+       // leave "put message" mode
+       smtp_check(".", 250);
+       // ... and say goodbye
+       smtp_check("QUIT", 221);
+       // cleanup
+       if (ENABLE_FEATURE_CLEAN_UP)
+               fclose(fp0);
 
        return EXIT_SUCCESS;
 }