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