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