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