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.
14 #include "fetchmail.h"
26 static struct opt extensions[] =
28 {"8BITMIME", ESMTP_8BITMIME},
31 {"AUTH ", ESMTP_AUTH},
34 #endif /* ODMR_ENABLE */
38 char smtp_response[MSGBUFSIZE];
40 static char smtp_mode = 'S';
42 void SMTP_setmode(char sl)
43 /* set whether we are speaking SMTP or LMTP */
48 int SMTP_helo(int sock,const char *host)
49 /* send a "HELO" message to the SMTP listener */
53 SockPrintf(sock,"HELO %s\r\n", host);
54 if (outlevel >= O_MONITOR)
55 report(stdout, "SMTP> HELO %s\n", host);
60 static void SMTP_auth_error(int sock, char *msg)
62 SockPrintf(sock, "*\r\n");
63 SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
64 if (outlevel >= O_MONITOR) report(stdout, msg);
67 static void SMTP_auth(int sock, char *username, char *password, char *buf)
68 /* ESMTP Authentication support for fetchmail by Wojciech Polak */
75 if (!username || !password) return;
77 memset(b64buf, 0, sizeof(b64buf));
78 memset(tmp, 0, sizeof(tmp));
80 if (strstr(buf, "CRAM-MD5")) {
81 unsigned char digest[16];
82 memset(digest, 0, sizeof(digest));
84 if (outlevel >= O_MONITOR)
85 report(stdout, GT_("ESMTP CRAM-MD5 Authentication...\n"));
86 SockPrintf(sock, "AUTH CRAM-MD5\r\n");
87 SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
88 strncpy(tmp, smtp_response, sizeof(tmp));
90 if (strncmp(tmp, "334 ", 4)) { /* Server rejects AUTH */
91 SMTP_auth_error(sock, GT_("Server rejected the AUTH command.\n"));
97 /* (hmh) from64tobits will not NULL-terminate strings! */
98 if (from64tobits(b64buf, p, sizeof(b64buf) - 1) <= 0) {
99 SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
102 if (outlevel >= O_DEBUG)
103 report(stdout, GT_("Challenge decoded: %s\n"), b64buf);
104 hmac_md5(password, strlen(password),
105 b64buf, strlen(b64buf), digest, sizeof(digest));
107 snprintf(tmp, sizeof(tmp),
110 #endif /* HAVE_SNPRINTF */
111 "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
112 username, digest[0], digest[1], digest[2], digest[3],
113 digest[4], digest[5], digest[6], digest[7], digest[8],
114 digest[9], digest[10], digest[11], digest[12], digest[13],
115 digest[14], digest[15]);
117 to64frombits(b64buf, tmp, strlen(tmp));
118 SockPrintf(sock, "%s\r\n", b64buf);
121 else if (strstr(buf, "PLAIN")) {
123 if (outlevel >= O_MONITOR)
124 report(stdout, GT_("ESMTP PLAIN Authentication...\n"));
126 snprintf(tmp, sizeof(tmp),
129 #endif /* HAVE_SNPRINTF */
130 "^%s^%s", username, password);
133 for (c = len - 1; c >= 0; c--)
138 to64frombits(b64buf, tmp, len);
139 SockPrintf(sock, "AUTH PLAIN %s\r\n", b64buf);
142 else if (strstr(buf, "LOGIN")) {
143 if (outlevel >= O_MONITOR)
144 report(stdout, GT_("ESMTP LOGIN Authentication...\n"));
145 SockPrintf(sock, "AUTH LOGIN\r\n");
146 SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
147 strncpy(tmp, smtp_response, sizeof(tmp));
149 if (strncmp(tmp, "334 ", 4)) { /* Server rejects AUTH */
150 SMTP_auth_error(sock, GT_("Server rejected the AUTH command.\n"));
154 p = strchr(tmp, ' ');
156 if (from64tobits(b64buf, p, sizeof(b64buf) - 1) <= 0) {
157 SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
160 to64frombits(b64buf, username, strlen(username));
161 SockPrintf(sock, "%s\r\n", b64buf);
162 SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
163 strncpy(tmp, smtp_response, sizeof(tmp));
164 p = strchr(tmp, ' ');
166 SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
170 memset(b64buf, 0, sizeof(b64buf));
171 if (from64tobits(b64buf, p, sizeof(b64buf) - 1) <= 0) {
172 SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
175 to64frombits(b64buf, password, strlen(password));
176 SockPrintf(sock, "%s\r\n", b64buf);
182 int SMTP_ehlo(int sock, const char *host, char *name, char *password, int *opt)
183 /* send a "EHLO" message to the SMTP listener, return extension status bits */
186 char auth_response[511];
188 SockPrintf(sock,"%cHLO %s\r\n", (smtp_mode == 'S') ? 'E' : smtp_mode, host);
189 if (outlevel >= O_MONITOR)
190 report(stdout, "%cMTP> %cHLO %s\n",
191 smtp_mode, (smtp_mode == 'S') ? 'E' : smtp_mode, host);
194 while ((SockRead(sock, smtp_response, sizeof(smtp_response)-1)) != -1)
196 int n = strlen(smtp_response);
198 if (smtp_response[strlen(smtp_response)-1] == '\n')
199 smtp_response[strlen(smtp_response)-1] = '\0';
200 if (smtp_response[strlen(smtp_response)-1] == '\r')
201 smtp_response[strlen(smtp_response)-1] = '\0';
204 smtp_response[n] = '\0';
205 if (outlevel >= O_MONITOR)
206 report(stdout, "SMTP< %s\n", smtp_response);
207 for (hp = extensions; hp->name; hp++)
208 if (!strncasecmp(hp->name, smtp_response+4, strlen(hp->name))) {
210 if (strncmp(hp->name, "AUTH ", 5) == 0)
211 strncpy(auth_response, smtp_response, sizeof(auth_response));
213 if ((smtp_response[0] == '1' || smtp_response[0] == '2' || smtp_response[0] == '3') && smtp_response[3] == ' ') {
214 if (*opt & ESMTP_AUTH)
215 SMTP_auth(sock, name, password, auth_response);
218 else if (smtp_response[3] != '-')
221 return SM_UNRECOVERABLE;
224 int SMTP_from(int sock, const char *from, const char *opts)
225 /* send a "MAIL FROM:" message to the SMTP listener */
228 char buf[MSGBUFSIZE];
230 if (strchr(from, '<'))
232 snprintf(buf, sizeof(buf),
235 #endif /* HAVE_SNPRINTF */
236 "MAIL FROM: %s", from);
239 snprintf(buf, sizeof(buf),
242 #endif /* HAVE_SNPRINTF */
243 "MAIL FROM:<%s>", from);
246 snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%s", opts);
249 #endif /* HAVE_SNPRINTF */
250 SockPrintf(sock,"%s\r\n", buf);
251 if (outlevel >= O_MONITOR)
252 report(stdout, "%cMTP> %s\n", smtp_mode, buf);
257 int SMTP_rcpt(int sock, const char *to)
258 /* send a "RCPT TO:" message to the SMTP listener */
262 SockPrintf(sock,"RCPT TO:<%s>\r\n", to);
263 if (outlevel >= O_MONITOR)
264 report(stdout, "%cMTP> RCPT TO:<%s>\n", smtp_mode, to);
269 int SMTP_data(int sock)
270 /* send a "DATA" message to the SMTP listener */
274 SockPrintf(sock,"DATA\r\n");
275 if (outlevel >= O_MONITOR)
276 report(stdout, "%cMTP> DATA\n", smtp_mode);
281 int SMTP_rset(int sock)
282 /* send a "RSET" message to the SMTP listener */
286 SockPrintf(sock,"RSET\r\n");
287 if (outlevel >= O_MONITOR)
288 report(stdout, "%cMTP> RSET\n", smtp_mode);
293 int SMTP_quit(int sock)
294 /* send a "QUIT" message to the SMTP listener */
298 SockPrintf(sock,"QUIT\r\n");
299 if (outlevel >= O_MONITOR)
300 report(stdout, "%cMTP> QUIT\n", smtp_mode);
305 int SMTP_eom(int sock)
306 /* send a message data terminator to the SMTP listener */
310 SockPrintf(sock,".\r\n");
311 if (outlevel >= O_MONITOR)
312 report(stdout, "%cMTP>. (EOM)\n", smtp_mode);
315 * When doing LMTP, must process many of these at the outer level.
317 if (smtp_mode == 'S')
325 int SMTP_ok(int sock)
326 /* returns status of SMTP connection */
328 while ((SockRead(sock, smtp_response, sizeof(smtp_response)-1)) != -1)
330 int n = strlen(smtp_response);
332 if (smtp_response[strlen(smtp_response)-1] == '\n')
333 smtp_response[strlen(smtp_response)-1] = '\0';
334 if (smtp_response[strlen(smtp_response)-1] == '\r')
335 smtp_response[strlen(smtp_response)-1] = '\0';
338 smtp_response[n] = '\0';
339 if (outlevel >= O_MONITOR)
340 report(stdout, "%cMTP< %s\n", smtp_mode, smtp_response);
341 if ((smtp_response[0] == '1' || smtp_response[0] == '2' || smtp_response[0] == '3') && smtp_response[3] == ' ')
343 else if (smtp_response[3] != '-')
346 if (outlevel >= O_MONITOR)
347 report(stderr, GT_("smtp listener protocol error"));
348 return SM_UNRECOVERABLE;
351 /* smtp.c ends here */