Add *message-id-disable*
[s-mailx.git] / smtp.c
blobe4e780d4bb9b2219555774947a6aa1e4b851638e
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 "rcv.h"
47 #include <setjmp.h>
48 #include <unistd.h>
49 #ifdef HAVE_SOCKETS
50 # include <sys/socket.h>
51 # include <netdb.h>
52 # include <netinet/in.h>
53 # ifdef HAVE_ARPA_INET_H
54 # include <arpa/inet.h>
55 # endif
56 #endif
58 #include "extern.h"
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, catgets(catd, CATSET, 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, catgets(catd, CATSET, 191,
108 "smtp-server: %s"), smtpbuf);
109 } while (smtpbuf[3] == '-');
110 return ret;
114 * Macros for talk_smtp.
116 #define _SMTP_ANSWER(x, ign_eof) \
117 if ((options & OPT_DEBUG) == 0) { \
118 int y; \
119 if ((y = read_smtp(sp, x, ign_eof)) != (x) && \
120 (!(ign_eof) || y != -1)) { \
121 if (b != NULL) \
122 free(b); \
123 return 1; \
127 #define SMTP_ANSWER(x) _SMTP_ANSWER(x, 0)
129 #define SMTP_OUT(x) if (options & OPT_VERBOSE) \
130 fprintf(stderr, ">>> %s", x); \
131 if ((options & OPT_DEBUG) == 0) \
132 swrite(sp, x);
135 * Talk to a SMTP server.
137 static int
138 talk_smtp(struct name *to, FILE *fi, struct sock *sp,
139 char *xserver, char *uhp, struct header *hp,
140 const char *user, const char *password, const char *skinned)
142 char o[LINESIZE], *authstr, *cp, *b = NULL;
143 struct str b64;
144 struct name *n;
145 size_t blen, cnt, bsize = 0;
146 enum { AUTH_NONE, AUTH_PLAIN, AUTH_LOGIN, AUTH_CRAM_MD5 } auth;
147 int inhdr = 1, inbcc = 0;
148 (void)hp;
149 (void)xserver;
150 (void)uhp;
152 if ((authstr = smtp_auth_var("", skinned)) == NULL)
153 auth = user && password ? AUTH_LOGIN : AUTH_NONE;
154 else if (strcmp(authstr, "plain") == 0)
155 auth = AUTH_PLAIN;
156 else if (strcmp(authstr, "login") == 0)
157 auth = AUTH_LOGIN;
158 else if (strcmp(authstr, "cram-md5") == 0) {
159 #ifdef HAVE_MD5
160 auth = AUTH_CRAM_MD5;
161 #else
162 fprintf(stderr, tr(277, "No CRAM-MD5 support compiled in.\n"));
163 return (1);
164 #endif
165 } else {
166 fprintf(stderr, tr(274,
167 "Unknown SMTP authentication method: %s\n"), authstr);
168 return 1;
170 if (auth != AUTH_NONE && (user == NULL || password == NULL)) {
171 fprintf(stderr, tr(275,
172 "User and password are necessary "
173 "for SMTP authentication.\n"));
174 return 1;
176 SMTP_ANSWER(2);
177 #ifdef HAVE_SSL
178 if (! sp->s_use_ssl && value("smtp-use-starttls")) {
179 char *server;
180 if ((cp = strchr(xserver, ':')) != NULL) {
181 server = salloc(cp - xserver + 1);
182 memcpy(server, xserver, cp - xserver);
183 server[cp - xserver] = '\0';
184 } else
185 server = xserver;
186 snprintf(o, sizeof o, "EHLO %s\r\n", nodename(1));
187 SMTP_OUT(o);
188 SMTP_ANSWER(2);
189 SMTP_OUT("STARTTLS\r\n");
190 SMTP_ANSWER(2);
191 if ((options & OPT_DEBUG) == 0 &&
192 ssl_open(server, sp, uhp) != OKAY)
193 return 1;
195 #else
196 if (value("smtp-use-starttls")) {
197 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
198 return 1;
200 #endif
201 if (auth != AUTH_NONE) {
202 snprintf(o, sizeof o, "EHLO %s\r\n", nodename(1));
203 SMTP_OUT(o);
204 SMTP_ANSWER(2);
205 switch (auth) {
206 case AUTH_NONE:
207 #ifndef HAVE_MD5
208 case AUTH_CRAM_MD5:
209 #endif
210 /* FALLTRHU
211 * Won't happen, but gcc(1) and clang(1) whine without
212 * and Coverity whines with; that's a hard one.. */
213 case AUTH_LOGIN:
214 SMTP_OUT("AUTH LOGIN\r\n");
215 SMTP_ANSWER(3);
216 (void)b64_encode_cp(&b64, user, B64_SALLOC|B64_CRLF);
217 SMTP_OUT(b64.s);
218 SMTP_ANSWER(3);
219 (void)b64_encode_cp(&b64, password,
220 B64_SALLOC|B64_CRLF);
221 SMTP_OUT(b64.s);
222 SMTP_ANSWER(2);
223 break;
224 case AUTH_PLAIN:
225 SMTP_OUT("AUTH PLAIN\r\n");
226 SMTP_ANSWER(3);
227 (void)snprintf(o, sizeof o, "%c%s%c%s",
228 '\0', user, '\0', password);
229 (void)b64_encode_buf(&b64, o, strlen(user) +
230 strlen(password) + 2, B64_SALLOC|B64_CRLF);
231 SMTP_OUT(b64.s);
232 SMTP_ANSWER(2);
233 break;
234 #ifdef HAVE_MD5
235 case AUTH_CRAM_MD5:
236 SMTP_OUT("AUTH CRAM-MD5\r\n");
237 SMTP_ANSWER(3);
238 for (cp = smtpbuf; digitchar(*cp); ++cp)
240 while (blankchar(*cp))
241 ++cp;
242 cp = cram_md5_string(user, password, cp);
243 SMTP_OUT(cp);
244 SMTP_ANSWER(2);
245 break;
246 #endif
248 } else {
249 snprintf(o, sizeof o, "HELO %s\r\n", nodename(1));
250 SMTP_OUT(o);
251 SMTP_ANSWER(2);
253 snprintf(o, sizeof o, "MAIL FROM:<%s>\r\n", skinned);
254 SMTP_OUT(o);
255 SMTP_ANSWER(2);
256 for (n = to; n != NULL; n = n->n_flink) {
257 if ((n->n_type & GDEL) == 0) {
258 snprintf(o, sizeof o, "RCPT TO:<%s>\r\n",
259 skinned_name(n));
260 SMTP_OUT(o);
261 SMTP_ANSWER(2);
264 SMTP_OUT("DATA\r\n");
265 SMTP_ANSWER(3);
266 fflush(fi);
267 rewind(fi);
268 cnt = fsize(fi);
269 while (fgetline(&b, &bsize, &cnt, &blen, fi, 1) != NULL) {
270 if (inhdr) {
271 if (*b == '\n') {
272 inhdr = 0;
273 inbcc = 0;
274 } else if (inbcc && blankchar(*b & 0377))
275 continue;
277 * We know what we have generated first, so
278 * do not look for whitespace before the ':'.
280 else if (ascncasecmp(b, "bcc: ", 5) == 0) {
281 inbcc = 1;
282 continue;
283 } else
284 inbcc = 0;
286 if (*b == '.') {
287 if (options & OPT_DEBUG)
288 putc('.', stderr);
289 else
290 swrite1(sp, ".", 1, 1);
292 if (options & OPT_DEBUG) {
293 fprintf(stderr, ">>> %s", b);
294 continue;
296 b[blen-1] = '\r';
297 b[blen] = '\n';
298 swrite1(sp, b, blen+1, 1);
300 SMTP_OUT(".\r\n");
301 SMTP_ANSWER(2);
302 SMTP_OUT("QUIT\r\n");
303 _SMTP_ANSWER(2, 1);
304 if (b != NULL)
305 free(b);
306 return 0;
309 char *
310 smtp_auth_var(char const *atype, char const *addr)
312 size_t tl, al, len;
313 char *var, *cp;
315 tl = strlen(atype);
316 al = strlen(addr);
317 len = tl + al + 10 + 1;
318 var = ac_alloc(len);
320 /* Try a 'user@host', i.e., address specific version first */
321 (void)snprintf(var, len, "smtp-auth%s-%s", atype, addr);
322 if ((cp = value(var)) == NULL) {
323 snprintf(var, len, "smtp-auth%s", atype);
324 cp = value(var);
326 if (cp != NULL)
327 cp = savestr(cp);
329 ac_free(var);
330 return cp;
334 * Connect to a SMTP server.
337 smtp_mta(char *volatile server, struct name *volatile to, FILE *fi,
338 struct header *hp, const char *user, const char *password,
339 const char *skinned)
341 struct sock so;
342 int use_ssl, ret;
343 sighandler_type saveterm;
345 memset(&so, 0, sizeof so);
346 saveterm = safe_signal(SIGTERM, SIG_IGN);
347 if (sigsetjmp(smtpjmp, 1)) {
348 safe_signal(SIGTERM, saveterm);
349 return 1;
351 if (saveterm != SIG_IGN)
352 safe_signal(SIGTERM, onterm);
353 if (strncmp(server, "smtp://", 7) == 0) {
354 use_ssl = 0;
355 server += 7;
356 #ifdef HAVE_SSL
357 } else if (strncmp(server, "smtps://", 8) == 0) {
358 use_ssl = 1;
359 server += 8;
360 #endif
361 } else
362 use_ssl = 0;
363 if ((options & OPT_DEBUG) == 0 && sopen(server, &so, use_ssl, server,
364 use_ssl ? "smtps" : "smtp",
365 (options & OPT_VERBOSE) != 0) != OKAY) {
366 safe_signal(SIGTERM, saveterm);
367 return 1;
369 so.s_desc = "SMTP";
370 ret = talk_smtp(to, fi, &so, server, server, hp,
371 user, password, skinned);
372 if ((options & OPT_DEBUG) == 0)
373 sclose(&so);
374 if (smtpbuf) {
375 free(smtpbuf);
376 smtpbuf = NULL;
377 smtpbufsize = 0;
379 safe_signal(SIGTERM, saveterm);
380 return ret;
382 #endif /* HAVE_SMTP */