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