THANKS: v14.7.5
[s-mailx.git] / smtp.c
blob0e57b8733ccb88e8b547669f52afa62c56284a1e
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ SMTP client.
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 - 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
8 */
9 /*
10 * Copyright (c) 2000
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
15 * are met:
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
39 * SUCH DAMAGE.
42 #ifndef HAVE_AMALGAMATION
43 # include "nail.h"
44 #endif
46 EMPTY_FILE(smtp)
47 #ifdef HAVE_SMTP
48 #include <sys/socket.h>
50 #include <netdb.h>
52 #include <netinet/in.h>
54 #ifdef HAVE_ARPA_INET_H
55 # include <arpa/inet.h>
56 #endif
58 #undef NL
59 #undef LINE
60 #define NL "\015\012"
61 #define LINE(X) X NL
63 struct smtp_line {
64 char *dat; /* Actual data */
65 size_t datlen;
66 char *buf; /* Memory buffer */
67 size_t bufsize;
70 static sigjmp_buf _smtp_jmp;
72 static void _smtp_onterm(int signo);
74 /* Get the SMTP server's answer, expecting val */
75 static int _smtp_read(struct sock *sp, struct smtp_line *slp, int val,
76 bool_t ign_eof, bool_t want_dat);
78 /* Talk to a SMTP server */
79 static bool_t _smtp_talk(struct sock *sp, struct sendbundle *sbp);
81 #ifdef HAVE_GSSAPI
82 static bool_t _smtp_gssapi(struct sock *sp, struct sendbundle *sbp,
83 struct smtp_line *slp);
84 #endif
86 static void
87 _smtp_onterm(int signo)
89 NYD_X; /* Signal handler */
90 UNUSED(signo);
91 siglongjmp(_smtp_jmp, 1);
94 static int
95 _smtp_read(struct sock *sp, struct smtp_line *slp, int val,
96 bool_t ign_eof, bool_t want_dat)
98 int rv, len;
99 char *cp;
100 NYD_ENTER;
102 do {
103 if ((len = sgetline(&slp->buf, &slp->bufsize, NULL, sp)) < 6) {
104 if (len >= 0 && !ign_eof)
105 fprintf(stderr, _("Unexpected EOF on SMTP connection\n"));
106 rv = -1;
107 goto jleave;
109 if (options & OPT_VERBVERB)
110 fputs(slp->buf, stderr);
111 switch (slp->buf[0]) {
112 case '1': rv = 1; break;
113 case '2': rv = 2; break;
114 case '3': rv = 3; break;
115 case '4': rv = 4; break;
116 default: rv = 5; break;
118 if (val != rv)
119 fprintf(stderr, _("smtp-server: %s"), slp->buf);
120 } while (slp->buf[3] == '-');
122 if (want_dat) {
123 for (cp = slp->buf; digitchar(*cp); --len, ++cp)
125 for (; blankchar(*cp); --len, ++cp)
127 slp->dat = cp;
128 assert(len >= 2);
129 len -= 2;
130 cp[slp->datlen = (size_t)len] = '\0';
132 jleave:
133 NYD_LEAVE;
134 return rv;
137 /* Indirect SMTP I/O */
138 #define _ANSWER(X, IGNEOF, WANTDAT) \
139 do if (!(options & OPT_DEBUG)) {\
140 int y;\
141 if ((y = _smtp_read(sp, slp, X, IGNEOF, WANTDAT)) != (X) &&\
142 (!(IGNEOF) || y != -1))\
143 goto jleave;\
144 } while (0)
145 #define _OUT(X) \
146 do {\
147 if (options & OPT_VERBVERB)\
148 fprintf(stderr, ">>> %s", X);\
149 if (!(options & OPT_DEBUG))\
150 swrite(sp, X);\
151 } while (0)
153 static bool_t
154 _smtp_talk(struct sock *sp, struct sendbundle *sbp)
156 char o[LINESIZE], *hostname;
157 struct smtp_line _sl, *slp = &_sl;
158 struct str b64;
159 struct name *n;
160 size_t blen, cnt;
161 bool_t inhdr = TRU1, inbcc = FAL0, rv = FAL0;
162 NYD_ENTER;
164 hostname = nodename(TRU1);
165 slp->buf = NULL;
166 slp->bufsize = 0;
168 /* Read greeting */
169 _ANSWER(2, FAL0, FAL0);
171 #ifdef HAVE_SSL
172 if (!sp->s_use_ssl && xok_blook(smtp_use_starttls, &sbp->sb_url, OXM_ALL)) {
173 snprintf(o, sizeof o, LINE("EHLO %s"), hostname);
174 _OUT(o);
175 _ANSWER(2, FAL0, FAL0);
177 _OUT(LINE("STARTTLS"));
178 _ANSWER(2, FAL0, FAL0);
180 if (!(options & OPT_DEBUG) &&
181 ssl_open(sbp->sb_url.url_host.s, sp, sbp->sb_url.url_u_h_p.s
182 ) != OKAY)
183 goto jleave;
185 #else
186 if (xok_blook(smtp_use_starttls, &sbp->sb_url, OXM_ALL)) {
187 fprintf(stderr, _("No SSL support compiled in.\n"));
188 goto jleave;
190 #endif
192 /* Shorthand: no authentication, plain HELO? */
193 if (sbp->sb_ccred.cc_authtype == AUTHTYPE_NONE) {
194 snprintf(o, sizeof o, LINE("HELO %s"), hostname);
195 _OUT(o);
196 _ANSWER(2, FAL0, FAL0);
197 goto jsend;
200 /* We'll have to deal with authentication */
201 snprintf(o, sizeof o, LINE("EHLO %s"), hostname);
202 _OUT(o);
203 _ANSWER(2, FAL0, FAL0);
205 switch (sbp->sb_ccred.cc_authtype) {
206 default:
207 /* FALLTHRU (doesn't happen) */
208 case AUTHTYPE_PLAIN:
209 _OUT(LINE("AUTH PLAIN"));
210 _ANSWER(3, FAL0, FAL0);
212 snprintf(o, sizeof o, "%c%s%c%s",
213 '\0', sbp->sb_ccred.cc_user.s, '\0', sbp->sb_ccred.cc_pass.s);
214 b64_encode_buf(&b64, o,
215 sbp->sb_ccred.cc_user.l + sbp->sb_ccred.cc_pass.l + 2,
216 B64_SALLOC | B64_CRLF);
217 _OUT(b64.s);
218 _ANSWER(2, FAL0, FAL0);
219 break;
220 case AUTHTYPE_LOGIN:
221 _OUT(LINE("AUTH LOGIN"));
222 _ANSWER(3, FAL0, FAL0);
224 b64_encode_cp(&b64, sbp->sb_ccred.cc_user.s, B64_SALLOC | B64_CRLF);
225 _OUT(b64.s);
226 _ANSWER(3, FAL0, FAL0);
228 b64_encode_cp(&b64, sbp->sb_ccred.cc_pass.s, B64_SALLOC | B64_CRLF);
229 _OUT(b64.s);
230 _ANSWER(2, FAL0, FAL0);
231 break;
232 #ifdef HAVE_MD5
233 case AUTHTYPE_CRAM_MD5:
234 _OUT(LINE("AUTH CRAM-MD5"));
235 _ANSWER(3, FAL0, TRU1);
236 { char *cp = cram_md5_string(&sbp->sb_ccred.cc_user,
237 &sbp->sb_ccred.cc_pass, slp->dat);
238 _OUT(cp);
240 _ANSWER(2, FAL0, FAL0);
241 break;
242 #endif
243 #ifdef HAVE_GSSAPI
244 case AUTHTYPE_GSSAPI:
245 if (!_smtp_gssapi(sp, sbp, slp))
246 goto jleave;
247 break;
248 #endif
251 jsend:
252 snprintf(o, sizeof o, LINE("MAIL FROM:<%s>"), sbp->sb_url.url_u_h.s);
253 _OUT(o);
254 _ANSWER(2, FAL0, FAL0);
256 for (n = sbp->sb_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));
259 _OUT(o);
260 _ANSWER(2, FAL0, FAL0);
264 _OUT(LINE("DATA"));
265 _ANSWER(3, FAL0, FAL0);
267 fflush_rewind(sbp->sb_input);
268 cnt = fsize(sbp->sb_input);
269 while (fgetline(&slp->buf, &slp->bufsize, &cnt, &blen, sbp->sb_input, 1)
270 != NULL) {
271 if (inhdr) {
272 if (*slp->buf == '\n')
273 inhdr = inbcc = FAL0;
274 else if (inbcc && blankchar(*slp->buf))
275 continue;
276 /* We know what we have generated first, so do not look for whitespace
277 * before the ':' */
278 else if (!ascncasecmp(slp->buf, "bcc: ", 5)) {
279 inbcc = TRU1;
280 continue;
281 } else
282 inbcc = FAL0;
285 if (*slp->buf == '.') {
286 if (options & OPT_DEBUG)
287 putc('.', stderr);
288 else
289 swrite1(sp, ".", 1, 1);
291 if (options & OPT_DEBUG) {
292 fprintf(stderr, ">>> %s", slp->buf);
293 continue;
295 slp->buf[blen - 1] = NL[0];
296 slp->buf[blen] = NL[1];
297 swrite1(sp, slp->buf, blen + 1, 1);
299 _OUT(LINE("."));
300 _ANSWER(2, FAL0, FAL0);
302 _OUT(LINE("QUIT"));
303 _ANSWER(2, TRU1, FAL0);
304 rv = TRU1;
305 jleave:
306 if (slp->buf != NULL)
307 free(slp->buf);
308 NYD_LEAVE;
309 return rv;
312 #ifdef HAVE_GSSAPI
313 # include "smtp_gssapi.h"
314 #endif
316 #undef _OUT
317 #undef _ANSWER
319 FL bool_t
320 smtp_mta(struct sendbundle *sbp)
322 struct sock so;
323 sighandler_type volatile saveterm;
324 bool_t volatile rv = FAL0;
325 NYD_ENTER;
327 saveterm = safe_signal(SIGTERM, SIG_IGN);
328 if (sigsetjmp(_smtp_jmp, 1))
329 goto jleave;
330 if (saveterm != SIG_IGN)
331 safe_signal(SIGTERM, &_smtp_onterm);
333 memset(&so, 0, sizeof so);
334 if (!(options & OPT_DEBUG) && !sopen(&so, &sbp->sb_url))
335 goto jleave;
337 so.s_desc = "SMTP";
338 rv = _smtp_talk(&so, sbp);
340 if (!(options & OPT_DEBUG))
341 sclose(&so);
342 jleave:
343 safe_signal(SIGTERM, saveterm);
344 NYD_LEAVE;
345 return rv;
348 #undef LINE
349 #undef NL
350 #endif /* HAVE_SMTP */
352 /* s-it-mode */