smtp.c:_smtp_talk(): remove unused variable
[s-mailx.git] / smtp.c
bloba25ae5d0056b03cbbf49045fa71128eee32df317
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, tr(241, "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, tr(191, "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 && ok_blook(smtp_use_starttls)) {
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_uhp.s) != OKAY)
182 goto jleave;
184 #else
185 if (ok_blook(smtp_use_starttls)) {
186 fprintf(stderr, tr(225, "No SSL support compiled in.\n"));
187 goto jleave;
189 #endif
191 /* Shorthand: no authentication, plain HELO? */
192 if (sbp->sb_ccred.cc_authtype == AUTHTYPE_NONE) {
193 snprintf(o, sizeof o, LINE("HELO %s"), hostname);
194 _OUT(o);
195 _ANSWER(2, FAL0, FAL0);
196 goto jsend;
199 /* We'll have to deal with authentication */
200 snprintf(o, sizeof o, LINE("EHLO %s"), hostname);
201 _OUT(o);
202 _ANSWER(2, FAL0, FAL0);
204 switch (sbp->sb_ccred.cc_authtype) {
205 default:
206 /* FALLTHRU (doesn't happen) */
207 case AUTHTYPE_PLAIN:
208 _OUT(LINE("AUTH PLAIN"));
209 _ANSWER(3, FAL0, FAL0);
211 snprintf(o, sizeof o, "%c%s%c%s",
212 '\0', sbp->sb_ccred.cc_user.s, '\0', sbp->sb_ccred.cc_pass.s);
213 b64_encode_buf(&b64, o,
214 sbp->sb_ccred.cc_user.l + sbp->sb_ccred.cc_pass.l + 2,
215 B64_SALLOC | B64_CRLF);
216 _OUT(b64.s);
217 _ANSWER(2, FAL0, FAL0);
218 break;
219 case AUTHTYPE_LOGIN:
220 _OUT(LINE("AUTH LOGIN"));
221 _ANSWER(3, FAL0, FAL0);
223 b64_encode_cp(&b64, sbp->sb_ccred.cc_user.s, B64_SALLOC | B64_CRLF);
224 _OUT(b64.s);
225 _ANSWER(3, FAL0, FAL0);
227 b64_encode_cp(&b64, sbp->sb_ccred.cc_pass.s, B64_SALLOC | B64_CRLF);
228 _OUT(b64.s);
229 _ANSWER(2, FAL0, FAL0);
230 break;
231 #ifdef HAVE_MD5
232 case AUTHTYPE_CRAM_MD5:
233 _OUT(LINE("AUTH CRAM-MD5"));
234 _ANSWER(3, FAL0, TRU1);
235 { char *cp = cram_md5_string(sbp->sb_ccred.cc_user.s,
236 sbp->sb_ccred.cc_pass.s, slp->dat);
237 _OUT(cp);
239 _ANSWER(2, FAL0, FAL0);
240 break;
241 #endif
242 #ifdef HAVE_GSSAPI
243 case AUTHTYPE_GSSAPI:
244 if (!_smtp_gssapi(sp, sbp, slp))
245 goto jleave;
246 break;
247 #endif
250 jsend:
251 snprintf(o, sizeof o, LINE("MAIL FROM:<%s>"), sbp->sb_url.url_uh.s);
252 _OUT(o);
253 _ANSWER(2, FAL0, FAL0);
255 for (n = sbp->sb_to; n != NULL; n = n->n_flink) {
256 if (!(n->n_type & GDEL)) {
257 snprintf(o, sizeof o, LINE("RCPT TO:<%s>"), skinned_name(n));
258 _OUT(o);
259 _ANSWER(2, FAL0, FAL0);
263 _OUT(LINE("DATA"));
264 _ANSWER(3, FAL0, FAL0);
266 fflush_rewind(sbp->sb_input);
267 cnt = fsize(sbp->sb_input);
268 while (fgetline(&slp->buf, &slp->bufsize, &cnt, &blen, sbp->sb_input, 1)
269 != NULL) {
270 if (inhdr) {
271 if (*slp->buf == '\n')
272 inhdr = inbcc = FAL0;
273 else if (inbcc && blankchar(*slp->buf))
274 continue;
275 /* We know what we have generated first, so do not look for whitespace
276 * before the ':' */
277 else if (!ascncasecmp(slp->buf, "bcc: ", 5)) {
278 inbcc = TRU1;
279 continue;
280 } else
281 inbcc = FAL0;
284 if (*slp->buf == '.') {
285 if (options & OPT_DEBUG)
286 putc('.', stderr);
287 else
288 swrite1(sp, ".", 1, 1);
290 if (options & OPT_DEBUG) {
291 fprintf(stderr, ">>> %s", slp->buf);
292 continue;
294 slp->buf[blen - 1] = NL[0];
295 slp->buf[blen] = NL[1];
296 swrite1(sp, slp->buf, blen + 1, 1);
298 _OUT(LINE("."));
299 _ANSWER(2, FAL0, FAL0);
301 _OUT(LINE("QUIT"));
302 _ANSWER(2, TRU1, FAL0);
303 rv = TRU1;
304 jleave:
305 if (slp->buf != NULL)
306 free(slp->buf);
307 NYD_LEAVE;
308 return rv;
311 #ifdef HAVE_GSSAPI
312 # include "smtp_gssapi.h"
313 #endif
315 #undef _OUT
316 #undef _ANSWER
318 FL bool_t
319 smtp_mta(struct sendbundle *sbp)
321 struct sock so;
322 sighandler_type volatile saveterm;
323 bool_t volatile rv = FAL0;
324 NYD_ENTER;
326 saveterm = safe_signal(SIGTERM, SIG_IGN);
327 if (sigsetjmp(_smtp_jmp, 1))
328 goto jleave;
329 if (saveterm != SIG_IGN)
330 safe_signal(SIGTERM, &_smtp_onterm);
332 memset(&so, 0, sizeof so);
333 if (!(options & OPT_DEBUG) && !sopen(&so, &sbp->sb_url))
334 goto jleave;
336 so.s_desc = "SMTP";
337 rv = _smtp_talk(&so, sbp);
339 if (!(options & OPT_DEBUG))
340 sclose(&so);
341 jleave:
342 safe_signal(SIGTERM, saveterm);
343 NYD_LEAVE;
344 return rv;
347 #undef LINE
348 #undef NL
349 #endif /* HAVE_SMTP */
351 /* vim:set fenc=utf-8:s-it-mode */