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