1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
3 *@ TODO - use initial responses to save a round-trip (RFC 4954)
4 *@ TODO - more (verbose) understanding+rection upon STATUS CODES
6 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
7 * Copyright (c) 2012 - 2015 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
11 * Gunnar Ritter. All rights reserved.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgement:
23 * This product includes software developed by Gunnar Ritter
24 * and his contributors.
25 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44 #ifndef HAVE_AMALGAMATION
50 #include <sys/socket.h>
53 char *dat
; /* Actual data */
55 char *buf
; /* Memory buffer */
59 static sigjmp_buf _smtp_jmp
;
61 static void _smtp_onterm(int signo
);
63 /* Get the SMTP server's answer, expecting val */
64 static int _smtp_read(struct sock
*sp
, struct smtp_line
*slp
, int val
,
65 bool_t ign_eof
, bool_t want_dat
);
67 /* Talk to a SMTP server */
68 static bool_t
_smtp_talk(struct sock
*sp
, struct sendbundle
*sbp
);
71 static bool_t
_smtp_gssapi(struct sock
*sp
, struct sendbundle
*sbp
,
72 struct smtp_line
*slp
);
76 _smtp_onterm(int signo
)
78 NYD_X
; /* Signal handler */
80 siglongjmp(_smtp_jmp
, 1);
84 _smtp_read(struct sock
*sp
, struct smtp_line
*slp
, int val
,
85 bool_t ign_eof
, bool_t want_dat
)
92 if ((len
= sgetline(&slp
->buf
, &slp
->bufsize
, NULL
, sp
)) < 6) {
93 if (len
>= 0 && !ign_eof
)
94 n_err(_("Unexpected EOF on SMTP connection\n"));
98 if (options
& OPT_VERBVERB
)
100 switch (slp
->buf
[0]) {
101 case '1': rv
= 1; break;
102 case '2': rv
= 2; break;
103 case '3': rv
= 3; break;
104 case '4': rv
= 4; break;
105 default: rv
= 5; break;
108 n_err(_("smtp-server: %s"), slp
->buf
);
109 } while (slp
->buf
[3] == '-');
112 for (cp
= slp
->buf
; digitchar(*cp
); --len
, ++cp
)
114 for (; blankchar(*cp
); --len
, ++cp
)
119 cp
[slp
->datlen
= (size_t)len
] = '\0';
126 /* Indirect SMTP I/O */
127 #define _ANSWER(X, IGNEOF, WANTDAT) \
128 do if (!(options & OPT_DEBUG)) {\
130 if ((y = _smtp_read(sp, slp, X, IGNEOF, WANTDAT)) != (X) &&\
131 (!(IGNEOF) || y != -1))\
136 if (options & OPT_VERBVERB)\
138 if (!(options & OPT_DEBUG))\
143 _smtp_talk(struct sock
*sp
, struct sendbundle
*sbp
)
145 char o
[LINESIZE
], *hostname
;
146 struct smtp_line _sl
, *slp
= &_sl
;
150 bool_t inhdr
= TRU1
, inbcc
= FAL0
, rv
= FAL0
;
153 hostname
= nodename(TRU1
);
158 _ANSWER(2, FAL0
, FAL0
);
161 if (!sp
->s_use_ssl
&& xok_blook(smtp_use_starttls
, &sbp
->sb_url
, OXM_ALL
)) {
162 snprintf(o
, sizeof o
, NETLINE("EHLO %s"), hostname
);
164 _ANSWER(2, FAL0
, FAL0
);
166 _OUT(NETLINE("STARTTLS"));
167 _ANSWER(2, FAL0
, FAL0
);
169 if (!(options
& OPT_DEBUG
) && ssl_open(&sbp
->sb_url
, sp
) != OKAY
)
173 if (xok_blook(smtp_use_starttls
, &sbp
->sb_url
, OXM_ALL
)) {
174 n_err(_("No SSL support compiled in\n"));
179 /* Shorthand: no authentication, plain HELO? */
180 if (sbp
->sb_ccred
.cc_authtype
== AUTHTYPE_NONE
) {
181 snprintf(o
, sizeof o
, NETLINE("HELO %s"), hostname
);
183 _ANSWER(2, FAL0
, FAL0
);
187 /* We'll have to deal with authentication */
188 snprintf(o
, sizeof o
, NETLINE("EHLO %s"), hostname
);
190 _ANSWER(2, FAL0
, FAL0
);
192 switch (sbp
->sb_ccred
.cc_authtype
) {
194 /* FALLTHRU (doesn't happen) */
196 _OUT(NETLINE("AUTH PLAIN"));
197 _ANSWER(3, FAL0
, FAL0
);
199 snprintf(o
, sizeof o
, "%c%s%c%s",
200 '\0', sbp
->sb_ccred
.cc_user
.s
, '\0', sbp
->sb_ccred
.cc_pass
.s
);
201 b64_encode_buf(&b64
, o
,
202 sbp
->sb_ccred
.cc_user
.l
+ sbp
->sb_ccred
.cc_pass
.l
+ 2,
203 B64_SALLOC
| B64_CRLF
);
205 _ANSWER(2, FAL0
, FAL0
);
208 _OUT(NETLINE("AUTH LOGIN"));
209 _ANSWER(3, FAL0
, FAL0
);
211 b64_encode_cp(&b64
, sbp
->sb_ccred
.cc_user
.s
, B64_SALLOC
| B64_CRLF
);
213 _ANSWER(3, FAL0
, FAL0
);
215 b64_encode_cp(&b64
, sbp
->sb_ccred
.cc_pass
.s
, B64_SALLOC
| B64_CRLF
);
217 _ANSWER(2, FAL0
, FAL0
);
220 case AUTHTYPE_CRAM_MD5
:
221 _OUT(NETLINE("AUTH CRAM-MD5"));
222 _ANSWER(3, FAL0
, TRU1
);
223 { char *cp
= cram_md5_string(&sbp
->sb_ccred
.cc_user
,
224 &sbp
->sb_ccred
.cc_pass
, slp
->dat
);
227 _ANSWER(2, FAL0
, FAL0
);
231 case AUTHTYPE_GSSAPI
:
232 if (options
& OPT_DEBUG
)
233 n_err(">>> %s", _(">>>Would perform GSS-API authentication now\n"));
234 else if (!_smtp_gssapi(sp
, sbp
, slp
))
241 snprintf(o
, sizeof o
, NETLINE("MAIL FROM:<%s>"), sbp
->sb_url
.url_u_h
.s
);
243 _ANSWER(2, FAL0
, FAL0
);
245 for (n
= sbp
->sb_to
; n
!= NULL
; n
= n
->n_flink
) {
246 if (!(n
->n_type
& GDEL
)) {
247 snprintf(o
, sizeof o
, NETLINE("RCPT TO:<%s>"), skinned_name(n
));
249 _ANSWER(2, FAL0
, FAL0
);
253 _OUT(NETLINE("DATA"));
254 _ANSWER(3, FAL0
, FAL0
);
256 fflush_rewind(sbp
->sb_input
);
257 cnt
= fsize(sbp
->sb_input
);
258 while (fgetline(&slp
->buf
, &slp
->bufsize
, &cnt
, &blen
, sbp
->sb_input
, 1)
261 if (*slp
->buf
== '\n')
262 inhdr
= inbcc
= FAL0
;
263 else if (inbcc
&& blankchar(*slp
->buf
))
265 /* We know what we have generated first, so do not look for whitespace
267 else if (!ascncasecmp(slp
->buf
, "bcc: ", 5)) {
274 if (options
& OPT_DEBUG
) {
275 n_err(">>> %s%s", (*slp
->buf
== '.' ? "." : ""), slp
->buf
);
278 if (*slp
->buf
== '.')
279 swrite1(sp
, ".", 1, 1); /* TODO I/O rewrite.. */
280 slp
->buf
[blen
- 1] = NETNL
[0];
281 slp
->buf
[blen
] = NETNL
[1];
282 swrite1(sp
, slp
->buf
, blen
+ 1, 1);
285 _ANSWER(2, FAL0
, FAL0
);
287 _OUT(NETLINE("QUIT"));
288 _ANSWER(2, TRU1
, FAL0
);
291 if (slp
->buf
!= NULL
)
298 # include "smtp_gssapi.h"
305 smtp_mta(struct sendbundle
*sbp
)
308 sighandler_type
volatile saveterm
;
309 bool_t
volatile rv
= FAL0
;
312 saveterm
= safe_signal(SIGTERM
, SIG_IGN
);
313 if (sigsetjmp(_smtp_jmp
, 1))
315 if (saveterm
!= SIG_IGN
)
316 safe_signal(SIGTERM
, &_smtp_onterm
);
318 memset(&so
, 0, sizeof so
);
319 if (!(options
& OPT_DEBUG
) && !sopen(&so
, &sbp
->sb_url
))
323 rv
= _smtp_talk(&so
, sbp
);
325 if (!(options
& OPT_DEBUG
))
328 safe_signal(SIGTERM
, saveterm
);
332 #endif /* HAVE_SMTP */