]> Pileus Git - ~andy/fetchmail/blob - pop3.c
Make SSL connection failures PS_SOCKET (not PS_AUTHFAIL). Reported by Thomas Wolff.
[~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 #include  <errno.h>
20
21 #include  "fetchmail.h"
22 #include  "socket.h"
23 #include  "i18n.h"
24
25 #ifdef OPIE_ENABLE
26 #include <opie.h>
27 #endif /* OPIE_ENABLE */
28
29 static int last;
30 #ifdef SDPS_ENABLE
31 char *sdps_envfrom;
32 char *sdps_envto;
33 #endif /* SDPS_ENABLE */
34
35 #ifdef OPIE_ENABLE
36 static char lastok[POPBUFSIZE+1];
37 #endif /* OPIE_ENABLE */
38
39 /* these variables are shared between the CAPA probe and the authenticator */
40 #if defined(GSSAPI)
41     flag has_gssapi = FALSE;
42 #endif /* defined(GSSAPI) */
43 #if defined(KERBEROS_V4) || defined(KERBEROS_V5)
44     flag has_kerberos = FALSE;
45 #endif /* defined(KERBEROS_V4) || defined(KERBEROS_V5) */
46     static flag has_cram = FALSE;
47 #ifdef OPIE_ENABLE
48     flag has_otp = FALSE;
49 #endif /* OPIE_ENABLE */
50 #ifdef SSL_ENABLE
51     static flag has_ssl = FALSE;
52 #endif /* SSL_ENABLE */
53
54 #ifdef NTLM_ENABLE
55 #include "ntlm.h"
56
57 static tSmbNtlmAuthRequest   request;              
58 static tSmbNtlmAuthChallenge challenge;
59 static tSmbNtlmAuthResponse  response;
60
61 /*
62  * NTLM support by Grant Edwards.
63  *
64  * Handle MS-Exchange NTLM authentication method.  This is the same
65  * as the NTLM auth used by Samba for SMB related services. We just
66  * encode the packets in base64 instead of sending them out via a
67  * network interface.
68  * 
69  * Much source (ntlm.h, smb*.c smb*.h) was borrowed from Samba.
70  */
71
72 static int do_pop3_ntlm(int sock, struct query *ctl,
73         int msn_instead /** if true, send AUTH MSN, else send AUTH NTLM */)
74 {
75     char msgbuf[2048];
76     int result,len;
77   
78     gen_send(sock, msn_instead ? "AUTH MSN" : "AUTH NTLM");
79
80     if ((result = gen_recv(sock, msgbuf, sizeof msgbuf)))
81         return result;
82   
83     if (msgbuf[0] != '+')
84         return PS_AUTHFAIL;
85   
86     buildSmbNtlmAuthRequest(&request,ctl->remotename,NULL);
87
88     if (outlevel >= O_DEBUG)
89         dumpSmbNtlmAuthRequest(stdout, &request);
90
91     memset(msgbuf,0,sizeof msgbuf);
92     to64frombits (msgbuf, (unsigned char*)&request, SmbLength(&request));
93   
94     if (outlevel >= O_MONITOR)
95         report(stdout, "POP3> %s\n", msgbuf);
96   
97     strcat(msgbuf,"\r\n");
98     SockWrite (sock, msgbuf, strlen (msgbuf));
99
100     if ((gen_recv(sock, msgbuf, sizeof msgbuf)))
101         return result;
102   
103     len = from64tobits ((unsigned char*)&challenge, msgbuf, sizeof(msgbuf));
104     
105     if (outlevel >= O_DEBUG)
106         dumpSmbNtlmAuthChallenge(stdout, &challenge);
107     
108     buildSmbNtlmAuthResponse(&challenge, &response,ctl->remotename,ctl->password);
109   
110     if (outlevel >= O_DEBUG)
111         dumpSmbNtlmAuthResponse(stdout, &response);
112   
113     memset(msgbuf,0,sizeof msgbuf);
114     to64frombits (msgbuf, (unsigned char*)&response, SmbLength(&response));
115
116     if (outlevel >= O_MONITOR)
117         report(stdout, "POP3> %s\n", msgbuf);
118       
119     strcat(msgbuf,"\r\n");
120     SockWrite (sock, msgbuf, strlen (msgbuf));
121   
122     if ((result = gen_recv (sock, msgbuf, sizeof msgbuf)))
123         return result;
124   
125     if (strstr (msgbuf, "OK"))
126         return PS_SUCCESS;
127     else
128         return PS_AUTHFAIL;
129 }
130 #endif /* NTLM */
131
132
133 #define DOTLINE(s)      (s[0] == '.' && (s[1]=='\r'||s[1]=='\n'||s[1]=='\0'))
134
135 static int pop3_ok (int sock, char *argbuf)
136 /* parse command response */
137 {
138     int ok;
139     char buf [POPBUFSIZE+1];
140     char *bufp;
141
142     if ((ok = gen_recv(sock, buf, sizeof(buf))) == 0)
143     {   bufp = buf;
144         if (*bufp == '+' || *bufp == '-')
145             bufp++;
146         else
147             return(PS_PROTOCOL);
148
149         while (isalpha((unsigned char)*bufp))
150             bufp++;
151
152         if (*bufp)
153           *(bufp++) = '\0';
154
155         if (strcmp(buf,"+OK") == 0)
156         {
157 #ifdef OPIE_ENABLE
158             strcpy(lastok, bufp);
159 #endif /* OPIE_ENABLE */
160             ok = 0;
161         }
162         else if (strncmp(buf,"-ERR", 4) == 0)
163         {
164             if (stage == STAGE_FETCH)
165                 ok = PS_TRANSIENT;
166             else if (stage > STAGE_GETAUTH)
167                 ok = PS_PROTOCOL;
168             /*
169              * We're checking for "lock busy", "unable to lock", 
170              * "already locked", "wait a few minutes" etc. here. 
171              * This indicates that we have to wait for the server to
172              * unwedge itself before we can poll again.
173              *
174              * PS_LOCKBUSY check empirically verified with two recent
175              * versions of the Berkeley popper; QPOP (version 2.2)  and
176              * QUALCOMM Pop server derived from UCB (version 2.1.4-R3)
177              * These are caught by the case-indifferent "lock" check.
178              * The "wait" catches "mail storage services unavailable,
179              * wait a few minutes and try again" on the InterMail server.
180              *
181              * If these aren't picked up on correctly, fetchmail will 
182              * think there is an authentication failure and wedge the
183              * connection in order to prevent futile polls.
184              *
185              * Gad, what a kluge.
186              */
187             else if (strstr(bufp,"lock")
188                      || strstr(bufp,"Lock")
189                      || strstr(bufp,"LOCK")
190                      || strstr(bufp,"wait")
191                      /* these are blessed by RFC 2449 */
192                      || strstr(bufp,"[IN-USE]")||strstr(bufp,"[LOGIN-DELAY]"))
193                 ok = PS_LOCKBUSY;
194             else if ((strstr(bufp,"Service")
195                      || strstr(bufp,"service"))
196                          && (strstr(bufp,"unavailable")))
197                 ok = PS_SERVBUSY;
198             else
199                 ok = PS_AUTHFAIL;
200             /*
201              * We always want to pass the user lock-busy messages, because
202              * they're red flags.  Other stuff (like AUTH failures on non-
203              * RFC1734 servers) only if we're debugging.
204              */
205             if (*bufp && (ok == PS_LOCKBUSY || outlevel >= O_MONITOR))
206               report(stderr, "%s\n", bufp);
207         }
208         else
209             ok = PS_PROTOCOL;
210
211 #if POPBUFSIZE > MSGBUFSIZE
212 #error "POPBUFSIZE must not be larger than MSGBUFSIZE"
213 #endif
214         if (argbuf != NULL)
215             strcpy(argbuf,bufp);
216     }
217
218     return(ok);
219 }
220
221
222
223 static int capa_probe(int sock)
224 /* probe the capabilities of the remote server */
225 {
226     int ok;
227
228 #if defined(GSSAPI)
229     has_gssapi = FALSE;
230 #endif /* defined(GSSAPI) */
231 #if defined(KERBEROS_V4) || defined(KERBEROS_V5)
232     has_kerberos = FALSE;
233 #endif /* defined(KERBEROS_V4) || defined(KERBEROS_V5) */
234     has_cram = FALSE;
235 #ifdef OPIE_ENABLE
236     has_otp = FALSE;
237 #endif /* OPIE_ENABLE */
238
239     ok = gen_transact(sock, "CAPA");
240     if (ok == PS_SUCCESS)
241     {
242         char buffer[64];
243
244         /* determine what authentication methods we have available */
245         while ((ok = gen_recv(sock, buffer, sizeof(buffer))) == 0)
246         {
247             if (DOTLINE(buffer))
248                 break;
249 #ifdef SSL_ENABLE
250             if (strstr(buffer, "STLS"))
251                 has_ssl = TRUE;
252 #endif /* SSL_ENABLE */
253 #if defined(GSSAPI)
254             if (strstr(buffer, "GSSAPI"))
255                 has_gssapi = TRUE;
256 #endif /* defined(GSSAPI) */
257 #if defined(KERBEROS_V4)
258             if (strstr(buffer, "KERBEROS_V4"))
259                 has_kerberos = TRUE;
260 #endif /* defined(KERBEROS_V4)  */
261 #ifdef OPIE_ENABLE
262             if (strstr(buffer, "X-OTP"))
263                 has_otp = TRUE;
264 #endif /* OPIE_ENABLE */
265             if (strstr(buffer, "CRAM-MD5"))
266                 has_cram = TRUE;
267         }
268     }
269     return(ok);
270 }
271
272 static void set_peek_capable(struct query *ctl)
273 {
274     /* we're peek-capable means that the use of TOP is enabled,
275      * see pop3_fetch for details - short story, we can use TOP if
276      * we have a means of reliably tracking which mail we need to
277      * refetch should the connection abort in the middle.
278      * fetchall forces RETR, as does keep without UIDL */
279     peek_capable = !ctl->fetchall && (!ctl->keep || ctl->server.uidl);
280 }
281
282 static int pop3_getauth(int sock, struct query *ctl, char *greeting)
283 /* apply for connection authorization */
284 {
285     int ok;
286     char *start,*end;
287     char *msg;
288 #ifdef OPIE_ENABLE
289     char *challenge;
290 #endif /* OPIE_ENABLE */
291 #ifdef SSL_ENABLE
292     flag did_stls = FALSE;
293 #endif /* SSL_ENABLE */
294
295 #if defined(GSSAPI)
296     has_gssapi = FALSE;
297 #endif /* defined(GSSAPI) */
298 #if defined(KERBEROS_V4) || defined(KERBEROS_V5)
299     has_kerberos = FALSE;
300 #endif /* defined(KERBEROS_V4) || defined(KERBEROS_V5) */
301     has_cram = FALSE;
302 #ifdef OPIE_ENABLE
303     has_otp = FALSE;
304 #endif /* OPIE_ENABLE */
305 #ifdef SSL_ENABLE
306     has_ssl = FALSE;
307 #endif /* SSL_ENABLE */
308
309     if (ctl->server.authenticate == A_SSH) {
310         return PS_SUCCESS;
311     }
312
313 #ifdef SDPS_ENABLE
314     /*
315      * This needs to catch both demon.co.uk and demon.net.
316      * If we see either, and we're in multidrop mode, try to use
317      * the SDPS *ENV extension.
318      */
319     if (!(ctl->server.sdps) && MULTIDROP(ctl) && strstr(greeting, "demon."))
320         ctl->server.sdps = TRUE;
321 #endif /* SDPS_ENABLE */
322
323 #ifdef NTLM_ENABLE
324     /* MSN servers require the use of NTLM (MSN) authentication */
325     if (!strcasecmp(ctl->server.pollname, "pop3.email.msn.com") ||
326             ctl->server.authenticate == A_MSN)
327         return (do_pop3_ntlm(sock, ctl, 1) == 0) ? PS_SUCCESS : PS_AUTHFAIL;
328     if (ctl->server.authenticate == A_NTLM)
329         return (do_pop3_ntlm(sock, ctl, 0) == 0) ? PS_SUCCESS : PS_AUTHFAIL;
330 #endif
331
332     switch (ctl->server.protocol) {
333     case P_POP3:
334 #ifdef RPA_ENABLE
335         /* XXX FIXME: AUTH probing (RFC1734) should become global */
336         /* CompuServe POP3 Servers as of 990730 want AUTH first for RPA */
337         if (strstr(ctl->remotename, "@compuserve.com"))
338         {
339             /* AUTH command should return a list of available mechanisms */
340             if (gen_transact(sock, "AUTH") == 0)
341             {
342                 char buffer[10];
343                 flag has_rpa = FALSE;
344
345                 while ((ok = gen_recv(sock, buffer, sizeof(buffer))) == 0)
346                 {
347                     if (DOTLINE(buffer))
348                         break;
349                     if (strncasecmp(buffer, "rpa", 3) == 0)
350                         has_rpa = TRUE;
351                 }
352                 if (has_rpa && !POP3_auth_rpa(ctl->remotename, 
353                                               ctl->password, sock))
354                     return(PS_SUCCESS);
355             }
356
357             return(PS_AUTHFAIL);
358         }
359 #endif /* RPA_ENABLE */
360
361         /*
362          * CAPA command may return a list including available
363          * authentication mechanisms.  if it doesn't, no harm done, we
364          * just fall back to a plain login.  Note that this code 
365          * latches the server's authentication type, so that in daemon mode
366          * the CAPA check only needs to be done once at start of run.
367          *
368          * If CAPA fails, then force the authentication method to PASSORD
369          * and repoll immediately.
370          *
371          * These authentication methods are blessed by RFC1734,
372          * describing the POP3 AUTHentication command.
373          */
374         if ((ctl->server.authenticate == A_ANY) ||
375             (ctl->server.authenticate == A_GSSAPI) ||
376             (ctl->server.authenticate == A_KERBEROS_V4) ||
377             (ctl->server.authenticate == A_OTP) ||
378             (ctl->server.authenticate == A_CRAM_MD5))
379         {
380             if ((ok = capa_probe(sock)) != PS_SUCCESS)
381             /* we are in STAGE_GETAUTH! */
382                 if (ok == PS_AUTHFAIL ||
383                     /* Some servers directly close the socket. However, if we
384                      * have already authenticated before, then a previous CAPA
385                      * must have succeeded. In that case, treat this as a
386                      * genuine socket error and do not change the auth method.
387                      */
388                     (ok == PS_SOCKET && !ctl->wehaveauthed))
389                 {
390                     ctl->server.authenticate = A_PASSWORD;
391                     /* repoll immediately */
392                     ok = PS_REPOLL;
393                     break;
394                 }
395         }
396
397 #ifdef SSL_ENABLE
398         if (has_ssl
399             && !ctl->use_ssl
400             && (!ctl->sslproto || !strcmp(ctl->sslproto,"tls1")))
401         {
402             char *realhost;
403
404            realhost = ctl->server.via ? ctl->server.via : ctl->server.pollname;
405            ok = gen_transact(sock, "STLS");
406
407            /* We use "tls1" instead of ctl->sslproto, as we want STLS,
408             * not other SSL protocols
409             */
410            if (ok == PS_SUCCESS &&
411                SSLOpen(sock,ctl->sslcert,ctl->sslkey,"tls1",ctl->sslcertck, ctl->sslcertpath,ctl->sslfingerprint,realhost,ctl->server.pollname) == -1)
412            {
413                if (!ctl->sslproto && !ctl->wehaveauthed)
414                {
415                    ctl->sslproto = xstrdup("");
416                    /* repoll immediately */
417                    return(PS_REPOLL);
418                }
419                report(stderr,
420                        GT_("SSL connection failed.\n"));
421                 return PS_SOCKET;
422             }
423            did_stls = TRUE;
424
425            /*
426             * RFC 2595 says this:
427             *
428             * "Once TLS has been started, the client MUST discard cached
429             * information about server capabilities and SHOULD re-issue the
430             * CAPABILITY command.  This is necessary to protect against
431             * man-in-the-middle attacks which alter the capabilities list prior
432             * to STARTTLS.  The server MAY advertise different capabilities
433             * after STARTTLS."
434             */
435            capa_probe(sock);
436         }
437 #endif /* SSL_ENABLE */
438
439         /*
440          * OK, we have an authentication type now.
441          */
442 #if defined(KERBEROS_V4)
443         /* 
444          * Servers doing KPOP have to go through a dummy login sequence
445          * rather than doing SASL.
446          */
447         if (has_kerberos &&
448             ctl->server.service && (strcmp(ctl->server.service, KPOP_PORT)!=0)
449             && (ctl->server.authenticate == A_KERBEROS_V4
450              || ctl->server.authenticate == A_KERBEROS_V5
451              || ctl->server.authenticate == A_ANY))
452         {
453             ok = do_rfc1731(sock, "AUTH", ctl->server.truename);
454             if (ok == PS_SUCCESS || ctl->server.authenticate != A_ANY)
455                 break;
456         }
457 #endif /* defined(KERBEROS_V4) || defined(KERBEROS_V5) */
458
459 #if defined(GSSAPI)
460         if (has_gssapi &&
461             (ctl->server.authenticate == A_GSSAPI ||
462              ctl->server.authenticate == A_ANY))
463         {
464             ok = do_gssauth(sock,"AUTH","pop",ctl->server.truename,ctl->remotename);
465             if (ok == PS_SUCCESS || ctl->server.authenticate != A_ANY)
466                 break;
467         }
468 #endif /* defined(GSSAPI) */
469
470 #ifdef OPIE_ENABLE
471         if (has_otp &&
472             (ctl->server.authenticate == A_OTP ||
473              ctl->server.authenticate == A_ANY))
474         {
475             ok = do_otp(sock, "AUTH", ctl);
476             if (ok == PS_SUCCESS || ctl->server.authenticate != A_ANY)
477                 break;
478         }
479 #endif /* OPIE_ENABLE */
480
481         if (ctl->server.authenticate == A_CRAM_MD5 || 
482             (has_cram && ctl->server.authenticate == A_ANY))
483         {
484             ok = do_cram_md5(sock, "AUTH", ctl, NULL);
485             if (ok == PS_SUCCESS || ctl->server.authenticate != A_ANY)
486                 break;
487         }
488
489         /* ordinary validation, no one-time password or RPA */ 
490         if ((ok = gen_transact(sock, "USER %s", ctl->remotename)))
491             break;
492
493 #ifdef OPIE_ENABLE
494         /* see RFC1938: A One-Time Password System */
495         if ((challenge = strstr(lastok, "otp-"))) {
496           char response[OPIE_RESPONSE_MAX+1];
497           int i;
498
499           i = opiegenerator(challenge, !strcmp(ctl->password, "opie") ? "" : ctl->password, response);
500           if ((i == -2) && !run.poll_interval) {
501             char secret[OPIE_SECRET_MAX+1];
502             fprintf(stderr, GT_("Secret pass phrase: "));
503             if (opiereadpass(secret, sizeof(secret), 0))
504               i = opiegenerator(challenge,  secret, response);
505             memset(secret, 0, sizeof(secret));
506           };
507
508           if (i) {
509             ok = PS_ERROR;
510             break;
511           };
512
513           ok = gen_transact(sock, "PASS %s", response);
514           break;
515         }
516 #endif /* OPIE_ENABLE */
517
518         strlcpy(shroud, ctl->password, sizeof(shroud));
519         ok = gen_transact(sock, "PASS %s", ctl->password);
520         shroud[0] = '\0';
521 #ifdef SSL_ENABLE
522         /* this is for servers which claim to support TLS, but actually
523          * don't! */
524         if (did_stls && ok == PS_SOCKET && !ctl->sslproto && !ctl->wehaveauthed)
525         {
526             ctl->sslproto = xstrdup("");
527             /* repoll immediately */
528             ok = PS_REPOLL;
529         }
530 #endif
531         break;
532
533     case P_APOP:
534         /* build MD5 digest from greeting timestamp + password */
535         /* find start of timestamp */
536         for (start = greeting;  *start != 0 && *start != '<';  start++)
537             continue;
538         if (*start == 0) {
539             report(stderr,
540                    GT_("Required APOP timestamp not found in greeting\n"));
541             return(PS_AUTHFAIL);
542         }
543
544         /* find end of timestamp */
545         for (end = start;  *end != 0  && *end != '>';  end++)
546             continue;
547         if (*end == 0 || end == start + 1) {
548             report(stderr, 
549                    GT_("Timestamp syntax error in greeting\n"));
550             return(PS_AUTHFAIL);
551         }
552         else
553             *++end = '\0';
554
555         /* copy timestamp and password into digestion buffer */
556         msg = xmalloc((end-start+1) + strlen(ctl->password) + 1);
557         strcpy(msg,start);
558         strcat(msg,ctl->password);
559         strcpy(ctl->digest, MD5Digest((unsigned char *)msg));
560         free(msg);
561
562         ok = gen_transact(sock, "APOP %s %s", ctl->remotename, ctl->digest);
563         break;
564
565     case P_RPOP:
566         if ((ok = gen_transact(sock,"USER %s", ctl->remotename)) == 0)
567             ok = gen_transact(sock, "RPOP %s", ctl->password);
568         break;
569
570     default:
571         report(stderr, GT_("Undefined protocol request in POP3_auth\n"));
572         ok = PS_ERROR;
573     }
574
575     if (ok != 0)
576     {
577         /* maybe we detected a lock-busy condition? */
578         if (ok == PS_LOCKBUSY)
579             report(stderr, GT_("lock busy!  Is another session active?\n")); 
580
581         return(ok);
582     }
583
584 /* Disable the sleep. Based on patch by Brian Candler 2004-04-19/2004-11-08,
585  * accepted by Matthias Andree.
586  *
587  * Rationale: the server must have locked the spool before returning +OK;
588  * this sleep just wastes time and hence, for modem and GSM CSD users, money. */
589 #ifdef WANT_BOGUS
590     /*
591      * Empirical experience shows some server/OS combinations
592      * may need a brief pause even after any lockfiles on the
593      * server are released, to give the server time to finish
594      * copying back very large mailfolders from the temp-file...
595      * this is only ever an issue with extremely large mailboxes.
596      */
597     sleep(3); /* to be _really_ safe, probably need sleep(5)! */
598 #endif
599
600     set_peek_capable(ctl);
601
602     /* we're approved */
603     return(PS_SUCCESS);
604 }
605
606 /* cut off C string at first POSIX space */
607 static void trim(char *s) {
608     s += strcspn(s, POSIX_space);
609     s[0] = '\0';
610 }
611
612 static int pop3_gettopid(int sock, int num , char *id, size_t idsize)
613 {
614     int ok;
615     int got_it;
616     char buf [POPBUFSIZE+1];
617     snprintf(buf, sizeof(buf), "TOP %d 1", num);
618     if ((ok = gen_transact(sock, buf )) != 0)
619        return ok;
620     got_it = 0;
621     while ((ok = gen_recv(sock, buf, sizeof(buf))) == 0) 
622     {
623         if (DOTLINE(buf))
624             break;
625         if (!got_it && 0 == strncasecmp("Message-Id:", buf, 11)) {
626             char *p = buf + 11;
627             got_it = 1;
628             p += strspn(p, POSIX_space);
629             strlcpy(id, p, idsize);
630             trim(id);
631         }
632     }
633     return 0;
634 }
635
636 /** Parse the UID response (leading +OK must have been
637  * stripped off) in buf, store the number in gotnum, and store the ID
638  * into the caller-provided buffer "id" of size "idsize".
639  * Returns PS_SUCCESS or PS_PROTOCOL for failure. */
640 static int parseuid(const char *buf, unsigned long *gotnum, char *id, size_t idsize)
641 {
642     const char *i;
643     char *j;
644
645     /* skip leading blanks ourselves */
646     i = buf;
647     i += strspn(i, POSIX_space);
648     errno = 0;
649     *gotnum = strtoul(i, &j, 10);
650     if (j == i || !*j || errno || NULL == strchr(POSIX_space, *j)) {
651         report(stderr, GT_("Cannot handle UIDL response from upstream server.\n"));
652         return PS_PROTOCOL;
653     }
654     j += strspn(j, POSIX_space);
655     strlcpy(id, j, idsize);
656     trim(id);
657     return PS_SUCCESS;
658 }
659
660 /** request UIDL for single message \a num and stuff the result into the
661  * buffer \a id which can hold \a idsize bytes */
662 static int pop3_getuidl(int sock, int num, char *id /** output */, size_t idsize)
663 {
664     int ok;
665     char buf [POPBUFSIZE+1];
666     unsigned long gotnum;
667
668     gen_send(sock, "UIDL %d", num);
669     if ((ok = pop3_ok(sock, buf)) != 0)
670         return(ok);
671     if ((ok = parseuid(buf, &gotnum, id, idsize)))
672         return ok;
673     if (gotnum != num) {
674         report(stderr, GT_("Server responded with UID for wrong message.\n"));
675         return PS_PROTOCOL;
676     }
677     return(PS_SUCCESS);
678 }
679
680 static int pop3_fastuidl( int sock,  struct query *ctl, unsigned int count, int *newp)
681 {
682     int ok;
683     unsigned int first_nr, last_nr, try_nr;
684     char id [IDLEN+1];
685
686     first_nr = 0;
687     last_nr = count + 1;
688     while (first_nr < last_nr - 1)
689     {
690         struct idlist   *new;
691
692         try_nr = (first_nr + last_nr) / 2;
693         if ((ok = pop3_getuidl(sock, try_nr, id, sizeof(id))) != 0)
694             return ok;
695         if ((new = str_in_list(&ctl->oldsaved, id, FALSE)))
696         {
697             flag mark = new->val.status.mark;
698             if (mark == UID_DELETED || mark == UID_EXPUNGED)
699             {
700                 if (outlevel >= O_VERBOSE)
701                     report(stderr, GT_("id=%s (num=%d) was deleted, but is still present!\n"), id, try_nr);
702                 /* just mark it as seen now! */
703                 new->val.status.mark = mark = UID_SEEN;
704             }
705
706             /* narrow the search region! */
707             if (mark == UID_UNSEEN)
708             {
709                 if (outlevel >= O_DEBUG)
710                     report(stdout, GT_("%u is unseen\n"), try_nr);
711                 last_nr = try_nr;
712             }
713             else
714                 first_nr = try_nr;
715
716             /* save the number */
717             new->val.status.num = try_nr;
718         }
719         else
720         {
721             if (outlevel >= O_DEBUG)
722                 report(stdout, GT_("%u is unseen\n"), try_nr);
723             last_nr = try_nr;
724
725             /* save it */
726             new = save_str(&ctl->oldsaved, id, UID_UNSEEN);
727             new->val.status.num = try_nr;
728         }
729     }
730     if (outlevel >= O_DEBUG && last_nr <= count)
731         report(stdout, GT_("%u is first unseen\n"), last_nr);
732
733     /* update last! */
734     *newp = count - first_nr;
735     last = first_nr;
736     return 0;
737 }
738
739 static int pop3_slowuidl( int sock,  struct query *ctl, int *countp, int *newp)
740 {
741     /* This approach tries to get the message headers from the
742      * remote hosts and compares the message-id to the already known
743      * ones:
744      *  + if the first message containes a new id, all messages on
745      *    the server will be new
746      *  + if the first is known, try to estimate the last known message
747      *    on the server and check. If this works you know the total number
748      *    of messages to get.
749      *  + Otherwise run a binary search to determine the last known message
750      */
751     int ok, nolinear = 0;
752     int first_nr, list_len, try_id, try_nr, add_id;
753     int num;
754     char id [IDLEN+1];
755
756     if ((ok = pop3_gettopid(sock, 1, id, sizeof(id))) != 0)
757         return ok;
758
759     if( ( first_nr = str_nr_in_list(&ctl->oldsaved, id) ) == -1 ) {
760         /* the first message is unknown -> all messages are new */
761         *newp = *countp;        
762         return 0;
763     }
764
765     /* check where we expect the latest known message */
766     list_len = count_list( &ctl->oldsaved );
767     try_id = list_len  - first_nr; /* -1 + 1 */
768     if( try_id > 1 ) {
769         if( try_id <= *countp ) {
770             if ((ok = pop3_gettopid(sock, try_id, id, sizeof(id))) != 0)
771                 return ok;
772     
773             try_nr = str_nr_last_in_list(&ctl->oldsaved, id);
774         } else {
775             try_id = *countp+1;
776             try_nr = -1;
777         }
778         if( try_nr != list_len -1 ) {
779             /* some messages inbetween have been deleted... */
780             if( try_nr == -1 ) {
781                 nolinear = 1;
782
783                 for( add_id = 1<<30; add_id > try_id-1; add_id >>= 1 )
784                     ;
785                 for( ; add_id; add_id >>= 1 ) {
786                     if( try_nr == -1 ) {
787                         if( try_id - add_id <= 1 ) {
788                             continue;
789                         }
790                         try_id -= add_id;
791                     } else 
792                         try_id += add_id;
793                     
794                     if ((ok = pop3_gettopid(sock, try_id, id, sizeof(id))) != 0)
795                         return ok;
796                     try_nr = str_nr_in_list(&ctl->oldsaved, id);
797                 }
798                 if( try_nr == -1 ) {
799                     try_id--;
800                 }
801             } else {
802                 report(stderr, 
803                        GT_("Messages inserted into list on server. Cannot handle this.\n"));
804                 return -1;
805             }
806         } 
807     }
808     /* the first try_id messages are known -> copy them to the newsaved list */
809     for( num = first_nr; num < list_len; num++ )
810     {
811         struct idlist   *new = save_str(&ctl->newsaved, 
812                                 str_from_nr_list(&ctl->oldsaved, num),
813                                 UID_UNSEEN);
814         new->val.status.num = num - first_nr + 1;
815     }
816
817     if( nolinear ) {
818         free_str_list(&ctl->oldsaved);
819         ctl->oldsaved = 0;
820         last = try_id;
821     }
822
823     *newp = *countp - try_id;
824     return 0;
825 }
826
827 static int pop3_getrange(int sock, 
828                          struct query *ctl,
829                          const char *folder,
830                          int *countp, int *newp, int *bytes)
831 /* get range of messages to be fetched */
832 {
833     int ok;
834     char buf [POPBUFSIZE+1];
835
836     /* Ensure that the new list is properly empty */
837     ctl->newsaved = (struct idlist *)NULL;
838
839 #ifdef MBOX
840     /* Alain Knaff suggests this, but it's not RFC standard */
841     if (folder)
842         if ((ok = gen_transact(sock, "MBOX %s", folder)))
843             return ok;
844 #endif /* MBOX */
845
846     /* get the total message count */
847     gen_send(sock, "STAT");
848     ok = pop3_ok(sock, buf);
849     if (ok == 0)
850         sscanf(buf,"%d %d", countp, bytes);
851     else
852         return(ok);
853
854     /*
855      * Newer, RFC-1725-conformant POP servers may not have the LAST command.
856      * We work as hard as possible to hide this ugliness, but it makes
857      * counting new messages intrinsically quadratic in the worst case.
858      */
859     last = 0;
860     *newp = -1;
861     if (*countp > 0 && !ctl->fetchall)
862     {
863         int fastuidl;
864         char id [IDLEN+1];
865
866         /* should we do fast uidl this time? */
867         fastuidl = ctl->fastuidl;
868         if (*countp > 7 &&              /* linear search is better if there are few mails! */
869             !ctl->flush &&              /* with flush, it is safer to disable fastuidl */
870             NUM_NONZERO (fastuidl))
871         {
872             if (fastuidl == 1)
873                 dofastuidl = 1;
874             else
875                 dofastuidl = ctl->fastuidlcount != 0;
876         }
877         else
878             dofastuidl = 0;
879
880         if (!ctl->server.uidl) {
881             gen_send(sock, "LAST");
882             ok = pop3_ok(sock, buf);
883         } else
884             ok = 1;
885         if (ok == 0)
886         {
887             if (sscanf(buf, "%d", &last) == 0)
888             {
889                 report(stderr, GT_("protocol error\n"));
890                 return(PS_ERROR);
891             }
892             *newp = (*countp - last);
893         }
894         else
895         {
896             if (dofastuidl)
897                 return(pop3_fastuidl( sock, ctl, *countp, newp));
898             /* grab the mailbox's UID list */
899             if ((ok = gen_transact(sock, "UIDL")) != 0)
900             {
901                 /* don't worry, yet! do it the slow way */
902                 if ((ok = pop3_slowuidl(sock, ctl, countp, newp)))
903                 {
904                     report(stderr, GT_("protocol error while fetching UIDLs\n"));
905                     return(PS_ERROR);
906                 }
907             }
908             else
909             {
910                 unsigned long unum;
911
912                 *newp = 0;
913                 while ((ok = gen_recv(sock, buf, sizeof(buf))) == PS_SUCCESS)
914                 {
915                     if (DOTLINE(buf))
916                         break;
917
918                     if (parseuid(buf, &unum, id, sizeof(id)) == PS_SUCCESS)
919                     {
920                         struct idlist   *old, *new;
921
922                         new = save_str(&ctl->newsaved, id, UID_UNSEEN);
923                         new->val.status.num = unum;
924
925                         if ((old = str_in_list(&ctl->oldsaved, id, FALSE)))
926                         {
927                             flag mark = old->val.status.mark;
928                             if (mark == UID_DELETED || mark == UID_EXPUNGED)
929                             {
930                                 /* XXX FIXME: switch 3 occurrences from
931                                  * (int)unum or (unsigned int)unum to
932                                  * remove the cast and use %lu - not now
933                                  * though, time for new release */
934                                 if (outlevel >= O_VERBOSE)
935                                     report(stderr, GT_("id=%s (num=%d) was deleted, but is still present!\n"), id, (int)unum);
936                                 /* just mark it as seen now! */
937                                 old->val.status.mark = mark = UID_SEEN;
938                             }
939                             new->val.status.mark = mark;
940                             if (mark == UID_UNSEEN)
941                             {
942                                 (*newp)++;
943                                 if (outlevel >= O_DEBUG)
944                                     report(stdout, GT_("%u is unseen\n"), (unsigned int)unum);
945                             }
946                         }
947                         else
948                         {
949                             (*newp)++;
950                             if (outlevel >= O_DEBUG)
951                                 report(stdout, GT_("%u is unseen\n"), (unsigned int)unum);
952                             /* add it to oldsaved also! In case, we do not
953                              * swap the lists (say, due to socket error),
954                              * the same mail will not be downloaded again.
955                              */
956                             old = save_str(&ctl->oldsaved, id, UID_UNSEEN);
957                             old->val.status.num = unum;
958                         }
959                     } else
960                         return PS_ERROR;
961                 }
962             }
963         }
964     }
965
966     return(PS_SUCCESS);
967 }
968
969 static int pop3_getpartialsizes(int sock, int first, int last, int *sizes)
970 /* capture the size of message #first */
971 {
972     int ok = 0, i;
973     char buf [POPBUFSIZE+1];
974     unsigned int num, size;
975
976     for (i = first; i <= last; i++) {
977         gen_send(sock, "LIST %d", i);
978         if ((ok = pop3_ok(sock, buf)) != 0)
979             return(ok);
980         if (sscanf(buf, "%u %u", &num, &size) == 2) {
981             if (num == i)
982                 sizes[i - first] = size;
983             else
984                 /* warn about possible attempt to induce buffer overrun
985                  *
986                  * we expect server reply message number and requested
987                  * message number to match */
988                 report(stderr, "Warning: ignoring bogus data for message sizes returned by server.\n");
989         }
990     }
991     return(ok);
992 }
993
994 static int pop3_getsizes(int sock, int count, int *sizes)
995 /* capture the sizes of all messages */
996 {
997     int ok;
998
999     if ((ok = gen_transact(sock, "LIST")) != 0)
1000         return(ok);
1001     else
1002     {
1003         char buf [POPBUFSIZE+1];
1004
1005         while ((ok = gen_recv(sock, buf, sizeof(buf))) == 0)
1006         {
1007             unsigned int num, size;
1008
1009             if (DOTLINE(buf))
1010                 break;
1011             else if (sscanf(buf, "%u %u", &num, &size) == 2) {
1012                 if (num > 0 && num <= count)
1013                     sizes[num - 1] = size;
1014                 else
1015                     /* warn about possible attempt to induce buffer overrun */
1016                     report(stderr, "Warning: ignoring bogus data for message sizes returned by server.\n");
1017             }
1018         }
1019
1020         return(ok);
1021     }
1022 }
1023
1024 static int pop3_is_old(int sock, struct query *ctl, int num)
1025 /* is the given message old? */
1026 {
1027     struct idlist *new;
1028     if (!ctl->oldsaved)
1029         return (num <= last);
1030     else if (dofastuidl)
1031     {
1032         char id [IDLEN+1];
1033
1034         if (num <= last)
1035             return(TRUE);
1036
1037         /* in fast uidl, we manipulate the old list only! */
1038
1039         if ((new = id_find(&ctl->oldsaved, num)))
1040         {
1041             /* we already have the id! */
1042             return(new->val.status.mark != UID_UNSEEN);
1043         }
1044
1045         /* get the uidl first! */
1046         if (pop3_getuidl(sock, num, id, sizeof(id)) != PS_SUCCESS)
1047             return(TRUE);
1048
1049         if ((new = str_in_list(&ctl->oldsaved, id, FALSE))) {
1050             /* we already have the id! */
1051             new->val.status.num = num;
1052             return(new->val.status.mark != UID_UNSEEN);
1053         }
1054
1055         /* save it */
1056         new = save_str(&ctl->oldsaved, id, UID_UNSEEN);
1057         new->val.status.num = num;
1058         return(FALSE);
1059     }
1060     else
1061         return ((new = id_find(&ctl->newsaved, num)) != NULL &&
1062             new->val.status.mark != UID_UNSEEN);
1063 }
1064
1065 #ifdef UNUSED
1066 /*
1067  * We could use this to fetch headers only as we do for IMAP.  The trouble 
1068  * is that there's no way to fetch the body only.  So the following RETR 
1069  * would have to re-fetch the header.  Enough messages have longer headers
1070  * than bodies to make this a net loss.
1071  */
1072 static int pop_fetch_headers(int sock, struct query *ctl,int number,int *lenp)
1073 /* request headers of nth message */
1074 {
1075     int ok;
1076     char buf[POPBUFSIZE+1];
1077
1078     gen_send(sock, "TOP %d 0", number);
1079     if ((ok = pop3_ok(sock, buf)) != 0)
1080         return(ok);
1081
1082     *lenp = -1;         /* we got sizes from the LIST response */
1083
1084     return(PS_SUCCESS);
1085 }
1086 #endif /* UNUSED */
1087
1088 static int pop3_fetch(int sock, struct query *ctl, int number, int *lenp)
1089 /* request nth message */
1090 {
1091     int ok;
1092     char buf[POPBUFSIZE+1];
1093
1094 #ifdef SDPS_ENABLE
1095     /*
1096      * See http://www.demon.net/services/mail/sdps-tech.html
1097      * for a description of what we're parsing here.
1098      */
1099     if (ctl->server.sdps)
1100     {
1101         int     linecount = 0;
1102
1103         sdps_envfrom = (char *)NULL;
1104         sdps_envto = (char *)NULL;
1105         gen_send(sock, "*ENV %d", number);
1106         do {
1107             if (gen_recv(sock, buf, sizeof(buf)))
1108             {
1109                 break;
1110             }
1111             linecount++;
1112             switch (linecount) {
1113             case 4:
1114                 /* No need to wrap envelope from address */
1115                 sdps_envfrom = xmalloc(strlen(buf)+1);
1116                 strcpy(sdps_envfrom,buf);
1117                 break;
1118             case 5:
1119                 /* Wrap address with To: <> so nxtaddr() likes it */
1120                 sdps_envto = xmalloc(strlen(buf)+7);
1121                 sprintf(sdps_envto,"To: <%s>",buf);
1122                 break;
1123             }
1124         } while
1125             (!(buf[0] == '.' && (buf[1] == '\r' || buf[1] == '\n' || buf[1] == '\0')));
1126     }
1127 #endif /* SDPS_ENABLE */
1128
1129     /*
1130      * Though the POP RFCs don't document this fact, on almost every
1131      * POP3 server I know of messages are marked "seen" only at the
1132      * time the OK response to a RETR is issued.
1133      *
1134      * This means we can use TOP to fetch the message without setting its
1135      * seen flag.  This is good!  It means that if the protocol exchange
1136      * craps out during the message, it will still be marked `unseen' on
1137      * the server.  (Exception: in early 1999 SpryNet's POP3 servers were
1138      * reported to mark messages seen on a TOP fetch.)
1139      *
1140      * However...*don't* do this if we're using keep to suppress deletion!
1141      * In that case, marking the seen flag is the only way to prevent the
1142      * message from being re-fetched on subsequent runs.
1143      *
1144      * Also use RETR (that means no TOP, no peek) if fetchall is on.
1145      * This gives us a workaround for servers like usa.net's that bungle
1146      * TOP.  It's pretty harmless because fetchall guarantees that any
1147      * message dropped by an interrupted RETR will be picked up on the
1148      * next poll of the site.
1149      *
1150      * We take advantage here of the fact that, according to all the
1151      * POP RFCs, "if the number of lines requested by the POP3 client
1152      * is greater than than the number of lines in the body, then the
1153      * POP3 server sends the entire message.").
1154      *
1155      * The line count passed (99999999) is the maximum value CompuServe will
1156      * accept; it's much lower than the natural value 2147483646 (the maximum
1157      * twos-complement signed 32-bit integer minus 1) */
1158     if (!peek_capable)
1159         gen_send(sock, "RETR %d", number);
1160     else
1161         gen_send(sock, "TOP %d 99999999", number);
1162     if ((ok = pop3_ok(sock, buf)) != 0)
1163         return(ok);
1164
1165     *lenp = -1;         /* we got sizes from the LIST response */
1166
1167     return(PS_SUCCESS);
1168 }
1169
1170 static void mark_uid_seen(struct query *ctl, int number)
1171 /* Tell the UID code we've seen this. */
1172 {
1173     struct idlist       *sdp;
1174
1175     if ((sdp = id_find(&ctl->newsaved, number)))
1176         sdp->val.status.mark = UID_SEEN;
1177     /* mark it as seen in oldsaved also! In case, we do not swap the lists
1178      * (say, due to socket error), the same mail will not be downloaded
1179      * again.
1180      */
1181     if ((sdp = id_find(&ctl->oldsaved, number)))
1182         sdp->val.status.mark = UID_SEEN;
1183 }
1184
1185 static int pop3_delete(int sock, struct query *ctl, int number)
1186 /* delete a given message */
1187 {
1188     int ok;
1189     mark_uid_seen(ctl, number);
1190     /* actually, mark for deletion -- doesn't happen until QUIT time */
1191     ok = gen_transact(sock, "DELE %d", number);
1192     if (ok != PS_SUCCESS)
1193         return(ok);
1194     delete_str(dofastuidl ? &ctl->oldsaved : &ctl->newsaved, number);
1195     return(PS_SUCCESS);
1196 }
1197
1198 static int pop3_mark_seen(int sock, struct query *ctl, int number)
1199 /* mark a given message as seen */
1200 {
1201     mark_uid_seen(ctl, number);
1202     return(PS_SUCCESS);
1203 }
1204
1205 static int pop3_logout(int sock, struct query *ctl)
1206 /* send logout command */
1207 {
1208     int ok;
1209
1210 #ifdef __UNUSED__
1211     /*
1212      * We used to do this in case the server marks messages deleted when seen.
1213      * (Yes, this has been reported, in the MercuryP/NLM server.
1214      * It's even legal under RFC 1939 (section 8) as a site policy.)
1215      * It interacted badly with UIDL, though.  Thomas Zajic wrote:
1216      * "Running 'fetchmail -F -v' and checking the logs, I found out
1217      * that fetchmail did in fact flush my mailbox properly, but sent
1218      * a RSET just before sending QUIT to log off.  This caused the
1219      * POP3 server to undo/forget about the previous DELEs, resetting
1220      * my mailbox to its original (ie.  unflushed) state. The
1221      * ~/.fetchids file did get flushed though, so the next time
1222      * fetchmail was run it saw all the old messages as new ones ..."
1223      */
1224      if (ctl->keep)
1225         gen_transact(sock, "RSET");
1226 #endif /* __UNUSED__ */
1227
1228     ok = gen_transact(sock, "QUIT");
1229     if (!ok)
1230         expunge_uids(ctl);
1231
1232     return(ok);
1233 }
1234
1235 static const struct method pop3 =
1236 {
1237     "POP3",             /* Post Office Protocol v3 */
1238     "pop3",             /* standard POP3 port */
1239     "pop3s",            /* ssl POP3 port */
1240     FALSE,              /* this is not a tagged protocol */
1241     TRUE,               /* this uses a message delimiter */
1242     pop3_ok,            /* parse command response */
1243     pop3_getauth,       /* get authorization */
1244     pop3_getrange,      /* query range of messages */
1245     pop3_getsizes,      /* we can get a list of sizes */
1246     pop3_getpartialsizes,       /* we can get the size of 1 mail */
1247     pop3_is_old,        /* how do we tell a message is old? */
1248     pop3_fetch,         /* request given message */
1249     NULL,               /* no way to fetch body alone */
1250     NULL,               /* no message trailer */
1251     pop3_delete,        /* how to delete a message */
1252     pop3_mark_seen,     /* how to mark a message as seen */
1253     NULL,               /* no action at end of mailbox */
1254     pop3_logout,        /* log out, we're done */
1255     FALSE,              /* no, we can't re-poll */
1256 };
1257
1258 int doPOP3 (struct query *ctl)
1259 /* retrieve messages using POP3 */
1260 {
1261 #ifndef MBOX
1262     if (ctl->mailboxes->id) {
1263         fprintf(stderr,GT_("Option --remote is not supported with POP3\n"));
1264         return(PS_SYNTAX);
1265     }
1266 #endif /* MBOX */
1267     set_peek_capable(ctl); /* XXX FIXME: is this needed or do we always
1268                               call this from pop3_getauth anyways? */
1269     return(do_protocol(ctl, &pop3));
1270 }
1271 #endif /* POP3_ENABLE */
1272
1273 /* pop3.c ends here */