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