]> diplodocus.org Git - nmh/blob - mts/smtp/smtp.c
When building on Mac OS X, don't set LDFLAGS=-s, since the linker doesn't like the...
[nmh] / mts / smtp / smtp.c
1 /*
2 * smtp.c -- nmh SMTP interface
3 *
4 * $Id$
5 */
6
7 #include <h/mh.h>
8 #include "smtp.h"
9 #include <h/mts.h>
10 #include <signal.h>
11 #include <h/signals.h>
12 #ifdef MPOP
13 #include <errno.h>
14 #endif
15
16 #ifdef CYRUS_SASL
17 #include <sasl.h>
18 #include <sys/socket.h>
19 #include <netinet/in.h>
20 #include <arpa/inet.h>
21 #include <netdb.h>
22 #include <errno.h>
23 #endif /* CYRUS_SASL */
24
25 /*
26 * This module implements an interface to SendMail very similar
27 * to the MMDF mm_(3) routines. The sm_() routines herein talk
28 * SMTP to a sendmail process, mapping SMTP reply codes into
29 * RP_-style codes.
30 */
31
32 /*
33 * On older 4.2BSD machines without the POSIX function `sigaction',
34 * the alarm handing stuff for time-outs will NOT work due to the way
35 * syscalls get restarted. This is not really crucial, since SendMail
36 * is generally well-behaved in this area.
37 */
38
39 #ifdef SENDMAILBUG
40 /*
41 * It appears that some versions of Sendmail will return Code 451
42 * when they don't really want to indicate a failure.
43 * "Code 451 almost always means sendmail has deferred; we don't
44 * really want bomb out at this point since sendmail will rectify
45 * things later." So, if you define SENDMAILBUG, Code 451 is
46 * considered the same as Code 250. Yuck!
47 */
48 #endif
49
50 #define TRUE 1
51 #define FALSE 0
52
53 #define NBITS ((sizeof (int)) * 8)
54
55 /*
56 * these codes must all be different!
57 */
58 #define SM_OPEN 90 /* Changed from 30 in case of nameserver flakiness */
59 #define SM_HELO 20
60 #define SM_RSET 15
61 #define SM_MAIL 40
62 #define SM_RCPT 120
63 #define SM_DATA 20
64 #define SM_TEXT 150
65 #define SM_DOT 180
66 #define SM_QUIT 30
67 #define SM_CLOS 10
68 #define SM_AUTH 45
69
70 static int sm_addrs = 0;
71 static int sm_alarmed = 0;
72 static int sm_child = NOTOK;
73 static int sm_debug = 0;
74 static int sm_nl = TRUE;
75 static int sm_verbose = 0;
76
77 static FILE *sm_rfp = NULL;
78 static FILE *sm_wfp = NULL;
79
80 #ifdef MPOP
81 static int sm_ispool = 0;
82 static char sm_tmpfil[BUFSIZ];
83 #endif /* MPOP */
84
85 #ifdef CYRUS_SASL
86 /*
87 * Some globals needed by SASL
88 */
89
90 static sasl_conn_t *conn = NULL; /* SASL connection state */
91 static int sasl_complete = 0; /* Has authentication succeded? */
92 static sasl_ssf_t sasl_ssf; /* Our security strength factor */
93 static char *sasl_pw_context[2]; /* Context to pass into sm_get_pass */
94 static int maxoutbuf; /* Maximum crypto output buffer */
95 static int sm_get_user(void *, int, const char **, unsigned *);
96 static int sm_get_pass(sasl_conn_t *, void *, int, sasl_secret_t **);
97
98 static sasl_callback_t callbacks[] = {
99 { SASL_CB_USER, sm_get_user, NULL },
100 #define SM_SASL_N_CB_USER 0
101 { SASL_CB_PASS, sm_get_pass, NULL },
102 #define SM_SASL_N_CB_PASS 1
103 { SASL_CB_LIST_END, NULL, NULL },
104 };
105 #endif /* CYRUS_SASL */
106
107 static char *sm_noreply = "No reply text given";
108 static char *sm_moreply = "; ";
109
110 struct smtp sm_reply; /* global... */
111
112 #define MAXEHLO 20
113
114 static int doingEHLO;
115 char *EHLOkeys[MAXEHLO + 1];
116
117 /*
118 * static prototypes
119 */
120 static int smtp_init (char *, char *, int, int, int, int, int, int,
121 char *, char *);
122 static int sendmail_init (char *, char *, int, int, int, int, int);
123
124 static int rclient (char *, char *, char *);
125 static int sm_ierror (char *fmt, ...);
126 static int smtalk (int time, char *fmt, ...);
127 static int sm_wrecord (char *, int);
128 static int sm_wstream (char *, int);
129 static int sm_werror (void);
130 static int smhear (void);
131 static int sm_rrecord (char *, int *);
132 static int sm_rerror (void);
133 static RETSIGTYPE alrmser (int);
134 static char *EHLOset (char *);
135
136 #ifdef MPOP
137 /*
138 * smtp.c's own static copy of several nmh library subroutines
139 */
140 static char **smail_brkstring (char *, char *, char *);
141 static int smail_brkany (char, char *);
142 char **smail_copyip (char **, char **, int);
143 #endif
144
145 #ifdef CYRUS_SASL
146 /*
147 * Function prototypes needed for SASL
148 */
149
150 static int sm_auth_sasl(char *, char *, char *);
151 #endif /* CYRUS_SASL */
152
153 /* from mts/generic/client.c */
154 int client (char *, char *, char *, int, char *, int);
155
156 int
157 sm_init (char *client, char *server, int watch, int verbose,
158 int debug, int onex, int queued, int sasl, char *saslmech,
159 char *user)
160 {
161 if (sm_mts == MTS_SMTP)
162 return smtp_init (client, server, watch, verbose,
163 debug, onex, queued, sasl, saslmech, user);
164 else
165 return sendmail_init (client, server, watch, verbose,
166 debug, onex, queued);
167 }
168
169 static int
170 smtp_init (char *client, char *server, int watch, int verbose,
171 int debug, int onex, int queued, int sasl, char *saslmech,
172 char *user)
173 {
174 #ifdef CYRUS_SASL
175 char *server_mechs;
176 #endif /* CYRUS_SASL */
177 int result, sd1, sd2;
178
179 if (watch)
180 verbose = TRUE;
181
182 sm_verbose = verbose;
183 sm_debug = debug;
184
185 #ifdef MPOP
186 if (sm_ispool)
187 goto all_done;
188 #endif
189
190 if (sm_rfp != NULL && sm_wfp != NULL)
191 goto send_options;
192
193 if (client == NULL || *client == '\0') {
194 if (clientname) {
195 client = clientname;
196 } else {
197 client = LocalName(); /* no clientname -> LocalName */
198 }
199 }
200
201 #ifdef ZMAILER
202 if (client == NULL || *client == '\0')
203 client = "localhost";
204 #endif
205
206 if ((sd1 = rclient (server, "tcp", "smtp")) == NOTOK)
207 return RP_BHST;
208
209 #ifdef MPOP
210 if (sm_ispool) {
211 if (sm_rfp) {
212 alarm (SM_CLOS);
213 fclose (sm_rfp);
214 alarm (0);
215 sm_rfp = NULL;
216 }
217 if ((sm_wfp = fdopen (sd1, "w")) == NULL) {
218 unlink (sm_tmpfil);
219 close (sd1);
220 return sm_ierror ("unable to fdopen");
221 }
222 all_done: ;
223 sm_reply.text[sm_reply.length = 0] = NULL;
224 return (sm_reply.code = RP_OK);
225 }
226 #endif /* MPOP */
227
228 if ((sd2 = dup (sd1)) == NOTOK) {
229 close (sd1);
230 return sm_ierror ("unable to dup");
231 }
232
233 SIGNAL (SIGALRM, alrmser);
234 SIGNAL (SIGPIPE, SIG_IGN);
235
236 if ((sm_rfp = fdopen (sd1, "r")) == NULL
237 || (sm_wfp = fdopen (sd2, "w")) == NULL) {
238 close (sd1);
239 close (sd2);
240 sm_rfp = sm_wfp = NULL;
241 return sm_ierror ("unable to fdopen");
242 }
243
244 sm_alarmed = 0;
245 alarm (SM_OPEN);
246 result = smhear ();
247 alarm (0);
248
249 switch (result) {
250 case 220:
251 break;
252
253 default:
254 sm_end (NOTOK);
255 return RP_RPLY;
256 }
257
258 /*
259 * Give EHLO or HELO command
260 */
261 if (client && *client) {
262 doingEHLO = 1;
263 result = smtalk (SM_HELO, "EHLO %s", client);
264 doingEHLO = 0;
265
266 if (result >= 500 && result <= 599)
267 result = smtalk (SM_HELO, "HELO %s", client);
268
269 if (result != 250) {
270 sm_end (NOTOK);
271 return RP_RPLY;
272 }
273 }
274
275 #ifdef CYRUS_SASL
276 /*
277 * If the user asked for SASL, then check to see if the SMTP server
278 * supports it. Otherwise, error out (because the SMTP server
279 * might have been spoofed; we don't want to just silently not
280 * do authentication
281 */
282
283 if (sasl) {
284 if (! (server_mechs = EHLOset("AUTH"))) {
285 sm_end(NOTOK);
286 return sm_ierror("SMTP server does not support SASL");
287 }
288
289 if (saslmech && stringdex(saslmech, server_mechs) == -1) {
290 sm_end(NOTOK);
291 return sm_ierror("Requested SASL mech \"%s\" is not in the "
292 "list of supported mechanisms:\n%s",
293 saslmech, server_mechs);
294 }
295
296 if (sm_auth_sasl(user, saslmech ? saslmech : server_mechs,
297 server) != RP_OK) {
298 sm_end(NOTOK);
299 return NOTOK;
300 }
301 }
302 #endif /* CYRUS_SASL */
303
304 send_options: ;
305 if (watch && EHLOset ("XVRB"))
306 smtalk (SM_HELO, "VERB on");
307 if (onex && EHLOset ("XONE"))
308 smtalk (SM_HELO, "ONEX");
309 if (queued && EHLOset ("XQUE"))
310 smtalk (SM_HELO, "QUED");
311
312 return RP_OK;
313 }
314
315 int
316 sendmail_init (char *client, char *server, int watch, int verbose,
317 int debug, int onex, int queued)
318 {
319 int i, result, vecp;
320 int pdi[2], pdo[2];
321 char *vec[15];
322
323 if (watch)
324 verbose = TRUE;
325
326 sm_verbose = verbose;
327 sm_debug = debug;
328 if (sm_rfp != NULL && sm_wfp != NULL)
329 return RP_OK;
330
331 if (client == NULL || *client == '\0') {
332 if (clientname)
333 client = clientname;
334 else
335 client = LocalName(); /* no clientname -> LocalName */
336 }
337
338 #ifdef ZMAILER
339 if (client == NULL || *client == '\0')
340 client = "localhost";
341 #endif
342
343 if (pipe (pdi) == NOTOK)
344 return sm_ierror ("no pipes");
345 if (pipe (pdo) == NOTOK) {
346 close (pdi[0]);
347 close (pdi[1]);
348 return sm_ierror ("no pipes");
349 }
350
351 for (i = 0; (sm_child = fork ()) == NOTOK && i < 5; i++)
352 sleep (5);
353
354 switch (sm_child) {
355 case NOTOK:
356 close (pdo[0]);
357 close (pdo[1]);
358 close (pdi[0]);
359 close (pdi[1]);
360 return sm_ierror ("unable to fork");
361
362 case OK:
363 if (pdo[0] != fileno (stdin))
364 dup2 (pdo[0], fileno (stdin));
365 if (pdi[1] != fileno (stdout))
366 dup2 (pdi[1], fileno (stdout));
367 if (pdi[1] != fileno (stderr))
368 dup2 (pdi[1], fileno (stderr));
369 for (i = fileno (stderr) + 1; i < NBITS; i++)
370 close (i);
371
372 vecp = 0;
373 vec[vecp++] = r1bindex (sendmail, '/');
374 vec[vecp++] = "-bs";
375 #ifndef ZMAILER
376 vec[vecp++] = watch ? "-odi" : queued ? "-odq" : "-odb";
377 vec[vecp++] = "-oem";
378 vec[vecp++] = "-om";
379 # ifndef RAND
380 if (verbose)
381 vec[vecp++] = "-ov";
382 # endif /* not RAND */
383 #endif /* not ZMAILER */
384 vec[vecp++] = NULL;
385
386 setgid (getegid ());
387 setuid (geteuid ());
388 execvp (sendmail, vec);
389 fprintf (stderr, "unable to exec ");
390 perror (sendmail);
391 _exit (-1); /* NOTREACHED */
392
393 default:
394 SIGNAL (SIGALRM, alrmser);
395 SIGNAL (SIGPIPE, SIG_IGN);
396
397 close (pdi[1]);
398 close (pdo[0]);
399 if ((sm_rfp = fdopen (pdi[0], "r")) == NULL
400 || (sm_wfp = fdopen (pdo[1], "w")) == NULL) {
401 close (pdi[0]);
402 close (pdo[1]);
403 sm_rfp = sm_wfp = NULL;
404 return sm_ierror ("unable to fdopen");
405 }
406 sm_alarmed = 0;
407 alarm (SM_OPEN);
408 result = smhear ();
409 alarm (0);
410 switch (result) {
411 case 220:
412 break;
413
414 default:
415 sm_end (NOTOK);
416 return RP_RPLY;
417 }
418
419 if (client && *client) {
420 doingEHLO = 1;
421 result = smtalk (SM_HELO, "EHLO %s", client);
422 doingEHLO = 0;
423
424 if (500 <= result && result <= 599)
425 result = smtalk (SM_HELO, "HELO %s", client);
426
427 switch (result) {
428 case 250:
429 break;
430
431 default:
432 sm_end (NOTOK);
433 return RP_RPLY;
434 }
435 }
436
437 #ifndef ZMAILER
438 if (onex)
439 smtalk (SM_HELO, "ONEX");
440 #endif
441 if (watch)
442 smtalk (SM_HELO, "VERB on");
443
444 return RP_OK;
445 }
446 }
447
448 #ifdef MPOP
449 # define MAXARGS 1000
450 #endif /* MPOP */
451
452 static int
453 rclient (char *server, char *protocol, char *service)
454 {
455 int sd;
456 char response[BUFSIZ];
457 #ifdef MPOP
458 char *cp;
459 #endif /* MPOP */
460
461 if ((sd = client (server, protocol, service, FALSE, response, sizeof(response))) != NOTOK)
462 return sd;
463
464 #ifdef MPOP
465 if (!server && servers && (cp = strchr(servers, '/'))) {
466 char **ap;
467 char *arguments[MAXARGS];
468
469 smail_copyip (smail_brkstring (cp = getcpy (servers), " ", "\n"), arguments, MAXARGS);
470
471 for (ap = arguments; *ap; ap++)
472 if (**ap == '/') {
473 char *dp;
474
475 if ((dp = strrchr(*ap, '/')) && *++dp == NULL)
476 *--dp = NULL;
477 snprintf (sm_tmpfil, sizeof(sm_tmpfil), "%s/smtpXXXXXX", *ap);
478 #ifdef HAVE_MKSTEMP
479 sd = mkstemp (sm_tmpfil);
480 #else
481 mktemp (sm_tmpfil);
482
483 if ((sd = creat (sm_tmpfil, 0600)) != NOTOK) {
484 sm_ispool = 1;
485 break;
486 }
487 #endif
488 }
489
490 free (cp);
491 if (sd != NOTOK)
492 return sd;
493 }
494 #endif /* MPOP */
495
496 sm_ierror ("%s", response);
497 return NOTOK;
498 }
499
500 #ifdef CYRUS_SASL
501 #include <sasl.h>
502 #include <sys/socket.h>
503 #include <netinet/in.h>
504 #include <arpa/inet.h>
505 #include <netdb.h>
506 #include <errno.h>
507 #endif /* CYRUS_SASL */
508
509 int
510 sm_winit (int mode, char *from)
511 {
512 char *smtpcom;
513
514 #ifdef MPOP
515 if (sm_ispool && !sm_wfp) {
516 strlen (strcpy (sm_reply.text, "unable to create new spool file"));
517 sm_reply.code = NOTOK;
518 return RP_BHST;
519 }
520 #endif /* MPOP */
521
522 switch (mode) {
523 case S_MAIL:
524 smtpcom = "MAIL";
525 break;
526
527 case S_SEND:
528 smtpcom = "SEND";
529 break;
530
531 case S_SOML:
532 smtpcom = "SOML";
533 break;
534
535 case S_SAML:
536 smtpcom = "SAML";
537 break;
538 }
539
540 switch (smtalk (SM_MAIL, "%s FROM:<%s>", smtpcom, from)) {
541 case 250:
542 sm_addrs = 0;
543 return RP_OK;
544
545 case 500:
546 case 501:
547 case 552:
548 return RP_PARM;
549
550 default:
551 return RP_RPLY;
552 }
553 }
554
555
556 int
557 sm_wadr (char *mbox, char *host, char *path)
558 {
559 switch (smtalk (SM_RCPT, host && *host ? "RCPT TO:<%s%s@%s>"
560 : "RCPT TO:<%s%s>",
561 path ? path : "", mbox, host)) {
562 case 250:
563 case 251:
564 sm_addrs++;
565 return RP_OK;
566
567 case 451:
568 #ifdef SENDMAILBUG
569 sm_addrs++;
570 return RP_OK;
571 #endif /* SENDMAILBUG */
572 case 421:
573 case 450:
574 case 452:
575 return RP_NO;
576
577 case 500:
578 case 501:
579 return RP_PARM;
580
581 case 550:
582 case 551:
583 case 552:
584 case 553:
585 return RP_USER;
586
587 default:
588 return RP_RPLY;
589 }
590 }
591
592
593 int
594 sm_waend (void)
595 {
596 switch (smtalk (SM_DATA, "DATA")) {
597 case 354:
598 sm_nl = TRUE;
599 return RP_OK;
600
601 case 451:
602 #ifdef SENDMAILBUG
603 sm_nl = TRUE;
604 return RP_OK;
605 #endif /* SENDMAILBUG */
606 case 421:
607 return RP_NO;
608
609 case 500:
610 case 501:
611 case 503:
612 case 554:
613 return RP_NDEL;
614
615 default:
616 return RP_RPLY;
617 }
618 }
619
620
621 int
622 sm_wtxt (char *buffer, int len)
623 {
624 int result;
625
626 sm_alarmed = 0;
627 alarm (SM_TEXT);
628 result = sm_wstream (buffer, len);
629 alarm (0);
630
631 return (result == NOTOK ? RP_BHST : RP_OK);
632 }
633
634
635 int
636 sm_wtend (void)
637 {
638 if (sm_wstream ((char *) NULL, 0) == NOTOK)
639 return RP_BHST;
640
641 switch (smtalk (SM_DOT + 3 * sm_addrs, ".")) {
642 case 250:
643 case 251:
644 return RP_OK;
645
646 case 451:
647 #ifdef SENDMAILBUG
648 return RP_OK;
649 #endif /* SENDMAILBUG */
650 case 452:
651 default:
652 return RP_NO;
653
654 case 552:
655 case 554:
656 return RP_NDEL;
657 }
658 }
659
660
661 int
662 sm_end (int type)
663 {
664 int status;
665 struct smtp sm_note;
666
667 if (sm_mts == MTS_SENDMAIL) {
668 switch (sm_child) {
669 case NOTOK:
670 case OK:
671 return RP_OK;
672
673 default:
674 break;
675 }
676 }
677
678 if (sm_rfp == NULL && sm_wfp == NULL)
679 return RP_OK;
680
681 switch (type) {
682 case OK:
683 smtalk (SM_QUIT, "QUIT");
684 break;
685
686 case NOTOK:
687 sm_note.code = sm_reply.code;
688 strncpy (sm_note.text, sm_reply.text, sm_note.length = sm_reply.length);/* fall */
689 case DONE:
690 if (smtalk (SM_RSET, "RSET") == 250 && type == DONE)
691 return RP_OK;
692 if (sm_mts == MTS_SMTP)
693 smtalk (SM_QUIT, "QUIT");
694 else {
695 kill (sm_child, SIGKILL);
696 discard (sm_rfp);
697 discard (sm_wfp);
698 }
699 if (type == NOTOK) {
700 sm_reply.code = sm_note.code;
701 strncpy (sm_reply.text, sm_note.text, sm_reply.length = sm_note.length);
702 }
703 break;
704 }
705
706 #ifdef MPOP
707 if (sm_ispool) {
708 sm_ispool = 0;
709
710 if (sm_wfp) {
711 unlink (sm_tmpfil);
712 fclose (sm_wfp);
713 sm_wfp = NULL;
714 }
715 }
716 #endif /* MPOP */
717
718 if (sm_rfp != NULL) {
719 alarm (SM_CLOS);
720 fclose (sm_rfp);
721 alarm (0);
722 }
723 if (sm_wfp != NULL) {
724 alarm (SM_CLOS);
725 fclose (sm_wfp);
726 alarm (0);
727 }
728
729 if (sm_mts == MTS_SMTP) {
730 status = 0;
731 #ifdef CYRUS_SASL
732 if (conn)
733 sasl_dispose(&conn);
734 #endif /* CYRUS_SASL */
735 } else {
736 status = pidwait (sm_child, OK);
737 sm_child = NOTOK;
738 }
739
740 sm_rfp = sm_wfp = NULL;
741 return (status ? RP_BHST : RP_OK);
742 }
743
744
745 #ifdef MPOP
746
747 int
748 sm_bulk (char *file)
749 {
750 int cc, i, j, k, result;
751 long pos;
752 char *dp, *bp, *cp, s;
753 char buffer[BUFSIZ], sender[BUFSIZ];
754 FILE *fp, *gp;
755
756 gp = NULL;
757 k = strlen (file) - sizeof(".bulk");
758 if ((fp = fopen (file, "r")) == NULL) {
759 int len;
760
761 snprintf (sm_reply.text, sizeof(sm_reply.text),
762 "unable to read %s: ", file);
763 bp = sm_reply.text;
764 len = strlen (bp);
765 bp += len;
766 if ((s = strerror (errno)))
767 strncpy (bp, s, sizeof(sm_reply.text) - len);
768 else
769 snprintf (bp, sizeof(sm_reply.text) - len, "Error %d", errno);
770 sm_reply.length = strlen (sm_reply.text);
771 sm_reply.code = NOTOK;
772 return RP_BHST;
773 }
774 if (sm_debug) {
775 printf ("reading file %s\n", file);
776 fflush (stdout);
777 }
778
779 i = j = 0;
780 while (fgets (buffer, sizeof(buffer), fp)) {
781 if (j++ == 0)
782 strncpy (sender, buffer + sizeof("MAIL FROM:") - 1, sizeof(sender));
783 if (strcmp (buffer, "DATA\r\n") == 0) {
784 i = 1;
785 break;
786 }
787 }
788 if (i == 0) {
789 if (sm_debug) {
790 printf ("no DATA...\n");
791 fflush (stdout);
792 }
793 losing0:
794 snprintf (buffer, sizeof(buffer), "%s.bad", file);
795 rename (file, buffer);
796 if (gp) {
797 snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
798 unlink (buffer);
799 fclose (gp);
800 }
801 fclose (fp);
802 return RP_OK;
803 }
804 if (j < 3) {
805 if (sm_debug) {
806 printf ("no %srecipients...\n", j < 1 ? "sender or " : "");
807 fflush (stdout);
808 }
809 goto losing0;
810 }
811
812 if ((cp = malloc ((size_t) (cc = (pos = ftell (fp)) + 1))) == NULL) {
813 sm_reply.length = strlen (strcpy (sm_reply.text, "out of memory"));
814 losing1: ;
815 sm_reply.code = NOTOK;
816 fclose (fp);
817 return RP_BHST;
818 }
819 fseek (fp, 0L, SEEK_SET);
820 for (dp = cp, i = 0; i++ < j; dp += strlen (dp))
821 if (fgets (dp, cc - (dp - cp), fp) == NULL) {
822 sm_reply.length = strlen (strcpy (sm_reply.text, "premature eof"));
823 losing2:
824 free (cp);
825 goto losing1;
826 }
827 *dp = NULL;
828
829 for (dp = cp, i = cc - 1; i > 0; dp += cc, i -= cc)
830 if ((cc = write (fileno (sm_wfp), dp, i)) == NOTOK) {
831 int len;
832 losing3:
833 strcpy (sm_reply.text, "error writing to server: ",
834 sizeof(sm_reply.text));
835 bp = sm_reply.text;
836 len = strlen (bp);
837 bp += len;
838 if ((s = strerror (errno)))
839 strncpy (bp, s, sizeof(sm_reply.text) - len);
840 else
841 snprintf (bp, sizeof(sm_reply.text) - len,
842 "unknown error %d", errno);
843 sm_reply.length = strlen (sm_reply.text);
844 goto losing2;
845 }
846 else
847 if (sm_debug) {
848 printf ("wrote %d octets to server\n", cc);
849 fflush (stdout);
850 }
851
852 for (dp = cp, i = 0; i++ < j; dp = strchr(dp, '\n'), dp++) {
853 if (sm_debug) {
854 if (bp = strchr(dp, '\r'))
855 *bp = NULL;
856 printf ("=> %s\n", dp);
857 fflush (stdout);
858 if (bp)
859 *bp = '\r';
860 }
861
862 switch (smhear () + (i == 1 ? 1000 : i != j ? 2000 : 3000)) {
863 case 1000 + 250:
864 sm_addrs = 0;
865 result = RP_OK;
866 break;
867
868 case 1000 + 500:
869 case 1000 + 501:
870 case 1000 + 552:
871 case 2000 + 500:
872 case 2000 + 501:
873 result = RP_PARM;
874 smtalk (SM_RSET, "RSET");
875 free (cp);
876 goto losing0;
877
878 case 2000 + 250:
879 case 2000 + 251:
880 sm_addrs++;
881 result = RP_OK;
882 break;
883
884 case 2000 + 451:
885 #ifdef SENDMAILBUG
886 sm_addrs++;
887 result = RP_OK;
888 break;
889 #endif
890 case 2000 + 421:
891 case 2000 + 450:
892 case 2000 + 452:
893 result = RP_NO;
894 goto bad_addr;
895
896 case 2000 + 550:
897 case 2000 + 551:
898 case 2000 + 552:
899 case 2000 + 553:
900 result = RP_USER;
901 bad_addr:
902 if (k <= 0 || strcmp (sender, "<>\r\n") == 0)
903 break;
904 if (gp == NULL) {
905 int l;
906 snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
907 if ((gp = fopen (buffer, "w+")) == NULL)
908 goto bad_data;
909 fprintf (gp, "MAIL FROM:<>\r\nRCPT TO:%sDATA\r\n", sender);
910 l = strlen (sender);
911 fprintf (gp,
912 "To: %*.*s\r\nSubject: Invalid addresses (%s)\r\n",
913 l - 4, l - 4, sender + 1, file);
914 fprintf (gp, "Date: %s\r\nFrom: Postmaster@%s\r\n\r\n",
915 dtimenow (0), LocalName ());
916 }
917 if (bp = strchr(dp, '\r'))
918 *bp = NULL;
919 fprintf (gp, "=> %s\r\n", dp);
920 if (bp)
921 *bp = '\r';
922 fprintf (gp, "<= %s\r\n", rp_string (result));
923 fflush (gp);
924 break;
925
926 case 3000 + 354:
927 #ifdef SENDMAILBUG
928 ok_data:
929 #endif
930 result = RP_OK;
931 break;
932
933 case 3000 + 451:
934 #ifdef SENDMAILBUG
935 goto ok_data;
936 #endif
937 case 3000 + 421:
938 result = RP_NO;
939 bad_data:
940 smtalk (SM_RSET, "RSET");
941 free (cp);
942 if (gp) {
943 snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
944 unlink (buffer);
945 fclose (gp);
946 }
947 fclose (fp);
948 return result;
949
950 case 3000 + 500:
951 case 3000 + 501:
952 case 3000 + 503:
953 case 3000 + 554:
954 smtalk (SM_RSET, "RSET");
955 free (cp);
956 goto no_dice;
957
958 default:
959 result = RP_RPLY;
960 goto bad_data;
961 }
962 }
963 free (cp);
964
965 {
966 #ifdef HAVE_ST_BLKSIZE
967 struct stat st;
968
969 if (fstat (fileno (sm_wfp), &st) == NOTOK || (cc = st.st_blksize) < BUFSIZ)
970 cc = BUFSIZ;
971 #else
972 cc = BUFSIZ;
973 #endif
974 if ((cp = malloc ((size_t) cc)) == NULL) {
975 smtalk (SM_RSET, "RSET");
976 sm_reply.length = strlen (strcpy (sm_reply.text, "out of memory"));
977 goto losing1;
978 }
979 }
980
981 fseek (fp, pos, SEEK_SET);
982 for (;;) {
983 int eof = 0;
984
985 for (dp = cp, i = cc; i > 0; dp += j, i -= j)
986 if ((j = fread (cp, sizeof(*cp), i, fp)) == OK) {
987 if (ferror (fp)) {
988 int len;
989
990 snprintf (sm_reply.text, sizeof(sm_reply.text),
991 "error reading %s: ", file);
992 bp = sm_reply.text;
993 len = strlen (bp);
994 bp += len;
995 if ((s = strerror (errno)))
996 strncpy (bp, s, sizeof(sm_reply.text) - len);
997 else
998 snprintf (bp, sizeof(sm_reply.text) - len,
999 "unknown error %d", errno);
1000 sm_reply.length = strlen (sm_reply.text);
1001 goto losing2;
1002 }
1003 cc = dp - cp;
1004 eof = 1;
1005 break;
1006 }
1007
1008 for (dp = cp, i = cc; i > 0; dp += j, i -= j)
1009 if ((j = write (fileno (sm_wfp), dp, i)) == NOTOK)
1010 goto losing3;
1011 else
1012 if (sm_debug) {
1013 printf ("wrote %d octets to server\n", j);
1014 fflush (stdout);
1015 }
1016
1017 if (eof)
1018 break;
1019 }
1020 free (cp);
1021
1022 switch (smhear ()) {
1023 case 250:
1024 case 251:
1025 #ifdef SENDMAILBUG
1026 ok_dot:
1027 #endif
1028 result = RP_OK;
1029 unlink (file);
1030 break;
1031
1032 case 451:
1033 #ifdef SENDMAILBUG
1034 goto ok_dot;
1035 #endif
1036 case 452:
1037 default:
1038 result = RP_NO;
1039 if (gp) {
1040 snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
1041 unlink (buffer);
1042 fclose (gp);
1043 gp = NULL;
1044 }
1045 break;
1046
1047 case 552:
1048 case 554:
1049 no_dice:
1050 result = RP_NDEL;
1051 if (k <= 0 || strcmp (sender, "<>\r\n") == 0) {
1052 unlink (file);
1053 break;
1054 }
1055 if (gp) {
1056 fflush (gp);
1057 ftruncate (fileno (gp), 0L);
1058 fseek (gp, 0L, SEEK_SET);
1059 }
1060 else {
1061 snprintf (buffer, sizeof(buffer), "%*.*sA.bulk", k, k, file);
1062 if ((gp = fopen (buffer, "w")) == NULL)
1063 break;
1064 }
1065 fprintf (gp, "MAIL FROM:<>\r\nRCPT TO:%sDATA\r\n", sender);
1066 i = strlen (sender);
1067 fprintf (gp, "To: %*.*s\r\nSubject: Failed mail (%s)\r\n",
1068 i - 4, i - 4, sender + 1, file);
1069 fprintf (gp, "Date: %s\r\nFrom: Postmaster@%s\r\n\r\n",
1070 dtimenow (0), LocalName ());
1071 break;
1072 }
1073
1074 if (gp) {
1075 fputs ("\r\n------- Begin Returned message\r\n\r\n", gp);
1076 fseek (fp, pos, SEEK_SET);
1077 while (fgets (buffer, sizeof(buffer), fp)) {
1078 if (buffer[0] == '-')
1079 fputs ("- ", gp);
1080 if (strcmp (buffer, ".\r\n"))
1081 fputs (buffer, gp);
1082 }
1083 fputs ("\r\n------- End Returned Message\r\n\r\n.\r\n", gp);
1084 fflush (gp);
1085 if (!ferror (gp))
1086 unlink (file);
1087 fclose (gp);
1088 }
1089 fclose (fp);
1090
1091 return result;
1092 }
1093 #endif /* MPOP */
1094
1095
1096 #ifdef CYRUS_SASL
1097 /*
1098 * This function implements SASL authentication for SMTP. If this function
1099 * completes successfully, then authentication is successful and we've
1100 * (optionally) negotiated a security layer.
1101 *
1102 * Right now we don't support session encryption.
1103 */
1104 static int
1105 sm_auth_sasl(char *user, char *mechlist, char *host)
1106 {
1107 int result, status, outlen;
1108 unsigned int buflen;
1109 char *buf, outbuf[BUFSIZ];
1110 const char *chosen_mech;
1111 sasl_security_properties_t secprops;
1112 sasl_external_properties_t extprops;
1113 sasl_ssf_t *ssf;
1114 int *outbufmax;
1115
1116 /*
1117 * Initialize the callback contexts
1118 */
1119
1120 if (user == NULL)
1121 user = getusername();
1122
1123 callbacks[SM_SASL_N_CB_USER].context = user;
1124
1125 /*
1126 * This is a _bit_ of a hack ... but if the hostname wasn't supplied
1127 * to us on the command line, then call getpeername and do a
1128 * reverse-address lookup on the IP address to get the name.
1129 */
1130
1131 if (!host) {
1132 struct sockaddr_in sin;
1133 int len = sizeof(sin);
1134 struct hostent *hp;
1135
1136 if (getpeername(fileno(sm_wfp), (struct sockaddr *) &sin, &len) < 0) {
1137 sm_ierror("getpeername on SMTP socket failed: %s",
1138 strerror(errno));
1139 return NOTOK;
1140 }
1141
1142 if ((hp = gethostbyaddr((void *) &sin.sin_addr, sizeof(sin.sin_addr),
1143 sin.sin_family)) == NULL) {
1144 sm_ierror("DNS lookup on IP address %s failed",
1145 inet_ntoa(sin.sin_addr));
1146 return NOTOK;
1147 }
1148
1149 host = strdup(hp->h_name);
1150 }
1151
1152 sasl_pw_context[0] = host;
1153 sasl_pw_context[1] = user;
1154
1155 callbacks[SM_SASL_N_CB_PASS].context = sasl_pw_context;
1156
1157 result = sasl_client_init(callbacks);
1158
1159 if (result != SASL_OK) {
1160 sm_ierror("SASL library initialization failed: %s",
1161 sasl_errstring(result, NULL, NULL));
1162 return NOTOK;
1163 }
1164
1165 result = sasl_client_new("smtp", host, NULL, SASL_SECURITY_LAYER, &conn);
1166
1167 if (result != SASL_OK) {
1168 sm_ierror("SASL client initialization failed: %s",
1169 sasl_errstring(result, NULL, NULL));
1170 return NOTOK;
1171 }
1172
1173 /*
1174 * Initialize the security properties
1175 */
1176
1177 memset(&secprops, 0, sizeof(secprops));
1178 secprops.maxbufsize = BUFSIZ;
1179 secprops.max_ssf = 0; /* XXX change this when we do encryption */
1180 memset(&extprops, 0, sizeof(extprops));
1181
1182 result = sasl_setprop(conn, SASL_SEC_PROPS, &secprops);
1183
1184 if (result != SASL_OK) {
1185 sm_ierror("SASL security property initialization failed: %s",
1186 sasl_errstring(result, NULL, NULL));
1187 return NOTOK;
1188 }
1189
1190 result = sasl_setprop(conn, SASL_SSF_EXTERNAL, &extprops);
1191
1192 if (result != SASL_OK) {
1193 sm_ierror("SASL external property initialization failed: %s",
1194 sasl_errstring(result, NULL, NULL));
1195 return NOTOK;
1196 }
1197
1198 /*
1199 * Start the actual protocol. Feed the mech list into the library
1200 * and get out a possible initial challenge
1201 */
1202
1203 result = sasl_client_start(conn, mechlist, NULL, NULL, &buf, &buflen,
1204 &chosen_mech);
1205
1206 if (result != SASL_OK && result != SASL_CONTINUE) {
1207 sm_ierror("SASL client start failed: %s",
1208 sasl_errstring(result, NULL, NULL));
1209 return NOTOK;
1210 }
1211
1212 /*
1213 * If we got an initial challenge, send it as part of the AUTH
1214 * command; otherwise, just send a plain AUTH command.
1215 */
1216
1217 if (buflen) {
1218 status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL);
1219 free(buf);
1220 if (status != SASL_OK) {
1221 sm_ierror("SASL base64 encode failed: %s",
1222 sasl_errstring(status, NULL, NULL));
1223 return NOTOK;
1224 }
1225
1226 status = smtalk(SM_AUTH, "AUTH %s %s", chosen_mech, outbuf);
1227 } else
1228 status = smtalk(SM_AUTH, "AUTH %s", chosen_mech);
1229
1230 /*
1231 * Now we loop until we either fail, get a SASL_OK, or a 235
1232 * response code. Receive the challenges and process them until
1233 * we're all done.
1234 */
1235
1236 while (result == SASL_CONTINUE) {
1237
1238 /*
1239 * If we get a 235 response, that means authentication has
1240 * succeeded and we need to break out of the loop (yes, even if
1241 * we still get SASL_CONTINUE from sasl_client_step()).
1242 *
1243 * Otherwise, if we get a message that doesn't seem to be a
1244 * valid response, then abort
1245 */
1246
1247 if (status == 235)
1248 break;
1249 else if (status < 300 || status > 399)
1250 return RP_BHST;
1251
1252 /*
1253 * Special case; a zero-length response from the SMTP server
1254 * is returned as a single =. If we get that, then set buflen
1255 * to be zero. Otherwise, just decode the response.
1256 */
1257
1258 if (strcmp("=", sm_reply.text) == 0) {
1259 outlen = 0;
1260 } else {
1261 result = sasl_decode64(sm_reply.text, sm_reply.length,
1262 outbuf, &outlen);
1263
1264 if (result != SASL_OK) {
1265 smtalk(SM_AUTH, "*");
1266 sm_ierror("SASL base64 decode failed: %s",
1267 sasl_errstring(result, NULL, NULL));
1268 return NOTOK;
1269 }
1270 }
1271
1272 result = sasl_client_step(conn, outbuf, outlen, NULL, &buf, &buflen);
1273
1274 if (result != SASL_OK && result != SASL_CONTINUE) {
1275 smtalk(SM_AUTH, "*");
1276 sm_ierror("SASL client negotiation failed: %s",
1277 sasl_errstring(result, NULL, NULL));
1278 return NOTOK;
1279 }
1280
1281 status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL);
1282 free(buf);
1283
1284 if (status != SASL_OK) {
1285 smtalk(SM_AUTH, "*");
1286 sm_ierror("SASL base64 encode failed: %s",
1287 sasl_errstring(status, NULL, NULL));
1288 return NOTOK;
1289 }
1290
1291 status = smtalk(SM_AUTH, outbuf);
1292 }
1293
1294 /*
1295 * Make sure that we got the correct response
1296 */
1297
1298 if (status < 200 || status > 299)
1299 return RP_BHST;
1300
1301 /*
1302 * Depending on the mechanism, we need to do a FINAL call to
1303 * sasl_client_step(). Do that now.
1304 */
1305
1306 result = sasl_client_step(conn, NULL, 0, NULL, &buf, &buflen);
1307
1308 if (result != SASL_OK) {
1309 sm_ierror("SASL final client negotiation failed: %s",
1310 sasl_errstring(result, NULL, NULL));
1311 return NOTOK;
1312 }
1313
1314 /*
1315 * We _should_ have completed the authentication successfully.
1316 * Get a few properties from the authentication exchange.
1317 */
1318
1319 result = sasl_getprop(conn, SASL_MAXOUTBUF, (void **) &outbufmax);
1320
1321 if (result != SASL_OK) {
1322 sm_ierror("Cannot retrieve SASL negotiated output buffer size: %s",
1323 sasl_errstring(result, NULL, NULL));
1324 return NOTOK;
1325 }
1326
1327 maxoutbuf = *outbufmax;
1328
1329 result = sasl_getprop(conn, SASL_SSF, (void **) &ssf);
1330
1331 sasl_ssf = *ssf;
1332
1333 if (result != SASL_OK) {
1334 sm_ierror("Cannot retrieve SASL negotiated security strength "
1335 "factor: %s", sasl_errstring(result, NULL, NULL));
1336 return NOTOK;
1337 }
1338
1339 if (maxoutbuf == 0 || maxoutbuf > BUFSIZ)
1340 maxoutbuf = BUFSIZ;
1341
1342 sasl_complete = 1;
1343
1344 return RP_OK;
1345 }
1346
1347 /*
1348 * Our callback functions to feed data to the SASL library
1349 */
1350
1351 static int
1352 sm_get_user(void *context, int id, const char **result, unsigned *len)
1353 {
1354 char *user = (char *) context;
1355
1356 if (! result || id != SASL_CB_USER)
1357 return SASL_BADPARAM;
1358
1359 *result = user;
1360 if (len)
1361 *len = strlen(user);
1362
1363 return SASL_OK;
1364 }
1365
1366 static int
1367 sm_get_pass(sasl_conn_t *conn, void *context, int id,
1368 sasl_secret_t **psecret)
1369 {
1370 char **pw_context = (char **) context;
1371 char *pass = NULL;
1372 int len;
1373
1374 if (! psecret || id != SASL_CB_PASS)
1375 return SASL_BADPARAM;
1376
1377 ruserpass(pw_context[0], &(pw_context[1]), &pass);
1378
1379 len = strlen(pass);
1380
1381 *psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len);
1382
1383 if (! *psecret) {
1384 free(pass);
1385 return SASL_NOMEM;
1386 }
1387
1388 (*psecret)->len = len;
1389 strcpy((*psecret)->data, pass);
1390 /* free(pass); */
1391
1392 return SASL_OK;
1393 }
1394 #endif /* CYRUS_SASL */
1395
1396 static int
1397 sm_ierror (char *fmt, ...)
1398 {
1399 va_list ap;
1400
1401 va_start(ap, fmt);
1402 vsnprintf (sm_reply.text, sizeof(sm_reply.text), fmt, ap);
1403 va_end(ap);
1404
1405 sm_reply.length = strlen (sm_reply.text);
1406 sm_reply.code = NOTOK;
1407
1408 return RP_BHST;
1409 }
1410
1411
1412 static int
1413 smtalk (int time, char *fmt, ...)
1414 {
1415 va_list ap;
1416 int result;
1417 char buffer[BUFSIZ];
1418
1419 va_start(ap, fmt);
1420 vsnprintf (buffer, sizeof(buffer), fmt, ap);
1421 va_end(ap);
1422
1423 if (sm_debug) {
1424 printf ("=> %s\n", buffer);
1425 fflush (stdout);
1426 }
1427
1428 #ifdef MPOP
1429 if (sm_ispool) {
1430 char file[BUFSIZ];
1431
1432 if (strcmp (buffer, ".") == 0)
1433 time = SM_DOT;
1434 fprintf (sm_wfp, "%s\r\n", buffer);
1435 switch (time) {
1436 case SM_DOT:
1437 fflush (sm_wfp);
1438 if (ferror (sm_wfp))
1439 return sm_werror ();
1440 snprintf (file, sizeof(file), "%s%c.bulk", sm_tmpfil,
1441 (char) (sm_ispool + 'a' - 1));
1442 if (rename (sm_tmpfil, file) == NOTOK) {
1443 int len;
1444 char *bp;
1445
1446 snprintf (sm_reply.text, sizeof(sm_reply.text),
1447 "error renaming %s to %s: ", sm_tmpfil, file);
1448 bp = sm_reply.text;
1449 len = strlen (bp);
1450 bp += len;
1451 if ((s = strerror (errno)))
1452 strncpy (bp, s, sizeof(sm_reply.text) - len);
1453 else
1454 snprintf (bp, sizeof(sm_reply.text) - len,
1455 "unknown error %d", errno);
1456 sm_reply.length = strlen (sm_reply.text);
1457 sm_reply.code = NOTOK;
1458 return RP_BHST;
1459 }
1460 fclose (sm_wfp);
1461 if (sm_wfp = fopen (sm_tmpfil, "w"))
1462 chmod (sm_tmpfil, 0600);
1463 sm_ispool++;
1464 /* and fall... */
1465
1466 case SM_MAIL:
1467 case SM_RCPT:
1468 result = 250;
1469 break;
1470
1471 case SM_RSET:
1472 fflush (sm_wfp);
1473 ftruncate (fileno (sm_wfp), 0L);
1474 fseek (sm_wfp, 0L, SEEK_SET);
1475 result = 250;
1476 break;
1477
1478 case SM_DATA:
1479 result = 354;
1480 break;
1481
1482 case SM_QUIT:
1483 unlink (sm_tmpfil);
1484 sm_ispool = 0;
1485 result = 221;
1486 break;
1487
1488 default:
1489 result = 500;
1490 break;
1491 }
1492 if (sm_debug) {
1493 printf ("<= %d\n", result);
1494 fflush (stdout);
1495 }
1496
1497 sm_reply.text[sm_reply.length = 0] = NULL;
1498 return (sm_reply.code = result);
1499 }
1500 #endif /* MPOP */
1501
1502 sm_alarmed = 0;
1503 alarm ((unsigned) time);
1504 if ((result = sm_wrecord (buffer, strlen (buffer))) != NOTOK)
1505 result = smhear ();
1506 alarm (0);
1507
1508 return result;
1509 }
1510
1511
1512 /*
1513 * write the buffer to the open SMTP channel
1514 */
1515
1516 static int
1517 sm_wrecord (char *buffer, int len)
1518 {
1519 if (sm_wfp == NULL)
1520 return sm_werror ();
1521
1522 fwrite (buffer, sizeof(*buffer), len, sm_wfp);
1523 fputs ("\r\n", sm_wfp);
1524 fflush (sm_wfp);
1525
1526 return (ferror (sm_wfp) ? sm_werror () : OK);
1527 }
1528
1529
1530 static int
1531 sm_wstream (char *buffer, int len)
1532 {
1533 char *bp;
1534 static char lc = '\0';
1535
1536 if (sm_wfp == NULL)
1537 return sm_werror ();
1538
1539 if (buffer == NULL && len == 0) {
1540 if (lc != '\n')
1541 fputs ("\r\n", sm_wfp);
1542 lc = '\0';
1543 return (ferror (sm_wfp) ? sm_werror () : OK);
1544 }
1545
1546 for (bp = buffer; len > 0; bp++, len--) {
1547 switch (*bp) {
1548 case '\n':
1549 sm_nl = TRUE;
1550 fputc ('\r', sm_wfp);
1551 break;
1552
1553 case '.':
1554 if (sm_nl)
1555 fputc ('.', sm_wfp);/* FALL THROUGH */
1556 default:
1557 sm_nl = FALSE;
1558 }
1559 fputc (*bp, sm_wfp);
1560 if (ferror (sm_wfp))
1561 return sm_werror ();
1562 }
1563
1564 if (bp > buffer)
1565 lc = *--bp;
1566 return (ferror (sm_wfp) ? sm_werror () : OK);
1567 }
1568
1569
1570 /*
1571 * On some systems, strlen and strcpy are defined as preprocessor macros. This
1572 * causes compile problems with the #ifdef MPOP in the middle. Should the
1573 * #ifdef MPOP be removed, remove these #undefs.
1574 */
1575 #ifdef strlen
1576 # undef strlen
1577 #endif
1578 #ifdef strcpy
1579 # undef strcpy
1580 #endif
1581
1582 static int
1583 sm_werror (void)
1584 {
1585 sm_reply.length =
1586 strlen (strcpy (sm_reply.text, sm_wfp == NULL ? "no socket opened"
1587 : sm_alarmed ? "write to socket timed out"
1588 #ifdef MPOP
1589 : sm_ispool ? "error writing to spool file"
1590 #endif
1591 : "error writing to socket"));
1592
1593 return (sm_reply.code = NOTOK);
1594 }
1595
1596
1597 static int
1598 smhear (void)
1599 {
1600 int i, code, cont, bc, rc, more;
1601 char *bp, *rp;
1602 char **ehlo, buffer[BUFSIZ];
1603
1604 if (doingEHLO) {
1605 static int at_least_once = 0;
1606
1607 if (at_least_once) {
1608 char *ep;
1609
1610 for (ehlo = EHLOkeys; *ehlo; ehlo++) {
1611 ep = *ehlo;
1612 free (ep);
1613 }
1614 } else {
1615 at_least_once = 1;
1616 }
1617
1618 ehlo = EHLOkeys;
1619 *ehlo = NULL;
1620 }
1621
1622 again: ;
1623
1624 sm_reply.length = 0;
1625 sm_reply.text[0] = 0;
1626 rp = sm_reply.text;
1627 rc = sizeof(sm_reply.text) - 1;
1628
1629 for (more = FALSE; sm_rrecord (bp = buffer, &bc) != NOTOK;) {
1630 if (sm_debug) {
1631 printf ("<= %s\n", buffer);
1632 fflush (stdout);
1633 }
1634
1635 if (doingEHLO
1636 && strncmp (buffer, "250", sizeof("250") - 1) == 0
1637 && (buffer[3] == '-' || doingEHLO == 2)
1638 && buffer[4]) {
1639 if (doingEHLO == 2) {
1640 if ((*ehlo = malloc ((size_t) (strlen (buffer + 4) + 1)))) {
1641 strcpy (*ehlo++, buffer + 4);
1642 *ehlo = NULL;
1643 if (ehlo >= EHLOkeys + MAXEHLO)
1644 doingEHLO = 0;
1645 }
1646 else
1647 doingEHLO = 0;
1648 }
1649 else
1650 doingEHLO = 2;
1651 }
1652
1653 for (; bc > 0 && (!isascii (*bp) || !isdigit (*bp)); bp++, bc--)
1654 continue;
1655
1656 cont = FALSE;
1657 code = atoi (bp);
1658 bp += 3, bc -= 3;
1659 for (; bc > 0 && isspace (*bp); bp++, bc--)
1660 continue;
1661 if (bc > 0 && *bp == '-') {
1662 cont = TRUE;
1663 bp++, bc--;
1664 for (; bc > 0 && isspace (*bp); bp++, bc--)
1665 continue;
1666 }
1667
1668 if (more) {
1669 if (code != sm_reply.code || cont)
1670 continue;
1671 more = FALSE;
1672 } else {
1673 sm_reply.code = code;
1674 more = cont;
1675 if (bc <= 0) {
1676 strncpy (buffer, sm_noreply, sizeof(buffer));
1677 bp = buffer;
1678 bc = strlen (sm_noreply);
1679 }
1680 }
1681
1682 if ((i = min (bc, rc)) > 0) {
1683 strncpy (rp, bp, i);
1684 rp += i;
1685 rc -= i;
1686 if (more && rc > strlen (sm_moreply) + 1) {
1687 strncpy (sm_reply.text + rc, sm_moreply, sizeof(sm_reply.text) - rc);
1688 rc += strlen (sm_moreply);
1689 }
1690 }
1691 if (more)
1692 continue;
1693 if (sm_reply.code < 100) {
1694 if (sm_verbose) {
1695 printf ("%s\n", sm_reply.text);
1696 fflush (stdout);
1697 }
1698 goto again;
1699 }
1700
1701 sm_reply.length = rp - sm_reply.text;
1702 return sm_reply.code;
1703 }
1704 return NOTOK;
1705 }
1706
1707
1708 static int
1709 sm_rrecord (char *buffer, int *len)
1710 {
1711 if (sm_rfp == NULL)
1712 return sm_rerror ();
1713
1714 buffer[*len = 0] = 0;
1715
1716 fgets (buffer, BUFSIZ, sm_rfp);
1717 *len = strlen (buffer);
1718 if (ferror (sm_rfp) || feof (sm_rfp))
1719 return sm_rerror ();
1720 if (buffer[*len - 1] != '\n')
1721 while (getc (sm_rfp) != '\n' && !ferror (sm_rfp) && !feof (sm_rfp))
1722 continue;
1723 else
1724 if (buffer[*len - 2] == '\r')
1725 *len -= 1;
1726 buffer[*len - 1] = 0;
1727
1728 return OK;
1729 }
1730
1731
1732 static int
1733 sm_rerror (void)
1734 {
1735 if (sm_mts == MTS_SMTP)
1736 sm_reply.length =
1737 strlen (strcpy (sm_reply.text, sm_rfp == NULL ? "no socket opened"
1738 : sm_alarmed ? "read from socket timed out"
1739 : feof (sm_rfp) ? "premature end-of-file on socket"
1740 : "error reading from socket"));
1741 else
1742 sm_reply.length =
1743 strlen (strcpy (sm_reply.text, sm_rfp == NULL ? "no pipe opened"
1744 : sm_alarmed ? "read from pipe timed out"
1745 : feof (sm_rfp) ? "premature end-of-file on pipe"
1746 : "error reading from pipe"));
1747
1748 return (sm_reply.code = NOTOK);
1749 }
1750
1751
1752 static RETSIGTYPE
1753 alrmser (int i)
1754 {
1755 #ifndef RELIABLE_SIGNALS
1756 SIGNAL (SIGALRM, alrmser);
1757 #endif
1758
1759 sm_alarmed++;
1760 if (sm_debug) {
1761 printf ("timed out...\n");
1762 fflush (stdout);
1763 }
1764 }
1765
1766
1767 char *
1768 rp_string (int code)
1769 {
1770 char *text;
1771 static char buffer[BUFSIZ];
1772
1773 switch (sm_reply.code != NOTOK ? code : NOTOK) {
1774 case RP_AOK:
1775 text = "AOK";
1776 break;
1777
1778 case RP_MOK:
1779 text = "MOK";
1780 break;
1781
1782 case RP_OK:
1783 text = "OK";
1784 break;
1785
1786 case RP_RPLY:
1787 text = "RPLY";
1788 break;
1789
1790 case RP_BHST:
1791 default:
1792 text = "BHST";
1793 snprintf (buffer, sizeof(buffer), "[%s] %s", text, sm_reply.text);
1794 return buffer;
1795
1796 case RP_PARM:
1797 text = "PARM";
1798 break;
1799
1800 case RP_NO:
1801 text = "NO";
1802 break;
1803
1804 case RP_USER:
1805 text = "USER";
1806 break;
1807
1808 case RP_NDEL:
1809 text = "NDEL";
1810 break;
1811 }
1812
1813 snprintf (buffer, sizeof(buffer), "[%s] %3d %s",
1814 text, sm_reply.code, sm_reply.text);
1815 return buffer;
1816 }
1817
1818
1819 #ifdef MPOP
1820
1821 static char *broken[MAXARGS + 1];
1822
1823 static char **
1824 smail_brkstring (char *strg, char *brksep, char *brkterm)
1825 {
1826 int bi;
1827 char c, *sp;
1828
1829 sp = strg;
1830
1831 for (bi = 0; bi < MAXARGS; bi++) {
1832 while (smail_brkany (c = *sp, brksep))
1833 *sp++ = 0;
1834 if (!c || smail_brkany (c, brkterm)) {
1835 *sp = 0;
1836 broken[bi] = 0;
1837 return broken;
1838 }
1839
1840 broken[bi] = sp;
1841 while ((c = *++sp) && !smail_brkany (c, brksep) && !smail_brkany (c, brkterm))
1842 continue;
1843 }
1844 broken[MAXARGS] = 0;
1845
1846 return broken;
1847 }
1848
1849
1850 /*
1851 * returns 1 if chr in strg, 0 otherwise
1852 */
1853 static int
1854 smail_brkany (char chr, char *strg)
1855 {
1856 char *sp;
1857
1858 if (strg)
1859 for (sp = strg; *sp; sp++)
1860 if (chr == *sp)
1861 return 1;
1862 return 0;
1863 }
1864
1865 /*
1866 * copy a string array and return pointer to end
1867 */
1868 char **
1869 smail_copyip (char **p, char **q, int len_q)
1870 {
1871 while (*p && --len_q > 0)
1872 *q++ = *p++;
1873
1874 *q = NULL;
1875
1876 return q;
1877 }
1878
1879 #endif /* MPOP */
1880
1881
1882 static char *
1883 EHLOset (char *s)
1884 {
1885 size_t len;
1886 char *ep, **ehlo;
1887
1888 len = strlen (s);
1889
1890 for (ehlo = EHLOkeys; *ehlo; ehlo++) {
1891 ep = *ehlo;
1892 if (strncmp (ep, s, len) == 0) {
1893 for (ep += len; *ep == ' '; ep++)
1894 continue;
1895 return ep;
1896 }
1897 }
1898
1899 return 0;
1900 }