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>.
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
40 #ifndef HAVE_AMALGAMATION
47 # include <sys/socket.h>
51 # include <netinet/in.h>
53 # ifdef HAVE_ARPA_INET_H
54 # include <arpa/inet.h>
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
,
73 siglongjmp(smtpjmp
, 1);
77 * Get the SMTP server's answer, expecting val.
80 read_smtp(struct sock
*sp
, int val
, int ign_eof
)
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"));
92 if (options
& OPT_VERBOSE
)
93 fputs(smtpbuf
, stderr
);
95 case '1': ret
= 1; break;
96 case '2': ret
= 2; break;
97 case '3': ret
= 3; break;
98 case '4': ret
= 4; break;
102 fprintf(stderr
, tr(191, "smtp-server: %s"), smtpbuf
);
103 } while (smtpbuf
[3] == '-');
108 * Macros for talk_smtp.
110 #define _SMTP_ANSWER(x, ign_eof) \
111 if ((options & OPT_DEBUG) == 0) { \
113 if ((y = read_smtp(sp, x, ign_eof)) != (x) && \
114 (!(ign_eof) || y != -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) \
129 * Talk to a SMTP server.
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
;
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;
146 if ((authstr
= smtp_auth_var("", skinned
)) == NULL
)
147 auth
= user
&& password
? AUTH_LOGIN
: AUTH_NONE
;
148 else if (strcmp(authstr
, "plain") == 0)
150 else if (strcmp(authstr
, "login") == 0)
152 else if (strcmp(authstr
, "cram-md5") == 0) {
154 auth
= AUTH_CRAM_MD5
;
156 fprintf(stderr
, tr(277, "No CRAM-MD5 support compiled in.\n"));
160 fprintf(stderr
, tr(274,
161 "Unknown SMTP authentication method: %s\n"), authstr
);
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"));
172 if (!sp
->s_use_ssl
&& ok_blook(smtp_use_starttls
)) {
174 if ((cp
= strchr(xserver
, ':')) != NULL
) {
175 server
= salloc(cp
- xserver
+ 1);
176 memcpy(server
, xserver
, cp
- xserver
);
177 server
[cp
- xserver
] = '\0';
180 snprintf(o
, sizeof o
, "EHLO %s\r\n", nodename(1));
183 SMTP_OUT("STARTTLS\r\n");
185 if ((options
& OPT_DEBUG
) == 0 &&
186 ssl_open(server
, sp
, uhp
) != OKAY
)
190 if (ok_blook(smtp_use_starttls
)) {
191 fprintf(stderr
, tr(225, "No SSL support compiled in.\n"));
195 if (auth
!= AUTH_NONE
) {
196 snprintf(o
, sizeof o
, "EHLO %s\r\n", nodename(1));
205 * Won't happen, but gcc(1) and clang(1) whine without
206 * and Coverity whines with; that's a hard one.. */
208 SMTP_OUT("AUTH LOGIN\r\n");
210 (void)b64_encode_cp(&b64
, user
, B64_SALLOC
|B64_CRLF
);
213 (void)b64_encode_cp(&b64
, password
,
214 B64_SALLOC
|B64_CRLF
);
219 SMTP_OUT("AUTH PLAIN\r\n");
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
);
230 SMTP_OUT("AUTH CRAM-MD5\r\n");
232 for (cp
= smtpbuf
; digitchar(*cp
); ++cp
)
234 while (blankchar(*cp
))
236 cp
= cram_md5_string(user
, password
, cp
);
243 snprintf(o
, sizeof o
, "HELO %s\r\n", nodename(1));
247 snprintf(o
, sizeof o
, "MAIL FROM:<%s>\r\n", skinned
);
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",
258 SMTP_OUT("DATA\r\n");
263 while (fgetline(&b
, &bsize
, &cnt
, &blen
, fi
, 1) != NULL
) {
268 } else if (inbcc
&& blankchar(*b
& 0377))
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) {
281 if (options
& OPT_DEBUG
)
284 swrite1(sp
, ".", 1, 1);
286 if (options
& OPT_DEBUG
) {
287 fprintf(stderr
, ">>> %s", b
);
292 swrite1(sp
, b
, blen
+1, 1);
296 SMTP_OUT("QUIT\r\n");
304 smtp_auth_var(char const *atype
, char const *addr
)
311 len
= tl
+ al
+ 10 + 1;
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
);
328 * Connect to a SMTP server.
331 smtp_mta(char *volatile server
, struct name
*volatile to
, FILE *fi
,
332 struct header
*hp
, const char *user
, const char *password
,
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
);
345 if (saveterm
!= SIG_IGN
)
346 safe_signal(SIGTERM
, onterm
);
347 if (strncmp(server
, "smtp://", 7) == 0) {
351 } else if (strncmp(server
, "smtps://", 8) == 0) {
357 if ((options
& OPT_DEBUG
) == 0 && sopen(server
, &so
, use_ssl
, server
,
358 (use_ssl
? "smtps" : "smtp")) != OKAY
) {
359 safe_signal(SIGTERM
, saveterm
);
363 ret
= talk_smtp(to
, fi
, &so
, server
, server
, hp
,
364 user
, password
, skinned
);
365 if ((options
& OPT_DEBUG
) == 0)
372 safe_signal(SIGTERM
, saveterm
);
375 #endif /* HAVE_SMTP */