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