NYD: names.c
[s-mailx.git] / smtp.c
blob574f4d637ea37c2464e046f7b2e66757ffc34fbb
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 - 2014 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 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 EMPTY_FILE(smtp)
45 #ifdef HAVE_SMTP
46 #ifdef HAVE_SOCKETS
47 # include <sys/socket.h>
49 # include <netdb.h>
51 # include <netinet/in.h>
53 # ifdef HAVE_ARPA_INET_H
54 # include <arpa/inet.h>
55 # endif
56 #endif
58 static char *smtpbuf;
59 static size_t smtpbufsize;
60 static sigjmp_buf smtpjmp;
62 static void onterm(int signo);
63 static int read_smtp(struct sock *sp, int val, int ign_eof);
64 static int talk_smtp(struct name *to, FILE *fi, struct sock *sp,
65 char *server, char *uhp, struct header *hp,
66 const char *user, const char *password,
67 const char *skinned);
69 static void
70 onterm(int signo)
72 (void)signo;
73 siglongjmp(smtpjmp, 1);
77 * Get the SMTP server's answer, expecting val.
79 static int
80 read_smtp(struct sock *sp, int val, int ign_eof)
82 int ret;
83 int len;
85 do {
86 if ((len = sgetline(&smtpbuf, &smtpbufsize, NULL, sp)) < 6) {
87 if (len >= 0 && !ign_eof)
88 fprintf(stderr, tr(241,
89 "Unexpected EOF on SMTP connection\n"));
90 return -1;
92 if (options & OPT_VERBOSE)
93 fputs(smtpbuf, stderr);
94 switch (*smtpbuf) {
95 case '1': ret = 1; break;
96 case '2': ret = 2; break;
97 case '3': ret = 3; break;
98 case '4': ret = 4; break;
99 default: ret = 5;
101 if (val != ret)
102 fprintf(stderr, tr(191, "smtp-server: %s"), smtpbuf);
103 } while (smtpbuf[3] == '-');
104 return ret;
108 * Macros for talk_smtp.
110 #define _SMTP_ANSWER(x, ign_eof) \
111 if ((options & OPT_DEBUG) == 0) { \
112 int y; \
113 if ((y = read_smtp(sp, x, ign_eof)) != (x) && \
114 (!(ign_eof) || y != -1)) { \
115 if (b != NULL) \
116 free(b); \
117 return 1; \
121 #define SMTP_ANSWER(x) _SMTP_ANSWER(x, 0)
123 #define SMTP_OUT(x) if (options & OPT_VERBOSE) \
124 fprintf(stderr, ">>> %s", x); \
125 if ((options & OPT_DEBUG) == 0) \
126 swrite(sp, x);
129 * Talk to a SMTP server.
131 static int
132 talk_smtp(struct name *to, FILE *fi, struct sock *sp,
133 char *xserver, char *uhp, struct header *hp,
134 const char *user, const char *password, const char *skinned)
136 char o[LINESIZE], *authstr, *cp, *b = NULL;
137 struct str b64;
138 struct name *n;
139 size_t blen, cnt, bsize = 0;
140 enum { AUTH_NONE, AUTH_PLAIN, AUTH_LOGIN, AUTH_CRAM_MD5 } auth;
141 int inhdr = 1, inbcc = 0;
142 (void)hp;
143 (void)xserver;
144 (void)uhp;
146 if ((authstr = smtp_auth_var("", skinned)) == NULL)
147 auth = user && password ? AUTH_LOGIN : AUTH_NONE;
148 else if (strcmp(authstr, "plain") == 0)
149 auth = AUTH_PLAIN;
150 else if (strcmp(authstr, "login") == 0)
151 auth = AUTH_LOGIN;
152 else if (strcmp(authstr, "cram-md5") == 0) {
153 #ifdef HAVE_MD5
154 auth = AUTH_CRAM_MD5;
155 #else
156 fprintf(stderr, tr(277, "No CRAM-MD5 support compiled in.\n"));
157 return (1);
158 #endif
159 } else {
160 fprintf(stderr, tr(274,
161 "Unknown SMTP authentication method: %s\n"), authstr);
162 return 1;
164 if (auth != AUTH_NONE && (user == NULL || password == NULL)) {
165 fprintf(stderr, tr(275,
166 "User and password are necessary "
167 "for SMTP authentication.\n"));
168 return 1;
170 SMTP_ANSWER(2);
171 #ifdef HAVE_SSL
172 if (!sp->s_use_ssl && ok_blook(smtp_use_starttls)) {
173 char *server;
174 if ((cp = strchr(xserver, ':')) != NULL) {
175 server = salloc(cp - xserver + 1);
176 memcpy(server, xserver, cp - xserver);
177 server[cp - xserver] = '\0';
178 } else
179 server = xserver;
180 snprintf(o, sizeof o, "EHLO %s\r\n", nodename(1));
181 SMTP_OUT(o);
182 SMTP_ANSWER(2);
183 SMTP_OUT("STARTTLS\r\n");
184 SMTP_ANSWER(2);
185 if ((options & OPT_DEBUG) == 0 &&
186 ssl_open(server, sp, uhp) != OKAY)
187 return 1;
189 #else
190 if (ok_blook(smtp_use_starttls)) {
191 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
192 return 1;
194 #endif
195 if (auth != AUTH_NONE) {
196 snprintf(o, sizeof o, "EHLO %s\r\n", nodename(1));
197 SMTP_OUT(o);
198 SMTP_ANSWER(2);
199 switch (auth) {
200 case AUTH_NONE:
201 #ifndef HAVE_MD5
202 case AUTH_CRAM_MD5:
203 #endif
204 /* FALLTRHU
205 * Won't happen, but gcc(1) and clang(1) whine without
206 * and Coverity whines with; that's a hard one.. */
207 case AUTH_LOGIN:
208 SMTP_OUT("AUTH LOGIN\r\n");
209 SMTP_ANSWER(3);
210 (void)b64_encode_cp(&b64, user, B64_SALLOC|B64_CRLF);
211 SMTP_OUT(b64.s);
212 SMTP_ANSWER(3);
213 (void)b64_encode_cp(&b64, password,
214 B64_SALLOC|B64_CRLF);
215 SMTP_OUT(b64.s);
216 SMTP_ANSWER(2);
217 break;
218 case AUTH_PLAIN:
219 SMTP_OUT("AUTH PLAIN\r\n");
220 SMTP_ANSWER(3);
221 (void)snprintf(o, sizeof o, "%c%s%c%s",
222 '\0', user, '\0', password);
223 (void)b64_encode_buf(&b64, o, strlen(user) +
224 strlen(password) + 2, B64_SALLOC|B64_CRLF);
225 SMTP_OUT(b64.s);
226 SMTP_ANSWER(2);
227 break;
228 #ifdef HAVE_MD5
229 case AUTH_CRAM_MD5:
230 SMTP_OUT("AUTH CRAM-MD5\r\n");
231 SMTP_ANSWER(3);
232 for (cp = smtpbuf; digitchar(*cp); ++cp)
234 while (blankchar(*cp))
235 ++cp;
236 cp = cram_md5_string(user, password, cp);
237 SMTP_OUT(cp);
238 SMTP_ANSWER(2);
239 break;
240 #endif
242 } else {
243 snprintf(o, sizeof o, "HELO %s\r\n", nodename(1));
244 SMTP_OUT(o);
245 SMTP_ANSWER(2);
247 snprintf(o, sizeof o, "MAIL FROM:<%s>\r\n", skinned);
248 SMTP_OUT(o);
249 SMTP_ANSWER(2);
250 for (n = to; n != NULL; n = n->n_flink) {
251 if ((n->n_type & GDEL) == 0) {
252 snprintf(o, sizeof o, "RCPT TO:<%s>\r\n",
253 skinned_name(n));
254 SMTP_OUT(o);
255 SMTP_ANSWER(2);
258 SMTP_OUT("DATA\r\n");
259 SMTP_ANSWER(3);
260 fflush(fi);
261 rewind(fi);
262 cnt = fsize(fi);
263 while (fgetline(&b, &bsize, &cnt, &blen, fi, 1) != NULL) {
264 if (inhdr) {
265 if (*b == '\n') {
266 inhdr = 0;
267 inbcc = 0;
268 } else if (inbcc && blankchar(*b & 0377))
269 continue;
271 * We know what we have generated first, so
272 * do not look for whitespace before the ':'.
274 else if (ascncasecmp(b, "bcc: ", 5) == 0) {
275 inbcc = 1;
276 continue;
277 } else
278 inbcc = 0;
280 if (*b == '.') {
281 if (options & OPT_DEBUG)
282 putc('.', stderr);
283 else
284 swrite1(sp, ".", 1, 1);
286 if (options & OPT_DEBUG) {
287 fprintf(stderr, ">>> %s", b);
288 continue;
290 b[blen-1] = '\r';
291 b[blen] = '\n';
292 swrite1(sp, b, blen+1, 1);
294 SMTP_OUT(".\r\n");
295 SMTP_ANSWER(2);
296 SMTP_OUT("QUIT\r\n");
297 _SMTP_ANSWER(2, 1);
298 if (b != NULL)
299 free(b);
300 return 0;
303 FL char *
304 smtp_auth_var(char const *atype, char const *addr)
306 size_t tl, al, len;
307 char *var, *cp;
309 tl = strlen(atype);
310 al = strlen(addr);
311 len = tl + al + 10 + 1;
312 var = ac_alloc(len);
314 /* Try a 'user@host', i.e., address specific version first */
315 (void)snprintf(var, len, "smtp-auth%s-%s", atype, addr);
316 if ((cp = vok_vlook(var)) == NULL) {
317 snprintf(var, len, "smtp-auth%s", atype);
318 cp = vok_vlook(var);
320 if (cp != NULL)
321 cp = savestr(cp);
323 ac_free(var);
324 return cp;
328 * Connect to a SMTP server.
330 FL int
331 smtp_mta(char *volatile server, struct name *volatile to, FILE *fi,
332 struct header *hp, const char *user, const char *password,
333 const char *skinned)
335 struct sock so;
336 int use_ssl, ret;
337 sighandler_type volatile saveterm;
339 memset(&so, 0, sizeof so);
340 saveterm = safe_signal(SIGTERM, SIG_IGN);
341 if (sigsetjmp(smtpjmp, 1)) {
342 safe_signal(SIGTERM, saveterm);
343 return 1;
345 if (saveterm != SIG_IGN)
346 safe_signal(SIGTERM, onterm);
347 if (strncmp(server, "smtp://", 7) == 0) {
348 use_ssl = 0;
349 server += 7;
350 #ifdef HAVE_SSL
351 } else if (strncmp(server, "smtps://", 8) == 0) {
352 use_ssl = 1;
353 server += 8;
354 #endif
355 } else
356 use_ssl = 0;
357 if ((options & OPT_DEBUG) == 0 && sopen(server, &so, use_ssl, server,
358 (use_ssl ? "smtps" : "smtp")) != OKAY) {
359 safe_signal(SIGTERM, saveterm);
360 return 1;
362 so.s_desc = "SMTP";
363 ret = talk_smtp(to, fi, &so, server, server, hp,
364 user, password, skinned);
365 if ((options & OPT_DEBUG) == 0)
366 sclose(&so);
367 if (smtpbuf) {
368 free(smtpbuf);
369 smtpbuf = NULL;
370 smtpbufsize = 0;
372 safe_signal(SIGTERM, saveterm);
373 return ret;
375 #endif /* HAVE_SMTP */