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.
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
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
42 static char sccsid
[] = "@(#)smtp.c 2.43 (gritter) 8/4/07";
48 #include <sys/utsname.h>
50 # include <sys/socket.h>
52 # include <netinet/in.h>
53 # ifdef HAVE_ARPA_INET_H
54 # include <arpa/inet.h>
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! */
82 * Return our hostname.
85 nodename(int mayoverride
)
87 static char *hostname
;
91 #ifdef HAVE_IPv6_FUNCS
92 struct addrinfo hints
, *res
;
93 #else /* !HAVE_IPv6_FUNCS */
95 #endif /* !HAVE_IPv6_FUNCS */
96 #endif /* HAVE_SOCKETS */
98 if (mayoverride
&& (hn
= value("hostname")) != NULL
&& *hn
) {
100 hostname
= sstrdup(hn
);
102 if (hostname
== NULL
) {
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
);
117 #else /* !HAVE_IPv6_FUNCS */
118 hent
= gethostbyname(hn
);
122 #endif /* !HAVE_IPv6_FUNCS */
123 #endif /* HAVE_SOCKETS */
124 hostname
= smalloc(strlen(hn
) + 1);
125 strcpy(hostname
, hn
);
131 * Return the user's From: address(es).
134 myaddrs(struct header
*hp
)
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
)
149 * When invoking sendmail directly, it's its task
150 * to generate a From: address.
152 if (value("smtp") == NULL
)
156 sz
= strlen(myname
) + strlen(hn
) + 2;
158 snprintf(addr
, sz
, "%s@%s", myname
, hn
);
164 myorigin(struct header
*hp
)
169 if ((cp
= myaddrs(hp
)) == NULL
||
170 (np
= sextract(cp
, GEXTRA
|GFULL
)) == NULL
)
172 return np
->n_flink
!= NULL
? value("sender") : cp
;
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
);
183 smtp_auth_var(const char *type
, const char *addr
)
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
)
193 snprintf(var
, len
, "smtp-auth%s", type
);
194 if ((cp
= value(var
)) != NULL
)
201 static char *smtpbuf
;
202 static size_t smtpbufsize
;
205 * Get the SMTP server's answer, expecting value.
208 read_smtp(struct sock
*sp
, int value
, int ign_eof
)
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"));
220 if (verbose
|| debug
|| _debug
)
221 fputs(smtpbuf
, stderr
);
223 case '1': ret
= 1; break;
224 case '2': ret
= 2; break;
225 case '3': ret
= 3; break;
226 case '4': ret
= 4; break;
230 fprintf(stderr
, catgets(catd
, CATSET
, 191,
231 "smtp-server: %s"), smtpbuf
);
232 } while (smtpbuf
[3] == '-');
237 * Macros for talk_smtp.
239 #define _SMTP_ANSWER(x, ign_eof) \
240 if (!debug && !_debug) { \
242 if ((y = read_smtp(sp, x, ign_eof)) != (x) && \
243 (!(ign_eof) || y != -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) \
258 * Talk to a SMTP server.
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
)
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;
273 if ((authstr
= smtp_auth_var("", skinned
)) == NULL
)
274 auth
= user
&& password
? AUTH_LOGIN
: AUTH_NONE
;
275 else if (strcmp(authstr
, "plain") == 0)
277 else if (strcmp(authstr
, "login") == 0)
279 else if (strcmp(authstr
, "cram-md5") == 0) {
281 auth
= AUTH_CRAM_MD5
;
283 fprintf(stderr
, tr(277, "No CRAM-MD5 support compiled in.\n"));
287 fprintf(stderr
, tr(274,
288 "Unknown SMTP authentication method: %s\n"), authstr
);
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"));
299 if (value("smtp-use-starttls") ||
300 value("smtp-use-tls") /* v11.0 compatibility */) {
302 if ((cp
= strchr(xserver
, ':')) != NULL
) {
303 server
= salloc(cp
- xserver
+ 1);
304 memcpy(server
, xserver
, cp
- xserver
);
305 server
[cp
- xserver
] = '\0';
308 snprintf(o
, sizeof o
, "EHLO %s\r\n", nodename(1));
311 SMTP_OUT("STARTTLS\r\n");
313 if (!debug
&& !_debug
&& ssl_open(server
, sp
, uhp
) != OKAY
)
317 if (value("smtp-use-starttls") || value("smtp-use-tls")) {
318 fprintf(stderr
, tr(225, "No SSL support compiled in.\n"));
321 #endif /* !USE_SSL */
322 if (auth
!= AUTH_NONE
) {
323 snprintf(o
, sizeof o
, "EHLO %s\r\n", nodename(1));
329 SMTP_OUT("AUTH LOGIN\r\n");
331 b64
= strtob64(user
);
332 snprintf(o
, sizeof o
, "%s\r\n", b64
);
336 b64
= strtob64(password
);
337 snprintf(o
, sizeof o
, "%s\r\n", b64
);
343 SMTP_OUT("AUTH PLAIN\r\n");
345 snprintf(o
, sizeof o
, "%c%s%c%s", '\0', user
, '\0',
347 b64
= memtob64(o
, strlen(user
)+strlen(password
)+2);
348 snprintf(o
, sizeof o
, "%s\r\n", b64
);
354 SMTP_OUT("AUTH CRAM-MD5\r\n");
356 for (cp
= smtpbuf
; digitchar(*cp
&0377); cp
++);
357 while (blankchar(*cp
&0377)) cp
++;
358 cp
= cram_md5_string(user
, password
, cp
);
365 snprintf(o
, sizeof o
, "HELO %s\r\n", nodename(1));
369 snprintf(o
, sizeof o
, "MAIL FROM:<%s>\r\n", skinned
);
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",
380 SMTP_OUT("DATA\r\n");
385 while (fgetline(&b
, &bsize
, &count
, &blen
, fi
, 1) != NULL
) {
390 } else if (inbcc
&& blankchar(*b
& 0377))
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) {
406 swrite1(sp
, ".", 1, 1);
408 if (debug
|| _debug
) {
409 fprintf(stderr
, ">>> %s", b
);
414 swrite1(sp
, b
, blen
+1, 1);
418 SMTP_OUT("QUIT\r\n");
425 static sigjmp_buf smtpjmp
;
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
)
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
);
453 if (saveterm
!= SIG_IGN
)
454 safe_signal(SIGTERM
, onterm
);
455 if (strncmp(server
, "smtp://", 7) == 0) {
459 } else if (strncmp(server
, "smtps://", 8) == 0) {
465 if (!debug
&& !_debug
&& sopen(server
, &so
, use_ssl
, server
,
466 use_ssl
? "smtps" : "smtp", verbose
) != OKAY
) {
467 safe_signal(SIGTERM
, saveterm
);
471 ret
= talk_smtp(to
, fi
, &so
, server
, server
, hp
,
472 user
, password
, skinned
);
473 if (!debug
&& !_debug
)
480 safe_signal(SIGTERM
, saveterm
);
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
)
495 fputs(catgets(catd
, CATSET
, 194,
496 "No SMTP support compiled in.\n"), stderr
);
499 #endif /* USE_SMTP */