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