openssl.c:_smime_sign_include_chain_creat(): use n_strsep() instead
[s-mailx.git] / smtp.c
blobcf0df9e160afe1b948f87db158725deb0e88f312
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ SMTP client.
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>.
8 */
9 /*
10 * Copyright (c) 2000
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
15 * are met:
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
39 * SUCH DAMAGE.
42 #ifndef HAVE_AMALGAMATION
43 # include "nail.h"
44 #endif
46 EMPTY_FILE(smtp)
47 #ifdef HAVE_SMTP
48 #include <sys/socket.h>
50 #include <netdb.h>
52 #include <netinet/in.h>
54 #ifdef HAVE_ARPA_INET_H
55 # include <arpa/inet.h>
56 #endif
58 #undef NL
59 #undef LINE
60 #define NL "\015\012"
61 #define LINE(X) X NL
63 struct smtp_line {
64 char *dat; /* Actual data */
65 size_t datlen;
66 char *buf; /* Memory buffer */
67 size_t bufsize;
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 bool_t _smtp_talk(struct sock *sp, struct sendbundle *sbp);
81 #ifdef HAVE_GSSAPI
82 static bool_t _smtp_gssapi(struct sock *sp, struct sendbundle *sbp,
83 struct smtp_line *slp);
84 #endif
86 static void
87 _smtp_onterm(int signo)
89 NYD_X; /* Signal handler */
90 UNUSED(signo);
91 siglongjmp(_smtp_jmp, 1);
94 static int
95 _smtp_read(struct sock *sp, struct smtp_line *slp, int val,
96 bool_t ign_eof, bool_t want_dat)
98 int rv, len;
99 char *cp;
100 NYD_ENTER;
102 do {
103 if ((len = sgetline(&slp->buf, &slp->bufsize, NULL, sp)) < 6) {
104 if (len >= 0 && !ign_eof)
105 fprintf(stderr, tr(241, "Unexpected EOF on SMTP connection\n"));
106 rv = -1;
107 goto jleave;
109 if (options & OPT_VERBOSE)
110 fputs(slp->buf, stderr);
111 switch (slp->buf[0]) {
112 case '1': rv = 1; break;
113 case '2': rv = 2; break;
114 case '3': rv = 3; break;
115 case '4': rv = 4; break;
116 default: rv = 5; break;
118 if (val != rv)
119 fprintf(stderr, tr(191, "smtp-server: %s"), slp->buf);
120 } while (slp->buf[3] == '-');
122 if (want_dat) {
123 for (cp = slp->buf; digitchar(*cp); --len, ++cp)
125 for (; blankchar(*cp); --len, ++cp)
127 slp->dat = cp;
128 assert(len >= 2);
129 len -= 2;
130 cp[slp->datlen = (size_t)len] = '\0';
132 jleave:
133 NYD_LEAVE;
134 return rv;
137 /* Indirect SMTP I/O */
138 #define _ANSWER(X, IGNEOF, WANTDAT) \
139 do if (!(options & OPT_DEBUG)) {\
140 int y;\
141 if ((y = _smtp_read(sp, slp, X, IGNEOF, WANTDAT)) != (X) &&\
142 (!(IGNEOF) || y != -1))\
143 goto jleave;\
144 } while (0)
145 #define _OUT(X) \
146 do {\
147 if (options & OPT_VERBOSE)\
148 fprintf(stderr, ">>> %s", X);\
149 if (!(options & OPT_DEBUG))\
150 swrite(sp, X);\
151 } while (0)
153 static bool_t
154 _smtp_talk(struct sock *sp, struct sendbundle *sbp)
156 char o[LINESIZE], *hostname, *cp;
157 struct smtp_line _sl, *slp = &_sl;
158 struct str b64;
159 struct name *n;
160 size_t blen, cnt;
161 bool_t inhdr = TRU1, inbcc = FAL0, rv = FAL0;
162 NYD_ENTER;
164 hostname = nodename(TRU1);
165 slp->buf = NULL;
166 slp->bufsize = 0;
168 /* Read greeting */
169 _ANSWER(2, FAL0, FAL0);
171 #ifdef HAVE_SSL
172 if (!sp->s_use_ssl && ok_blook(smtp_use_starttls)) {
173 snprintf(o, sizeof o, LINE("EHLO %s"), hostname);
174 _OUT(o);
175 _ANSWER(2, FAL0, FAL0);
177 _OUT(LINE("STARTTLS"));
178 _ANSWER(2, FAL0, FAL0);
180 if (!(options & OPT_DEBUG) &&
181 ssl_open(sbp->sb_url.url_host.s, sp, sbp->sb_url.url_uhp.s) != OKAY)
182 goto jleave;
184 #else
185 if (ok_blook(smtp_use_starttls)) {
186 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
187 goto jleave;
189 #endif
191 /* Shorthand: no authentication, plain HELO? */
192 if (sbp->sb_ccred.cc_authtype == AUTHTYPE_NONE) {
193 snprintf(o, sizeof o, LINE("HELO %s"), hostname);
194 _OUT(o);
195 _ANSWER(2, FAL0, FAL0);
196 goto jsend;
199 /* We'll have to deal with authentication */
200 snprintf(o, sizeof o, LINE("EHLO %s"), hostname);
201 _OUT(o);
202 _ANSWER(2, FAL0, FAL0);
204 switch (sbp->sb_ccred.cc_authtype) {
205 default:
206 /* FALLTHRU (doesn't happen) */
207 case AUTHTYPE_PLAIN:
208 _OUT(LINE("AUTH PLAIN"));
209 _ANSWER(3, FAL0, FAL0);
211 snprintf(o, sizeof o, "%c%s%c%s",
212 '\0', sbp->sb_ccred.cc_user.s, '\0', sbp->sb_ccred.cc_pass.s);
213 b64_encode_buf(&b64, o,
214 sbp->sb_ccred.cc_user.l + sbp->sb_ccred.cc_pass.l + 2,
215 B64_SALLOC | B64_CRLF);
216 _OUT(b64.s);
217 _ANSWER(2, FAL0, FAL0);
218 break;
219 case AUTHTYPE_LOGIN:
220 _OUT(LINE("AUTH LOGIN"));
221 _ANSWER(3, FAL0, FAL0);
223 b64_encode_cp(&b64, sbp->sb_ccred.cc_user.s, B64_SALLOC | B64_CRLF);
224 _OUT(b64.s);
225 _ANSWER(3, FAL0, FAL0);
227 b64_encode_cp(&b64, sbp->sb_ccred.cc_pass.s, B64_SALLOC | B64_CRLF);
228 _OUT(b64.s);
229 _ANSWER(2, FAL0, FAL0);
230 break;
231 #ifdef HAVE_MD5
232 case AUTHTYPE_CRAM_MD5:
233 _OUT(LINE("AUTH CRAM-MD5"));
234 _ANSWER(3, FAL0, TRU1);
235 cp = cram_md5_string(sbp->sb_ccred.cc_user.s, sbp->sb_ccred.cc_pass.s,
236 slp->dat);
237 _OUT(cp);
238 _ANSWER(2, FAL0, FAL0);
239 break;
240 #endif
241 #ifdef HAVE_GSSAPI
242 case AUTHTYPE_GSSAPI:
243 if (!_smtp_gssapi(sp, sbp, slp))
244 goto jleave;
245 break;
246 #endif
249 jsend:
250 snprintf(o, sizeof o, LINE("MAIL FROM:<%s>"), sbp->sb_url.url_uh.s);
251 _OUT(o);
252 _ANSWER(2, FAL0, FAL0);
254 for (n = sbp->sb_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));
257 _OUT(o);
258 _ANSWER(2, FAL0, FAL0);
262 _OUT(LINE("DATA"));
263 _ANSWER(3, FAL0, FAL0);
265 fflush_rewind(sbp->sb_input);
266 cnt = fsize(sbp->sb_input);
267 while (fgetline(&slp->buf, &slp->bufsize, &cnt, &blen, sbp->sb_input, 1)
268 != NULL) {
269 if (inhdr) {
270 if (*slp->buf == '\n')
271 inhdr = inbcc = FAL0;
272 else if (inbcc && blankchar(*slp->buf))
273 continue;
274 /* We know what we have generated first, so do not look for whitespace
275 * before the ':' */
276 else if (!ascncasecmp(slp->buf, "bcc: ", 5)) {
277 inbcc = TRU1;
278 continue;
279 } else
280 inbcc = FAL0;
283 if (*slp->buf == '.') {
284 if (options & OPT_DEBUG)
285 putc('.', stderr);
286 else
287 swrite1(sp, ".", 1, 1);
289 if (options & OPT_DEBUG) {
290 fprintf(stderr, ">>> %s", slp->buf);
291 continue;
293 slp->buf[blen - 1] = NL[0];
294 slp->buf[blen] = NL[1];
295 swrite1(sp, slp->buf, blen + 1, 1);
297 _OUT(LINE("."));
298 _ANSWER(2, FAL0, FAL0);
300 _OUT(LINE("QUIT"));
301 _ANSWER(2, TRU1, FAL0);
302 rv = TRU1;
303 jleave:
304 if (slp->buf != NULL)
305 free(slp->buf);
306 NYD_LEAVE;
307 return rv;
310 #ifdef HAVE_GSSAPI
311 # include "smtp_gssapi.h"
312 #endif
314 #undef _OUT
315 #undef _ANSWER
317 FL bool_t
318 smtp_mta(struct sendbundle *sbp)
320 struct sock so;
321 sighandler_type volatile saveterm;
322 bool_t volatile rv = FAL0;
323 NYD_ENTER;
325 saveterm = safe_signal(SIGTERM, SIG_IGN);
326 if (sigsetjmp(_smtp_jmp, 1))
327 goto jleave;
328 if (saveterm != SIG_IGN)
329 safe_signal(SIGTERM, &_smtp_onterm);
331 memset(&so, 0, sizeof so);
332 if (!(options & OPT_DEBUG) && !sopen(&so, &sbp->sb_url))
333 goto jleave;
335 so.s_desc = "SMTP";
336 rv = _smtp_talk(&so, sbp);
338 if (!(options & OPT_DEBUG))
339 sclose(&so);
340 jleave:
341 safe_signal(SIGTERM, saveterm);
342 NYD_LEAVE;
343 return rv;
346 #undef LINE
347 #undef NL
348 #endif /* HAVE_SMTP */
350 /* vim:set fenc=utf-8:s-it-mode */