mk-mk.in: EXT_CFLAGS: remove .eh frames!..
[s-mailx.git] / smtp.c
blob6bf46097c98b621b36fcb433187de1070189aebf
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ SMTP client and other internet related functions.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2013 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 2000
9 * Gunnar Ritter. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by Gunnar Ritter
22 * and his contributors.
23 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #include "config.h"
42 #ifndef HAVE_SMTP
43 typedef int avoid_empty_file_compiler_warning;
44 #else
45 #include "nail.h"
47 #ifdef HAVE_SOCKETS
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
57 #endif
59 #ifdef HAVE_MD5
60 # include "md5.h"
61 #endif
63 static char *smtpbuf;
64 static size_t smtpbufsize;
65 static sigjmp_buf smtpjmp;
67 static void onterm(int signo);
68 static int read_smtp(struct sock *sp, int val, int ign_eof);
69 static int talk_smtp(struct name *to, FILE *fi, struct sock *sp,
70 char *server, char *uhp, struct header *hp,
71 const char *user, const char *password,
72 const char *skinned);
74 static void
75 onterm(int signo)
77 (void)signo;
78 siglongjmp(smtpjmp, 1);
82 * Get the SMTP server's answer, expecting val.
84 static int
85 read_smtp(struct sock *sp, int val, int ign_eof)
87 int ret;
88 int len;
90 do {
91 if ((len = sgetline(&smtpbuf, &smtpbufsize, NULL, sp)) < 6) {
92 if (len >= 0 && !ign_eof)
93 fprintf(stderr, tr(241,
94 "Unexpected EOF on SMTP connection\n"));
95 return -1;
97 if (options & OPT_VERBOSE)
98 fputs(smtpbuf, stderr);
99 switch (*smtpbuf) {
100 case '1': ret = 1; break;
101 case '2': ret = 2; break;
102 case '3': ret = 3; break;
103 case '4': ret = 4; break;
104 default: ret = 5;
106 if (val != ret)
107 fprintf(stderr, tr(191, "smtp-server: %s"), smtpbuf);
108 } while (smtpbuf[3] == '-');
109 return ret;
113 * Macros for talk_smtp.
115 #define _SMTP_ANSWER(x, ign_eof) \
116 if ((options & OPT_DEBUG) == 0) { \
117 int y; \
118 if ((y = read_smtp(sp, x, ign_eof)) != (x) && \
119 (!(ign_eof) || y != -1)) { \
120 if (b != NULL) \
121 free(b); \
122 return 1; \
126 #define SMTP_ANSWER(x) _SMTP_ANSWER(x, 0)
128 #define SMTP_OUT(x) if (options & OPT_VERBOSE) \
129 fprintf(stderr, ">>> %s", x); \
130 if ((options & OPT_DEBUG) == 0) \
131 swrite(sp, x);
134 * Talk to a SMTP server.
136 static int
137 talk_smtp(struct name *to, FILE *fi, struct sock *sp,
138 char *xserver, char *uhp, struct header *hp,
139 const char *user, const char *password, const char *skinned)
141 char o[LINESIZE], *authstr, *cp, *b = NULL;
142 struct str b64;
143 struct name *n;
144 size_t blen, cnt, bsize = 0;
145 enum { AUTH_NONE, AUTH_PLAIN, AUTH_LOGIN, AUTH_CRAM_MD5 } auth;
146 int inhdr = 1, inbcc = 0;
147 (void)hp;
148 (void)xserver;
149 (void)uhp;
151 if ((authstr = smtp_auth_var("", skinned)) == NULL)
152 auth = user && password ? AUTH_LOGIN : AUTH_NONE;
153 else if (strcmp(authstr, "plain") == 0)
154 auth = AUTH_PLAIN;
155 else if (strcmp(authstr, "login") == 0)
156 auth = AUTH_LOGIN;
157 else if (strcmp(authstr, "cram-md5") == 0) {
158 #ifdef HAVE_MD5
159 auth = AUTH_CRAM_MD5;
160 #else
161 fprintf(stderr, tr(277, "No CRAM-MD5 support compiled in.\n"));
162 return (1);
163 #endif
164 } else {
165 fprintf(stderr, tr(274,
166 "Unknown SMTP authentication method: %s\n"), authstr);
167 return 1;
169 if (auth != AUTH_NONE && (user == NULL || password == NULL)) {
170 fprintf(stderr, tr(275,
171 "User and password are necessary "
172 "for SMTP authentication.\n"));
173 return 1;
175 SMTP_ANSWER(2);
176 #ifdef HAVE_SSL
177 if (! sp->s_use_ssl && value("smtp-use-starttls")) {
178 char *server;
179 if ((cp = strchr(xserver, ':')) != NULL) {
180 server = salloc(cp - xserver + 1);
181 memcpy(server, xserver, cp - xserver);
182 server[cp - xserver] = '\0';
183 } else
184 server = xserver;
185 snprintf(o, sizeof o, "EHLO %s\r\n", nodename(1));
186 SMTP_OUT(o);
187 SMTP_ANSWER(2);
188 SMTP_OUT("STARTTLS\r\n");
189 SMTP_ANSWER(2);
190 if ((options & OPT_DEBUG) == 0 &&
191 ssl_open(server, sp, uhp) != OKAY)
192 return 1;
194 #else
195 if (value("smtp-use-starttls")) {
196 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
197 return 1;
199 #endif
200 if (auth != AUTH_NONE) {
201 snprintf(o, sizeof o, "EHLO %s\r\n", nodename(1));
202 SMTP_OUT(o);
203 SMTP_ANSWER(2);
204 switch (auth) {
205 case AUTH_NONE:
206 #ifndef HAVE_MD5
207 case AUTH_CRAM_MD5:
208 #endif
209 /* FALLTRHU
210 * Won't happen, but gcc(1) and clang(1) whine without
211 * and Coverity whines with; that's a hard one.. */
212 case AUTH_LOGIN:
213 SMTP_OUT("AUTH LOGIN\r\n");
214 SMTP_ANSWER(3);
215 (void)b64_encode_cp(&b64, user, B64_SALLOC|B64_CRLF);
216 SMTP_OUT(b64.s);
217 SMTP_ANSWER(3);
218 (void)b64_encode_cp(&b64, password,
219 B64_SALLOC|B64_CRLF);
220 SMTP_OUT(b64.s);
221 SMTP_ANSWER(2);
222 break;
223 case AUTH_PLAIN:
224 SMTP_OUT("AUTH PLAIN\r\n");
225 SMTP_ANSWER(3);
226 (void)snprintf(o, sizeof o, "%c%s%c%s",
227 '\0', user, '\0', password);
228 (void)b64_encode_buf(&b64, o, strlen(user) +
229 strlen(password) + 2, B64_SALLOC|B64_CRLF);
230 SMTP_OUT(b64.s);
231 SMTP_ANSWER(2);
232 break;
233 #ifdef HAVE_MD5
234 case AUTH_CRAM_MD5:
235 SMTP_OUT("AUTH CRAM-MD5\r\n");
236 SMTP_ANSWER(3);
237 for (cp = smtpbuf; digitchar(*cp); ++cp)
239 while (blankchar(*cp))
240 ++cp;
241 cp = cram_md5_string(user, password, cp);
242 SMTP_OUT(cp);
243 SMTP_ANSWER(2);
244 break;
245 #endif
247 } else {
248 snprintf(o, sizeof o, "HELO %s\r\n", nodename(1));
249 SMTP_OUT(o);
250 SMTP_ANSWER(2);
252 snprintf(o, sizeof o, "MAIL FROM:<%s>\r\n", skinned);
253 SMTP_OUT(o);
254 SMTP_ANSWER(2);
255 for (n = to; n != NULL; n = n->n_flink) {
256 if ((n->n_type & GDEL) == 0) {
257 snprintf(o, sizeof o, "RCPT TO:<%s>\r\n",
258 skinned_name(n));
259 SMTP_OUT(o);
260 SMTP_ANSWER(2);
263 SMTP_OUT("DATA\r\n");
264 SMTP_ANSWER(3);
265 fflush(fi);
266 rewind(fi);
267 cnt = fsize(fi);
268 while (fgetline(&b, &bsize, &cnt, &blen, fi, 1) != NULL) {
269 if (inhdr) {
270 if (*b == '\n') {
271 inhdr = 0;
272 inbcc = 0;
273 } else if (inbcc && blankchar(*b & 0377))
274 continue;
276 * We know what we have generated first, so
277 * do not look for whitespace before the ':'.
279 else if (ascncasecmp(b, "bcc: ", 5) == 0) {
280 inbcc = 1;
281 continue;
282 } else
283 inbcc = 0;
285 if (*b == '.') {
286 if (options & OPT_DEBUG)
287 putc('.', stderr);
288 else
289 swrite1(sp, ".", 1, 1);
291 if (options & OPT_DEBUG) {
292 fprintf(stderr, ">>> %s", b);
293 continue;
295 b[blen-1] = '\r';
296 b[blen] = '\n';
297 swrite1(sp, b, blen+1, 1);
299 SMTP_OUT(".\r\n");
300 SMTP_ANSWER(2);
301 SMTP_OUT("QUIT\r\n");
302 _SMTP_ANSWER(2, 1);
303 if (b != NULL)
304 free(b);
305 return 0;
308 char *
309 smtp_auth_var(char const *atype, char const *addr)
311 size_t tl, al, len;
312 char *var, *cp;
314 tl = strlen(atype);
315 al = strlen(addr);
316 len = tl + al + 10 + 1;
317 var = ac_alloc(len);
319 /* Try a 'user@host', i.e., address specific version first */
320 (void)snprintf(var, len, "smtp-auth%s-%s", atype, addr);
321 if ((cp = value(var)) == NULL) {
322 snprintf(var, len, "smtp-auth%s", atype);
323 cp = value(var);
325 if (cp != NULL)
326 cp = savestr(cp);
328 ac_free(var);
329 return cp;
333 * Connect to a SMTP server.
336 smtp_mta(char *volatile server, struct name *volatile to, FILE *fi,
337 struct header *hp, const char *user, const char *password,
338 const char *skinned)
340 struct sock so;
341 int use_ssl, ret;
342 sighandler_type saveterm;
344 memset(&so, 0, sizeof so);
345 saveterm = safe_signal(SIGTERM, SIG_IGN);
346 if (sigsetjmp(smtpjmp, 1)) {
347 safe_signal(SIGTERM, saveterm);
348 return 1;
350 if (saveterm != SIG_IGN)
351 safe_signal(SIGTERM, onterm);
352 if (strncmp(server, "smtp://", 7) == 0) {
353 use_ssl = 0;
354 server += 7;
355 #ifdef HAVE_SSL
356 } else if (strncmp(server, "smtps://", 8) == 0) {
357 use_ssl = 1;
358 server += 8;
359 #endif
360 } else
361 use_ssl = 0;
362 if ((options & OPT_DEBUG) == 0 && sopen(server, &so, use_ssl, server,
363 use_ssl ? "smtps" : "smtp",
364 (options & OPT_VERBOSE) != 0) != OKAY) {
365 safe_signal(SIGTERM, saveterm);
366 return 1;
368 so.s_desc = "SMTP";
369 ret = talk_smtp(to, fi, &so, server, server, hp,
370 user, password, skinned);
371 if ((options & OPT_DEBUG) == 0)
372 sclose(&so);
373 if (smtpbuf) {
374 free(smtpbuf);
375 smtpbuf = NULL;
376 smtpbufsize = 0;
378 safe_signal(SIGTERM, saveterm);
379 return ret;
381 #endif /* HAVE_SMTP */