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