]> Pileus Git - ~andy/fetchmail/blob - pop3.c
3ee5489befd1273c961cc1a1b4b58589ef84e42c
[~andy/fetchmail] / pop3.c
1 /*
2  * pop3.c -- POP3 protocol methods
3  *
4  * For license terms, see the file COPYING in this directory.
5  */
6
7 #include  <config.h>
8
9 #include  <stdio.h>
10 #include  <string.h>
11 #include  <ctype.h>
12 #if defined(HAVE_UNISTD_H)
13 #include <unistd.h>
14 #endif
15 #if defined(STDC_HEADERS)
16 #include  <stdlib.h>
17 #endif
18  
19 #include  "fetchmail.h"
20
21 #define PROTOCOL_ERROR  {error(0, 0, "protocol error"); return(PS_ERROR);}
22
23 static int last;
24
25 int pop3_ok (FILE *sockfp, char *argbuf)
26 /* parse command response */
27 {
28     int ok;
29     char buf [POPBUFSIZE+1];
30     char *bufp;
31
32     if (fgets(buf, sizeof(buf), sockfp)) {
33         if (buf[strlen(buf)-1] == '\n')
34             buf[strlen(buf)-1] = '\0';
35         if (buf[strlen(buf)-1] == '\r')
36             buf[strlen(buf)-1] = '\r';
37         if (outlevel == O_VERBOSE)
38             error(0, 0, "POP3< %s", buf);
39
40         bufp = buf;
41         if (*bufp == '+' || *bufp == '-')
42             bufp++;
43         else
44             return(PS_PROTOCOL);
45
46         while (isalpha(*bufp))
47             bufp++;
48         *(bufp++) = '\0';
49
50         if (strcmp(buf,"+OK") == 0)
51             ok = 0;
52         else if (strcmp(buf,"-ERR") == 0)
53             ok = PS_ERROR;
54         else
55             ok = PS_PROTOCOL;
56
57         if (argbuf != NULL)
58             strcpy(argbuf,bufp);
59     }
60     else 
61         ok = PS_SOCKET;
62
63     return(ok);
64 }
65
66 int pop3_getauth(FILE *sockfp, struct query *ctl, char *greeting)
67 /* apply for connection authorization */
68 {
69     /* build MD5 digest from greeting timestamp + password */
70     if (ctl->protocol == P_APOP) 
71     {
72         char *start,*end;
73         char *msg;
74
75         /* find start of timestamp */
76         for (start = greeting;  *start != 0 && *start != '<';  start++)
77             continue;
78         if (*start == 0) {
79             error(0, 0, "Required APOP timestamp not found in greeting");
80             return(PS_AUTHFAIL);
81         }
82
83         /* find end of timestamp */
84         for (end = start;  *end != 0  && *end != '>';  end++)
85             continue;
86         if (*end == 0 || end == start + 1) {
87             error(0, 0, "Timestamp syntax error in greeting");
88             return(PS_AUTHFAIL);
89         }
90         else
91             *++end = '\0';
92
93         /* copy timestamp and password into digestion buffer */
94         msg = (char *)xmalloc((end-start+1) + strlen(ctl->password) + 1);
95         strcpy(msg,start);
96         strcat(msg,ctl->password);
97
98         strcpy(ctl->digest, MD5Digest(msg));
99         free(msg);
100     }
101
102     switch (ctl->protocol) {
103     case P_POP3:
104         if ((gen_transact(sockfp,"USER %s", ctl->remotename)) != 0)
105             PROTOCOL_ERROR
106
107         if ((gen_transact(sockfp, "PASS %s", ctl->password)) != 0)
108             PROTOCOL_ERROR
109         break;
110
111     case P_APOP:
112         if ((gen_transact(sockfp, "APOP %s %s",
113                           ctl->remotename, ctl->digest)) != 0)
114             PROTOCOL_ERROR
115         break;
116
117     default:
118         error(0, 0, "Undefined protocol request in POP3_auth");
119     }
120
121     /* we're approved */
122     return(0);
123 }
124
125 static int pop3_getrange(FILE *sockfp, struct query *ctl, int*countp, int*newp)
126 /* get range of messages to be fetched */
127 {
128     int ok;
129     char buf [POPBUFSIZE+1];
130
131     /* Ensure that the new list is properly empty */
132     ctl->newsaved = (struct idlist *)NULL;
133
134     /* get the total message count */
135     gen_send(sockfp, "STAT");
136     ok = pop3_ok(sockfp, buf);
137     if (ok == 0)
138         sscanf(buf,"%d %*d", countp);
139     else
140         return(ok);
141
142     /*
143      * Newer, RFC-1725-conformant POP servers may not have the LAST command.
144      * We work as hard as possible to hide this ugliness, but it makes 
145      * counting new messages intrinsically quadratic in the worst case.
146      */
147     last = 0;
148     *newp = -1;
149     if (*countp > 0 && !ctl->fetchall)
150     {
151         char id [IDLEN+1];
152
153         gen_send(sockfp,"LAST");
154         ok = pop3_ok(sockfp, buf);
155         if (ok == 0)
156         {
157             if (sscanf(buf, "%d", &last) == 0)
158                 PROTOCOL_ERROR
159             *newp = (*countp - last);
160         }
161         else
162         {
163             /* grab the mailbox's UID list */
164             if ((ok = gen_transact(sockfp, "UIDL")) != 0)
165                 PROTOCOL_ERROR
166             else
167             {
168                 int     num;
169
170                 *newp = 0;
171                 while (fgets(buf, sizeof(buf), sockfp))
172                 {
173                     if (buf[strlen(buf)-1] == '\n')
174                         buf[strlen(buf)-1] = '\0';
175                     if (buf[strlen(buf)-1] == '\r')
176                         buf[strlen(buf)-1] = '\r';
177                     if (outlevel == O_VERBOSE)
178                         error(0, 0, "POP3< %s", buf);
179                     if (buf[0] == '.')
180                         break;
181                     else if (sscanf(buf, "%d %s", &num, id) == 2)
182                     {
183                         save_str(&ctl->newsaved, num, id);
184
185                         /* note: ID comparison is caseblind */
186                         if (!str_in_list(&ctl->oldsaved, id))
187                             (*newp)++;
188                     }
189                 }
190             }
191         }
192     }
193
194     return(0);
195 }
196
197 static int pop3_getsizes(FILE *sockfp, int count, int *sizes)
198 /* capture the sizes of all messages */
199 {
200     int ok;
201
202     if ((ok = gen_transact(sockfp, "LIST")) != 0)
203         return(ok);
204     else
205     {
206         char buf [POPBUFSIZE+1];
207
208         while (fgets(buf, sizeof(buf), sockfp))
209         {
210             int num, size;
211
212             if (buf[strlen(buf)-1] == '\n')
213                 buf[strlen(buf)-1] = '\0';
214             if (buf[strlen(buf)-1] == '\r')
215                 buf[strlen(buf)-1] = '\r';
216             if (outlevel == O_VERBOSE)
217                 error(0, 0, "POP3< %s", buf);
218             if (buf[0] == '.')
219                 break;
220             else if (sscanf(buf, "%d %d", &num, &size) == 2)
221                 sizes[num - 1] = size;
222             else
223                 sizes[num - 1] = -1;
224         }
225
226         return(0);
227     }
228 }
229
230 static int pop3_is_old(FILE *sockfp, struct query *ctl, int num)
231 /* is the given message old? */
232 {
233     if (!ctl->oldsaved)
234         return (num <= last);
235     else
236         /* note: ID comparison is caseblind */
237         return (str_in_list(&ctl->oldsaved,
238                             str_find (&ctl->newsaved, num)));
239 }
240
241 static int pop3_fetch(FILE *sockfp, int number, int *lenp)
242 /* request nth message */
243 {
244     int ok;
245     char buf [POPBUFSIZE+1], *cp;
246
247     gen_send(sockfp, "RETR %d", number);
248     if ((ok = pop3_ok(sockfp, buf)) != 0)
249         return(ok);
250     /* look for "nnn octets" -- there may or may not be preceding cruft */
251     if ((cp = strstr(buf, " octets")) == (char *)NULL)
252         *lenp = 0;
253     else
254     {
255         while (--cp > buf && isdigit(*cp))
256             continue;
257         *lenp = atoi(cp);
258     }
259     return(0);
260 }
261
262 static int pop3_delete(FILE *sockfp, struct query *ctl, int number)
263 /* delete a given message */
264 {
265     return(gen_transact(sockfp, "DELE %d", number));
266 }
267
268 const static struct method pop3 =
269 {
270     "POP3",             /* Post Office Protocol v3 */
271     110,                /* standard POP3 port */
272     0,                  /* this is not a tagged protocol */
273     1,                  /* this uses a message delimiter */
274     pop3_ok,            /* parse command response */
275     pop3_getauth,       /* get authorization */
276     pop3_getrange,      /* query range of messages */
277     pop3_getsizes,      /* we can get a list of sizes */
278     pop3_is_old,        /* how do we tell a message is old? */
279     pop3_fetch,         /* request given message */
280     NULL,               /* no message trailer */
281     pop3_delete,        /* how to delete a message */
282     NULL,               /* no POP3 expunge command */
283     "QUIT",             /* the POP3 exit command */
284 };
285
286 int doPOP3 (struct query *ctl)
287 /* retrieve messages using POP3 */
288 {
289     if (ctl->mailbox[0]) {
290         fprintf(stderr,"Option --remote is not supported with POP3\n");
291         return(PS_SYNTAX);
292     }
293     peek_capable = FALSE;
294     return(do_protocol(ctl, &pop3));
295 }
296
297 /* pop3.c ends here */