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