1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
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>
63 static sigjmp_buf _smtp_jmp
;
65 static void _smtp_onterm(int signo
);
67 /* Get the SMTP server's answer, expecting val */
68 static int _smtp_read(struct sock
*sp
, char **buf
, size_t *bufsize
, int val
,
71 /* Talk to a SMTP server */
72 static int _smtp_talk(struct name
*to
, FILE *fi
, struct sock
*sp
,
73 char *server
, char *uhp
, struct header
*hp
, char const *user
,
74 char const *password
, char const *skinned
);
77 _smtp_onterm(int signo
)
79 NYD_X
; /* Signal handler */
81 siglongjmp(_smtp_jmp
, 1);
85 _smtp_read(struct sock
*sp
, char **buf
, size_t *bufsize
, int val
, int ign_eof
)
91 if ((len
= sgetline(buf
, bufsize
, NULL
, sp
)) < 6) {
92 if (len
>= 0 && !ign_eof
)
93 fprintf(stderr
, tr(241, "Unexpected EOF on SMTP connection\n"));
97 if (options
& OPT_VERBOSE
)
100 case '1': rv
= 1; break;
101 case '2': rv
= 2; break;
102 case '3': rv
= 3; break;
103 case '4': rv
= 4; break;
104 default: rv
= 5; break;
107 fprintf(stderr
, tr(191, "smtp-server: %s"), *buf
);
108 } while ((*buf
)[3] == '-');
115 _smtp_talk(struct name
*to
, FILE *fi
, struct sock
*sp
, char *xserver
,
116 char *uhp
, struct header
*hp
, char const *user
, char const *password
,
119 #define _ANSWER(X, IGNEOF) \
120 do if (!(options & OPT_DEBUG)) {\
122 if ((y = _smtp_read(sp, &buf, &bsize, X, IGNEOF)) != (X) &&\
123 (!(IGNEOF) || y != -1))\
128 if (options & OPT_VERBOSE)\
129 fprintf(stderr, ">>> %s", X);\
130 if (!(options & OPT_DEBUG))\
134 char o
[LINESIZE
], *authstr
, *cp
, *buf
= NULL
;
137 size_t blen
, cnt
, bsize
= 0;
138 enum {AUTH_NONE
, AUTH_PLAIN
, AUTH_LOGIN
, AUTH_CRAM_MD5
} auth
;
139 int inhdr
= 1, inbcc
= 0, rv
= 1;
145 if ((authstr
= smtp_auth_var("", skinned
)) == NULL
)
146 auth
= (user
!= NULL
&& password
!= NULL
) ? AUTH_LOGIN
: AUTH_NONE
;
147 else if (!strcmp(authstr
, "plain"))
149 else if (!strcmp(authstr
, "login"))
151 else if (!strcmp(authstr
, "cram-md5")) {
153 auth
= AUTH_CRAM_MD5
;
155 fprintf(stderr
, tr(277, "No CRAM-MD5 support compiled in.\n"));
159 fprintf(stderr
, tr(274, "Unknown SMTP authentication method: %s\n"),
164 if (auth
!= AUTH_NONE
&& (user
== NULL
|| password
== NULL
)) {
165 fprintf(stderr
, tr(275,
166 "User and password are necessary for SMTP authentication.\n"));
172 if (!sp
->s_use_ssl
&& ok_blook(smtp_use_starttls
)) {
175 if ((cp
= strchr(xserver
, ':')) != NULL
)
176 server
= savestrbuf(xserver
, PTR2SIZE(cp
- xserver
));
179 snprintf(o
, sizeof o
, LINE("EHLO %s"), nodename(1));
183 _OUT(LINE("STARTTLS"));
186 if (!(options
& OPT_DEBUG
) && ssl_open(server
, sp
, uhp
) != OKAY
)
190 if (ok_blook(smtp_use_starttls
)) {
191 fprintf(stderr
, tr(225, "No SSL support compiled in.\n"));
196 if (auth
!= AUTH_NONE
) {
197 snprintf(o
, sizeof o
, LINE("EHLO %s"), nodename(1));
207 * XXX Won't happen, but gcc(1) and clang(1) whine without
208 * XXX and Coverity whines with; that's a hard one.. */
210 _OUT(LINE("AUTH LOGIN"));
213 b64_encode_cp(&b64
, user
, B64_SALLOC
| B64_CRLF
);
217 b64_encode_cp(&b64
, password
, B64_SALLOC
| B64_CRLF
);
222 _OUT(LINE("AUTH PLAIN"));
225 snprintf(o
, sizeof o
, "%c%s%c%s", '\0', user
, '\0', password
);
226 b64_encode_buf(&b64
, o
, strlen(user
) + strlen(password
) + 2,
227 B64_SALLOC
| B64_CRLF
);
233 _OUT(LINE("AUTH CRAM-MD5"));
236 for (cp
= buf
; digitchar(*cp
); ++cp
)
238 while (blankchar(*cp
))
240 cp
= cram_md5_string(user
, password
, cp
);
247 snprintf(o
, sizeof o
, LINE("HELO %s"), nodename(1));
252 snprintf(o
, sizeof o
, LINE("MAIL FROM:<%s>"), skinned
);
256 for (n
= to
; n
!= NULL
; n
= n
->n_flink
) {
257 if (!(n
->n_type
& GDEL
)) {
258 snprintf(o
, sizeof o
, LINE("RCPT TO:<%s>"), skinned_name(n
));
269 while (fgetline(&buf
, &bsize
, &cnt
, &blen
, fi
, 1) != NULL
) {
274 } else if (inbcc
&& blankchar(*buf
))
276 /* We know what we have generated first, so do not look for whitespace
278 else if (!ascncasecmp(buf
, "bcc: ", 5)) {
286 if (options
& OPT_DEBUG
)
289 swrite1(sp
, ".", 1, 1);
291 if (options
& OPT_DEBUG
) {
292 fprintf(stderr
, ">>> %s", buf
);
295 buf
[blen
- 1] = NL
[0];
297 swrite1(sp
, buf
, blen
+ 1, 1);
316 smtp_auth_var(char const *atype
, char const *addr
) /* FIXME GENERIC */
324 len
= tl
+ al
+ 10 +1;
327 /* Try a 'user@host', i.e., address specific version first */
328 snprintf(var
, len
, "smtp-auth%s-%s", atype
, addr
);
329 if ((cp
= vok_vlook(var
)) == NULL
) {
330 snprintf(var
, len
, "smtp-auth%s", atype
);
342 smtp_mta(char *volatile server
, struct name
*volatile to
, FILE *fi
,
343 struct header
*hp
, char const *user
, char const *password
,
347 sighandler_type
volatile saveterm
;
351 memset(&so
, 0, sizeof so
);
353 saveterm
= safe_signal(SIGTERM
, SIG_IGN
);
354 if (sigsetjmp(_smtp_jmp
, 1))
356 if (saveterm
!= SIG_IGN
)
357 safe_signal(SIGTERM
, &_smtp_onterm
);
359 if (!strncmp(server
, "smtp://", 7)) {
363 } else if (!strncmp(server
, "smtps://", 8)) {
370 if (!(options
& OPT_DEBUG
) && sopen(server
, &so
, use_ssl
, server
,
371 (use_ssl
? "smtps" : "smtp")) != OKAY
)
375 rv
= _smtp_talk(to
, fi
, &so
, server
, server
, hp
, user
, password
, skinned
);
377 if (!(options
& OPT_DEBUG
))
380 safe_signal(SIGTERM
, saveterm
);
387 #endif /* HAVE_SMTP */
389 /* vim:set fenc=utf-8:s-it-mode */