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 - 2013 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
43 typedef int avoid_empty_file_compiler_warning
;
48 # include <sys/socket.h>
52 # include <netinet/in.h>
54 # ifdef HAVE_ARPA_INET_H
55 # include <arpa/inet.h>
64 static size_t smtpbufsize
;
65 static sigjmp_buf smtpjmp
;
67 static void onterm(int signo
);
68 static int read_smtp(struct sock
*sp
, int val
, int ign_eof
);
69 static int talk_smtp(struct name
*to
, FILE *fi
, struct sock
*sp
,
70 char *server
, char *uhp
, struct header
*hp
,
71 const char *user
, const char *password
,
78 siglongjmp(smtpjmp
, 1);
82 * Get the SMTP server's answer, expecting val.
85 read_smtp(struct sock
*sp
, int val
, int ign_eof
)
91 if ((len
= sgetline(&smtpbuf
, &smtpbufsize
, NULL
, sp
)) < 6) {
92 if (len
>= 0 && !ign_eof
)
93 fprintf(stderr
, tr(241,
94 "Unexpected EOF on SMTP connection\n"));
97 if (options
& OPT_VERBOSE
)
98 fputs(smtpbuf
, stderr
);
100 case '1': ret
= 1; break;
101 case '2': ret
= 2; break;
102 case '3': ret
= 3; break;
103 case '4': ret
= 4; break;
107 fprintf(stderr
, tr(191, "smtp-server: %s"), smtpbuf
);
108 } while (smtpbuf
[3] == '-');
113 * Macros for talk_smtp.
115 #define _SMTP_ANSWER(x, ign_eof) \
116 if ((options & OPT_DEBUG) == 0) { \
118 if ((y = read_smtp(sp, x, ign_eof)) != (x) && \
119 (!(ign_eof) || y != -1)) { \
126 #define SMTP_ANSWER(x) _SMTP_ANSWER(x, 0)
128 #define SMTP_OUT(x) if (options & OPT_VERBOSE) \
129 fprintf(stderr, ">>> %s", x); \
130 if ((options & OPT_DEBUG) == 0) \
134 * Talk to a SMTP server.
137 talk_smtp(struct name
*to
, FILE *fi
, struct sock
*sp
,
138 char *xserver
, char *uhp
, struct header
*hp
,
139 const char *user
, const char *password
, const char *skinned
)
141 char o
[LINESIZE
], *authstr
, *cp
, *b
= NULL
;
144 size_t blen
, cnt
, bsize
= 0;
145 enum { AUTH_NONE
, AUTH_PLAIN
, AUTH_LOGIN
, AUTH_CRAM_MD5
} auth
;
146 int inhdr
= 1, inbcc
= 0;
151 if ((authstr
= smtp_auth_var("", skinned
)) == NULL
)
152 auth
= user
&& password
? AUTH_LOGIN
: AUTH_NONE
;
153 else if (strcmp(authstr
, "plain") == 0)
155 else if (strcmp(authstr
, "login") == 0)
157 else if (strcmp(authstr
, "cram-md5") == 0) {
159 auth
= AUTH_CRAM_MD5
;
161 fprintf(stderr
, tr(277, "No CRAM-MD5 support compiled in.\n"));
165 fprintf(stderr
, tr(274,
166 "Unknown SMTP authentication method: %s\n"), authstr
);
169 if (auth
!= AUTH_NONE
&& (user
== NULL
|| password
== NULL
)) {
170 fprintf(stderr
, tr(275,
171 "User and password are necessary "
172 "for SMTP authentication.\n"));
177 if (! sp
->s_use_ssl
&& value("smtp-use-starttls")) {
179 if ((cp
= strchr(xserver
, ':')) != NULL
) {
180 server
= salloc(cp
- xserver
+ 1);
181 memcpy(server
, xserver
, cp
- xserver
);
182 server
[cp
- xserver
] = '\0';
185 snprintf(o
, sizeof o
, "EHLO %s\r\n", nodename(1));
188 SMTP_OUT("STARTTLS\r\n");
190 if ((options
& OPT_DEBUG
) == 0 &&
191 ssl_open(server
, sp
, uhp
) != OKAY
)
195 if (value("smtp-use-starttls")) {
196 fprintf(stderr
, tr(225, "No SSL support compiled in.\n"));
200 if (auth
!= AUTH_NONE
) {
201 snprintf(o
, sizeof o
, "EHLO %s\r\n", nodename(1));
210 * Won't happen, but gcc(1) and clang(1) whine without
211 * and Coverity whines with; that's a hard one.. */
213 SMTP_OUT("AUTH LOGIN\r\n");
215 (void)b64_encode_cp(&b64
, user
, B64_SALLOC
|B64_CRLF
);
218 (void)b64_encode_cp(&b64
, password
,
219 B64_SALLOC
|B64_CRLF
);
224 SMTP_OUT("AUTH PLAIN\r\n");
226 (void)snprintf(o
, sizeof o
, "%c%s%c%s",
227 '\0', user
, '\0', password
);
228 (void)b64_encode_buf(&b64
, o
, strlen(user
) +
229 strlen(password
) + 2, B64_SALLOC
|B64_CRLF
);
235 SMTP_OUT("AUTH CRAM-MD5\r\n");
237 for (cp
= smtpbuf
; digitchar(*cp
); ++cp
)
239 while (blankchar(*cp
))
241 cp
= cram_md5_string(user
, password
, cp
);
248 snprintf(o
, sizeof o
, "HELO %s\r\n", nodename(1));
252 snprintf(o
, sizeof o
, "MAIL FROM:<%s>\r\n", skinned
);
255 for (n
= to
; n
!= NULL
; n
= n
->n_flink
) {
256 if ((n
->n_type
& GDEL
) == 0) {
257 snprintf(o
, sizeof o
, "RCPT TO:<%s>\r\n",
263 SMTP_OUT("DATA\r\n");
268 while (fgetline(&b
, &bsize
, &cnt
, &blen
, fi
, 1) != NULL
) {
273 } else if (inbcc
&& blankchar(*b
& 0377))
276 * We know what we have generated first, so
277 * do not look for whitespace before the ':'.
279 else if (ascncasecmp(b
, "bcc: ", 5) == 0) {
286 if (options
& OPT_DEBUG
)
289 swrite1(sp
, ".", 1, 1);
291 if (options
& OPT_DEBUG
) {
292 fprintf(stderr
, ">>> %s", b
);
297 swrite1(sp
, b
, blen
+1, 1);
301 SMTP_OUT("QUIT\r\n");
309 smtp_auth_var(char const *atype
, char const *addr
)
316 len
= tl
+ al
+ 10 + 1;
319 /* Try a 'user@host', i.e., address specific version first */
320 (void)snprintf(var
, len
, "smtp-auth%s-%s", atype
, addr
);
321 if ((cp
= value(var
)) == NULL
) {
322 snprintf(var
, len
, "smtp-auth%s", atype
);
333 * Connect to a SMTP server.
336 smtp_mta(char *volatile server
, struct name
*volatile to
, FILE *fi
,
337 struct header
*hp
, const char *user
, const char *password
,
342 sighandler_type saveterm
;
344 memset(&so
, 0, sizeof so
);
345 saveterm
= safe_signal(SIGTERM
, SIG_IGN
);
346 if (sigsetjmp(smtpjmp
, 1)) {
347 safe_signal(SIGTERM
, saveterm
);
350 if (saveterm
!= SIG_IGN
)
351 safe_signal(SIGTERM
, onterm
);
352 if (strncmp(server
, "smtp://", 7) == 0) {
356 } else if (strncmp(server
, "smtps://", 8) == 0) {
362 if ((options
& OPT_DEBUG
) == 0 && sopen(server
, &so
, use_ssl
, server
,
363 use_ssl
? "smtps" : "smtp",
364 (options
& OPT_VERBOSE
) != 0) != OKAY
) {
365 safe_signal(SIGTERM
, saveterm
);
369 ret
= talk_smtp(to
, fi
, &so
, server
, server
, hp
,
370 user
, password
, skinned
);
371 if ((options
& OPT_DEBUG
) == 0)
378 safe_signal(SIGTERM
, saveterm
);
381 #endif /* HAVE_SMTP */