Support optional WANT_MD5 configuration
[s-mailx.git] / smtp.c
bloba3dab744406476ae027fa33625ae49733fd8e663
1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 Steffen "Daode" Nurpmeso.
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 lint
41 #ifdef DOSCCS
42 static char sccsid[] = "@(#)smtp.c 2.43 (gritter) 8/4/07";
43 #endif
44 #endif /* not lint */
46 #include "rcv.h"
48 #include <sys/utsname.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
57 #include <unistd.h>
58 #include <setjmp.h>
60 #include "extern.h"
62 #ifdef USE_MD5
63 # include "md5.h"
64 #endif
67 * Mail -- a mail program
69 * SMTP client and other internet related functions.
72 /* TODO longjmp() globbering as in cmd1.c and cmd3.c (see there)
73 * TODO Problem: Popen doesn't encapsulate all cases of open failures,
74 * TODO may leave child running if fdopen() fails! */
76 #ifdef USE_SMTP
77 static int verbose;
78 static int _debug;
79 #endif
82 * Return our hostname.
84 char *
85 nodename(int mayoverride)
87 static char *hostname;
88 char *hn;
89 struct utsname ut;
90 #ifdef HAVE_SOCKETS
91 #ifdef HAVE_IPv6_FUNCS
92 struct addrinfo hints, *res;
93 #else /* !HAVE_IPv6_FUNCS */
94 struct hostent *hent;
95 #endif /* !HAVE_IPv6_FUNCS */
96 #endif /* HAVE_SOCKETS */
98 if (mayoverride && (hn = value("hostname")) != NULL && *hn) {
99 free(hostname);
100 hostname = sstrdup(hn);
102 if (hostname == NULL) {
103 uname(&ut);
104 hn = ut.nodename;
105 #ifdef HAVE_SOCKETS
106 #ifdef HAVE_IPv6_FUNCS
107 memset(&hints, 0, sizeof hints);
108 hints.ai_socktype = SOCK_DGRAM; /* dummy */
109 hints.ai_flags = AI_CANONNAME;
110 if (getaddrinfo(hn, "0", &hints, &res) == 0) {
111 if (res->ai_canonname) {
112 hn = salloc(strlen(res->ai_canonname) + 1);
113 strcpy(hn, res->ai_canonname);
115 freeaddrinfo(res);
117 #else /* !HAVE_IPv6_FUNCS */
118 hent = gethostbyname(hn);
119 if (hent != NULL) {
120 hn = hent->h_name;
122 #endif /* !HAVE_IPv6_FUNCS */
123 #endif /* HAVE_SOCKETS */
124 hostname = smalloc(strlen(hn) + 1);
125 strcpy(hostname, hn);
127 return hostname;
131 * Return the user's From: address(es).
133 char *
134 myaddrs(struct header *hp)
136 char *cp, *hn;
137 static char *addr;
138 size_t sz;
140 if (hp != NULL && hp->h_from != NULL) {
141 if (hp->h_from->n_fullname)
142 return savestr(hp->h_from->n_fullname);
143 if (hp->h_from->n_name)
144 return savestr(hp->h_from->n_name);
146 if ((cp = value("from")) != NULL)
147 return cp;
149 * When invoking sendmail directly, it's its task
150 * to generate a From: address.
152 if (value("smtp") == NULL)
153 return NULL;
154 if (addr == NULL) {
155 hn = nodename(1);
156 sz = strlen(myname) + strlen(hn) + 2;
157 addr = smalloc(sz);
158 snprintf(addr, sz, "%s@%s", myname, hn);
160 return addr;
163 char *
164 myorigin(struct header *hp)
166 char *cp;
167 struct name *np;
169 if ((cp = myaddrs(hp)) == NULL ||
170 (np = sextract(cp, GEXTRA|GFULL)) == NULL)
171 return NULL;
172 return np->n_flink != NULL ? value("sender") : cp;
175 #ifdef USE_SMTP
177 static int read_smtp(struct sock *sp, int value, int ign_eof);
178 static int talk_smtp(struct name *to, FILE *fi, struct sock *sp,
179 char *server, char *uhp, struct header *hp,
180 const char *user, const char *password, const char *skinned);
182 char *
183 smtp_auth_var(const char *type, const char *addr)
185 char *var, *cp;
186 int len;
188 var = ac_alloc(len = strlen(type) + strlen(addr) + 7);
189 snprintf(var, len, "smtp-auth%s-%s", type, addr);
190 if ((cp = value(var)) != NULL)
191 cp = savestr(cp);
192 else {
193 snprintf(var, len, "smtp-auth%s", type);
194 if ((cp = value(var)) != NULL)
195 cp = savestr(cp);
197 ac_free(var);
198 return cp;
201 static char *smtpbuf;
202 static size_t smtpbufsize;
205 * Get the SMTP server's answer, expecting value.
207 static int
208 read_smtp(struct sock *sp, int value, int ign_eof)
210 int ret;
211 int len;
213 do {
214 if ((len = sgetline(&smtpbuf, &smtpbufsize, NULL, sp)) < 6) {
215 if (len >= 0 && !ign_eof)
216 fprintf(stderr, catgets(catd, CATSET, 241,
217 "Unexpected EOF on SMTP connection\n"));
218 return -1;
220 if (verbose || debug || _debug)
221 fputs(smtpbuf, stderr);
222 switch (*smtpbuf) {
223 case '1': ret = 1; break;
224 case '2': ret = 2; break;
225 case '3': ret = 3; break;
226 case '4': ret = 4; break;
227 default: ret = 5;
229 if (value != ret)
230 fprintf(stderr, catgets(catd, CATSET, 191,
231 "smtp-server: %s"), smtpbuf);
232 } while (smtpbuf[3] == '-');
233 return ret;
237 * Macros for talk_smtp.
239 #define _SMTP_ANSWER(x, ign_eof) \
240 if (!debug && !_debug) { \
241 int y; \
242 if ((y = read_smtp(sp, x, ign_eof)) != (x) && \
243 (!(ign_eof) || y != -1)) { \
244 if (b != NULL) \
245 free(b); \
246 return 1; \
250 #define SMTP_ANSWER(x) _SMTP_ANSWER(x, 0)
252 #define SMTP_OUT(x) if (verbose || debug || _debug) \
253 fprintf(stderr, ">>> %s", x); \
254 if (!debug && !_debug) \
255 swrite(sp, x);
258 * Talk to a SMTP server.
260 static int
261 talk_smtp(struct name *to, FILE *fi, struct sock *sp,
262 char *xserver, char *uhp, struct header *hp,
263 const char *user, const char *password, const char *skinned)
265 struct name *n;
266 char *b = NULL, o[LINESIZE];
267 size_t blen, bsize = 0, count;
268 char *b64, *authstr, *cp;
269 enum { AUTH_NONE, AUTH_PLAIN, AUTH_LOGIN, AUTH_CRAM_MD5 } auth;
270 int inhdr = 1, inbcc = 0;
271 (void)hp;
273 if ((authstr = smtp_auth_var("", skinned)) == NULL)
274 auth = user && password ? AUTH_LOGIN : AUTH_NONE;
275 else if (strcmp(authstr, "plain") == 0)
276 auth = AUTH_PLAIN;
277 else if (strcmp(authstr, "login") == 0)
278 auth = AUTH_LOGIN;
279 else if (strcmp(authstr, "cram-md5") == 0) {
280 #ifdef USE_MD5
281 auth = AUTH_CRAM_MD5;
282 #else
283 fprintf(stderr, tr(277, "No CRAM-MD5 support compiled in.\n"));
284 return (1);
285 #endif
286 } else {
287 fprintf(stderr, tr(274,
288 "Unknown SMTP authentication method: %s\n"), authstr);
289 return 1;
291 if (auth != AUTH_NONE && (user == NULL || password == NULL)) {
292 fprintf(stderr, tr(275,
293 "User and password are necessary "
294 "for SMTP authentication.\n"));
295 return 1;
297 SMTP_ANSWER(2);
298 #ifdef USE_SSL
299 if (value("smtp-use-starttls") ||
300 value("smtp-use-tls") /* v11.0 compatibility */) {
301 char *server;
302 if ((cp = strchr(xserver, ':')) != NULL) {
303 server = salloc(cp - xserver + 1);
304 memcpy(server, xserver, cp - xserver);
305 server[cp - xserver] = '\0';
306 } else
307 server = xserver;
308 snprintf(o, sizeof o, "EHLO %s\r\n", nodename(1));
309 SMTP_OUT(o);
310 SMTP_ANSWER(2);
311 SMTP_OUT("STARTTLS\r\n");
312 SMTP_ANSWER(2);
313 if (!debug && !_debug && ssl_open(server, sp, uhp) != OKAY)
314 return 1;
316 #else /* !USE_SSL */
317 if (value("smtp-use-starttls") || value("smtp-use-tls")) {
318 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
319 return 1;
321 #endif /* !USE_SSL */
322 if (auth != AUTH_NONE) {
323 snprintf(o, sizeof o, "EHLO %s\r\n", nodename(1));
324 SMTP_OUT(o);
325 SMTP_ANSWER(2);
326 switch (auth) {
327 default:
328 case AUTH_LOGIN:
329 SMTP_OUT("AUTH LOGIN\r\n");
330 SMTP_ANSWER(3);
331 b64 = strtob64(user);
332 snprintf(o, sizeof o, "%s\r\n", b64);
333 free(b64);
334 SMTP_OUT(o);
335 SMTP_ANSWER(3);
336 b64 = strtob64(password);
337 snprintf(o, sizeof o, "%s\r\n", b64);
338 free(b64);
339 SMTP_OUT(o);
340 SMTP_ANSWER(2);
341 break;
342 case AUTH_PLAIN:
343 SMTP_OUT("AUTH PLAIN\r\n");
344 SMTP_ANSWER(3);
345 snprintf(o, sizeof o, "%c%s%c%s", '\0', user, '\0',
346 password);
347 b64 = memtob64(o, strlen(user)+strlen(password)+2);
348 snprintf(o, sizeof o, "%s\r\n", b64);
349 SMTP_OUT(o);
350 SMTP_ANSWER(2);
351 break;
352 #ifdef USE_MD5
353 case AUTH_CRAM_MD5:
354 SMTP_OUT("AUTH CRAM-MD5\r\n");
355 SMTP_ANSWER(3);
356 for (cp = smtpbuf; digitchar(*cp&0377); cp++);
357 while (blankchar(*cp&0377)) cp++;
358 cp = cram_md5_string(user, password, cp);
359 SMTP_OUT(cp);
360 SMTP_ANSWER(2);
361 break;
362 #endif
364 } else {
365 snprintf(o, sizeof o, "HELO %s\r\n", nodename(1));
366 SMTP_OUT(o);
367 SMTP_ANSWER(2);
369 snprintf(o, sizeof o, "MAIL FROM:<%s>\r\n", skinned);
370 SMTP_OUT(o);
371 SMTP_ANSWER(2);
372 for (n = to; n != NULL; n = n->n_flink) {
373 if ((n->n_type & GDEL) == 0) {
374 snprintf(o, sizeof o, "RCPT TO:<%s>\r\n",
375 skin(n->n_name));
376 SMTP_OUT(o);
377 SMTP_ANSWER(2);
380 SMTP_OUT("DATA\r\n");
381 SMTP_ANSWER(3);
382 fflush(fi);
383 rewind(fi);
384 count = fsize(fi);
385 while (fgetline(&b, &bsize, &count, &blen, fi, 1) != NULL) {
386 if (inhdr) {
387 if (*b == '\n') {
388 inhdr = 0;
389 inbcc = 0;
390 } else if (inbcc && blankchar(*b & 0377))
391 continue;
393 * We know what we have generated first, so
394 * do not look for whitespace before the ':'.
396 else if (ascncasecmp(b, "bcc: ", 5) == 0) {
397 inbcc = 1;
398 continue;
399 } else
400 inbcc = 0;
402 if (*b == '.') {
403 if (debug || _debug)
404 putc('.', stderr);
405 else
406 swrite1(sp, ".", 1, 1);
408 if (debug || _debug) {
409 fprintf(stderr, ">>> %s", b);
410 continue;
412 b[blen-1] = '\r';
413 b[blen] = '\n';
414 swrite1(sp, b, blen+1, 1);
416 SMTP_OUT(".\r\n");
417 SMTP_ANSWER(2);
418 SMTP_OUT("QUIT\r\n");
419 _SMTP_ANSWER(2, 1);
420 if (b != NULL)
421 free(b);
422 return 0;
425 static sigjmp_buf smtpjmp;
427 static void
428 onterm(int signo)
430 (void)signo;
431 siglongjmp(smtpjmp, 1);
435 * Connect to a SMTP server.
438 smtp_mta(char *server, struct name *to, FILE *fi, struct header *hp,
439 const char *user, const char *password, const char *skinned)
441 struct sock so;
442 int use_ssl, ret;
443 sighandler_type saveterm;
445 memset(&so, 0, sizeof so);
446 verbose = value("verbose") != NULL;
447 _debug = value("debug") != NULL;
448 saveterm = safe_signal(SIGTERM, SIG_IGN);
449 if (sigsetjmp(smtpjmp, 1)) {
450 safe_signal(SIGTERM, saveterm);
451 return 1;
453 if (saveterm != SIG_IGN)
454 safe_signal(SIGTERM, onterm);
455 if (strncmp(server, "smtp://", 7) == 0) {
456 use_ssl = 0;
457 server += 7;
458 #ifdef USE_SSL
459 } else if (strncmp(server, "smtps://", 8) == 0) {
460 use_ssl = 1;
461 server += 8;
462 #endif
463 } else
464 use_ssl = 0;
465 if (!debug && !_debug && sopen(server, &so, use_ssl, server,
466 use_ssl ? "smtps" : "smtp", verbose) != OKAY) {
467 safe_signal(SIGTERM, saveterm);
468 return 1;
470 so.s_desc = "SMTP";
471 ret = talk_smtp(to, fi, &so, server, server, hp,
472 user, password, skinned);
473 if (!debug && !_debug)
474 sclose(&so);
475 if (smtpbuf) {
476 free(smtpbuf);
477 smtpbuf = NULL;
478 smtpbufsize = 0;
480 safe_signal(SIGTERM, saveterm);
481 return ret;
483 #else /* !USE_SMTP */
485 smtp_mta(char *server, struct name *to, FILE *fi, struct header *hp,
486 const char *user, const char *password, const char *skinned)
488 (void)server;
489 (void)to;
490 (void)fi;
491 (void)hp;
492 (void)user;
493 (void)password;
494 (void)skinned;
495 fputs(catgets(catd, CATSET, 194,
496 "No SMTP support compiled in.\n"), stderr);
497 return 1;
499 #endif /* USE_SMTP */