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