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 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
8 * SPDX-License-Identifier: BSD-4-Clause
12 * Gunnar Ritter. All rights reserved.
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions
17 * 1. Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 * notice, this list of conditions and the following disclaimer in the
21 * documentation and/or other materials provided with the distribution.
22 * 3. All advertising materials mentioning features or use of this software
23 * must display the following acknowledgement:
24 * This product includes software developed by Gunnar Ritter
25 * and his contributors.
26 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
27 * may be used to endorse or promote products derived from this software
28 * without specific prior written permission.
30 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45 #ifndef HAVE_AMALGAMATION
51 #include <sys/socket.h>
54 char *dat
; /* Actual data */
56 char *buf
; /* Memory buffer */
60 static sigjmp_buf _smtp_jmp
;
62 static void _smtp_onterm(int signo
);
64 /* Get the SMTP server's answer, expecting val */
65 static int _smtp_read(struct sock
*sp
, struct smtp_line
*slp
, int val
,
66 bool_t ign_eof
, bool_t want_dat
);
68 /* Talk to a SMTP server */
69 static bool_t
_smtp_talk(struct sock
*sp
, struct sendbundle
*sbp
);
72 static bool_t
_smtp_gssapi(struct sock
*sp
, struct sendbundle
*sbp
,
73 struct smtp_line
*slp
);
77 _smtp_onterm(int signo
)
79 NYD_X
; /* Signal handler */
81 siglongjmp(_smtp_jmp
, 1);
85 _smtp_read(struct sock
*sp
, struct smtp_line
*slp
, int val
,
86 bool_t ign_eof
, bool_t want_dat
)
93 if ((len
= sgetline(&slp
->buf
, &slp
->bufsize
, NULL
, sp
)) < 6) {
94 if (len
>= 0 && !ign_eof
)
95 n_err(_("Unexpected EOF on SMTP connection\n"));
99 if (n_poption
& n_PO_VERBVERB
)
101 switch (slp
->buf
[0]) {
102 case '1': rv
= 1; break;
103 case '2': rv
= 2; break;
104 case '3': rv
= 3; break;
105 case '4': rv
= 4; break;
106 default: rv
= 5; break;
109 n_err(_("smtp-server: %s"), slp
->buf
);
110 } while (slp
->buf
[3] == '-');
113 for (cp
= slp
->buf
; digitchar(*cp
); --len
, ++cp
)
115 for (; blankchar(*cp
); --len
, ++cp
)
120 cp
[slp
->datlen
= (size_t)len
] = '\0';
127 /* Indirect SMTP I/O */
128 #define _ANSWER(X, IGNEOF, WANTDAT) \
129 do if (!(n_poption & n_PO_DEBUG)) {\
131 if ((y = _smtp_read(sp, slp, X, IGNEOF, WANTDAT)) != (X) &&\
132 (!(IGNEOF) || y != -1))\
137 if (n_poption & n_PO_D_VV){\
138 /* TODO for now n_err() cannot normalize newlines in %s expansions */\
139 char *__x__ = savestr(X), *__y__ = &__x__[strlen(__x__)];\
140 while(__y__ > __x__ && (__y__[-1] == '\n' || __y__[-1] == '\r'))\
143 n_err(">>> %s\n", __x__);\
145 if (!(n_poption & n_PO_DEBUG))\
150 _smtp_talk(struct sock
*sp
, struct sendbundle
*sbp
) /* TODO n_string etc. */
153 char const *hostname
;
154 struct smtp_line _sl
, *slp
= &_sl
;
158 bool_t inhdr
= TRU1
, inbcc
= FAL0
, rv
= FAL0
;
161 hostname
= n_nodename(TRU1
);
166 _ANSWER(2, FAL0
, FAL0
);
169 if (!sp
->s_use_tls
&& xok_blook(smtp_use_starttls
, &sbp
->sb_url
, OXM_ALL
)) {
170 snprintf(o
, sizeof o
, NETLINE("EHLO %s"), hostname
);
172 _ANSWER(2, FAL0
, FAL0
);
174 _OUT(NETLINE("STARTTLS"));
175 _ANSWER(2, FAL0
, FAL0
);
177 if(!(n_poption
& n_PO_DEBUG
) && !n_tls_open(&sbp
->sb_url
, sp
))
181 if (xok_blook(smtp_use_starttls
, &sbp
->sb_url
, OXM_ALL
)) {
182 n_err(_("No TLS support compiled in\n"));
187 /* Shorthand: no authentication, plain HELO? */
188 if (sbp
->sb_ccred
.cc_authtype
== AUTHTYPE_NONE
) {
189 snprintf(o
, sizeof o
, NETLINE("HELO %s"), hostname
);
191 _ANSWER(2, FAL0
, FAL0
);
195 /* We'll have to deal with authentication */
196 snprintf(o
, sizeof o
, NETLINE("EHLO %s"), hostname
);
198 _ANSWER(2, FAL0
, FAL0
);
200 switch (sbp
->sb_ccred
.cc_authtype
) {
202 /* FALLTHRU (doesn't happen) */
204 cnt
= sbp
->sb_ccred
.cc_user
.l
;
205 if(sbp
->sb_ccred
.cc_pass
.l
>= UIZ_MAX
- 2 ||
206 cnt
>= UIZ_MAX
- 2 - sbp
->sb_ccred
.cc_pass
.l
){
208 n_err(_("Credentials overflow buffer sizes\n"));
211 cnt
+= sbp
->sb_ccred
.cc_pass
.l
;
213 if(cnt
>= sizeof(o
) - 2)
216 if(b64_encode_calc_size(cnt
) == UIZ_MAX
)
219 _OUT(NETLINE("AUTH PLAIN"));
220 _ANSWER(3, FAL0
, FAL0
);
222 snprintf(o
, sizeof o
, "%c%s%c%s",
223 '\0', sbp
->sb_ccred
.cc_user
.s
, '\0', sbp
->sb_ccred
.cc_pass
.s
);
224 if(b64_encode_buf(&b64
, o
, cnt
, B64_SALLOC
| B64_CRLF
) == NULL
)
227 _ANSWER(2, FAL0
, FAL0
);
230 if(b64_encode_calc_size(sbp
->sb_ccred
.cc_user
.l
) == UIZ_MAX
||
231 b64_encode_calc_size(sbp
->sb_ccred
.cc_pass
.l
) == UIZ_MAX
)
234 _OUT(NETLINE("AUTH LOGIN"));
235 _ANSWER(3, FAL0
, FAL0
);
237 if(b64_encode_buf(&b64
, sbp
->sb_ccred
.cc_user
.s
, sbp
->sb_ccred
.cc_user
.l
,
238 B64_SALLOC
| B64_CRLF
) == NULL
)
241 _ANSWER(3, FAL0
, FAL0
);
243 if(b64_encode_buf(&b64
, sbp
->sb_ccred
.cc_pass
.s
, sbp
->sb_ccred
.cc_pass
.l
,
244 B64_SALLOC
| B64_CRLF
) == NULL
)
247 _ANSWER(2, FAL0
, FAL0
);
250 case AUTHTYPE_CRAM_MD5
:{
253 _OUT(NETLINE("AUTH CRAM-MD5"));
254 _ANSWER(3, FAL0
, TRU1
);
256 if((cp
= cram_md5_string(&sbp
->sb_ccred
.cc_user
, &sbp
->sb_ccred
.cc_pass
,
260 _ANSWER(2, FAL0
, FAL0
);
264 case AUTHTYPE_GSSAPI
:
265 if (n_poption
& n_PO_DEBUG
)
266 n_err(_(">>> We would perform GSS-API authentication now\n"));
267 else if (!_smtp_gssapi(sp
, sbp
, slp
))
274 snprintf(o
, sizeof o
, NETLINE("MAIL FROM:<%s>"), sbp
->sb_url
.url_u_h
.s
);
276 _ANSWER(2, FAL0
, FAL0
);
278 for(n
= sbp
->sb_to
; n
!= NULL
; n
= n
->n_flink
){
279 if (!(n
->n_type
& GDEL
)) { /* TODO should not happen!?! */
280 if(n
->n_flags
& NAME_ADDRSPEC_WITHOUT_DOMAIN
)
281 snprintf(o
, sizeof o
, NETLINE("RCPT TO:<%s@%s>"),
282 skinned_name(n
), hostname
);
284 snprintf(o
, sizeof o
, NETLINE("RCPT TO:<%s>"), skinned_name(n
));
286 _ANSWER(2, FAL0
, FAL0
);
290 _OUT(NETLINE("DATA"));
291 _ANSWER(3, FAL0
, FAL0
);
293 fflush_rewind(sbp
->sb_input
);
294 cnt
= fsize(sbp
->sb_input
);
295 while (fgetline(&slp
->buf
, &slp
->bufsize
, &cnt
, &blen
, sbp
->sb_input
, 1)
298 if (*slp
->buf
== '\n')
299 inhdr
= inbcc
= FAL0
;
300 else if (inbcc
&& blankchar(*slp
->buf
))
302 /* We know what we have generated first, so do not look for whitespace
304 else if (!ascncasecmp(slp
->buf
, "bcc: ", 5)) {
311 if (n_poption
& n_PO_DEBUG
) {
312 slp
->buf
[blen
- 1] = '\0';
313 n_err(">>> %s%s\n", (*slp
->buf
== '.' ? "." : n_empty
), slp
->buf
);
316 if (*slp
->buf
== '.')
317 swrite1(sp
, ".", 1, 1); /* TODO I/O rewrite.. */
318 slp
->buf
[blen
- 1] = NETNL
[0];
319 slp
->buf
[blen
] = NETNL
[1];
320 swrite1(sp
, slp
->buf
, blen
+ 1, 1);
323 _ANSWER(2, FAL0
, FAL0
);
325 _OUT(NETLINE("QUIT"));
326 _ANSWER(2, TRU1
, FAL0
);
329 if (slp
->buf
!= NULL
)
336 # include "smtp-gssapi.h"
343 smtp_mta(struct sendbundle
*sbp
)
346 sighandler_type
volatile saveterm
;
347 bool_t
volatile rv
= FAL0
;
350 saveterm
= safe_signal(SIGTERM
, SIG_IGN
);
351 if (sigsetjmp(_smtp_jmp
, 1))
353 if (saveterm
!= SIG_IGN
)
354 safe_signal(SIGTERM
, &_smtp_onterm
);
356 if(n_poption
& n_PO_DEBUG
)
357 memset(&so
, 0, sizeof so
);
358 else if(!sopen(&so
, &sbp
->sb_url
))
362 rv
= _smtp_talk(&so
, sbp
);
364 if (!(n_poption
& n_PO_DEBUG
))
367 safe_signal(SIGTERM
, saveterm
);
371 #endif /* HAVE_SMTP */