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