home *** CD-ROM | disk | FTP | other *** search
/ ftp.uv.es / 2014.11.ftp.uv.es.tar / ftp.uv.es / pub / unix / pine4.10.tar.gz / pine4.10.tar / pine4.10 / imap / src / c-client / smtp.c < prev    next >
C/C++ Source or Header  |  1998-12-01  |  21KB  |  609 lines

  1. /*
  2.  * Program:    Simple Mail Transfer Protocol (SMTP) routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    27 July 1988
  13.  * Last Edited:    1 December 1998
  14.  *
  15.  * Sponsorship:    The original version of this work was developed in the
  16.  *        Symbolic Systems Resources Group of the Knowledge Systems
  17.  *        Laboratory at Stanford University in 1987-88, and was funded
  18.  *        by the Biomedical Research Technology Program of the National
  19.  *        Institutes of Health under grant number RR-00785.
  20.  *
  21.  * Original version Copyright 1988 by The Leland Stanford Junior University
  22.  * Copyright 1998 by the University of Washington
  23.  *
  24.  *  Permission to use, copy, modify, and distribute this software and its
  25.  * documentation for any purpose and without fee is hereby granted, provided
  26.  * that the above copyright notices appear in all copies and that both the
  27.  * above copyright notices and this permission notice appear in supporting
  28.  * documentation, and that the name of the University of Washington or The
  29.  * Leland Stanford Junior University not be used in advertising or publicity
  30.  * pertaining to distribution of the software without specific, written prior
  31.  * permission.  This software is made available "as is", and
  32.  * THE UNIVERSITY OF WASHINGTON AND THE LELAND STANFORD JUNIOR UNIVERSITY
  33.  * DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO THIS SOFTWARE,
  34.  * INCLUDING WITHOUT LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
  35.  * FITNESS FOR A PARTICULAR PURPOSE, AND IN NO EVENT SHALL THE UNIVERSITY OF
  36.  * WASHINGTON OR THE LELAND STANFORD JUNIOR UNIVERSITY BE LIABLE FOR ANY
  37.  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
  38.  * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
  39.  * CONTRACT, TORT (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF
  40.  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  41.  *
  42.  */
  43.  
  44. #include <ctype.h>
  45. #include <stdio.h>
  46. #include "mail.h"
  47. #include "osdep.h"
  48. #include "smtp.h"
  49. #include "rfc822.h"
  50. #include "misc.h"
  51.  
  52.  
  53. /* Mailer parameters */
  54.  
  55. static unsigned long smtp_maxlogintrials = MAXLOGINTRIALS;
  56. static long smtp_port = 0;    /* default port override */
  57. static long smtp_altport = 0;
  58. static char *smtp_altname = NIL;
  59.  
  60.  
  61. /* SMTP limits, current as of most recent draft */
  62.  
  63. #define SMTPMAXLOCALPART 64
  64. #define SMTPMAXDOMAIN 255
  65. #define SMTPMAXPATH 256
  66.  
  67.  
  68. /* I have seen local parts of more than 64 octets, in spite of the SMTP
  69.  * limits.  So, we'll have a more generous limit that's still guaranteed
  70.  * not to pop the buffer, and let the server worry about it.  As of this
  71.  * writing, it comes out to 240.  Anyone with a mailbox name larger than
  72.  * that is in serious need of a life or at least a new ISP!  23 June 1998
  73.  */
  74.  
  75. #define MAXLOCALPART ((MAILTMPLEN - (SMTPMAXDOMAIN + SMTPMAXPATH + 32)) / 2)
  76.  
  77. /* Mail Transfer Protocol manipulate driver parameters
  78.  * Accepts: function code
  79.  *        function-dependent value
  80.  * Returns: function-dependent return value
  81.  */
  82.  
  83. void *smtp_parameters (long function,void *value)
  84. {
  85.   switch ((int) function) {
  86.   case SET_MAXLOGINTRIALS:
  87.     smtp_maxlogintrials = (unsigned long) value;
  88.     break;
  89.   case GET_MAXLOGINTRIALS:
  90.     value = (void *) smtp_maxlogintrials;
  91.     break;
  92.   case SET_SMTPPORT:
  93.     smtp_port = (long) value;
  94.     break;
  95.   case GET_SMTPPORT:
  96.     value = (void *) smtp_port;
  97.     break;
  98.   case SET_ALTSMTPPORT:
  99.     smtp_altport = (long) value;
  100.     break;
  101.   case GET_ALTSMTPPORT:
  102.     value = (void *) smtp_altport;
  103.     break;
  104.   case SET_ALTSMTPNAME:
  105.     smtp_altname = (char *) value;
  106.     break;
  107.   case GET_ALTSMTPNAME:
  108.     value = (void *) smtp_altname;
  109.     break;
  110.   default:
  111.     value = NIL;        /* error case */
  112.     break;
  113.   }
  114.   return value;
  115. }
  116.  
  117. /* Mail Transfer Protocol open connection
  118.  * Accepts: network driver
  119.  *        service host list
  120.  *        port number
  121.  *        service name
  122.  *        SMTP open options
  123.  * Returns: SEND stream on success, NIL on failure
  124.  */
  125.  
  126. SENDSTREAM *smtp_open_full (NETDRIVER *dv,char **hostlist,char *service,
  127.                 unsigned long port,long options)
  128. {
  129.   SENDSTREAM *stream = NIL;
  130.   long reply;
  131.   char *s,tmp[MAILTMPLEN];
  132.   NETSTREAM *netstream;
  133.   NETMBX mb;
  134.   if (!(hostlist && *hostlist)) mm_log ("Missing SMTP service host",ERROR);
  135.                 /* maximum domain name is 64 characters */
  136.   else do if (strlen (*hostlist) < SMTPMAXDOMAIN) {
  137.     sprintf (tmp,"{%.1000s/%.20s}",*hostlist,service ? service : "smtp");
  138.     if (!mail_valid_net_parse (tmp,&mb) || mb.anoflag) {
  139.       sprintf (tmp,"Invalid host specifier: %.80s",*hostlist);
  140.       mm_log (tmp,ERROR);
  141.     }
  142.     else {            /* did user supply port or service? */
  143.       if (mb.port || smtp_port)
  144.     sprintf (s = tmp,"%.1000s:%ld",mb.host,mb.port ? mb.port : smtp_port);
  145.       else s = mb.host;        /* simple host name */
  146.                 /* try to open ordinary connection */
  147.       if (netstream = mb.altflag ?
  148.       net_open ((NETDRIVER *) mail_parameters (NIL,GET_ALTDRIVER,NIL),s,
  149.             (char *) mail_parameters (NIL,GET_ALTSMTPNAME,NIL),
  150.             (unsigned long) mail_parameters(NIL,GET_ALTSMTPPORT,NIL)):
  151.       net_open (dv,s,mb.service,port)) {
  152.     stream = (SENDSTREAM *) memset (fs_get (sizeof (SENDSTREAM)),0,
  153.                     sizeof (SENDSTREAM));
  154.     stream->netstream = netstream;
  155.     if (options & SOP_DEBUG) stream->debug = T;
  156.     if (options &(SOP_DSN | SOP_DSN_NOTIFY_FAILURE | SOP_DSN_NOTIFY_DELAY |
  157.               SOP_DSN_NOTIFY_SUCCESS | SOP_DSN_RETURN_FULL)) {
  158.       ESMTP.dsn.want = T;
  159.       if (options & SOP_DSN_NOTIFY_FAILURE) ESMTP.dsn.notify.failure = T;
  160.       if (options & SOP_DSN_NOTIFY_DELAY) ESMTP.dsn.notify.delay = T;
  161.       if (options & SOP_DSN_NOTIFY_SUCCESS) ESMTP.dsn.notify.success = T;
  162.       if (options & SOP_DSN_RETURN_FULL) ESMTP.dsn.full = T;
  163.     }
  164.     if (options & SOP_8BITMIME) ESMTP.eightbit.want = T;
  165.                 /* get name of local host to use */
  166.     s = strcmp ("localhost",lcase (strcpy (tmp,mb.host))) ?
  167.       net_localhost (netstream) : "localhost";
  168.  
  169.     do reply = smtp_reply (stream);
  170.     while ((reply < 100) || (stream->reply[3] == '-'));
  171.     if (reply != SMTPGREET){/* get SMTP greeting */
  172.       sprintf (tmp,"SMTP greeting failure: %.80s",stream->reply);
  173.       mm_log (tmp,ERROR);
  174.       stream = smtp_close (stream);
  175.     }
  176.     else if ((reply = smtp_ehlo (stream,s,&mb)) == SMTPOK) {
  177.       ESMTP.ok = T;
  178.       if (mb.secflag || mb.user[0]) {
  179.         if (ESMTP.auth) {    /* have authenticators? */
  180.           if (!smtp_auth (stream,&mb,tmp)) stream = smtp_close(stream);
  181.         }
  182.         else {        /* no available authenticators */
  183.           sprintf (tmp,"%sSMTP authentication not available: %.80s",
  184.                mb.secflag ? "Secure " : "",mb.host);
  185.           mm_log (tmp,ERROR);
  186.           stream = smtp_close (stream);
  187.         }
  188.       }
  189.     }
  190.     else if (mb.secflag || mb.user[0]) {
  191.       sprintf (tmp,"ESMTP failure: %.80s",stream->reply);
  192.       mm_log (tmp,ERROR);
  193.       stream = smtp_close (stream);
  194.     }
  195.                 /* try ordinary SMTP then */
  196.     else if ((reply = smtp_send_work (stream,"HELO",s)) != SMTPOK) {
  197.       sprintf (tmp,"SMTP hello failure: %.80s",stream->reply);
  198.       mm_log (tmp,ERROR);
  199.       stream = smtp_close (stream);
  200.     }
  201.       }
  202.     }
  203.   } while (!stream && *++hostlist);
  204.   return stream;
  205. }
  206.  
  207. /* SMTP authenticate
  208.  * Accepts: stream to login
  209.  *        parsed network mailbox structure
  210.  *        scratch buffer
  211.  *        place to return user name
  212.  * Returns: T on success, NIL on failure
  213.  */
  214.  
  215. long smtp_auth (SENDSTREAM *stream,NETMBX *mb,char *tmp)
  216. {
  217.   unsigned long trial,auths;
  218.   char *lsterr = NIL;
  219.   char usr[MAILTMPLEN];
  220.   AUTHENTICATOR *at;
  221.   for (auths = ESMTP.auth; stream->netstream && auths &&
  222.        (at = mail_lookup_auth (find_rightmost_bit (&auths) + 1)); ) {
  223.     if (lsterr) {        /* previous authenticator failed? */
  224.       sprintf (tmp,"Retrying using %s authentication after %s",
  225.            at->name,lsterr);
  226.       mm_log (tmp,NIL);
  227.       fs_give ((void **) &lsterr);
  228.     }
  229.     for (trial = 1; stream->netstream && trial &&
  230.      (trial <= smtp_maxlogintrials); )
  231.       if (smtp_send_work (stream,"AUTH",at->name)) {
  232.     if ((*at->client) (smtp_challenge,smtp_response,mb,stream,&trial,usr)&&
  233.         (stream->replycode == SMTPAUTHED)) return LONGT;
  234.     lsterr = cpystr (stream->reply);
  235.       }
  236.   }
  237.   if (lsterr) {            /* previous authenticator failed? */
  238.     sprintf (tmp,"Can not authenticate to SMTP server: %s",lsterr);
  239.     mm_log (tmp,ERROR);
  240.     fs_give ((void **) &lsterr);
  241.   }
  242.   return NIL;            /* authentication failed */
  243. }
  244.  
  245. /* Get challenge to authenticator in binary
  246.  * Accepts: stream
  247.  *        pointer to returned size
  248.  * Returns: challenge or NIL if not challenge
  249.  */
  250.  
  251. void *smtp_challenge (void *s,unsigned long *len)
  252. {
  253.   SENDSTREAM *stream = (SENDSTREAM *) s;
  254.   return (stream->replycode == SMTPAUTHREADY) ?
  255.     rfc822_base64 ((unsigned char *) stream->reply+4,
  256.            strlen (stream->reply+4),len) : NIL;
  257. }
  258.  
  259.  
  260. /* Send authenticator response in BASE64
  261.  * Accepts: MAIL stream
  262.  *        string to send
  263.  *        length of string
  264.  * Returns: T, always
  265.  */
  266.  
  267. long smtp_response (void *s,char *response,unsigned long size)
  268. {
  269.   SENDSTREAM *stream = (SENDSTREAM *) s;
  270.   unsigned long i,j;
  271.   char *t,*u;
  272.   if (response) {        /* make CRLFless BASE64 string */
  273.     if (size) {
  274.       for (t = (char *) rfc822_binary ((void *) response,size,&i),u = t,j = 0;
  275.        j < i; j++) if (t[j] > ' ') *u++ = t[j];
  276.       *u = '\0';        /* tie off string */
  277.       i = smtp_send_work (stream,t,NIL);
  278.       fs_give ((void **) &t);
  279.     }
  280.     else i = smtp_send_work (stream,"",NIL);
  281.   }
  282.                 /* abort requested */
  283.   else i = smtp_send_work (stream,"*",NIL);
  284.   return LONGT;
  285. }
  286.  
  287. /* Mail Transfer Protocol close connection
  288.  * Accepts: SEND stream
  289.  * Returns: NIL always
  290.  */
  291.  
  292. SENDSTREAM *smtp_close (SENDSTREAM *stream)
  293. {
  294.   if (stream) {            /* send "QUIT" */
  295.     smtp_send_work (stream,"QUIT",NIL);
  296.                 /* close TCP connection */
  297.     net_close (stream->netstream);
  298.     if (stream->reply) fs_give ((void **) &stream->reply);
  299.     fs_give ((void **) &stream);/* flush the stream */
  300.   }
  301.   return NIL;
  302. }
  303.  
  304. /* Mail Transfer Protocol deliver mail
  305.  * Accepts: SEND stream
  306.  *        delivery option (MAIL, SEND, SAML, SOML)
  307.  *        message envelope
  308.  *        message body
  309.  * Returns: T on success, NIL on failure
  310.  */
  311.  
  312. long smtp_mail (SENDSTREAM *stream,char *type,ENVELOPE *env,BODY *body)
  313. {
  314.   /* Note: This assumes that the envelope will never generate a header of
  315.    * more than 8K.  If your client generates godzilla headers, you will
  316.    * need to install your own rfc822out_t routine via SET_RFC822OUTPUT
  317.    * to use in place of this.
  318.    */
  319.   char tmp[8*MAILTMPLEN];
  320.   long error = NIL;
  321.   if (!(env->to || env->cc || env->bcc)) {
  322.                   /* no recipients in request */
  323.     smtp_fake (stream,SMTPHARDERROR,"No recipients specified");
  324.     return NIL;
  325.   }
  326.   smtp_send (stream,"RSET",NIL);/* make sure stream is in good shape */
  327.   strcpy (tmp,"FROM:<");    /* compose "MAIL FROM:<return-path>" */
  328.   if (env->return_path && env->return_path->host &&
  329.       !((env->return_path->adl &&
  330.      (strlen (env->return_path->adl) > SMTPMAXPATH)) ||
  331.     (strlen (env->return_path->mailbox) > SMTPMAXLOCALPART) ||
  332.     (strlen (env->return_path->host) > SMTPMAXDOMAIN)))
  333.     rfc822_address (tmp,env->return_path);
  334.   strcat (tmp,">");
  335.   if (ESMTP.ok) {
  336.     if (ESMTP.eightbit.ok && ESMTP.eightbit.want) strcat(tmp," BODY=8BITMIME");
  337.     if (ESMTP.dsn.ok && ESMTP.dsn.want)
  338.       strcat (tmp,ESMTP.dsn.full ? " RET=FULL" : " RET=HDRS");
  339.   }
  340.                 /* send "MAIL FROM" command */
  341.   if (!(smtp_send (stream,type,tmp) == SMTPOK)) return NIL;
  342.                 /* negotiate the recipients */
  343.   if (env->to) smtp_rcpt (stream,env->to,&error);
  344.   if (env->cc) smtp_rcpt (stream,env->cc,&error);
  345.   if (env->bcc) smtp_rcpt (stream,env->bcc,&error);
  346.   if (error) {            /* any recipients failed? */
  347.                       /* reset the stream */
  348.     smtp_send (stream,"RSET",NIL);
  349.     smtp_fake (stream,SMTPHARDERROR,"One or more recipients failed");
  350.     return NIL;
  351.   }
  352.                 /* negotiate data command */
  353.   if (!(smtp_send (stream,"DATA",NIL) == SMTPREADY)) return NIL;
  354.                 /* set up error in case failure */
  355.   smtp_fake (stream,SMTPSOFTFATAL,"SMTP connection went away!");
  356.                 /* output data, return success status */
  357.   return rfc822_output (tmp,env,body,smtp_soutr,stream->netstream,
  358.             ESMTP.eightbit.ok && ESMTP.eightbit.want) &&
  359.               (smtp_send (stream,".",NIL) == SMTPOK);
  360. }
  361.  
  362. /* Internal routines */
  363.  
  364.  
  365. /* Simple Mail Transfer Protocol send recipient
  366.  * Accepts: SMTP stream
  367.  *        address list
  368.  *        pointer to error flag
  369.  */
  370.  
  371. void smtp_rcpt (SENDSTREAM *stream,ADDRESS *adr,long *error)
  372. {
  373.   char *s,tmp[MAILTMPLEN];
  374.   while (adr) {            /* for each address on the list */
  375.                 /* clear any former error */
  376.     if (adr->error) fs_give ((void **) &adr->error);
  377.     if (adr->host) {        /* ignore group syntax */
  378.                 /* enforce SMTP limits to protect the buffer */
  379.       if (adr->adl && (strlen (adr->adl) > SMTPMAXPATH)) {
  380.     adr->error = cpystr ("501 Path too long");
  381.     *error = T;
  382.       }
  383.       else if (strlen (adr->mailbox) > MAXLOCALPART) {
  384.     adr->error = cpystr ("501 Recipient name too long");
  385.     *error = T;
  386.       }
  387.       if ((strlen (adr->host) > SMTPMAXDOMAIN)) {
  388.     adr->error = cpystr ("501 Recipient domain too long");
  389.     *error = T;
  390.       }
  391.       else {
  392.     strcpy (tmp,"TO:<");    /* compose "RCPT TO:<return-path>" */
  393.     rfc822_address (tmp,adr);
  394.     strcat (tmp,">");
  395.                 /* want notifications */
  396.     if (ESMTP.ok && ESMTP.dsn.ok && ESMTP.dsn.want) {
  397.                 /* yes, start with prefix */
  398.       strcat (tmp," NOTIFY=");
  399.       s = tmp + strlen (tmp);
  400.       if (ESMTP.dsn.notify.failure) strcat (s,"FAILURE,");
  401.       if (ESMTP.dsn.notify.delay) strcat (s,"DELAY,");
  402.       if (ESMTP.dsn.notify.success) strcat (s,"SUCCESS,");
  403.                 /* tie off last comma */
  404.       if (*s) s[strlen (s) - 1] = '\0';
  405.       else strcat (tmp,"NEVER");
  406.     }
  407.                 /* send "RCPT TO" command */
  408.     if (!(smtp_send (stream,"RCPT",tmp) == SMTPOK)) {
  409.       *error = T;        /* note that an error occurred */
  410.       adr->error = cpystr (stream->reply);
  411.     }
  412.       }
  413.     }
  414.     adr = adr->next;        /* do any subsequent recipients */
  415.   }
  416. }
  417.  
  418. /* Simple Mail Transfer Protocol send command
  419.  * Accepts: SMTP stream
  420.  *        text
  421.  * Returns: reply code
  422.  */
  423.  
  424. long smtp_send (SENDSTREAM *stream,char *command,char *args)
  425. {
  426.   long ret;
  427.   do ret = smtp_send_work (stream,command,args);
  428.   while (ESMTP.auth && smtp_send_auth (stream,ret));
  429.   return ret;
  430. }
  431.  
  432.  
  433. /* SMTP send command worker routine
  434.  * Accepts: SEND stream
  435.  *        text
  436.  * Returns: reply code
  437.  */
  438.  
  439. long smtp_send_work (SENDSTREAM *stream,char *command,char *args)
  440. {
  441.   char tmp[MAILTMPLEN+64];
  442.                 /* build the complete command */
  443.   if (args) sprintf (tmp,"%s %s",command,args);
  444.   else strcpy (tmp,command);
  445.   if (stream->debug) mm_dlog (tmp);
  446.   strcat (tmp,"\015\012");
  447.                 /* send the command */
  448.   if (!net_soutr (stream->netstream,tmp))
  449.     return smtp_fake (stream,SMTPSOFTFATAL,"SMTP connection broken (command)");
  450.   do stream->replycode = smtp_reply (stream);
  451.   while ((stream->replycode < 100) || (stream->reply[3] == '-'));
  452.   return stream->replycode;
  453. }
  454.  
  455.  
  456. /* SMTP send authentication if needed
  457.  * Accepts: SEND stream
  458.  *        code from previous command
  459.  * Returns: T if need to redo command, NIL otherwise
  460.  */
  461.  
  462. long smtp_send_auth (SENDSTREAM *stream,long code)
  463. {
  464.   NETMBX mb;
  465.   char tmp[MAILTMPLEN];
  466.   switch (code) {
  467.   case SMTPWANTAUTH: case SMTPWANTAUTH2:
  468.     sprintf (tmp,"{%s/smtp}<none>",net_host (stream->netstream));
  469.     mail_valid_net_parse (tmp,&mb);
  470.     return smtp_auth (stream,&mb,tmp);
  471.   }
  472.   return NIL;            /* no auth needed */
  473. }
  474.  
  475. /* Simple Mail Transfer Protocol get reply
  476.  * Accepts: SMTP stream
  477.  * Returns: reply code
  478.  */
  479.  
  480. long smtp_reply (SENDSTREAM *stream)
  481. {
  482.   smtpverbose_t pv = (smtpverbose_t) mail_parameters (NIL,GET_SMTPVERBOSE,NIL);
  483.   long reply;
  484.                 /* flush old reply */
  485.   if (stream->reply) fs_give ((void **) &stream->reply);
  486.                   /* get reply */
  487.   if (!(stream->reply = net_getline (stream->netstream)))
  488.     return smtp_fake (stream,SMTPSOFTFATAL,"SMTP connection went away!");
  489.   if (stream->debug) mm_dlog (stream->reply);
  490.   reply = atol (stream->reply);    /* return response code */
  491.   if (pv && (reply < 100)) (*pv) (stream->reply);
  492.   return reply;
  493. }
  494.  
  495. /* Simple Mail Transfer Protocol send EHLO
  496.  * Accepts: SMTP stream
  497.  *        host name to use in EHLO
  498.  *        NETMBX structure
  499.  * Returns: reply code
  500.  */
  501.  
  502. long smtp_ehlo (SENDSTREAM *stream,char *host,NETMBX *mb)
  503. {
  504.   unsigned long i;
  505.   unsigned int j;
  506.   char *s,tmp[MAILTMPLEN];
  507.   sprintf (tmp,"EHLO %s",host);    /* build the complete command */
  508.   if (stream->debug) mm_dlog (tmp);
  509.   strcat (tmp,"\015\012");
  510.                 /* send the command */
  511.   if (!net_soutr (stream->netstream,tmp))
  512.     return smtp_fake (stream,SMTPSOFTFATAL,"SMTP connection broken (EHLO)");
  513.                 /* got an OK reply? */
  514.   do if ((i = smtp_reply (stream)) == SMTPOK) {
  515.     ucase (strncpy (tmp,stream->reply+4,MAILTMPLEN-1));
  516.     tmp[MAILTMPLEN-1] = '\0';
  517.                 /* note EHLO options */
  518.     if ((tmp[0] == '8') && (tmp[1] == 'B') && (tmp[2] == 'I') &&
  519.     (tmp[3] == 'T') && (tmp[4] == 'M') && (tmp[5] == 'I') &&
  520.     (tmp[6] == 'M') && (tmp[7] == 'E') && !tmp[8]) ESMTP.eightbit.ok = T;
  521.     else if ((tmp[0] == 'S') && (tmp[1] == 'I') && (tmp[2] == 'Z') &&
  522.          (tmp[3] == 'E') && (!tmp[4] || tmp[4] == ' ')) {
  523.       if (tmp[4]) ESMTP.size.limit = atoi (tmp+5);
  524.       ESMTP.size.ok = T;
  525.     }
  526.     else if ((tmp[0] == 'A') && (tmp[1] == 'U') && (tmp[2] == 'T') &&
  527.          (tmp[3] == 'H') && ((tmp[4] == ' ') || (tmp[4] == '='))) {
  528.       for (s = strtok (tmp+5," "); s && *s; s = strtok (NIL," "))
  529.     if ((j = mail_lookup_auth_name (s,mb->secflag)) &&
  530.         (--j < (8 * sizeof (ESMTP.auth)))) ESMTP.auth |= (1 << j);
  531.     }
  532.     else if ((tmp[0] == 'D') && (tmp[1] == 'S') && (tmp[2] == 'N') && !tmp[3])
  533.       ESMTP.dsn.ok = T;
  534.     else if ((tmp[0] == 'S') && (tmp[1] == 'E') && (tmp[2] == 'N') &&
  535.          (tmp[3] == 'D') && !tmp[4]) ESMTP.service.send = T;
  536.     else if ((tmp[0] == 'S') && (tmp[1] == 'O') && (tmp[2] == 'M') &&
  537.          (tmp[3] == 'L') && !tmp[4]) ESMTP.service.soml = T;
  538.     else if ((tmp[0] == 'S') && (tmp[1] == 'A') && (tmp[2] == 'M') &&
  539.          (tmp[3] == 'L') && !tmp[4]) ESMTP.service.saml = T;
  540.     else if ((tmp[0] == 'E') && (tmp[1] == 'X') && (tmp[2] == 'P') &&
  541.          (tmp[3] == 'N') && !tmp[4]) ESMTP.service.expn = T;
  542.     else if ((tmp[0] == 'H') && (tmp[1] == 'E') && (tmp[2] == 'L') &&
  543.          (tmp[3] == 'P') && !tmp[4]) ESMTP.service.help = T;
  544.     else if ((tmp[0] == 'T') && (tmp[1] == 'U') && (tmp[2] == 'R') &&
  545.          (tmp[3] == 'N') && !tmp[4]) ESMTP.service.turn = T;
  546.     else if ((tmp[0] == 'E') && (tmp[1] == 'T') && (tmp[2] == 'R') &&
  547.          (tmp[3] == 'N') && !tmp[4]) ESMTP.service.etrn = T;
  548.  
  549.     else if ((tmp[0] == 'R') && (tmp[1] == 'E') && (tmp[2] == 'L') &&
  550.          (tmp[3] == 'A') && (tmp[4] == 'Y') && !tmp[5])
  551.       ESMTP.service.relay = T;
  552.     else if ((tmp[0] == 'P') && (tmp[1] == 'I') && (tmp[2] == 'P') &&
  553.          (tmp[3] == 'E') && (tmp[4] == 'L') && (tmp[5] == 'I') &&
  554.          (tmp[6] == 'N') && (tmp[7] == 'I') && (tmp[8] == 'N') &&
  555.          (tmp[9] == 'G') && !tmp[10]) ESMTP.service.pipe = T;
  556.     else if ((tmp[0] == 'E') && (tmp[1] == 'N') && (tmp[2] == 'H') &&
  557.          (tmp[3] == 'A') && (tmp[4] == 'N') && (tmp[5] == 'C') &&
  558.          (tmp[6] == 'E') && (tmp[7] == 'D') && (tmp[8] == 'S') &&
  559.          (tmp[9] == 'T') && (tmp[10] == 'A') && (tmp[11] == 'T') &&
  560.          (tmp[12] == 'U') && (tmp[13] == 'S') && (tmp[14] == 'C') &&
  561.          (tmp[15] == 'O') && (tmp[16] == 'D') && (tmp[17] == 'E') &&
  562.          (tmp[18] == 'S') && !tmp[19]) ESMTP.service.ensc = T;
  563.   }
  564.   while ((i < 100) || (stream->reply[3] == '-'));
  565.   return i;            /* return the response code */
  566. }
  567.  
  568. /* Simple Mail Transfer Protocol set fake error
  569.  * Accepts: SMTP stream
  570.  *        SMTP error code
  571.  *        error text
  572.  * Returns: error code
  573.  */
  574.  
  575. long smtp_fake (SENDSTREAM *stream,long code,char *text)
  576. {
  577.                 /* flush any old reply */
  578.   if (stream->reply ) fs_give ((void **) &stream->reply);
  579.                   /* set up pseudo-reply string */
  580.   stream->reply = (char *) fs_get (20+strlen (text));
  581.   sprintf (stream->reply,"%ld %s",code,text);
  582.   return code;            /* return error code */
  583. }
  584.  
  585.  
  586. /* Simple Mail Transfer Protocol filter mail
  587.  * Accepts: stream
  588.  *        string
  589.  * Returns: T on success, NIL on failure
  590.  */
  591.  
  592. long smtp_soutr (void *stream,char *s)
  593. {
  594.   char c,*t;
  595.                 /* "." on first line */
  596.   if (s[0] == '.') net_soutr (stream,".");
  597.                 /* find lines beginning with a "." */
  598.   while (t = strstr (s,"\015\012.")) {
  599.     c = *(t += 3);        /* remember next character after "." */
  600.     *t = '\0';            /* tie off string */
  601.                 /* output prefix */
  602.     if (!net_soutr (stream,s)) return NIL;
  603.     *t = c;            /* restore delimiter */
  604.     s = t - 1;            /* push pointer up to the "." */
  605.   }
  606.                 /* output remainder of text */
  607.   return *s ? net_soutr (stream,s) : T;
  608. }
  609.