]> diplodocus.org Git - nmh/blob - mts/sendmail/sendmail.c
* I had alphabetized the --configure options in the --help output
[nmh] / mts / sendmail / sendmail.c
1
2 /*
3 * sendmail.c -- nmh sendmail interface
4 *
5 * $Id$
6 */
7
8 #include <h/mh.h>
9 #include <mts/smtp/smtp.h>
10 #include <zotnet/mts/mts.h>
11 #include <signal.h>
12 #include "h/signals.h" /* for SIGNAL() */
13 #ifdef MPOP
14 #include <errno.h>
15 #endif
16
17 /*
18 * This module implements an interface to SendMail very similar
19 * to the MMDF mm_(3) routines. The sm_() routines herein talk
20 * SMTP to a sendmail process, mapping SMTP reply codes into
21 * RP_-style codes.
22 */
23
24 /*
25 * On older 4.2BSD machines without the POSIX function `sigaction',
26 * the alarm handing stuff for time-outs will NOT work due to the way
27 * syscalls get restarted. This is not really crucial, since SendMail
28 * is generally well-behaved in this area.
29 */
30
31 #ifdef SENDMAILBUG
32 /*
33 * It appears that some versions of Sendmail will return Code 451
34 * when they don't really want to indicate a failure.
35 * "Code 451 almost always means sendmail has deferred; we don't
36 * really want bomb out at this point since sendmail will rectify
37 * things later." So, if you define SENDMAILBUG, Code 451 is
38 * considered the same as Code 250. Yuck!
39 */
40 #endif
41
42 #define TRUE 1
43 #define FALSE 0
44
45 #define NBITS ((sizeof (int)) * 8)
46
47 /*
48 * these codes must all be different!
49 */
50 #define SM_OPEN 90 /* Changed from 30 in case of nameserver flakiness */
51 #define SM_HELO 20
52 #define SM_RSET 15
53 #define SM_MAIL 40
54 #define SM_RCPT 120
55 #define SM_DATA 20
56 #define SM_TEXT 150
57 #define SM_DOT 180
58 #define SM_QUIT 30
59 #define SM_CLOS 10
60
61 static int sm_addrs = 0;
62 static int sm_alarmed = 0;
63 static int sm_child = NOTOK;
64 static int sm_debug = 0;
65 static int sm_nl = TRUE;
66 static int sm_verbose = 0;
67
68 static FILE *sm_rfp = NULL;
69 static FILE *sm_wfp = NULL;
70
71 #ifdef MPOP
72 static int sm_ispool = 0;
73 static char sm_tmpfil[BUFSIZ];
74 #endif /* MPOP */
75
76 static char *sm_noreply = "No reply text given";
77 static char *sm_moreply = "; ";
78
79 struct smtp sm_reply; /* global... */
80
81 static int doingEHLO;
82
83 #define MAXEHLO 20
84 char *EHLOkeys[MAXEHLO + 1];
85
86 /*
87 * static prototypes
88 */
89 static int sm_ierror (char *fmt, ...);
90 static int smtalk (int time, char *fmt, ...);
91 static int sm_wrecord (char *, int);
92 static int sm_wstream (char *, int);
93 static int sm_werror (void);
94 static int smhear (void);
95 static int sm_rrecord (char *, int *);
96 static int sm_rerror (void);
97 static RETSIGTYPE alrmser (int);
98
99
100 int
101 sm_init (char *client, char *server, int watch, int verbose,
102 int debug, int onex, int queued)
103 {
104 int i, result, vecp;
105 int pdi[2], pdo[2];
106 char *vec[15];
107
108 if (watch)
109 verbose = TRUE;
110
111 sm_verbose = verbose;
112 sm_debug = debug;
113 if (sm_rfp != NULL && sm_wfp != NULL)
114 return RP_OK;
115
116 if (client == NULL || *client == '\0') {
117 if (clientname)
118 client = clientname;
119 else
120 client = LocalName(); /* no clientname -> LocalName */
121 }
122
123 #ifdef ZMAILER
124 if (client == NULL || *client == '\0')
125 client = "localhost";
126 #endif
127
128 if (pipe (pdi) == NOTOK)
129 return sm_ierror ("no pipes");
130 if (pipe (pdo) == NOTOK) {
131 close (pdi[0]);
132 close (pdi[1]);
133 return sm_ierror ("no pipes");
134 }
135
136 for (i = 0; (sm_child = fork ()) == NOTOK && i < 5; i++)
137 sleep (5);
138
139 switch (sm_child) {
140 case NOTOK:
141 close (pdo[0]);
142 close (pdo[1]);
143 close (pdi[0]);
144 close (pdi[1]);
145 return sm_ierror ("unable to fork");
146
147 case OK:
148 if (pdo[0] != fileno (stdin))
149 dup2 (pdo[0], fileno (stdin));
150 if (pdi[1] != fileno (stdout))
151 dup2 (pdi[1], fileno (stdout));
152 if (pdi[1] != fileno (stderr))
153 dup2 (pdi[1], fileno (stderr));
154 for (i = fileno (stderr) + 1; i < NBITS; i++)
155 close (i);
156
157 vecp = 0;
158 vec[vecp++] = r1bindex (sendmail, '/');
159 vec[vecp++] = "-bs";
160 #ifndef ZMAILER
161 vec[vecp++] = watch ? "-odi" : queued ? "-odq" : "-odb";
162 vec[vecp++] = "-oem";
163 vec[vecp++] = "-om";
164 # ifndef RAND
165 if (verbose)
166 vec[vecp++] = "-ov";
167 # endif /* not RAND */
168 #endif /* not ZMAILER */
169 vec[vecp++] = NULL;
170
171 setgid (getegid ());
172 setuid (geteuid ());
173 execvp (sendmail, vec);
174 fprintf (stderr, "unable to exec ");
175 perror (sendmail);
176 _exit (-1); /* NOTREACHED */
177
178 default:
179 SIGNAL (SIGALRM, alrmser);
180 SIGNAL (SIGPIPE, SIG_IGN);
181
182 close (pdi[1]);
183 close (pdo[0]);
184 if ((sm_rfp = fdopen (pdi[0], "r")) == NULL
185 || (sm_wfp = fdopen (pdo[1], "w")) == NULL) {
186 close (pdi[0]);
187 close (pdo[1]);
188 sm_rfp = sm_wfp = NULL;
189 return sm_ierror ("unable to fdopen");
190 }
191 sm_alarmed = 0;
192 alarm (SM_OPEN);
193 result = smhear ();
194 alarm (0);
195 switch (result) {
196 case 220:
197 break;
198
199 default:
200 sm_end (NOTOK);
201 return RP_RPLY;
202 }
203
204 if (client && *client) {
205 doingEHLO = 1;
206 result = smtalk (SM_HELO, "EHLO %s", client);
207 doingEHLO = 0;
208
209 if (500 <= result && result <= 599)
210 result = smtalk (SM_HELO, "HELO %s", client);
211
212 switch (result) {
213 case 250:
214 break;
215
216 default:
217 sm_end (NOTOK);
218 return RP_RPLY;
219 }
220 }
221
222 #ifndef ZMAILER
223 if (onex)
224 smtalk (SM_HELO, "ONEX");
225 #endif
226 if (watch)
227 smtalk (SM_HELO, "VERB on");
228
229 return RP_OK;
230 }
231 }
232
233
234 int
235 sm_winit (int mode, char *from)
236 {
237 #ifdef MPOP
238 if (sm_ispool && !sm_wfp) {
239 strlen (strcpy (sm_reply.text, "unable to create new spool file"));
240 sm_reply.code = NOTOK;
241 return RP_BHST;
242 }
243 #endif /* MPOP */
244
245 switch (smtalk (SM_MAIL, "%s FROM:<%s>",
246 mode == S_SEND ? "SEND" : mode == S_SOML ? "SOML"
247 : mode == S_SAML ? "SAML" : "MAIL", from)) {
248 case 250:
249 sm_addrs = 0;
250 return RP_OK;
251
252 case 500:
253 case 501:
254 case 552:
255 return RP_PARM;
256
257 default:
258 return RP_RPLY;
259 }
260 }
261
262
263 int
264 sm_wadr (char *mbox, char *host, char *path)
265 {
266 switch (smtalk (SM_RCPT, host && *host ? "RCPT TO:<%s%s@%s>"
267 : "RCPT TO:<%s%s>",
268 path ? path : "", mbox, host)) {
269 case 250:
270 case 251:
271 sm_addrs++;
272 return RP_OK;
273
274 case 451:
275 #ifdef SENDMAILBUG
276 sm_addrs++;
277 return RP_OK;
278 #endif /* SENDMAILBUG */
279 case 421:
280 case 450:
281 case 452:
282 return RP_NO;
283
284 case 500:
285 case 501:
286 return RP_PARM;
287
288 case 550:
289 case 551:
290 case 552:
291 case 553:
292 return RP_USER;
293
294 default:
295 return RP_RPLY;
296 }
297 }
298
299
300 int
301 sm_waend (void)
302 {
303 switch (smtalk (SM_DATA, "DATA")) {
304 case 354:
305 sm_nl = TRUE;
306 return RP_OK;
307
308 case 451:
309 #ifdef SENDMAILBUG
310 sm_nl = TRUE;
311 return RP_OK;
312 #endif /* SENDMAILBUG */
313 case 421:
314 return RP_NO;
315
316 case 500:
317 case 501:
318 case 503:
319 case 554:
320 return RP_NDEL;
321
322 default:
323 return RP_RPLY;
324 }
325 }
326
327
328 int
329 sm_wtxt (char *buffer, int len)
330 {
331 int result;
332
333 sm_alarmed = 0;
334 alarm (SM_TEXT);
335 result = sm_wstream (buffer, len);
336 alarm (0);
337
338 return (result == NOTOK ? RP_BHST : RP_OK);
339 }
340
341
342 int
343 sm_wtend (void)
344 {
345 if (sm_wstream ((char *) NULL, 0) == NOTOK)
346 return RP_BHST;
347
348 switch (smtalk (SM_DOT + 3 * sm_addrs, ".")) {
349 case 250:
350 case 251:
351 return RP_OK;
352
353 case 451:
354 #ifdef SENDMAILBUG
355 return RP_OK;
356 #endif /* SENDMAILBUG */
357 case 452:
358 default:
359 return RP_NO;
360
361 case 552:
362 case 554:
363 return RP_NDEL;
364 }
365 }
366
367
368 int
369 sm_end (int type)
370 {
371 int status;
372 struct smtp sm_note;
373
374 switch (sm_child) {
375 case NOTOK:
376 case OK:
377 return RP_OK;
378
379 default:
380 break;
381 }
382
383 if (sm_rfp == NULL && sm_wfp == NULL)
384 return RP_OK;
385
386 switch (type) {
387 case OK:
388 smtalk (SM_QUIT, "QUIT");
389 break;
390
391 case NOTOK:
392 sm_note.code = sm_reply.code;
393 strncpy (sm_note.text, sm_reply.text, sm_note.length = sm_reply.length);/* fall */
394 case DONE:
395 if (smtalk (SM_RSET, "RSET") == 250 && type == DONE)
396 return RP_OK;
397 kill (sm_child, SIGKILL);
398 discard (sm_rfp);
399 discard (sm_wfp);
400 if (type == NOTOK) {
401 sm_reply.code = sm_note.code;
402 strncpy (sm_reply.text, sm_note.text, sm_reply.length = sm_note.length);
403 }
404 break;
405 }
406 if (sm_rfp != NULL) {
407 alarm (SM_CLOS);
408 fclose (sm_rfp);
409 alarm (0);
410 }
411 if (sm_wfp != NULL) {
412 alarm (SM_CLOS);
413 fclose (sm_wfp);
414 alarm (0);
415 }
416
417 status = pidwait (sm_child, OK);
418
419 sm_child = NOTOK;
420 sm_rfp = sm_wfp = NULL;
421
422 return (status ? RP_BHST : RP_OK);
423 }
424
425
426 static int
427 sm_ierror (char *fmt, ...)
428 {
429 va_list ap;
430
431 va_start(ap, fmt);
432 vsnprintf (sm_reply.text, sizeof(sm_reply.text), fmt, ap);
433 va_end(ap);
434
435 sm_reply.length = strlen (sm_reply.text);
436 sm_reply.code = NOTOK;
437
438 return RP_BHST;
439 }
440
441
442 static int
443 smtalk (int time, char *fmt, ...)
444 {
445 int result;
446 char buffer[BUFSIZ];
447 va_list ap;
448
449 va_start(ap, fmt);
450 vsnprintf (buffer, sizeof(buffer), fmt, ap);
451 va_end(ap);
452
453 if (sm_debug) {
454 printf ("=> %s\n", buffer);
455 fflush (stdout);
456 }
457
458 #ifdef MPOP
459 if (sm_ispool) {
460 char file[BUFSIZ];
461
462 if (strcmp (buffer, ".") == 0)
463 time = SM_DOT;
464 fprintf (sm_wfp, "%s\r\n", buffer);
465 switch (time) {
466 case SM_DOT:
467 fflush (sm_wfp);
468 if (ferror (sm_wfp))
469 return sm_werror ();
470 snprintf (file, sizeof(file), "%s%c.bulk", sm_tmpfil,
471 (char) (sm_ispool + 'a' - 1));
472 if (rename (sm_tmpfil, file) == NOTOK) {
473 int len;
474 char *bp;
475
476 snprintf (sm_reply.text, sizeof(sm_reply.text),
477 "error renaming %s to %s: ", sm_tmpfil, file);
478 bp = sm_reply.text;
479 len = strlen (bp);
480 bp += len;
481 if ((s = strerror (errno)))
482 strncpy (bp, s, sizeof(sm_reply.text) - len);
483 else
484 snprintf (bp, sizeof(sm_reply.text) - len,
485 "unknown error %d", errno);
486 sm_reply.length = strlen (sm_reply.text);
487 sm_reply.code = NOTOK;
488 return RP_BHST;
489 }
490 fclose (sm_wfp);
491 if (sm_wfp = fopen (sm_tmpfil, "w"))
492 chmod (sm_tmpfil, 0600);
493 sm_ispool++;
494 /* and fall... */
495
496 case SM_MAIL:
497 case SM_RCPT:
498 result = 250;
499 break;
500
501 case SM_RSET:
502 fflush (sm_wfp);
503 ftruncate (fileno (sm_wfp), 0L);
504 fseek (sm_wfp, 0L, SEEK_SET);
505 result = 250;
506 break;
507
508 case SM_DATA:
509 result = 354;
510 break;
511
512 case SM_QUIT:
513 unlink (sm_tmpfil);
514 sm_ispool = 0;
515 result = 221;
516 break;
517
518 default:
519 result = 500;
520 break;
521 }
522 if (sm_debug) {
523 printf ("<= %d\n", result);
524 fflush (stdout);
525 }
526
527 sm_reply.text[sm_reply.length = 0] = NULL;
528 return (sm_reply.code = result);
529 }
530 #endif /* MPOP */
531
532 sm_alarmed = 0;
533 alarm ((unsigned) time);
534 if ((result = sm_wrecord (buffer, strlen (buffer))) != NOTOK)
535 result = smhear ();
536 alarm (0);
537
538 return result;
539 }
540
541
542 static int
543 sm_wrecord (char *buffer, int len)
544 {
545 if (sm_wfp == NULL)
546 return sm_werror ();
547
548 fwrite (buffer, sizeof *buffer, len, sm_wfp);
549 fputs ("\r\n", sm_wfp);
550 fflush (sm_wfp);
551
552 return (ferror (sm_wfp) ? sm_werror () : OK);
553 }
554
555
556 static int
557 sm_wstream (char *buffer, int len)
558 {
559 char *bp;
560 static char lc = 0;
561
562 if (sm_wfp == NULL)
563 return sm_werror ();
564
565 if (buffer == NULL && len == 0) {
566 if (lc != '\n')
567 fputs ("\r\n", sm_wfp);
568 lc = 0;
569 return (ferror (sm_wfp) ? sm_werror () : OK);
570 }
571
572 for (bp = buffer; len > 0; bp++, len--) {
573 switch (*bp) {
574 case '\n':
575 sm_nl = TRUE;
576 fputc ('\r', sm_wfp);
577 break;
578
579 case '.':
580 if (sm_nl)
581 fputc ('.', sm_wfp);/* FALL THROUGH */
582 default:
583 sm_nl = FALSE;
584 }
585 fputc (*bp, sm_wfp);
586 if (ferror (sm_wfp))
587 return sm_werror ();
588 }
589
590 if (bp > buffer)
591 lc = *--bp;
592 return (ferror (sm_wfp) ? sm_werror () : OK);
593 }
594
595
596 #ifdef _AIX
597 /*
598 * AIX by default will inline the strlen and strcpy commands by redefining
599 * them as __strlen and __strcpy respectively. This causes compile problems
600 * with the #ifdef MPOP in the middle. Should the #ifdef MPOP be removed,
601 * remove these #undefs.
602 */
603 # undef strlen
604 # undef strcpy
605 #endif /* _AIX */
606
607 static int
608 sm_werror (void)
609 {
610 sm_reply.length =
611 strlen (strcpy (sm_reply.text, sm_wfp == NULL ? "no pipe opened"
612 : sm_alarmed ? "write to pipe timed out"
613 : "error writing to pipe"));
614
615 return (sm_reply.code = NOTOK);
616 }
617
618
619 static int
620 smhear (void)
621 {
622 int i, code, cont, bc, rc, more;
623 char *bp, *rp;
624 char **ehlo, buffer[BUFSIZ];
625
626 if (doingEHLO) {
627 static int at_least_once = 0;
628
629 if (at_least_once) {
630 for (ehlo = EHLOkeys; *ehlo; ehlo++)
631 free (*ehlo);
632 } else {
633 at_least_once = 1;
634 }
635
636 *(ehlo = EHLOkeys) = NULL;
637 }
638
639 again:
640
641 sm_reply.text[sm_reply.length = 0] = 0;
642
643 rp = sm_reply.text;
644 rc = sizeof(sm_reply.text) - 1;
645
646 for (more = FALSE; sm_rrecord (bp = buffer, &bc) != NOTOK;) {
647 if (sm_debug) {
648 printf ("<= %s\n", buffer);
649 fflush (stdout);
650 }
651
652 if (doingEHLO
653 && strncmp (buffer, "250", sizeof("250") - 1) == 0
654 && (buffer[3] == '-' || doingEHLO == 2)
655 && buffer[4]) {
656 if (doingEHLO == 2) {
657 if ((*ehlo = malloc ((size_t) (strlen (buffer + 4) + 1)))) {
658 strcpy (*ehlo++, buffer + 4);
659 *ehlo = NULL;
660 if (ehlo >= EHLOkeys + MAXEHLO)
661 doingEHLO = 0;
662 }
663 else
664 doingEHLO = 0;
665 }
666 else
667 doingEHLO = 2;
668 }
669
670 for (; bc > 0 && (!isascii (*bp) || !isdigit (*bp)); bp++, bc--)
671 continue;
672
673 cont = FALSE;
674 code = atoi (bp);
675 bp += 3, bc -= 3;
676 for (; bc > 0 && isspace (*bp); bp++, bc--)
677 continue;
678 if (bc > 0 && *bp == '-') {
679 cont = TRUE;
680 bp++, bc--;
681 for (; bc > 0 && isspace (*bp); bp++, bc--)
682 continue;
683 }
684
685 if (more) {
686 if (code != sm_reply.code || cont)
687 continue;
688 more = FALSE;
689 } else {
690 sm_reply.code = code;
691 more = cont;
692 if (bc <= 0) {
693 strncpy (buffer, sm_noreply, sizeof(buffer));
694 bp = buffer;
695 bc = strlen (sm_noreply);
696 }
697 }
698 if ((i = min (bc, rc)) > 0) {
699 strncpy (rp, bp, i);
700 rp += i;
701 rc -= i;
702 if (more && rc > strlen (sm_moreply) + 1) {
703 strncpy (sm_reply.text + rc, sm_moreply, sizeof(sm_reply.text) - rc);
704 rc += strlen (sm_moreply);
705 }
706 }
707 if (more)
708 continue;
709 if (sm_reply.code < 100) {
710 if (sm_verbose) {
711 printf ("%s\n", sm_reply.text);
712 fflush (stdout);
713 }
714 goto again;
715 }
716
717 sm_reply.length = rp - sm_reply.text;
718
719 return sm_reply.code;
720 }
721
722 return NOTOK;
723 }
724
725
726 static int
727 sm_rrecord (char *buffer, int *len)
728 {
729 if (sm_rfp == NULL)
730 return sm_rerror ();
731
732 buffer[*len = 0] = 0;
733
734 fgets (buffer, BUFSIZ, sm_rfp);
735 *len = strlen (buffer);
736 if (ferror (sm_rfp) || feof (sm_rfp))
737 return sm_rerror ();
738 if (buffer[*len - 1] != '\n')
739 while (getc (sm_rfp) != '\n' && !ferror (sm_rfp) && !feof (sm_rfp))
740 continue;
741 else
742 if (buffer[*len - 2] == '\r')
743 *len -= 1;
744 buffer[*len - 1] = 0;
745
746 return OK;
747 }
748
749
750 static int
751 sm_rerror (void)
752 {
753 sm_reply.length =
754 strlen (strcpy (sm_reply.text, sm_rfp == NULL ? "no pipe opened"
755 : sm_alarmed ? "read from pipe timed out"
756 : feof (sm_rfp) ? "premature end-of-file on pipe"
757 : "error reading from pipe"));
758
759 return (sm_reply.code = NOTOK);
760 }
761
762
763 static RETSIGTYPE
764 alrmser (int i)
765 {
766 #ifndef RELIABLE_SIGNALS
767 SIGNAL (SIGALRM, alrmser);
768 #endif
769
770 sm_alarmed++;
771 if (sm_debug) {
772 printf ("timed out...\n");
773 fflush (stdout);
774 }
775 }
776
777
778 char *
779 rp_string (int code)
780 {
781 char *text;
782 static char buffer[BUFSIZ];
783
784 switch (sm_reply.code != NOTOK ? code : NOTOK) {
785 case RP_AOK:
786 text = "AOK";
787 break;
788
789 case RP_MOK:
790 text = "MOK";
791 break;
792
793 case RP_OK:
794 text = "OK";
795 break;
796
797 case RP_RPLY:
798 text = "RPLY";
799 break;
800
801 case RP_BHST:
802 default:
803 text = "BHST";
804 snprintf (buffer, sizeof(buffer), "[%s] %s", text, sm_reply.text);
805 return buffer;
806
807 case RP_PARM:
808 text = "PARM";
809 break;
810
811 case RP_NO:
812 text = "NO";
813 break;
814
815 case RP_USER:
816 text = "USER";
817 break;
818
819 case RP_NDEL:
820 text = "NDEL";
821 break;
822 }
823
824 snprintf (buffer, sizeof(buffer), "[%s] %3d %s",
825 text, sm_reply.code, sm_reply.text);
826 return buffer;
827 }
828