]> diplodocus.org Git - nmh/blob - mts/smtp/smtp.c
Put in a prototype for client() to get rid of the "default prototype" warning.
[nmh] / mts / smtp / smtp.c
1
2 /*
3 * smtp.c -- nmh SMTP interface
4 *
5 * $Id$
6 */
7
8 #include <h/mh.h>
9 #include "smtp.h"
10 #include <zotnet/mts/mts.h>
11 #include <signal.h>
12 #include <h/signals.h>
13 #ifdef MPOP
14 #include <errno.h>
15 #endif
16
17
18
19 /*
20 * This module implements an interface to SendMail very similar
21 * to the MMDF mm_(3) routines. The sm_() routines herein talk
22 * SMTP to a sendmail process, mapping SMTP reply codes into
23 * RP_-style codes.
24 */
25
26 /*
27 * On older 4.2BSD machines without the POSIX function `sigaction',
28 * the alarm handing stuff for time-outs will NOT work due to the way
29 * syscalls get restarted. This is not really crucial, since SendMail
30 * is generally well-behaved in this area.
31 */
32
33 #ifdef SENDMAILBUG
34 /*
35 * It appears that some versions of Sendmail will return Code 451
36 * when they don't really want to indicate a failure.
37 * "Code 451 almost always means sendmail has deferred; we don't
38 * really want bomb out at this point since sendmail will rectify
39 * things later." So, if you define SENDMAILBUG, Code 451 is
40 * considered the same as Code 250. Yuck!
41 */
42 #endif
43
44 #define TRUE 1
45 #define FALSE 0
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_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
81 #define MAXEHLO 20
82
83 static int doingEHLO;
84 char *EHLOkeys[MAXEHLO + 1];
85
86 /*
87 * static prototypes
88 */
89 static int rclient (char *, char *, char *);
90 static int sm_ierror (char *fmt, ...);
91 static int smtalk (int time, char *fmt, ...);
92 static int sm_wrecord (char *, int);
93 static int sm_wstream (char *, int);
94 static int sm_werror (void);
95 static int smhear (void);
96 static int sm_rrecord (char *, int *);
97 static int sm_rerror (void);
98 static RETSIGTYPE alrmser (int);
99 static char *EHLOset (char *);
100
101 #ifdef MPOP
102 /*
103 * smtp.c's own static copy of several nmh library subroutines
104 */
105 static char **smail_brkstring (char *, char *, char *);
106 static int smail_brkany (char, char *);
107 char **smail_copyip (char **, char **, int);
108 #endif
109
110 /* from zotnet/mts/client.c */
111 int client (char *, char *, char *, int, char *, int);
112
113 int
114 sm_init (char *client, char *server, int watch, int verbose,
115 int debug, int onex, int queued)
116 {
117 int result, sd1, sd2;
118
119 if (watch)
120 verbose = TRUE;
121
122 sm_verbose = verbose;
123 sm_debug = debug;
124
125 #ifdef MPOP
126 if (sm_ispool)
127 goto all_done;
128 #endif
129
130 if (sm_rfp != NULL && sm_wfp != NULL)
131 goto send_options;
132
133 if (client == NULL || *client == '\0') {
134 if (clientname) {
135 client = clientname;
136 } else {
137 client = LocalName(); /* no clientname -> LocalName */
138 }
139 }
140
141 #ifdef ZMAILER
142 if (client == NULL || *client == '\0')
143 client = "localhost";
144 #endif
145
146 if ((sd1 = rclient (server, "tcp", "smtp")) == NOTOK)
147 return RP_BHST;
148
149 #ifdef MPOP
150 if (sm_ispool) {
151 if (sm_rfp) {
152 alarm (SM_CLOS);
153 fclose (sm_rfp);
154 alarm (0);
155 sm_rfp = NULL;
156 }
157 if ((sm_wfp = fdopen (sd1, "w")) == NULL) {
158 unlink (sm_tmpfil);
159 close (sd1);
160 return sm_ierror ("unable to fdopen");
161 }
162 all_done: ;
163 sm_reply.text[sm_reply.length = 0] = NULL;
164 return (sm_reply.code = RP_OK);
165 }
166 #endif /* MPOP */
167
168 if ((sd2 = dup (sd1)) == NOTOK) {
169 close (sd1);
170 return sm_ierror ("unable to dup");
171 }
172
173 SIGNAL (SIGALRM, alrmser);
174 SIGNAL (SIGPIPE, SIG_IGN);
175
176 if ((sm_rfp = fdopen (sd1, "r")) == NULL
177 || (sm_wfp = fdopen (sd2, "w")) == NULL) {
178 close (sd1);
179 close (sd2);
180 sm_rfp = sm_wfp = NULL;
181 return sm_ierror ("unable to fdopen");
182 }
183
184 sm_alarmed = 0;
185 alarm (SM_OPEN);
186 result = smhear ();
187 alarm (0);
188
189 switch (result) {
190 case 220:
191 break;
192
193 default:
194 sm_end (NOTOK);
195 return RP_RPLY;
196 }
197
198 /*
199 * Give EHLO or HELO command
200 */
201 if (client && *client) {
202 doingEHLO = 1;
203 result = smtalk (SM_HELO, "EHLO %s", client);
204 doingEHLO = 0;
205
206 if (result >= 500 && result <= 599)
207 result = smtalk (SM_HELO, "HELO %s", client);
208
209 if (result != 250) {
210 sm_end (NOTOK);
211 return RP_RPLY;
212 }
213 }
214
215 send_options: ;
216 if (watch && EHLOset ("XVRB"))
217 smtalk (SM_HELO, "VERB on");
218 if (onex && EHLOset ("XONE"))
219 smtalk (SM_HELO, "ONEX");
220 if (queued && EHLOset ("XQUE"))
221 smtalk (SM_HELO, "QUED");
222
223 return RP_OK;
224 }
225
226
227 #ifdef MPOP
228 # define MAXARGS 1000
229 #endif /* MPOP */
230
231 static int
232 rclient (char *server, char *protocol, char *service)
233 {
234 int sd;
235 char response[BUFSIZ];
236 #ifdef MPOP
237 char *cp;
238 #endif /* MPOP */
239
240 if ((sd = client (server, protocol, service, FALSE, response, sizeof(response))) != NOTOK)
241 return sd;
242
243 #ifdef MPOP
244 if (!server && servers && (cp = strchr(servers, '/'))) {
245 char **ap;
246 char *arguments[MAXARGS];
247
248 smail_copyip (smail_brkstring (cp = getcpy (servers), " ", "\n"), arguments, MAXARGS);
249
250 for (ap = arguments; *ap; ap++)
251 if (**ap == '/') {
252 char *dp;
253
254 if ((dp = strrchr(*ap, '/')) && *++dp == NULL)
255 *--dp = NULL;
256 snprintf (sm_tmpfil, sizeof(sm_tmpfil), "%s/smtpXXXXXX", *ap);
257 #ifdef HAVE_MKSTEMP
258 sd = mkstemp (sm_tmpfil);
259 #else
260 mktemp (sm_tmpfil);
261
262 if ((sd = creat (sm_tmpfil, 0600)) != NOTOK) {
263 sm_ispool = 1;
264 break;
265 }
266 #endif
267 }
268
269 free (cp);
270 if (sd != NOTOK)
271 return sd;
272 }
273 #endif /* MPOP */
274
275 sm_ierror ("%s", response);
276 return NOTOK;
277 }
278
279
280 int
281 sm_winit (int mode, char *from)
282 {
283 char *smtpcom;
284
285 #ifdef MPOP
286 if (sm_ispool && !sm_wfp) {
287 strlen (strcpy (sm_reply.text, "unable to create new spool file"));
288 sm_reply.code = NOTOK;
289 return RP_BHST;
290 }
291 #endif /* MPOP */
292
293 switch (mode) {
294 case S_MAIL:
295 smtpcom = "MAIL";
296 break;
297
298 case S_SEND:
299 smtpcom = "SEND";
300 break;
301
302 case S_SOML:
303 smtpcom = "SOML";
304 break;
305
306 case S_SAML:
307 smtpcom = "SAML";
308 break;
309 }
310
311 switch (smtalk (SM_MAIL, "%s FROM:<%s>", smtpcom, from)) {
312 case 250:
313 sm_addrs = 0;
314 return RP_OK;
315
316 case 500:
317 case 501:
318 case 552:
319 return RP_PARM;
320
321 default:
322 return RP_RPLY;
323 }
324 }
325
326
327 int
328 sm_wadr (char *mbox, char *host, char *path)
329 {
330 switch (smtalk (SM_RCPT, host && *host ? "RCPT TO:<%s%s@%s>"
331 : "RCPT TO:<%s%s>",
332 path ? path : "", mbox, host)) {
333 case 250:
334 case 251:
335 sm_addrs++;
336 return RP_OK;
337
338 case 451:
339 #ifdef SENDMAILBUG
340 sm_addrs++;
341 return RP_OK;
342 #endif /* SENDMAILBUG */
343 case 421:
344 case 450:
345 case 452:
346 return RP_NO;
347
348 case 500:
349 case 501:
350 return RP_PARM;
351
352 case 550:
353 case 551:
354 case 552:
355 case 553:
356 return RP_USER;
357
358 default:
359 return RP_RPLY;
360 }
361 }
362
363
364 int
365 sm_waend (void)
366 {
367 switch (smtalk (SM_DATA, "DATA")) {
368 case 354:
369 sm_nl = TRUE;
370 return RP_OK;
371
372 case 451:
373 #ifdef SENDMAILBUG
374 sm_nl = TRUE;
375 return RP_OK;
376 #endif /* SENDMAILBUG */
377 case 421:
378 return RP_NO;
379
380 case 500:
381 case 501:
382 case 503:
383 case 554:
384 return RP_NDEL;
385
386 default:
387 return RP_RPLY;
388 }
389 }
390
391
392 int
393 sm_wtxt (char *buffer, int len)
394 {
395 int result;
396
397 sm_alarmed = 0;
398 alarm (SM_TEXT);
399 result = sm_wstream (buffer, len);
400 alarm (0);
401
402 return (result == NOTOK ? RP_BHST : RP_OK);
403 }
404
405
406 int
407 sm_wtend (void)
408 {
409 if (sm_wstream ((char *) NULL, 0) == NOTOK)
410 return RP_BHST;
411
412 switch (smtalk (SM_DOT + 3 * sm_addrs, ".")) {
413 case 250:
414 case 251:
415 return RP_OK;
416
417 case 451:
418 #ifdef SENDMAILBUG
419 return RP_OK;
420 #endif /* SENDMAILBUG */
421 case 452:
422 default:
423 return RP_NO;
424
425 case 552:
426 case 554:
427 return RP_NDEL;
428 }
429 }
430
431
432 int
433 sm_end (int type)
434 {
435 int status;
436 struct smtp sm_note;
437
438 if (sm_rfp == NULL && sm_wfp == NULL)
439 return RP_OK;
440
441 switch (type) {
442 case OK:
443 smtalk (SM_QUIT, "QUIT");
444 break;
445
446 case NOTOK:
447 sm_note.code = sm_reply.code;
448 strncpy (sm_note.text, sm_reply.text, sm_note.length = sm_reply.length);/* fall */
449 case DONE:
450 if (smtalk (SM_RSET, "RSET") == 250 && type == DONE)
451 return RP_OK;
452 smtalk (SM_QUIT, "QUIT");
453 if (type == NOTOK) {
454 sm_reply.code = sm_note.code;
455 strncpy (sm_reply.text, sm_note.text, sm_reply.length = sm_note.length);
456 }
457 break;
458 }
459
460 #ifdef MPOP
461 if (sm_ispool) {
462 sm_ispool = 0;
463
464 if (sm_wfp) {
465 unlink (sm_tmpfil);
466 fclose (sm_wfp);
467 sm_wfp = NULL;
468 }
469 }
470 #endif /* MPOP */
471
472 if (sm_rfp != NULL) {
473 alarm (SM_CLOS);
474 fclose (sm_rfp);
475 alarm (0);
476 }
477 if (sm_wfp != NULL) {
478 alarm (SM_CLOS);
479 fclose (sm_wfp);
480 alarm (0);
481 }
482
483 status = 0;
484 sm_rfp = sm_wfp = NULL;
485 return (status ? RP_BHST : RP_OK);
486 }
487
488
489 #ifdef MPOP
490
491 int
492 sm_bulk (char *file)
493 {
494 int cc, i, j, k, result;
495 long pos;
496 char *dp, *bp, *cp, s;
497 char buffer[BUFSIZ], sender[BUFSIZ];
498 FILE *fp, *gp;
499
500 gp = NULL;
501 k = strlen (file) - sizeof(".bulk");
502 if ((fp = fopen (file, "r")) == NULL) {
503 int len;
504
505 snprintf (sm_reply.text, sizeof(sm_reply.text),
506 "unable to read %s: ", file);
507 bp = sm_reply.text;
508 len = strlen (bp);
509 bp += len;
510 if ((s = strerror (errno)))
511 strncpy (bp, s, sizeof(sm_reply.text) - len);
512 else
513 snprintf (bp, sizeof(sm_reply.text) - len, "Error %d", errno);
514 sm_reply.length = strlen (sm_reply.text);
515 sm_reply.code = NOTOK;
516 return RP_BHST;
517 }
518 if (sm_debug) {
519 printf ("reading file %s\n", file);
520 fflush (stdout);
521 }
522
523 i = j = 0;
524 while (fgets (buffer, sizeof(buffer), fp)) {
525 if (j++ == 0)
526 strncpy (sender, buffer + sizeof("MAIL FROM:") - 1, sizeof(sender));
527 if (strcmp (buffer, "DATA\r\n") == 0) {
528 i = 1;
529 break;
530 }
531 }
532 if (i == 0) {
533 if (sm_debug) {
534 printf ("no DATA...\n");
535 fflush (stdout);
536 }
537 losing0:
538 snprintf (buffer, sizeof(buffer), "%s.bad", file);
539 rename (file, buffer);
540 if (gp) {
541 snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
542 unlink (buffer);
543 fclose (gp);
544 }
545 fclose (fp);
546 return RP_OK;
547 }
548 if (j < 3) {
549 if (sm_debug) {
550 printf ("no %srecipients...\n", j < 1 ? "sender or " : "");
551 fflush (stdout);
552 }
553 goto losing0;
554 }
555
556 if ((cp = malloc ((size_t) (cc = (pos = ftell (fp)) + 1))) == NULL) {
557 sm_reply.length = strlen (strcpy (sm_reply.text, "out of memory"));
558 losing1: ;
559 sm_reply.code = NOTOK;
560 fclose (fp);
561 return RP_BHST;
562 }
563 fseek (fp, 0L, SEEK_SET);
564 for (dp = cp, i = 0; i++ < j; dp += strlen (dp))
565 if (fgets (dp, cc - (dp - cp), fp) == NULL) {
566 sm_reply.length = strlen (strcpy (sm_reply.text, "premature eof"));
567 losing2:
568 free (cp);
569 goto losing1;
570 }
571 *dp = NULL;
572
573 for (dp = cp, i = cc - 1; i > 0; dp += cc, i -= cc)
574 if ((cc = write (fileno (sm_wfp), dp, i)) == NOTOK) {
575 int len;
576 losing3:
577 strcpy (sm_reply.text, "error writing to server: ",
578 sizeof(sm_reply.text));
579 bp = sm_reply.text;
580 len = strlen (bp);
581 bp += len;
582 if ((s = strerror (errno)))
583 strncpy (bp, s, sizeof(sm_reply.text) - len);
584 else
585 snprintf (bp, sizeof(sm_reply.text) - len,
586 "unknown error %d", errno);
587 sm_reply.length = strlen (sm_reply.text);
588 goto losing2;
589 }
590 else
591 if (sm_debug) {
592 printf ("wrote %d octets to server\n", cc);
593 fflush (stdout);
594 }
595
596 for (dp = cp, i = 0; i++ < j; dp = strchr(dp, '\n'), dp++) {
597 if (sm_debug) {
598 if (bp = strchr(dp, '\r'))
599 *bp = NULL;
600 printf ("=> %s\n", dp);
601 fflush (stdout);
602 if (bp)
603 *bp = '\r';
604 }
605
606 switch (smhear () + (i == 1 ? 1000 : i != j ? 2000 : 3000)) {
607 case 1000 + 250:
608 sm_addrs = 0;
609 result = RP_OK;
610 break;
611
612 case 1000 + 500:
613 case 1000 + 501:
614 case 1000 + 552:
615 case 2000 + 500:
616 case 2000 + 501:
617 result = RP_PARM;
618 smtalk (SM_RSET, "RSET");
619 free (cp);
620 goto losing0;
621
622 case 2000 + 250:
623 case 2000 + 251:
624 sm_addrs++;
625 result = RP_OK;
626 break;
627
628 case 2000 + 451:
629 #ifdef SENDMAILBUG
630 sm_addrs++;
631 result = RP_OK;
632 break;
633 #endif
634 case 2000 + 421:
635 case 2000 + 450:
636 case 2000 + 452:
637 result = RP_NO;
638 goto bad_addr;
639
640 case 2000 + 550:
641 case 2000 + 551:
642 case 2000 + 552:
643 case 2000 + 553:
644 result = RP_USER;
645 bad_addr:
646 if (k <= 0 || strcmp (sender, "<>\r\n") == 0)
647 break;
648 if (gp == NULL) {
649 int l;
650 snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
651 if ((gp = fopen (buffer, "w+")) == NULL)
652 goto bad_data;
653 fprintf (gp, "MAIL FROM:<>\r\nRCPT TO:%sDATA\r\n", sender);
654 l = strlen (sender);
655 fprintf (gp,
656 "To: %*.*s\r\nSubject: Invalid addresses (%s)\r\n",
657 l - 4, l - 4, sender + 1, file);
658 fprintf (gp, "Date: %s\r\nFrom: Postmaster@%s\r\n\r\n",
659 dtimenow (0), LocalName ());
660 }
661 if (bp = strchr(dp, '\r'))
662 *bp = NULL;
663 fprintf (gp, "=> %s\r\n", dp);
664 if (bp)
665 *bp = '\r';
666 fprintf (gp, "<= %s\r\n", rp_string (result));
667 fflush (gp);
668 break;
669
670 case 3000 + 354:
671 #ifdef SENDMAILBUG
672 ok_data:
673 #endif
674 result = RP_OK;
675 break;
676
677 case 3000 + 451:
678 #ifdef SENDMAILBUG
679 goto ok_data;
680 #endif
681 case 3000 + 421:
682 result = RP_NO;
683 bad_data:
684 smtalk (SM_RSET, "RSET");
685 free (cp);
686 if (gp) {
687 snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
688 unlink (buffer);
689 fclose (gp);
690 }
691 fclose (fp);
692 return result;
693
694 case 3000 + 500:
695 case 3000 + 501:
696 case 3000 + 503:
697 case 3000 + 554:
698 smtalk (SM_RSET, "RSET");
699 free (cp);
700 goto no_dice;
701
702 default:
703 result = RP_RPLY;
704 goto bad_data;
705 }
706 }
707 free (cp);
708
709 {
710 #ifdef HAVE_ST_BLKSIZE
711 struct stat st;
712
713 if (fstat (fileno (sm_wfp), &st) == NOTOK || (cc = st.st_blksize) < BUFSIZ)
714 cc = BUFSIZ;
715 #else
716 cc = BUFSIZ;
717 #endif
718 if ((cp = malloc ((size_t) cc)) == NULL) {
719 smtalk (SM_RSET, "RSET");
720 sm_reply.length = strlen (strcpy (sm_reply.text, "out of memory"));
721 goto losing1;
722 }
723 }
724
725 fseek (fp, pos, SEEK_SET);
726 for (;;) {
727 int eof = 0;
728
729 for (dp = cp, i = cc; i > 0; dp += j, i -= j)
730 if ((j = fread (cp, sizeof(*cp), i, fp)) == OK) {
731 if (ferror (fp)) {
732 int len;
733
734 snprintf (sm_reply.text, sizeof(sm_reply.text),
735 "error reading %s: ", file);
736 bp = sm_reply.text;
737 len = strlen (bp);
738 bp += len;
739 if ((s = strerror (errno)))
740 strncpy (bp, s, sizeof(sm_reply.text) - len);
741 else
742 snprintf (bp, sizeof(sm_reply.text) - len,
743 "unknown error %d", errno);
744 sm_reply.length = strlen (sm_reply.text);
745 goto losing2;
746 }
747 cc = dp - cp;
748 eof = 1;
749 break;
750 }
751
752 for (dp = cp, i = cc; i > 0; dp += j, i -= j)
753 if ((j = write (fileno (sm_wfp), dp, i)) == NOTOK)
754 goto losing3;
755 else
756 if (sm_debug) {
757 printf ("wrote %d octets to server\n", j);
758 fflush (stdout);
759 }
760
761 if (eof)
762 break;
763 }
764 free (cp);
765
766 switch (smhear ()) {
767 case 250:
768 case 251:
769 #ifdef SENDMAILBUG
770 ok_dot:
771 #endif
772 result = RP_OK;
773 unlink (file);
774 break;
775
776 case 451:
777 #ifdef SENDMAILBUG
778 goto ok_dot;
779 #endif
780 case 452:
781 default:
782 result = RP_NO;
783 if (gp) {
784 snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
785 unlink (buffer);
786 fclose (gp);
787 gp = NULL;
788 }
789 break;
790
791 case 552:
792 case 554:
793 no_dice:
794 result = RP_NDEL;
795 if (k <= 0 || strcmp (sender, "<>\r\n") == 0) {
796 unlink (file);
797 break;
798 }
799 if (gp) {
800 fflush (gp);
801 ftruncate (fileno (gp), 0L);
802 fseek (gp, 0L, SEEK_SET);
803 }
804 else {
805 snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
806 if ((gp = fopen (buffer, "w")) == NULL)
807 break;
808 }
809 fprintf (gp, "MAIL FROM:<>\r\nRCPT TO:%sDATA\r\n", sender);
810 i = strlen (sender);
811 fprintf (gp, "To: %*.*s\r\nSubject: Failed mail (%s)\r\n",
812 i - 4, i - 4, sender + 1, file);
813 fprintf (gp, "Date: %s\r\nFrom: Postmaster@%s\r\n\r\n",
814 dtimenow (0), LocalName ());
815 break;
816 }
817
818 if (gp) {
819 fputs ("\r\n------- Begin Returned message\r\n\r\n", gp);
820 fseek (fp, pos, SEEK_SET);
821 while (fgets (buffer, sizeof(buffer), fp)) {
822 if (buffer[0] == '-')
823 fputs ("- ", gp);
824 if (strcmp (buffer, ".\r\n"))
825 fputs (buffer, gp);
826 }
827 fputs ("\r\n------- End Returned Message\r\n\r\n.\r\n", gp);
828 fflush (gp);
829 if (!ferror (gp))
830 unlink (file);
831 fclose (gp);
832 }
833 fclose (fp);
834
835 return result;
836 }
837 #endif /* MPOP */
838
839
840 static int
841 sm_ierror (char *fmt, ...)
842 {
843 va_list ap;
844
845 va_start(ap, fmt);
846 vsnprintf (sm_reply.text, sizeof(sm_reply.text), fmt, ap);
847 va_end(ap);
848
849 sm_reply.length = strlen (sm_reply.text);
850 sm_reply.code = NOTOK;
851
852 return RP_BHST;
853 }
854
855
856 static int
857 smtalk (int time, char *fmt, ...)
858 {
859 va_list ap;
860 int result;
861 char buffer[BUFSIZ];
862
863 va_start(ap, fmt);
864 vsnprintf (buffer, sizeof(buffer), fmt, ap);
865 va_end(ap);
866
867 if (sm_debug) {
868 printf ("=> %s\n", buffer);
869 fflush (stdout);
870 }
871
872 #ifdef MPOP
873 if (sm_ispool) {
874 char file[BUFSIZ];
875
876 if (strcmp (buffer, ".") == 0)
877 time = SM_DOT;
878 fprintf (sm_wfp, "%s\r\n", buffer);
879 switch (time) {
880 case SM_DOT:
881 fflush (sm_wfp);
882 if (ferror (sm_wfp))
883 return sm_werror ();
884 snprintf (file, sizeof(file), "%s%c.bulk", sm_tmpfil,
885 (char) (sm_ispool + 'a' - 1));
886 if (rename (sm_tmpfil, file) == NOTOK) {
887 int len;
888 char *bp;
889
890 snprintf (sm_reply.text, sizeof(sm_reply.text),
891 "error renaming %s to %s: ", sm_tmpfil, file);
892 bp = sm_reply.text;
893 len = strlen (bp);
894 bp += len;
895 if ((s = strerror (errno)))
896 strncpy (bp, s, sizeof(sm_reply.text) - len);
897 else
898 snprintf (bp, sizeof(sm_reply.text) - len,
899 "unknown error %d", errno);
900 sm_reply.length = strlen (sm_reply.text);
901 sm_reply.code = NOTOK;
902 return RP_BHST;
903 }
904 fclose (sm_wfp);
905 if (sm_wfp = fopen (sm_tmpfil, "w"))
906 chmod (sm_tmpfil, 0600);
907 sm_ispool++;
908 /* and fall... */
909
910 case SM_MAIL:
911 case SM_RCPT:
912 result = 250;
913 break;
914
915 case SM_RSET:
916 fflush (sm_wfp);
917 ftruncate (fileno (sm_wfp), 0L);
918 fseek (sm_wfp, 0L, SEEK_SET);
919 result = 250;
920 break;
921
922 case SM_DATA:
923 result = 354;
924 break;
925
926 case SM_QUIT:
927 unlink (sm_tmpfil);
928 sm_ispool = 0;
929 result = 221;
930 break;
931
932 default:
933 result = 500;
934 break;
935 }
936 if (sm_debug) {
937 printf ("<= %d\n", result);
938 fflush (stdout);
939 }
940
941 sm_reply.text[sm_reply.length = 0] = NULL;
942 return (sm_reply.code = result);
943 }
944 #endif /* MPOP */
945
946 sm_alarmed = 0;
947 alarm ((unsigned) time);
948 if ((result = sm_wrecord (buffer, strlen (buffer))) != NOTOK)
949 result = smhear ();
950 alarm (0);
951
952 return result;
953 }
954
955
956 /*
957 * write the buffer to the open SMTP channel
958 */
959
960 static int
961 sm_wrecord (char *buffer, int len)
962 {
963 if (sm_wfp == NULL)
964 return sm_werror ();
965
966 fwrite (buffer, sizeof(*buffer), len, sm_wfp);
967 fputs ("\r\n", sm_wfp);
968 fflush (sm_wfp);
969
970 return (ferror (sm_wfp) ? sm_werror () : OK);
971 }
972
973
974 static int
975 sm_wstream (char *buffer, int len)
976 {
977 char *bp;
978 static char lc = '\0';
979
980 if (sm_wfp == NULL)
981 return sm_werror ();
982
983 if (buffer == NULL && len == 0) {
984 if (lc != '\n')
985 fputs ("\r\n", sm_wfp);
986 lc = '\0';
987 return (ferror (sm_wfp) ? sm_werror () : OK);
988 }
989
990 for (bp = buffer; len > 0; bp++, len--) {
991 switch (*bp) {
992 case '\n':
993 sm_nl = TRUE;
994 fputc ('\r', sm_wfp);
995 break;
996
997 case '.':
998 if (sm_nl)
999 fputc ('.', sm_wfp);/* FALL THROUGH */
1000 default:
1001 sm_nl = FALSE;
1002 }
1003 fputc (*bp, sm_wfp);
1004 if (ferror (sm_wfp))
1005 return sm_werror ();
1006 }
1007
1008 if (bp > buffer)
1009 lc = *--bp;
1010 return (ferror (sm_wfp) ? sm_werror () : OK);
1011 }
1012
1013
1014 /*
1015 * On some systems, strlen and strcpy are defined as preprocessor macros. This
1016 * causes compile problems with the #ifdef MPOP in the middle. Should the
1017 * #ifdef MPOP be removed, remove these #undefs.
1018 */
1019 #ifdef strlen
1020 # undef strlen
1021 #endif
1022 #ifdef strcpy
1023 # undef strcpy
1024 #endif
1025
1026 static int
1027 sm_werror (void)
1028 {
1029 sm_reply.length =
1030 strlen (strcpy (sm_reply.text, sm_wfp == NULL ? "no socket opened"
1031 : sm_alarmed ? "write to socket timed out"
1032 #ifdef MPOP
1033 : sm_ispool ? "error writing to spool file"
1034 #endif
1035 : "error writing to socket"));
1036
1037 return (sm_reply.code = NOTOK);
1038 }
1039
1040
1041 static int
1042 smhear (void)
1043 {
1044 int i, code, cont, bc, rc, more;
1045 char *bp, *rp;
1046 char **ehlo, buffer[BUFSIZ];
1047
1048 if (doingEHLO) {
1049 static int at_least_once = 0;
1050
1051 if (at_least_once) {
1052 char *ep;
1053
1054 for (ehlo = EHLOkeys; *ehlo; ehlo++) {
1055 ep = *ehlo;
1056 free (ep);
1057 }
1058 } else {
1059 at_least_once = 1;
1060 }
1061
1062 ehlo = EHLOkeys;
1063 *ehlo = NULL;
1064 }
1065
1066 again: ;
1067
1068 sm_reply.length = 0;
1069 sm_reply.text[0] = 0;
1070 rp = sm_reply.text;
1071 rc = sizeof(sm_reply.text) - 1;
1072
1073 for (more = FALSE; sm_rrecord (bp = buffer, &bc) != NOTOK;) {
1074 if (sm_debug) {
1075 printf ("<= %s\n", buffer);
1076 fflush (stdout);
1077 }
1078
1079 if (doingEHLO
1080 && strncmp (buffer, "250", sizeof("250") - 1) == 0
1081 && (buffer[3] == '-' || doingEHLO == 2)
1082 && buffer[4]) {
1083 if (doingEHLO == 2) {
1084 if ((*ehlo = malloc ((size_t) (strlen (buffer + 4) + 1)))) {
1085 strcpy (*ehlo++, buffer + 4);
1086 *ehlo = NULL;
1087 if (ehlo >= EHLOkeys + MAXEHLO)
1088 doingEHLO = 0;
1089 }
1090 else
1091 doingEHLO = 0;
1092 }
1093 else
1094 doingEHLO = 2;
1095 }
1096
1097 for (; bc > 0 && (!isascii (*bp) || !isdigit (*bp)); bp++, bc--)
1098 continue;
1099
1100 cont = FALSE;
1101 code = atoi (bp);
1102 bp += 3, bc -= 3;
1103 for (; bc > 0 && isspace (*bp); bp++, bc--)
1104 continue;
1105 if (bc > 0 && *bp == '-') {
1106 cont = TRUE;
1107 bp++, bc--;
1108 for (; bc > 0 && isspace (*bp); bp++, bc--)
1109 continue;
1110 }
1111
1112 if (more) {
1113 if (code != sm_reply.code || cont)
1114 continue;
1115 more = FALSE;
1116 } else {
1117 sm_reply.code = code;
1118 more = cont;
1119 if (bc <= 0) {
1120 strncpy (buffer, sm_noreply, sizeof(buffer));
1121 bp = buffer;
1122 bc = strlen (sm_noreply);
1123 }
1124 }
1125
1126 if ((i = min (bc, rc)) > 0) {
1127 strncpy (rp, bp, i);
1128 rp += i, rc -= i;
1129 if (more && rc > strlen (sm_moreply) + 1) {
1130 strcpy (sm_reply.text + rc, sm_moreply);
1131 rc += strlen (sm_moreply);
1132 }
1133 }
1134 if (more)
1135 continue;
1136 if (sm_reply.code < 100) {
1137 if (sm_verbose) {
1138 printf ("%s\n", sm_reply.text);
1139 fflush (stdout);
1140 }
1141 goto again;
1142 }
1143
1144 sm_reply.length = rp - sm_reply.text;
1145 return sm_reply.code;
1146 }
1147 return NOTOK;
1148 }
1149
1150
1151 static int
1152 sm_rrecord (char *buffer, int *len)
1153 {
1154 if (sm_rfp == NULL)
1155 return sm_rerror ();
1156
1157 buffer[*len = 0] = 0;
1158
1159 fgets (buffer, BUFSIZ, sm_rfp);
1160 *len = strlen (buffer);
1161 if (ferror (sm_rfp) || feof (sm_rfp))
1162 return sm_rerror ();
1163 if (buffer[*len - 1] != '\n')
1164 while (getc (sm_rfp) != '\n' && !ferror (sm_rfp) && !feof (sm_rfp))
1165 continue;
1166 else
1167 if (buffer[*len - 2] == '\r')
1168 *len -= 1;
1169 buffer[*len - 1] = 0;
1170
1171 return OK;
1172 }
1173
1174
1175 static int
1176 sm_rerror (void)
1177 {
1178 sm_reply.length =
1179 strlen (strcpy (sm_reply.text, sm_rfp == NULL ? "no socket opened"
1180 : sm_alarmed ? "read from socket timed out"
1181 : feof (sm_rfp) ? "premature end-of-file on socket"
1182 : "error reading from socket"));
1183
1184 return (sm_reply.code = NOTOK);
1185 }
1186
1187
1188 static RETSIGTYPE
1189 alrmser (int i)
1190 {
1191 #ifndef RELIABLE_SIGNALS
1192 SIGNAL (SIGALRM, alrmser);
1193 #endif
1194
1195 sm_alarmed++;
1196 if (sm_debug) {
1197 printf ("timed out...\n");
1198 fflush (stdout);
1199 }
1200 }
1201
1202
1203 char *
1204 rp_string (int code)
1205 {
1206 char *text;
1207 static char buffer[BUFSIZ];
1208
1209 switch (sm_reply.code != NOTOK ? code : NOTOK) {
1210 case RP_AOK:
1211 text = "AOK";
1212 break;
1213
1214 case RP_MOK:
1215 text = "MOK";
1216 break;
1217
1218 case RP_OK:
1219 text = "OK";
1220 break;
1221
1222 case RP_RPLY:
1223 text = "RPLY";
1224 break;
1225
1226 case RP_BHST:
1227 default:
1228 text = "BHST";
1229 snprintf (buffer, sizeof(buffer), "[%s] %s", text, sm_reply.text);
1230 return buffer;
1231
1232 case RP_PARM:
1233 text = "PARM";
1234 break;
1235
1236 case RP_NO:
1237 text = "NO";
1238 break;
1239
1240 case RP_USER:
1241 text = "USER";
1242 break;
1243
1244 case RP_NDEL:
1245 text = "NDEL";
1246 break;
1247 }
1248
1249 snprintf (buffer, sizeof(buffer), "[%s] %3d %s",
1250 text, sm_reply.code, sm_reply.text);
1251 return buffer;
1252 }
1253
1254
1255 #ifdef MPOP
1256
1257 static char *broken[MAXARGS + 1];
1258
1259 static char **
1260 smail_brkstring (char *strg, char *brksep, char *brkterm)
1261 {
1262 int bi;
1263 char c, *sp;
1264
1265 sp = strg;
1266
1267 for (bi = 0; bi < MAXARGS; bi++) {
1268 while (smail_brkany (c = *sp, brksep))
1269 *sp++ = 0;
1270 if (!c || smail_brkany (c, brkterm)) {
1271 *sp = 0;
1272 broken[bi] = 0;
1273 return broken;
1274 }
1275
1276 broken[bi] = sp;
1277 while ((c = *++sp) && !smail_brkany (c, brksep) && !smail_brkany (c, brkterm))
1278 continue;
1279 }
1280 broken[MAXARGS] = 0;
1281
1282 return broken;
1283 }
1284
1285
1286 /*
1287 * returns 1 if chr in strg, 0 otherwise
1288 */
1289 static int
1290 smail_brkany (char chr, char *strg)
1291 {
1292 char *sp;
1293
1294 if (strg)
1295 for (sp = strg; *sp; sp++)
1296 if (chr == *sp)
1297 return 1;
1298 return 0;
1299 }
1300
1301 /*
1302 * copy a string array and return pointer to end
1303 */
1304 char **
1305 smail_copyip (char **p, char **q, int len_q)
1306 {
1307 while (*p && --len_q > 0)
1308 *q++ = *p++;
1309
1310 *q = NULL;
1311
1312 return q;
1313 }
1314
1315 #endif /* MPOP */
1316
1317
1318 static char *
1319 EHLOset (char *s)
1320 {
1321 size_t len;
1322 char *ep, **ehlo;
1323
1324 len = strlen (s);
1325
1326 for (ehlo = EHLOkeys; *ehlo; ehlo++) {
1327 ep = *ehlo;
1328 if (strncmp (ep, s, len) == 0) {
1329 for (ep += len; *ep == ' '; ep++)
1330 continue;
1331 return ep;
1332 }
1333 }
1334
1335 return 0;
1336 }