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