* Merged mts/sendmail functionality into mts/smtp; switching between
[mmh] / mts / smtp / smtp.c
1
2 /*
3  * smtp.c -- nmh SMTP interface
4  *
5  * $Id$
6  */
7
8 #include <h/mh.h>
9 #include "smtp.h"
10 #include <zotnet/mts/mts.h>
11 #include <signal.h>
12 #include <h/signals.h>
13 #ifdef MPOP
14 #include <errno.h>
15 #endif
16
17 /*
18  * This module implements an interface to SendMail very similar
19  * to the MMDF mm_(3) routines.  The sm_() routines herein talk
20  * SMTP to a sendmail process, mapping SMTP reply codes into
21  * RP_-style codes.
22  */
23
24 /*
25  * On older 4.2BSD machines without the POSIX function `sigaction',
26  * the alarm handing stuff for time-outs will NOT work due to the way
27  * syscalls get restarted.  This is not really crucial, since SendMail
28  * is generally well-behaved in this area.
29  */
30
31 #ifdef SENDMAILBUG
32 /*
33  * It appears that some versions of Sendmail will return Code 451
34  * when they don't really want to indicate a failure.
35  * "Code 451 almost always means sendmail has deferred; we don't
36  * really want bomb out at this point since sendmail will rectify
37  * things later."  So, if you define SENDMAILBUG, Code 451 is
38  * considered the same as Code 250.  Yuck!
39  */
40 #endif
41
42 #define TRUE    1
43 #define FALSE   0
44
45 #define NBITS ((sizeof (int)) * 8)
46
47 /*
48  * these codes must all be different!
49  */
50 #define SM_OPEN  90      /* Changed from 30 in case of nameserver flakiness */
51 #define SM_HELO  20
52 #define SM_RSET  15
53 #define SM_MAIL  40
54 #define SM_RCPT 120
55 #define SM_DATA  20
56 #define SM_TEXT 150
57 #define SM_DOT  180
58 #define SM_QUIT  30
59 #define SM_CLOS  10
60
61 static int sm_addrs = 0;
62 static int sm_alarmed = 0;
63 static int sm_child = NOTOK;
64 static int sm_debug = 0;
65 static int sm_nl = TRUE;
66 static int sm_verbose = 0;
67
68 static FILE *sm_rfp = NULL;
69 static FILE *sm_wfp = NULL;
70
71 #ifdef MPOP
72 static int sm_ispool = 0;
73 static char sm_tmpfil[BUFSIZ];
74 #endif /* MPOP */
75
76 static char *sm_noreply = "No reply text given";
77 static char *sm_moreply = "; ";
78
79 struct smtp sm_reply;           /* global... */
80
81 #define MAXEHLO 20
82
83 static int doingEHLO;
84 char *EHLOkeys[MAXEHLO + 1];
85
86 /*
87  * static prototypes
88  */
89 static int smtp_init (char *, char *, int, int, int, int, int);
90 static int sendmail_init (char *, char *, int, int, int, int, int);
91
92 static int rclient (char *, char *, char *);
93 static int sm_ierror (char *fmt, ...);
94 static int smtalk (int time, char *fmt, ...);
95 static int sm_wrecord (char *, int);
96 static int sm_wstream (char *, int);
97 static int sm_werror (void);
98 static int smhear (void);
99 static int sm_rrecord (char *, int *);
100 static int sm_rerror (void);
101 static RETSIGTYPE alrmser (int);
102 static char *EHLOset (char *);
103
104 #ifdef MPOP
105 /*
106  * smtp.c's own static copy of several nmh library subroutines
107  */
108 static char **smail_brkstring (char *, char *, char *);
109 static int smail_brkany (char, char *);
110 char **smail_copyip (char **, char **, int);
111 #endif
112
113 /* from zotnet/mts/client.c */
114 int client (char *, char *, char *, int, char *, int);
115
116 int
117 sm_init (char *client, char *server, int watch, int verbose,
118          int debug, int onex, int queued)
119 {
120     if (sm_mts == MTS_SMTP)
121         return smtp_init (client, server, watch, verbose,
122                           debug, onex, queued);
123     else
124         return sendmail_init (client, server, watch, verbose,
125                               debug, onex, queued);
126 }
127
128 static int
129 smtp_init (char *client, char *server, int watch, int verbose,
130            int debug, int onex, int queued)
131 {
132     int result, sd1, sd2;
133
134     if (watch)
135         verbose = TRUE;
136
137     sm_verbose = verbose;
138     sm_debug = debug;
139
140 #ifdef MPOP
141     if (sm_ispool)
142         goto all_done;
143 #endif
144
145     if (sm_rfp != NULL && sm_wfp != NULL)
146         goto send_options;
147
148     if (client == NULL || *client == '\0') {
149         if (clientname) {
150             client = clientname;
151         } else {
152             client = LocalName();       /* no clientname -> LocalName */
153         }
154     }
155
156 #ifdef ZMAILER
157     if (client == NULL || *client == '\0')
158         client = "localhost";
159 #endif
160
161     if ((sd1 = rclient (server, "tcp", "smtp")) == NOTOK)
162         return RP_BHST;
163
164 #ifdef MPOP
165     if (sm_ispool) {
166         if (sm_rfp) {
167             alarm (SM_CLOS);
168             fclose (sm_rfp);
169             alarm (0);
170             sm_rfp = NULL;
171         }
172         if ((sm_wfp = fdopen (sd1, "w")) == NULL) {
173             unlink (sm_tmpfil);
174             close (sd1);
175             return sm_ierror ("unable to fdopen");
176         }
177 all_done: ;
178         sm_reply.text[sm_reply.length = 0] = NULL;
179         return (sm_reply.code = RP_OK);
180     }
181 #endif /* MPOP */
182
183     if ((sd2 = dup (sd1)) == NOTOK) {
184         close (sd1);
185         return sm_ierror ("unable to dup");
186     }
187
188     SIGNAL (SIGALRM, alrmser);
189     SIGNAL (SIGPIPE, SIG_IGN);
190
191     if ((sm_rfp = fdopen (sd1, "r")) == NULL
192             || (sm_wfp = fdopen (sd2, "w")) == NULL) {
193         close (sd1);
194         close (sd2);
195         sm_rfp = sm_wfp = NULL;
196         return sm_ierror ("unable to fdopen");
197     }
198
199     sm_alarmed = 0;
200     alarm (SM_OPEN);
201     result = smhear ();
202     alarm (0);
203
204     switch (result) {
205         case 220: 
206             break;
207
208         default: 
209             sm_end (NOTOK);
210             return RP_RPLY;
211     }
212
213     /*
214      * Give EHLO or HELO command
215      */
216     if (client && *client) {
217         doingEHLO = 1;
218         result = smtalk (SM_HELO, "EHLO %s", client);
219         doingEHLO = 0;
220
221         if (result >= 500 && result <= 599)
222             result = smtalk (SM_HELO, "HELO %s", client);
223
224         if (result != 250) {
225             sm_end (NOTOK);
226             return RP_RPLY;
227         }
228     }
229
230 send_options: ;
231     if (watch && EHLOset ("XVRB"))
232         smtalk (SM_HELO, "VERB on");
233     if (onex && EHLOset ("XONE"))
234         smtalk (SM_HELO, "ONEX");
235     if (queued && EHLOset ("XQUE"))
236         smtalk (SM_HELO, "QUED");
237
238     return RP_OK;
239 }
240
241 int
242 sendmail_init (char *client, char *server, int watch, int verbose,
243                int debug, int onex, int queued)
244 {
245     int i, result, vecp;
246     int pdi[2], pdo[2];
247     char *vec[15];
248
249     if (watch)
250         verbose = TRUE;
251
252     sm_verbose = verbose;
253     sm_debug = debug;
254     if (sm_rfp != NULL && sm_wfp != NULL)
255         return RP_OK;
256
257     if (client == NULL || *client == '\0') {
258         if (clientname)
259             client = clientname;
260         else
261             client = LocalName();       /* no clientname -> LocalName */
262     }
263
264 #ifdef ZMAILER
265     if (client == NULL || *client == '\0')
266         client = "localhost";
267 #endif
268
269     if (pipe (pdi) == NOTOK)
270         return sm_ierror ("no pipes");
271     if (pipe (pdo) == NOTOK) {
272         close (pdi[0]);
273         close (pdi[1]);
274         return sm_ierror ("no pipes");
275     }
276
277     for (i = 0; (sm_child = fork ()) == NOTOK && i < 5; i++)
278         sleep (5);
279
280     switch (sm_child) {
281         case NOTOK: 
282             close (pdo[0]);
283             close (pdo[1]);
284             close (pdi[0]);
285             close (pdi[1]);
286             return sm_ierror ("unable to fork");
287
288         case OK: 
289             if (pdo[0] != fileno (stdin))
290                 dup2 (pdo[0], fileno (stdin));
291             if (pdi[1] != fileno (stdout))
292                 dup2 (pdi[1], fileno (stdout));
293             if (pdi[1] != fileno (stderr))
294                 dup2 (pdi[1], fileno (stderr));
295             for (i = fileno (stderr) + 1; i < NBITS; i++)
296                 close (i);
297
298             vecp = 0;
299             vec[vecp++] = r1bindex (sendmail, '/');
300             vec[vecp++] = "-bs";
301 #ifndef ZMAILER
302             vec[vecp++] = watch ? "-odi" : queued ? "-odq" : "-odb";
303             vec[vecp++] = "-oem";
304             vec[vecp++] = "-om";
305 # ifndef RAND
306             if (verbose)
307                 vec[vecp++] = "-ov";
308 # endif /* not RAND */
309 #endif /* not ZMAILER */
310             vec[vecp++] = NULL;
311
312             setgid (getegid ());
313             setuid (geteuid ());
314             execvp (sendmail, vec);
315             fprintf (stderr, "unable to exec ");
316             perror (sendmail);
317             _exit (-1);         /* NOTREACHED */
318
319         default: 
320             SIGNAL (SIGALRM, alrmser);
321             SIGNAL (SIGPIPE, SIG_IGN);
322
323             close (pdi[1]);
324             close (pdo[0]);
325             if ((sm_rfp = fdopen (pdi[0], "r")) == NULL
326                     || (sm_wfp = fdopen (pdo[1], "w")) == NULL) {
327                 close (pdi[0]);
328                 close (pdo[1]);
329                 sm_rfp = sm_wfp = NULL;
330                 return sm_ierror ("unable to fdopen");
331             }
332             sm_alarmed = 0;
333             alarm (SM_OPEN);
334             result = smhear ();
335             alarm (0);
336             switch (result) {
337                 case 220: 
338                     break;
339
340                 default: 
341                     sm_end (NOTOK);
342                     return RP_RPLY;
343             }
344
345             if (client && *client) {
346                 doingEHLO = 1;
347                 result = smtalk (SM_HELO, "EHLO %s", client);
348                 doingEHLO = 0;
349
350                 if (500 <= result && result <= 599)
351                     result = smtalk (SM_HELO, "HELO %s", client);
352
353                 switch (result) {
354                     case 250:
355                         break;
356
357                     default:
358                         sm_end (NOTOK);
359                         return RP_RPLY;
360                 }
361             }
362
363 #ifndef ZMAILER
364             if (onex)
365                 smtalk (SM_HELO, "ONEX");
366 #endif
367             if (watch)
368                 smtalk (SM_HELO, "VERB on");
369
370             return RP_OK;
371     }
372 }
373
374 #ifdef MPOP
375 # define MAXARGS  1000
376 #endif /* MPOP */
377
378 static int
379 rclient (char *server, char *protocol, char *service)
380 {
381     int sd;
382     char response[BUFSIZ];
383 #ifdef MPOP
384     char *cp;
385 #endif /* MPOP */
386
387     if ((sd = client (server, protocol, service, FALSE, response, sizeof(response))) != NOTOK)
388         return sd;
389
390 #ifdef MPOP
391     if (!server && servers && (cp = strchr(servers, '/'))) {
392         char **ap;
393         char *arguments[MAXARGS];
394
395         smail_copyip (smail_brkstring (cp = getcpy (servers), " ", "\n"), arguments, MAXARGS);
396
397         for (ap = arguments; *ap; ap++)
398             if (**ap == '/') {
399                 char *dp;
400
401                 if ((dp = strrchr(*ap, '/')) && *++dp == NULL)
402                     *--dp = NULL;
403                 snprintf (sm_tmpfil, sizeof(sm_tmpfil), "%s/smtpXXXXXX", *ap);
404 #ifdef HAVE_MKSTEMP
405                 sd = mkstemp (sm_tmpfil);
406 #else
407                 mktemp (sm_tmpfil);
408
409                 if ((sd = creat (sm_tmpfil, 0600)) != NOTOK) {
410                     sm_ispool = 1;
411                     break;
412                 }
413 #endif
414             }
415
416         free (cp);
417         if (sd != NOTOK)
418             return sd;
419     }
420 #endif /* MPOP */
421
422     sm_ierror ("%s", response);
423     return NOTOK;
424 }
425
426
427 int
428 sm_winit (int mode, char *from)
429 {
430     char *smtpcom;
431
432 #ifdef MPOP
433     if (sm_ispool && !sm_wfp) {
434         strlen (strcpy (sm_reply.text, "unable to create new spool file"));
435         sm_reply.code = NOTOK;
436         return RP_BHST;
437     }
438 #endif /* MPOP */
439
440     switch (mode) {
441         case S_MAIL:
442             smtpcom = "MAIL";
443             break;
444
445         case S_SEND:
446             smtpcom = "SEND";
447             break;
448
449         case S_SOML:
450             smtpcom = "SOML";
451             break;
452
453         case S_SAML:
454             smtpcom = "SAML";
455             break;
456     }
457
458     switch (smtalk (SM_MAIL, "%s FROM:<%s>", smtpcom, from)) {
459         case 250: 
460             sm_addrs = 0;
461             return RP_OK;
462
463         case 500: 
464         case 501: 
465         case 552: 
466             return RP_PARM;
467
468         default: 
469             return RP_RPLY;
470     }
471 }
472
473
474 int
475 sm_wadr (char *mbox, char *host, char *path)
476 {
477     switch (smtalk (SM_RCPT, host && *host ? "RCPT TO:<%s%s@%s>"
478                                            : "RCPT TO:<%s%s>",
479                              path ? path : "", mbox, host)) {
480         case 250: 
481         case 251: 
482             sm_addrs++;
483             return RP_OK;
484
485         case 451: 
486 #ifdef SENDMAILBUG
487             sm_addrs++;
488             return RP_OK;
489 #endif /* SENDMAILBUG */
490         case 421: 
491         case 450: 
492         case 452: 
493             return RP_NO;
494
495         case 500: 
496         case 501: 
497             return RP_PARM;
498
499         case 550: 
500         case 551: 
501         case 552: 
502         case 553: 
503             return RP_USER;
504
505         default: 
506             return RP_RPLY;
507     }
508 }
509
510
511 int
512 sm_waend (void)
513 {
514     switch (smtalk (SM_DATA, "DATA")) {
515         case 354: 
516             sm_nl = TRUE;
517             return RP_OK;
518
519         case 451: 
520 #ifdef SENDMAILBUG
521             sm_nl = TRUE;
522             return RP_OK;
523 #endif /* SENDMAILBUG */
524         case 421: 
525             return RP_NO;
526
527         case 500: 
528         case 501: 
529         case 503: 
530         case 554: 
531             return RP_NDEL;
532
533         default: 
534             return RP_RPLY;
535     }
536 }
537
538
539 int
540 sm_wtxt (char *buffer, int len)
541 {
542     int result;
543
544     sm_alarmed = 0;
545     alarm (SM_TEXT);
546     result = sm_wstream (buffer, len);
547     alarm (0);
548
549     return (result == NOTOK ? RP_BHST : RP_OK);
550 }
551
552
553 int
554 sm_wtend (void)
555 {
556     if (sm_wstream ((char *) NULL, 0) == NOTOK)
557         return RP_BHST;
558
559     switch (smtalk (SM_DOT + 3 * sm_addrs, ".")) {
560         case 250: 
561         case 251: 
562             return RP_OK;
563
564         case 451: 
565 #ifdef SENDMAILBUG
566             return RP_OK;
567 #endif /* SENDMAILBUG */
568         case 452: 
569         default: 
570             return RP_NO;
571
572         case 552: 
573         case 554: 
574             return RP_NDEL;
575     }
576 }
577
578
579 int
580 sm_end (int type)
581 {
582     int status;
583     struct smtp sm_note;
584
585     if (sm_mts == MTS_SENDMAIL) {
586         switch (sm_child) {
587             case NOTOK: 
588             case OK: 
589                 return RP_OK;
590
591             default: 
592                 break;
593         }
594     }
595
596     if (sm_rfp == NULL && sm_wfp == NULL)
597         return RP_OK;
598
599     switch (type) {
600         case OK: 
601             smtalk (SM_QUIT, "QUIT");
602             break;
603
604         case NOTOK: 
605             sm_note.code = sm_reply.code;
606             strncpy (sm_note.text, sm_reply.text, sm_note.length = sm_reply.length);/* fall */
607         case DONE: 
608             if (smtalk (SM_RSET, "RSET") == 250 && type == DONE)
609                 return RP_OK;
610             if (sm_mts == MTS_SMTP)
611                 smtalk (SM_QUIT, "QUIT");
612             else {
613                 kill (sm_child, SIGKILL);
614                 discard (sm_rfp);
615                 discard (sm_wfp);
616             }
617             if (type == NOTOK) {
618                 sm_reply.code = sm_note.code;
619                 strncpy (sm_reply.text, sm_note.text, sm_reply.length = sm_note.length);
620             }
621             break;
622     }
623
624 #ifdef MPOP
625     if (sm_ispool) {
626         sm_ispool = 0;
627
628         if (sm_wfp) {
629             unlink (sm_tmpfil);
630             fclose (sm_wfp);
631             sm_wfp = NULL;
632         }
633     }
634 #endif /* MPOP */
635
636     if (sm_rfp != NULL) {
637         alarm (SM_CLOS);
638         fclose (sm_rfp);
639         alarm (0);
640     }
641     if (sm_wfp != NULL) {
642         alarm (SM_CLOS);
643         fclose (sm_wfp);
644         alarm (0);
645     }
646
647     if (sm_mts == MTS_SMTP)
648         status = 0;
649     else {
650         status = pidwait (sm_child, OK);
651         sm_child = NOTOK;
652     }
653
654     sm_rfp = sm_wfp = NULL;
655     return (status ? RP_BHST : RP_OK);
656 }
657
658
659 #ifdef MPOP
660
661 int
662 sm_bulk (char *file)
663 {
664     int cc, i, j, k, result;
665     long pos;
666     char *dp, *bp, *cp, s;
667     char buffer[BUFSIZ], sender[BUFSIZ];
668     FILE *fp, *gp;
669
670     gp = NULL;
671     k = strlen (file) - sizeof(".bulk");
672     if ((fp = fopen (file, "r")) == NULL) {
673         int len;
674
675         snprintf (sm_reply.text, sizeof(sm_reply.text),
676                 "unable to read %s: ", file);
677         bp = sm_reply.text;
678         len = strlen (bp);
679         bp += len;
680         if ((s = strerror (errno)))
681             strncpy (bp, s, sizeof(sm_reply.text) - len);
682         else
683             snprintf (bp, sizeof(sm_reply.text) - len, "Error %d", errno);
684         sm_reply.length = strlen (sm_reply.text);
685         sm_reply.code = NOTOK;
686         return RP_BHST;
687     }
688     if (sm_debug) {
689         printf ("reading file %s\n", file);
690         fflush (stdout);
691     }
692
693     i = j = 0;
694     while (fgets (buffer, sizeof(buffer), fp)) {
695         if (j++ == 0)
696             strncpy (sender, buffer + sizeof("MAIL FROM:") - 1, sizeof(sender));
697         if (strcmp (buffer, "DATA\r\n") == 0) {
698             i = 1;
699             break;
700         }
701     }
702     if (i == 0) {
703         if (sm_debug) {
704             printf ("no DATA...\n");
705             fflush (stdout);
706         }
707 losing0:
708         snprintf (buffer, sizeof(buffer), "%s.bad", file);
709         rename (file, buffer);
710         if (gp) {
711             snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
712             unlink (buffer);
713             fclose (gp);
714         }
715         fclose (fp);
716         return RP_OK;
717     }
718     if (j < 3) {
719         if (sm_debug) {
720             printf ("no %srecipients...\n", j < 1 ? "sender or " : "");
721             fflush (stdout);
722         }
723         goto losing0;
724     }
725
726     if ((cp = malloc ((size_t) (cc = (pos = ftell (fp)) + 1))) == NULL) {
727         sm_reply.length = strlen (strcpy (sm_reply.text, "out of memory"));
728 losing1: ;
729         sm_reply.code = NOTOK;
730         fclose (fp);
731         return RP_BHST;
732     }
733     fseek (fp, 0L, SEEK_SET);
734     for (dp = cp, i = 0; i++ < j; dp += strlen (dp))
735         if (fgets (dp, cc - (dp - cp), fp) == NULL) {
736             sm_reply.length = strlen (strcpy (sm_reply.text, "premature eof"));
737 losing2:
738             free (cp);
739             goto losing1;
740         }
741     *dp = NULL;
742
743     for (dp = cp, i = cc - 1; i > 0; dp += cc, i -= cc)
744         if ((cc = write (fileno (sm_wfp), dp, i)) == NOTOK) {
745             int len;
746 losing3:
747             strcpy (sm_reply.text, "error writing to server: ",
748                 sizeof(sm_reply.text));
749             bp = sm_reply.text;
750             len = strlen (bp);
751             bp += len;
752             if ((s = strerror (errno)))
753                 strncpy (bp, s, sizeof(sm_reply.text) - len);
754             else
755                 snprintf (bp, sizeof(sm_reply.text) - len,
756                         "unknown error %d", errno);
757             sm_reply.length = strlen (sm_reply.text);
758             goto losing2;
759         }
760         else
761             if (sm_debug) {
762                 printf ("wrote %d octets to server\n", cc);
763                 fflush (stdout);
764             }
765
766     for (dp = cp, i = 0; i++ < j; dp = strchr(dp, '\n'), dp++) {
767         if (sm_debug) {
768             if (bp = strchr(dp, '\r'))
769                 *bp = NULL;
770             printf ("=> %s\n", dp);
771             fflush (stdout);
772             if (bp)
773                 *bp = '\r';
774         }
775
776         switch (smhear () + (i == 1 ? 1000 : i != j ? 2000 : 3000)) {
777             case 1000 + 250:
778                 sm_addrs = 0;
779                 result = RP_OK;
780                 break;
781
782             case 1000 + 500: 
783             case 1000 + 501: 
784             case 1000 + 552: 
785             case 2000 + 500: 
786             case 2000 + 501:
787                 result = RP_PARM;
788                 smtalk (SM_RSET, "RSET");
789                 free (cp);
790                 goto losing0;
791
792             case 2000 + 250:
793             case 2000 + 251:
794                 sm_addrs++;
795                 result = RP_OK;
796                 break;
797
798             case 2000 + 451: 
799 #ifdef SENDMAILBUG
800                 sm_addrs++;
801                 result = RP_OK;
802                 break;
803 #endif
804             case 2000 + 421: 
805             case 2000 + 450: 
806             case 2000 + 452: 
807                 result = RP_NO;
808                 goto bad_addr;
809
810             case 2000 + 550: 
811             case 2000 + 551: 
812             case 2000 + 552: 
813             case 2000 + 553: 
814                 result = RP_USER;
815 bad_addr:
816                 if (k <= 0 || strcmp (sender, "<>\r\n") == 0)
817                     break;
818                 if (gp == NULL) {
819                     int     l;
820                     snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
821                     if ((gp = fopen (buffer, "w+")) == NULL)
822                         goto bad_data;
823                     fprintf (gp, "MAIL FROM:<>\r\nRCPT TO:%sDATA\r\n", sender);
824                     l = strlen (sender);
825                     fprintf (gp,
826                              "To: %*.*s\r\nSubject: Invalid addresses (%s)\r\n",
827                              l - 4, l - 4, sender + 1, file);
828                     fprintf (gp, "Date: %s\r\nFrom: Postmaster@%s\r\n\r\n",
829                              dtimenow (0), LocalName ());
830                 }
831                 if (bp = strchr(dp, '\r'))
832                     *bp = NULL;
833                 fprintf (gp, "=>        %s\r\n", dp);
834                 if (bp)
835                     *bp = '\r';
836                 fprintf (gp, "<= %s\r\n", rp_string (result));
837                 fflush (gp);
838                 break;
839
840             case 3000 + 354: 
841 #ifdef SENDMAILBUG
842 ok_data:
843 #endif
844                 result = RP_OK;
845                 break;
846
847             case 3000 + 451: 
848 #ifdef SENDMAILBUG
849                 goto ok_data;
850 #endif
851             case 3000 + 421:
852                 result = RP_NO;
853 bad_data:
854                 smtalk (SM_RSET, "RSET");
855                 free (cp);
856                 if (gp) {
857                     snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
858                     unlink (buffer);
859                     fclose (gp);
860                 }
861                 fclose (fp);
862                 return result;
863
864             case 3000 + 500: 
865             case 3000 + 501: 
866             case 3000 + 503: 
867             case 3000 + 554: 
868                 smtalk (SM_RSET, "RSET");
869                 free (cp);
870                 goto no_dice;
871
872             default:
873                 result = RP_RPLY;
874                 goto bad_data;
875         }
876     }
877     free (cp);
878
879     {
880 #ifdef HAVE_ST_BLKSIZE
881         struct stat st;
882
883         if (fstat (fileno (sm_wfp), &st) == NOTOK || (cc = st.st_blksize) < BUFSIZ)
884             cc = BUFSIZ;
885 #else
886         cc = BUFSIZ;
887 #endif
888         if ((cp = malloc ((size_t) cc)) == NULL) {
889             smtalk (SM_RSET, "RSET");
890             sm_reply.length = strlen (strcpy (sm_reply.text, "out of memory"));
891             goto losing1;
892         }
893     }
894
895     fseek (fp, pos, SEEK_SET);
896     for (;;) {
897         int eof = 0;
898
899         for (dp = cp, i = cc; i > 0; dp += j, i -= j)
900             if ((j = fread (cp, sizeof(*cp), i, fp)) == OK) {
901                 if (ferror (fp)) {
902                     int len;
903
904                     snprintf (sm_reply.text, sizeof(sm_reply.text),
905                         "error reading %s: ", file);
906                     bp = sm_reply.text;
907                     len = strlen (bp);
908                     bp += len;
909                     if ((s = strerror (errno)))
910                         strncpy (bp, s, sizeof(sm_reply.text) - len);
911                     else
912                         snprintf (bp, sizeof(sm_reply.text) - len,
913                                 "unknown error %d", errno);
914                     sm_reply.length = strlen (sm_reply.text);
915                     goto losing2;
916                 }
917                 cc = dp - cp;
918                 eof = 1;
919                 break;
920             }
921
922         for (dp = cp, i = cc; i > 0; dp += j, i -= j)
923             if ((j = write (fileno (sm_wfp), dp, i)) == NOTOK)
924                 goto losing3;
925             else
926                 if (sm_debug) {
927                     printf ("wrote %d octets to server\n", j);
928                     fflush (stdout);
929                 }
930
931         if (eof)
932             break;
933     }
934     free (cp);
935
936     switch (smhear ()) {
937         case 250: 
938         case 251: 
939 #ifdef SENDMAILBUG
940 ok_dot:
941 #endif
942             result = RP_OK;
943             unlink (file);
944             break;
945
946         case 451: 
947 #ifdef SENDMAILBUG
948             goto ok_dot;
949 #endif
950         case 452: 
951         default: 
952             result = RP_NO;
953             if (gp) {
954                 snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
955                 unlink (buffer);
956                 fclose (gp);
957                 gp = NULL;
958             }
959             break;
960
961         case 552: 
962         case 554: 
963 no_dice:
964             result = RP_NDEL;
965             if (k <= 0 || strcmp (sender, "<>\r\n") == 0) {
966                 unlink (file);
967                 break;
968             }
969             if (gp) {
970                 fflush (gp);
971                 ftruncate (fileno (gp), 0L);
972                 fseek (gp, 0L, SEEK_SET);
973             }
974             else {
975                 snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
976                 if ((gp = fopen (buffer, "w")) == NULL)
977                     break;
978             }
979             fprintf (gp, "MAIL FROM:<>\r\nRCPT TO:%sDATA\r\n", sender);
980             i = strlen (sender);
981             fprintf (gp, "To: %*.*s\r\nSubject: Failed mail (%s)\r\n",
982                      i - 4, i - 4, sender + 1, file);
983             fprintf (gp, "Date: %s\r\nFrom: Postmaster@%s\r\n\r\n",
984                      dtimenow (0), LocalName ());
985             break;
986     }
987
988     if (gp) {
989         fputs ("\r\n------- Begin Returned message\r\n\r\n", gp);
990         fseek (fp, pos, SEEK_SET);
991         while (fgets (buffer, sizeof(buffer), fp)) {
992             if (buffer[0] == '-')
993                 fputs ("- ", gp);
994             if (strcmp (buffer, ".\r\n"))
995                 fputs (buffer, gp);
996         }
997         fputs ("\r\n------- End Returned Message\r\n\r\n.\r\n", gp);
998         fflush (gp);
999         if (!ferror (gp))
1000             unlink (file);
1001         fclose (gp);
1002     }
1003     fclose (fp);
1004
1005     return result;
1006 }
1007 #endif /* MPOP */
1008
1009
1010 static int
1011 sm_ierror (char *fmt, ...)
1012 {
1013     va_list ap;
1014
1015     va_start(ap, fmt);
1016     vsnprintf (sm_reply.text, sizeof(sm_reply.text), fmt, ap);
1017     va_end(ap);
1018
1019     sm_reply.length = strlen (sm_reply.text);
1020     sm_reply.code = NOTOK;
1021
1022     return RP_BHST;
1023 }
1024
1025
1026 static int
1027 smtalk (int time, char *fmt, ...)
1028 {
1029     va_list ap;
1030     int result;
1031     char buffer[BUFSIZ];
1032
1033     va_start(ap, fmt);
1034     vsnprintf (buffer, sizeof(buffer), fmt, ap);
1035     va_end(ap);
1036
1037     if (sm_debug) {
1038         printf ("=> %s\n", buffer);
1039         fflush (stdout);
1040     }
1041
1042 #ifdef MPOP
1043     if (sm_ispool) {
1044         char file[BUFSIZ];
1045
1046         if (strcmp (buffer, ".") == 0)
1047             time = SM_DOT;
1048         fprintf (sm_wfp, "%s\r\n", buffer);
1049         switch (time) {
1050             case SM_DOT:
1051                 fflush (sm_wfp);
1052                 if (ferror (sm_wfp))
1053                     return sm_werror ();
1054                 snprintf (file, sizeof(file), "%s%c.bulk", sm_tmpfil,
1055                                 (char) (sm_ispool + 'a' - 1));
1056                 if (rename (sm_tmpfil, file) == NOTOK) {
1057                     int len;
1058                     char *bp;
1059
1060                     snprintf (sm_reply.text, sizeof(sm_reply.text),
1061                         "error renaming %s to %s: ", sm_tmpfil, file);
1062                     bp = sm_reply.text;
1063                     len = strlen (bp);
1064                     bp += len;
1065                     if ((s = strerror (errno)))
1066                         strncpy (bp, s, sizeof(sm_reply.text) - len);
1067                     else
1068                         snprintf (bp, sizeof(sm_reply.text) - len,
1069                                 "unknown error %d", errno);
1070                     sm_reply.length = strlen (sm_reply.text);
1071                     sm_reply.code = NOTOK;
1072                     return RP_BHST;
1073                 }
1074                 fclose (sm_wfp);
1075                 if (sm_wfp = fopen (sm_tmpfil, "w"))
1076                     chmod (sm_tmpfil, 0600);
1077                 sm_ispool++;
1078                 /* and fall... */
1079
1080             case SM_MAIL:
1081             case SM_RCPT:
1082                 result = 250;
1083                 break;
1084
1085             case SM_RSET:
1086                 fflush (sm_wfp);
1087                 ftruncate (fileno (sm_wfp), 0L);
1088                 fseek (sm_wfp, 0L, SEEK_SET);
1089                 result = 250;
1090                 break;
1091
1092             case SM_DATA:
1093                 result = 354;
1094                 break;
1095
1096             case SM_QUIT:
1097                 unlink (sm_tmpfil);
1098                 sm_ispool = 0;
1099                 result = 221;
1100                 break;
1101
1102             default:
1103                 result = 500;
1104                 break;
1105         }
1106         if (sm_debug) {
1107             printf ("<= %d\n", result);
1108             fflush (stdout);
1109         }
1110
1111         sm_reply.text[sm_reply.length = 0] = NULL;
1112         return (sm_reply.code = result);
1113     }
1114 #endif /* MPOP */
1115
1116     sm_alarmed = 0;
1117     alarm ((unsigned) time);
1118     if ((result = sm_wrecord (buffer, strlen (buffer))) != NOTOK)
1119         result = smhear ();
1120     alarm (0);
1121
1122     return result;
1123 }
1124
1125
1126 /*
1127  * write the buffer to the open SMTP channel
1128  */
1129
1130 static int
1131 sm_wrecord (char *buffer, int len)
1132 {
1133     if (sm_wfp == NULL)
1134         return sm_werror ();
1135
1136     fwrite (buffer, sizeof(*buffer), len, sm_wfp);
1137     fputs ("\r\n", sm_wfp);
1138     fflush (sm_wfp);
1139
1140     return (ferror (sm_wfp) ? sm_werror () : OK);
1141 }
1142
1143
1144 static int
1145 sm_wstream (char *buffer, int len)
1146 {
1147     char  *bp;
1148     static char lc = '\0';
1149
1150     if (sm_wfp == NULL)
1151         return sm_werror ();
1152
1153     if (buffer == NULL && len == 0) {
1154         if (lc != '\n')
1155             fputs ("\r\n", sm_wfp);
1156         lc = '\0';
1157         return (ferror (sm_wfp) ? sm_werror () : OK);
1158     }
1159
1160     for (bp = buffer; len > 0; bp++, len--) {
1161         switch (*bp) {
1162             case '\n': 
1163                 sm_nl = TRUE;
1164                 fputc ('\r', sm_wfp);
1165                 break;
1166
1167             case '.': 
1168                 if (sm_nl)
1169                     fputc ('.', sm_wfp);/* FALL THROUGH */
1170             default: 
1171                 sm_nl = FALSE;
1172         }
1173         fputc (*bp, sm_wfp);
1174         if (ferror (sm_wfp))
1175             return sm_werror ();
1176     }
1177
1178     if (bp > buffer)
1179         lc = *--bp;
1180     return (ferror (sm_wfp) ? sm_werror () : OK);
1181 }
1182
1183
1184 /*
1185  * On some systems, strlen and strcpy are defined as preprocessor macros.  This
1186  * causes compile problems with the #ifdef MPOP in the middle.  Should the
1187  * #ifdef MPOP be removed, remove these #undefs.
1188  */
1189 #ifdef strlen
1190 # undef strlen
1191 #endif
1192 #ifdef strcpy
1193 # undef strcpy
1194 #endif
1195
1196 static int
1197 sm_werror (void)
1198 {
1199     sm_reply.length =
1200         strlen (strcpy (sm_reply.text, sm_wfp == NULL ? "no socket opened"
1201             : sm_alarmed ? "write to socket timed out"
1202 #ifdef MPOP
1203             : sm_ispool ? "error writing to spool file"
1204 #endif
1205             : "error writing to socket"));
1206
1207     return (sm_reply.code = NOTOK);
1208 }
1209
1210
1211 static int
1212 smhear (void)
1213 {
1214     int i, code, cont, bc, rc, more;
1215     char *bp, *rp;
1216     char **ehlo, buffer[BUFSIZ];
1217
1218     if (doingEHLO) {
1219         static int at_least_once = 0;
1220
1221         if (at_least_once) {
1222             char *ep;
1223
1224             for (ehlo = EHLOkeys; *ehlo; ehlo++) {
1225                 ep = *ehlo;
1226                 free (ep);
1227             }
1228         } else {
1229             at_least_once = 1;
1230         }
1231
1232         ehlo = EHLOkeys;
1233         *ehlo = NULL;
1234     }
1235
1236 again: ;
1237
1238     sm_reply.length = 0;
1239     sm_reply.text[0] = 0;
1240     rp = sm_reply.text;
1241     rc = sizeof(sm_reply.text) - 1;
1242
1243     for (more = FALSE; sm_rrecord (bp = buffer, &bc) != NOTOK;) {
1244         if (sm_debug) {
1245             printf ("<= %s\n", buffer);
1246             fflush (stdout);
1247         }
1248
1249         if (doingEHLO
1250                 && strncmp (buffer, "250", sizeof("250") - 1) == 0
1251                 && (buffer[3] == '-' || doingEHLO == 2)
1252                 && buffer[4]) {
1253             if (doingEHLO == 2) {
1254                 if ((*ehlo = malloc ((size_t) (strlen (buffer + 4) + 1)))) {
1255                     strcpy (*ehlo++, buffer + 4);
1256                     *ehlo = NULL;
1257                     if (ehlo >= EHLOkeys + MAXEHLO)
1258                         doingEHLO = 0;
1259                 }
1260                 else
1261                     doingEHLO = 0;
1262             }
1263             else
1264                 doingEHLO = 2;
1265         }
1266
1267         for (; bc > 0 && (!isascii (*bp) || !isdigit (*bp)); bp++, bc--)
1268             continue;
1269
1270         cont = FALSE;
1271         code = atoi (bp);
1272         bp += 3, bc -= 3;
1273         for (; bc > 0 && isspace (*bp); bp++, bc--)
1274             continue;
1275         if (bc > 0 && *bp == '-') {
1276             cont = TRUE;
1277             bp++, bc--;
1278             for (; bc > 0 && isspace (*bp); bp++, bc--)
1279                 continue;
1280         }
1281
1282         if (more) {
1283             if (code != sm_reply.code || cont)
1284                 continue;
1285             more = FALSE;
1286         } else {
1287             sm_reply.code = code;
1288             more = cont;
1289             if (bc <= 0) {
1290                 strncpy (buffer, sm_noreply, sizeof(buffer));
1291                 bp = buffer;
1292                 bc = strlen (sm_noreply);
1293             }
1294         }
1295
1296         if ((i = min (bc, rc)) > 0) {
1297             strncpy (rp, bp, i);
1298             rp += i;
1299             rc -= i;
1300             if (more && rc > strlen (sm_moreply) + 1) {
1301                 strncpy (sm_reply.text + rc, sm_moreply, sizeof(sm_reply.text) - rc);
1302                 rc += strlen (sm_moreply);
1303             }
1304         }
1305         if (more)
1306             continue;
1307         if (sm_reply.code < 100) {
1308             if (sm_verbose) {
1309                 printf ("%s\n", sm_reply.text);
1310                 fflush (stdout);
1311             }
1312             goto again;
1313         }
1314
1315         sm_reply.length = rp - sm_reply.text;
1316         return sm_reply.code;
1317     }
1318     return NOTOK;
1319 }
1320
1321
1322 static int
1323 sm_rrecord (char *buffer, int *len)
1324 {
1325     if (sm_rfp == NULL)
1326         return sm_rerror ();
1327
1328     buffer[*len = 0] = 0;
1329
1330     fgets (buffer, BUFSIZ, sm_rfp);
1331     *len = strlen (buffer);
1332     if (ferror (sm_rfp) || feof (sm_rfp))
1333         return sm_rerror ();
1334     if (buffer[*len - 1] != '\n')
1335         while (getc (sm_rfp) != '\n' && !ferror (sm_rfp) && !feof (sm_rfp))
1336             continue;
1337     else
1338         if (buffer[*len - 2] == '\r')
1339             *len -= 1;
1340     buffer[*len - 1] = 0;
1341
1342     return OK;
1343 }
1344
1345
1346 static int
1347 sm_rerror (void)
1348 {
1349     if (sm_mts == MTS_SMTP)
1350         sm_reply.length =
1351             strlen (strcpy (sm_reply.text, sm_rfp == NULL ? "no socket opened"
1352                 : sm_alarmed ? "read from socket timed out"
1353                 : feof (sm_rfp) ? "premature end-of-file on socket"
1354                 : "error reading from socket"));
1355     else
1356         sm_reply.length =
1357             strlen (strcpy (sm_reply.text, sm_rfp == NULL ? "no pipe opened"
1358                 : sm_alarmed ? "read from pipe timed out"
1359                 : feof (sm_rfp) ? "premature end-of-file on pipe"
1360                 : "error reading from pipe"));
1361
1362     return (sm_reply.code = NOTOK);
1363 }
1364
1365
1366 static RETSIGTYPE
1367 alrmser (int i)
1368 {
1369 #ifndef RELIABLE_SIGNALS
1370     SIGNAL (SIGALRM, alrmser);
1371 #endif
1372
1373     sm_alarmed++;
1374     if (sm_debug) {
1375         printf ("timed out...\n");
1376         fflush (stdout);
1377     }
1378 }
1379
1380
1381 char *
1382 rp_string (int code)
1383 {
1384     char *text;
1385     static char buffer[BUFSIZ];
1386
1387     switch (sm_reply.code != NOTOK ? code : NOTOK) {
1388         case RP_AOK:
1389             text = "AOK";
1390             break;
1391
1392         case RP_MOK:
1393             text = "MOK";
1394             break;
1395
1396         case RP_OK: 
1397             text = "OK";
1398             break;
1399
1400         case RP_RPLY: 
1401             text = "RPLY";
1402             break;
1403
1404         case RP_BHST: 
1405         default: 
1406             text = "BHST";
1407             snprintf (buffer, sizeof(buffer), "[%s] %s", text, sm_reply.text);
1408             return buffer;
1409
1410         case RP_PARM: 
1411             text = "PARM";
1412             break;
1413
1414         case RP_NO: 
1415             text = "NO";
1416             break;
1417
1418         case RP_USER: 
1419             text = "USER";
1420             break;
1421
1422         case RP_NDEL: 
1423             text = "NDEL";
1424             break;
1425     }
1426
1427     snprintf (buffer, sizeof(buffer), "[%s] %3d %s",
1428                 text, sm_reply.code, sm_reply.text);
1429     return buffer;
1430 }
1431
1432
1433 #ifdef MPOP
1434
1435 static char *broken[MAXARGS + 1];
1436
1437 static char **
1438 smail_brkstring (char *strg, char *brksep, char *brkterm)
1439 {
1440     int bi;
1441     char c, *sp;
1442
1443     sp = strg;
1444
1445     for (bi = 0; bi < MAXARGS; bi++) {
1446         while (smail_brkany (c = *sp, brksep))
1447             *sp++ = 0;
1448         if (!c || smail_brkany (c, brkterm)) {
1449             *sp = 0;
1450             broken[bi] = 0;
1451             return broken;
1452         }
1453
1454         broken[bi] = sp;
1455         while ((c = *++sp) && !smail_brkany (c, brksep) && !smail_brkany (c, brkterm))
1456             continue;
1457     }
1458     broken[MAXARGS] = 0;
1459
1460     return broken;
1461 }
1462
1463
1464 /*
1465  * returns 1 if chr in strg, 0 otherwise
1466  */
1467 static int
1468 smail_brkany (char chr, char *strg)
1469 {
1470     char *sp;
1471  
1472     if (strg)
1473         for (sp = strg; *sp; sp++)
1474             if (chr == *sp)
1475                 return 1;
1476     return 0;
1477 }
1478
1479 /*
1480  * copy a string array and return pointer to end
1481  */
1482 char **
1483 smail_copyip (char **p, char **q, int len_q)
1484 {
1485     while (*p && --len_q > 0)
1486         *q++ = *p++;
1487
1488     *q = NULL;
1489  
1490     return q;
1491 }
1492
1493 #endif /* MPOP */
1494
1495
1496 static char *
1497 EHLOset (char *s)
1498 {
1499     size_t len;
1500     char *ep, **ehlo;
1501
1502     len = strlen (s);
1503
1504     for (ehlo = EHLOkeys; *ehlo; ehlo++) {
1505         ep = *ehlo;
1506         if (strncmp (ep, s, len) == 0) {
1507             for (ep += len; *ep == ' '; ep++)
1508                 continue;
1509             return ep;
1510         }
1511     }
1512
1513     return 0;
1514 }