smtp.c
上传用户:ycwykj01
上传日期:2007-01-04
资源大小:1819k
文件大小:20k
源码类别:

网络编程

开发平台:

Unix_Linux

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