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