]> diplodocus.org Git - nmh/commitdiff
Merge remote-tracking branch 'origin' into smtputf8
authorDavid Levine <levinedl@acm.org>
Sun, 25 Sep 2016 13:28:05 +0000 (09:28 -0400)
committerDavid Levine <levinedl@acm.org>
Sun, 25 Sep 2016 13:28:05 +0000 (09:28 -0400)
1  2 
Makefile.am
docs/pending-release-notes
mts/smtp/smtp.c
mts/smtp/smtp.h
test/send/test-sendfrom
uip/post.c
uip/send.c

diff --combined Makefile.am
index ca46a4cd7d1ca4ea0dcbaaa12d49427b81e49ea1,4197dc7550c4c213770069ebda5c59c8f8ac71ff..cb1c39fbd5c4c68d0ca81d1602f14ded9de804c6
@@@ -94,8 -94,7 +94,8 @@@ TESTS = test/ali/test-ali test/anno/tes
        test/post/test-post-dcc test/post/test-post-fcc \
        test/post/test-post-multifrom test/post/test-post-envelope \
        test/post/test-post-group test/post/test-mts test/post/test-messageid \
 -      test/post/test-sendfiles test/prompter/test-prompter \
 +      test/post/test-rfc6531 test/post/test-sendfiles \
 +      test/prompter/test-prompter \
        test/rcv/test-rcvdist test/rcv/test-rcvpack test/rcv/test-rcvstore \
        test/rcv/test-rcvtty test/refile/test-refile test/repl/test-convert \
        test/repl/test-if-str test/repl/test-trailing-newline \
@@@ -118,8 -117,8 +118,8 @@@ check_PROGRAMS = test/getfullname test/
  ## distcheck, $nmhetcdir.  For distcheck, prepend $MH_INST_DIR (from
  ## test/common.sh.in), which is based on $MH_TEST_DIR (from
  ## $TESTS_ENVIRONMENT).
- DISTCHECK_CONFIGURE_FLAGS = DISABLE_SETGID_MAIL=1 \
-                           NMHETCDIRINST='$${abs_builddir}/test/testdir/inst'
+ AM_DISTCHECK_CONFIGURE_FLAGS = DISABLE_SETGID_MAIL=1 \
+                       NMHETCDIRINST='$${abs_builddir}/test/testdir/inst'
  
  ##
  ## Stuff that should be cleaned via "make clean"
@@@ -198,9 -197,9 +198,9 @@@ noinst_LIBRARIES = sbr/libmh.a mts/libm
  noinst_HEADERS = h/addrsbr.h h/aliasbr.h h/crawl_folders.h h/dropsbr.h \
                 h/fmt_compile.h h/fmt_scan.h h/icalendar.h h/md5.h h/mf.h \
                 h/mh.h h/mhcachesbr.h h/mhparse.h h/mime.h \
-                h/mts.h h/nmh.h h/picksbr.h h/popsbr.h h/prototypes.h \
-                h/rcvmail.h h/scansbr.h h/signals.h h/tws.h h/utils.h \
-                mts/smtp/smtp.h sbr/ctype-checked.h h/oauth.h \
+                h/mts.h h/nmh.h h/netsec.h h/picksbr.h h/popsbr.h \
+                h/prototypes.h h/rcvmail.h h/scansbr.h h/signals.h h/tws.h \
+                h/utils.h mts/smtp/smtp.h sbr/ctype-checked.h h/oauth.h \
                 thirdparty/jsmn/jsmn.h
  
  ##
@@@ -241,7 -240,8 +241,8 @@@ dist_doc_DATA = COPYRIGHT INSTALL NEWS 
  ##
  docs_contribdir = $(docdir)/contrib
  dist_docs_contrib_SCRIPTS = docs/contrib/replyfilter docs/contrib/build_nmh \
-                           docs/contrib/ml docs/contrib/vpick
+                           docs/contrib/localpostproc docs/contrib/ml \
+                           docs/contrib/vpick docs/contrib/replaliases
  dist_docs_contrib_DATA = docs/contrib/replaliases
  
  ##
@@@ -343,7 -343,8 +344,8 @@@ uip_forw_SOURCES = uip/forw.c uip/whatn
  uip_forw_LDADD = $(LDADD) $(READLINELIB) $(TERMLIB) $(ICONVLIB) $(POSTLINK)
  
  uip_inc_SOURCES = uip/inc.c uip/scansbr.c uip/dropsbr.c uip/popsbr.c
- uip_inc_LDADD = $(LDADD) $(TERMLIB) $(ICONVLIB) $(SASLLIB) $(CURLLIB) $(POSTLINK)
+ uip_inc_LDADD = $(LDADD) $(TERMLIB) $(ICONVLIB) $(SASLLIB) $(CURLLIB) \
+               $(TLSLIB) $(POSTLINK)
  
  uip_install_mh_SOURCES = uip/install-mh.c
  uip_install_mh_LDADD = $(LDADD) $(POSTLINK)
@@@ -390,7 -391,7 +392,7 @@@ uip_mhstore_SOURCES = uip/mhstore.c uip
  uip_mhstore_LDADD = $(LDADD) $(TERMLIB) $(ICONVLIB) $(POSTLINK)
  
  uip_msgchk_SOURCES = uip/msgchk.c uip/popsbr.c
- uip_msgchk_LDADD = $(LDADD) $(SASLLIB) $(CURLLIB) $(POSTLINK)
+ uip_msgchk_LDADD = $(LDADD) $(SASLLIB) $(CURLLIB) $(TLSLIB) $(POSTLINK)
  
  uip_new_SOURCES = uip/new.c
  uip_new_LDADD = $(LDADD) $(POSTLINK)
@@@ -610,7 -611,8 +612,8 @@@ sbr_libmh_a_SOURCES = sbr/addrsbr.c sbr
                      sbr/m_draft.c sbr/m_getfld.c sbr/m_gmprot.c \
                      sbr/m_maildir.c sbr/m_name.c sbr/m_popen.c sbr/m_rand.c \
                      sbr/makedir.c sbr/md5.c sbr/message_id.c \
-                     sbr/mime_type.c sbr/mts.c sbr/norm_charmap.c sbr/path.c \
+                     sbr/mime_type.c sbr/mts.c sbr/netsec.c \
+                     sbr/norm_charmap.c sbr/path.c \
                      sbr/peekc.c sbr/pidwait.c sbr/pidstatus.c \
                      sbr/print_help.c sbr/print_sw.c sbr/print_version.c \
                      sbr/push.c sbr/putenv.c sbr/refile.c sbr/remdir.c \
index 70ed4ff626d924c7ade9f3fd84c59bac5c1c02e3,4dfc9ec54699d5be184762d9bb87434f361e1826..f44eb066dbcd17dfbb1d9757a61301388b53082c
@@@ -48,8 -48,7 +48,10 @@@ NEW FEATURE
  - post(8) -snoop now attempts to decode base64-encoded SMTP traffic.
  - folder(1) -nocreate now prints a warning message for a non-existant folder.
  - mhfixmsg(1) now allows -decodetext binary, though 8bit is still the default.
-   RFC 6532, assuming all 8-bit message header field bodies are UTF-8.
+ - inc(1) and msgchk(1) now support TLS encryption natively.
 +- Support for SMTPUTF8 (RFC 6531) has been added.  mhshow(1) already supported
++  RFC 6532, assuming all 8-bit message header field bodies are UTF-8 and use
++  of a UTF-8 locale.
  
  -----------------
  OBSOLETE FEATURES
diff --combined mts/smtp/smtp.c
index 47215a8b583246f27e073ebeb12baf50559dc29c,4fb6b1f4c9fdcb5f2d655f3f3cea714d747a80f5..b5e68fa1fa6b03cc90d8ea8e3f2ab4046febed45
  #include <h/mts.h>
  #include <h/signals.h>
  #include <h/utils.h>
- #include <h/oauth.h>
- 
- #ifdef CYRUS_SASL
- #include <sasl/sasl.h>
- #include <sasl/saslutil.h>
- # if SASL_VERSION_FULL < 0x020125
-     /* Cyrus SASL 2.1.25 introduced the sasl_callback_ft prototype,
-        which has an explicit void parameter list, according to best
-        practice.  So we need to cast to avoid compile warnings.
-        Provide this prototype for earlier versions. */
-     typedef int (*sasl_callback_ft)();
- # endif /* SASL_VERSION_FULL < 0x020125 */
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <netdb.h>
- #endif /* CYRUS_SASL */
+ #include <h/netsec.h>
  
- #ifdef TLS_SUPPORT
- #include <openssl/ssl.h>
- #include <openssl/err.h>
- #endif /* TLS_SUPPORT */
+ #include <sys/socket.h>
  
  /*
   * This module implements an interface to SendMail very similar
  #define       SM_AUTH  45
  
  static int sm_addrs = 0;
- static int sm_alarmed = 0;
  static int sm_child = NOTOK;
  static int sm_debug = 0;
  static int sm_nl = TRUE;
  static int sm_verbose = 0;
- 
- static FILE *sm_rfp = NULL;
- static FILE *sm_wfp = NULL;
- 
- static int next_line_encoded = 0;
- 
- #ifdef CYRUS_SASL
- /*
-  * Some globals needed by SASL
-  */
- 
- static sasl_conn_t *conn = NULL;      /* SASL connection state */
- static int sasl_complete = 0;         /* Has authentication succeeded? */
- static sasl_ssf_t sasl_ssf;           /* Our security strength factor */
- static int maxoutbuf;                 /* Maximum crypto output buffer */
- static char *sasl_outbuffer;          /* SASL output buffer for encryption */
- static int sasl_outbuflen;            /* Current length of data in outbuf */
- static int sm_get_user(void *, int, const char **, unsigned *);
- static int sm_get_pass(sasl_conn_t *, void *, int, sasl_secret_t **);
- 
- static sasl_callback_t callbacks[] = {
-     { SASL_CB_USER, (sasl_callback_ft) sm_get_user, NULL },
- #define SM_SASL_N_CB_USER 0
-     { SASL_CB_AUTHNAME, (sasl_callback_ft) sm_get_user, NULL },
- #define SM_SASL_N_CB_AUTHNAME 1
-     { SASL_CB_PASS, (sasl_callback_ft) sm_get_pass, NULL },
- #define SM_SASL_N_CB_PASS 2
-     { SASL_CB_LIST_END, NULL, NULL },
- };
- 
- #else /* CYRUS_SASL */
- int sasl_ssf = 0;
- #endif /* CYRUS_SASL */
- 
- #ifdef TLS_SUPPORT
- static SSL_CTX *sslctx = NULL;
- static SSL *ssl = NULL;
- static BIO *sbior = NULL;
- static BIO *sbiow = NULL;
- static BIO *io = NULL;
- 
- static int tls_negotiate(void);
- #endif /* TLS_SUPPORT */
- 
- #if defined(CYRUS_SASL) || defined(TLS_SUPPORT)
- #define SASL_MAXRECVBUF 65536
- static int sm_fgetc(FILE *);
- static char *sasl_inbuffer;           /* SASL input buffer for encryption */
- static char *sasl_inptr;              /* Pointer to current inbuf position */
- static int sasl_inbuflen;             /* Current length of data in inbuf */
- #else
- #define sm_fgetc fgetc
- #endif
- 
- static int tls_active = 0;
+ static netsec_context *nsc = NULL;
  
  static char *sm_noreply = "No reply text given";
  static char *sm_moreply = "; ";
@@@ -153,65 -79,42 +79,42 @@@ static char *EHLOkeys[MAXEHLO + 1]
  /*
   * static prototypes
   */
- static int smtp_init (char *, char *, char *, int, int, int, int, int,
-                     char *, char *, const char *, int);
- static int sendmail_init (char *, char *, int, int, int, int, int,
-                           char *, char *);
+ static int smtp_init (char *, char *, char *, int, int, int, int, const char *,
+                     const char *, const char *, int);
+ static int sendmail_init (char *, int, int, int, int, const char *,
+                         const char *);
  
  static int rclient (char *, char *);
- static int sm_ierror (char *fmt, ...);
+ static int sm_ierror (const char *fmt, ...);
+ static int sm_nerror (char *);
  static int smtalk (int time, char *fmt, ...);
- static int sm_wrecord (char *, int);
  static int sm_wstream (char *, int);
- static int sm_werror (void);
  static int smhear (void);
- static int sm_rrecord (char *, int *);
- static int sm_rerror (int);
- static void alrmser (int);
  static char *EHLOset (char *);
- static char *prepare_for_display (const char *, int *);
- static int sm_fwrite(char *, int);
- static int sm_fputs(char *);
- static int sm_fputc(int);
- static void sm_fflush(void);
- static int sm_fgets(char *, int, FILE *);
- static int sm_auth_xoauth2(const char *, const char *, int);
- 
- #ifdef CYRUS_SASL
- /*
-  * Function prototypes needed for SASL
-  */
- 
- static int sm_auth_sasl(char *, int, char *, char *);
- #endif /* CYRUS_SASL */
+ static int sm_sasl_callback(enum sasl_message_type, unsigned const char *,
+                           unsigned int, unsigned char **, unsigned int *,
+                           char **);
  
  int
  sm_init (char *client, char *server, char *port, int watch, int verbose,
-          int debug, int sasl, int saslssf, char *saslmech, char *user,
+          int debug, int sasl, const char *saslmech, const char *user,
           const char *oauth_svc, int tls)
  {
      if (sm_mts == MTS_SMTP)
        return smtp_init (client, server, port, watch, verbose,
-                         debug, sasl, saslssf, saslmech, user,
-                           oauth_svc, tls);
+                         debug, sasl, saslmech, user, oauth_svc, tls);
      else
-       return sendmail_init (client, server, watch, verbose,
-                               debug, sasl, saslssf, saslmech, user);
+       return sendmail_init (client, watch, verbose, debug, sasl,
+                             saslmech, user);
  }
  
  static int
  smtp_init (char *client, char *server, char *port, int watch, int verbose,
-          int debug,
-            int sasl, int saslssf, char *saslmech, char *user,
+          int debug, int sasl, const char *saslmech, const char *user,
             const char *oauth_svc, int tls)
  {
-     int result, sd1, sd2;
- #ifndef CYRUS_SASL
-     NMH_UNUSED (sasl);
-     NMH_UNUSED (saslssf);
-     NMH_UNUSED (saslmech);
-     NMH_UNUSED (user);
- #endif /* CYRUS_SASL */
+     int result, sd1;
+     char *errstr;
  
      if (watch)
        verbose = TRUE;
      sm_verbose = verbose;
      sm_debug = debug;
  
-     if (sm_rfp != NULL && sm_wfp != NULL)
+     if (nsc != NULL)
        goto send_options;
  
      if (client == NULL || *client == '\0') {
      if (client == NULL || *client == '\0')
        client = "localhost";
  
- #if defined(CYRUS_SASL) || defined(TLS_SUPPORT)
-     sasl_inbuffer = malloc(SASL_MAXRECVBUF);
-     if (!sasl_inbuffer)
-       return sm_ierror("Unable to allocate %d bytes for read buffer",
-                        SASL_MAXRECVBUF);
- #endif /* CYRUS_SASL || TLS_SUPPORT */
+     nsc = netsec_init();
  
-     if ((sd1 = rclient (server, port)) == NOTOK)
-       return RP_BHST;
+     if (user)
+       netsec_set_userid(nsc, user);
+ 
+     if (sm_debug)
+       netsec_set_snoop(nsc, 1);
  
-     if ((sd2 = dup (sd1)) == NOTOK) {
-       close (sd1);
-       return sm_ierror ("unable to dup");
+     if (sasl) {
+       if (netsec_set_sasl_params(nsc, server, "smtp", saslmech,
+                                  sm_sasl_callback, &errstr) != OK)
+           return sm_nerror(errstr);
+     }
+ 
+     if (oauth_svc) {
+       if (netsec_set_oauth_service(nsc, oauth_svc) != OK)
+           return sm_ierror("OAuth2 not supported");
      }
  
-     SIGNAL (SIGALRM, alrmser);
+     if ((sd1 = rclient (server, port)) == NOTOK)
+       return RP_BHST;
+ 
      SIGNAL (SIGPIPE, SIG_IGN);
  
-     if ((sm_rfp = fdopen (sd1, "r")) == NULL
-           || (sm_wfp = fdopen (sd2, "w")) == NULL) {
-       close (sd1);
-       close (sd2);
-       sm_rfp = sm_wfp = NULL;
-       return sm_ierror ("unable to fdopen");
-     }
+     netsec_set_fd(nsc, sd1, sd1);
  
-     tls_active = 0;
+     if (tls) {
+       if (netsec_set_tls(nsc, 1, &errstr) != OK)
+           return sm_nerror(errstr);
+     }
  
- #ifdef TLS_SUPPORT
      /*
       * If tls == 2, that means that the user requested "initial" TLS,
       * which happens right after the connection has opened.  Do that
       */
  
      if (tls == 2) {
-       result = tls_negotiate();
- 
-       /*
-        * Note: if tls_negotiate() fails it will call sm_end() for us,
-        * which closes the connection.
-        */
-       if (result != RP_OK)
-           return result;
+       if (netsec_negotiate_tls(nsc, &errstr) != OK) {
+           sm_end(NOTOK);
+           return sm_nerror(errstr);
+       }
      }
- #endif /* TLS_SUPPORT */
  
-     sm_alarmed = 0;
-     alarm (SM_OPEN);
+     netsec_set_timeout(nsc, SM_OPEN);
      result = smhear ();
-     alarm (0);
  
      switch (result) {
        case 220: 
        return RP_RPLY;
      }
  
- #ifdef TLS_SUPPORT
      /*
       * If the user requested TLS support, then try to do the STARTTLS command
       * as part of the initial dialog.  Assuming this works, we then need to
         * negotiation.  Oblige them.
         */
  
-       result = tls_negotiate();
- 
-       if (result != RP_OK)
-           return result;
+       if (netsec_negotiate_tls(nsc, &errstr) != OK) {
+           sm_end(NOTOK);
+           return sm_nerror(errstr);
+       }
  
        doingEHLO = 1;
        result = smtalk (SM_HELO, "EHLO %s", client);
            return RP_RPLY;
        }
      }
- #else  /* TLS_SUPPORT */
-     NMH_UNUSED (tls);
- #endif /* TLS_SUPPORT */
  
- #ifdef CYRUS_SASL
      /*
       * If the user asked for SASL, then check to see if the SMTP server
       * supports it.  Otherwise, error out (because the SMTP server
            return sm_ierror("SMTP server does not support SASL");
        }
  
-       if (saslmech && stringdex(saslmech, server_mechs) == -1) {
-           sm_end(NOTOK);
-           return sm_ierror("Requested SASL mech \"%s\" is not in the "
-                            "list of supported mechanisms:\n%s",
-                            saslmech, server_mechs);
-       }
- 
-         /* Don't call sm_auth_sasl() for XAUTH2 with -sasl.  Instead, call
-            sm_auth_xoauth2() below. */
-       if (oauth_svc == NULL  &&
-             sm_auth_sasl(user, saslssf, saslmech ? saslmech : server_mechs,
-                        server) != RP_OK) {
-           sm_end(NOTOK);
-           return NOTOK;
-       }
-     }
- #endif /* CYRUS_SASL */
- 
-     if (oauth_svc != NULL) {
-         char *server_mechs;
-       if ((server_mechs = EHLOset("AUTH")) == NULL
-             || stringdex("XOAUTH2", server_mechs) == -1) {
-           sm_end(NOTOK);
-           return sm_ierror("SMTP server does not support SASL XOAUTH2");
-       }
-       if (sm_auth_xoauth2(user, oauth_svc, debug) != RP_OK) {
+       if (netsec_negotiate_sasl(nsc, server_mechs, &errstr) != OK) {
            sm_end(NOTOK);
-           return NOTOK;
+           return sm_nerror(errstr);
        }
      }
  
@@@ -411,28 -279,19 +279,19 @@@ send_options: 
  }
  
  int
- sendmail_init (char *client, char *server, int watch, int verbose,
-                int debug, int sasl, int saslssf, char *saslmech, char *user)
+ sendmail_init (char *client, int watch, int verbose, int debug, int sasl,
+              const char *saslmech, const char *user)
  {
      unsigned int i, result, vecp;
      int pdi[2], pdo[2];
-     char *vec[15];
- #ifdef CYRUS_SASL
-     char *server_mechs;
- #else  /* CYRUS_SASL */
-     NMH_UNUSED (server);
-     NMH_UNUSED (sasl);
-     NMH_UNUSED (saslssf);
-     NMH_UNUSED (saslmech);
-     NMH_UNUSED (user);
- #endif /* CYRUS_SASL */
+     char *vec[15], *errstr;
  
      if (watch)
        verbose = TRUE;
  
      sm_verbose = verbose;
      sm_debug = debug;
-     if (sm_rfp != NULL && sm_wfp != NULL)
+     if (nsc)
        return RP_OK;
  
      if (client == NULL || *client == '\0') {
      if (client == NULL || *client == '\0')
        client = "localhost";
  
- #if defined(CYRUS_SASL) || defined(TLS_SUPPORT)
-     sasl_inbuffer = malloc(SASL_MAXRECVBUF);
-     if (!sasl_inbuffer)
-       return sm_ierror("Unable to allocate %d bytes for read buffer",
-                        SASL_MAXRECVBUF);
- #endif /* CYRUS_SASL || TLS_SUPPORT */
+     nsc = netsec_init();
+ 
+     if (user)
+       netsec_set_userid(nsc, user);
+ 
+     if (sm_debug)
+       netsec_set_snoop(nsc, 1);
+ 
+     if (sasl) {
+       if (netsec_set_sasl_params(nsc, client, "smtp", saslmech,
+                                  sm_sasl_callback, &errstr) != OK)
+           return sm_nerror(errstr);
+     }
  
      if (pipe (pdi) == NOTOK)
        return sm_ierror ("no pipes");
            _exit (-1);         /* NOTREACHED */
  
        default: 
-           SIGNAL (SIGALRM, alrmser);
            SIGNAL (SIGPIPE, SIG_IGN);
  
            close (pdi[1]);
            close (pdo[0]);
-           if ((sm_rfp = fdopen (pdi[0], "r")) == NULL
-                   || (sm_wfp = fdopen (pdo[1], "w")) == NULL) {
-               close (pdi[0]);
-               close (pdo[1]);
-               sm_rfp = sm_wfp = NULL;
-               return sm_ierror ("unable to fdopen");
-           }
-           sm_alarmed = 0;
-           alarm (SM_OPEN);
+ 
+           netsec_set_fd(nsc, pdi[i], pdo[1]);
+           netsec_set_timeout(nsc, SM_OPEN);
            result = smhear ();
-           alarm (0);
            switch (result) {
                case 220: 
                    break;
                    return RP_RPLY;
            }
  
- #ifdef CYRUS_SASL
-     /*
-      * If the user asked for SASL, then check to see if the SMTP server
-      * supports it.  Otherwise, error out (because the SMTP server
-      * might have been spoofed; we don't want to just silently not
-      * do authentication
-      */
- 
-     if (sasl) {
-       if (! (server_mechs = EHLOset("AUTH"))) {
-           sm_end(NOTOK);
-           return sm_ierror("SMTP server does not support SASL");
-       }
- 
-       if (saslmech && stringdex(saslmech, server_mechs) == -1) {
-           sm_end(NOTOK);
-           return sm_ierror("Requested SASL mech \"%s\" is not in the "
-                            "list of supported mechanisms:\n%s",
-                            saslmech, server_mechs);
-       }
- 
-       if (sm_auth_sasl(user, saslssf, saslmech ? saslmech : server_mechs,
-                        server) != RP_OK) {
-           sm_end(NOTOK);
-           return NOTOK;
-       }
-     }
- #endif /* CYRUS_SASL */
+           /*
+            * If the user asked for SASL, then check to see if the SMTP server
+            * supports it.  Otherwise, error out (because the SMTP server
+            * might have been spoofed; we don't want to just silently not
+            * do authentication
+            */
+ 
+           if (sasl) {
+               char *server_mechs;
+               if (! (server_mechs = EHLOset("AUTH"))) {
+                   sm_end(NOTOK);
+                   return sm_ierror("SMTP server does not support SASL");
+               }
+               if (netsec_negotiate_sasl(nsc, server_mechs, &errstr) != OK) {
+                   sm_end(NOTOK);
+                   return sm_nerror(errstr);
+               }
+           }
  
            if (watch)
                smtalk (SM_HELO, "VERB on");
@@@ -593,22 -441,9 +441,22 @@@ rclient (char *server, char *service
  }
  
  int
 -sm_winit (char *from)
 +sm_winit (char *from, int smtputf8)
  {
 -    switch (smtalk (SM_MAIL, "MAIL FROM:<%s>", from)) {
 +    const char *const mail_parameters = smtputf8
 +        ? " BODY=8BITMIME SMTPUTF8"
 +        : "";
 +
 +    /* Just for information, if an attempt is made to send to an 8-bit
 +       address without specifying SMTPUTF8, Gmail responds with
 +       555 5.5.2 Syntax error.
 +       Gmail doesn't require the 8BITMIME, but RFC 6531 Sec. 1.2 does. */
 +    if (smtputf8  &&  (! EHLOset("8BITMIME") || ! EHLOset("SMTPUTF8"))) {
 +      sm_end (NOTOK);
 +      return RP_UCMD;
 +    }
 +
 +    switch (smtalk (SM_MAIL, "MAIL FROM:<%s>%s", from, mail_parameters)) {
        case 250: 
            sm_addrs = 0;
            return RP_OK;
@@@ -694,10 -529,7 +542,7 @@@ sm_wtxt (char *buffer, int len
  {
      int result;
  
-     sm_alarmed = 0;
-     alarm (SM_TEXT);
      result = sm_wstream (buffer, len);
-     alarm (0);
  
      return (result == NOTOK ? RP_BHST : RP_OK);
  }
@@@ -746,7 -578,7 +591,7 @@@ sm_end (int type
        }
      }
  
-     if (sm_rfp == NULL && sm_wfp == NULL)
+     if (nsc == NULL)
        return RP_OK;
  
      switch (type) {
            break;
      }
  
- #ifdef TLS_SUPPORT
-     if (tls_active) {
-       BIO_ssl_shutdown(io);
-       BIO_free_all(io);
-     }
- #endif /* TLS_SUPPORT */
- 
-     if (sm_rfp != NULL) {
-       alarm (SM_CLOS);
-       fclose (sm_rfp);
-       alarm (0);
-     }
-     if (sm_wfp != NULL) {
-       alarm (SM_CLOS);
-       fclose (sm_wfp);
-       alarm (0);
+     if (nsc != NULL) {
+       netsec_shutdown(nsc, 1);
+       nsc = NULL;
      }
  
      if (sm_mts == MTS_SMTP) {
        status = 0;
- #ifdef CYRUS_SASL
-       if (conn) {
-           sasl_dispose(&conn);
-           if (sasl_outbuffer) {
-               free(sasl_outbuffer);
-           }
-       }
-       if (sasl_inbuffer)
-           free(sasl_inbuffer);
- #endif /* CYRUS_SASL */
      } else if (sm_child != NOTOK) {
        status = pidwait (sm_child, OK);
        sm_child = NOTOK;
        status = OK;
      }
  
-     sm_rfp = sm_wfp = NULL;
      return (status ? RP_BHST : RP_OK);
  }
  
- #ifdef CYRUS_SASL
- /*
-  * This function implements SASL authentication for SMTP.  If this function
-  * completes successfully, then authentication is successful and we've
-  * (optionally) negotiated a security layer.
-  */
- 
- #define CHECKB64SIZE(insize, outbuf, outsize) \
-     { size_t wantout = (((insize + 2) / 3) * 4) + 32; \
-       if (wantout > outsize) { \
-           outbuf = mh_xrealloc(outbuf, outsize = wantout); \
-       } \
-     }
  
  static int
- sm_auth_sasl(char *user, int saslssf, char *mechlist, char *inhost)
+ sm_ierror (const char *fmt, ...)
  {
-     int result, status;
-     unsigned int buflen, outlen;
-     char *buf, *outbuf = NULL, host[NI_MAXHOST];
-     const char *chosen_mech;
-     sasl_security_properties_t secprops;
-     sasl_ssf_t *ssf;
-     int *outbufmax;
-     struct nmh_creds creds = { 0, 0, 0 };
-     size_t outbufsize = 0;
- 
-     /*
-      * Initialize the callback contexts
-      */
- 
-     /*
-      * This is a _bit_ of a hack ... but if the hostname wasn't supplied
-      * to us on the command line, then call getpeername and do a
-      * reverse-address lookup on the IP address to get the name.
-      */
- 
-     memset(host, 0, sizeof(host));
- 
-     if (!inhost) {
-       struct sockaddr_storage sin;
-       socklen_t len = sizeof(sin);
-       int result;
- 
-       if (getpeername(fileno(sm_wfp), (struct sockaddr *) &sin, &len) < 0) {
-           sm_ierror("getpeername on SMTP socket failed: %s",
-                     strerror(errno));
-           return NOTOK;
-       }
- 
-       result = getnameinfo((struct sockaddr *) &sin, len, host, sizeof(host),
-                            NULL, 0, NI_NAMEREQD);
-       if (result != 0) {
-           sm_ierror("Unable to look up name of connected host: %s",
-                     gai_strerror(result));
-           return NOTOK;
-       }
-     } else {
-       strncpy(host, inhost, sizeof(host) - 1);
-     }
- 
-     /* It's OK to copy the addresses here.  The callbacks that use
-        them will only be called before this function returns. */
-     creds.host = host;
-     creds.user = user;
-     callbacks[SM_SASL_N_CB_USER].context = &creds;
-     callbacks[SM_SASL_N_CB_AUTHNAME].context = &creds;
-     callbacks[SM_SASL_N_CB_PASS].context = &creds;
- 
-     result = sasl_client_init(callbacks);
- 
-     if (result != SASL_OK) {
-       sm_ierror("SASL library initialization failed: %s",
-                 sasl_errstring(result, NULL, NULL));
-       return NOTOK;
-     }
- 
-     result = sasl_client_new("smtp", host, NULL, NULL, NULL, 0, &conn);
- 
-     if (result != SASL_OK) {
-       sm_ierror("SASL client initialization failed: %s",
-                 sasl_errstring(result, NULL, NULL));
-       return NOTOK;
-     }
- 
-     /*
-      * Initialize the security properties.  But if TLS is active, then
-      * don't negotiate encryption here.
-      */
- 
-     memset(&secprops, 0, sizeof(secprops));
-     secprops.maxbufsize = SASL_MAXRECVBUF;
-     secprops.max_ssf =
-       tls_active ? 0 : (saslssf != -1 ? (unsigned int) saslssf : UINT_MAX);
- 
-     result = sasl_setprop(conn, SASL_SEC_PROPS, &secprops);
- 
-     if (result != SASL_OK) {
-       sm_ierror("SASL security property initialization failed: %s",
-                 sasl_errstring(result, NULL, NULL));
-       return NOTOK;
-     }
- 
-     /*
-      * Start the actual protocol.  Feed the mech list into the library
-      * and get out a possible initial challenge
-      */
- 
-     result = sasl_client_start(conn, mechlist, NULL, (const char **) &buf,
-                              &buflen, (const char **) &chosen_mech);
- 
-     if (result != SASL_OK && result != SASL_CONTINUE) {
-       sm_ierror("SASL client start failed: %s", sasl_errdetail(conn));
-       return NOTOK;
-     }
- 
-     /*
-      * If we got an initial challenge, send it as part of the AUTH
-      * command; otherwise, just send a plain AUTH command.
-      */
- 
-     if (buflen) {
-       CHECKB64SIZE(buflen, outbuf, outbufsize);
-       status = sasl_encode64(buf, buflen, outbuf, outbufsize, NULL);
-       if (status != SASL_OK) {
-           sm_ierror("SASL base64 encode failed: %s",
-                     sasl_errstring(status, NULL, NULL));
-           if (outbuf)
-               free(outbuf);
-           return NOTOK;
-       }
- 
-       status = smtalk(SM_AUTH, "AUTH %s %s", chosen_mech, outbuf);
-     } else
-       status = smtalk(SM_AUTH, "AUTH %s", chosen_mech);
- 
-     /*
-      * Now we loop until we either fail, get a SASL_OK, or a 235
-      * response code.  Receive the challenges and process them until
-      * we're all done.
-      */
- 
-     while (result == SASL_CONTINUE) {
- 
-       /*
-        * If we get a 235 response, that means authentication has
-        * succeeded and we need to break out of the loop (yes, even if
-        * we still get SASL_CONTINUE from sasl_client_step()).
-        *
-        * Otherwise, if we get a message that doesn't seem to be a
-        * valid response, then abort
-        */
- 
-       if (status == 235)
-           break;
-       else if (status < 300 || status > 399) {
-           if (outbuf)
-               free(outbuf);
-           return RP_BHST;
-       }
-       
-       /*
-        * Special case; a zero-length response from the SMTP server
-        * is returned as a single =.  If we get that, then set buflen
-        * to be zero.  Otherwise, just decode the response.
-        */
-       
-       if (strcmp("=", sm_reply.text) == 0) {
-           outlen = 0;
-       } else {
-           if (sm_reply.length > (int) outbufsize) {
-               outbuf = mh_xrealloc(outbuf, outbufsize = sm_reply.length);
-           }
- 
-           result = sasl_decode64(sm_reply.text, sm_reply.length,
-                                  outbuf, outbufsize, &outlen);
-           if (result != SASL_OK) {
-               smtalk(SM_AUTH, "*");
-               sm_ierror("SASL base64 decode failed: %s",
-                         sasl_errstring(result, NULL, NULL));
-               if (outbuf)
-                   free(outbuf);
-               return NOTOK;
-           }
-       }
- 
-       result = sasl_client_step(conn, outbuf, outlen, NULL,
-                                 (const char **) &buf, &buflen);
- 
-       if (result != SASL_OK && result != SASL_CONTINUE) {
-           smtalk(SM_AUTH, "*");
-           sm_ierror("SASL client negotiation failed: %s",
-                     sasl_errstring(result, NULL, NULL));
-           if (outbuf)
-               free(outbuf);
-           return NOTOK;
-       }
- 
-       CHECKB64SIZE(buflen, outbuf, outbufsize);
-       status = sasl_encode64(buf, buflen, outbuf, outbufsize, NULL);
- 
-       if (status != SASL_OK) {
-           smtalk(SM_AUTH, "*");
-           sm_ierror("SASL base64 encode failed: %s",
-                     sasl_errstring(status, NULL, NULL));
-           if (outbuf)
-               free(outbuf);
-           return NOTOK;
-       }
-       
-       status = smtalk(SM_AUTH, outbuf);
-     }
- 
-     if (outbuf)
-       free(outbuf);
- 
-     /*
-      * Make sure that we got the correct response
-      */
- 
-     if (status < 200 || status > 299)
-       return RP_BHST;
- 
-     /*
-      * We _should_ have completed the authentication successfully.
-      * Get a few properties from the authentication exchange.
-      */
- 
-     result = sasl_getprop(conn, SASL_MAXOUTBUF, (const void **) &outbufmax);
- 
-     if (result != SASL_OK) {
-       sm_ierror("Cannot retrieve SASL negotiated output buffer size: %s",
-                 sasl_errstring(result, NULL, NULL));
-       return NOTOK;
-     }
- 
-     maxoutbuf = *outbufmax;
- 
-     result = sasl_getprop(conn, SASL_SSF, (const void **) &ssf);
- 
-     sasl_ssf = *ssf;
- 
-     if (result != SASL_OK) {
-       sm_ierror("Cannot retrieve SASL negotiated security strength "
-                 "factor: %s", sasl_errstring(result, NULL, NULL));
-       return NOTOK;
-     }
- 
-     if (sasl_ssf > 0) {
-       sasl_outbuffer = malloc(maxoutbuf);
+     va_list ap;
  
-       if (sasl_outbuffer == NULL) {
-               sm_ierror("Unable to allocate %d bytes for SASL output "
-                         "buffer", maxoutbuf);
-               return NOTOK;
-       }
-       sasl_outbuflen = 0;
-       sasl_inbuflen = 0;
-       sasl_inptr = sasl_inbuffer;
-     } else {
-       sasl_outbuffer = NULL;
-         /* Don't NULL out sasl_inbuffer because it could be used in
-            sm_fgetc (). */
-     }
+     va_start(ap, fmt);
+     vsnprintf (sm_reply.text, sizeof(sm_reply.text), fmt, ap);
+     va_end(ap);
  
-     sasl_complete = 1;
+     sm_reply.length = strlen (sm_reply.text);
+     sm_reply.code = NOTOK;
  
-     return RP_OK;
+     return RP_BHST;
  }
  
  /*
-  * Our callback functions to feed data to the SASL library
+  * Like sm_ierror, but assume it's an allocated error string we need to free.
   */
  
  static int
- sm_get_user(void *context, int id, const char **result, unsigned *len)
- {
-     nmh_creds_t creds = (nmh_creds_t) context;
- 
-     if (! result || ((id != SASL_CB_USER) && (id != SASL_CB_AUTHNAME)))
-       return SASL_BADPARAM;
- 
-     if (creds->user == NULL) {
-         /*
-          * Pass the 1 third argument to nmh_get_credentials() so
-          * that a default user if the -user switch to send(1)/post(8)
-          * wasn't used, and so that a default password will be supplied.
-          * That's used when those values really don't matter, and only
-          * with legacy/.netrc, i.e., with a credentials profile entry.
-          */
-         if (nmh_get_credentials (creds->host, creds->user, 1, creds) != OK) {
-             return SASL_BADPARAM;
-         }
-     }
- 
-     *result = creds->user;
-     if (len)
-       *len = strlen(creds->user);
- 
-     return SASL_OK;
- }
- 
- static int
- sm_get_pass(sasl_conn_t *conn, void *context, int id,
-           sasl_secret_t **psecret)
+ sm_nerror (char *str)
  {
-     nmh_creds_t creds = (nmh_creds_t) context;
-     int len;
- 
-     NMH_UNUSED (conn);
- 
-     if (! psecret || id != SASL_CB_PASS)
-       return SASL_BADPARAM;
- 
-     if (creds->password == NULL) {
-         /*
-          * Pass the 0 third argument to nmh_get_credentials() so
-          * that the default password isn't used.  With legacy/.netrc
-          * credentials support, we'll only get here if the -user
-          * switch to send(1)/post(8) wasn't used.
-          */
-         if (nmh_get_credentials (creds->host, creds->user, 0, creds) != OK) {
-             return SASL_BADPARAM;
-         }
-     }
- 
-     len = strlen (creds->password);
- 
-     if (! (*psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len))) {
-       return SASL_NOMEM;
-     }
- 
-     (*psecret)->len = len;
-     strcpy((char *) (*psecret)->data, creds->password);
- 
-     return SASL_OK;
- }
- #endif /* CYRUS_SASL */
- 
- /* https://developers.google.com/gmail/xoauth2_protocol */
- static int
- sm_auth_xoauth2(const char *user, const char *oauth_svc, int snoop)
- {
-     const char *xoauth_client_res;
-     int status;
- 
- #ifdef OAUTH_SUPPORT
-     xoauth_client_res = mh_oauth_do_xoauth(user, oauth_svc,
-                                          snoop ? stderr : NULL);
- 
-     if (xoauth_client_res == NULL) {
-       return sm_ierror("Internal error: mh_oauth_do_xoauth() returned NULL");
-     }
- #else
-     NMH_UNUSED(user);
-     NMH_UNUSED(snoop);
-     adios(NULL, "sendfrom built without OAUTH_SUPPORT, "
-           "so oauth_svc %s is not supported", oauth_svc);
- #endif /* OAUTH_SUPPORT */
- 
-     status = smtalk(SM_AUTH, "AUTH XOAUTH2 %s", xoauth_client_res);
-     if (status == 235) {
-         /* It worked! */
-         return RP_OK;
-     }
- 
-     /*
-      * Status is 334 and sm_reply.text contains base64-encoded JSON.  As far as
-      * epg can tell, no matter the error, the JSON is always the same:
-      * {"status":"400","schemes":"Bearer","scope":"https://mail.google.com/"}
-      * I tried these errors:
-      * - garbage token
-      * - expired token
-      * - wrong scope
-      * - wrong username
-      */
-     /* Then we're supposed to send an empty response ("\r\n"). */
-     smtalk(SM_AUTH, "");
-     /*
-      * And now we always get this, again, no matter the error:
-      * 535-5.7.8 Username and Password not accepted. Learn more at
-      * 535 5.7.8 http://support.google.com/mail/bin/answer.py?answer=14257
-      */
-     return RP_BHST;
- }
- 
- static int
- sm_ierror (char *fmt, ...)
- {
-     va_list ap;
- 
-     va_start(ap, fmt);
-     vsnprintf (sm_reply.text, sizeof(sm_reply.text), fmt, ap);
-     va_end(ap);
- 
-     sm_reply.length = strlen (sm_reply.text);
+     strncpy(sm_reply.text, str, sizeof(sm_reply.text));
+     sm_reply.text[sizeof(sm_reply.text) - 1] = '\0';
+     sm_reply.length = strlen(sm_reply.text);
      sm_reply.code = NOTOK;
+     free(str);
  
      return RP_BHST;
  }
@@@ -1232,334 -671,91 +684,91 @@@ static in
  smtalk (int time, char *fmt, ...)
  {
      va_list ap;
+     char *errstr;
      int result;
-     char *buffer;
-     size_t bufsize = BUFSIZ;
- 
-     buffer = mh_xmalloc(bufsize);
  
      va_start(ap, fmt);
-     result = vsnprintf (buffer, bufsize, fmt, ap);
+     result = netsec_vprintf (nsc, &errstr, fmt, ap);
      va_end(ap);
  
-     if (result > (int) bufsize) {
-       buffer = mh_xrealloc(buffer, bufsize = result + 1);
-       va_start(ap, fmt);
-       vsnprintf (buffer, bufsize, fmt, ap);
-       va_end(ap);
-     }
- 
-     if (sm_debug) {
-       char *decoded_buffer =
-             prepare_for_display (buffer, &next_line_encoded);
- 
-       if (sasl_ssf)
-               printf("(sasl-encrypted) ");
-       if (tls_active)
-               printf("(tls-encrypted) ");
-       printf ("=> %s\n", decoded_buffer);
-       free (decoded_buffer);
-       fflush (stdout);
-     }
- 
-     sm_alarmed = 0;
-     alarm ((unsigned) time);
-     if ((result = sm_wrecord (buffer, strlen (buffer))) != NOTOK)
-       result = smhear ();
-     alarm (0);
- 
-     free(buffer);
+     if (result != OK)
+       return sm_nerror(errstr);
  
-     return result;
- }
+     if (netsec_printf (nsc, &errstr, "\r\n") != OK)
+       return sm_nerror(errstr);
  
+     if (netsec_flush (nsc, &errstr) != OK)
+       return sm_nerror(errstr);
  
- /*
-  * write the buffer to the open SMTP channel
-  */
+     netsec_set_timeout(nsc, time);
  
- static int
- sm_wrecord (char *buffer, int len)
- {
-     if (sm_wfp == NULL)
-       return sm_werror ();
- 
-     sm_fwrite (buffer, len);
-     sm_fputs ("\r\n");
-     sm_fflush ();
- 
-     return (ferror (sm_wfp) ? sm_werror () : OK);
+     return smhear ();
  }
  
  
  static int
  sm_wstream (char *buffer, int len)
  {
-     char  *bp;
+     char *bp, *errstr;
      static char lc = '\0';
+     int rc;
  
-     if (sm_wfp == NULL)
-       return sm_werror ();
+     if (nsc == NULL) {
+       sm_ierror("No socket opened");
+       return NOTOK;
+     }
  
      if (buffer == NULL && len == 0) {
+       rc = OK;
        if (lc != '\n')
-           sm_fputs ("\r\n");
+           rc = netsec_write(nsc, "\r\n", 2, &errstr);
        lc = '\0';
-       return (ferror (sm_wfp) ? sm_werror () : OK);
+       if (rc != OK)
+           sm_nerror(errstr);
+       return rc;
      }
  
      for (bp = buffer; bp && len > 0; bp++, len--) {
        switch (*bp) {
            case '\n': 
                sm_nl = TRUE;
-               sm_fputc ('\r');
+               if (netsec_write(nsc, "\r", 1, &errstr) != OK) {
+                   sm_nerror(errstr);
+                   return NOTOK;
+               }
                break;
  
            case '.': 
                if (sm_nl)
-                   sm_fputc ('.');/* FALL THROUGH */
+                   if (netsec_write(nsc, ".", 1, &errstr) != OK) {
+                   sm_nerror(errstr);
+                   return NOTOK;
+               } /* FALL THROUGH */
+ 
            default: 
                sm_nl = FALSE;
        }
-       sm_fputc (*bp);
-       if (ferror (sm_wfp))
-           return sm_werror ();
+       if (netsec_write(nsc, bp, 1, &errstr) != OK) {
+           sm_nerror(errstr);
+           return NOTOK;
+       }
      }
  
      if (bp > buffer)
        lc = *--bp;
-     return (ferror (sm_wfp) ? sm_werror () : OK);
- }
- 
- /*
-  * Write out to the network, but do buffering for SASL (if enabled)
-  */
- 
- static int
- sm_fwrite(char *buffer, int len)
- {
- #ifdef CYRUS_SASL
-     const char *output;
-     unsigned int outputlen;
- 
-     if (sasl_complete == 0 || sasl_ssf == 0) {
- #endif /* CYRUS_SASL */
- #ifdef TLS_SUPPORT
-       if (tls_active) {
-           int ret;
- 
-           ret = BIO_write(io, buffer, len);
- 
-           if (ret <= 0) {
-               sm_ierror("TLS error during write: %s",
-                         ERR_error_string(ERR_get_error(), NULL));
-               return NOTOK;
-           }
-       } else
- #endif /* TLS_SUPPORT */
-       if ((int) fwrite(buffer, sizeof(*buffer), len, sm_wfp) < len) {
-           advise ("sm_fwrite", "fwrite");
-       }
- #ifdef CYRUS_SASL
-     } else {
-       while (len >= maxoutbuf - sasl_outbuflen) {
-           memcpy(sasl_outbuffer + sasl_outbuflen, buffer,
-                  maxoutbuf - sasl_outbuflen);
-           len -= maxoutbuf - sasl_outbuflen;
-           sasl_outbuflen = 0;
- 
-           if (sasl_encode(conn, sasl_outbuffer, maxoutbuf,
-                           &output, &outputlen) != SASL_OK) {
-               sm_ierror("Unable to SASL encode connection data: %s",
-                         sasl_errdetail(conn));
-               return NOTOK;
-           }
- 
-           if (fwrite(output, sizeof(*output), outputlen, sm_wfp) <
-               outputlen) {
-               advise ("sm_fwrite", "fwrite");
-           }
-       }
- 
-       if (len > 0) {
-           memcpy(sasl_outbuffer + sasl_outbuflen, buffer, len);
-           sasl_outbuflen += len;
-       }
-     }
- #endif /* CYRUS_SASL */
-     return ferror(sm_wfp) ? NOTOK : RP_OK;
- }
- 
- #ifdef TLS_SUPPORT
- /*
-  * Negotiate Transport Layer Security
-  */
- 
- static int
- tls_negotiate(void)
- {
-     BIO *ssl_bio;
- 
-     if (! sslctx) {
-       const SSL_METHOD *method;
- 
-       SSL_library_init();
-       SSL_load_error_strings();
- 
-       method = TLSv1_client_method(); /* Not sure about this */
- 
-       /* Older ssl takes a non-const arg. */
-       sslctx = SSL_CTX_new((SSL_METHOD *) method);
- 
-       if (! sslctx) {
-           sm_end(NOTOK);
-           return sm_ierror("Unable to initialize OpenSSL context: %s",
-                            ERR_error_string(ERR_get_error(), NULL));
-       }
-     }
- 
-     ssl = SSL_new(sslctx);
- 
-     if (! ssl) {
-       sm_end(NOTOK);
-       return sm_ierror("Unable to create SSL connection: %s",
-                        ERR_error_string(ERR_get_error(), NULL));
-     }
- 
-     sbior = BIO_new_socket(fileno(sm_rfp), BIO_NOCLOSE);
-     sbiow = BIO_new_socket(fileno(sm_wfp), BIO_NOCLOSE);
- 
-     if (sbior == NULL || sbiow == NULL) {
-       sm_end(NOTOK);
-       return sm_ierror("Unable to create BIO endpoints: %s",
-                        ERR_error_string(ERR_get_error(), NULL));
-     }
- 
-     SSL_set_bio(ssl, sbior, sbiow);
-     SSL_set_connect_state(ssl);
- 
-     /*
-      * Set up a BIO to handle buffering for us
-      */
- 
-     io = BIO_new(BIO_f_buffer());
- 
-     if (! io) {
-       sm_end(NOTOK);
-       return sm_ierror("Unable to create a buffer BIO: %s",
-                        ERR_error_string(ERR_get_error(), NULL));
-     }
- 
-     ssl_bio = BIO_new(BIO_f_ssl());
- 
-     if (! ssl_bio) {
-       sm_end(NOTOK);
-       return sm_ierror("Unable to create a SSL BIO: %s",
-                        ERR_error_string(ERR_get_error(), NULL));
-     }
- 
-     BIO_set_ssl(ssl_bio, ssl, BIO_CLOSE);
-     BIO_push(io, ssl_bio);
- 
-     /*
-      * Try doing the handshake now
-      */
- 
-     if (BIO_do_handshake(io) < 1) {
-       sm_end(NOTOK);
-       return sm_ierror("Unable to negotiate SSL connection: %s",
-                        ERR_error_string(ERR_get_error(), NULL));
-     }
- 
-     if (sm_debug) {
-       const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
-       printf("SSL negotiation successful: %s(%d) %s\n",
-              SSL_CIPHER_get_name(cipher),
-              SSL_CIPHER_get_bits(cipher, NULL),
-              SSL_CIPHER_get_version(cipher));
- 
-     }
- 
-     tls_active = 1;
- 
-     return RP_OK;
- }
- #endif /* TLS_SUPPORT */
- 
- /*
-  * Convenience functions to replace occurrences of fputs() and fputc()
-  */
- 
- static int
- sm_fputs(char *buffer)
- {
-     return sm_fwrite(buffer, strlen(buffer));
- }
- 
- static int
- sm_fputc(int c)
- {
-     char h = c;
- 
-     return sm_fwrite(&h, 1);
- }
- 
- /*
-  * Flush out any pending data on the connection
-  */
- 
- static void
- sm_fflush(void)
- {
- #ifdef CYRUS_SASL
-     const char *output;
-     unsigned int outputlen;
-     int result;
- 
-     if (sasl_complete == 1 && sasl_ssf > 0 && sasl_outbuflen > 0) {
-       result = sasl_encode(conn, sasl_outbuffer, sasl_outbuflen,
-                            &output, &outputlen);
-       if (result != SASL_OK) {
-           sm_ierror("Unable to SASL encode connection data: %s",
-                     sasl_errdetail(conn));
-           return;
-       }
- 
-       if (fwrite(output, sizeof(*output), outputlen, sm_wfp) < outputlen) {
-           advise ("sm_fflush", "fwrite");
-       }
-       sasl_outbuflen = 0;
-     }
- #endif /* CYRUS_SASL */
- 
- #ifdef TLS_SUPPORT
-     if (tls_active) {
-       (void) BIO_flush(io);
-     }
- #endif /* TLS_SUPPORT */
- 
-     fflush(sm_wfp);
- }
- 
- static int
- sm_werror (void)
- {
-     sm_reply.length =
-       strlen (strcpy (sm_reply.text, sm_wfp == NULL ? "no socket opened"
-           : sm_alarmed ? "write to socket timed out"
-           : "error writing to socket"));
- 
-     return (sm_reply.code = NOTOK);
+     return OK;
  }
  
  
  static int
  smhear (void)
  {
-     int i, code, cont, bc = 0, rc, more;
+     int i, code, cont, more;
+     size_t buflen, rc;
      unsigned char *bp;
      char *rp;
-     char **ehlo = EHLOkeys, buffer[BUFSIZ];
+     char *errstr;
+     char **ehlo = EHLOkeys, *buffer;
  
      if (doingEHLO) {
        static int at_least_once = 0;
@@@ -1586,20 -782,8 +795,8 @@@ again: 
      rp = sm_reply.text;
      rc = sizeof(sm_reply.text) - 1;
  
-     for (more = FALSE; sm_rrecord ((char *) (bp = (unsigned char *) buffer),
-                                  &bc) != NOTOK ; ) {
-       if (sm_debug) {
-           char *decoded_buffer =
-                 prepare_for_display (buffer, &next_line_encoded);
- 
-           if (sasl_ssf > 0)
-               printf("(sasl-decrypted) ");
-           if (tls_active)
-               printf("(tls-decrypted) ");
-           printf ("<= %s\n", decoded_buffer);
-           free (decoded_buffer);
-           fflush (stdout);
-       }
+     for (more = FALSE; (buffer = netsec_readline(nsc, &buflen,
+                                                &errstr)) != NULL ; ) {
  
        if (doingEHLO
                && strncmp (buffer, "250", sizeof("250") - 1) == 0
                doingEHLO = 2;
        }
  
-       for (; bc > 0 && (!isascii (*bp) || !isdigit (*bp)); bp++, bc--)
+       bp = (unsigned char *) buffer;
+ 
+       for (; buflen > 0 && (!isascii (*bp) || !isdigit (*bp)); bp++, buflen--)
            continue;
  
        cont = FALSE;
        code = atoi ((char *) bp);
-       bp += 3, bc -= 3;
-       for (; bc > 0 && isspace (*bp); bp++, bc--)
+       bp += 3, buflen -= 3;
+       for (; buflen > 0 && isspace (*bp); bp++, buflen--)
            continue;
-       if (bc > 0 && *bp == '-') {
+       if (buflen > 0 && *bp == '-') {
            cont = TRUE;
-           bp++, bc--;
-           for (; bc > 0 && isspace (*bp); bp++, bc--)
+           bp++, buflen--;
+           for (; buflen > 0 && isspace (*bp); bp++, buflen--)
                continue;
        }
  
        } else {
            sm_reply.code = code;
            more = cont;
-           if (bc <= 0) {
-               /* can never fail to 0-terminate because of size of buffer vs fixed string */
-               strncpy (buffer, sm_noreply, sizeof(buffer));
-               bp = (unsigned char *) buffer;
-               bc = strlen (sm_noreply);
+           if (buflen <= 0) {
+               bp = (unsigned char *) sm_noreply;
+               buflen = strlen (sm_noreply);
            }
        }
  
-       if ((i = min (bc, rc)) > 0) {
+       if ((i = min (buflen, rc)) > 0) {
            memcpy (rp, bp, i);
            rp += i;
            rc -= i;
            i = strlen(sm_moreply);
-           if (more && rc > i + 1) {
+           if (more && (int) rc > i + 1) {
                memcpy (rp, sm_moreply, i); /* safe because of check in if() */
                rp += i;
                rc -= i;
        sm_reply.text[sm_reply.length] = 0;
        return sm_reply.code;
      }
-     return NOTOK;
- }
- 
- 
- static int
- sm_rrecord (char *buffer, int *len)
- {
-     int retval;
- 
-     if (sm_rfp == NULL)
-       return sm_rerror(0);
- 
-     buffer[*len = 0] = 0;
- 
-     if ((retval = sm_fgets (buffer, BUFSIZ, sm_rfp)) != RP_OK)
-       return sm_rerror (retval);
-     *len = strlen (buffer);
-     /* *len should be >0 except on EOF, but check for safety's sake */
-     if (*len == 0)
-       return sm_rerror (RP_EOF);
-     if (buffer[*len - 1] != '\n')
-       while ((retval = sm_fgetc (sm_rfp)) != '\n' && retval != EOF &&
-              retval != -2)
-           continue;
-     else
-       if ((*len > 1) && (buffer[*len - 2] == '\r'))
-           *len -= 1;
-     *len -= 1;
-     buffer[*len] = 0;
- 
-     return OK;
- }
- 
- /*
-  * Our version of fgets, which calls our private fgetc function
-  */
- 
- static int
- sm_fgets(char *buffer, int size, FILE *f)
- {
-     int c;
- 
-      do {
-       c = sm_fgetc(f);
- 
-       if (c == EOF)
-           return RP_EOF;
- 
-       if (c == -2)
-           return NOTOK;
  
-       *buffer++ = c;
-      } while (size > 1 && c != '\n');
- 
-      *buffer = '\0';
- 
-      return RP_OK;
- }
- 
- 
- #if defined(CYRUS_SASL) || defined(TLS_SUPPORT)
- /*
-  * Read from the network, but do SASL or TLS encryption
-  */
- 
- static int
- sm_fgetc(FILE *f)
- {
-     char tmpbuf[BUFSIZ], *retbuf;
-     unsigned int retbufsize = 0;
-     int cc, result;
- 
-     /*
-      * If we have leftover data, return it
-      */
- 
-     if (sasl_inbuflen) {
-       sasl_inbuflen--;
-       return (int) *sasl_inptr++;
-     }
- 
-     /*
-      * If not, read from the network until we have some data to return
-      */
- 
-     while (retbufsize == 0) {
- 
- #ifdef TLS_SUPPORT
-       if (tls_active) {
-           cc = SSL_read(ssl, tmpbuf, sizeof(tmpbuf));
- 
-           if (cc == 0) {
-               result = SSL_get_error(ssl, cc);
- 
-               if (result != SSL_ERROR_ZERO_RETURN) {
-                   sm_ierror("TLS peer aborted connection");
-               }
- 
-               return EOF;
-           }
- 
-           if (cc < 0) {
-               sm_ierror("SSL_read failed: %s",
-                         ERR_error_string(ERR_get_error(), NULL));
-               return -2;
-           }
-       } else
- #endif /* TLS_SUPPORT */
- 
-       cc = read(fileno(f), tmpbuf, sizeof(tmpbuf));
- 
-       if (cc == 0)
-           return EOF;
- 
-       if (cc < 0) {
-           sm_ierror("Unable to read from network: %s", strerror(errno));
-           return -2;
-       }
- 
-       /*
-        * Don't call sasl_decode unless sasl is complete and we have
-        * encryption working
-        */
- 
- #ifdef CYRUS_SASL
-       if (sasl_complete == 0 || sasl_ssf == 0) {
-           retbuf = tmpbuf;
-           retbufsize = cc;
-       } else {
-           result = sasl_decode(conn, tmpbuf, cc, (const char **) &retbuf,
-                                &retbufsize);
- 
-           if (result != SASL_OK) {
-               sm_ierror("Unable to decode SASL network data: %s",
-                         sasl_errdetail(conn));
-               return -2;
-           }
-       }
- #else /* ! CYRUS_SASL */
-       retbuf = tmpbuf;
-       retbufsize = cc;
- #endif /* CYRUS_SASL */
-     }
- 
-     if (retbufsize > SASL_MAXRECVBUF) {
-       sm_ierror("Received data (%d bytes) is larger than the buffer "
-                 "size (%d bytes)", retbufsize, SASL_MAXRECVBUF);
-       return -2;
-     }
- 
-     memcpy(sasl_inbuffer, retbuf, retbufsize);
-     sasl_inptr = sasl_inbuffer + 1;
-     sasl_inbuflen = retbufsize - 1;
- 
-     return (int) sasl_inbuffer[0];
- }
- #endif /* CYRUS_SASL || TLS_SUPPORT */
- 
- static int
- sm_rerror (int rc)
- {
-     if (sm_mts == MTS_SMTP)
-       sm_reply.length =
-           strlen (strcpy (sm_reply.text, sm_rfp == NULL ? "no socket opened"
-               : sm_alarmed ? "read from socket timed out"
-               : rc == RP_EOF ? "premature end-of-file on socket"
-               : "error reading from socket"));
-     else
-       sm_reply.length =
-           strlen (strcpy (sm_reply.text, sm_rfp == NULL ? "no pipe opened"
-               : sm_alarmed ? "read from pipe timed out"
-               : rc == RP_EOF ? "premature end-of-file on pipe"
-               : "error reading from pipe"));
- 
-     return (sm_reply.code = NOTOK);
- }
- 
- 
- static void
- alrmser (int i)
- {
-     NMH_UNUSED (i);
- 
- #ifndef       RELIABLE_SIGNALS
-     SIGNAL (SIGALRM, alrmser);
- #endif
- 
-     sm_alarmed++;
-     if (sm_debug) {
-       printf ("timed out...\n");
-       fflush (stdout);
-     }
+     sm_nerror(errstr);
+     return NOTOK;
  }
  
  
@@@ -1940,68 -935,143 +948,143 @@@ EHLOset (char *s
      return 0;
  }
  
- 
  /*
-  * Detects, using heuristics, if an SMTP server or client response string
-  * contains a base64-encoded portion.  If it does, decodes it and replaces
-  * any non-printable characters with a hex representation.  Caller is
-  * responsible for free'ing return value.  If the decode fails, a copy of
-  * the input string is returned.
+  * Our SASL callback; we are either given SASL tokens to generate network
+  * protocols messages for, or we decode incoming protocol messages and
+  * convert them to binary SASL tokens to pass up into the SASL library.
   */
- static
- char *
- prepare_for_display (const char *string, int *next_line_encoded) {
-     const char *start = NULL;
-     const char *decoded;
-     size_t decoded_len;
-     int prefix_len = -1;
- 
-     if (strncmp (string, "AUTH ", 5) == 0) {
-         /* AUTH line:  the mechanism isn't encoded.  If there's an initial
-            response, it must be base64 encoded.. */
-         char *mechanism = strchr (string + 5, ' ');
- 
-         if (mechanism != NULL) {
-             prefix_len = (int) (mechanism - string + 1);
-         } /* else no space following the mechanism, so no initial response */
-         *next_line_encoded = 0;
-     } else if (strncmp (string, "334 ", 4) == 0) {
-         /* 334 is the server's request for user or password. */
-         prefix_len = 4;
-         /* The next (client response) line must be base64 encoded. */
-         *next_line_encoded = 1;
-     } else if (*next_line_encoded) {
-         /* "next" line now refers to this line, which is a base64-encoded
-            client response. */
-         prefix_len = 0;
-         *next_line_encoded = 0;
-     } else {
-         *next_line_encoded = 0;
-     }
  
-     /* Don't attempt to decoded unencoded initial response ('=') or cancel
-        response ('*'). */
-     if (prefix_len > -1  &&
-         string[prefix_len] != '='  &&  string[prefix_len] != '*') {
-         start = string + prefix_len;
-     }
+ static int
+ sm_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata,
+                unsigned int indatalen, unsigned char **outdata,
+                unsigned int *outdatalen, char **errstr)
+ {
+     int rc, snoopoffset;
+     char *mech, *line;
+     size_t len;
  
-     if (start  &&  decodeBase64 (start, &decoded, &decoded_len, 1, NULL) == OK) {
-         char *hexified;
-         char *prefix = mh_xmalloc(prefix_len + 1);
-         char *display_string;
- 
-         /* prefix is the beginning portion, which isn't base64 encoded. */
-         snprintf (prefix, prefix_len + 1, "%*s", prefix_len, string);
-         hexify ((const unsigned char *) decoded, decoded_len, &hexified);
-         /* Wrap the decoded portion in "b64<>". */
-         display_string = concat (prefix, "b64<", hexified, ">", NULL);
-         free (hexified);
-         free (prefix);
-         free ((char *) decoded);
- 
-         return display_string;
-     } else {
-         return getcpy (string);
+     switch (mtype) {
+     case NETSEC_SASL_START:
+       /*
+        * Generate an AUTH message; if we were given an input token
+        * then generate a an AUTH message that includes the initial
+        * response.
+        */
+ 
+       mech = netsec_get_sasl_mechanism(nsc);
+ 
+       if (indatalen) {
+           char *b64data;
+ 
+           b64data = mh_xmalloc(BASE64SIZE(indatalen));
+           writeBase64raw(indata, indatalen, (unsigned char *) b64data);
+ 
+           netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder,
+                                     &snoopoffset);
+           snoopoffset = 6 + strlen(mech);
+           rc = netsec_printf(nsc, errstr, "AUTH %s %s\r\n", mech, b64data);
+           free(b64data);
+           netsec_set_snoop_callback(nsc, NULL, NULL);
+       } else {
+           rc = netsec_printf(nsc, errstr, "AUTH %s\r\n", mech);
+       }
+ 
+       if (rc != OK)
+           return NOTOK;
+ 
+       if (netsec_flush(nsc, errstr) != OK)
+           return NOTOK;
+ 
+       break;
+ 
+     case NETSEC_SASL_READ:
+       /*
+        * Read in a line that should contain a 334 response code, followed
+        * by base64 response data.
+        */
+ 
+       netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder, &snoopoffset);
+       snoopoffset = 4;
+       line = netsec_readline(nsc, &len, errstr);
+       netsec_set_snoop_callback(nsc, NULL, NULL);
+ 
+       if (line == NULL)
+           return NOTOK;
+ 
+       if (len < 4) {
+           netsec_err(errstr, "Invalid format for SASL response");
+           return NOTOK;
+       }
+ 
+       if (strncmp(line, "334 ", 4) != 0) {
+           netsec_err(errstr, "Improper SASL protocol response: %s", line);
+           return NOTOK;
+       }
+ 
+       if (len == 4) {
+           *outdata = NULL;
+           *outdatalen = 0;
+       } else {
+           rc = decodeBase64(line + 4, outdata, &len, 0, NULL);
+           if (rc != OK) {
+               netsec_err(errstr, "Unable to decode base64 response");
+               return NOTOK;
+           }
+           *outdatalen = len;
+       }
+       break;
+ 
+     case NETSEC_SASL_WRITE:
+       /*
+        * The output encoding is pretty simple, so this is easy.
+        */
+       if (indatalen == 0) {
+           rc = netsec_printf(nsc, errstr, "\r\n");
+       } else {
+           unsigned char *b64data;
+           b64data = mh_xmalloc(BASE64SIZE(indatalen));
+           writeBase64raw(indata, indatalen, b64data);
+           netsec_set_snoop_callback(nsc, netsec_b64_snoop_decoder, NULL);
+           rc = netsec_printf(nsc, errstr, "%s\r\n", b64data);
+           netsec_set_snoop_callback(nsc, NULL, NULL);
+           free(b64data);
+       }
+ 
+       if (rc != OK)
+           return NOTOK;
+ 
+       if (netsec_flush(nsc, errstr) != OK)
+           return NOTOK;
+       break;
+ 
+     case NETSEC_SASL_FINISH:
+       /*
+        * Finish the protocol; we're looking for a 235 message.
+        */
+       line = netsec_readline(nsc, &len, errstr);
+       if (line == NULL)
+           return NOTOK;
+ 
+       if (strncmp(line, "235 ", 4) != 0) {
+           if (len > 4)
+               netsec_err(errstr, "Authentication failed: %s", line + 4);
+           else
+               netsec_err(errstr, "Authentication failed: %s", line);
+           return NOTOK;
+       }
+       break;
+ 
+     case NETSEC_SASL_CANCEL:
+       /*
+        * Cancel the SASL exchange; this is done by sending a single "*".
+        */
+       rc = netsec_printf(nsc, errstr, "*\r\n");
+       if (rc == OK)
+           rc = netsec_flush(nsc, errstr);
+       if (rc != OK)
+           return NOTOK;
+       break;
      }
+ 
+     return OK;
  }
diff --combined mts/smtp/smtp.h
index 3063866e6396d3092d5a66c11fc06574a40cf44f,45e8c4c3213fa4c5d3c8ba11b177923560e373ac..fb842fe6f241dd1b8b52036d3808aae0f2eea1ef
@@@ -16,9 -16,9 +16,9 @@@ struct smtp 
   * prototypes
   */
  /* int client (); */
- int sm_init (char *, char *, char *, int, int, int, int, int, char *, char *,
-              const char *, int);
+ int sm_init (char *, char *, char *, int, int, int, int, const char *,
 -           const char *, const char *, int);
 -int sm_winit (char *);
++             const char *, const char *, int);
 +int sm_winit (char *, int);
  int sm_wadr (char *, char *, char *);
  int sm_waend (void);
  int sm_wtxt (char *, int);
diff --combined test/send/test-sendfrom
index 2d704f16e898c2e3a6ee4c3f877ffc65df119b00,8e55f7ab8fc17a749d9a026de174466a074055b8..0f2dda84ef3b29a8948844b9a3309c88fcdf6c02
@@@ -51,21 -51,14 +51,23 @@@ QUI
  EOF
  
  cat > "${testname}.post-expected" <<EOF
+ Trying to connect to "127.0.0.1" ...
+ Connecting to 127.0.0.1:${localport}...
  <= 220 Not really an ESMTP server
  => EHLO nosuchhost.example.com
 +<= 250-ready
 +<= 250-8BITMIME
 +<= 250-SMTPUTF8
  <= 250 I'll buy that for a dollar!
  => MAIL FROM:<nobody@example.com>
 +<= 250-ready
 +<= 250-8BITMIME
 +<= 250-SMTPUTF8
  <= 250 I'll buy that for a dollar!
  => RCPT TO:<somebody@example.com>
 +<= 250-ready
 +<= 250-8BITMIME
 +<= 250-SMTPUTF8
  <= 250 I'll buy that for a dollar!
  => DATA
  <= 354 Go ahead
@@@ -76,7 -69,7 +78,7 @@@
  EOF
  
  test_post "${testname}.actual" "${testname}.expected" \
-     > ${testname}.post 2>/dev/null
+     2> ${testname}.post
  
  check "${testname}.post-expected" "${testname}.post"
  
diff --combined uip/post.c
index 47941afcfe544bc78092e9ae1546874095fd7b65,0a42ad2ff429f3d57061a2937e2e79c53245ffa8..db9c02986cb078f223045c404af0588762e57fdf
@@@ -83,7 -83,6 +83,6 @@@
      X("partno", -6, PARTSW) \
      X("sasl", SASLminc(4), SASLSW) \
      X("nosasl", SASLminc(6), NOSASLSW) \
-     X("saslmaxssf", SASLminc(10), SASLMXSSFSW) \
      X("saslmech", SASLminc(5), SASLMECHSW) \
      X("user", SASLminc(-4), USERSW) \
      X("port server submission port name/number", 4, PORTSW) \
@@@ -231,7 -230,6 +230,6 @@@ static int checksw = 0;            /* whom -chec
  static int linepos=0;         /* putadr()'s position on the line       */
  static int nameoutput=0;      /* putadr() has output header name       */
  static int sasl=0;            /* Use SASL auth for SMTP                */
- static int saslssf=-1;                /* Our maximum SSF for SASL              */
  static char *saslmech=NULL;   /* Force use of particular SASL mech     */
  static char *user=NULL;               /* Authenticate as this user             */
  static char *port="submission";       /* Name of server port for SMTP submission */
@@@ -278,7 -276,7 +276,7 @@@ static char *partno = NULL
  /*
   * static prototypes
   */
 -static void putfmt (char *, char *, FILE *);
 +static void putfmt (char *, char *, int *, FILE *);
  static void start_headers (void);
  static void finish_headers (FILE *);
  static int get_header (char *, struct headers *);
@@@ -290,14 -288,14 +288,14 @@@ static void anno (void)
  static int annoaux (struct mailname *);
  static void insert_fcc (struct headers *, char *);
  static void make_bcc_file (int);
 -static void verify_all_addresses (int, char *, int, char *);
 +static void verify_all_addresses (int, int, char *, int, char *);
  static void chkadr (void);
  static void sigon (void);
  static void sigoff (void);
  static void p_refile (char *);
  static void fcc (char *, char *);
  static void die (char *, char *, ...);
 -static void post (char *, int, int, char *, int, char *);
 +static void post (char *, int, int, int, char *, int, char *);
  static void do_text (char *file, int fd);
  static void do_an_address (struct mailname *, int);
  static void do_addresses (int, int);
@@@ -308,7 -306,6 +306,7 @@@ in
  main (int argc, char **argv)
  {
      int state, compnum, dashstuff = 0, swnum, oauth_flag = 0;
 +    int eai = 0; /* use Email Address Internationalization (EAI) (SMTPUTF8) */
      char *cp, *msg = NULL, **argp, **arguments, *envelope;
      char buf[BUFSIZ], name[NAMESZ], *auth_svc = NULL;
      FILE *in, *out;
                    sasl = 0;
                    continue;
  
-               case SASLMXSSFSW:
-                   if (!(cp = *argp++) || *cp == '-')
-                       adios (NULL, "missing argument to %s", argp[-2]);
-                   saslssf = atoi(cp);
-                   continue;
-               
                case SASLMECHSW:
                    if (!(saslmech = *argp++) || *saslmech == '-')
                        adios (NULL, "missing argument to %s", argp[-2]);
        switch (state = m_getfld (&gstate, name, buf, &bufsz, in)) {
            case FLD: 
            case FLDPLUS: 
 -              compnum++;
 +                compnum++;
                cp = add (buf, NULL);
                while (state == FLDPLUS) {
                    bufsz = sizeof buf;
                    state = m_getfld (&gstate, name, buf, &bufsz, in);
                    cp = add (buf, cp);
                }
 -              putfmt (name, cp, out);
 +              putfmt (name, cp, &eai, out);
                free (cp);
                continue;
  
      /* If we are doing a "whom" check */
      if (whomsw) {
        /* This won't work with MTS_SENDMAIL_PIPE. */
 -        verify_all_addresses (1, envelope, oauth_flag, auth_svc);
 +        verify_all_addresses (1, eai, envelope, oauth_flag, auth_svc);
        done (0);
      }
  
                   verify_all_addresses with MTS_SENDMAIL_PIPE, but
                   that might require running sendmail as root.  Note
                   that spost didn't verify addresses. */
 -              verify_all_addresses (verbose, envelope, oauth_flag, auth_svc);
 +              verify_all_addresses (verbose, eai, envelope, oauth_flag,
 +                                      auth_svc);
            }
 -          post (tmpfil, 0, verbose, envelope, oauth_flag, auth_svc);
 +          post (tmpfil, 0, verbose, eai, envelope, oauth_flag, auth_svc);
        }
 -      post (bccfil, 1, verbose, envelope, oauth_flag, auth_svc);
 +      post (bccfil, 1, verbose, eai, envelope, oauth_flag, auth_svc);
        (void) m_unlink (bccfil);
      } else {
 -      post (tmpfil, 0, isatty (1), envelope, oauth_flag, auth_svc);
 +      post (tmpfil, 0, isatty (1), eai, envelope, oauth_flag, auth_svc);
      }
  
      p_refile (tmpfil);
   */
  
  static void
 -putfmt (char *name, char *str, FILE *out)
 +putfmt (char *name, char *str, int *eai, FILE *out)
  {
      int count, grp, i, keep;
      char *cp, *pp, *qp;
        return;
      }
  
 +    if (! *eai) {
 +        /* Check each header field value to see if it has any 8-bit characters.
 +           If it does, enable EAI support. */
 +        if (contains8bit(str, NULL)) {
 +            if (verbose) {
 +                printf ("EAI/SMTPUTF8 enabled\n");
 +            }
 +
 +            /* Enable SMTPUTF8. */
 +            *eai = 1;
 +
 +            /* Enable passing of utf-8 setting to getname()/getadrx(). */
 +            enable_eai();
 +        }
 +    }
 +
      if ((i = get_header (name, hdrtab)) == NOTOK) {
 -      if (strncasecmp (name, "nmh-", 4)) {
 +        if (strncasecmp (name, "nmh-", 4)) {
            fprintf (out, "%s: %s", name, str);
        } else {
            /* Filter out all Nmh-* headers, because Norm asked.  They
      }
  
      tmpaddrs.m_next = NULL;
 -    for (count = 0; (cp = getname (str)); count++)
 +
 +    for (count = 0; (cp = getname (str)); count++) {
        if ((mp = getm (cp, NULL, 0, error, sizeof(error)))) {
            if (tmpaddrs.m_next)
                np->m_next = mp;
            else
                badmsg++;
        }
 +    }
  
      if (count < 1) {
        if (hdr->flags & HNIL)
@@@ -1605,8 -1577,8 +1597,8 @@@ do_addresses (int bccque, int talk
   */
  
  static void
 -post (char *file, int bccque, int talk, char *envelope, int oauth_flag,
 -      char *auth_svc)
 +post (char *file, int bccque, int talk, int eai, char *envelope,
 +      int oauth_flag, char *auth_svc)
  {
      int fd;
      int       retval, i;
                break;
        }
      } else {
 -        if (rp_isbad (retval = sm_init (clientsw, serversw, port, watch,
 -                                        verbose, snoop, sasl, saslmech, user,
 +      if (rp_isbad (retval = sm_init (clientsw, serversw, port, watch,
-                                       verbose, snoop, sasl, saslssf,
-                                       saslmech, user,
++                                      verbose, snoop, sasl, saslmech, user,
                                        oauth_flag ? auth_svc : NULL, tls))
 -            || rp_isbad (retval = sm_winit (envelope)))
 +           || rp_isbad (retval = sm_winit (envelope, eai))) {
            die (NULL, "problem initializing server; %s", rp_string (retval));
 +      }
  
          do_addresses (bccque, talk && verbose);
          if ((fd = open (file, O_RDONLY)) == NOTOK)
  /* Address Verification */
  
  static void
 -verify_all_addresses (int talk, char *envelope, int oauth_flag, char *auth_svc)
 +verify_all_addresses (int talk, int eai, char *envelope, int oauth_flag,
 +                      char *auth_svc)
  {
      int retval;
      struct mailname *lp;
  
      sigon ();
  
 -    if (!whomsw || checksw)
 +    if (!whomsw || checksw) {
        if (rp_isbad (retval = sm_init (clientsw, serversw, port, watch,
-                                       verbose, snoop, sasl, saslssf,
-                                       saslmech, user,
+                                       verbose, snoop, sasl, saslmech, user,
                                        oauth_flag ? auth_svc : NULL, tls))
 -              || rp_isbad (retval = sm_winit (envelope)))
 +              || rp_isbad (retval = sm_winit (envelope, eai))) {
            die (NULL, "problem initializing server; %s", rp_string (retval));
 +      }
 +    }
  
      if (talk && !whomsw)
        printf (" -- Address Verification --\n");
diff --combined uip/send.c
index a4911989a234df52f45d5f6af71fc1dabe8715e6,4a6e0399cb2e9570918dc9d8f4a8964c80a44f07..1e48ea3a415e10d5846038c1b650f5efd8115205
@@@ -63,7 -63,6 +63,6 @@@
      X("snoop", 5, SNOOPSW) \
      X("sasl", SASLminc(4), SASLSW) \
      X("nosasl", SASLminc(6), NOSASLSW) \
-     X("saslmaxssf", SASLminc(6), SASLMXSSFSW) \
      X("saslmech mechanism", SASLminc(6), SASLMECHSW) \
      X("authservice", SASLminc(0), AUTHSERVICESW) \
      X("user username", SASLminc(-4), USERSW) \
@@@ -287,7 -286,6 +286,6 @@@ main (int argc, char **argv
                case WIDTHSW: 
                case CLIESW: 
                case SERVSW: 
-               case SASLMXSSFSW:
                case PORTSW:
                case MTSSM:
                case MTSSW:
@@@ -478,7 -476,7 +476,7 @@@ go_to_it
      closefds (3);
  
      for (msgnum = 0; msgnum < msgp; msgnum++) {
 -      switch (sendsbr (vec, vecp, program, msgs[msgnum], &st, 1, auth_svc)) {
 +        switch (sendsbr (vec, vecp, program, msgs[msgnum], &st, 1, auth_svc)) {
            case DONE: 
                done (++status);
            case NOTOK: