Removed a remaining RETSIGTYPE. Also change a few locals from int to unsigned to...
[mmh] / mts / smtp / smtp.c
1 /*
2  * smtp.c -- nmh SMTP interface
3  *
4  * This code is Copyright (c) 2002, by the authors of nmh.  See the
5  * COPYRIGHT file in the root directory of the nmh distribution for
6  * complete copyright information.
7  */
8
9 #include <h/mh.h>
10 #include "smtp.h"
11 #include <h/mts.h>
12 #include <signal.h>
13 #include <h/signals.h>
14
15 #ifdef CYRUS_SASL
16 #include <sasl/sasl.h>
17 #include <sasl/saslutil.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 #ifdef TLS_SUPPORT
26 #include <openssl/ssl.h>
27 #include <openssl/err.h>
28 #endif /* TLS_SUPPORT */
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 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 char *sasl_outbuffer;            /* SASL output buffer for encryption */
96 static int sasl_outbuflen;              /* Current length of data in outbuf */
97 static int sm_get_user(void *, int, const char **, unsigned *);
98 static int sm_get_pass(sasl_conn_t *, void *, int, sasl_secret_t **);
99
100 static sasl_callback_t callbacks[] = {
101     { SASL_CB_USER, sm_get_user, NULL },
102 #define SM_SASL_N_CB_USER 0
103     { SASL_CB_PASS, sm_get_pass, NULL },
104 #define SM_SASL_N_CB_PASS 1
105     { SASL_CB_AUTHNAME, sm_get_user, NULL },
106 #define SM_SASL_N_CB_AUTHNAME 2
107     { SASL_CB_LIST_END, NULL, NULL },
108 };
109
110 #else /* CYRUS_SASL */
111 int sasl_ssf = 0;
112 #endif /* CYRUS_SASL */
113
114 #ifdef TLS_SUPPORT
115 static SSL_CTX *sslctx = NULL;
116 static SSL *ssl = NULL;
117 static BIO *sbior = NULL;
118 static BIO *sbiow = NULL;
119 static BIO *io = NULL;
120 #endif /* TLS_SUPPORT */
121
122 #if defined(CYRUS_SASL) || defined(TLS_SUPPORT)
123 #define SASL_MAXRECVBUF 65536
124 static int sm_fgetc(FILE *);
125 static char *sasl_inbuffer;             /* SASL input buffer for encryption */
126 static char *sasl_inptr;                /* Pointer to current inbuf position */
127 static int sasl_inbuflen;               /* Current length of data in inbuf */
128 #else
129 #define sm_fgetc fgetc
130 #endif
131
132 static int tls_active = 0;
133
134 static char *sm_noreply = "No reply text given";
135 static char *sm_moreply = "; ";
136
137 struct smtp sm_reply;           /* global... */
138
139 #define MAXEHLO 20
140
141 static int doingEHLO;
142 char *EHLOkeys[MAXEHLO + 1];
143
144 /*
145  * static prototypes
146  */
147 static int smtp_init (char *, char *, char *, int, int, int, int, int, int,
148                       char *, char *, int);
149 static int sendmail_init (char *, char *, int, int, int, int, int, int,
150                           char *, char *);
151
152 static int rclient (char *, char *);
153 static int sm_ierror (char *fmt, ...);
154 static int smtalk (int time, char *fmt, ...);
155 static int sm_wrecord (char *, int);
156 static int sm_wstream (char *, int);
157 static int sm_werror (void);
158 static int smhear (void);
159 static int sm_rrecord (char *, int *);
160 static int sm_rerror (int);
161 static void alrmser (int);
162 static char *EHLOset (char *);
163 static int sm_fwrite(char *, int);
164 static int sm_fputs(char *);
165 static int sm_fputc(int);
166 static void sm_fflush(void);
167 static int sm_fgets(char *, int, FILE *);
168
169 #ifdef CYRUS_SASL
170 /*
171  * Function prototypes needed for SASL
172  */
173
174 static int sm_auth_sasl(char *, char *, char *);
175 #endif /* CYRUS_SASL */
176
177 int
178 sm_init (char *client, char *server, char *port, int watch, int verbose,
179          int debug, int onex, int queued, int sasl, char *saslmech,
180          char *user, int tls)
181 {
182     if (sm_mts == MTS_SMTP)
183         return smtp_init (client, server, port, watch, verbose,
184                           debug, onex, queued, sasl, saslmech, user, tls);
185     else
186         return sendmail_init (client, server, watch, verbose,
187                               debug, onex, queued, sasl, saslmech, user);
188 }
189
190 static int
191 smtp_init (char *client, char *server, char *port, int watch, int verbose,
192            int debug, int onex, int queued,
193            int sasl, char *saslmech, char *user, int tls)
194 {
195 #ifdef CYRUS_SASL
196     char *server_mechs;
197 #endif /* CYRUS_SASL */
198     int result, sd1, sd2;
199
200     if (watch)
201         verbose = TRUE;
202
203     sm_verbose = verbose;
204     sm_debug = debug;
205
206     if (sm_rfp != NULL && sm_wfp != NULL)
207         goto send_options;
208
209     if (client == NULL || *client == '\0') {
210         if (clientname) {
211             client = clientname;
212         } else {
213             client = LocalName();       /* no clientname -> LocalName */
214         }
215     }
216
217     /*
218      * Last-ditch check just in case client still isn't set to anything
219      */
220
221     if (client == NULL || *client == '\0')
222         client = "localhost";
223
224 #if defined(CYRUS_SASL) || defined(TLS_SUPPORT)
225     sasl_inbuffer = malloc(SASL_MAXRECVBUF);
226     if (!sasl_inbuffer)
227         return sm_ierror("Unable to allocate %d bytes for read buffer",
228                          SASL_MAXRECVBUF);
229 #endif /* CYRUS_SASL || TLS_SUPPORT */
230
231     if ((sd1 = rclient (server, port)) == NOTOK)
232         return RP_BHST;
233
234     if ((sd2 = dup (sd1)) == NOTOK) {
235         close (sd1);
236         return sm_ierror ("unable to dup");
237     }
238
239     SIGNAL (SIGALRM, alrmser);
240     SIGNAL (SIGPIPE, SIG_IGN);
241
242     if ((sm_rfp = fdopen (sd1, "r")) == NULL
243             || (sm_wfp = fdopen (sd2, "w")) == NULL) {
244         close (sd1);
245         close (sd2);
246         sm_rfp = sm_wfp = NULL;
247         return sm_ierror ("unable to fdopen");
248     }
249
250     tls_active = 0;
251
252     sm_alarmed = 0;
253     alarm (SM_OPEN);
254     result = smhear ();
255     alarm (0);
256
257     switch (result) {
258         case 220: 
259             break;
260
261         default: 
262             sm_end (NOTOK);
263             return RP_RPLY;
264     }
265
266     /*
267      * Give EHLO or HELO command
268      */
269
270     doingEHLO = 1;
271     result = smtalk (SM_HELO, "EHLO %s", client);
272     doingEHLO = 0;
273
274     if (result >= 500 && result <= 599)
275         result = smtalk (SM_HELO, "HELO %s", client);
276
277     if (result != 250) {
278         sm_end (NOTOK);
279         return RP_RPLY;
280     }
281
282 #ifdef TLS_SUPPORT
283     /*
284      * If the user requested TLS support, then try to do the STARTTLS command
285      * as part of the initial dialog.  Assuming this works, we then need to
286      * restart the EHLO dialog after TLS negotiation is complete.
287      */
288
289     if (tls) {
290         BIO *ssl_bio;
291
292         if (! EHLOset("STARTTLS")) {
293             sm_end(NOTOK);
294             return sm_ierror("SMTP server does not support TLS");
295         }
296
297         result = smtalk(SM_HELO, "STARTTLS");
298
299         if (result != 220) {
300             sm_end(NOTOK);
301             return RP_RPLY;
302         }
303
304         /*
305          * Okay, the other side should be waiting for us to start TLS
306          * negotiation.  Oblige them.
307          */
308
309         if (! sslctx) {
310             SSL_METHOD *method;
311
312             SSL_library_init();
313             SSL_load_error_strings();
314
315             method = TLSv1_client_method();     /* Not sure about this */
316
317             sslctx = SSL_CTX_new(method);
318
319             if (! sslctx) {
320                 sm_end(NOTOK);
321                 return sm_ierror("Unable to initialize OpenSSL context: %s",
322                                  ERR_error_string(ERR_get_error(), NULL));
323             }
324         }
325
326         ssl = SSL_new(sslctx);
327
328         if (! ssl) {
329             sm_end(NOTOK);
330             return sm_ierror("Unable to create SSL connection: %s",
331                              ERR_error_string(ERR_get_error(), NULL));
332         }
333
334         sbior = BIO_new_socket(fileno(sm_rfp), BIO_NOCLOSE);
335         sbiow = BIO_new_socket(fileno(sm_wfp), BIO_NOCLOSE);
336
337         if (sbior == NULL || sbiow == NULL) {
338             sm_end(NOTOK);
339             return sm_ierror("Unable to create BIO endpoints: %s",
340                              ERR_error_string(ERR_get_error(), NULL));
341         }
342
343         SSL_set_bio(ssl, sbior, sbiow);
344         SSL_set_connect_state(ssl);
345
346         /*
347          * Set up a BIO to handle buffering for us
348          */
349
350         io = BIO_new(BIO_f_buffer());
351
352         if (! io) {
353             sm_end(NOTOK);
354             return sm_ierror("Unable to create a buffer BIO: %s",
355                              ERR_error_string(ERR_get_error(), NULL));
356         }
357
358         ssl_bio = BIO_new(BIO_f_ssl());
359
360         if (! ssl_bio) {
361             sm_end(NOTOK);
362             return sm_ierror("Unable to create a SSL BIO: %s",
363                              ERR_error_string(ERR_get_error(), NULL));
364         }
365
366         BIO_set_ssl(ssl_bio, ssl, BIO_CLOSE);
367         BIO_push(io, ssl_bio);
368
369         /*
370          * Try doing the handshake now
371          */
372
373         if (BIO_do_handshake(io) < 1) {
374             sm_end(NOTOK);
375             return sm_ierror("Unable to negotiate SSL connection: %s",
376                              ERR_error_string(ERR_get_error(), NULL));
377         }
378
379         if (sm_debug) {
380             SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
381             printf("SSL negotiation successful: %s(%d) %s\n",
382                    SSL_CIPHER_get_name(cipher),
383                    SSL_CIPHER_get_bits(cipher, NULL),
384                    SSL_CIPHER_get_version(cipher));
385
386         }
387
388         tls_active = 1;
389
390         doingEHLO = 1;
391         result = smtalk (SM_HELO, "EHLO %s", client);
392         doingEHLO = 0;
393
394         if (result != 250) {
395             sm_end (NOTOK);
396             return RP_RPLY;
397         }
398     }
399 #endif /* TLS_SUPPORT */
400
401 #ifdef CYRUS_SASL
402     /*
403      * If the user asked for SASL, then check to see if the SMTP server
404      * supports it.  Otherwise, error out (because the SMTP server
405      * might have been spoofed; we don't want to just silently not
406      * do authentication
407      */
408
409     if (sasl) {
410         if (! (server_mechs = EHLOset("AUTH"))) {
411             sm_end(NOTOK);
412             return sm_ierror("SMTP server does not support SASL");
413         }
414
415         if (saslmech && stringdex(saslmech, server_mechs) == -1) {
416             sm_end(NOTOK);
417             return sm_ierror("Requested SASL mech \"%s\" is not in the "
418                              "list of supported mechanisms:\n%s",
419                              saslmech, server_mechs);
420         }
421
422         if (sm_auth_sasl(user, saslmech ? saslmech : server_mechs,
423                          server) != RP_OK) {
424             sm_end(NOTOK);
425             return NOTOK;
426         }
427     }
428 #endif /* CYRUS_SASL */
429
430 send_options: ;
431     if (watch && EHLOset ("XVRB"))
432         smtalk (SM_HELO, "VERB on");
433     if (onex && EHLOset ("XONE"))
434         smtalk (SM_HELO, "ONEX");
435     if (queued && EHLOset ("XQUE"))
436         smtalk (SM_HELO, "QUED");
437
438     return RP_OK;
439 }
440
441 int
442 sendmail_init (char *client, char *server, int watch, int verbose,
443                int debug, int onex, int queued,
444                int sasl, char *saslmech, char *user)
445 {
446 #ifdef CYRUS_SASL
447     char *server_mechs;
448 #endif /* CYRUS_SASL */
449     unsigned int i, result, vecp;
450     int pdi[2], pdo[2];
451     char *vec[15];
452
453     if (watch)
454         verbose = TRUE;
455
456     sm_verbose = verbose;
457     sm_debug = debug;
458     if (sm_rfp != NULL && sm_wfp != NULL)
459         return RP_OK;
460
461     if (client == NULL || *client == '\0') {
462         if (clientname)
463             client = clientname;
464         else
465             client = LocalName();       /* no clientname -> LocalName */
466     }
467
468     /*
469      * Last-ditch check just in case client still isn't set to anything
470      */
471
472     if (client == NULL || *client == '\0')
473         client = "localhost";
474
475 #if defined(CYRUS_SASL) || defined(TLS_SUPPORT)
476     sasl_inbuffer = malloc(SASL_MAXRECVBUF);
477     if (!sasl_inbuffer)
478         return sm_ierror("Unable to allocate %d bytes for read buffer",
479                          SASL_MAXRECVBUF);
480 #endif /* CYRUS_SASL || TLS_SUPPORT */
481
482     if (pipe (pdi) == NOTOK)
483         return sm_ierror ("no pipes");
484     if (pipe (pdo) == NOTOK) {
485         close (pdi[0]);
486         close (pdi[1]);
487         return sm_ierror ("no pipes");
488     }
489
490     for (i = 0; (sm_child = fork ()) == NOTOK && i < 5; i++)
491         sleep (5);
492
493     switch (sm_child) {
494         case NOTOK: 
495             close (pdo[0]);
496             close (pdo[1]);
497             close (pdi[0]);
498             close (pdi[1]);
499             return sm_ierror ("unable to fork");
500
501         case OK: 
502             if (pdo[0] != fileno (stdin))
503                 dup2 (pdo[0], fileno (stdin));
504             if (pdi[1] != fileno (stdout))
505                 dup2 (pdi[1], fileno (stdout));
506             if (pdi[1] != fileno (stderr))
507                 dup2 (pdi[1], fileno (stderr));
508             for (i = fileno (stderr) + 1; i < NBITS; i++)
509                 close (i);
510
511             vecp = 0;
512             vec[vecp++] = r1bindex (sendmail, '/');
513             vec[vecp++] = "-bs";
514             vec[vecp++] = watch ? "-odi" : queued ? "-odq" : "-odb";
515             vec[vecp++] = "-oem";
516             vec[vecp++] = "-om";
517 # ifndef RAND
518             if (verbose)
519                 vec[vecp++] = "-ov";
520 # endif /* not RAND */
521             vec[vecp++] = NULL;
522
523             setgid (getegid ());
524             setuid (geteuid ());
525             execvp (sendmail, vec);
526             fprintf (stderr, "unable to exec ");
527             perror (sendmail);
528             _exit (-1);         /* NOTREACHED */
529
530         default: 
531             SIGNAL (SIGALRM, alrmser);
532             SIGNAL (SIGPIPE, SIG_IGN);
533
534             close (pdi[1]);
535             close (pdo[0]);
536             if ((sm_rfp = fdopen (pdi[0], "r")) == NULL
537                     || (sm_wfp = fdopen (pdo[1], "w")) == NULL) {
538                 close (pdi[0]);
539                 close (pdo[1]);
540                 sm_rfp = sm_wfp = NULL;
541                 return sm_ierror ("unable to fdopen");
542             }
543             sm_alarmed = 0;
544             alarm (SM_OPEN);
545             result = smhear ();
546             alarm (0);
547             switch (result) {
548                 case 220: 
549                     break;
550
551                 default: 
552                     sm_end (NOTOK);
553                     return RP_RPLY;
554             }
555
556             doingEHLO = 1;
557             result = smtalk (SM_HELO, "EHLO %s", client);
558             doingEHLO = 0;
559
560             if (500 <= result && result <= 599)
561                 result = smtalk (SM_HELO, "HELO %s", client);
562
563             switch (result) {
564                 case 250:
565                     break;
566
567                 default:
568                     sm_end (NOTOK);
569                     return RP_RPLY;
570             }
571
572 #ifdef CYRUS_SASL
573     /*
574      * If the user asked for SASL, then check to see if the SMTP server
575      * supports it.  Otherwise, error out (because the SMTP server
576      * might have been spoofed; we don't want to just silently not
577      * do authentication
578      */
579
580     if (sasl) {
581         if (! (server_mechs = EHLOset("AUTH"))) {
582             sm_end(NOTOK);
583             return sm_ierror("SMTP server does not support SASL");
584         }
585
586         if (saslmech && stringdex(saslmech, server_mechs) == -1) {
587             sm_end(NOTOK);
588             return sm_ierror("Requested SASL mech \"%s\" is not in the "
589                              "list of supported mechanisms:\n%s",
590                              saslmech, server_mechs);
591         }
592
593         if (sm_auth_sasl(user, saslmech ? saslmech : server_mechs,
594                          server) != RP_OK) {
595             sm_end(NOTOK);
596             return NOTOK;
597         }
598     }
599 #endif /* CYRUS_SASL */
600
601             if (onex)
602                 smtalk (SM_HELO, "ONEX");
603             if (watch)
604                 smtalk (SM_HELO, "VERB on");
605
606             return RP_OK;
607     }
608 }
609
610 static int
611 rclient (char *server, char *service)
612 {
613     int sd;
614     char response[BUFSIZ];
615
616     if ((sd = client (server, service, response, sizeof(response),
617                       sm_debug)) != NOTOK)
618         return sd;
619
620     sm_ierror ("%s", response);
621     return NOTOK;
622 }
623
624 int
625 sm_winit (int mode, char *from)
626 {
627     char *smtpcom = NULL;
628
629     switch (mode) {
630         case S_MAIL:
631             smtpcom = "MAIL";
632             break;
633
634         case S_SEND:
635             smtpcom = "SEND";
636             break;
637
638         case S_SOML:
639             smtpcom = "SOML";
640             break;
641
642         case S_SAML:
643             smtpcom = "SAML";
644             break;
645
646         default:
647             /* Hopefully, we do not get here. */
648             break;
649     }
650
651     switch (smtalk (SM_MAIL, "%s FROM:<%s>", smtpcom, from)) {
652         case 250: 
653             sm_addrs = 0;
654             return RP_OK;
655
656         case 500: 
657         case 501: 
658         case 552: 
659             return RP_PARM;
660
661         default: 
662             return RP_RPLY;
663     }
664 }
665
666
667 int
668 sm_wadr (char *mbox, char *host, char *path)
669 {
670     switch (smtalk (SM_RCPT, host && *host ? "RCPT TO:<%s%s@%s>"
671                                            : "RCPT TO:<%s%s>",
672                              path ? path : "", mbox, host)) {
673         case 250: 
674         case 251: 
675             sm_addrs++;
676             return RP_OK;
677
678         case 451: 
679 #ifdef SENDMAILBUG
680             sm_addrs++;
681             return RP_OK;
682 #endif /* SENDMAILBUG */
683         case 421: 
684         case 450: 
685         case 452: 
686             return RP_NO;
687
688         case 500: 
689         case 501: 
690             return RP_PARM;
691
692         case 550: 
693         case 551: 
694         case 552: 
695         case 553: 
696             return RP_USER;
697
698         default: 
699             return RP_RPLY;
700     }
701 }
702
703
704 int
705 sm_waend (void)
706 {
707     switch (smtalk (SM_DATA, "DATA")) {
708         case 354: 
709             sm_nl = TRUE;
710             return RP_OK;
711
712         case 451: 
713 #ifdef SENDMAILBUG
714             sm_nl = TRUE;
715             return RP_OK;
716 #endif /* SENDMAILBUG */
717         case 421: 
718             return RP_NO;
719
720         case 500: 
721         case 501: 
722         case 503: 
723         case 554: 
724             return RP_NDEL;
725
726         default: 
727             return RP_RPLY;
728     }
729 }
730
731
732 int
733 sm_wtxt (char *buffer, int len)
734 {
735     int result;
736
737     sm_alarmed = 0;
738     alarm (SM_TEXT);
739     result = sm_wstream (buffer, len);
740     alarm (0);
741
742     return (result == NOTOK ? RP_BHST : RP_OK);
743 }
744
745
746 int
747 sm_wtend (void)
748 {
749     if (sm_wstream ((char *) NULL, 0) == NOTOK)
750         return RP_BHST;
751
752     switch (smtalk (SM_DOT + 3 * sm_addrs, ".")) {
753         case 250: 
754         case 251: 
755             return RP_OK;
756
757         case 451: 
758 #ifdef SENDMAILBUG
759             return RP_OK;
760 #endif /* SENDMAILBUG */
761         case 452: 
762         default: 
763             return RP_NO;
764
765         case 552: 
766         case 554: 
767             return RP_NDEL;
768     }
769 }
770
771
772 int
773 sm_end (int type)
774 {
775     int status;
776     struct smtp sm_note;
777
778     if (sm_mts == MTS_SENDMAIL) {
779         switch (sm_child) {
780             case NOTOK: 
781             case OK: 
782                 return RP_OK;
783
784             default: 
785                 break;
786         }
787     }
788
789     if (sm_rfp == NULL && sm_wfp == NULL)
790         return RP_OK;
791
792     switch (type) {
793         case OK: 
794             smtalk (SM_QUIT, "QUIT");
795             break;
796
797         case NOTOK: 
798             sm_note.code = sm_reply.code;
799             sm_note.length = sm_reply.length;
800             memcpy (sm_note.text, sm_reply.text, sm_reply.length + 1);/* fall */
801         case DONE: 
802             if (smtalk (SM_RSET, "RSET") == 250 && type == DONE)
803                 return RP_OK;
804             if (sm_mts == MTS_SMTP)
805                 smtalk (SM_QUIT, "QUIT");
806             else {
807                 kill (sm_child, SIGKILL);
808                 discard (sm_rfp);
809                 discard (sm_wfp);
810             }
811             if (type == NOTOK) {
812                 sm_reply.code = sm_note.code;
813                 sm_reply.length = sm_note.length;
814                 memcpy (sm_reply.text, sm_note.text, sm_note.length + 1);
815             }
816             break;
817     }
818
819 #ifdef TLS_SUPPORT
820     if (tls_active) {
821         BIO_ssl_shutdown(io);
822         BIO_free_all(io);
823     }
824 #endif /* TLS_SUPPORT */
825
826     if (sm_rfp != NULL) {
827         alarm (SM_CLOS);
828         fclose (sm_rfp);
829         alarm (0);
830     }
831     if (sm_wfp != NULL) {
832         alarm (SM_CLOS);
833         fclose (sm_wfp);
834         alarm (0);
835     }
836
837     if (sm_mts == MTS_SMTP) {
838         status = 0;
839 #ifdef CYRUS_SASL
840         if (conn) {
841             sasl_dispose(&conn);
842             if (sasl_outbuffer) {
843                 free(sasl_outbuffer);
844             }
845         }
846         if (sasl_inbuffer)
847             free(sasl_inbuffer);
848 #endif /* CYRUS_SASL */
849     } else {
850         status = pidwait (sm_child, OK);
851         sm_child = NOTOK;
852     }
853
854     sm_rfp = sm_wfp = NULL;
855     return (status ? RP_BHST : RP_OK);
856 }
857
858 #ifdef CYRUS_SASL
859 /*
860  * This function implements SASL authentication for SMTP.  If this function
861  * completes successfully, then authentication is successful and we've
862  * (optionally) negotiated a security layer.
863  */
864 static int
865 sm_auth_sasl(char *user, char *mechlist, char *inhost)
866 {
867     int result, status;
868     unsigned int buflen, outlen;
869     char *buf, outbuf[BUFSIZ], host[NI_MAXHOST];
870     const char *chosen_mech;
871     sasl_security_properties_t secprops;
872     sasl_ssf_t *ssf;
873     int *outbufmax;
874
875     /*
876      * Initialize the callback contexts
877      */
878
879     if (user == NULL)
880         user = getusername();
881
882     callbacks[SM_SASL_N_CB_USER].context = user;
883     callbacks[SM_SASL_N_CB_AUTHNAME].context = user;
884
885     /*
886      * This is a _bit_ of a hack ... but if the hostname wasn't supplied
887      * to us on the command line, then call getpeername and do a
888      * reverse-address lookup on the IP address to get the name.
889      */
890
891     memset(host, 0, sizeof(host));
892
893     if (!inhost) {
894         struct sockaddr_storage sin;
895         socklen_t len = sizeof(sin);
896         int result;
897
898         if (getpeername(fileno(sm_wfp), (struct sockaddr *) &sin, &len) < 0) {
899             sm_ierror("getpeername on SMTP socket failed: %s",
900                       strerror(errno));
901             return NOTOK;
902         }
903
904         result = getnameinfo((struct sockaddr *) &sin, len, host, sizeof(host),
905                              NULL, 0, NI_NAMEREQD);
906         if (result != 0) {
907             sm_ierror("Unable to look up name of connected host: %s",
908                       gai_strerror(result));
909             return NOTOK;
910         }
911     } else {
912         strncpy(host, inhost, sizeof(host) - 1);
913     }
914
915     sasl_pw_context[0] = host;
916     sasl_pw_context[1] = user;
917
918     callbacks[SM_SASL_N_CB_PASS].context = sasl_pw_context;
919
920     result = sasl_client_init(callbacks);
921
922     if (result != SASL_OK) {
923         sm_ierror("SASL library initialization failed: %s",
924                   sasl_errstring(result, NULL, NULL));
925         return NOTOK;
926     }
927
928     result = sasl_client_new("smtp", host, NULL, NULL, NULL, 0, &conn);
929
930     if (result != SASL_OK) {
931         sm_ierror("SASL client initialization failed: %s",
932                   sasl_errstring(result, NULL, NULL));
933         return NOTOK;
934     }
935
936     /*
937      * Initialize the security properties.  But if TLS is active, then
938      * don't negotiate encryption here.
939      */
940
941     memset(&secprops, 0, sizeof(secprops));
942     secprops.maxbufsize = SASL_MAXRECVBUF;
943     secprops.max_ssf = tls_active ? 0 : UINT_MAX;
944
945     result = sasl_setprop(conn, SASL_SEC_PROPS, &secprops);
946
947     if (result != SASL_OK) {
948         sm_ierror("SASL security property initialization failed: %s",
949                   sasl_errstring(result, NULL, NULL));
950         return NOTOK;
951     }
952
953     /*
954      * Start the actual protocol.  Feed the mech list into the library
955      * and get out a possible initial challenge
956      */
957
958     result = sasl_client_start(conn, mechlist, NULL, (const char **) &buf,
959                                &buflen, (const char **) &chosen_mech);
960
961     if (result != SASL_OK && result != SASL_CONTINUE) {
962         sm_ierror("SASL client start failed: %s", sasl_errdetail(conn));
963         return NOTOK;
964     }
965
966     /*
967      * If we got an initial challenge, send it as part of the AUTH
968      * command; otherwise, just send a plain AUTH command.
969      */
970
971     if (buflen) {
972         status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL);
973         if (status != SASL_OK) {
974             sm_ierror("SASL base64 encode failed: %s",
975                       sasl_errstring(status, NULL, NULL));
976             return NOTOK;
977         }
978
979         status = smtalk(SM_AUTH, "AUTH %s %s", chosen_mech, outbuf);
980     } else
981         status = smtalk(SM_AUTH, "AUTH %s", chosen_mech);
982
983     /*
984      * Now we loop until we either fail, get a SASL_OK, or a 235
985      * response code.  Receive the challenges and process them until
986      * we're all done.
987      */
988
989     while (result == SASL_CONTINUE) {
990
991         /*
992          * If we get a 235 response, that means authentication has
993          * succeeded and we need to break out of the loop (yes, even if
994          * we still get SASL_CONTINUE from sasl_client_step()).
995          *
996          * Otherwise, if we get a message that doesn't seem to be a
997          * valid response, then abort
998          */
999
1000         if (status == 235)
1001             break;
1002         else if (status < 300 || status > 399)
1003             return RP_BHST;
1004         
1005         /*
1006          * Special case; a zero-length response from the SMTP server
1007          * is returned as a single =.  If we get that, then set buflen
1008          * to be zero.  Otherwise, just decode the response.
1009          */
1010         
1011         if (strcmp("=", sm_reply.text) == 0) {
1012             outlen = 0;
1013         } else {
1014             result = sasl_decode64(sm_reply.text, sm_reply.length,
1015                                    outbuf, sizeof(outbuf), &outlen);
1016         
1017             if (result != SASL_OK) {
1018                 smtalk(SM_AUTH, "*");
1019                 sm_ierror("SASL base64 decode failed: %s",
1020                           sasl_errstring(result, NULL, NULL));
1021                 return NOTOK;
1022             }
1023         }
1024
1025         result = sasl_client_step(conn, outbuf, outlen, NULL,
1026                                   (const char **) &buf, &buflen);
1027
1028         if (result != SASL_OK && result != SASL_CONTINUE) {
1029             smtalk(SM_AUTH, "*");
1030             sm_ierror("SASL client negotiation failed: %s",
1031                       sasl_errstring(result, NULL, NULL));
1032             return NOTOK;
1033         }
1034
1035         status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL);
1036
1037         if (status != SASL_OK) {
1038             smtalk(SM_AUTH, "*");
1039             sm_ierror("SASL base64 encode failed: %s",
1040                       sasl_errstring(status, NULL, NULL));
1041             return NOTOK;
1042         }
1043         
1044         status = smtalk(SM_AUTH, outbuf);
1045     }
1046
1047     /*
1048      * Make sure that we got the correct response
1049      */
1050
1051     if (status < 200 || status > 299)
1052         return RP_BHST;
1053
1054     /*
1055      * We _should_ have completed the authentication successfully.
1056      * Get a few properties from the authentication exchange.
1057      */
1058
1059     result = sasl_getprop(conn, SASL_MAXOUTBUF, (const void **) &outbufmax);
1060
1061     if (result != SASL_OK) {
1062         sm_ierror("Cannot retrieve SASL negotiated output buffer size: %s",
1063                   sasl_errstring(result, NULL, NULL));
1064         return NOTOK;
1065     }
1066
1067     maxoutbuf = *outbufmax;
1068
1069     result = sasl_getprop(conn, SASL_SSF, (const void **) &ssf);
1070
1071     sasl_ssf = *ssf;
1072
1073     if (result != SASL_OK) {
1074         sm_ierror("Cannot retrieve SASL negotiated security strength "
1075                   "factor: %s", sasl_errstring(result, NULL, NULL));
1076         return NOTOK;
1077     }
1078
1079     if (sasl_ssf > 0) {
1080         sasl_outbuffer = malloc(maxoutbuf);
1081
1082         if (sasl_outbuffer == NULL) {
1083                 sm_ierror("Unable to allocate %d bytes for SASL output "
1084                           "buffer", maxoutbuf);
1085                 return NOTOK;
1086         }
1087         sasl_outbuflen = 0;
1088         sasl_inbuflen = 0;
1089         sasl_inptr = sasl_inbuffer;
1090     } else {
1091         sasl_outbuffer = NULL;
1092         /* Don't NULL out sasl_inbuffer because it could be used in
1093            sm_fgetc (). */
1094     }
1095
1096     sasl_complete = 1;
1097
1098     return RP_OK;
1099 }
1100
1101 /*
1102  * Our callback functions to feed data to the SASL library
1103  */
1104
1105 static int
1106 sm_get_user(void *context, int id, const char **result, unsigned *len)
1107 {
1108     char *user = (char *) context;
1109
1110     if (! result || ((id != SASL_CB_USER) && (id != SASL_CB_AUTHNAME)))
1111         return SASL_BADPARAM;
1112
1113     *result = user;
1114     if (len)
1115         *len = strlen(user);
1116
1117     return SASL_OK;
1118 }
1119
1120 static int
1121 sm_get_pass(sasl_conn_t *conn, void *context, int id,
1122             sasl_secret_t **psecret)
1123 {
1124     char **pw_context = (char **) context;
1125     char *pass = NULL;
1126     int len;
1127
1128     if (! psecret || id != SASL_CB_PASS)
1129         return SASL_BADPARAM;
1130
1131     ruserpass(pw_context[0], &(pw_context[1]), &pass);
1132
1133     len = strlen(pass);
1134
1135     *psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len);
1136
1137     if (! *psecret) {
1138         free(pass);
1139         return SASL_NOMEM;
1140     }
1141
1142     (*psecret)->len = len;
1143     strcpy((char *) (*psecret)->data, pass);
1144 /*    free(pass); */
1145
1146     return SASL_OK;
1147 }
1148 #endif /* CYRUS_SASL */
1149
1150 static int
1151 sm_ierror (char *fmt, ...)
1152 {
1153     va_list ap;
1154
1155     va_start(ap, fmt);
1156     vsnprintf (sm_reply.text, sizeof(sm_reply.text), fmt, ap);
1157     va_end(ap);
1158
1159     sm_reply.length = strlen (sm_reply.text);
1160     sm_reply.code = NOTOK;
1161
1162     return RP_BHST;
1163 }
1164
1165 static int
1166 smtalk (int time, char *fmt, ...)
1167 {
1168     va_list ap;
1169     int result;
1170     char buffer[BUFSIZ];
1171
1172     va_start(ap, fmt);
1173     vsnprintf (buffer, sizeof(buffer), fmt, ap);
1174     va_end(ap);
1175
1176     if (sm_debug) {
1177         if (sasl_ssf)
1178                 printf("(sasl-encrypted) ");
1179         if (tls_active)
1180                 printf("(tls-encrypted) ");
1181         printf ("=> %s\n", buffer);
1182         fflush (stdout);
1183     }
1184
1185     sm_alarmed = 0;
1186     alarm ((unsigned) time);
1187     if ((result = sm_wrecord (buffer, strlen (buffer))) != NOTOK)
1188         result = smhear ();
1189     alarm (0);
1190
1191     return result;
1192 }
1193
1194
1195 /*
1196  * write the buffer to the open SMTP channel
1197  */
1198
1199 static int
1200 sm_wrecord (char *buffer, int len)
1201 {
1202     if (sm_wfp == NULL)
1203         return sm_werror ();
1204
1205     sm_fwrite (buffer, len);
1206     sm_fputs ("\r\n");
1207     sm_fflush ();
1208
1209     return (ferror (sm_wfp) ? sm_werror () : OK);
1210 }
1211
1212
1213 static int
1214 sm_wstream (char *buffer, int len)
1215 {
1216     char  *bp;
1217     static char lc = '\0';
1218
1219     if (sm_wfp == NULL)
1220         return sm_werror ();
1221
1222     if (buffer == NULL && len == 0) {
1223         if (lc != '\n')
1224             sm_fputs ("\r\n");
1225         lc = '\0';
1226         return (ferror (sm_wfp) ? sm_werror () : OK);
1227     }
1228
1229     for (bp = buffer; len > 0; bp++, len--) {
1230         switch (*bp) {
1231             case '\n': 
1232                 sm_nl = TRUE;
1233                 sm_fputc ('\r');
1234                 break;
1235
1236             case '.': 
1237                 if (sm_nl)
1238                     sm_fputc ('.');/* FALL THROUGH */
1239             default: 
1240                 sm_nl = FALSE;
1241         }
1242         sm_fputc (*bp);
1243         if (ferror (sm_wfp))
1244             return sm_werror ();
1245     }
1246
1247     if (bp > buffer)
1248         lc = *--bp;
1249     return (ferror (sm_wfp) ? sm_werror () : OK);
1250 }
1251
1252 /*
1253  * Write out to the network, but do buffering for SASL (if enabled)
1254  */
1255
1256 static int
1257 sm_fwrite(char *buffer, int len)
1258 {
1259 #ifdef CYRUS_SASL
1260     const char *output;
1261     unsigned int outputlen;
1262
1263     if (sasl_complete == 0 || sasl_ssf == 0) {
1264 #endif /* CYRUS_SASL */
1265 #ifdef TLS_SUPPORT
1266         if (tls_active) {
1267             int ret;
1268
1269             ret = BIO_write(io, buffer, len);
1270
1271             if (ret <= 0) {
1272                 sm_ierror("TLS error during write: %s",
1273                           ERR_error_string(ERR_get_error(), NULL));
1274                 return NOTOK;
1275             }
1276         } else
1277 #endif /* TLS_SUPPORT */
1278         fwrite(buffer, sizeof(*buffer), len, sm_wfp);
1279 #ifdef CYRUS_SASL
1280     } else {
1281         while (len >= maxoutbuf - sasl_outbuflen) {
1282             memcpy(sasl_outbuffer + sasl_outbuflen, buffer,
1283                    maxoutbuf - sasl_outbuflen);
1284             len -= maxoutbuf - sasl_outbuflen;
1285             sasl_outbuflen = 0;
1286
1287             if (sasl_encode(conn, sasl_outbuffer, maxoutbuf,
1288                             &output, &outputlen) != SASL_OK) {
1289                 sm_ierror("Unable to SASL encode connection data: %s",
1290                           sasl_errdetail(conn));
1291                 return NOTOK;
1292             }
1293
1294             fwrite(output, sizeof(*output), outputlen, sm_wfp);
1295         }
1296
1297         if (len > 0) {
1298             memcpy(sasl_outbuffer + sasl_outbuflen, buffer, len);
1299             sasl_outbuflen += len;
1300         }
1301     }
1302 #endif /* CYRUS_SASL */
1303     return ferror(sm_wfp) ? NOTOK : RP_OK;
1304 }
1305
1306 /*
1307  * Convenience functions to replace occurences of fputs() and fputc()
1308  */
1309
1310 static int
1311 sm_fputs(char *buffer)
1312 {
1313     return sm_fwrite(buffer, strlen(buffer));
1314 }
1315
1316 static int
1317 sm_fputc(int c)
1318 {
1319     char h = c;
1320
1321     return sm_fwrite(&h, 1);
1322 }
1323
1324 /*
1325  * Flush out any pending data on the connection
1326  */
1327
1328 static void
1329 sm_fflush(void)
1330 {
1331 #ifdef CYRUS_SASL
1332     const char *output;
1333     unsigned int outputlen;
1334     int result;
1335
1336     if (sasl_complete == 1 && sasl_ssf > 0 && sasl_outbuflen > 0) {
1337         result = sasl_encode(conn, sasl_outbuffer, sasl_outbuflen,
1338                              &output, &outputlen);
1339         if (result != SASL_OK) {
1340             sm_ierror("Unable to SASL encode connection data: %s",
1341                       sasl_errdetail(conn));
1342             return;
1343         }
1344
1345         fwrite(output, sizeof(*output), outputlen, sm_wfp);
1346         sasl_outbuflen = 0;
1347     }
1348 #endif /* CYRUS_SASL */
1349
1350 #ifdef TLS_SUPPORT
1351     if (tls_active) {
1352         (void) BIO_flush(io);
1353     }
1354 #endif /* TLS_SUPPORT */
1355
1356     fflush(sm_wfp);
1357 }
1358
1359 static int
1360 sm_werror (void)
1361 {
1362     sm_reply.length =
1363         strlen (strcpy (sm_reply.text, sm_wfp == NULL ? "no socket opened"
1364             : sm_alarmed ? "write to socket timed out"
1365             : "error writing to socket"));
1366
1367     return (sm_reply.code = NOTOK);
1368 }
1369
1370
1371 static int
1372 smhear (void)
1373 {
1374     int i, code, cont, bc = 0, rc, more;
1375     unsigned char *bp;
1376     char *rp;
1377     char **ehlo = NULL, buffer[BUFSIZ];
1378
1379     if (doingEHLO) {
1380         static int at_least_once = 0;
1381
1382         if (at_least_once) {
1383             char *ep;
1384
1385             for (ehlo = EHLOkeys; *ehlo; ehlo++) {
1386                 ep = *ehlo;
1387                 free (ep);
1388             }
1389         } else {
1390             at_least_once = 1;
1391         }
1392
1393         ehlo = EHLOkeys;
1394         *ehlo = NULL;
1395     }
1396
1397 again: ;
1398
1399     sm_reply.length = 0;
1400     sm_reply.text[0] = 0;
1401     rp = sm_reply.text;
1402     rc = sizeof(sm_reply.text) - 1;
1403
1404     for (more = FALSE; sm_rrecord ((char *) (bp = (unsigned char *) buffer),
1405                                    &bc) != NOTOK ; ) {
1406         if (sm_debug) {
1407             if (sasl_ssf > 0)
1408                 printf("(sasl-decrypted) ");
1409             if (tls_active)
1410                 printf("(tls-decrypted) ");
1411             printf ("<= %s\n", buffer);
1412             fflush (stdout);
1413         }
1414
1415         if (doingEHLO
1416                 && strncmp (buffer, "250", sizeof("250") - 1) == 0
1417                 && (buffer[3] == '-' || doingEHLO == 2)
1418                 && buffer[4]) {
1419             if (doingEHLO == 2) {
1420                 if ((*ehlo = malloc ((size_t) (strlen (buffer + 4) + 1)))) {
1421                     strcpy (*ehlo++, buffer + 4);
1422                     *ehlo = NULL;
1423                     if (ehlo >= EHLOkeys + MAXEHLO)
1424                         doingEHLO = 0;
1425                 }
1426                 else
1427                     doingEHLO = 0;
1428             }
1429             else
1430                 doingEHLO = 2;
1431         }
1432
1433         for (; bc > 0 && (!isascii (*bp) || !isdigit (*bp)); bp++, bc--)
1434             continue;
1435
1436         cont = FALSE;
1437         code = atoi ((char *) bp);
1438         bp += 3, bc -= 3;
1439         for (; bc > 0 && isspace (*bp); bp++, bc--)
1440             continue;
1441         if (bc > 0 && *bp == '-') {
1442             cont = TRUE;
1443             bp++, bc--;
1444             for (; bc > 0 && isspace (*bp); bp++, bc--)
1445                 continue;
1446         }
1447
1448         if (more) {
1449             if (code != sm_reply.code || cont)
1450                 continue;
1451             more = FALSE;
1452         } else {
1453             sm_reply.code = code;
1454             more = cont;
1455             if (bc <= 0) {
1456                 /* can never fail to 0-terminate because of size of buffer vs fixed string */
1457                 strncpy (buffer, sm_noreply, sizeof(buffer));
1458                 bp = (unsigned char *) buffer;
1459                 bc = strlen (sm_noreply);
1460             }
1461         }
1462
1463         if ((i = min (bc, rc)) > 0) {
1464             memcpy (rp, bp, i);
1465             rp += i;
1466             rc -= i;
1467             i = strlen(sm_moreply);
1468             if (more && rc > i + 1) {
1469                 memcpy (rp, sm_moreply, i); /* safe because of check in if() */
1470                 rp += i;
1471                 rc -= i;
1472             }
1473         }
1474         if (more)
1475             continue;
1476         if (sm_reply.code < 100) {
1477             if (sm_verbose) {
1478                 printf ("%s\n", sm_reply.text);
1479                 fflush (stdout);
1480             }
1481             goto again;
1482         }
1483
1484         sm_reply.length = rp - sm_reply.text;
1485         sm_reply.text[sm_reply.length] = 0;
1486         return sm_reply.code;
1487     }
1488     return NOTOK;
1489 }
1490
1491
1492 static int
1493 sm_rrecord (char *buffer, int *len)
1494 {
1495     int retval;
1496
1497     if (sm_rfp == NULL)
1498         return sm_rerror(0);
1499
1500     buffer[*len = 0] = 0;
1501
1502     if ((retval = sm_fgets (buffer, BUFSIZ, sm_rfp)) != RP_OK)
1503         return retval;
1504     *len = strlen (buffer);
1505     /* *len should be >0 except on EOF, but check for safety's sake */
1506     if (*len == 0)
1507         return sm_rerror (RP_EOF);
1508     if (buffer[*len - 1] != '\n')
1509         while ((retval = sm_fgetc (sm_rfp)) != '\n' && retval != EOF &&
1510                retval != -2)
1511             continue;
1512     else
1513         if ((*len > 1) && (buffer[*len - 2] == '\r'))
1514             *len -= 1;
1515     *len -= 1;
1516     buffer[*len] = 0;
1517
1518     return OK;
1519 }
1520
1521 /*
1522  * Our version of fgets, which calls our private fgetc function
1523  */
1524
1525 static int
1526 sm_fgets(char *buffer, int size, FILE *f)
1527 {
1528     int c;
1529
1530      do {
1531         c = sm_fgetc(f);
1532
1533         if (c == EOF)
1534             return RP_EOF;
1535
1536         if (c == -2)
1537             return NOTOK;
1538
1539         *buffer++ = c;
1540      } while (size > 1 && c != '\n');
1541
1542      *buffer = '\0';
1543
1544      return RP_OK;
1545 }
1546
1547
1548 #if defined(CYRUS_SASL) || defined(TLS_SUPPORT)
1549 /*
1550  * Read from the network, but do SASL or TLS encryption
1551  */
1552
1553 static int
1554 sm_fgetc(FILE *f)
1555 {
1556     char tmpbuf[BUFSIZ], *retbuf;
1557     unsigned int retbufsize = 0;
1558     int cc, result;
1559
1560     /*
1561      * If we have leftover data, return it
1562      */
1563
1564     if (sasl_inbuflen) {
1565         sasl_inbuflen--;
1566         return (int) *sasl_inptr++;
1567     }
1568
1569     /*
1570      * If not, read from the network until we have some data to return
1571      */
1572
1573     while (retbufsize == 0) {
1574
1575 #ifdef TLS_SUPPORT
1576         if (tls_active) {
1577             cc = SSL_read(ssl, tmpbuf, sizeof(tmpbuf));
1578
1579             if (cc == 0) {
1580                 result = SSL_get_error(ssl, cc);
1581
1582                 if (result != SSL_ERROR_ZERO_RETURN) {
1583                     sm_ierror("TLS peer aborted connection");
1584                 }
1585
1586                 return EOF;
1587             }
1588
1589             if (cc < 0) {
1590                 sm_ierror("SSL_read failed: %s",
1591                           ERR_error_string(ERR_get_error(), NULL));
1592                 return -2;
1593             }
1594         } else
1595 #endif /* TLS_SUPPORT */
1596
1597         cc = read(fileno(f), tmpbuf, sizeof(tmpbuf));
1598
1599         if (cc == 0)
1600             return EOF;
1601
1602         if (cc < 0) {
1603             sm_ierror("Unable to read from network: %s", strerror(errno));
1604             return -2;
1605         }
1606
1607         /*
1608          * Don't call sasl_decode unless sasl is complete and we have
1609          * encryption working
1610          */
1611
1612 #ifdef CYRUS_SASL
1613         if (sasl_complete == 0 || sasl_ssf == 0) {
1614             retbuf = tmpbuf;
1615             retbufsize = cc;
1616         } else {
1617             result = sasl_decode(conn, tmpbuf, cc, (const char **) &retbuf,
1618                                  &retbufsize);
1619
1620             if (result != SASL_OK) {
1621                 sm_ierror("Unable to decode SASL network data: %s",
1622                           sasl_errdetail(conn));
1623                 return -2;
1624             }
1625         }
1626 #else /* ! CYRUS_SASL */
1627         retbuf = tmpbuf;
1628         retbufsize = cc;
1629 #endif /* CYRUS_SASL */
1630     }
1631
1632     if (retbufsize > SASL_MAXRECVBUF) {
1633         sm_ierror("Received data (%d bytes) is larger than the buffer "
1634                   "size (%d bytes)", retbufsize, SASL_MAXRECVBUF);
1635         return -2;
1636     }
1637
1638     memcpy(sasl_inbuffer, retbuf, retbufsize);
1639     sasl_inptr = sasl_inbuffer + 1;
1640     sasl_inbuflen = retbufsize - 1;
1641
1642     return (int) sasl_inbuffer[0];
1643 }
1644 #endif /* CYRUS_SASL || TLS_SUPPORT */
1645
1646 static int
1647 sm_rerror (int rc)
1648 {
1649     if (sm_mts == MTS_SMTP)
1650         sm_reply.length =
1651             strlen (strcpy (sm_reply.text, sm_rfp == NULL ? "no socket opened"
1652                 : sm_alarmed ? "read from socket timed out"
1653                 : rc == RP_EOF ? "premature end-of-file on socket"
1654                 : "error reading from socket"));
1655     else
1656         sm_reply.length =
1657             strlen (strcpy (sm_reply.text, sm_rfp == NULL ? "no pipe opened"
1658                 : sm_alarmed ? "read from pipe timed out"
1659                 : rc == RP_EOF ? "premature end-of-file on pipe"
1660                 : "error reading from pipe"));
1661
1662     return (sm_reply.code = NOTOK);
1663 }
1664
1665
1666 static void
1667 alrmser (int i)
1668 {
1669 #ifndef RELIABLE_SIGNALS
1670     SIGNAL (SIGALRM, alrmser);
1671 #endif
1672
1673     sm_alarmed++;
1674     if (sm_debug) {
1675         printf ("timed out...\n");
1676         fflush (stdout);
1677     }
1678 }
1679
1680
1681 char *
1682 rp_string (int code)
1683 {
1684     char *text;
1685     static char buffer[BUFSIZ];
1686
1687     switch (sm_reply.code != NOTOK ? code : NOTOK) {
1688         case RP_AOK:
1689             text = "AOK";
1690             break;
1691
1692         case RP_MOK:
1693             text = "MOK";
1694             break;
1695
1696         case RP_OK: 
1697             text = "OK";
1698             break;
1699
1700         case RP_RPLY: 
1701             text = "RPLY";
1702             break;
1703
1704         case RP_BHST: 
1705         default: 
1706             text = "BHST";
1707             snprintf (buffer, sizeof(buffer), "[%s] %s", text, sm_reply.text);
1708             return buffer;
1709
1710         case RP_PARM: 
1711             text = "PARM";
1712             break;
1713
1714         case RP_NO: 
1715             text = "NO";
1716             break;
1717
1718         case RP_USER: 
1719             text = "USER";
1720             break;
1721
1722         case RP_NDEL: 
1723             text = "NDEL";
1724             break;
1725     }
1726
1727     snprintf (buffer, sizeof(buffer), "[%s] %3d %s",
1728                 text, sm_reply.code, sm_reply.text);
1729     return buffer;
1730 }
1731
1732 static char *
1733 EHLOset (char *s)
1734 {
1735     size_t len;
1736     char *ep, **ehlo;
1737
1738     len = strlen (s);
1739
1740     for (ehlo = EHLOkeys; *ehlo; ehlo++) {
1741         ep = *ehlo;
1742         if (strncmp (ep, s, len) == 0) {
1743             for (ep += len; *ep == ' '; ep++)
1744                 continue;
1745             return ep;
1746         }
1747     }
1748
1749     return 0;
1750 }