]> Pileus Git - ~andy/fetchmail/blob - smtp.c
Update to 6.3.5.
[~andy/fetchmail] / smtp.c
1 /*
2  * smtp.c -- code for speaking SMTP to a listener port
3  *
4  * Concept due to Harry Hochheiser.  Implementation by ESR.  Cleanup and
5  * strict RFC821 compliance by Cameron MacPherson.
6  *
7  * Copyright 1997 Eric S. Raymond
8  * For license terms, see the file COPYING in this directory.
9  */
10
11 #include "config.h"
12 #include "fetchmail.h"
13
14 #include <stdio.h>
15 #include <unistd.h>
16 #include <string.h>
17 #include <signal.h>
18 #include "socket.h"
19 #include "smtp.h"
20 #include "i18n.h"
21
22 struct opt
23 {
24     const char *name;
25     int value;
26 };
27
28 static struct opt extensions[] =
29 {
30     {"8BITMIME",        ESMTP_8BITMIME},
31     {"SIZE",            ESMTP_SIZE},
32     {"ETRN",            ESMTP_ETRN},
33     {"AUTH ",           ESMTP_AUTH},
34 #ifdef ODMR_ENABLE
35     {"ATRN",            ESMTP_ATRN},
36 #endif /* ODMR_ENABLE */
37     {(char *)NULL, 0},
38 };
39
40 char smtp_response[MSGBUFSIZE];
41
42 int SMTP_helo(int sock, char smtp_mode, const char *host)
43 /* send a "HELO" message to the SMTP listener */
44 {
45   int ok;
46
47   SockPrintf(sock,"HELO %s\r\n", host);
48   if (outlevel >= O_MONITOR)
49       report(stdout, "%cMTP> HELO %s\n", smtp_mode, host);
50   ok = SMTP_ok(sock, smtp_mode);
51   return ok;
52 }
53
54 static void SMTP_auth_error(int sock, const char *msg)
55 {
56     SockPrintf(sock, "*\r\n");
57     SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
58     if (outlevel >= O_MONITOR) report(stdout, msg);
59 }
60
61 static void SMTP_auth(int sock, char smtp_mode, char *username, char *password, char *buf)
62 /* ESMTP Authentication support for fetchmail by Wojciech Polak */
63 {       
64         int c;
65         char *p = 0;
66         char b64buf[512];
67         char tmp[512];
68
69         if (!username || !password) return;
70
71         memset(b64buf, 0, sizeof(b64buf));
72         memset(tmp, 0, sizeof(tmp));
73
74         if (strstr(buf, "CRAM-MD5")) {
75                 unsigned char digest[16];
76                 memset(digest, 0, sizeof(digest));
77
78                 if (outlevel >= O_MONITOR)
79                         report(stdout, GT_("ESMTP CRAM-MD5 Authentication...\n"));
80                 SockPrintf(sock, "AUTH CRAM-MD5\r\n");
81                 SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
82                 strncpy(tmp, smtp_response, sizeof(tmp));
83                 tmp[sizeof(tmp)-1] = '\0';
84
85                 if (strncmp(tmp, "334 ", 4)) { /* Server rejects AUTH */
86                         SMTP_auth_error(sock, GT_("Server rejected the AUTH command.\n"));
87                         return;
88                 }
89
90                 p = strchr(tmp, ' ');
91                 p++;
92                 /* (hmh) from64tobits will not NULL-terminate strings! */
93                 if (from64tobits(b64buf, p, sizeof(b64buf) - 1) <= 0) {
94                         SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
95                         return;
96                 }
97                 if (outlevel >= O_DEBUG)
98                         report(stdout, GT_("Challenge decoded: %s\n"), b64buf);
99                 hmac_md5(password, strlen(password),
100                          b64buf, strlen(b64buf), digest, sizeof(digest));
101                 snprintf(tmp, sizeof(tmp),
102                 "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
103                 username,  digest[0], digest[1], digest[2], digest[3],
104                 digest[4], digest[5], digest[6], digest[7], digest[8],
105                 digest[9], digest[10], digest[11], digest[12], digest[13],
106                 digest[14], digest[15]);
107
108                 to64frombits(b64buf, tmp, strlen(tmp));
109                 SockPrintf(sock, "%s\r\n", b64buf);
110                 SMTP_ok(sock, smtp_mode);
111         }
112         else if (strstr(buf, "PLAIN")) {
113                 int len;
114                 if (outlevel >= O_MONITOR)
115                         report(stdout, GT_("ESMTP PLAIN Authentication...\n"));
116                 snprintf(tmp, sizeof(tmp), "^%s^%s", username, password);
117
118                 len = strlen(tmp);
119                 for (c = len - 1; c >= 0; c--)
120                 {
121                         if (tmp[c] == '^')
122                                 tmp[c] = '\0';
123                 }
124                 to64frombits(b64buf, tmp, len);
125                 SockPrintf(sock, "AUTH PLAIN %s\r\n", b64buf);
126                 SMTP_ok(sock, smtp_mode);
127         }
128         else if (strstr(buf, "LOGIN")) {
129                 if (outlevel >= O_MONITOR)
130                         report(stdout, GT_("ESMTP LOGIN Authentication...\n"));
131                 SockPrintf(sock, "AUTH LOGIN\r\n");
132                 SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
133                 strncpy(tmp, smtp_response, sizeof(tmp));
134                 tmp[sizeof(tmp)-1] = '\0';
135
136                 if (strncmp(tmp, "334 ", 4)) { /* Server rejects AUTH */
137                         SMTP_auth_error(sock, GT_("Server rejected the AUTH command.\n"));
138                         return;
139                 }
140
141                 p = strchr(tmp, ' ');
142                 p++;
143                 if (from64tobits(b64buf, p, sizeof(b64buf) - 1) <= 0) {
144                         SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
145                         return;
146                 }
147                 to64frombits(b64buf, username, strlen(username));
148                 SockPrintf(sock, "%s\r\n", b64buf);
149                 SockRead(sock, smtp_response, sizeof(smtp_response) - 1);
150                 strncpy(tmp, smtp_response, sizeof(tmp));
151                 tmp[sizeof(tmp)-1] = '\0';
152                 p = strchr(tmp, ' ');
153                 if (!p) {
154                         SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
155                         return;
156                 }
157                 p++;
158                 memset(b64buf, 0, sizeof(b64buf));
159                 if (from64tobits(b64buf, p, sizeof(b64buf) - 1) <= 0) {
160                         SMTP_auth_error(sock, GT_("Bad base64 reply from server.\n"));
161                         return;
162                 }
163                 to64frombits(b64buf, password, strlen(password));
164                 SockPrintf(sock, "%s\r\n", b64buf);
165                 SMTP_ok(sock, smtp_mode);
166         }
167         return;
168 }
169
170 int SMTP_ehlo(int sock, char smtp_mode, const char *host, char *name, char *password, int *opt)
171 /* send a "EHLO" message to the SMTP listener, return extension status bits */
172 {
173   struct opt *hp;
174   char auth_response[511];
175
176   SockPrintf(sock,"%cHLO %s\r\n", (smtp_mode == 'S') ? 'E' : smtp_mode, host);
177   if (outlevel >= O_MONITOR)
178       report(stdout, "%cMTP> %cHLO %s\n", 
179             smtp_mode, (smtp_mode == 'S') ? 'E' : smtp_mode, host);
180
181   *opt = 0;
182   while ((SockRead(sock, smtp_response, sizeof(smtp_response)-1)) != -1)
183   {
184       int  n = strlen(smtp_response);
185
186       if (smtp_response[strlen(smtp_response)-1] == '\n')
187           smtp_response[strlen(smtp_response)-1] = '\0';
188       if (smtp_response[strlen(smtp_response)-1] == '\r')
189           smtp_response[strlen(smtp_response)-1] = '\0';
190       if (n < 4)
191           return SM_ERROR;
192       smtp_response[n] = '\0';
193       if (outlevel >= O_MONITOR)
194           report(stdout, "%cMTP< %s\n", smtp_mode, smtp_response);
195       for (hp = extensions; hp->name; hp++)
196           if (!strncasecmp(hp->name, smtp_response+4, strlen(hp->name))) {
197               *opt |= hp->value;
198               if (strncmp(hp->name, "AUTH ", 5) == 0)
199                 strncpy(auth_response, smtp_response, sizeof(auth_response));
200                 auth_response[sizeof(auth_response)-1] = '\0';
201           }
202       if ((smtp_response[0] == '1' || smtp_response[0] == '2' || smtp_response[0] == '3') && smtp_response[3] == ' ') {
203           if (*opt & ESMTP_AUTH)
204                 SMTP_auth(sock, smtp_mode, name, password, auth_response);
205           return SM_OK;
206       }
207       else if (smtp_response[3] != '-')
208           return SM_ERROR;
209   }
210   return SM_UNRECOVERABLE;
211 }
212
213 int SMTP_from(int sock, char smtp_mode, const char *from, const char *opts)
214 /* send a "MAIL FROM:" message to the SMTP listener */
215 {
216     int ok;
217     char buf[MSGBUFSIZE];
218
219     if (from[0]=='<')
220         snprintf(buf, sizeof(buf), "MAIL FROM:%s", from);
221     else
222         snprintf(buf, sizeof(buf), "MAIL FROM:<%s>", from);
223     if (opts)
224         snprintf(buf+strlen(buf), sizeof(buf)-strlen(buf), "%s", opts);
225     SockPrintf(sock,"%s\r\n", buf);
226     if (outlevel >= O_MONITOR)
227         report(stdout, "%cMTP> %s\n", smtp_mode, buf);
228     ok = SMTP_ok(sock, smtp_mode);
229     return ok;
230 }
231
232 int SMTP_rcpt(int sock, char smtp_mode, const char *to)
233 /* send a "RCPT TO:" message to the SMTP listener */
234 {
235   int ok;
236
237   SockPrintf(sock,"RCPT TO:<%s>\r\n", to);
238   if (outlevel >= O_MONITOR)
239       report(stdout, "%cMTP> RCPT TO:<%s>\n", smtp_mode, to);
240   ok = SMTP_ok(sock, smtp_mode);
241   return ok;
242 }
243
244 int SMTP_data(int sock, char smtp_mode)
245 /* send a "DATA" message to the SMTP listener */
246 {
247   int ok;
248
249   SockPrintf(sock,"DATA\r\n");
250   if (outlevel >= O_MONITOR)
251       report(stdout, "%cMTP> DATA\n", smtp_mode);
252   ok = SMTP_ok(sock, smtp_mode);
253   return ok;
254 }
255
256 int SMTP_rset(int sock, char smtp_mode)
257 /* send a "RSET" message to the SMTP listener */
258 {
259   int ok;
260
261   SockPrintf(sock,"RSET\r\n");
262   if (outlevel >= O_MONITOR)
263       report(stdout, "%cMTP> RSET\n", smtp_mode);
264   ok = SMTP_ok(sock, smtp_mode);
265   return ok;
266 }
267
268 int SMTP_quit(int sock, char smtp_mode)
269 /* send a "QUIT" message to the SMTP listener */
270 {
271   int ok;
272
273   SockPrintf(sock,"QUIT\r\n");
274   if (outlevel >= O_MONITOR)
275       report(stdout, "%cMTP> QUIT\n", smtp_mode);
276   ok = SMTP_ok(sock, smtp_mode);
277   return ok;
278 }
279
280 int SMTP_eom(int sock, char smtp_mode)
281 /* send a message data terminator to the SMTP listener */
282 {
283   int ok;
284
285   SockPrintf(sock,".\r\n");
286   if (outlevel >= O_MONITOR)
287       report(stdout, "%cMTP>. (EOM)\n", smtp_mode);
288
289   /* 
290    * When doing LMTP, must process many of these at the outer level. 
291    */
292   if (smtp_mode == 'S')
293       ok = SMTP_ok(sock, smtp_mode);
294   else
295       ok = SM_OK;
296
297   return ok;
298 }
299
300 time_t last_smtp_ok = 0;
301
302 int SMTP_ok(int sock, char smtp_mode)
303 /* returns status of SMTP connection */
304 {
305     SIGHANDLERTYPE alrmsave;
306
307     /* set an alarm for smtp ok */
308     alrmsave = set_signal_handler(SIGALRM, null_signal_handler);
309     set_timeout(mytimeout);
310
311     while ((SockRead(sock, smtp_response, sizeof(smtp_response)-1)) != -1)
312     {
313         int n;
314
315         /* restore alarm */
316         set_timeout(0);
317         set_signal_handler(SIGALRM, alrmsave);
318
319         n = strlen(smtp_response);
320         if (n > 0 && smtp_response[n-1] == '\n')
321             n--;
322         if (n > 0 && smtp_response[n-1] == '\r')
323             n--;
324         smtp_response[n] = '\0';
325         if (outlevel >= O_MONITOR)
326             report(stdout, "%cMTP< %s\n", smtp_mode, smtp_response);
327         if (n < 4 ||
328             (smtp_response[3] != ' ' && smtp_response[3] != '-'))
329         {
330             if (outlevel >= O_MONITOR)
331                 report(stderr, GT_("smtp listener protocol error\n"));
332             return SM_UNRECOVERABLE;
333         }
334
335         last_smtp_ok = time((time_t *) NULL);
336
337         if ((smtp_response[0] == '1' || smtp_response[0] == '2' || smtp_response[0] == '3') &&
338             smtp_response[3] == ' ')
339             return SM_OK;
340         else if (smtp_response[3] != '-')
341             return SM_ERROR;
342
343         /* set an alarm for smtp ok */
344         set_signal_handler(SIGALRM, null_signal_handler);
345         set_timeout(mytimeout);
346
347     }
348
349     /* restore alarm */
350     set_timeout(0);
351     set_signal_handler(SIGALRM, alrmsave);
352
353     if (outlevel >= O_MONITOR)
354         report(stderr, GT_("smtp listener protocol error\n"));
355     return SM_UNRECOVERABLE;
356 }
357
358 /* smtp.c ends here */