2 * smtp.c -- code for speaking SMTP to a listener port
4 * Concept due to Harry Hochheiser. Implementation by ESR. Cleanup and
5 * strict RFC821 compliance by Cameron MacPherson.
7 * Copyright 1997 Eric S. Raymond, 2009 Matthias Andree
8 * For license terms, see the file COPYING in this directory.
12 #include "fetchmail.h"
29 static struct opt extensions[] =
31 {"8BITMIME", ESMTP_8BITMIME},
34 {"AUTH ", ESMTP_AUTH},
37 #endif /* ODMR_ENABLE */
41 char smtp_response[MSGBUFSIZE];
43 /* XXX: this must not be used for LMTP! */
44 int SMTP_helo(int sock, char smtp_mode, const char *host)
45 /* send a "HELO" message to the SMTP listener */
49 SockPrintf(sock,"HELO %s\r\n", host);
50 if (outlevel >= O_MONITOR)
51 report(stdout, "%cMTP> HELO %s\n", smtp_mode, host);
52 ok = SMTP_ok(sock, smtp_mode, TIMEOUT_HELO);
56 static void SMTP_auth_error(int sock, const char *msg)
58 SockPrintf(sock, "*\r\n");
59 SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
60 if (outlevel >= O_MONITOR) report(stdout, "%s", msg);
63 static void SMTP_auth(int sock, char smtp_mode, char *username, char *password, char *buf)
64 /* ESMTP Authentication support for fetchmail by Wojciech Polak */
71 if (!username || !password) return;
73 memset(b64buf, 0, sizeof(b64buf));
74 memset(tmp, 0, sizeof(tmp));
76 if (strstr(buf, "CRAM-MD5")) {
77 unsigned char digest[16];
78 memset(digest, 0, sizeof(digest));
80 if (outlevel >= O_MONITOR)
81 report(stdout, GT_("ESMTP CRAM-MD5 Authentication...\n"));
82 SockPrintf(sock, "AUTH CRAM-MD5\r\n");
83 SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
84 strlcpy(tmp, smtp_response, sizeof(tmp));
86 if (strncmp(tmp, "334", 3)) { /* Server rejects AUTH */
87 SMTP_auth_error(sock, GT_("Server rejected the AUTH command.\n"));
93 /* (hmh) from64tobits will not NULL-terminate strings! */
94 if (from64tobits(b64buf, p, sizeof(b64buf) - 1) <= 0) {
95 SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
98 if (outlevel >= O_DEBUG)
99 report(stdout, GT_("Challenge decoded: %s\n"), b64buf);
100 hmac_md5((unsigned char *)password, strlen(password),
101 (unsigned char *)b64buf, strlen(b64buf), digest, sizeof(digest));
102 snprintf(tmp, sizeof(tmp),
103 "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
104 username, digest[0], digest[1], digest[2], digest[3],
105 digest[4], digest[5], digest[6], digest[7], digest[8],
106 digest[9], digest[10], digest[11], digest[12], digest[13],
107 digest[14], digest[15]);
109 to64frombits(b64buf, tmp, strlen(tmp));
110 SockPrintf(sock, "%s\r\n", b64buf);
111 SMTP_ok(sock, smtp_mode, TIMEOUT_DEFAULT);
113 else if (strstr(buf, "PLAIN")) {
115 if (outlevel >= O_MONITOR)
116 report(stdout, GT_("ESMTP PLAIN Authentication...\n"));
117 snprintf(tmp, sizeof(tmp), "^%s^%s", username, password);
120 for (c = len - 1; c >= 0; c--)
125 to64frombits(b64buf, tmp, len);
126 SockPrintf(sock, "AUTH PLAIN %s\r\n", b64buf);
127 SMTP_ok(sock, smtp_mode, TIMEOUT_DEFAULT);
129 else if (strstr(buf, "LOGIN")) {
130 if (outlevel >= O_MONITOR)
131 report(stdout, GT_("ESMTP LOGIN Authentication...\n"));
132 SockPrintf(sock, "AUTH LOGIN\r\n");
133 SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
134 strlcpy(tmp, smtp_response, sizeof(tmp));
136 if (strncmp(tmp, "334", 3)) { /* Server rejects AUTH */
137 SMTP_auth_error(sock, GT_("Server rejected the AUTH command.\n"));
141 p = strchr(tmp, ' ');
143 if (from64tobits(b64buf, p, sizeof(b64buf) - 1) <= 0) {
144 SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
147 to64frombits(b64buf, username, strlen(username));
148 SockPrintf(sock, "%s\r\n", b64buf);
149 SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
150 strlcpy(tmp, smtp_response, sizeof(tmp));
151 p = strchr(tmp, ' ');
153 SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
157 memset(b64buf, 0, sizeof(b64buf));
158 if (from64tobits(b64buf, p, sizeof(b64buf) - 1) <= 0) {
159 SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
162 to64frombits(b64buf, password, strlen(password));
163 SockPrintf(sock, "%s\r\n", b64buf);
164 SMTP_ok(sock, smtp_mode, TIMEOUT_DEFAULT);
169 int SMTP_ehlo(int sock, char smtp_mode, const char *host, char *name, char *password, int *opt)
170 /* send a "EHLO" message to the SMTP listener, return extension status bits */
173 char auth_response[511];
174 SIGHANDLERTYPE alrmsave;
175 const int tmout = (mytimeout >= TIMEOUT_HELO ? mytimeout : TIMEOUT_HELO);
177 SockPrintf(sock,"%cHLO %s\r\n", (smtp_mode == 'S') ? 'E' : smtp_mode, host);
178 if (outlevel >= O_MONITOR)
179 report(stdout, "%cMTP> %cHLO %s\n",
180 smtp_mode, (smtp_mode == 'S') ? 'E' : smtp_mode, host);
182 alrmsave = set_signal_handler(SIGALRM, null_signal_handler);
186 while ((SockRead(sock, smtp_response, sizeof(smtp_response)-1)) != -1)
188 int n = strlen(smtp_response);
191 (void)set_signal_handler(SIGALRM, alrmsave);
193 if (smtp_response[strlen(smtp_response)-1] == '\n')
194 smtp_response[strlen(smtp_response)-1] = '\0';
195 if (smtp_response[strlen(smtp_response)-1] == '\r')
196 smtp_response[strlen(smtp_response)-1] = '\0';
199 smtp_response[n] = '\0';
200 if (outlevel >= O_MONITOR)
201 report(stdout, "%cMTP< %s\n", smtp_mode, smtp_response);
202 for (hp = extensions; hp->name; hp++)
203 if (!strncasecmp(hp->name, smtp_response+4, strlen(hp->name))) {
205 if (strncmp(hp->name, "AUTH ", 5) == 0)
206 strncpy(auth_response, smtp_response, sizeof(auth_response));
207 auth_response[sizeof(auth_response)-1] = '\0';
209 if ((smtp_response[0] == '1' || smtp_response[0] == '2' || smtp_response[0] == '3') && smtp_response[3] == ' ') {
210 if (*opt & ESMTP_AUTH)
211 SMTP_auth(sock, smtp_mode, name, password, auth_response);
214 else if (smtp_response[3] != '-')
217 alrmsave = set_signal_handler(SIGALRM, null_signal_handler);
220 return SM_UNRECOVERABLE;
223 int SMTP_from(int sock, char smtp_mode, const char *from, const char *opts)
224 /* send a "MAIL FROM:" message to the SMTP listener */
227 char buf[MSGBUFSIZE];
230 snprintf(buf, sizeof(buf), "MAIL FROM:%s", from);
232 snprintf(buf, sizeof(buf), "MAIL FROM:<%s>", from);
234 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%s", opts);
235 SockPrintf(sock,"%s\r\n", buf);
236 if (outlevel >= O_MONITOR)
237 report(stdout, "%cMTP> %s\n", smtp_mode, buf);
238 ok = SMTP_ok(sock, smtp_mode, TIMEOUT_MAIL);
242 int SMTP_rcpt(int sock, char smtp_mode, const char *to)
243 /* send a "RCPT TO:" message to the SMTP listener */
247 SockPrintf(sock,"RCPT TO:<%s>\r\n", to);
248 if (outlevel >= O_MONITOR)
249 report(stdout, "%cMTP> RCPT TO:<%s>\n", smtp_mode, to);
250 ok = SMTP_ok(sock, smtp_mode, TIMEOUT_RCPT);
254 int SMTP_data(int sock, char smtp_mode)
255 /* send a "DATA" message to the SMTP listener */
259 SockPrintf(sock,"DATA\r\n");
260 if (outlevel >= O_MONITOR)
261 report(stdout, "%cMTP> DATA\n", smtp_mode);
262 ok = SMTP_ok(sock, smtp_mode, TIMEOUT_DATA);
266 int SMTP_rset(int sock, char smtp_mode)
267 /* send a "RSET" message to the SMTP listener */
271 SockPrintf(sock,"RSET\r\n");
272 if (outlevel >= O_MONITOR)
273 report(stdout, "%cMTP> RSET\n", smtp_mode);
274 ok = SMTP_ok(sock, smtp_mode, TIMEOUT_DEFAULT);
278 int SMTP_quit(int sock, char smtp_mode)
279 /* send a "QUIT" message to the SMTP listener */
283 SockPrintf(sock,"QUIT\r\n");
284 if (outlevel >= O_MONITOR)
285 report(stdout, "%cMTP> QUIT\n", smtp_mode);
286 ok = SMTP_ok(sock, smtp_mode, TIMEOUT_DEFAULT);
290 int SMTP_eom(int sock, char smtp_mode)
291 /* send a message data terminator to the SMTP listener */
293 SockPrintf(sock,".\r\n");
294 if (outlevel >= O_MONITOR)
295 report(stdout, "%cMTP>. (EOM)\n", smtp_mode);
298 * When doing LMTP, must process many of these at the outer level.
300 if (smtp_mode == 'S')
301 return SMTP_ok(sock, smtp_mode, TIMEOUT_EOM);
306 time_t last_smtp_ok = 0;
308 int SMTP_ok(int sock, char smtp_mode, int mintimeout)
309 /**< returns status of SMTP connection and saves the message in
310 * smtp_response, without trailing [CR]LF, but with normalized CRLF
311 * between multiple lines of multi-line replies */
313 SIGHANDLERTYPE alrmsave;
314 char reply[MSGBUFSIZE], *i;
316 /* set an alarm for smtp ok */
317 alrmsave = set_signal_handler(SIGALRM, null_signal_handler);
318 set_timeout(mytimeout >= mintimeout ? mytimeout : mintimeout);
320 smtp_response[0] = '\0';
322 while ((SockRead(sock, reply, sizeof(reply)-1)) != -1)
328 set_signal_handler(SIGALRM, alrmsave);
331 if (n > 0 && reply[n-1] == '\n')
333 if (n > 0 && reply[n-1] == '\r')
337 /* stomp over control characters */
338 for (i = reply; *i; i++)
339 if (iscntrl((unsigned char)*i))
342 if (outlevel >= O_MONITOR)
343 report(stdout, "%cMTP< %s\n", smtp_mode, reply);
344 /* note that \0 is part of the strchr search string and the
345 * blank after the reply code is optional (RFC 5321 4.2.1) */
346 if (n < 3 || !strchr(" -", reply[3]))
348 if (outlevel >= O_MONITOR)
349 report(stderr, GT_("smtp listener protocol error\n"));
350 return SM_UNRECOVERABLE;
353 last_smtp_ok = time((time_t *) NULL);
355 strlcat(smtp_response, reply, sizeof(smtp_response));
357 if (strchr("123", reply[0])
358 && isdigit((unsigned char)reply[1])
359 && isdigit((unsigned char)reply[2])
360 && strchr(" ", reply[3])) /* matches space and \0 */ {
362 } else if (reply[3] != '-')
365 strlcat(smtp_response, "\r\n", sizeof(smtp_response));
367 /* set an alarm for smtp ok */
368 set_signal_handler(SIGALRM, null_signal_handler);
369 set_timeout(mytimeout);
374 set_signal_handler(SIGALRM, alrmsave);
376 if (outlevel >= O_MONITOR)
377 report(stderr, GT_("smtp listener protocol error\n"));
378 return SM_UNRECOVERABLE;
381 /* smtp.c ends here */