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