]> Pileus Git - ~andy/fetchmail/blob - pop3.c
The great name change.
[~andy/fetchmail] / pop3.c
1 /* Copyright 1993-95 by Carl Harris, Jr. Copyright 1996 by Eric S. Raymond
2  * All rights reserved.
3  * For license terms, see the file COPYING in this directory.
4  */
5
6 /***********************************************************************
7   module:       pop3.c
8   project:      fetchmail
9   programmer:   Carl Harris, ceharris@mal.com
10                 Hacks and bug fixes by esr.
11   description:  POP3 client code.
12
13  ***********************************************************************/
14
15 #include  <config.h>
16
17 #include  <stdio.h>
18 #if defined(STDC_HEADERS)
19 #include  <string.h>
20 #endif
21 #if defined(HAVE_UNISTD_H)
22 #include  <unistd.h>
23 #endif
24 #include  <errno.h>
25
26 #include  "socket.h"
27 #include  "fetchmail.h"
28
29 #ifdef HAVE_PROTOTYPES
30 /* prototypes for internal functions */
31 int POP3_sendSTAT (int *msgcount, int socket);
32 int POP3_sendLAST (int *last, int socket);
33 int POP3_sendUIDL (int num, int socket, char **cp);
34 int POP3_BuildDigest (char *buf, struct hostrec *options);
35 #endif
36
37
38 /*********************************************************************
39
40  Method declarations for POP3 
41
42  *********************************************************************/
43
44 int pop3_ok (argbuf,socket)
45 /* parse command response */
46 char *argbuf;
47 int socket;
48 {
49   int ok;
50   char buf [POPBUFSIZE+1];
51   char *bufp;
52
53   if (SockGets(socket, buf, sizeof(buf)) >= 0) {
54     if (outlevel == O_VERBOSE)
55       fprintf(stderr,"%s\n",buf);
56
57     bufp = buf;
58     if (*bufp == '+' || *bufp == '-')
59       bufp++;
60     else
61       return(PS_PROTOCOL);
62
63     while (isalpha(*bufp))
64       bufp++;
65     *(bufp++) = '\0';
66
67     if (strcmp(buf,"+OK") == 0)
68       ok = 0;
69     else if (strcmp(buf,"-ERR") == 0)
70       ok = PS_ERROR;
71     else
72       ok = PS_PROTOCOL;
73
74     if (argbuf != NULL)
75       strcpy(argbuf,bufp);
76   }
77   else 
78     ok = PS_SOCKET;
79
80   return(ok);
81 }
82
83 int pop3_getauth(socket, queryctl, greeting)
84 /* apply for connection authorization */
85 int socket;
86 struct hostrec *queryctl;
87 char *greeting;
88 {
89     char buf [POPBUFSIZE+1];
90
91 #if defined(HAVE_APOP_SUPPORT)
92     /* build MD5 digest from greeting timestamp + password */
93     if (queryctl->protocol == P_APOP) 
94         if (POP3_BuildDigest(greeting,queryctl) != 0) {
95             return(PS_AUTHFAIL);
96         }
97 #endif  /* HAVE_APOP_SUPPORT */
98
99     switch (queryctl->protocol) {
100     case P_POP3:
101         SockPrintf(socket,"USER %s\r\n",queryctl->remotename);
102         if (outlevel == O_VERBOSE)
103             fprintf(stderr,"> USER %s\n",queryctl->remotename);
104         if (pop3_ok(buf,socket) != 0)
105             goto badAuth;
106
107         if (queryctl->rpopid[0])
108         {
109             SockPrintf(socket, "RPOP %s\r\n", queryctl->rpopid);
110             if (outlevel == O_VERBOSE)
111                 fprintf(stderr,"> RPOP %s %s\n",queryctl->rpopid);
112         }
113         else
114         {
115             SockPrintf(socket,"PASS %s\r\n",queryctl->password);
116             if (outlevel == O_VERBOSE)
117                 fprintf(stderr,"> PASS password\n");
118         }
119         if (pop3_ok(buf,socket) != 0)
120             goto badAuth;
121         break;
122
123 #if defined(HAVE_APOP_SUPPORT)
124     case P_APOP:
125         SockPrintf(socket,"APOP %s %s\r\n", 
126                    queryctl->remotename, queryctl->digest);
127         if (outlevel == O_VERBOSE)
128             fprintf(stderr,"> APOP %s %s\n",queryctl->remotename, queryctl->digest);
129         if (pop3_ok(buf,socket) != 0) 
130             goto badAuth;
131         break;
132 #endif  /* HAVE_APOP_SUPPORT */
133
134     default:
135         fprintf(stderr,"Undefined protocol request in POP3_auth\n");
136     }
137
138     /* we're approved */
139     return(0);
140
141     /*NOTREACHED*/
142
143 badAuth:
144     if (outlevel > O_SILENT && outlevel < O_VERBOSE)
145         fprintf(stderr,"%s\n",buf);
146     else
147         ; /* say nothing */
148
149     return(PS_ERROR);
150 }
151
152 static int use_uidl;
153
154 static pop3_getrange(socket, queryctl, countp, firstp)
155 /* get range of messages to be fetched */
156 int socket;
157 struct hostrec *queryctl;
158 int *countp;
159 int *firstp;
160 {
161   int ok;
162
163   ok = POP3_sendSTAT(countp,socket);
164   if (ok != 0) {
165     return(ok);
166   }
167
168   /*
169    * Ask for number of last message retrieved.  
170    * Newer, RFC-1760-conformant POP servers may not have the LAST command.
171    * Therefore we don't croak if we get a nonzero return.  Instead, send
172    * UIDL and try to find the last received ID stored for this host in
173    * the list we get back.
174    */
175   *firstp = 1;
176   use_uidl = 0;
177   if (!queryctl->fetchall) {
178     char buf [POPBUFSIZE+1];
179     char id [IDLEN+1];
180     int num;
181
182     /* try LAST first */
183     ok = POP3_sendLAST(firstp, socket);
184     use_uidl = (ok != 0); 
185
186     /* otherwise, if we have a stored last ID for this host,
187      * send UIDL and search the returned list for it
188      */ 
189     if (use_uidl && queryctl->lastid[0]) {
190       if ((ok = POP3_sendUIDL(-1, socket, 0)) == 0) {
191           while (SockGets(socket, buf, sizeof(buf)) >= 0) {
192             if (outlevel == O_VERBOSE)
193               fprintf(stderr,"%s\n",buf);
194             if (strcmp(buf, ".\n") == 0) {
195               break;
196             }
197             if (sscanf(buf, "%d %s\n", &num, id) == 2)
198                 if (strcmp(id, queryctl->lastid) == 0)
199                     *firstp = num;
200           }
201        }
202     }
203
204     if (ok == 0)
205       (*firstp)++;
206   }
207
208   return(0);
209 }
210
211 static int pop3_fetch(socket, number, limit, lenp)
212 /* request nth message */
213 int socket;
214 int number;
215 int limit;
216 int *lenp; 
217 {
218     *lenp = 0;
219     if (limit) 
220         return(gen_transact(socket, "TOP %d %d", number, limit));
221       else 
222         return(gen_transact(socket, "RETR %d", number));
223 }
224
225 static pop3_trail(socket, queryctl, number)
226 /* update the last-seen field for this host */
227 int socket;
228 struct hostrec *queryctl;
229 int number;
230 {
231     char *cp;
232     int ok = 0;
233
234     if (use_uidl && (ok = POP3_sendUIDL(number, socket, &cp)) == 0)
235         (void) strcpy(queryctl->lastid, cp);
236     return(ok);
237 }
238
239 static struct method pop3 =
240 {
241     "POP3",                             /* Post Office Protocol v3 */
242     110,                                /* standard POP3 port */
243     0,                                  /* this is not a tagged protocol */
244     1,                                  /* this uses a message delimiter */
245     pop3_ok,                            /* parse command response */
246     pop3_getauth,                       /* get authorization */
247     pop3_getrange,                      /* query range of messages */
248     pop3_fetch,                         /* request given message */
249     pop3_trail,                         /* eat message trailer */
250     "DELE %d",                          /* set POP3 delete flag */
251     NULL,                               /* the POP3 expunge command */
252     "QUIT",                             /* the POP3 exit command */
253 };
254
255 int doPOP3 (queryctl)
256 /* retrieve messages using POP3 */
257 struct hostrec *queryctl;
258 {
259     if (queryctl->remotefolder[0]) {
260         fprintf(stderr,"Option --remote is not supported with POP3\n");
261         return(PS_SYNTAX);
262     }
263
264     return(do_protocol(queryctl, &pop3));
265 }
266
267 /*********************************************************************
268   function:      POP3_sendSTAT
269   description:   send the STAT command to the POP3 server to find
270                  out how many messages are waiting.
271   arguments:     
272     count        pointer to an integer to receive the message count.
273     socket       socket to which the POP3 server is connected.
274
275   return value:  return code from POP3_OK.
276   calls:         POP3_OK, SockPrintf
277   globals:       reads outlevel.
278  *********************************************************************/
279
280 int POP3_sendSTAT (msgcount,socket)
281 int *msgcount;
282 int socket;
283 {
284   int ok;
285   char buf [POPBUFSIZE+1];
286   int totalsize;
287
288   SockPrintf(socket,"STAT\r\n");
289   if (outlevel == O_VERBOSE)
290     fprintf(stderr,"> STAT\n");
291   
292   ok = pop3_ok(buf,socket);
293   if (ok == 0)
294     sscanf(buf,"%d %d",msgcount,&totalsize);
295   else if (outlevel > O_SILENT && outlevel < O_VERBOSE)
296     fprintf(stderr,"%s\n",buf);
297
298   return(ok);
299 }
300
301 /******************************************************************
302   function:     POP3_sendLAST
303   description:  send the LAST command to the server, which should
304                 return the number of the last message number retrieved 
305                 from the server.
306   arguments:
307     last        integer buffer to receive last message# 
308
309   ret. value:   zero if success, else status code.
310   globals:      SockPrintf, pop3_ok.
311   calls:        reads outlevel.
312  *****************************************************************/
313
314 int POP3_sendLAST (last, socket)
315 int *last;
316 int socket;
317 {
318   int ok;
319   char buf [POPBUFSIZE];
320
321   SockPrintf(socket,"LAST\r\n");
322   if (outlevel == O_VERBOSE)
323     fprintf(stderr,"> LAST\n");
324
325   ok = pop3_ok(buf,socket);
326   if (ok == 0 && sscanf(buf,"%d",last) == 0)
327     ok = PS_ERROR;
328
329   if (ok != 0 && outlevel > O_SILENT) 
330     fprintf(stderr,"Server says '%s' to LAST command.\n",buf);
331
332   return(ok);
333 }
334
335 /******************************************************************
336   function:     POP3_sendUIDL
337   description:  send the UIDL command to the server, 
338
339   arguments:
340     num         number of message to query (may be -1)
341
342   ret. value:   zero if success, else status code.
343   globals:      SockPrintf, pop3_ok.
344   calls:        reads outlevel.
345  *****************************************************************/
346
347 int POP3_sendUIDL (num, socket, cp)
348 int num;
349 int socket;
350 char **cp;
351 {
352   int ok;
353   char buf [POPBUFSIZE];
354   static char id[IDLEN];
355
356   (void) strcpy(buf, "UIDL\r\n");
357   if (num > -1)
358     (void) sprintf(buf, "UIDL %d\r\n", num);
359  
360   SockPrintf(socket, buf);
361   if (outlevel == O_VERBOSE)
362     fprintf(stderr,"> %s", buf);
363
364   ok = pop3_ok(buf,socket);
365   if (ok != 0 && outlevel > O_SILENT) 
366     fprintf(stderr,"Server says '%s' to UIDL command.\n",buf);
367
368   if (cp) {
369     sscanf(buf, "%*d %s\n", id);
370     *cp = id;
371   }
372   return(ok);
373 }
374
375
376 /******************************************************************
377   function:     POP3_BuildDigest
378   description:  Construct the MD5 digest for the current session,
379                 using the user-specified password, and the time
380                 stamp in the POP3 greeting.
381   arguments:
382     buf         greeting string
383     queryctl    merged options record.
384
385   ret. value:   zero on success, nonzero if no timestamp found in
386                 greeting.
387   globals:      none.
388   calls:        MD5Digest.
389  *****************************************************************/
390
391 #if defined(HAVE_APOP_SUPPORT)
392 POP3_BuildDigest (buf,queryctl)
393 char *buf;
394 struct hostrec *queryctl;
395 {
396   char *start,*end;
397   char *msg;
398
399   /* find start of timestamp */
400   for (start = buf;  *start != 0 && *start != '<';  start++)
401     ;
402   if (*start == 0) {
403     fprintf(stderr,"Required APOP timestamp not found in greeting\n");
404     return(-1);
405   }
406
407   /* find end of timestamp */
408   for (end = start;  *end != 0  && *end != '>';  end++)
409     ;
410   if (*end == 0 || (end - start - 1) == 1) {
411     fprintf(stderr,"Timestamp syntax error in greeting\n");
412     return(-1);
413   }
414
415   /* copy timestamp and password into digestion buffer */
416   msg = (char *) malloc((end-start-1) + strlen(queryctl->password) + 1);
417   *(++end) = 0;
418   strcpy(msg,start);
419   strcat(msg,queryctl->password);
420
421   strcpy(queryctl->digest, MD5Digest(msg));
422   free(msg);
423   return(0);
424 }
425 #endif  /* HAVE_APOP_SUPPORT */
426