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
;
50 # include <sys/socket.h>
52 # include <netinet/in.h>
53 # ifdef HAVE_ARPA_INET_H
54 # 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
, catgets(catd
, CATSET
, 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
, catgets(catd
, CATSET
, 191,
108 "smtp-server: %s"), smtpbuf
);
109 } while (smtpbuf
[3] == '-');
114 * Macros for talk_smtp.
116 #define _SMTP_ANSWER(x, ign_eof) \
117 if ((options & OPT_DEBUG) == 0) { \
119 if ((y = read_smtp(sp, x, ign_eof)) != (x) && \
120 (!(ign_eof) || y != -1)) { \
127 #define SMTP_ANSWER(x) _SMTP_ANSWER(x, 0)
129 #define SMTP_OUT(x) if (options & OPT_VERBOSE) \
130 fprintf(stderr, ">>> %s", x); \
131 if ((options & OPT_DEBUG) == 0) \
135 * Talk to a SMTP server.
138 talk_smtp(struct name
*to
, FILE *fi
, struct sock
*sp
,
139 char *xserver
, char *uhp
, struct header
*hp
,
140 const char *user
, const char *password
, const char *skinned
)
142 char o
[LINESIZE
], *authstr
, *cp
, *b
= NULL
;
145 size_t blen
, cnt
, bsize
= 0;
146 enum { AUTH_NONE
, AUTH_PLAIN
, AUTH_LOGIN
, AUTH_CRAM_MD5
} auth
;
147 int inhdr
= 1, inbcc
= 0;
152 if ((authstr
= smtp_auth_var("", skinned
)) == NULL
)
153 auth
= user
&& password
? AUTH_LOGIN
: AUTH_NONE
;
154 else if (strcmp(authstr
, "plain") == 0)
156 else if (strcmp(authstr
, "login") == 0)
158 else if (strcmp(authstr
, "cram-md5") == 0) {
160 auth
= AUTH_CRAM_MD5
;
162 fprintf(stderr
, tr(277, "No CRAM-MD5 support compiled in.\n"));
166 fprintf(stderr
, tr(274,
167 "Unknown SMTP authentication method: %s\n"), authstr
);
170 if (auth
!= AUTH_NONE
&& (user
== NULL
|| password
== NULL
)) {
171 fprintf(stderr
, tr(275,
172 "User and password are necessary "
173 "for SMTP authentication.\n"));
178 if (! sp
->s_use_ssl
&& value("smtp-use-starttls")) {
180 if ((cp
= strchr(xserver
, ':')) != NULL
) {
181 server
= salloc(cp
- xserver
+ 1);
182 memcpy(server
, xserver
, cp
- xserver
);
183 server
[cp
- xserver
] = '\0';
186 snprintf(o
, sizeof o
, "EHLO %s\r\n", nodename(1));
189 SMTP_OUT("STARTTLS\r\n");
191 if ((options
& OPT_DEBUG
) == 0 &&
192 ssl_open(server
, sp
, uhp
) != OKAY
)
196 if (value("smtp-use-starttls")) {
197 fprintf(stderr
, tr(225, "No SSL support compiled in.\n"));
201 if (auth
!= AUTH_NONE
) {
202 snprintf(o
, sizeof o
, "EHLO %s\r\n", nodename(1));
211 * Won't happen, but gcc(1) and clang(1) whine without
212 * and Coverity whines with; that's a hard one.. */
214 SMTP_OUT("AUTH LOGIN\r\n");
216 (void)b64_encode_cp(&b64
, user
, B64_SALLOC
|B64_CRLF
);
219 (void)b64_encode_cp(&b64
, password
,
220 B64_SALLOC
|B64_CRLF
);
225 SMTP_OUT("AUTH PLAIN\r\n");
227 (void)snprintf(o
, sizeof o
, "%c%s%c%s",
228 '\0', user
, '\0', password
);
229 (void)b64_encode_buf(&b64
, o
, strlen(user
) +
230 strlen(password
) + 2, B64_SALLOC
|B64_CRLF
);
236 SMTP_OUT("AUTH CRAM-MD5\r\n");
238 for (cp
= smtpbuf
; digitchar(*cp
); ++cp
)
240 while (blankchar(*cp
))
242 cp
= cram_md5_string(user
, password
, cp
);
249 snprintf(o
, sizeof o
, "HELO %s\r\n", nodename(1));
253 snprintf(o
, sizeof o
, "MAIL FROM:<%s>\r\n", skinned
);
256 for (n
= to
; n
!= NULL
; n
= n
->n_flink
) {
257 if ((n
->n_type
& GDEL
) == 0) {
258 snprintf(o
, sizeof o
, "RCPT TO:<%s>\r\n",
264 SMTP_OUT("DATA\r\n");
269 while (fgetline(&b
, &bsize
, &cnt
, &blen
, fi
, 1) != NULL
) {
274 } else if (inbcc
&& blankchar(*b
& 0377))
277 * We know what we have generated first, so
278 * do not look for whitespace before the ':'.
280 else if (ascncasecmp(b
, "bcc: ", 5) == 0) {
287 if (options
& OPT_DEBUG
)
290 swrite1(sp
, ".", 1, 1);
292 if (options
& OPT_DEBUG
) {
293 fprintf(stderr
, ">>> %s", b
);
298 swrite1(sp
, b
, blen
+1, 1);
302 SMTP_OUT("QUIT\r\n");
310 smtp_auth_var(char const *atype
, char const *addr
)
317 len
= tl
+ al
+ 10 + 1;
320 /* Try a 'user@host', i.e., address specific version first */
321 (void)snprintf(var
, len
, "smtp-auth%s-%s", atype
, addr
);
322 if ((cp
= value(var
)) == NULL
) {
323 snprintf(var
, len
, "smtp-auth%s", atype
);
334 * Connect to a SMTP server.
337 smtp_mta(char *volatile server
, struct name
*volatile to
, FILE *fi
,
338 struct header
*hp
, const char *user
, const char *password
,
343 sighandler_type saveterm
;
345 memset(&so
, 0, sizeof so
);
346 saveterm
= safe_signal(SIGTERM
, SIG_IGN
);
347 if (sigsetjmp(smtpjmp
, 1)) {
348 safe_signal(SIGTERM
, saveterm
);
351 if (saveterm
!= SIG_IGN
)
352 safe_signal(SIGTERM
, onterm
);
353 if (strncmp(server
, "smtp://", 7) == 0) {
357 } else if (strncmp(server
, "smtps://", 8) == 0) {
363 if ((options
& OPT_DEBUG
) == 0 && sopen(server
, &so
, use_ssl
, server
,
364 use_ssl
? "smtps" : "smtp",
365 (options
& OPT_VERBOSE
) != 0) != OKAY
) {
366 safe_signal(SIGTERM
, saveterm
);
370 ret
= talk_smtp(to
, fi
, &so
, server
, server
, hp
,
371 user
, password
, skinned
);
372 if ((options
& OPT_DEBUG
) == 0)
379 safe_signal(SIGTERM
, saveterm
);
382 #endif /* HAVE_SMTP */