1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
3 *@ TODO - use initial responses to save a round-trip (RFC 4954)
4 *@ TODO - more (verbose) understanding+rection upon STATUS CODES
6 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
7 * Copyright (c) 2012 - 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
11 * Gunnar Ritter. All rights reserved.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgement:
23 * This product includes software developed by Gunnar Ritter
24 * and his contributors.
25 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 #ifndef HAVE_AMALGAMATION
48 #include <sys/socket.h>
52 #include <netinet/in.h>
54 #ifdef HAVE_ARPA_INET_H
55 # include <arpa/inet.h>
64 char *dat
; /* Actual data */
66 char *buf
; /* Memory buffer */
70 static sigjmp_buf _smtp_jmp
;
72 static void _smtp_onterm(int signo
);
74 /* Get the SMTP server's answer, expecting val */
75 static int _smtp_read(struct sock
*sp
, struct smtp_line
*slp
, int val
,
76 bool_t ign_eof
, bool_t want_dat
);
78 /* Talk to a SMTP server */
79 static int _smtp_talk(struct name
*to
, struct header
*hp
, FILE *fi
,
80 struct sock
*sp
, struct url
*urlp
, struct ccred
*ccred
);
83 static bool_t
_smtp_gssapi(struct sock
*sp
, struct url
*urlp
,
84 struct ccred
*ccred
, struct smtp_line
*slp
);
88 _smtp_onterm(int signo
)
90 NYD_X
; /* Signal handler */
92 siglongjmp(_smtp_jmp
, 1);
96 _smtp_read(struct sock
*sp
, struct smtp_line
*slp
, int val
,
97 bool_t ign_eof
, bool_t want_dat
)
104 if ((len
= sgetline(&slp
->buf
, &slp
->bufsize
, NULL
, sp
)) < 6) {
105 if (len
>= 0 && !ign_eof
)
106 fprintf(stderr
, tr(241, "Unexpected EOF on SMTP connection\n"));
110 if (options
& OPT_VERBOSE
)
111 fputs(slp
->buf
, stderr
);
112 switch (slp
->buf
[0]) {
113 case '1': rv
= 1; break;
114 case '2': rv
= 2; break;
115 case '3': rv
= 3; break;
116 case '4': rv
= 4; break;
117 default: rv
= 5; break;
120 fprintf(stderr
, tr(191, "smtp-server: %s"), slp
->buf
);
121 } while (slp
->buf
[3] == '-');
124 for (cp
= slp
->buf
; digitchar(*cp
); --len
, ++cp
)
126 for (; blankchar(*cp
); --len
, ++cp
)
131 cp
[slp
->datlen
= (size_t)len
] = '\0';
138 /* Indirect SMTP I/O */
139 #define _ANSWER(X, IGNEOF, WANTDAT) \
140 do if (!(options & OPT_DEBUG)) {\
142 if ((y = _smtp_read(sp, slp, X, IGNEOF, WANTDAT)) != (X) &&\
143 (!(IGNEOF) || y != -1))\
148 if (options & OPT_VERBOSE)\
149 fprintf(stderr, ">>> %s", X);\
150 if (!(options & OPT_DEBUG))\
155 _smtp_talk(struct name
*to
, struct header
*hp
, FILE *fi
, struct sock
*sp
,
156 struct url
*urlp
, struct ccred
*ccred
)
158 char o
[LINESIZE
], *cp
;
159 struct smtp_line _sl
, *slp
= &_sl
;
163 int inhdr
= 1, inbcc
= 0, rv
= 1;
171 _ANSWER(2, FAL0
, FAL0
);
174 if (!sp
->s_use_ssl
&& ok_blook(smtp_use_starttls
)) {
175 snprintf(o
, sizeof o
, LINE("EHLO %s"), nodename(1));
177 _ANSWER(2, FAL0
, FAL0
);
179 _OUT(LINE("STARTTLS"));
180 _ANSWER(2, FAL0
, FAL0
);
182 if (!(options
& OPT_DEBUG
) &&
183 ssl_open(urlp
->url_host
.s
, sp
, urlp
->url_uhp
.s
) != OKAY
)
187 if (ok_blook(smtp_use_starttls
)) {
188 fprintf(stderr
, tr(225, "No SSL support compiled in.\n"));
193 /* Shorthand: no authentication, plain HELO? */
194 if (ccred
->cc_authtype
== AUTHTYPE_NONE
) {
195 snprintf(o
, sizeof o
, LINE("HELO %s"), nodename(1));
197 _ANSWER(2, FAL0
, FAL0
);
201 /* We'll have to deal with authentication */
202 snprintf(o
, sizeof o
, LINE("EHLO %s"), nodename(1));
204 _ANSWER(2, FAL0
, FAL0
);
206 switch (ccred
->cc_authtype
) {
208 /* FALLTHRU (doesn't happen) */
210 _OUT(LINE("AUTH PLAIN"));
211 _ANSWER(3, FAL0
, FAL0
);
213 snprintf(o
, sizeof o
, "%c%s%c%s",
214 '\0', ccred
->cc_user
.s
, '\0', ccred
->cc_pass
.s
);
215 b64_encode_buf(&b64
, o
, ccred
->cc_user
.l
+ ccred
->cc_pass
.l
+ 2,
216 B64_SALLOC
| B64_CRLF
);
218 _ANSWER(2, FAL0
, FAL0
);
221 _OUT(LINE("AUTH LOGIN"));
222 _ANSWER(3, FAL0
, FAL0
);
224 b64_encode_cp(&b64
, ccred
->cc_user
.s
, B64_SALLOC
| B64_CRLF
);
226 _ANSWER(3, FAL0
, FAL0
);
228 b64_encode_cp(&b64
, ccred
->cc_pass
.s
, B64_SALLOC
| B64_CRLF
);
230 _ANSWER(2, FAL0
, FAL0
);
233 case AUTHTYPE_CRAM_MD5
:
234 _OUT(LINE("AUTH CRAM-MD5"));
235 _ANSWER(3, FAL0
, TRU1
);
236 cp
= cram_md5_string(ccred
->cc_user
.s
, ccred
->cc_pass
.s
, slp
->dat
);
238 _ANSWER(2, FAL0
, FAL0
);
242 case AUTHTYPE_GSSAPI
:
243 if (!_smtp_gssapi(sp
, urlp
, ccred
, slp
))
250 snprintf(o
, sizeof o
, LINE("MAIL FROM:<%s>"), urlp
->url_uh
.s
);
252 _ANSWER(2, FAL0
, FAL0
);
254 for (n
= to
; n
!= NULL
; n
= n
->n_flink
) {
255 if (!(n
->n_type
& GDEL
)) {
256 snprintf(o
, sizeof o
, LINE("RCPT TO:<%s>"), skinned_name(n
));
258 _ANSWER(2, FAL0
, FAL0
);
263 _ANSWER(3, FAL0
, FAL0
);
267 while (fgetline(&slp
->buf
, &slp
->bufsize
, &cnt
, &blen
, fi
, 1) != NULL
) {
269 if (*slp
->buf
== '\n') {
272 } else if (inbcc
&& blankchar(*slp
->buf
))
274 /* We know what we have generated first, so do not look for whitespace
276 else if (!ascncasecmp(slp
->buf
, "bcc: ", 5)) {
283 if (*slp
->buf
== '.') {
284 if (options
& OPT_DEBUG
)
287 swrite1(sp
, ".", 1, 1);
289 if (options
& OPT_DEBUG
) {
290 fprintf(stderr
, ">>> %s", slp
->buf
);
293 slp
->buf
[blen
- 1] = NL
[0];
294 slp
->buf
[blen
] = NL
[1];
295 swrite1(sp
, slp
->buf
, blen
+ 1, 1);
298 _ANSWER(2, FAL0
, FAL0
);
301 _ANSWER(2, TRU1
, FAL0
);
304 if (slp
->buf
!= NULL
)
311 # include "smtp_gssapi.h"
318 smtp_mta(struct url
*urlp
, struct name
* volatile to
, FILE *fi
,
319 struct header
*hp
, struct ccred
*ccred
)
322 sighandler_type
volatile saveterm
;
326 saveterm
= safe_signal(SIGTERM
, SIG_IGN
);
327 if (sigsetjmp(_smtp_jmp
, 1))
329 if (saveterm
!= SIG_IGN
)
330 safe_signal(SIGTERM
, &_smtp_onterm
);
332 memset(&so
, 0, sizeof so
);
333 if (!(options
& OPT_DEBUG
) && sopen(&so
, urlp
) != OKAY
)
337 rv
= _smtp_talk(to
, hp
, fi
, &so
, urlp
, ccred
);
339 if (!(options
& OPT_DEBUG
))
342 safe_signal(SIGTERM
, saveterm
);
349 #endif /* HAVE_SMTP */
351 /* vim:set fenc=utf-8:s-it-mode */