makeconfig, Makefile: extend to support variable INCLUDES..
[s-mailx.git] / smtp.c
blobb4561e3b1785445a3594a46a3f60989eac0fa7d1
1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 */
6 /*
7 * Copyright (c) 2000
8 * Gunnar Ritter. All rights reserved.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by Gunnar Ritter
21 * and his contributors.
22 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
39 #ifndef lint
40 #ifdef DOSCCS
41 static char sccsid[] = "@(#)smtp.c 2.43 (gritter) 8/4/07";
42 #endif
43 #endif /* not lint */
45 #include "rcv.h"
47 #include <sys/utsname.h>
48 #ifdef HAVE_SOCKETS
49 #include <sys/socket.h>
50 #include <netdb.h>
51 #include <netinet/in.h>
52 #ifdef HAVE_ARPA_INET_H
53 #include <arpa/inet.h>
54 #endif /* HAVE_ARPA_INET_H */
55 #endif /* HAVE_SOCKETS */
56 #include <unistd.h>
57 #include <setjmp.h>
59 #include "extern.h"
60 #include "md5.h"
63 * Mail -- a mail program
65 * SMTP client and other internet related functions.
68 #ifdef HAVE_SOCKETS
69 static int verbose;
70 static int _debug;
71 #endif
74 * Return our hostname.
76 char *
77 nodename(int mayoverride)
79 static char *hostname;
80 char *hn;
81 struct utsname ut;
82 #ifdef HAVE_SOCKETS
83 #ifdef HAVE_IPv6_FUNCS
84 struct addrinfo hints, *res;
85 #else /* !HAVE_IPv6_FUNCS */
86 struct hostent *hent;
87 #endif /* !HAVE_IPv6_FUNCS */
88 #endif /* HAVE_SOCKETS */
90 if (mayoverride && (hn = value("hostname")) != NULL && *hn) {
91 free(hostname);
92 hostname = sstrdup(hn);
94 if (hostname == NULL) {
95 uname(&ut);
96 hn = ut.nodename;
97 #ifdef HAVE_SOCKETS
98 #ifdef HAVE_IPv6_FUNCS
99 memset(&hints, 0, sizeof hints);
100 hints.ai_socktype = SOCK_DGRAM; /* dummy */
101 hints.ai_flags = AI_CANONNAME;
102 if (getaddrinfo(hn, "0", &hints, &res) == 0) {
103 if (res->ai_canonname) {
104 hn = salloc(strlen(res->ai_canonname) + 1);
105 strcpy(hn, res->ai_canonname);
107 freeaddrinfo(res);
109 #else /* !HAVE_IPv6_FUNCS */
110 hent = gethostbyname(hn);
111 if (hent != NULL) {
112 hn = hent->h_name;
114 #endif /* !HAVE_IPv6_FUNCS */
115 #endif /* HAVE_SOCKETS */
116 hostname = smalloc(strlen(hn) + 1);
117 strcpy(hostname, hn);
119 return hostname;
123 * Return the user's From: address(es).
125 char *
126 myaddrs(struct header *hp)
128 char *cp, *hn;
129 static char *addr;
130 size_t sz;
132 if (hp != NULL && hp->h_from != NULL) {
133 if (hp->h_from->n_fullname)
134 return savestr(hp->h_from->n_fullname);
135 if (hp->h_from->n_name)
136 return savestr(hp->h_from->n_name);
138 if ((cp = value("from")) != NULL)
139 return cp;
141 * When invoking sendmail directly, it's its task
142 * to generate a From: address.
144 if (value("smtp") == NULL)
145 return NULL;
146 if (addr == NULL) {
147 hn = nodename(1);
148 sz = strlen(myname) + strlen(hn) + 2;
149 addr = smalloc(sz);
150 snprintf(addr, sz, "%s@%s", myname, hn);
152 return addr;
155 char *
156 myorigin(struct header *hp)
158 char *cp;
159 struct name *np;
161 if ((cp = myaddrs(hp)) == NULL ||
162 (np = sextract(cp, GEXTRA|GFULL)) == NULL)
163 return NULL;
164 return np->n_flink != NULL ? value("sender") : cp;
167 #ifdef HAVE_SOCKETS
169 static int read_smtp(struct sock *sp, int value, int ign_eof);
170 static int talk_smtp(struct name *to, FILE *fi, struct sock *sp,
171 char *server, char *uhp, struct header *hp,
172 const char *user, const char *password, const char *skinned);
174 char *
175 smtp_auth_var(const char *type, const char *addr)
177 char *var, *cp;
178 int len;
180 var = ac_alloc(len = strlen(type) + strlen(addr) + 7);
181 snprintf(var, len, "smtp-auth%s-%s", type, addr);
182 if ((cp = value(var)) != NULL)
183 cp = savestr(cp);
184 else {
185 snprintf(var, len, "smtp-auth%s", type);
186 if ((cp = value(var)) != NULL)
187 cp = savestr(cp);
189 ac_free(var);
190 return cp;
193 static char *smtpbuf;
194 static size_t smtpbufsize;
197 * Get the SMTP server's answer, expecting value.
199 static int
200 read_smtp(struct sock *sp, int value, int ign_eof)
202 int ret;
203 int len;
205 do {
206 if ((len = sgetline(&smtpbuf, &smtpbufsize, NULL, sp)) < 6) {
207 if (len >= 0 && !ign_eof)
208 fprintf(stderr, catgets(catd, CATSET, 241,
209 "Unexpected EOF on SMTP connection\n"));
210 return -1;
212 if (verbose || debug || _debug)
213 fputs(smtpbuf, stderr);
214 switch (*smtpbuf) {
215 case '1': ret = 1; break;
216 case '2': ret = 2; break;
217 case '3': ret = 3; break;
218 case '4': ret = 4; break;
219 default: ret = 5;
221 if (value != ret)
222 fprintf(stderr, catgets(catd, CATSET, 191,
223 "smtp-server: %s"), smtpbuf);
224 } while (smtpbuf[3] == '-');
225 return ret;
229 * Macros for talk_smtp.
231 #define _SMTP_ANSWER(x, ign_eof) \
232 if (!debug && !_debug) { \
233 int y; \
234 if ((y = read_smtp(sp, x, ign_eof)) != (x) && \
235 (!(ign_eof) || y != -1)) { \
236 if (b != NULL) \
237 free(b); \
238 return 1; \
242 #define SMTP_ANSWER(x) _SMTP_ANSWER(x, 0)
244 #define SMTP_OUT(x) if (verbose || debug || _debug) \
245 fprintf(stderr, ">>> %s", x); \
246 if (!debug && !_debug) \
247 swrite(sp, x);
250 * Talk to a SMTP server.
252 static int
253 talk_smtp(struct name *to, FILE *fi, struct sock *sp,
254 char *xserver, char *uhp, struct header *hp,
255 const char *user, const char *password, const char *skinned)
257 struct name *n;
258 char *b = NULL, o[LINESIZE];
259 size_t blen, bsize = 0, count;
260 char *b64, *authstr, *cp;
261 enum { AUTH_NONE, AUTH_PLAIN, AUTH_LOGIN, AUTH_CRAM_MD5 } auth;
262 int inhdr = 1, inbcc = 0;
264 if ((authstr = smtp_auth_var("", skinned)) == NULL)
265 auth = user && password ? AUTH_LOGIN : AUTH_NONE;
266 else if (strcmp(authstr, "plain") == 0)
267 auth = AUTH_PLAIN;
268 else if (strcmp(authstr, "login") == 0)
269 auth = AUTH_LOGIN;
270 else if (strcmp(authstr, "cram-md5") == 0)
271 auth = AUTH_CRAM_MD5;
272 else {
273 fprintf(stderr, "Unknown SMTP authentication "
274 "method: \"%s\"\n", authstr);
275 return 1;
277 if (auth != AUTH_NONE && (user == NULL || password == NULL)) {
278 fprintf(stderr, "User and password are necessary "
279 "for SMTP authentication.\n");
280 return 1;
282 SMTP_ANSWER(2);
283 #ifdef USE_SSL
284 if (value("smtp-use-starttls") ||
285 value("smtp-use-tls") /* v11.0 compatibility */) {
286 char *server;
287 if ((cp = strchr(xserver, ':')) != NULL) {
288 server = salloc(cp - xserver + 1);
289 memcpy(server, xserver, cp - xserver);
290 server[cp - xserver] = '\0';
291 } else
292 server = xserver;
293 snprintf(o, sizeof o, "EHLO %s\r\n", nodename(1));
294 SMTP_OUT(o);
295 SMTP_ANSWER(2);
296 SMTP_OUT("STARTTLS\r\n");
297 SMTP_ANSWER(2);
298 if (!debug && !_debug && ssl_open(server, sp, uhp) != OKAY)
299 return 1;
301 #else /* !USE_SSL */
302 if (value("smtp-use-starttls") || value("smtp-use-tls")) {
303 fprintf(stderr, "No SSL support compiled in.\n");
304 return 1;
306 #endif /* !USE_SSL */
307 if (auth != AUTH_NONE) {
308 snprintf(o, sizeof o, "EHLO %s\r\n", nodename(1));
309 SMTP_OUT(o);
310 SMTP_ANSWER(2);
311 switch (auth) {
312 default:
313 case AUTH_LOGIN:
314 SMTP_OUT("AUTH LOGIN\r\n");
315 SMTP_ANSWER(3);
316 b64 = strtob64(user);
317 snprintf(o, sizeof o, "%s\r\n", b64);
318 free(b64);
319 SMTP_OUT(o);
320 SMTP_ANSWER(3);
321 b64 = strtob64(password);
322 snprintf(o, sizeof o, "%s\r\n", b64);
323 free(b64);
324 SMTP_OUT(o);
325 SMTP_ANSWER(2);
326 break;
327 case AUTH_PLAIN:
328 SMTP_OUT("AUTH PLAIN\r\n");
329 SMTP_ANSWER(3);
330 snprintf(o, sizeof o, "%c%s%c%s", '\0', user, '\0',
331 password);
332 b64 = memtob64(o, strlen(user)+strlen(password)+2);
333 snprintf(o, sizeof o, "%s\r\n", b64);
334 SMTP_OUT(o);
335 SMTP_ANSWER(2);
336 break;
337 case AUTH_CRAM_MD5:
338 SMTP_OUT("AUTH CRAM-MD5\r\n");
339 SMTP_ANSWER(3);
340 for (cp = smtpbuf; digitchar(*cp&0377); cp++);
341 while (blankchar(*cp&0377)) cp++;
342 cp = cram_md5_string(user, password, cp);
343 SMTP_OUT(cp);
344 SMTP_ANSWER(2);
345 break;
347 } else {
348 snprintf(o, sizeof o, "HELO %s\r\n", nodename(1));
349 SMTP_OUT(o);
350 SMTP_ANSWER(2);
352 snprintf(o, sizeof o, "MAIL FROM:<%s>\r\n", skinned);
353 SMTP_OUT(o);
354 SMTP_ANSWER(2);
355 for (n = to; n != NULL; n = n->n_flink) {
356 if ((n->n_type & GDEL) == 0) {
357 snprintf(o, sizeof o, "RCPT TO:<%s>\r\n",
358 skin(n->n_name));
359 SMTP_OUT(o);
360 SMTP_ANSWER(2);
363 SMTP_OUT("DATA\r\n");
364 SMTP_ANSWER(3);
365 fflush(fi);
366 rewind(fi);
367 count = fsize(fi);
368 while (fgetline(&b, &bsize, &count, &blen, fi, 1) != NULL) {
369 if (inhdr) {
370 if (*b == '\n') {
371 inhdr = 0;
372 inbcc = 0;
373 } else if (inbcc && blankchar(*b & 0377))
374 continue;
376 * We know what we have generated first, so
377 * do not look for whitespace before the ':'.
379 else if (ascncasecmp(b, "bcc: ", 5) == 0) {
380 inbcc = 1;
381 continue;
382 } else
383 inbcc = 0;
385 if (*b == '.') {
386 if (debug || _debug)
387 putc('.', stderr);
388 else
389 swrite1(sp, ".", 1, 1);
391 if (debug || _debug) {
392 fprintf(stderr, ">>> %s", b);
393 continue;
395 b[blen-1] = '\r';
396 b[blen] = '\n';
397 swrite1(sp, b, blen+1, 1);
399 SMTP_OUT(".\r\n");
400 SMTP_ANSWER(2);
401 SMTP_OUT("QUIT\r\n");
402 _SMTP_ANSWER(2, 1);
403 if (b != NULL)
404 free(b);
405 return 0;
408 static sigjmp_buf smtpjmp;
410 static void
411 onterm(int signo)
413 siglongjmp(smtpjmp, 1);
417 * Connect to a SMTP server.
420 smtp_mta(char *server, struct name *to, FILE *fi, struct header *hp,
421 const char *user, const char *password, const char *skinned)
423 struct sock so;
424 int use_ssl, ret;
425 sighandler_type saveterm;
427 memset(&so, 0, sizeof so);
428 verbose = value("verbose") != NULL;
429 _debug = value("debug") != NULL;
430 saveterm = safe_signal(SIGTERM, SIG_IGN);
431 if (sigsetjmp(smtpjmp, 1)) {
432 safe_signal(SIGTERM, saveterm);
433 return 1;
435 if (saveterm != SIG_IGN)
436 safe_signal(SIGTERM, onterm);
437 if (strncmp(server, "smtp://", 7) == 0) {
438 use_ssl = 0;
439 server += 7;
440 #ifdef USE_SSL
441 } else if (strncmp(server, "smtps://", 8) == 0) {
442 use_ssl = 1;
443 server += 8;
444 #endif
445 } else
446 use_ssl = 0;
447 if (!debug && !_debug && sopen(server, &so, use_ssl, server,
448 use_ssl ? "smtps" : "smtp", verbose) != OKAY) {
449 safe_signal(SIGTERM, saveterm);
450 return 1;
452 so.s_desc = "SMTP";
453 ret = talk_smtp(to, fi, &so, server, server, hp,
454 user, password, skinned);
455 if (!debug && !_debug)
456 sclose(&so);
457 if (smtpbuf) {
458 free(smtpbuf);
459 smtpbuf = NULL;
460 smtpbufsize = 0;
462 safe_signal(SIGTERM, saveterm);
463 return ret;
465 #else /* !HAVE_SOCKETS */
467 smtp_mta(char *server, struct name *to, FILE *fi, struct header *hp,
468 const char *user, const char *password, const char *skinned)
470 fputs(catgets(catd, CATSET, 194,
471 "No SMTP support compiled in.\n"), stderr);
472 return 1;
474 #endif /* !HAVE_SOCKETS */