Allow *write* with no arguments..
[s-mailx.git] / smtp.c
blob75fd1468b50163bb2e8104cbcdbf4570ef66ebb1
1 /*
2 * S-nail - 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 #include "rcv.h"
42 #include <sys/utsname.h>
43 #ifdef HAVE_SOCKETS
44 # include <sys/socket.h>
45 # include <netdb.h>
46 # include <netinet/in.h>
47 # ifdef HAVE_ARPA_INET_H
48 # include <arpa/inet.h>
49 # endif
50 #endif
51 #include <unistd.h>
52 #include <setjmp.h>
54 #include "extern.h"
56 #ifdef USE_MD5
57 # include "md5.h"
58 #endif
61 * Mail -- a mail program
63 * SMTP client and other internet related functions.
66 /* TODO longjmp() globbering as in cmd1.c and cmd3.c (see there)
67 * TODO Problem: Popen doesn't encapsulate all cases of open failures,
68 * TODO may leave child running if fdopen() fails! */
70 #ifdef USE_SMTP
71 static int verbose;
72 static int _debug;
73 #endif
76 * Return our hostname.
78 char *
79 nodename(int mayoverride)
81 static char *hostname;
82 char *hn;
83 struct utsname ut;
84 #ifdef HAVE_SOCKETS
85 # ifdef USE_IPV6
86 struct addrinfo hints, *res;
87 # else
88 struct hostent *hent;
89 # endif
90 #endif
92 if (mayoverride && (hn = value("hostname")) != NULL && *hn) {
93 free(hostname);
94 hostname = sstrdup(hn);
96 if (hostname == NULL) {
97 uname(&ut);
98 hn = ut.nodename;
99 #ifdef HAVE_SOCKETS
100 # ifdef USE_IPV6
101 memset(&hints, 0, sizeof hints);
102 hints.ai_socktype = SOCK_DGRAM; /* dummy */
103 hints.ai_flags = AI_CANONNAME;
104 if (getaddrinfo(hn, "0", &hints, &res) == 0) {
105 if (res->ai_canonname) {
106 hn = salloc(strlen(res->ai_canonname) + 1);
107 strcpy(hn, res->ai_canonname);
109 freeaddrinfo(res);
111 # else
112 hent = gethostbyname(hn);
113 if (hent != NULL) {
114 hn = hent->h_name;
116 # endif
117 #endif
118 hostname = smalloc(strlen(hn) + 1);
119 strcpy(hostname, hn);
121 return hostname;
125 * Return the user's From: address(es).
127 char *
128 myaddrs(struct header *hp)
130 char *cp, *hn;
131 static char *addr;
132 size_t sz;
134 if (hp != NULL && hp->h_from != NULL) {
135 if (hp->h_from->n_fullname)
136 return savestr(hp->h_from->n_fullname);
137 if (hp->h_from->n_name)
138 return savestr(hp->h_from->n_name);
140 if ((cp = value("from")) != NULL)
141 return cp;
143 * When invoking sendmail directly, it's its task
144 * to generate a From: address.
146 if (value("smtp") == NULL)
147 return NULL;
148 if (addr == NULL) {
149 hn = nodename(1);
150 sz = strlen(myname) + strlen(hn) + 2;
151 addr = smalloc(sz);
152 snprintf(addr, sz, "%s@%s", myname, hn);
154 return addr;
157 char *
158 myorigin(struct header *hp)
160 char *cp;
161 struct name *np;
163 if ((cp = myaddrs(hp)) == NULL ||
164 (np = sextract(cp, GEXTRA|GFULL)) == NULL)
165 return NULL;
166 return np->n_flink != NULL ? value("sender") : cp;
169 #ifdef USE_SMTP
171 static int read_smtp(struct sock *sp, int value, int ign_eof);
172 static int talk_smtp(struct name *to, FILE *fi, struct sock *sp,
173 char *server, char *uhp, struct header *hp,
174 const char *user, const char *password, const char *skinned);
176 char *
177 smtp_auth_var(const char *type, const char *addr)
179 char *var, *cp;
180 int len;
182 var = ac_alloc(len = strlen(type) + strlen(addr) + 7);
183 snprintf(var, len, "smtp-auth%s-%s", type, addr);
184 if ((cp = value(var)) != NULL)
185 cp = savestr(cp);
186 else {
187 snprintf(var, len, "smtp-auth%s", type);
188 if ((cp = value(var)) != NULL)
189 cp = savestr(cp);
191 ac_free(var);
192 return cp;
195 static char *smtpbuf;
196 static size_t smtpbufsize;
199 * Get the SMTP server's answer, expecting value.
201 static int
202 read_smtp(struct sock *sp, int value, int ign_eof)
204 int ret;
205 int len;
207 do {
208 if ((len = sgetline(&smtpbuf, &smtpbufsize, NULL, sp)) < 6) {
209 if (len >= 0 && !ign_eof)
210 fprintf(stderr, catgets(catd, CATSET, 241,
211 "Unexpected EOF on SMTP connection\n"));
212 return -1;
214 if (verbose || debug || _debug)
215 fputs(smtpbuf, stderr);
216 switch (*smtpbuf) {
217 case '1': ret = 1; break;
218 case '2': ret = 2; break;
219 case '3': ret = 3; break;
220 case '4': ret = 4; break;
221 default: ret = 5;
223 if (value != ret)
224 fprintf(stderr, catgets(catd, CATSET, 191,
225 "smtp-server: %s"), smtpbuf);
226 } while (smtpbuf[3] == '-');
227 return ret;
231 * Macros for talk_smtp.
233 #define _SMTP_ANSWER(x, ign_eof) \
234 if (!debug && !_debug) { \
235 int y; \
236 if ((y = read_smtp(sp, x, ign_eof)) != (x) && \
237 (!(ign_eof) || y != -1)) { \
238 if (b != NULL) \
239 free(b); \
240 return 1; \
244 #define SMTP_ANSWER(x) _SMTP_ANSWER(x, 0)
246 #define SMTP_OUT(x) if (verbose || debug || _debug) \
247 fprintf(stderr, ">>> %s", x); \
248 if (!debug && !_debug) \
249 swrite(sp, x);
252 * Talk to a SMTP server.
254 static int
255 talk_smtp(struct name *to, FILE *fi, struct sock *sp,
256 char *xserver, char *uhp, struct header *hp,
257 const char *user, const char *password, const char *skinned)
259 struct name *n;
260 char *b = NULL, o[LINESIZE];
261 size_t blen, bsize = 0, count;
262 char *b64, *authstr, *cp;
263 enum { AUTH_NONE, AUTH_PLAIN, AUTH_LOGIN, AUTH_CRAM_MD5 } auth;
264 int inhdr = 1, inbcc = 0;
265 (void)hp;
267 if ((authstr = smtp_auth_var("", skinned)) == NULL)
268 auth = user && password ? AUTH_LOGIN : AUTH_NONE;
269 else if (strcmp(authstr, "plain") == 0)
270 auth = AUTH_PLAIN;
271 else if (strcmp(authstr, "login") == 0)
272 auth = AUTH_LOGIN;
273 else if (strcmp(authstr, "cram-md5") == 0) {
274 #ifdef USE_MD5
275 auth = AUTH_CRAM_MD5;
276 #else
277 fprintf(stderr, tr(277, "No CRAM-MD5 support compiled in.\n"));
278 return (1);
279 #endif
280 } else {
281 fprintf(stderr, tr(274,
282 "Unknown SMTP authentication method: %s\n"), authstr);
283 return 1;
285 if (auth != AUTH_NONE && (user == NULL || password == NULL)) {
286 fprintf(stderr, tr(275,
287 "User and password are necessary "
288 "for SMTP authentication.\n"));
289 return 1;
291 SMTP_ANSWER(2);
292 #ifdef USE_SSL
293 if (value("smtp-use-starttls") ||
294 value("smtp-use-tls") /* v11.0 compatibility */) {
295 char *server;
296 if ((cp = strchr(xserver, ':')) != NULL) {
297 server = salloc(cp - xserver + 1);
298 memcpy(server, xserver, cp - xserver);
299 server[cp - xserver] = '\0';
300 } else
301 server = xserver;
302 snprintf(o, sizeof o, "EHLO %s\r\n", nodename(1));
303 SMTP_OUT(o);
304 SMTP_ANSWER(2);
305 SMTP_OUT("STARTTLS\r\n");
306 SMTP_ANSWER(2);
307 if (!debug && !_debug && ssl_open(server, sp, uhp) != OKAY)
308 return 1;
310 #else /* !USE_SSL */
311 if (value("smtp-use-starttls") || value("smtp-use-tls")) {
312 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
313 return 1;
315 #endif /* !USE_SSL */
316 if (auth != AUTH_NONE) {
317 snprintf(o, sizeof o, "EHLO %s\r\n", nodename(1));
318 SMTP_OUT(o);
319 SMTP_ANSWER(2);
320 switch (auth) {
321 default:
322 case AUTH_LOGIN:
323 SMTP_OUT("AUTH LOGIN\r\n");
324 SMTP_ANSWER(3);
325 b64 = strtob64(user);
326 snprintf(o, sizeof o, "%s\r\n", b64);
327 free(b64);
328 SMTP_OUT(o);
329 SMTP_ANSWER(3);
330 b64 = strtob64(password);
331 snprintf(o, sizeof o, "%s\r\n", b64);
332 free(b64);
333 SMTP_OUT(o);
334 SMTP_ANSWER(2);
335 break;
336 case AUTH_PLAIN:
337 SMTP_OUT("AUTH PLAIN\r\n");
338 SMTP_ANSWER(3);
339 snprintf(o, sizeof o, "%c%s%c%s", '\0', user, '\0',
340 password);
341 b64 = memtob64(o, strlen(user)+strlen(password)+2);
342 snprintf(o, sizeof o, "%s\r\n", b64);
343 SMTP_OUT(o);
344 SMTP_ANSWER(2);
345 break;
346 #ifdef USE_MD5
347 case AUTH_CRAM_MD5:
348 SMTP_OUT("AUTH CRAM-MD5\r\n");
349 SMTP_ANSWER(3);
350 for (cp = smtpbuf; digitchar(*cp&0377); cp++);
351 while (blankchar(*cp&0377)) cp++;
352 cp = cram_md5_string(user, password, cp);
353 SMTP_OUT(cp);
354 SMTP_ANSWER(2);
355 break;
356 #endif
358 } else {
359 snprintf(o, sizeof o, "HELO %s\r\n", nodename(1));
360 SMTP_OUT(o);
361 SMTP_ANSWER(2);
363 snprintf(o, sizeof o, "MAIL FROM:<%s>\r\n", skinned);
364 SMTP_OUT(o);
365 SMTP_ANSWER(2);
366 for (n = to; n != NULL; n = n->n_flink) {
367 if ((n->n_type & GDEL) == 0) {
368 snprintf(o, sizeof o, "RCPT TO:<%s>\r\n",
369 skinned_name(n));
370 SMTP_OUT(o);
371 SMTP_ANSWER(2);
374 SMTP_OUT("DATA\r\n");
375 SMTP_ANSWER(3);
376 fflush(fi);
377 rewind(fi);
378 count = fsize(fi);
379 while (fgetline(&b, &bsize, &count, &blen, fi, 1) != NULL) {
380 if (inhdr) {
381 if (*b == '\n') {
382 inhdr = 0;
383 inbcc = 0;
384 } else if (inbcc && blankchar(*b & 0377))
385 continue;
387 * We know what we have generated first, so
388 * do not look for whitespace before the ':'.
390 else if (ascncasecmp(b, "bcc: ", 5) == 0) {
391 inbcc = 1;
392 continue;
393 } else
394 inbcc = 0;
396 if (*b == '.') {
397 if (debug || _debug)
398 putc('.', stderr);
399 else
400 swrite1(sp, ".", 1, 1);
402 if (debug || _debug) {
403 fprintf(stderr, ">>> %s", b);
404 continue;
406 b[blen-1] = '\r';
407 b[blen] = '\n';
408 swrite1(sp, b, blen+1, 1);
410 SMTP_OUT(".\r\n");
411 SMTP_ANSWER(2);
412 SMTP_OUT("QUIT\r\n");
413 _SMTP_ANSWER(2, 1);
414 if (b != NULL)
415 free(b);
416 return 0;
419 static sigjmp_buf smtpjmp;
421 static void
422 onterm(int signo)
424 (void)signo;
425 siglongjmp(smtpjmp, 1);
429 * Connect to a SMTP server.
432 smtp_mta(char *volatile server, struct name *volatile to, FILE *fi,
433 struct header *hp, const char *user, const char *password,
434 const char *skinned)
436 struct sock so;
437 int use_ssl, ret;
438 sighandler_type saveterm;
440 memset(&so, 0, sizeof so);
441 verbose = value("verbose") != NULL;
442 _debug = value("debug") != NULL;
443 saveterm = safe_signal(SIGTERM, SIG_IGN);
444 if (sigsetjmp(smtpjmp, 1)) {
445 safe_signal(SIGTERM, saveterm);
446 return 1;
448 if (saveterm != SIG_IGN)
449 safe_signal(SIGTERM, onterm);
450 if (strncmp(server, "smtp://", 7) == 0) {
451 use_ssl = 0;
452 server += 7;
453 #ifdef USE_SSL
454 } else if (strncmp(server, "smtps://", 8) == 0) {
455 use_ssl = 1;
456 server += 8;
457 #endif
458 } else
459 use_ssl = 0;
460 if (!debug && !_debug && sopen(server, &so, use_ssl, server,
461 use_ssl ? "smtps" : "smtp", verbose) != OKAY) {
462 safe_signal(SIGTERM, saveterm);
463 return 1;
465 so.s_desc = "SMTP";
466 ret = talk_smtp(to, fi, &so, server, server, hp,
467 user, password, skinned);
468 if (!debug && !_debug)
469 sclose(&so);
470 if (smtpbuf) {
471 free(smtpbuf);
472 smtpbuf = NULL;
473 smtpbufsize = 0;
475 safe_signal(SIGTERM, saveterm);
476 return ret;
478 #else /* !USE_SMTP */
480 smtp_mta(char *server, struct name *to, FILE *fi, struct header *hp,
481 const char *user, const char *password, const char *skinned)
483 (void)server;
484 (void)to;
485 (void)fi;
486 (void)hp;
487 (void)user;
488 (void)password;
489 (void)skinned;
490 fputs(catgets(catd, CATSET, 194,
491 "No SMTP support compiled in.\n"), stderr);
492 return 1;
494 #endif /* USE_SMTP */