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 const int tmout = (mytimeout >= TIMEOUT_HELO ? mytimeout : TIMEOUT_HELO);
176 if (SockTimeout(sock, tmout) != 0)
177 return SM_UNRECOVERABLE;
179 SockPrintf(sock,"%cHLO %s\r\n", (smtp_mode == 'S') ? 'E' : smtp_mode, host);
180 if (outlevel >= O_MONITOR)
181 report(stdout, "%cMTP> %cHLO %s\n",
182 smtp_mode, (smtp_mode == 'S') ? 'E' : smtp_mode, host);
185 while ((SockRead(sock, smtp_response, sizeof(smtp_response)-1)) != -1)
191 n = strlen(smtp_response);
192 if (n > 0 && smtp_response[n-1] == '\n')
193 smtp_response[--n] = '\0';
194 if (n > 0 && smtp_response[n-1] == '\r')
195 smtp_response[--n] = '\0';
198 smtp_response[n] = '\0';
199 if (outlevel >= O_MONITOR)
200 report(stdout, "%cMTP< %s\n", smtp_mode, smtp_response);
201 for (hp = extensions; hp->name; hp++)
202 if (!strncasecmp(hp->name, smtp_response+4, strlen(hp->name))) {
204 if (strncmp(hp->name, "AUTH ", 5) == 0)
205 strncpy(auth_response, smtp_response, sizeof(auth_response));
206 auth_response[sizeof(auth_response)-1] = '\0';
208 if ((smtp_response[0] == '1' || smtp_response[0] == '2' || smtp_response[0] == '3') && smtp_response[3] == ' ') {
209 if (*opt & ESMTP_AUTH)
210 SMTP_auth(sock, smtp_mode, name, password, auth_response);
213 else if (smtp_response[3] != '-')
216 return SM_UNRECOVERABLE;
219 int SMTP_from(int sock, char smtp_mode, const char *from, const char *opts)
220 /* send a "MAIL FROM:" message to the SMTP listener */
223 char buf[MSGBUFSIZE];
226 snprintf(buf, sizeof(buf), "MAIL FROM:%s", from);
228 snprintf(buf, sizeof(buf), "MAIL FROM:<%s>", from);
230 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%s", opts);
231 SockPrintf(sock,"%s\r\n", buf);
232 if (outlevel >= O_MONITOR)
233 report(stdout, "%cMTP> %s\n", smtp_mode, buf);
234 ok = SMTP_ok(sock, smtp_mode, TIMEOUT_MAIL);
238 int SMTP_rcpt(int sock, char smtp_mode, const char *to)
239 /* send a "RCPT TO:" message to the SMTP listener */
243 SockPrintf(sock,"RCPT TO:<%s>\r\n", to);
244 if (outlevel >= O_MONITOR)
245 report(stdout, "%cMTP> RCPT TO:<%s>\n", smtp_mode, to);
246 ok = SMTP_ok(sock, smtp_mode, TIMEOUT_RCPT);
250 int SMTP_data(int sock, char smtp_mode)
251 /* send a "DATA" message to the SMTP listener */
255 SockPrintf(sock,"DATA\r\n");
256 if (outlevel >= O_MONITOR)
257 report(stdout, "%cMTP> DATA\n", smtp_mode);
258 ok = SMTP_ok(sock, smtp_mode, TIMEOUT_DATA);
262 int SMTP_rset(int sock, char smtp_mode)
263 /* send a "RSET" message to the SMTP listener */
267 SockPrintf(sock,"RSET\r\n");
268 if (outlevel >= O_MONITOR)
269 report(stdout, "%cMTP> RSET\n", smtp_mode);
270 ok = SMTP_ok(sock, smtp_mode, TIMEOUT_DEFAULT);
274 int SMTP_quit(int sock, char smtp_mode)
275 /* send a "QUIT" message to the SMTP listener */
279 SockPrintf(sock,"QUIT\r\n");
280 if (outlevel >= O_MONITOR)
281 report(stdout, "%cMTP> QUIT\n", smtp_mode);
282 ok = SMTP_ok(sock, smtp_mode, TIMEOUT_DEFAULT);
286 int SMTP_eom(int sock, char smtp_mode)
287 /* send a message data terminator to the SMTP listener */
289 SockPrintf(sock,".\r\n");
290 if (outlevel >= O_MONITOR)
291 report(stdout, "%cMTP>. (EOM)\n", smtp_mode);
294 * When doing LMTP, must process many of these at the outer level.
296 if (smtp_mode == 'S')
297 return SMTP_ok(sock, smtp_mode, TIMEOUT_EOM);
302 time_t last_smtp_ok = 0;
304 int SMTP_ok(int sock, char smtp_mode, int mintimeout)
305 /**< returns status of SMTP connection and saves the message in
306 * smtp_response, without trailing [CR]LF, but with normalized CRLF
307 * between multiple lines of multi-line replies */
309 char reply[MSGBUFSIZE], *i;
310 int tmo = mytimeout >= mintimeout ? mytimeout : mintimeout;
312 smtp_response[0] = '\0';
314 if (SockTimeout(sock, tmo))
315 return SM_UNRECOVERABLE;
317 while ((SockRead(sock, reply, sizeof(reply)-1)) != -1)
322 if (n > 0 && reply[n-1] == '\n')
324 if (n > 0 && reply[n-1] == '\r')
327 /* stomp over control characters */
328 for (i = reply; *i; i++)
329 if (iscntrl((unsigned char)*i))
332 if (outlevel >= O_MONITOR)
333 report(stdout, "%cMTP< %s\n", smtp_mode, reply);
334 /* note that \0 is part of the strchr search string and the
335 * blank after the reply code is optional (RFC 5321 4.2.1) */
336 if (n < 3 || !strchr(" -", reply[3]))
338 if (outlevel >= O_MONITOR)
339 report(stderr, GT_("smtp listener protocol error\n"));
340 return SM_UNRECOVERABLE;
343 last_smtp_ok = time((time_t *) NULL);
345 strlcat(smtp_response, reply, sizeof(smtp_response));
347 if (strchr("123", reply[0])
348 && isdigit((unsigned char)reply[1])
349 && isdigit((unsigned char)reply[2])
350 && strchr(" ", reply[3])) /* matches space and \0 */ {
352 } else if (reply[3] != '-')
355 strlcat(smtp_response, "\r\n", sizeof(smtp_response));
358 if (outlevel >= O_MONITOR)
359 report(stderr, GT_("smtp listener protocol error\n"));
360 return SM_UNRECOVERABLE;
363 /* smtp.c ends here */