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