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
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>
63 static size_t smtpbufsize
;
64 static sigjmp_buf smtpjmp
;
66 static void onterm(int signo
);
67 static int read_smtp(struct sock
*sp
, int val
, int ign_eof
);
68 static int talk_smtp(struct name
*to
, FILE *fi
, struct sock
*sp
,
69 char *server
, char *uhp
, struct header
*hp
,
70 const char *user
, const char *password
,
77 siglongjmp(smtpjmp
, 1);
81 * Get the SMTP server's answer, expecting val.
84 read_smtp(struct sock
*sp
, int val
, int ign_eof
)
90 if ((len
= sgetline(&smtpbuf
, &smtpbufsize
, NULL
, sp
)) < 6) {
91 if (len
>= 0 && !ign_eof
)
92 fprintf(stderr
, tr(241,
93 "Unexpected EOF on SMTP connection\n"));
96 if (options
& OPT_VERBOSE
)
97 fputs(smtpbuf
, stderr
);
99 case '1': ret
= 1; break;
100 case '2': ret
= 2; break;
101 case '3': ret
= 3; break;
102 case '4': ret
= 4; break;
106 fprintf(stderr
, tr(191, "smtp-server: %s"), smtpbuf
);
107 } while (smtpbuf
[3] == '-');
112 * Macros for talk_smtp.
114 #define _SMTP_ANSWER(x, ign_eof) \
115 if ((options & OPT_DEBUG) == 0) { \
117 if ((y = read_smtp(sp, x, ign_eof)) != (x) && \
118 (!(ign_eof) || y != -1)) { \
125 #define SMTP_ANSWER(x) _SMTP_ANSWER(x, 0)
127 #define SMTP_OUT(x) if (options & OPT_VERBOSE) \
128 fprintf(stderr, ">>> %s", x); \
129 if ((options & OPT_DEBUG) == 0) \
133 * Talk to a SMTP server.
136 talk_smtp(struct name
*to
, FILE *fi
, struct sock
*sp
,
137 char *xserver
, char *uhp
, struct header
*hp
,
138 const char *user
, const char *password
, const char *skinned
)
140 char o
[LINESIZE
], *authstr
, *cp
, *b
= NULL
;
143 size_t blen
, cnt
, bsize
= 0;
144 enum { AUTH_NONE
, AUTH_PLAIN
, AUTH_LOGIN
, AUTH_CRAM_MD5
} auth
;
145 int inhdr
= 1, inbcc
= 0;
150 if ((authstr
= smtp_auth_var("", skinned
)) == NULL
)
151 auth
= user
&& password
? AUTH_LOGIN
: AUTH_NONE
;
152 else if (strcmp(authstr
, "plain") == 0)
154 else if (strcmp(authstr
, "login") == 0)
156 else if (strcmp(authstr
, "cram-md5") == 0) {
158 auth
= AUTH_CRAM_MD5
;
160 fprintf(stderr
, tr(277, "No CRAM-MD5 support compiled in.\n"));
164 fprintf(stderr
, tr(274,
165 "Unknown SMTP authentication method: %s\n"), authstr
);
168 if (auth
!= AUTH_NONE
&& (user
== NULL
|| password
== NULL
)) {
169 fprintf(stderr
, tr(275,
170 "User and password are necessary "
171 "for SMTP authentication.\n"));
176 if (! sp
->s_use_ssl
&& value("smtp-use-starttls")) {
178 if ((cp
= strchr(xserver
, ':')) != NULL
) {
179 server
= salloc(cp
- xserver
+ 1);
180 memcpy(server
, xserver
, cp
- xserver
);
181 server
[cp
- xserver
] = '\0';
184 snprintf(o
, sizeof o
, "EHLO %s\r\n", nodename(1));
187 SMTP_OUT("STARTTLS\r\n");
189 if ((options
& OPT_DEBUG
) == 0 &&
190 ssl_open(server
, sp
, uhp
) != OKAY
)
194 if (value("smtp-use-starttls")) {
195 fprintf(stderr
, tr(225, "No SSL support compiled in.\n"));
199 if (auth
!= AUTH_NONE
) {
200 snprintf(o
, sizeof o
, "EHLO %s\r\n", nodename(1));
209 * Won't happen, but gcc(1) and clang(1) whine without
210 * and Coverity whines with; that's a hard one.. */
212 SMTP_OUT("AUTH LOGIN\r\n");
214 (void)b64_encode_cp(&b64
, user
, B64_SALLOC
|B64_CRLF
);
217 (void)b64_encode_cp(&b64
, password
,
218 B64_SALLOC
|B64_CRLF
);
223 SMTP_OUT("AUTH PLAIN\r\n");
225 (void)snprintf(o
, sizeof o
, "%c%s%c%s",
226 '\0', user
, '\0', password
);
227 (void)b64_encode_buf(&b64
, o
, strlen(user
) +
228 strlen(password
) + 2, B64_SALLOC
|B64_CRLF
);
234 SMTP_OUT("AUTH CRAM-MD5\r\n");
236 for (cp
= smtpbuf
; digitchar(*cp
); ++cp
)
238 while (blankchar(*cp
))
240 cp
= cram_md5_string(user
, password
, cp
);
247 snprintf(o
, sizeof o
, "HELO %s\r\n", nodename(1));
251 snprintf(o
, sizeof o
, "MAIL FROM:<%s>\r\n", skinned
);
254 for (n
= to
; n
!= NULL
; n
= n
->n_flink
) {
255 if ((n
->n_type
& GDEL
) == 0) {
256 snprintf(o
, sizeof o
, "RCPT TO:<%s>\r\n",
262 SMTP_OUT("DATA\r\n");
267 while (fgetline(&b
, &bsize
, &cnt
, &blen
, fi
, 1) != NULL
) {
272 } else if (inbcc
&& blankchar(*b
& 0377))
275 * We know what we have generated first, so
276 * do not look for whitespace before the ':'.
278 else if (ascncasecmp(b
, "bcc: ", 5) == 0) {
285 if (options
& OPT_DEBUG
)
288 swrite1(sp
, ".", 1, 1);
290 if (options
& OPT_DEBUG
) {
291 fprintf(stderr
, ">>> %s", b
);
296 swrite1(sp
, b
, blen
+1, 1);
300 SMTP_OUT("QUIT\r\n");
308 smtp_auth_var(char const *atype
, char const *addr
)
315 len
= tl
+ al
+ 10 + 1;
318 /* Try a 'user@host', i.e., address specific version first */
319 (void)snprintf(var
, len
, "smtp-auth%s-%s", atype
, addr
);
320 if ((cp
= value(var
)) == NULL
) {
321 snprintf(var
, len
, "smtp-auth%s", atype
);
332 * Connect to a SMTP server.
335 smtp_mta(char *volatile server
, struct name
*volatile to
, FILE *fi
,
336 struct header
*hp
, const char *user
, const char *password
,
341 sighandler_type
volatile saveterm
;
343 memset(&so
, 0, sizeof so
);
344 saveterm
= safe_signal(SIGTERM
, SIG_IGN
);
345 if (sigsetjmp(smtpjmp
, 1)) {
346 safe_signal(SIGTERM
, saveterm
);
349 if (saveterm
!= SIG_IGN
)
350 safe_signal(SIGTERM
, onterm
);
351 if (strncmp(server
, "smtp://", 7) == 0) {
355 } else if (strncmp(server
, "smtps://", 8) == 0) {
361 if ((options
& OPT_DEBUG
) == 0 && sopen(server
, &so
, use_ssl
, server
,
362 use_ssl
? "smtps" : "smtp",
363 (options
& OPT_VERBOSE
) != 0) != OKAY
) {
364 safe_signal(SIGTERM
, saveterm
);
368 ret
= talk_smtp(to
, fi
, &so
, server
, server
, hp
,
369 user
, password
, skinned
);
370 if ((options
& OPT_DEBUG
) == 0)
377 safe_signal(SIGTERM
, saveterm
);
380 #endif /* HAVE_SMTP */