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