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