* docs/MAIL.FILTERING: added note on removing procmail -f or
[mmh] / mts / smtp / smtp.c
1 /*
2  * smtp.c -- nmh SMTP interface
3  *
4  * $Id$
5  *
6  * This code is Copyright (c) 2002, by the authors of nmh.  See the
7  * COPYRIGHT file in the root directory of the nmh distribution for
8  * complete copyright information.
9  */
10
11 #include <h/mh.h>
12 #include "smtp.h"
13 #include <h/mts.h>
14 #include <signal.h>
15 #include <h/signals.h>
16 #ifdef MPOP
17 #include <errno.h>
18 #endif
19
20 #ifdef CYRUS_SASL
21 #include <sasl/sasl.h>
22 #include <sasl/saslutil.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 #include <netdb.h>
27 #include <errno.h>
28 #endif /* CYRUS_SASL */
29
30 /*
31  * This module implements an interface to SendMail very similar
32  * to the MMDF mm_(3) routines.  The sm_() routines herein talk
33  * SMTP to a sendmail process, mapping SMTP reply codes into
34  * RP_-style codes.
35  */
36
37 /*
38  * On older 4.2BSD machines without the POSIX function `sigaction',
39  * the alarm handing stuff for time-outs will NOT work due to the way
40  * syscalls get restarted.  This is not really crucial, since SendMail
41  * is generally well-behaved in this area.
42  */
43
44 #ifdef SENDMAILBUG
45 /*
46  * It appears that some versions of Sendmail will return Code 451
47  * when they don't really want to indicate a failure.
48  * "Code 451 almost always means sendmail has deferred; we don't
49  * really want bomb out at this point since sendmail will rectify
50  * things later."  So, if you define SENDMAILBUG, Code 451 is
51  * considered the same as Code 250.  Yuck!
52  */
53 #endif
54
55 #define TRUE    1
56 #define FALSE   0
57
58 #define NBITS ((sizeof (int)) * 8)
59
60 /*
61  * these codes must all be different!
62  */
63 #define SM_OPEN  300      /* Changed to 5 minutes to comply with a SHOULD in RFC 1123 */
64 #define SM_HELO  20
65 #define SM_RSET  15
66 #define SM_MAIL  301      /* changed to 5 minutes and a second (for uniqueness), see above */
67 #define SM_RCPT  302      /* see above */
68 #define SM_DATA  120      /* see above */
69 #define SM_TEXT 180     /* see above */
70 #define SM_DOT  600     /* see above */
71 #define SM_QUIT  30
72 #define SM_CLOS  10
73 #define SM_AUTH  45
74
75 static int sm_addrs = 0;
76 static int sm_alarmed = 0;
77 static int sm_child = NOTOK;
78 static int sm_debug = 0;
79 static int sm_nl = TRUE;
80 static int sm_verbose = 0;
81
82 static FILE *sm_rfp = NULL;
83 static FILE *sm_wfp = NULL;
84
85 #ifdef MPOP
86 static int sm_ispool = 0;
87 static char sm_tmpfil[BUFSIZ];
88 #endif /* MPOP */
89
90 #ifdef CYRUS_SASL
91 /*
92  * Some globals needed by SASL
93  */
94
95 static sasl_conn_t *conn = NULL;        /* SASL connection state */
96 static int sasl_complete = 0;           /* Has authentication succeded? */
97 static sasl_ssf_t sasl_ssf;             /* Our security strength factor */
98 static char *sasl_pw_context[2];        /* Context to pass into sm_get_pass */
99 static int maxoutbuf;                   /* Maximum crypto output buffer */
100 static int sm_get_user(void *, int, const char **, unsigned *);
101 static int sm_get_pass(sasl_conn_t *, void *, int, sasl_secret_t **);
102
103 static sasl_callback_t callbacks[] = {
104     { SASL_CB_USER, sm_get_user, NULL },
105 #define SM_SASL_N_CB_USER 0
106     { SASL_CB_PASS, sm_get_pass, NULL },
107 #define SM_SASL_N_CB_PASS 1
108     { SASL_CB_AUTHNAME, sm_get_user, NULL },
109 #define SM_SASL_N_CB_AUTHNAME 2
110     { SASL_CB_LIST_END, NULL, NULL },
111 };
112 #endif /* CYRUS_SASL */
113
114 static char *sm_noreply = "No reply text given";
115 static char *sm_moreply = "; ";
116
117 struct smtp sm_reply;           /* global... */
118
119 #define MAXEHLO 20
120
121 static int doingEHLO;
122 char *EHLOkeys[MAXEHLO + 1];
123
124 /*
125  * static prototypes
126  */
127 static int smtp_init (char *, char *, int, int, int, int, int, int,
128                       char *, char *);
129 static int sendmail_init (char *, char *, int, int, int, int, int);
130
131 static int rclient (char *, char *, char *);
132 static int sm_ierror (char *fmt, ...);
133 static int smtalk (int time, char *fmt, ...);
134 static int sm_wrecord (char *, int);
135 static int sm_wstream (char *, int);
136 static int sm_werror (void);
137 static int smhear (void);
138 static int sm_rrecord (char *, int *);
139 static int sm_rerror (void);
140 static RETSIGTYPE alrmser (int);
141 static char *EHLOset (char *);
142
143 #ifdef MPOP
144 static int sm_perror (char *fmt, ...);
145 /*
146  * smtp.c's own static copy of several nmh library subroutines
147  */
148 static char **smail_brkstring (char *, char *, char *);
149 static int smail_brkany (char, char *);
150 char **smail_copyip (char **, char **, int);
151 #endif
152
153 #ifdef CYRUS_SASL
154 /*
155  * Function prototypes needed for SASL
156  */
157
158 static int sm_auth_sasl(char *, char *, char *);
159 #endif /* CYRUS_SASL */
160
161 /* from mts/generic/client.c */
162 int client (char *, char *, char *, int, char *, int);
163
164 int
165 sm_init (char *client, char *server, int watch, int verbose,
166          int debug, int onex, int queued, int sasl, char *saslmech,
167          char *user)
168 {
169     if (sm_mts == MTS_SMTP)
170         return smtp_init (client, server, watch, verbose,
171                           debug, onex, queued, sasl, saslmech, user);
172     else
173         return sendmail_init (client, server, watch, verbose,
174                               debug, onex, queued);
175 }
176
177 static int
178 smtp_init (char *client, char *server, int watch, int verbose,
179            int debug, int onex, int queued, int sasl, char *saslmech,
180            char *user)
181 {
182 #ifdef CYRUS_SASL
183     char *server_mechs;
184 #endif /* CYRUS_SASL */
185     int result, sd1, sd2;
186
187     if (watch)
188         verbose = TRUE;
189
190     sm_verbose = verbose;
191     sm_debug = debug;
192
193 #ifdef MPOP
194     if (sm_ispool)
195         goto all_done;
196 #endif
197
198     if (sm_rfp != NULL && sm_wfp != NULL)
199         goto send_options;
200
201     if (client == NULL || *client == '\0') {
202         if (clientname) {
203             client = clientname;
204         } else {
205             client = LocalName();       /* no clientname -> LocalName */
206         }
207     }
208
209 #ifdef ZMAILER
210     if (client == NULL || *client == '\0')
211         client = "localhost";
212 #endif
213
214     if ((sd1 = rclient (server, "tcp", "smtp")) == NOTOK)
215         return RP_BHST;
216
217 #ifdef MPOP
218     if (sm_ispool) {
219         if (sm_rfp) {
220             alarm (SM_CLOS);
221             fclose (sm_rfp);
222             alarm (0);
223             sm_rfp = NULL;
224         }
225         if ((sm_wfp = fdopen (sd1, "w")) == NULL) {
226             unlink (sm_tmpfil);
227             close (sd1);
228             return sm_ierror ("unable to fdopen");
229         }
230 all_done: ;
231         sm_reply.text[sm_reply.length = 0] = NULL;
232         return (sm_reply.code = RP_OK);
233     }
234 #endif /* MPOP */
235
236     if ((sd2 = dup (sd1)) == NOTOK) {
237         close (sd1);
238         return sm_ierror ("unable to dup");
239     }
240
241     SIGNAL (SIGALRM, alrmser);
242     SIGNAL (SIGPIPE, SIG_IGN);
243
244     if ((sm_rfp = fdopen (sd1, "r")) == NULL
245             || (sm_wfp = fdopen (sd2, "w")) == NULL) {
246         close (sd1);
247         close (sd2);
248         sm_rfp = sm_wfp = NULL;
249         return sm_ierror ("unable to fdopen");
250     }
251
252     sm_alarmed = 0;
253     alarm (SM_OPEN);
254     result = smhear ();
255     alarm (0);
256
257     switch (result) {
258         case 220: 
259             break;
260
261         default: 
262             sm_end (NOTOK);
263             return RP_RPLY;
264     }
265
266     /*
267      * Give EHLO or HELO command
268      */
269     if (client && *client) {
270         doingEHLO = 1;
271         result = smtalk (SM_HELO, "EHLO %s", client);
272         doingEHLO = 0;
273
274         if (result >= 500 && result <= 599)
275             result = smtalk (SM_HELO, "HELO %s", client);
276
277         if (result != 250) {
278             sm_end (NOTOK);
279             return RP_RPLY;
280         }
281     }
282
283 #ifdef CYRUS_SASL
284     /*
285      * If the user asked for SASL, then check to see if the SMTP server
286      * supports it.  Otherwise, error out (because the SMTP server
287      * might have been spoofed; we don't want to just silently not
288      * do authentication
289      */
290
291     if (sasl) {
292         if (! (server_mechs = EHLOset("AUTH"))) {
293             sm_end(NOTOK);
294             return sm_ierror("SMTP server does not support SASL");
295         }
296
297         if (saslmech && stringdex(saslmech, server_mechs) == -1) {
298             sm_end(NOTOK);
299             return sm_ierror("Requested SASL mech \"%s\" is not in the "
300                              "list of supported mechanisms:\n%s",
301                              saslmech, server_mechs);
302         }
303
304         if (sm_auth_sasl(user, saslmech ? saslmech : server_mechs,
305                          server) != RP_OK) {
306             sm_end(NOTOK);
307             return NOTOK;
308         }
309     }
310 #endif /* CYRUS_SASL */
311
312 send_options: ;
313     if (watch && EHLOset ("XVRB"))
314         smtalk (SM_HELO, "VERB on");
315     if (onex && EHLOset ("XONE"))
316         smtalk (SM_HELO, "ONEX");
317     if (queued && EHLOset ("XQUE"))
318         smtalk (SM_HELO, "QUED");
319
320     return RP_OK;
321 }
322
323 int
324 sendmail_init (char *client, char *server, int watch, int verbose,
325                int debug, int onex, int queued)
326 {
327     int i, result, vecp;
328     int pdi[2], pdo[2];
329     char *vec[15];
330
331     if (watch)
332         verbose = TRUE;
333
334     sm_verbose = verbose;
335     sm_debug = debug;
336     if (sm_rfp != NULL && sm_wfp != NULL)
337         return RP_OK;
338
339     if (client == NULL || *client == '\0') {
340         if (clientname)
341             client = clientname;
342         else
343             client = LocalName();       /* no clientname -> LocalName */
344     }
345
346 #ifdef ZMAILER
347     if (client == NULL || *client == '\0')
348         client = "localhost";
349 #endif
350
351     if (pipe (pdi) == NOTOK)
352         return sm_ierror ("no pipes");
353     if (pipe (pdo) == NOTOK) {
354         close (pdi[0]);
355         close (pdi[1]);
356         return sm_ierror ("no pipes");
357     }
358
359     for (i = 0; (sm_child = fork ()) == NOTOK && i < 5; i++)
360         sleep (5);
361
362     switch (sm_child) {
363         case NOTOK: 
364             close (pdo[0]);
365             close (pdo[1]);
366             close (pdi[0]);
367             close (pdi[1]);
368             return sm_ierror ("unable to fork");
369
370         case OK: 
371             if (pdo[0] != fileno (stdin))
372                 dup2 (pdo[0], fileno (stdin));
373             if (pdi[1] != fileno (stdout))
374                 dup2 (pdi[1], fileno (stdout));
375             if (pdi[1] != fileno (stderr))
376                 dup2 (pdi[1], fileno (stderr));
377             for (i = fileno (stderr) + 1; i < NBITS; i++)
378                 close (i);
379
380             vecp = 0;
381             vec[vecp++] = r1bindex (sendmail, '/');
382             vec[vecp++] = "-bs";
383 #ifndef ZMAILER
384             vec[vecp++] = watch ? "-odi" : queued ? "-odq" : "-odb";
385             vec[vecp++] = "-oem";
386             vec[vecp++] = "-om";
387 # ifndef RAND
388             if (verbose)
389                 vec[vecp++] = "-ov";
390 # endif /* not RAND */
391 #endif /* not ZMAILER */
392             vec[vecp++] = NULL;
393
394             setgid (getegid ());
395             setuid (geteuid ());
396             execvp (sendmail, vec);
397             fprintf (stderr, "unable to exec ");
398             perror (sendmail);
399             _exit (-1);         /* NOTREACHED */
400
401         default: 
402             SIGNAL (SIGALRM, alrmser);
403             SIGNAL (SIGPIPE, SIG_IGN);
404
405             close (pdi[1]);
406             close (pdo[0]);
407             if ((sm_rfp = fdopen (pdi[0], "r")) == NULL
408                     || (sm_wfp = fdopen (pdo[1], "w")) == NULL) {
409                 close (pdi[0]);
410                 close (pdo[1]);
411                 sm_rfp = sm_wfp = NULL;
412                 return sm_ierror ("unable to fdopen");
413             }
414             sm_alarmed = 0;
415             alarm (SM_OPEN);
416             result = smhear ();
417             alarm (0);
418             switch (result) {
419                 case 220: 
420                     break;
421
422                 default: 
423                     sm_end (NOTOK);
424                     return RP_RPLY;
425             }
426
427             if (client && *client) {
428                 doingEHLO = 1;
429                 result = smtalk (SM_HELO, "EHLO %s", client);
430                 doingEHLO = 0;
431
432                 if (500 <= result && result <= 599)
433                     result = smtalk (SM_HELO, "HELO %s", client);
434
435                 switch (result) {
436                     case 250:
437                         break;
438
439                     default:
440                         sm_end (NOTOK);
441                         return RP_RPLY;
442                 }
443             }
444
445 #ifndef ZMAILER
446             if (onex)
447                 smtalk (SM_HELO, "ONEX");
448 #endif
449             if (watch)
450                 smtalk (SM_HELO, "VERB on");
451
452             return RP_OK;
453     }
454 }
455
456 #ifdef MPOP
457 # define MAXARGS  1000
458 #endif /* MPOP */
459
460 static int
461 rclient (char *server, char *protocol, char *service)
462 {
463     int sd;
464     char response[BUFSIZ];
465 #ifdef MPOP
466     char *cp;
467 #endif /* MPOP */
468
469     if ((sd = client (server, protocol, service, FALSE, response, sizeof(response))) != NOTOK)
470         return sd;
471
472 #ifdef MPOP
473     if (!server && servers && (cp = strchr(servers, '/'))) {
474         char **ap;
475         char *arguments[MAXARGS];
476
477         smail_copyip (smail_brkstring (cp = getcpy (servers), " ", "\n"), arguments, MAXARGS);
478
479         for (ap = arguments; *ap; ap++)
480             if (**ap == '/') {
481                 char *dp;
482
483                 if ((dp = strrchr(*ap, '/')) && *++dp == NULL)
484                     *--dp = NULL;
485                 snprintf (sm_tmpfil, sizeof(sm_tmpfil), "%s/smtpXXXXXX", *ap);
486 #ifdef HAVE_MKSTEMP
487                 sd = mkstemp (sm_tmpfil);
488 #else
489                 mktemp (sm_tmpfil);
490
491                 if ((sd = creat (sm_tmpfil, 0600)) != NOTOK) {
492                     sm_ispool = 1;
493                     break;
494                 }
495 #endif
496             }
497
498         free (cp);
499         if (sd != NOTOK)
500             return sd;
501     }
502 #endif /* MPOP */
503
504     sm_ierror ("%s", response);
505     return NOTOK;
506 }
507
508 int
509 sm_winit (int mode, char *from)
510 {
511     char *smtpcom;
512
513 #ifdef MPOP
514     if (sm_ispool && !sm_wfp) {
515         sm_reply.length = strlen (strcpy (sm_reply.text, "unable to create new spool file"));
516         sm_reply.code = NOTOK;
517         return RP_BHST;
518     }
519 #endif /* MPOP */
520
521     switch (mode) {
522         case S_MAIL:
523             smtpcom = "MAIL";
524             break;
525
526         case S_SEND:
527             smtpcom = "SEND";
528             break;
529
530         case S_SOML:
531             smtpcom = "SOML";
532             break;
533
534         case S_SAML:
535             smtpcom = "SAML";
536             break;
537     }
538
539     switch (smtalk (SM_MAIL, "%s FROM:<%s>", smtpcom, from)) {
540         case 250: 
541             sm_addrs = 0;
542             return RP_OK;
543
544         case 500: 
545         case 501: 
546         case 552: 
547             return RP_PARM;
548
549         default: 
550             return RP_RPLY;
551     }
552 }
553
554
555 int
556 sm_wadr (char *mbox, char *host, char *path)
557 {
558     switch (smtalk (SM_RCPT, host && *host ? "RCPT TO:<%s%s@%s>"
559                                            : "RCPT TO:<%s%s>",
560                              path ? path : "", mbox, host)) {
561         case 250: 
562         case 251: 
563             sm_addrs++;
564             return RP_OK;
565
566         case 451: 
567 #ifdef SENDMAILBUG
568             sm_addrs++;
569             return RP_OK;
570 #endif /* SENDMAILBUG */
571         case 421: 
572         case 450: 
573         case 452: 
574             return RP_NO;
575
576         case 500: 
577         case 501: 
578             return RP_PARM;
579
580         case 550: 
581         case 551: 
582         case 552: 
583         case 553: 
584             return RP_USER;
585
586         default: 
587             return RP_RPLY;
588     }
589 }
590
591
592 int
593 sm_waend (void)
594 {
595     switch (smtalk (SM_DATA, "DATA")) {
596         case 354: 
597             sm_nl = TRUE;
598             return RP_OK;
599
600         case 451: 
601 #ifdef SENDMAILBUG
602             sm_nl = TRUE;
603             return RP_OK;
604 #endif /* SENDMAILBUG */
605         case 421: 
606             return RP_NO;
607
608         case 500: 
609         case 501: 
610         case 503: 
611         case 554: 
612             return RP_NDEL;
613
614         default: 
615             return RP_RPLY;
616     }
617 }
618
619
620 int
621 sm_wtxt (char *buffer, int len)
622 {
623     int result;
624
625     sm_alarmed = 0;
626     alarm (SM_TEXT);
627     result = sm_wstream (buffer, len);
628     alarm (0);
629
630     return (result == NOTOK ? RP_BHST : RP_OK);
631 }
632
633
634 int
635 sm_wtend (void)
636 {
637     if (sm_wstream ((char *) NULL, 0) == NOTOK)
638         return RP_BHST;
639
640     switch (smtalk (SM_DOT + 3 * sm_addrs, ".")) {
641         case 250: 
642         case 251: 
643             return RP_OK;
644
645         case 451: 
646 #ifdef SENDMAILBUG
647             return RP_OK;
648 #endif /* SENDMAILBUG */
649         case 452: 
650         default: 
651             return RP_NO;
652
653         case 552: 
654         case 554: 
655             return RP_NDEL;
656     }
657 }
658
659
660 int
661 sm_end (int type)
662 {
663     int status;
664     struct smtp sm_note;
665
666     if (sm_mts == MTS_SENDMAIL) {
667         switch (sm_child) {
668             case NOTOK: 
669             case OK: 
670                 return RP_OK;
671
672             default: 
673                 break;
674         }
675     }
676
677     if (sm_rfp == NULL && sm_wfp == NULL)
678         return RP_OK;
679
680     switch (type) {
681         case OK: 
682             smtalk (SM_QUIT, "QUIT");
683             break;
684
685         case NOTOK: 
686             sm_note.code = sm_reply.code;
687             sm_note.length = sm_reply.length;
688             memcpy (sm_note.text, sm_reply.text, sm_reply.length + 1);/* fall */
689         case DONE: 
690             if (smtalk (SM_RSET, "RSET") == 250 && type == DONE)
691                 return RP_OK;
692             if (sm_mts == MTS_SMTP)
693                 smtalk (SM_QUIT, "QUIT");
694             else {
695                 kill (sm_child, SIGKILL);
696                 discard (sm_rfp);
697                 discard (sm_wfp);
698             }
699             if (type == NOTOK) {
700                 sm_reply.code = sm_note.code;
701                 sm_reply.length = sm_note.length;
702                 memcpy (sm_reply.text, sm_note.text, sm_note.length + 1);
703             }
704             break;
705     }
706
707 #ifdef MPOP
708     if (sm_ispool) {
709         sm_ispool = 0;
710
711         if (sm_wfp) {
712             unlink (sm_tmpfil);
713             fclose (sm_wfp);
714             sm_wfp = NULL;
715         }
716     }
717 #endif /* MPOP */
718
719     if (sm_rfp != NULL) {
720         alarm (SM_CLOS);
721         fclose (sm_rfp);
722         alarm (0);
723     }
724     if (sm_wfp != NULL) {
725         alarm (SM_CLOS);
726         fclose (sm_wfp);
727         alarm (0);
728     }
729
730     if (sm_mts == MTS_SMTP) {
731         status = 0;
732 #ifdef CYRUS_SASL
733         if (conn)
734             sasl_dispose(&conn);
735 #endif /* CYRUS_SASL */
736     } else {
737         status = pidwait (sm_child, OK);
738         sm_child = NOTOK;
739     }
740
741     sm_rfp = sm_wfp = NULL;
742     return (status ? RP_BHST : RP_OK);
743 }
744
745
746 #ifdef MPOP
747
748 int
749 sm_bulk (char *file)
750 {
751     int cc, i, j, k, result;
752     long pos;
753     char *dp, *bp, *cp, s;
754     char buffer[BUFSIZ], sender[BUFSIZ];
755     FILE *fp, *gp;
756
757     gp = NULL;
758     k = strlen (file) - sizeof(".bulk");
759     if ((fp = fopen (file, "r")) == NULL) {
760         return sm_perror("unable to read %s: ", file);
761     }
762     if (sm_debug) {
763         printf ("reading file %s\n", file);
764         fflush (stdout);
765     }
766
767     i = j = 0;
768     while (fgets (buffer, sizeof(buffer), fp)) {
769         if (j++ == 0)
770             strncpy (sender, buffer + sizeof("MAIL FROM:") - 1, sizeof(sender));
771         if (strcmp (buffer, "DATA\r\n") == 0) {
772             i = 1;
773             break;
774         }
775     }
776     if (i == 0) {
777         if (sm_debug) {
778             printf ("no DATA...\n");
779             fflush (stdout);
780         }
781 losing0:
782         snprintf (buffer, sizeof(buffer), "%s.bad", file);
783         rename (file, buffer);
784         if (gp) {
785             snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
786             unlink (buffer);
787             fclose (gp);
788         }
789         fclose (fp);
790         return RP_OK;
791     }
792     if (j < 3) {
793         if (sm_debug) {
794             printf ("no %srecipients...\n", j < 1 ? "sender or " : "");
795             fflush (stdout);
796         }
797         goto losing0;
798     }
799
800     if ((cp = malloc ((size_t) (cc = (pos = ftell (fp)) + 1))) == NULL) {
801         sm_reply.length = strlen (strcpy (sm_reply.text, "out of memory"));
802 losing1: ;
803         sm_reply.code = NOTOK;
804         fclose (fp);
805         return RP_BHST;
806     }
807     fseek (fp, 0L, SEEK_SET);
808     for (dp = cp, i = 0; i++ < j; dp += strlen (dp))
809         if (fgets (dp, cc - (dp - cp), fp) == NULL) {
810             sm_reply.length = strlen (strcpy (sm_reply.text, "premature eof"));
811 losing2:
812             free (cp);
813             goto losing1;
814         }
815     *dp = NULL;
816
817     for (dp = cp, i = cc - 1; i > 0; dp += cc, i -= cc)
818         if ((cc = write (fileno (sm_wfp), dp, i)) == NOTOK) {
819             int len;
820 losing3:
821             sm_perror("error writing to server: ");
822             goto losing2;
823         }
824         else
825             if (sm_debug) {
826                 printf ("wrote %d octets to server\n", cc);
827                 fflush (stdout);
828             }
829
830     for (dp = cp, i = 0; i++ < j; dp = strchr(dp, '\n'), dp++) {
831         if (sm_debug) {
832             if (bp = strchr(dp, '\r'))
833                 *bp = NULL;
834             printf ("=> %s\n", dp);
835             fflush (stdout);
836             if (bp)
837                 *bp = '\r';
838         }
839
840         switch (smhear () + (i == 1 ? 1000 : i != j ? 2000 : 3000)) {
841             case 1000 + 250:
842                 sm_addrs = 0;
843                 result = RP_OK;
844                 break;
845
846             case 1000 + 500: 
847             case 1000 + 501: 
848             case 1000 + 552: 
849             case 2000 + 500: 
850             case 2000 + 501:
851                 result = RP_PARM;
852                 smtalk (SM_RSET, "RSET");
853                 free (cp);
854                 goto losing0;
855
856             case 2000 + 250:
857             case 2000 + 251:
858                 sm_addrs++;
859                 result = RP_OK;
860                 break;
861
862             case 2000 + 451: 
863 #ifdef SENDMAILBUG
864                 sm_addrs++;
865                 result = RP_OK;
866                 break;
867 #endif
868             case 2000 + 421: 
869             case 2000 + 450: 
870             case 2000 + 452: 
871                 result = RP_NO;
872                 goto bad_addr;
873
874             case 2000 + 550: 
875             case 2000 + 551: 
876             case 2000 + 552: 
877             case 2000 + 553: 
878                 result = RP_USER;
879 bad_addr:
880                 if (k <= 0 || strcmp (sender, "<>\r\n") == 0)
881                     break;
882                 if (gp == NULL) {
883                     int     l;
884                     snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
885                     if ((gp = fopen (buffer, "w+")) == NULL)
886                         goto bad_data;
887                     fprintf (gp, "MAIL FROM:<>\r\nRCPT TO:%sDATA\r\n", sender);
888                     l = strlen (sender);
889                     fprintf (gp,
890                              "To: %*.*s\r\nSubject: Invalid addresses (%s)\r\n",
891                              l - 4, l - 4, sender + 1, file);
892                     fprintf (gp, "Date: %s\r\nFrom: Postmaster@%s\r\n\r\n",
893                              dtimenow (0), LocalName ());
894                 }
895                 if (bp = strchr(dp, '\r'))
896                     *bp = NULL;
897                 fprintf (gp, "=>        %s\r\n", dp);
898                 if (bp)
899                     *bp = '\r';
900                 fprintf (gp, "<= %s\r\n", rp_string (result));
901                 fflush (gp);
902                 break;
903
904             case 3000 + 354: 
905 #ifdef SENDMAILBUG
906 ok_data:
907 #endif
908                 result = RP_OK;
909                 break;
910
911             case 3000 + 451: 
912 #ifdef SENDMAILBUG
913                 goto ok_data;
914 #endif
915             case 3000 + 421:
916                 result = RP_NO;
917 bad_data:
918                 smtalk (SM_RSET, "RSET");
919                 free (cp);
920                 if (gp) {
921                     snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
922                     unlink (buffer);
923                     fclose (gp);
924                 }
925                 fclose (fp);
926                 return result;
927
928             case 3000 + 500: 
929             case 3000 + 501: 
930             case 3000 + 503: 
931             case 3000 + 554: 
932                 smtalk (SM_RSET, "RSET");
933                 free (cp);
934                 goto no_dice;
935
936             default:
937                 result = RP_RPLY;
938                 goto bad_data;
939         }
940     }
941     free (cp);
942
943     {
944 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
945         struct stat st;
946
947         if (fstat (fileno (sm_wfp), &st) == NOTOK || (cc = st.st_blksize) < BUFSIZ)
948             cc = BUFSIZ;
949 #else
950         cc = BUFSIZ;
951 #endif
952         if ((cp = malloc ((size_t) cc)) == NULL) {
953             smtalk (SM_RSET, "RSET");
954             sm_reply.length = strlen (strcpy (sm_reply.text, "out of memory"));
955             goto losing1;
956         }
957     }
958
959     fseek (fp, pos, SEEK_SET);
960     for (;;) {
961         int eof = 0;
962
963         for (dp = cp, i = cc; i > 0; dp += j, i -= j)
964             if ((j = fread (cp, sizeof(*cp), i, fp)) == OK) {
965                 if (ferror (fp)) {
966                     sm_perror("error reading %s: ", file);
967                     goto losing2;
968                 }
969                 cc = dp - cp;
970                 eof = 1;
971                 break;
972             }
973
974         for (dp = cp, i = cc; i > 0; dp += j, i -= j)
975             if ((j = write (fileno (sm_wfp), dp, i)) == NOTOK)
976                 goto losing3;
977             else
978                 if (sm_debug) {
979                     printf ("wrote %d octets to server\n", j);
980                     fflush (stdout);
981                 }
982
983         if (eof)
984             break;
985     }
986     free (cp);
987
988     switch (smhear ()) {
989         case 250: 
990         case 251: 
991 #ifdef SENDMAILBUG
992 ok_dot:
993 #endif
994             result = RP_OK;
995             unlink (file);
996             break;
997
998         case 451: 
999 #ifdef SENDMAILBUG
1000             goto ok_dot;
1001 #endif
1002         case 452: 
1003         default: 
1004             result = RP_NO;
1005             if (gp) {
1006                 snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
1007                 unlink (buffer);
1008                 fclose (gp);
1009                 gp = NULL;
1010             }
1011             break;
1012
1013         case 552: 
1014         case 554: 
1015 no_dice:
1016             result = RP_NDEL;
1017             if (k <= 0 || strcmp (sender, "<>\r\n") == 0) {
1018                 unlink (file);
1019                 break;
1020             }
1021             if (gp) {
1022                 fflush (gp);
1023                 ftruncate (fileno (gp), 0L);
1024                 fseek (gp, 0L, SEEK_SET);
1025             }
1026             else {
1027                 snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
1028                 if ((gp = fopen (buffer, "w")) == NULL)
1029                     break;
1030             }
1031             fprintf (gp, "MAIL FROM:<>\r\nRCPT TO:%sDATA\r\n", sender);
1032             i = strlen (sender);
1033             fprintf (gp, "To: %*.*s\r\nSubject: Failed mail (%s)\r\n",
1034                      i - 4, i - 4, sender + 1, file);
1035             fprintf (gp, "Date: %s\r\nFrom: Postmaster@%s\r\n\r\n",
1036                      dtimenow (0), LocalName ());
1037             break;
1038     }
1039
1040     if (gp) {
1041         fputs ("\r\n------- Begin Returned message\r\n\r\n", gp);
1042         fseek (fp, pos, SEEK_SET);
1043         while (fgets (buffer, sizeof(buffer), fp)) {
1044             if (buffer[0] == '-')
1045                 fputs ("- ", gp);
1046             if (strcmp (buffer, ".\r\n"))
1047                 fputs (buffer, gp);
1048         }
1049         fputs ("\r\n------- End Returned Message\r\n\r\n.\r\n", gp);
1050         fflush (gp);
1051         if (!ferror (gp))
1052             unlink (file);
1053         fclose (gp);
1054     }
1055     fclose (fp);
1056
1057     return result;
1058 }
1059 #endif /* MPOP */
1060
1061
1062 #ifdef CYRUS_SASL
1063 /*
1064  * This function implements SASL authentication for SMTP.  If this function
1065  * completes successfully, then authentication is successful and we've
1066  * (optionally) negotiated a security layer.
1067  *
1068  * Right now we don't support session encryption.
1069  */
1070 static int
1071 sm_auth_sasl(char *user, char *mechlist, char *host)
1072 {
1073     int result, status;
1074     unsigned int buflen, outlen;
1075     char *buf, outbuf[BUFSIZ];
1076     const char *chosen_mech;
1077     sasl_security_properties_t secprops;
1078     sasl_ssf_t *ssf;
1079     int *outbufmax;
1080
1081     /*
1082      * Initialize the callback contexts
1083      */
1084
1085     if (user == NULL)
1086         user = getusername();
1087
1088     callbacks[SM_SASL_N_CB_USER].context = user;
1089     callbacks[SM_SASL_N_CB_AUTHNAME].context = user;
1090
1091     /*
1092      * This is a _bit_ of a hack ... but if the hostname wasn't supplied
1093      * to us on the command line, then call getpeername and do a
1094      * reverse-address lookup on the IP address to get the name.
1095      */
1096
1097     if (!host) {
1098         struct sockaddr_in sin;
1099         int len = sizeof(sin);
1100         struct hostent *hp;
1101
1102         if (getpeername(fileno(sm_wfp), (struct sockaddr *) &sin, &len) < 0) {
1103             sm_ierror("getpeername on SMTP socket failed: %s",
1104                       strerror(errno));
1105             return NOTOK;
1106         }
1107
1108         if ((hp = gethostbyaddr((void *) &sin.sin_addr, sizeof(sin.sin_addr),
1109                                 sin.sin_family)) == NULL) {
1110             sm_ierror("DNS lookup on IP address %s failed",
1111                       inet_ntoa(sin.sin_addr));
1112             return NOTOK;
1113         }
1114
1115         host = strdup(hp->h_name);
1116     }
1117
1118     sasl_pw_context[0] = host;
1119     sasl_pw_context[1] = user;
1120
1121     callbacks[SM_SASL_N_CB_PASS].context = sasl_pw_context;
1122
1123     result = sasl_client_init(callbacks);
1124
1125     if (result != SASL_OK) {
1126         sm_ierror("SASL library initialization failed: %s",
1127                   sasl_errstring(result, NULL, NULL));
1128         return NOTOK;
1129     }
1130
1131     result = sasl_client_new("smtp", host, NULL, NULL, NULL, 0, &conn);
1132
1133     if (result != SASL_OK) {
1134         sm_ierror("SASL client initialization failed: %s",
1135                   sasl_errstring(result, NULL, NULL));
1136         return NOTOK;
1137     }
1138
1139     /*
1140      * Initialize the security properties
1141      */
1142
1143     memset(&secprops, 0, sizeof(secprops));
1144     secprops.maxbufsize = BUFSIZ;
1145     secprops.max_ssf = 0;       /* XXX change this when we do encryption */
1146
1147     result = sasl_setprop(conn, SASL_SEC_PROPS, &secprops);
1148
1149     if (result != SASL_OK) {
1150         sm_ierror("SASL security property initialization failed: %s",
1151                   sasl_errstring(result, NULL, NULL));
1152         return NOTOK;
1153     }
1154
1155     /*
1156      * Start the actual protocol.  Feed the mech list into the library
1157      * and get out a possible initial challenge
1158      */
1159
1160     result = sasl_client_start(conn, mechlist, NULL, (const char **) &buf,
1161                                &buflen, (const char **) &chosen_mech);
1162
1163     if (result != SASL_OK && result != SASL_CONTINUE) {
1164         sm_ierror("SASL client start failed: %s",
1165                   sasl_errstring(result, NULL, NULL));
1166         return NOTOK;
1167     }
1168
1169     /*
1170      * If we got an initial challenge, send it as part of the AUTH
1171      * command; otherwise, just send a plain AUTH command.
1172      */
1173
1174     if (buflen) {
1175         status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL);
1176         if (status != SASL_OK) {
1177             sm_ierror("SASL base64 encode failed: %s",
1178                       sasl_errstring(status, NULL, NULL));
1179             return NOTOK;
1180         }
1181
1182         status = smtalk(SM_AUTH, "AUTH %s %s", chosen_mech, outbuf);
1183     } else
1184         status = smtalk(SM_AUTH, "AUTH %s", chosen_mech);
1185
1186     /*
1187      * Now we loop until we either fail, get a SASL_OK, or a 235
1188      * response code.  Receive the challenges and process them until
1189      * we're all done.
1190      */
1191
1192     while (result == SASL_CONTINUE) {
1193
1194         /*
1195          * If we get a 235 response, that means authentication has
1196          * succeeded and we need to break out of the loop (yes, even if
1197          * we still get SASL_CONTINUE from sasl_client_step()).
1198          *
1199          * Otherwise, if we get a message that doesn't seem to be a
1200          * valid response, then abort
1201          */
1202
1203         if (status == 235)
1204             break;
1205         else if (status < 300 || status > 399)
1206             return RP_BHST;
1207         
1208         /*
1209          * Special case; a zero-length response from the SMTP server
1210          * is returned as a single =.  If we get that, then set buflen
1211          * to be zero.  Otherwise, just decode the response.
1212          */
1213         
1214         if (strcmp("=", sm_reply.text) == 0) {
1215             outlen = 0;
1216         } else {
1217             result = sasl_decode64(sm_reply.text, sm_reply.length,
1218                                    outbuf, sizeof(outbuf), &outlen);
1219         
1220             if (result != SASL_OK) {
1221                 smtalk(SM_AUTH, "*");
1222                 sm_ierror("SASL base64 decode failed: %s",
1223                           sasl_errstring(result, NULL, NULL));
1224                 return NOTOK;
1225             }
1226         }
1227
1228         result = sasl_client_step(conn, outbuf, outlen, NULL,
1229                                   (const char **) &buf, &buflen);
1230
1231         if (result != SASL_OK && result != SASL_CONTINUE) {
1232             smtalk(SM_AUTH, "*");
1233             sm_ierror("SASL client negotiation failed: %s",
1234                       sasl_errstring(result, NULL, NULL));
1235             return NOTOK;
1236         }
1237
1238         status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL);
1239
1240         if (status != SASL_OK) {
1241             smtalk(SM_AUTH, "*");
1242             sm_ierror("SASL base64 encode failed: %s",
1243                       sasl_errstring(status, NULL, NULL));
1244             return NOTOK;
1245         }
1246         
1247         status = smtalk(SM_AUTH, outbuf);
1248     }
1249
1250     /*
1251      * Make sure that we got the correct response
1252      */
1253
1254     if (status < 200 || status > 299)
1255         return RP_BHST;
1256
1257     /*
1258      * We _should_ have completed the authentication successfully.
1259      * Get a few properties from the authentication exchange.
1260      */
1261
1262     result = sasl_getprop(conn, SASL_MAXOUTBUF, (const void **) &outbufmax);
1263
1264     if (result != SASL_OK) {
1265         sm_ierror("Cannot retrieve SASL negotiated output buffer size: %s",
1266                   sasl_errstring(result, NULL, NULL));
1267         return NOTOK;
1268     }
1269
1270     maxoutbuf = *outbufmax;
1271
1272     result = sasl_getprop(conn, SASL_SSF, (const void **) &ssf);
1273
1274     sasl_ssf = *ssf;
1275
1276     if (result != SASL_OK) {
1277         sm_ierror("Cannot retrieve SASL negotiated security strength "
1278                   "factor: %s", sasl_errstring(result, NULL, NULL));
1279         return NOTOK;
1280     }
1281
1282     if (maxoutbuf == 0 || maxoutbuf > BUFSIZ)
1283         maxoutbuf = BUFSIZ;
1284
1285     sasl_complete = 1;
1286
1287     return RP_OK;
1288 }
1289
1290 /*
1291  * Our callback functions to feed data to the SASL library
1292  */
1293
1294 static int
1295 sm_get_user(void *context, int id, const char **result, unsigned *len)
1296 {
1297     char *user = (char *) context;
1298
1299     if (! result || ((id != SASL_CB_USER) && (id != SASL_CB_AUTHNAME)))
1300         return SASL_BADPARAM;
1301
1302     *result = user;
1303     if (len)
1304         *len = strlen(user);
1305
1306     return SASL_OK;
1307 }
1308
1309 static int
1310 sm_get_pass(sasl_conn_t *conn, void *context, int id,
1311             sasl_secret_t **psecret)
1312 {
1313     char **pw_context = (char **) context;
1314     char *pass = NULL;
1315     int len;
1316
1317     if (! psecret || id != SASL_CB_PASS)
1318         return SASL_BADPARAM;
1319
1320     ruserpass(pw_context[0], &(pw_context[1]), &pass);
1321
1322     len = strlen(pass);
1323
1324     *psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len);
1325
1326     if (! *psecret) {
1327         free(pass);
1328         return SASL_NOMEM;
1329     }
1330
1331     (*psecret)->len = len;
1332     strcpy((char *) (*psecret)->data, pass);
1333 /*    free(pass); */
1334
1335     return SASL_OK;
1336 }
1337 #endif /* CYRUS_SASL */
1338
1339 static int
1340 sm_ierror (char *fmt, ...)
1341 {
1342     va_list ap;
1343
1344     va_start(ap, fmt);
1345     vsnprintf (sm_reply.text, sizeof(sm_reply.text), fmt, ap);
1346     va_end(ap);
1347
1348     sm_reply.length = strlen (sm_reply.text);
1349     sm_reply.code = NOTOK;
1350
1351     return RP_BHST;
1352 }
1353
1354 #ifdef MPOP
1355 static int
1356 sm_perror (char *fmt, ...)
1357 {
1358     /* Fill in sm_reply with a suitable error string based on errno.
1359      * This isn't particularly MPOP specific, it just happens that that's
1360      * the only code that uses it currently.
1361      */
1362     char *bp, *s;
1363     int len, eno = errno;
1364
1365     va_list ap;
1366     va_start(ap,fmt);
1367     vsnprintf (sm_reply.text, sizeof(sm_reply.text), fmt, ap);
1368     va_end(ap);
1369
1370     bp = sm_reply.text;
1371     len = strlen(bp);
1372     bp += len;
1373     if ((s = strerror(eno)))
1374         snprintf(bp, sizeof(sm_reply.text) - len, "%s", s);
1375     else
1376         snprintf(bp, sizeof(sm_reply.text) - len, "unknown error %d", eno);
1377     
1378     sm_reply.length = strlen (sm_reply.text);
1379     sm_reply.code = NOTOK;
1380
1381     return RP_BHST;
1382 }
1383 #endif
1384
1385 static int
1386 smtalk (int time, char *fmt, ...)
1387 {
1388     va_list ap;
1389     int result;
1390     char buffer[BUFSIZ];
1391
1392     va_start(ap, fmt);
1393     vsnprintf (buffer, sizeof(buffer), fmt, ap);
1394     va_end(ap);
1395
1396     if (sm_debug) {
1397         printf ("=> %s\n", buffer);
1398         fflush (stdout);
1399     }
1400
1401 #ifdef MPOP
1402     if (sm_ispool) {
1403         char file[BUFSIZ];
1404
1405         if (strcmp (buffer, ".") == 0)
1406             time = SM_DOT;
1407         fprintf (sm_wfp, "%s\r\n", buffer);
1408         switch (time) {
1409             case SM_DOT:
1410                 fflush (sm_wfp);
1411                 if (ferror (sm_wfp))
1412                     return sm_werror ();
1413                 snprintf (file, sizeof(file), "%s%c.bulk", sm_tmpfil,
1414                                 (char) (sm_ispool + 'a' - 1));
1415                 if (rename (sm_tmpfil, file) == NOTOK) {
1416                     return sm_perror("error renaming %s to %s: ", sm_tmpfil, file);
1417                 }
1418                 fclose (sm_wfp);
1419                 if (sm_wfp = fopen (sm_tmpfil, "w"))
1420                     chmod (sm_tmpfil, 0600);
1421                 sm_ispool++;
1422                 /* and fall... */
1423
1424             case SM_MAIL:
1425             case SM_RCPT:
1426                 result = 250;
1427                 break;
1428
1429             case SM_RSET:
1430                 fflush (sm_wfp);
1431                 ftruncate (fileno (sm_wfp), 0L);
1432                 fseek (sm_wfp, 0L, SEEK_SET);
1433                 result = 250;
1434                 break;
1435
1436             case SM_DATA:
1437                 result = 354;
1438                 break;
1439
1440             case SM_QUIT:
1441                 unlink (sm_tmpfil);
1442                 sm_ispool = 0;
1443                 result = 221;
1444                 break;
1445
1446             default:
1447                 result = 500;
1448                 break;
1449         }
1450         if (sm_debug) {
1451             printf ("<= %d\n", result);
1452             fflush (stdout);
1453         }
1454
1455         sm_reply.text[sm_reply.length = 0] = NULL;
1456         return (sm_reply.code = result);
1457     }
1458 #endif /* MPOP */
1459
1460     sm_alarmed = 0;
1461     alarm ((unsigned) time);
1462     if ((result = sm_wrecord (buffer, strlen (buffer))) != NOTOK)
1463         result = smhear ();
1464     alarm (0);
1465
1466     return result;
1467 }
1468
1469
1470 /*
1471  * write the buffer to the open SMTP channel
1472  */
1473
1474 static int
1475 sm_wrecord (char *buffer, int len)
1476 {
1477     if (sm_wfp == NULL)
1478         return sm_werror ();
1479
1480     fwrite (buffer, sizeof(*buffer), len, sm_wfp);
1481     fputs ("\r\n", sm_wfp);
1482     fflush (sm_wfp);
1483
1484     return (ferror (sm_wfp) ? sm_werror () : OK);
1485 }
1486
1487
1488 static int
1489 sm_wstream (char *buffer, int len)
1490 {
1491     char  *bp;
1492     static char lc = '\0';
1493
1494     if (sm_wfp == NULL)
1495         return sm_werror ();
1496
1497     if (buffer == NULL && len == 0) {
1498         if (lc != '\n')
1499             fputs ("\r\n", sm_wfp);
1500         lc = '\0';
1501         return (ferror (sm_wfp) ? sm_werror () : OK);
1502     }
1503
1504     for (bp = buffer; len > 0; bp++, len--) {
1505         switch (*bp) {
1506             case '\n': 
1507                 sm_nl = TRUE;
1508                 fputc ('\r', sm_wfp);
1509                 break;
1510
1511             case '.': 
1512                 if (sm_nl)
1513                     fputc ('.', sm_wfp);/* FALL THROUGH */
1514             default: 
1515                 sm_nl = FALSE;
1516         }
1517         fputc (*bp, sm_wfp);
1518         if (ferror (sm_wfp))
1519             return sm_werror ();
1520     }
1521
1522     if (bp > buffer)
1523         lc = *--bp;
1524     return (ferror (sm_wfp) ? sm_werror () : OK);
1525 }
1526
1527
1528 /*
1529  * On some systems, strlen and strcpy are defined as preprocessor macros.  This
1530  * causes compile problems with the #ifdef MPOP in the middle.  Should the
1531  * #ifdef MPOP be removed, remove these #undefs.
1532  */
1533 #ifdef strlen
1534 # undef strlen
1535 #endif
1536 #ifdef strcpy
1537 # undef strcpy
1538 #endif
1539
1540 static int
1541 sm_werror (void)
1542 {
1543     sm_reply.length =
1544         strlen (strcpy (sm_reply.text, sm_wfp == NULL ? "no socket opened"
1545             : sm_alarmed ? "write to socket timed out"
1546 #ifdef MPOP
1547             : sm_ispool ? "error writing to spool file"
1548 #endif
1549             : "error writing to socket"));
1550
1551     return (sm_reply.code = NOTOK);
1552 }
1553
1554
1555 static int
1556 smhear (void)
1557 {
1558     int i, code, cont, bc, rc, more;
1559     unsigned char *bp;
1560     char *rp;
1561     char **ehlo, buffer[BUFSIZ];
1562
1563     if (doingEHLO) {
1564         static int at_least_once = 0;
1565
1566         if (at_least_once) {
1567             char *ep;
1568
1569             for (ehlo = EHLOkeys; *ehlo; ehlo++) {
1570                 ep = *ehlo;
1571                 free (ep);
1572             }
1573         } else {
1574             at_least_once = 1;
1575         }
1576
1577         ehlo = EHLOkeys;
1578         *ehlo = NULL;
1579     }
1580
1581 again: ;
1582
1583     sm_reply.length = 0;
1584     sm_reply.text[0] = 0;
1585     rp = sm_reply.text;
1586     rc = sizeof(sm_reply.text) - 1;
1587
1588     for (more = FALSE; sm_rrecord (bp = buffer, &bc) != NOTOK;) {
1589         if (sm_debug) {
1590             printf ("<= %s\n", buffer);
1591             fflush (stdout);
1592         }
1593
1594         if (doingEHLO
1595                 && strncmp (buffer, "250", sizeof("250") - 1) == 0
1596                 && (buffer[3] == '-' || doingEHLO == 2)
1597                 && buffer[4]) {
1598             if (doingEHLO == 2) {
1599                 if ((*ehlo = malloc ((size_t) (strlen (buffer + 4) + 1)))) {
1600                     strcpy (*ehlo++, buffer + 4);
1601                     *ehlo = NULL;
1602                     if (ehlo >= EHLOkeys + MAXEHLO)
1603                         doingEHLO = 0;
1604                 }
1605                 else
1606                     doingEHLO = 0;
1607             }
1608             else
1609                 doingEHLO = 2;
1610         }
1611
1612         for (; bc > 0 && (!isascii (*bp) || !isdigit (*bp)); bp++, bc--)
1613             continue;
1614
1615         cont = FALSE;
1616         code = atoi (bp);
1617         bp += 3, bc -= 3;
1618         for (; bc > 0 && isspace (*bp); bp++, bc--)
1619             continue;
1620         if (bc > 0 && *bp == '-') {
1621             cont = TRUE;
1622             bp++, bc--;
1623             for (; bc > 0 && isspace (*bp); bp++, bc--)
1624                 continue;
1625         }
1626
1627         if (more) {
1628             if (code != sm_reply.code || cont)
1629                 continue;
1630             more = FALSE;
1631         } else {
1632             sm_reply.code = code;
1633             more = cont;
1634             if (bc <= 0) {
1635                 /* can never fail to 0-terminate because of size of buffer vs fixed string */
1636                 strncpy (buffer, sm_noreply, sizeof(buffer));
1637                 bp = buffer;
1638                 bc = strlen (sm_noreply);
1639             }
1640         }
1641
1642         if ((i = min (bc, rc)) > 0) {
1643             memcpy (rp, bp, i);
1644             rp += i;
1645             rc -= i;
1646             i = strlen(sm_moreply);
1647             if (more && rc > i + 1) {
1648                 memcpy (rp, sm_moreply, i); /* safe because of check in if() */
1649                 rp += i;
1650                 rc -= i;
1651             }
1652         }
1653         if (more)
1654             continue;
1655         if (sm_reply.code < 100) {
1656             if (sm_verbose) {
1657                 printf ("%s\n", sm_reply.text);
1658                 fflush (stdout);
1659             }
1660             goto again;
1661         }
1662
1663         sm_reply.length = rp - sm_reply.text;
1664         sm_reply.text[sm_reply.length] = 0;
1665         return sm_reply.code;
1666     }
1667     return NOTOK;
1668 }
1669
1670
1671 static int
1672 sm_rrecord (char *buffer, int *len)
1673 {
1674     if (sm_rfp == NULL)
1675         return sm_rerror ();
1676
1677     buffer[*len = 0] = 0;
1678
1679     fgets (buffer, BUFSIZ, sm_rfp);
1680     *len = strlen (buffer);
1681     /* *len should be >0 except on EOF, but check for safety's sake */
1682     if (ferror (sm_rfp) || feof (sm_rfp) || (*len == 0))
1683         return sm_rerror ();
1684     if (buffer[*len - 1] != '\n')
1685         while (getc (sm_rfp) != '\n' && !ferror (sm_rfp) && !feof (sm_rfp))
1686             continue;
1687     else
1688         if ((*len > 1) && (buffer[*len - 2] == '\r'))
1689             *len -= 1;
1690     *len -= 1;
1691     buffer[*len] = 0;
1692
1693     return OK;
1694 }
1695
1696
1697 static int
1698 sm_rerror (void)
1699 {
1700     if (sm_mts == MTS_SMTP)
1701         sm_reply.length =
1702             strlen (strcpy (sm_reply.text, sm_rfp == NULL ? "no socket opened"
1703                 : sm_alarmed ? "read from socket timed out"
1704                 : feof (sm_rfp) ? "premature end-of-file on socket"
1705                 : "error reading from socket"));
1706     else
1707         sm_reply.length =
1708             strlen (strcpy (sm_reply.text, sm_rfp == NULL ? "no pipe opened"
1709                 : sm_alarmed ? "read from pipe timed out"
1710                 : feof (sm_rfp) ? "premature end-of-file on pipe"
1711                 : "error reading from pipe"));
1712
1713     return (sm_reply.code = NOTOK);
1714 }
1715
1716
1717 static RETSIGTYPE
1718 alrmser (int i)
1719 {
1720 #ifndef RELIABLE_SIGNALS
1721     SIGNAL (SIGALRM, alrmser);
1722 #endif
1723
1724     sm_alarmed++;
1725     if (sm_debug) {
1726         printf ("timed out...\n");
1727         fflush (stdout);
1728     }
1729 }
1730
1731
1732 char *
1733 rp_string (int code)
1734 {
1735     char *text;
1736     static char buffer[BUFSIZ];
1737
1738     switch (sm_reply.code != NOTOK ? code : NOTOK) {
1739         case RP_AOK:
1740             text = "AOK";
1741             break;
1742
1743         case RP_MOK:
1744             text = "MOK";
1745             break;
1746
1747         case RP_OK: 
1748             text = "OK";
1749             break;
1750
1751         case RP_RPLY: 
1752             text = "RPLY";
1753             break;
1754
1755         case RP_BHST: 
1756         default: 
1757             text = "BHST";
1758             snprintf (buffer, sizeof(buffer), "[%s] %s", text, sm_reply.text);
1759             return buffer;
1760
1761         case RP_PARM: 
1762             text = "PARM";
1763             break;
1764
1765         case RP_NO: 
1766             text = "NO";
1767             break;
1768
1769         case RP_USER: 
1770             text = "USER";
1771             break;
1772
1773         case RP_NDEL: 
1774             text = "NDEL";
1775             break;
1776     }
1777
1778     snprintf (buffer, sizeof(buffer), "[%s] %3d %s",
1779                 text, sm_reply.code, sm_reply.text);
1780     return buffer;
1781 }
1782
1783
1784 #ifdef MPOP
1785
1786 static char *broken[MAXARGS + 1];
1787
1788 static char **
1789 smail_brkstring (char *strg, char *brksep, char *brkterm)
1790 {
1791     int bi;
1792     char c, *sp;
1793
1794     sp = strg;
1795
1796     for (bi = 0; bi < MAXARGS; bi++) {
1797         while (smail_brkany (c = *sp, brksep))
1798             *sp++ = 0;
1799         if (!c || smail_brkany (c, brkterm)) {
1800             *sp = 0;
1801             broken[bi] = 0;
1802             return broken;
1803         }
1804
1805         broken[bi] = sp;
1806         while ((c = *++sp) && !smail_brkany (c, brksep) && !smail_brkany (c, brkterm))
1807             continue;
1808     }
1809     broken[MAXARGS] = 0;
1810
1811     return broken;
1812 }
1813
1814
1815 /*
1816  * returns 1 if chr in strg, 0 otherwise
1817  */
1818 static int
1819 smail_brkany (char chr, char *strg)
1820 {
1821     char *sp;
1822  
1823     if (strg)
1824         for (sp = strg; *sp; sp++)
1825             if (chr == *sp)
1826                 return 1;
1827     return 0;
1828 }
1829
1830 /*
1831  * copy a string array and return pointer to end
1832  */
1833 char **
1834 smail_copyip (char **p, char **q, int len_q)
1835 {
1836     while (*p && --len_q > 0)
1837         *q++ = *p++;
1838
1839     *q = NULL;
1840  
1841     return q;
1842 }
1843
1844 #endif /* MPOP */
1845
1846
1847 static char *
1848 EHLOset (char *s)
1849 {
1850     size_t len;
1851     char *ep, **ehlo;
1852
1853     len = strlen (s);
1854
1855     for (ehlo = EHLOkeys; *ehlo; ehlo++) {
1856         ep = *ehlo;
1857         if (strncmp (ep, s, len) == 0) {
1858             for (ep += len; *ep == ' '; ep++)
1859                 continue;
1860             return ep;
1861         }
1862     }
1863
1864     return 0;
1865 }