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
8 * For license terms, see the file COPYING in this directory.
15 #include "fetchmail.h"
27 static struct opt extensions[] =
29 {"8BITMIME", ESMTP_8BITMIME},
32 {"AUTH ", ESMTP_AUTH},
35 #endif /* ODMR_ENABLE */
39 char smtp_response[MSGBUFSIZE];
41 static char smtp_mode = 'S';
43 void SMTP_setmode(char sl)
44 /* set whether we are speaking SMTP or LMTP */
49 int SMTP_helo(int sock,const char *host)
50 /* send a "HELO" message to the SMTP listener */
54 SockPrintf(sock,"HELO %s\r\n", host);
55 if (outlevel >= O_MONITOR)
56 report(stdout, "SMTP> HELO %s\n", host);
61 static void SMTP_auth_error(int sock, char *msg)
63 SockPrintf(sock, "*\r\n");
64 SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
65 if (outlevel >= O_MONITOR) report(stdout, msg);
68 static void SMTP_auth(int sock, char *username, char *password, char *buf)
69 /* ESMTP Authentication support for fetchmail by Wojciech Polak */
76 if (!username || !password) return;
78 memset(b64buf, 0, sizeof(b64buf));
79 memset(tmp, 0, sizeof(tmp));
81 if (strstr(buf, "CRAM-MD5")) {
82 unsigned char digest[16];
83 memset(digest, 0, sizeof(digest));
85 if (outlevel >= O_MONITOR)
86 report(stdout, GT_("ESMTP CRAM-MD5 Authentication...\n"));
87 SockPrintf(sock, "AUTH CRAM-MD5\r\n");
88 SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
89 strncpy(tmp, smtp_response, sizeof(tmp));
90 tmp[sizeof(tmp)-1] = '\0';
92 if (strncmp(tmp, "334 ", 4)) { /* Server rejects AUTH */
93 SMTP_auth_error(sock, GT_("Server rejected the AUTH command.\n"));
99 /* (hmh) from64tobits will not NULL-terminate strings! */
100 if (from64tobits(b64buf, p, sizeof(b64buf) - 1) <= 0) {
101 SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
104 if (outlevel >= O_DEBUG)
105 report(stdout, GT_("Challenge decoded: %s\n"), b64buf);
106 hmac_md5(password, strlen(password),
107 b64buf, strlen(b64buf), digest, sizeof(digest));
109 snprintf(tmp, sizeof(tmp),
112 #endif /* HAVE_SNPRINTF */
113 "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
114 username, digest[0], digest[1], digest[2], digest[3],
115 digest[4], digest[5], digest[6], digest[7], digest[8],
116 digest[9], digest[10], digest[11], digest[12], digest[13],
117 digest[14], digest[15]);
119 to64frombits(b64buf, tmp, strlen(tmp));
120 SockPrintf(sock, "%s\r\n", b64buf);
123 else if (strstr(buf, "PLAIN")) {
125 if (outlevel >= O_MONITOR)
126 report(stdout, GT_("ESMTP PLAIN Authentication...\n"));
128 snprintf(tmp, sizeof(tmp),
131 #endif /* HAVE_SNPRINTF */
132 "^%s^%s", username, password);
135 for (c = len - 1; c >= 0; c--)
140 to64frombits(b64buf, tmp, len);
141 SockPrintf(sock, "AUTH PLAIN %s\r\n", b64buf);
144 else if (strstr(buf, "LOGIN")) {
145 if (outlevel >= O_MONITOR)
146 report(stdout, GT_("ESMTP LOGIN Authentication...\n"));
147 SockPrintf(sock, "AUTH LOGIN\r\n");
148 SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
149 strncpy(tmp, smtp_response, sizeof(tmp));
150 tmp[sizeof(tmp)-1] = '\0';
152 if (strncmp(tmp, "334 ", 4)) { /* Server rejects AUTH */
153 SMTP_auth_error(sock, GT_("Server rejected the AUTH command.\n"));
157 p = strchr(tmp, ' ');
159 if (from64tobits(b64buf, p, sizeof(b64buf) - 1) <= 0) {
160 SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
163 to64frombits(b64buf, username, strlen(username));
164 SockPrintf(sock, "%s\r\n", b64buf);
165 SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
166 strncpy(tmp, smtp_response, sizeof(tmp));
167 tmp[sizeof(tmp)-1] = '\0';
168 p = strchr(tmp, ' ');
170 SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
174 memset(b64buf, 0, sizeof(b64buf));
175 if (from64tobits(b64buf, p, sizeof(b64buf) - 1) <= 0) {
176 SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
179 to64frombits(b64buf, password, strlen(password));
180 SockPrintf(sock, "%s\r\n", b64buf);
186 int SMTP_ehlo(int sock, const char *host, char *name, char *password, int *opt)
187 /* send a "EHLO" message to the SMTP listener, return extension status bits */
190 char auth_response[511];
192 SockPrintf(sock,"%cHLO %s\r\n", (smtp_mode == 'S') ? 'E' : smtp_mode, host);
193 if (outlevel >= O_MONITOR)
194 report(stdout, "%cMTP> %cHLO %s\n",
195 smtp_mode, (smtp_mode == 'S') ? 'E' : smtp_mode, host);
198 while ((SockRead(sock, smtp_response, sizeof(smtp_response)-1)) != -1)
200 int n = strlen(smtp_response);
202 if (smtp_response[strlen(smtp_response)-1] == '\n')
203 smtp_response[strlen(smtp_response)-1] = '\0';
204 if (smtp_response[strlen(smtp_response)-1] == '\r')
205 smtp_response[strlen(smtp_response)-1] = '\0';
208 smtp_response[n] = '\0';
209 if (outlevel >= O_MONITOR)
210 report(stdout, "SMTP< %s\n", smtp_response);
211 for (hp = extensions; hp->name; hp++)
212 if (!strncasecmp(hp->name, smtp_response+4, strlen(hp->name))) {
214 if (strncmp(hp->name, "AUTH ", 5) == 0)
215 strncpy(auth_response, smtp_response, sizeof(auth_response));
216 auth_response[sizeof(auth_response)-1] = '\0';
218 if ((smtp_response[0] == '1' || smtp_response[0] == '2' || smtp_response[0] == '3') && smtp_response[3] == ' ') {
219 if (*opt & ESMTP_AUTH)
220 SMTP_auth(sock, name, password, auth_response);
223 else if (smtp_response[3] != '-')
226 return SM_UNRECOVERABLE;
229 int SMTP_from(int sock, const char *from, const char *opts)
230 /* send a "MAIL FROM:" message to the SMTP listener */
233 char buf[MSGBUFSIZE];
237 snprintf(buf, sizeof(buf),
240 #endif /* HAVE_SNPRINTF */
241 "MAIL FROM:%s", from);
244 snprintf(buf, sizeof(buf),
247 #endif /* HAVE_SNPRINTF */
248 "MAIL FROM:<%s>", from);
251 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%s", opts);
254 #endif /* HAVE_SNPRINTF */
255 SockPrintf(sock,"%s\r\n", buf);
256 if (outlevel >= O_MONITOR)
257 report(stdout, "%cMTP> %s\n", smtp_mode, buf);
262 int SMTP_rcpt(int sock, const char *to)
263 /* send a "RCPT TO:" message to the SMTP listener */
267 SockPrintf(sock,"RCPT TO:<%s>\r\n", to);
268 if (outlevel >= O_MONITOR)
269 report(stdout, "%cMTP> RCPT TO:<%s>\n", smtp_mode, to);
274 int SMTP_data(int sock)
275 /* send a "DATA" message to the SMTP listener */
279 SockPrintf(sock,"DATA\r\n");
280 if (outlevel >= O_MONITOR)
281 report(stdout, "%cMTP> DATA\n", smtp_mode);
286 int SMTP_rset(int sock)
287 /* send a "RSET" message to the SMTP listener */
291 SockPrintf(sock,"RSET\r\n");
292 if (outlevel >= O_MONITOR)
293 report(stdout, "%cMTP> RSET\n", smtp_mode);
298 int SMTP_quit(int sock)
299 /* send a "QUIT" message to the SMTP listener */
303 SockPrintf(sock,"QUIT\r\n");
304 if (outlevel >= O_MONITOR)
305 report(stdout, "%cMTP> QUIT\n", smtp_mode);
310 int SMTP_eom(int sock)
311 /* send a message data terminator to the SMTP listener */
315 SockPrintf(sock,".\r\n");
316 if (outlevel >= O_MONITOR)
317 report(stdout, "%cMTP>. (EOM)\n", smtp_mode);
320 * When doing LMTP, must process many of these at the outer level.
322 if (smtp_mode == 'S')
330 time_t last_smtp_ok = 0;
332 int SMTP_ok(int sock)
333 /* returns status of SMTP connection */
335 SIGHANDLERTYPE alrmsave;
337 /* set an alarm for smtp ok */
338 alrmsave = set_signal_handler(SIGALRM, null_signal_handler);
339 set_timeout(mytimeout);
341 while ((SockRead(sock, smtp_response, sizeof(smtp_response)-1)) != -1)
347 set_signal_handler(SIGALRM, alrmsave);
349 n = strlen(smtp_response);
350 if (n > 0 && smtp_response[n-1] == '\n')
352 if (n > 0 && smtp_response[n-1] == '\r')
354 smtp_response[n] = '\0';
355 if (outlevel >= O_MONITOR)
356 report(stdout, "%cMTP< %s\n", smtp_mode, smtp_response);
358 (smtp_response[3] != ' ' && smtp_response[3] != '-'))
360 if (outlevel >= O_MONITOR)
361 report(stderr, GT_("smtp listener protocol error\n"));
362 return SM_UNRECOVERABLE;
365 last_smtp_ok = time((time_t *) NULL);
367 if ((smtp_response[0] == '1' || smtp_response[0] == '2' || smtp_response[0] == '3') &&
368 smtp_response[3] == ' ')
370 else if (smtp_response[3] != '-')
373 /* set an alarm for smtp ok */
374 set_signal_handler(SIGALRM, null_signal_handler);
375 set_timeout(mytimeout);
381 set_signal_handler(SIGALRM, alrmsave);
383 if (outlevel >= O_MONITOR)
384 report(stderr, GT_("smtp listener protocol error\n"));
385 return SM_UNRECOVERABLE;
388 /* smtp.c ends here */