]> Pileus Git - ~andy/fetchmail/blob - pop3.c
a2f7d5f09132007a3dcebe2fdc95a9453125bbef
[~andy/fetchmail] / pop3.c
1 /*
2  * pop3.c -- POP3 protocol methods
3  *
4  * Copyright 1998 by Eric S. Raymond.
5  * For license terms, see the file COPYING in this directory.
6  */
7
8 #include  "config.h"
9 #ifdef POP3_ENABLE
10 #include  <stdio.h>
11 #include  <string.h>
12 #include  <ctype.h>
13 #if defined(HAVE_UNISTD_H)
14 #include <unistd.h>
15 #endif
16 #if defined(STDC_HEADERS)
17 #include  <stdlib.h>
18 #endif
19  
20 #include  "fetchmail.h"
21 #include  "socket.h"
22 #include  "i18n.h"
23
24 #if OPIE_ENABLE
25 #include <opie.h>
26 #endif /* OPIE_ENABLE */
27
28 #ifndef strstr          /* glibc-2.1 declares this as a macro */
29 extern char *strstr();  /* needed on sysV68 R3V7.1. */
30 #endif /* strstr */
31
32 static int last;
33 #ifdef SDPS_ENABLE
34 char *sdps_envfrom;
35 char *sdps_envto;
36 #endif /* SDPS_ENABLE */
37
38 #if OPIE_ENABLE
39 static char lastok[POPBUFSIZE+1];
40 #endif /* OPIE_ENABLE */
41
42 #define DOTLINE(s)      (s[0] == '.' && (s[1]=='\r'||s[1]=='\n'||s[1]=='\0'))
43
44 static int pop3_ok (int sock, char *argbuf)
45 /* parse command response */
46 {
47     int ok;
48     char buf [POPBUFSIZE+1];
49     char *bufp;
50
51     if ((ok = gen_recv(sock, buf, sizeof(buf))) == 0)
52     {   bufp = buf;
53         if (*bufp == '+' || *bufp == '-')
54             bufp++;
55         else
56             return(PS_PROTOCOL);
57
58         while (isalpha(*bufp))
59             bufp++;
60
61         if (*bufp)
62           *(bufp++) = '\0';
63
64         if (strcmp(buf,"+OK") == 0)
65         {
66 #if OPIE_ENABLE
67             strcpy(lastok, bufp);
68 #endif /* OPIE_ENABLE */
69             ok = 0;
70         }
71         else if (strncmp(buf,"-ERR", 4) == 0)
72         {
73             if (stage == STAGE_FETCH)
74                 ok = PS_TRANSIENT;
75             else if (stage > STAGE_GETAUTH)
76                 ok = PS_PROTOCOL;
77             /*
78              * We're checking for "lock busy", "unable to lock", 
79              * "already locked", "wait a few minutes" etc. here. 
80              * This indicates that we have to wait for the server to
81              * unwedge itself before we can poll again.
82              *
83              * PS_LOCKBUSY check empirically verified with two recent
84              * versions of the Berkeley popper; QPOP (version 2.2)  and
85              * QUALCOMM Pop server derived from UCB (version 2.1.4-R3)
86              * These are caught by the case-indifferent "lock" check.
87              * The "wait" catches "mail storage services unavailable,
88              * wait a few minutes and try again" on the InterMail server.
89              *
90              * If these aren't picked up on correctly, fetchmail will 
91              * think there is an authentication failure and wedge the
92              * connection in order to prevent futile polls.
93              *
94              * Gad, what a kluge.
95              */
96             else if (strstr(bufp,"lock")
97                      || strstr(bufp,"Lock")
98                      || strstr(bufp,"LOCK")
99                      || strstr(bufp,"wait")
100                      /* these are blessed by RFC 2449 */
101                      || strstr(bufp,"[IN-USE]")||strstr(bufp,"[LOGIN-DELAY]"))
102                 ok = PS_LOCKBUSY;
103             else if ((strstr(bufp,"Service")
104                      || strstr(bufp,"service"))
105                          && (strstr(bufp,"unavailable")))
106                 ok = PS_SERVBUSY;
107             else
108                 ok = PS_AUTHFAIL;
109             /*
110              * We always want to pass the user lock-busy messages, because
111              * they're red flags.  Other stuff (like AUTH failures on non-
112              * RFC1734 servers) only if we're debugging.
113              */
114             if (*bufp && (ok == PS_LOCKBUSY || outlevel >= O_MONITOR))
115               report(stderr, "%s\n", bufp);
116         }
117         else
118             ok = PS_PROTOCOL;
119
120         if (argbuf != NULL)
121             strcpy(argbuf,bufp);
122     }
123
124     return(ok);
125 }
126
127 static int pop3_getauth(int sock, struct query *ctl, char *greeting)
128 /* apply for connection authorization */
129 {
130     int ok;
131     char *start,*end;
132     char *msg;
133 #if OPIE_ENABLE
134     char *challenge;
135 #endif /* OPIE_ENABLE */
136 #if defined(GSSAPI)
137     flag has_gssapi = FALSE;
138 #endif /* defined(GSSAPI) */
139 #if defined(KERBEROS_V4) || defined(KERBEROS_V5)
140     flag has_kerberos = FALSE;
141 #endif /* defined(KERBEROS_V4) || defined(KERBEROS_V5) */
142     flag has_cram = FALSE;
143 #ifdef OPIE_ENABLE
144     flag has_otp = FALSE;
145 #endif /* OPIE_ENABLE */
146 #ifdef SSL_ENABLE
147     flag has_ssl = FALSE;
148     flag did_stls = FALSE;
149 #endif /* SSL_ENABLE */
150
151 #ifdef SDPS_ENABLE
152     /*
153      * This needs to catch both demon.co.uk and demon.net.
154      * If we see either, and we're in multidrop mode, try to use
155      * the SDPS *ENV extension.
156      */
157     if (!(ctl->server.sdps) && MULTIDROP(ctl) && strstr(greeting, "demon."))
158         ctl->server.sdps = TRUE;
159 #endif /* SDPS_ENABLE */
160
161     switch (ctl->server.protocol) {
162     case P_POP3:
163 #ifdef RPA_ENABLE
164         /* CompuServe POP3 Servers as of 990730 want AUTH first for RPA */
165         if (strstr(ctl->remotename, "@compuserve.com"))
166         {
167             /* AUTH command should return a list of available mechanisms */
168             if (gen_transact(sock, "AUTH") == 0)
169             {
170                 char buffer[10];
171                 flag has_rpa = FALSE;
172
173                 while ((ok = gen_recv(sock, buffer, sizeof(buffer))) == 0)
174                 {
175                     if (DOTLINE(buffer))
176                         break;
177                     if (strncasecmp(buffer, "rpa", 3) == 0)
178                         has_rpa = TRUE;
179                 }
180                 if (has_rpa && !POP3_auth_rpa(ctl->remotename, 
181                                               ctl->password, sock))
182                     return(PS_SUCCESS);
183             }
184
185             return(PS_AUTHFAIL);
186         }
187 #endif /* RPA_ENABLE */
188
189         /*
190          * CAPA command may return a list including available
191          * authentication mechanisms.  if it doesn't, no harm done, we
192          * just fall back to a plain login.  Note that this code 
193          * latches the server's authentication type, so that in daemon mode
194          * the CAPA check only needs to be done once at start of run.
195          *
196          * If CAPA fails, then force the authentication method to PASSORD
197          * and repoll immediately.
198          *
199          * These authentication methods are blessed by RFC1734,
200          * describing the POP3 AUTHentication command.
201          */
202         if (ctl->server.authenticate == A_ANY)
203         {
204             ok = gen_transact(sock, "CAPA");
205             if (ok == PS_SUCCESS)
206             {
207                 char buffer[64];
208
209                 /* determine what authentication methods we have available */
210                 while ((ok = gen_recv(sock, buffer, sizeof(buffer))) == 0)
211                 {
212                     if (DOTLINE(buffer))
213                         break;
214 #ifdef SSL_ENABLE
215                     if (strstr(buffer, "STLS"))
216                         has_ssl = TRUE;
217 #endif /* SSL_ENABLE */
218 #if defined(GSSAPI)
219                     if (strstr(buffer, "GSSAPI"))
220                         has_gssapi = TRUE;
221 #endif /* defined(GSSAPI) */
222 #if defined(KERBEROS_V4)
223                     if (strstr(buffer, "KERBEROS_V4"))
224                         has_kerberos = TRUE;
225 #endif /* defined(KERBEROS_V4)  */
226 #ifdef OPIE_ENABLE
227                     if (strstr(buffer, "X-OTP"))
228                         has_otp = TRUE;
229 #endif /* OPIE_ENABLE */
230                     if (strstr(buffer, "CRAM-MD5"))
231                         has_cram = TRUE;
232                 }
233             }
234             /* we are in STAGE_GETAUTH! */
235             else if (ok == PS_AUTHFAIL ||
236                 /* Some servers directly close the socket. However, if we
237                  * have already authenticated before, then a previous CAPA
238                  * must have succeeded. In that case, treat this as a
239                  * genuine socket error and do not change the auth method.
240                  */
241                 (ok == PS_SOCKET && !ctl->wehaveauthed))
242             {
243                 ctl->server.authenticate = A_PASSWORD;
244                 /* repoll immediately */
245                 ok = PS_REPOLL;
246                 break;
247             }
248         }
249
250 #ifdef SSL_ENABLE
251         if (has_ssl
252             && !ctl->use_ssl
253             && (!ctl->sslproto || !strcmp(ctl->sslproto,"tls1")))
254         {
255             char *realhost;
256
257            realhost = ctl->server.via ? ctl->server.via : ctl->server.pollname;
258            gen_transact(sock, "STLS");
259
260            /* We use "tls1" instead of ctl->sslproto, as we want STLS,
261             * not other SSL protocols
262             */
263            if (SSLOpen(sock,ctl->sslcert,ctl->sslkey,"tls1",ctl->sslcertck, ctl->sslcertpath,ctl->sslfingerprint,realhost,ctl->server.pollname) == -1)
264            {
265                if (!ctl->sslproto && !ctl->wehaveauthed)
266                {
267                    ctl->sslproto = xstrdup("");
268                    /* repoll immediately */
269                    return(PS_REPOLL);
270                }
271                report(stderr,
272                        GT_("SSL connection failed.\n"));
273                 return(PS_AUTHFAIL);
274             }
275            did_stls = TRUE;
276         }
277 #endif /* SSL_ENABLE */
278
279         /*
280          * OK, we have an authentication type now.
281          */
282 #if defined(KERBEROS_V4)
283         /* 
284          * Servers doing KPOP have to go through a dummy login sequence
285          * rather than doing SASL.
286          */
287         if (has_kerberos &&
288 #if INET6_ENABLE
289             ctl->server.service && (strcmp(ctl->server.service, KPOP_PORT)!=0)
290 #else /* INET6_ENABLE */
291             ctl->server.port != KPOP_PORT
292 #endif /* INET6_ENABLE */
293             && (ctl->server.authenticate == A_KERBEROS_V4
294              || ctl->server.authenticate == A_KERBEROS_V5
295              || ctl->server.authenticate == A_ANY))
296         {
297             ok = do_rfc1731(sock, "AUTH", ctl->server.truename);
298             if (ok == PS_SUCCESS || ctl->server.authenticate != A_ANY)
299                 break;
300         }
301 #endif /* defined(KERBEROS_V4) || defined(KERBEROS_V5) */
302
303 #if defined(GSSAPI)
304         if (has_gssapi &&
305             (ctl->server.authenticate == A_GSSAPI ||
306              ctl->server.authenticate == A_ANY))
307         {
308             ok = do_gssauth(sock,"AUTH",ctl->server.truename,ctl->remotename);
309             if (ok == PS_SUCCESS || ctl->server.authenticate != A_ANY)
310                 break;
311         }
312 #endif /* defined(GSSAPI) */
313
314 #ifdef OPIE_ENABLE
315         if (has_otp &&
316             (ctl->server.authenticate == A_OTP ||
317              ctl->server.authenticate == A_ANY))
318         {
319             ok = do_otp(sock, "AUTH", ctl);
320             if (ok == PS_SUCCESS || ctl->server.authenticate != A_ANY)
321                 break;
322         }
323 #endif /* OPIE_ENABLE */
324
325         if (has_cram &&
326             (ctl->server.authenticate == A_CRAM_MD5 ||
327              ctl->server.authenticate == A_ANY))
328         {
329             ok = do_cram_md5(sock, "AUTH", ctl, NULL);
330             if (ok == PS_SUCCESS || ctl->server.authenticate != A_ANY)
331                 break;
332         }
333
334         /* ordinary validation, no one-time password or RPA */ 
335         gen_transact(sock, "USER %s", ctl->remotename);
336
337 #if OPIE_ENABLE
338         /* see RFC1938: A One-Time Password System */
339         if (challenge = strstr(lastok, "otp-")) {
340           char response[OPIE_RESPONSE_MAX+1];
341           int i;
342
343           i = opiegenerator(challenge, !strcmp(ctl->password, "opie") ? "" : ctl->password, response);
344           if ((i == -2) && !run.poll_interval) {
345             char secret[OPIE_SECRET_MAX+1];
346             fprintf(stderr, GT_("Secret pass phrase: "));
347             if (opiereadpass(secret, sizeof(secret), 0))
348               i = opiegenerator(challenge,  secret, response);
349             memset(secret, 0, sizeof(secret));
350           };
351
352           if (i) {
353             ok = PS_ERROR;
354             break;
355           };
356
357           ok = gen_transact(sock, "PASS %s", response);
358           break;
359         }
360 #endif /* OPIE_ENABLE */
361
362         strcpy(shroud, ctl->password);
363         ok = gen_transact(sock, "PASS %s", ctl->password);
364         shroud[0] = '\0';
365 #ifdef SSL_ENABLE
366         /* this is for servers which claim to support TLS, but actually
367          * don't! */
368         if (did_stls && ok == PS_SOCKET && !ctl->sslproto && !ctl->wehaveauthed)
369         {
370             ctl->sslproto = xstrdup("");
371             /* repoll immediately */
372             ok = PS_REPOLL;
373         }
374 #endif
375         break;
376
377     case P_APOP:
378         /* build MD5 digest from greeting timestamp + password */
379         /* find start of timestamp */
380         for (start = greeting;  *start != 0 && *start != '<';  start++)
381             continue;
382         if (*start == 0) {
383             report(stderr,
384                    GT_("Required APOP timestamp not found in greeting\n"));
385             return(PS_AUTHFAIL);
386         }
387
388         /* find end of timestamp */
389         for (end = start;  *end != 0  && *end != '>';  end++)
390             continue;
391         if (*end == 0 || end == start + 1) {
392             report(stderr, 
393                    GT_("Timestamp syntax error in greeting\n"));
394             return(PS_AUTHFAIL);
395         }
396         else
397             *++end = '\0';
398
399         /* copy timestamp and password into digestion buffer */
400         xalloca(msg, char *, (end-start+1) + strlen(ctl->password) + 1);
401         strcpy(msg,start);
402         strcat(msg,ctl->password);
403
404         strcpy(ctl->digest, MD5Digest((unsigned char *)msg));
405
406         ok = gen_transact(sock, "APOP %s %s", ctl->remotename, ctl->digest);
407         break;
408
409     case P_RPOP:
410         if ((ok = gen_transact(sock,"USER %s", ctl->remotename)) == 0)
411             ok = gen_transact(sock, "RPOP %s", ctl->password);
412         break;
413
414     default:
415         report(stderr, GT_("Undefined protocol request in POP3_auth\n"));
416         ok = PS_ERROR;
417     }
418
419     if (ok != 0)
420     {
421         /* maybe we detected a lock-busy condition? */
422         if (ok == PS_LOCKBUSY)
423             report(stderr, GT_("lock busy!  Is another session active?\n")); 
424
425         return(ok);
426     }
427
428     /*
429      * Empirical experience shows some server/OS combinations
430      * may need a brief pause even after any lockfiles on the
431      * server are released, to give the server time to finish
432      * copying back very large mailfolders from the temp-file...
433      * this is only ever an issue with extremely large mailboxes.
434      */
435     sleep(3); /* to be _really_ safe, probably need sleep(5)! */
436
437     /* we're peek-capable if use of TOP is enabled */
438     peek_capable = !(ctl->fetchall || ctl->keep);
439
440     /* we're approved */
441     return(PS_SUCCESS);
442 }
443
444 static int pop3_gettopid( int sock, int num , char *id)
445 {
446     int ok;
447     int got_it;
448     char buf [POPBUFSIZE+1];
449     sprintf( buf, "TOP %d 1", num );
450     if ((ok = gen_transact(sock, buf )) != 0)
451        return ok; 
452     got_it = 0;
453     while ((ok = gen_recv(sock, buf, sizeof(buf))) == 0) 
454     {
455         if (DOTLINE(buf))
456             break;
457         if ( ! got_it && ! strncasecmp("Message-Id:", buf, 11 )) {
458             got_it = 1;
459             /* prevent stack overflows */
460             buf[IDLEN+12] = 0;
461             sscanf( buf+12, "%s", id);
462         }
463     }
464     return 0;
465 }
466
467 static int pop3_slowuidl( int sock,  struct query *ctl, int *countp, int *newp)
468 {
469     /* This approach tries to get the message headers from the
470      * remote hosts and compares the message-id to the already known
471      * ones:
472      *  + if the first message containes a new id, all messages on
473      *    the server will be new
474      *  + if the first is known, try to estimate the last known message
475      *    on the server and check. If this works you know the total number
476      *    of messages to get.
477      *  + Otherwise run a binary search to determine the last known message
478      */
479     int ok, nolinear = 0;
480     int first_nr, list_len, try_id, try_nr, add_id;
481     int num;
482     char id [IDLEN+1];
483     
484     if( (ok = pop3_gettopid( sock, 1, id )) != 0 )
485         return ok;
486     
487     if( ( first_nr = str_nr_in_list(&ctl->oldsaved, id) ) == -1 ) {
488         /* the first message is unknown -> all messages are new */
489         *newp = *countp;        
490         return 0;
491     }
492
493     /* check where we expect the latest known message */
494     list_len = count_list( &ctl->oldsaved );
495     try_id = list_len  - first_nr; /* -1 + 1 */
496     if( try_id > 1 ) {
497         if( try_id <= *countp ) {
498             if( (ok = pop3_gettopid( sock, try_id, id )) != 0 )
499                 return ok;
500     
501             try_nr = str_nr_last_in_list(&ctl->oldsaved, id);
502         } else {
503             try_id = *countp+1;
504             try_nr = -1;
505         }
506         if( try_nr != list_len -1 ) {
507             /* some messages inbetween have been deleted... */
508             if( try_nr == -1 ) {
509                 nolinear = 1;
510
511                 for( add_id = 1<<30; add_id > try_id-1; add_id >>= 1 )
512                     ;
513                 for( ; add_id; add_id >>= 1 ) {
514                     if( try_nr == -1 ) {
515                         if( try_id - add_id <= 1 ) {
516                             continue;
517                         }
518                         try_id -= add_id;
519                     } else 
520                         try_id += add_id;
521                     
522                     if( (ok = pop3_gettopid( sock, try_id, id )) != 0 )
523                         return ok;
524                     try_nr = str_nr_in_list(&ctl->oldsaved, id);
525                 }
526                 if( try_nr == -1 ) {
527                     try_id--;
528                 }
529             } else {
530                 report(stderr, 
531                        GT_("Messages inserted into list on server. Cannot handle this.\n"));
532                 return -1;
533             }
534         } 
535     }
536     /* the first try_id messages are known -> copy them to the newsaved list */
537     for( num = first_nr; num < list_len; num++ )
538     {
539         struct idlist   *new = save_str(&ctl->newsaved, 
540                                 str_from_nr_list(&ctl->oldsaved, num),
541                                 UID_UNSEEN);
542         new->val.status.num = num - first_nr + 1;
543     }
544
545     if( nolinear ) {
546         free_str_list(&ctl->oldsaved);
547         ctl->oldsaved = 0;
548         last = try_id;
549     }
550
551     *newp = *countp - try_id;
552     return 0;
553 }
554
555 static int pop3_getrange(int sock, 
556                          struct query *ctl,
557                          const char *folder,
558                          int *countp, int *newp, int *bytes)
559 /* get range of messages to be fetched */
560 {
561     int ok;
562     char buf [POPBUFSIZE+1];
563
564     /* Ensure that the new list is properly empty */
565     ctl->newsaved = (struct idlist *)NULL;
566
567 #ifdef MBOX
568     /* Alain Knaff suggests this, but it's not RFC standard */
569     if (folder)
570         if ((ok = gen_transact(sock, "MBOX %s", folder)))
571             return ok;
572 #endif /* MBOX */
573
574     /* get the total message count */
575     gen_send(sock, "STAT");
576     ok = pop3_ok(sock, buf);
577     if (ok == 0)
578         sscanf(buf,"%d %d", countp, bytes);
579     else
580         return(ok);
581
582     /*
583      * Newer, RFC-1725-conformant POP servers may not have the LAST command.
584      * We work as hard as possible to hide this ugliness, but it makes 
585      * counting new messages intrinsically quadratic in the worst case.
586      */
587     last = 0;
588     *newp = -1;
589     if (*countp > 0 && !ctl->fetchall)
590     {
591         char id [IDLEN+1];
592
593         if (!ctl->server.uidl) {
594             gen_send(sock, "LAST");
595             ok = pop3_ok(sock, buf);
596         } else
597             ok = 1;
598         if (ok == 0)
599         {
600             if (sscanf(buf, "%d", &last) == 0)
601             {
602                 report(stderr, GT_("protocol error\n"));
603                 return(PS_ERROR);
604             }
605             *newp = (*countp - last);
606         }
607         else
608         {
609             /* grab the mailbox's UID list */
610             if ((ok = gen_transact(sock, "UIDL")) != 0)
611             {
612                 /* don't worry, yet! do it the slow way */
613                 if((ok = pop3_slowuidl( sock, ctl, countp, newp))!=0)
614                 {
615                     report(stderr, GT_("protocol error while fetching UIDLs\n"));
616                     return(PS_ERROR);
617                 }
618             }
619             else
620             {
621                 int     num;
622
623                 *newp = 0;
624                 while ((ok = gen_recv(sock, buf, sizeof(buf))) == 0)
625                 {
626                     if (DOTLINE(buf))
627                         break;
628                     else if (sscanf(buf, "%d %s", &num, id) == 2)
629                     {
630                         struct idlist   *new;
631
632                         new = save_str(&ctl->newsaved, id, UID_UNSEEN);
633                         new->val.status.num = num;
634
635                         if (str_in_list(&ctl->oldsaved, id, FALSE)) {
636                             new->val.status.mark = UID_SEEN;
637                             str_set_mark(&ctl->oldsaved, id, UID_SEEN);
638                         }
639                         else
640                             (*newp)++;
641                     }
642                 }
643             }
644         }
645     }
646
647     return(PS_SUCCESS);
648 }
649
650 static int pop3_getsizes(int sock, int count, int *sizes)
651 /* capture the sizes of all messages */
652 {
653     int ok;
654
655     if ((ok = gen_transact(sock, "LIST")) != 0)
656         return(ok);
657     else
658     {
659         char buf [POPBUFSIZE+1];
660
661         while ((ok = gen_recv(sock, buf, sizeof(buf))) == 0)
662         {
663             unsigned int num, size;
664
665             if (DOTLINE(buf))
666                 break;
667             else if (sscanf(buf, "%u %u", &num, &size) == 2) {
668                 if (num > 0 && num <= count)
669                     sizes[num - 1] = size;
670                 else
671                     /* warn about possible attempt to induce buffer overrun */
672                     report(stderr, "Warning: ignoring bogus data for message sizes returned by server.\n");
673             }
674         }
675
676         return(ok);
677     }
678 }
679
680 static int pop3_is_old(int sock, struct query *ctl, int num)
681 /* is the given message old? */
682 {
683     if (!ctl->oldsaved)
684         return (num <= last);
685     else
686         return (str_in_list(&ctl->oldsaved,
687                             str_find(&ctl->newsaved, num), FALSE));
688 }
689
690 #ifdef UNUSED
691 /*
692  * We could use this to fetch headers only as we do for IMAP.  The trouble 
693  * is that there's no way to fetch the body only.  So the following RETR 
694  * would have to re-fetch the header.  Enough messages have longer headers
695  * than bodies to make this a net loss.
696  */
697 static int pop_fetch_headers(int sock, struct query *ctl,int number,int *lenp)
698 /* request headers of nth message */
699 {
700     int ok;
701     char buf[POPBUFSIZE+1];
702
703     gen_send(sock, "TOP %d 0", number);
704     if ((ok = pop3_ok(sock, buf)) != 0)
705         return(ok);
706
707     *lenp = -1;         /* we got sizes from the LIST response */
708
709     return(PS_SUCCESS);
710 }
711 #endif /* UNUSED */
712
713 static int pop3_fetch(int sock, struct query *ctl, int number, int *lenp)
714 /* request nth message */
715 {
716     int ok;
717     char buf[POPBUFSIZE+1];
718
719 #ifdef SDPS_ENABLE
720     /*
721      * See http://www.demon.net/services/mail/sdps-tech.html
722      * for a description of what we're parsing here.
723      */
724     if (ctl->server.sdps)
725     {
726         int     linecount = 0;
727
728         sdps_envfrom = (char *)NULL;
729         sdps_envto = (char *)NULL;
730         gen_send(sock, "*ENV %d", number);
731         do {
732             if (gen_recv(sock, buf, sizeof(buf)))
733             {
734                 break;
735             }
736             linecount++;
737             switch (linecount) {
738             case 4:
739                 /* No need to wrap envelope from address */
740                 sdps_envfrom = xmalloc(strlen(buf)+1);
741                 strcpy(sdps_envfrom,buf);
742                 break;
743             case 5:
744                 /* Wrap address with To: <> so nxtaddr() likes it */
745                 sdps_envto = xmalloc(strlen(buf)+7);
746                 sprintf(sdps_envto,"To: <%s>",buf);
747                 break;
748             }
749         } while
750             (!(buf[0] == '.' && (buf[1] == '\r' || buf[1] == '\n' || buf[1] == '\0')));
751     }
752 #endif /* SDPS_ENABLE */
753
754     /*
755      * Though the POP RFCs don't document this fact, on almost every
756      * POP3 server I know of messages are marked "seen" only at the
757      * time the OK response to a RETR is issued.
758      *
759      * This means we can use TOP to fetch the message without setting its
760      * seen flag.  This is good!  It means that if the protocol exchange
761      * craps out during the message, it will still be marked `unseen' on
762      * the server.  (Exception: in early 1999 SpryNet's POP3 servers were
763      * reported to mark messages seen on a TOP fetch.)
764      *
765      * However...*don't* do this if we're using keep to suppress deletion!
766      * In that case, marking the seen flag is the only way to prevent the
767      * message from being re-fetched on subsequent runs.
768      *
769      * Also use RETR if fetchall is on.  This gives us a workaround
770      * for servers like usa.net's that bungle TOP.  It's pretty
771      * harmless because fetchall guarantees that any message dropped
772      * by an interrupted RETR will be picked up on the next poll of the
773      * site.
774      *
775      * We take advantage here of the fact that, according to all the
776      * POP RFCs, "if the number of lines requested by the POP3 client
777      * is greater than than the number of lines in the body, then the
778      * POP3 server sends the entire message.").
779      *
780      * The line count passed (99999999) is the maximum value CompuServe will
781      * accept; it's much lower than the natural value 2147483646 (the maximum
782      * twos-complement signed 32-bit integer minus 1) */
783     if (ctl->keep || ctl->fetchall)
784         gen_send(sock, "RETR %d", number);
785     else
786         gen_send(sock, "TOP %d 99999999", number);
787     if ((ok = pop3_ok(sock, buf)) != 0)
788         return(ok);
789
790     *lenp = -1;         /* we got sizes from the LIST response */
791
792     return(PS_SUCCESS);
793 }
794
795 static void mark_uid_seen(struct query *ctl, int number)
796 /* Tell the UID code we've seen this. */
797 {
798     if (ctl->newsaved)
799     {
800         struct idlist   *sdp;
801
802         for (sdp = ctl->newsaved; sdp; sdp = sdp->next)
803             if (sdp->val.status.num == number)
804             {
805                 sdp->val.status.mark = UID_SEEN;
806                 save_str(&ctl->oldsaved, sdp->id,UID_SEEN);
807             }
808     }
809 }
810
811 static int pop3_delete(int sock, struct query *ctl, int number)
812 /* delete a given message */
813 {
814     int ok;
815     mark_uid_seen(ctl, number);
816     /* actually, mark for deletion -- doesn't happen until QUIT time */
817     ok = gen_transact(sock, "DELE %d", number);
818     if (ok != PS_SUCCESS)
819         return(ok);
820     delete_str(&ctl->newsaved, number);
821     return(PS_SUCCESS);
822 }
823
824 static int pop3_mark_seen(int sock, struct query *ctl, int number)
825 /* mark a given message as seen */
826 {
827     mark_uid_seen(ctl, number);
828     return(PS_SUCCESS);
829 }
830
831 static int pop3_logout(int sock, struct query *ctl)
832 /* send logout command */
833 {
834     int ok;
835
836 #ifdef __UNUSED__
837     /*
838      * We used to do this in case the server marks messages deleted when seen.
839      * (Yes, this has been reported, in the MercuryP/NLM server.
840      * It's even legal under RFC 1939 (section 8) as a site policy.)
841      * It interacted badly with UIDL, though.  Thomas Zajic wrote:
842      * "Running 'fetchmail -F -v' and checking the logs, I found out
843      * that fetchmail did in fact flush my mailbox properly, but sent
844      * a RSET just before sending QUIT to log off.  This caused the
845      * POP3 server to undo/forget about the previous DELEs, resetting
846      * my mailbox to its original (ie.  unflushed) state. The
847      * ~/.fetchids file did get flushed though, so the next time
848      * fetchmail was run it saw all the old messages as new ones ..."
849      */
850      if (ctl->keep)
851         gen_transact(sock, "RSET");
852 #endif /* __UNUSED__ */
853
854     ok = gen_transact(sock, "QUIT");
855     if (!ok)
856         expunge_uids(ctl);
857
858     if (ctl->lastid)
859     {
860         free(ctl->lastid);
861         ctl->lastid = NULL;
862     }
863
864     return(ok);
865 }
866
867 const static struct method pop3 =
868 {
869     "POP3",             /* Post Office Protocol v3 */
870 #if INET6_ENABLE
871     "pop3",             /* standard POP3 port */
872     "pop3s",            /* ssl POP3 port */
873 #else /* INET6_ENABLE */
874     110,                /* standard POP3 port */
875     995,                /* ssl POP3 port */
876 #endif /* INET6_ENABLE */
877     FALSE,              /* this is not a tagged protocol */
878     TRUE,               /* this uses a message delimiter */
879     pop3_ok,            /* parse command response */
880     pop3_getauth,       /* get authorization */
881     pop3_getrange,      /* query range of messages */
882     pop3_getsizes,      /* we can get a list of sizes */
883     pop3_is_old,        /* how do we tell a message is old? */
884     pop3_fetch,         /* request given message */
885     NULL,               /* no way to fetch body alone */
886     NULL,               /* no message trailer */
887     pop3_delete,        /* how to delete a message */
888     pop3_mark_seen,     /* how to mark a message as seen */
889     pop3_logout,        /* log out, we're done */
890     FALSE,              /* no, we can't re-poll */
891 };
892
893 int doPOP3 (struct query *ctl)
894 /* retrieve messages using POP3 */
895 {
896 #ifndef MBOX
897     if (ctl->mailboxes->id) {
898         fprintf(stderr,GT_("Option --remote is not supported with POP3\n"));
899         return(PS_SYNTAX);
900     }
901 #endif /* MBOX */
902     peek_capable = !ctl->fetchall;
903     return(do_protocol(ctl, &pop3));
904 }
905 #endif /* POP3_ENABLE */
906
907 /* pop3.c ends here */