]> Pileus Git - ~andy/fetchmail/blob - imap.c
1f62f696c5be76d5420c1392ff262bee34105d72
[~andy/fetchmail] / imap.c
1 /*
2  * imap.c -- IMAP2bis/IMAP4 protocol methods
3  *
4  * Copyright 1997 by Eric S. Raymond
5  * For license terms, see the file COPYING in this directory.
6  */
7
8 #include  "config.h"
9 #include  <stdio.h>
10 #include  <string.h>
11 #include  <ctype.h>
12 #if defined(STDC_HEADERS)
13 #include  <stdlib.h>
14 #include  <limits.h>
15 #include  <errno.h>
16 #endif
17 #include  "fetchmail.h"
18 #include  "socket.h"
19
20 #include  "i18n.h"
21
22 /* imap_version values */
23 #define IMAP2           -1      /* IMAP2 or IMAP2BIS, RFC1176 */
24 #define IMAP4           0       /* IMAP4 rev 0, RFC1730 */
25 #define IMAP4rev1       1       /* IMAP4 rev 1, RFC2060 */
26
27 /* global variables: please reinitialize them explicitly for proper
28  * working in daemon mode */
29
30 /* TODO: session variables to be initialized before server greeting */
31 static int preauth = FALSE;
32
33 /* session variables initialized in capa_probe() or imap_getauth() */
34 static char capabilities[MSGBUFSIZE+1];
35 static int imap_version = IMAP4;
36 static flag do_idle = FALSE, has_idle = FALSE;
37 static int expunge_period = 1;
38
39 /* mailbox variables initialized in imap_getrange() */
40 static int count = 0, recentcount = 0, unseen = 0, deletions = 0;
41 static unsigned int startcount = 1;
42 static int expunged = 0;
43 static unsigned int *unseen_messages;
44
45 /* for "IMAP> EXPUNGE" */
46 static int recentcount_ok = 0;
47
48 /* for "IMAP> IDLE" */
49 static int saved_timeout = 0;
50
51 static int imap_ok(int sock, char *argbuf)
52 /* parse command response */
53 {
54     char buf[MSGBUFSIZE+1];
55
56     do {
57         int     ok;
58         char    *cp;
59
60         if ((ok = gen_recv(sock, buf, sizeof(buf))))
61             return(ok);
62
63         /* all tokens in responses are caseblind */
64         for (cp = buf; *cp; cp++)
65             if (islower((unsigned char)*cp))
66                 *cp = toupper((unsigned char)*cp);
67
68         /* interpret untagged status responses
69          * First check if we really have an untagged response, starting
70          * with "*" SPACE. Then, for each individual check, use a BLANK
71          * before the word to avoid confusion with the \Recent flag or
72          * similar */
73         if (buf[0] == '*' && buf[1] == ' ') {
74             if (strstr(buf, " CAPABILITY")) {
75                 strlcpy(capabilities, buf + 12, sizeof(capabilities));
76             }
77             else if (strstr(buf, " EXISTS"))
78             {
79                 count = atoi(buf+2);
80                 /*
81                  * Don't trust the message count passed by the server.
82                  * Without this check, it might be possible to do a
83                  * DNS-spoofing attack that would pass back a ridiculous 
84                  * count, and allocate a malloc area that would overlap
85                  * a portion of the stack.
86                  */
87                 if (count > INT_MAX/sizeof(int))
88                 {
89                     report(stderr, GT_("bogus message count!"));
90                     return(PS_PROTOCOL);
91                 }
92
93                 /*
94                  * Nasty kluge to handle RFC2177 IDLE.  If we know we're idling
95                  * we can't wait for the tag matching the IDLE; we have to tell the
96                  * server the IDLE is finished by shipping back a DONE when we
97                  * see an EXISTS.  Only after that will a tagged response be
98                  * shipped.  The idling flag also gets cleared on a timeout.
99                  */
100                 if (stage == STAGE_IDLE)
101                 {
102                     /* If IDLE isn't supported, we were only sending NOOPs anyway. */
103                     if (has_idle)
104                     {
105                         /* we do our own write and report here to disable tagging */
106                         SockWrite(sock, "DONE\r\n", 6);
107                         if (outlevel >= O_MONITOR)
108                             report(stdout, "IMAP> DONE\n");
109                     }
110
111                     mytimeout = saved_timeout;
112                     stage = STAGE_FETCH;
113                 }
114             }
115             else if (strstr(buf, " RECENT"))
116             {
117                 recentcount_ok = 1;
118                 recentcount = atoi(buf+2);
119             }
120             else if (strstr(buf, " EXPUNGE"))
121             {
122                 /* the response "* 10 EXPUNGE" means that the currently
123                  * tenth (i.e. only one) message has been deleted */
124                 if (atoi(buf+2) > 0)
125                     count--;
126                 if (count < 0)
127                     count = 0;
128             }
129             else if (strstr(buf, " PREAUTH"))
130             {
131                 preauth = TRUE;
132             }
133                 /*
134                  * The server may decide to make the mailbox read-only, 
135                  * which causes fetchmail to go into a endless loop
136                  * fetching the same message over and over again. 
137                  * 
138                  * However, for check_only, we use EXAMINE which will
139                  * mark the mailbox read-only as per the RFC.
140                  * 
141                  * This checks for the condition and aborts if 
142                  * the mailbox is read-only. 
143                  *
144                  * See RFC 2060 section 6.3.1 (SELECT).
145                  * See RFC 2060 section 6.3.2 (EXAMINE).
146                  */ 
147             else if (!check_only && strstr(buf, "[READ-ONLY]"))
148             {
149                 return(PS_LOCKBUSY);
150             }
151         }
152     } while
153         (tag[0] != '\0' && strncmp(buf, tag, strlen(tag)));
154
155     if (tag[0] == '\0')
156     {
157         if (argbuf)
158             strcpy(argbuf, buf);
159         return(PS_SUCCESS); 
160     }
161     else
162     {
163         char    *cp;
164
165         /* skip the tag */
166         for (cp = buf; !isspace((unsigned char)*cp); cp++)
167             continue;
168         while (isspace((unsigned char)*cp))
169             cp++;
170
171         if (strncasecmp(cp, "OK", 2) == 0)
172         {
173             if (argbuf)
174                 strcpy(argbuf, cp);
175             return(PS_SUCCESS);
176         }
177         else if (strncasecmp(cp, "BAD", 3) == 0)
178             return(PS_ERROR);
179         else if (strncasecmp(cp, "NO", 2) == 0)
180         {
181             if (stage == STAGE_GETAUTH) 
182                 return(PS_AUTHFAIL);    /* RFC2060, 6.2.2 */
183             else
184                 return(PS_ERROR);
185         }
186         else
187             return(PS_PROTOCOL);
188     }
189 }
190
191 #ifdef NTLM_ENABLE
192 #include "ntlm.h"
193
194 /*
195  * NTLM support by Grant Edwards.
196  *
197  * Handle MS-Exchange NTLM authentication method.  This is the same
198  * as the NTLM auth used by Samba for SMB related services. We just
199  * encode the packets in base64 instead of sending them out via a
200  * network interface.
201  * 
202  * Much source (ntlm.h, smb*.c smb*.h) was borrowed from Samba.
203  */
204
205 static int do_imap_ntlm(int sock, struct query *ctl)
206 {
207     tSmbNtlmAuthRequest request;
208     tSmbNtlmAuthChallenge challenge;
209     tSmbNtlmAuthResponse response;
210
211     char msgbuf[2048];
212     int result,len;
213
214     gen_send(sock, "AUTHENTICATE NTLM");
215
216     if ((result = gen_recv(sock, msgbuf, sizeof msgbuf)))
217         return result;
218   
219     if (msgbuf[0] != '+')
220         return PS_AUTHFAIL;
221   
222     buildSmbNtlmAuthRequest(&request,ctl->remotename,NULL);
223
224     if (outlevel >= O_DEBUG)
225         dumpSmbNtlmAuthRequest(stdout, &request);
226
227     memset(msgbuf,0,sizeof msgbuf);
228     to64frombits (msgbuf, (unsigned char*)&request, SmbLength(&request));
229   
230     if (outlevel >= O_MONITOR)
231         report(stdout, "IMAP> %s\n", msgbuf);
232   
233     strcat(msgbuf,"\r\n");
234     SockWrite (sock, msgbuf, strlen (msgbuf));
235
236     if ((gen_recv(sock, msgbuf, sizeof msgbuf)))
237         return result;
238   
239     len = from64tobits ((char*)&challenge, msgbuf, sizeof(challenge));
240     
241     if (outlevel >= O_DEBUG)
242         dumpSmbNtlmAuthChallenge(stdout, &challenge);
243     
244     buildSmbNtlmAuthResponse(&challenge, &response,ctl->remotename,ctl->password);
245   
246     if (outlevel >= O_DEBUG)
247         dumpSmbNtlmAuthResponse(stdout, &response);
248   
249     memset(msgbuf,0,sizeof msgbuf);
250     to64frombits (msgbuf, (unsigned char*)&response, SmbLength(&response));
251
252     if (outlevel >= O_MONITOR)
253         report(stdout, "IMAP> %s\n", msgbuf);
254       
255     strcat(msgbuf,"\r\n");
256     SockWrite (sock, msgbuf, strlen (msgbuf));
257   
258     if ((result = gen_recv (sock, msgbuf, sizeof msgbuf)))
259         return result;
260   
261     if (strstr (msgbuf, "OK"))
262         return PS_SUCCESS;
263     else
264         return PS_AUTHFAIL;
265 }
266 #endif /* NTLM */
267
268 static int imap_canonicalize(char *result, char *raw, int maxlen)
269 /* encode an IMAP password as per RFC1730's quoting conventions */
270 {
271     int i, j;
272
273     j = 0;
274     for (i = 0; i < strlen(raw) && i < maxlen; i++)
275     {
276         if ((raw[i] == '\\') || (raw[i] == '"'))
277             result[j++] = '\\';
278         result[j++] = raw[i];
279     }
280     result[j] = '\0';
281
282     return(i);
283 }
284
285 static void capa_probe(int sock, struct query *ctl)
286 /* set capability variables from a CAPA probe */
287 {
288     int ok;
289
290     /* probe to see if we're running IMAP4 and can use RFC822.PEEK */
291     capabilities[0] = '\0';
292     if ((ok = gen_transact(sock, "CAPABILITY")) == PS_SUCCESS)
293     {
294         char    *cp;
295
296         /* capability checks are supposed to be caseblind */
297         for (cp = capabilities; *cp; cp++)
298             *cp = toupper((unsigned char)*cp);
299
300         /* UW-IMAP server 10.173 notifies in all caps, but RFC2060 says we
301            should expect a response in mixed-case */
302         if (strstr(capabilities, "IMAP4REV1"))
303         {
304             imap_version = IMAP4rev1;
305             if (outlevel >= O_DEBUG)
306                 report(stdout, GT_("Protocol identified as IMAP4 rev 1\n"));
307         }
308         else
309         {
310             imap_version = IMAP4;
311             if (outlevel >= O_DEBUG)
312                 report(stdout, GT_("Protocol identified as IMAP4 rev 0\n"));
313         }
314     }
315     else if (ok == PS_ERROR)
316     {
317         imap_version = IMAP2;
318         if (outlevel >= O_DEBUG)
319             report(stdout, GT_("Protocol identified as IMAP2 or IMAP2BIS\n"));
320     }
321
322     /* 
323      * Handle idling.  We depend on coming through here on startup
324      * and after each timeout (including timeouts during idles).
325      */
326     do_idle = ctl->idle;
327     if (ctl->idle)
328     {
329         if (strstr(capabilities, "IDLE"))
330             has_idle = TRUE;
331         else
332             has_idle = FALSE;
333         if (outlevel >= O_VERBOSE)
334             report(stdout, GT_("will idle after poll\n"));
335     }
336
337     peek_capable = (imap_version >= IMAP4);
338 }
339
340 static int imap_getauth(int sock, struct query *ctl, char *greeting)
341 /* apply for connection authorization */
342 {
343     int ok = 0;
344 #ifdef SSL_ENABLE
345     flag did_stls = FALSE;
346 #endif /* SSL_ENABLE */
347
348     /*
349      * Assumption: expunges are cheap, so we want to do them
350      * after every message unless user said otherwise.
351      */
352     if (NUM_SPECIFIED(ctl->expunge))
353         expunge_period = NUM_VALUE_OUT(ctl->expunge);
354     else
355         expunge_period = 1;
356
357     capa_probe(sock, ctl);
358
359     /* 
360      * If either (a) we saw a PREAUTH token in the greeting, or
361      * (b) the user specified ssh preauthentication, then we're done.
362      */
363     if (preauth || ctl->server.authenticate == A_SSH)
364     {
365         preauth = FALSE;  /* reset for the next session */
366         return(PS_SUCCESS);
367     }
368
369 #ifdef SSL_ENABLE
370     if ((!ctl->sslproto || !strcmp(ctl->sslproto,"tls1"))
371         && !ctl->use_ssl
372         && strstr(capabilities, "STARTTLS"))
373     {
374            char *realhost;
375
376            realhost = ctl->server.via ? ctl->server.via : ctl->server.pollname;
377            ok = gen_transact(sock, "STARTTLS");
378
379            /* We use "tls1" instead of ctl->sslproto, as we want STARTTLS,
380             * not other SSL protocols
381             */
382            if (ok == PS_SUCCESS &&
383                SSLOpen(sock,ctl->sslcert,ctl->sslkey,"tls1",ctl->sslcertck, ctl->sslcertpath,ctl->sslfingerprint,realhost,ctl->server.pollname) == -1)
384            {
385                if (!ctl->sslproto && !ctl->wehaveauthed)
386                {
387                    ctl->sslproto = xstrdup("");
388                    /* repoll immediately */
389                    return(PS_REPOLL);
390                }
391                report(stderr,
392                       GT_("SSL connection failed.\n"));
393                return PS_SOCKET;
394            }
395            did_stls = TRUE;
396
397            /*
398             * RFC 2595 says this:
399             *
400             * "Once TLS has been started, the client MUST discard cached
401             * information about server capabilities and SHOULD re-issue the
402             * CAPABILITY command.  This is necessary to protect against
403             * man-in-the-middle attacks which alter the capabilities list prior
404             * to STARTTLS.  The server MAY advertise different capabilities
405             * after STARTTLS."
406             */
407            capa_probe(sock, ctl);
408     }
409 #endif /* SSL_ENABLE */
410
411     /*
412      * Time to authenticate the user.
413      * Try the protocol variants that don't require passwords first.
414      */
415     ok = PS_AUTHFAIL;
416
417 #ifdef GSSAPI
418     if ((ctl->server.authenticate == A_ANY 
419          || ctl->server.authenticate == A_GSSAPI)
420         && strstr(capabilities, "AUTH=GSSAPI"))
421     {
422         if ((ok = do_gssauth(sock, "AUTHENTICATE", "imap",
423                         ctl->server.truename, ctl->remotename)))
424         {
425             /* SASL cancellation of authentication */
426             gen_send(sock, "*");
427             if (ctl->server.authenticate != A_ANY)
428                 return ok;
429         } else  {
430             return ok;
431         }
432     }
433 #endif /* GSSAPI */
434
435 #ifdef KERBEROS_V4
436     if ((ctl->server.authenticate == A_ANY 
437          || ctl->server.authenticate == A_KERBEROS_V4
438          || ctl->server.authenticate == A_KERBEROS_V5) 
439         && strstr(capabilities, "AUTH=KERBEROS_V4"))
440     {
441         if ((ok = do_rfc1731(sock, "AUTHENTICATE", ctl->server.truename)))
442         {
443             /* SASL cancellation of authentication */
444             gen_send(sock, "*");
445             if(ctl->server.authenticate != A_ANY)
446                 return ok;
447         }
448         else
449             return ok;
450     }
451 #endif /* KERBEROS_V4 */
452
453     /*
454      * No such luck.  OK, now try the variants that mask your password
455      * in a challenge-response.
456      */
457
458     if ((ctl->server.authenticate == A_ANY && strstr(capabilities, "AUTH=CRAM-MD5"))
459         || ctl->server.authenticate == A_CRAM_MD5)
460     {
461         if ((ok = do_cram_md5 (sock, "AUTHENTICATE", ctl, NULL)))
462         {
463             /* SASL cancellation of authentication */
464             gen_send(sock, "*");
465             if(ctl->server.authenticate != A_ANY)
466                 return ok;
467         }
468         else
469             return ok;
470     }
471
472 #ifdef OPIE_ENABLE
473     if ((ctl->server.authenticate == A_ANY 
474          || ctl->server.authenticate == A_OTP)
475         && strstr(capabilities, "AUTH=X-OTP")) {
476         if ((ok = do_otp(sock, "AUTHENTICATE", ctl)))
477         {
478             /* SASL cancellation of authentication */
479             gen_send(sock, "*");
480             if(ctl->server.authenticate != A_ANY)
481                 return ok;
482         } else {
483             return ok;
484         }
485     }
486 #else
487     if (ctl->server.authenticate == A_OTP)
488     {
489         report(stderr, 
490            GT_("Required OTP capability not compiled into fetchmail\n"));
491     }
492 #endif /* OPIE_ENABLE */
493
494 #ifdef NTLM_ENABLE
495     if ((ctl->server.authenticate == A_ANY 
496          || ctl->server.authenticate == A_NTLM) 
497         && strstr (capabilities, "AUTH=NTLM")) {
498         if ((ok = do_imap_ntlm(sock, ctl)))
499         {
500             /* SASL cancellation of authentication */
501             gen_send(sock, "*");
502             if(ctl->server.authenticate != A_ANY)
503                 return ok;
504         }
505         else
506             return(ok);
507     }
508 #else
509     if (ctl->server.authenticate == A_NTLM)
510     {
511         report(stderr, 
512            GT_("Required NTLM capability not compiled into fetchmail\n"));
513     }
514 #endif /* NTLM_ENABLE */
515
516 #ifdef __UNUSED__       /* The Cyrus IMAP4rev1 server chokes on this */
517     /* this handles either AUTH=LOGIN or AUTH-LOGIN */
518     if ((imap_version >= IMAP4rev1) && (!strstr(capabilities, "LOGIN")))
519     {
520         report(stderr, 
521                GT_("Required LOGIN capability not supported by server\n"));
522     }
523 #endif /* __UNUSED__ */
524
525     /* 
526      * We're stuck with sending the password en clair.
527      * The reason for this odd-looking logic is that some
528      * servers return LOGINDISABLED even though login 
529      * actually works.  So arrange things in such a way that
530      * setting auth passwd makes it ignore this capability.
531      */
532     if((ctl->server.authenticate==A_ANY&&!strstr(capabilities,"LOGINDISABLED"))
533         || ctl->server.authenticate == A_PASSWORD)
534     {
535         /* these sizes guarantee no buffer overflow */
536         char *remotename, *password;
537         size_t rnl, pwl;
538         rnl = 2 * strlen(ctl->remotename) + 1;
539         pwl = 2 * strlen(ctl->password) + 1;
540         remotename = xmalloc(rnl);
541         password = xmalloc(pwl);
542
543         imap_canonicalize(remotename, ctl->remotename, rnl);
544         imap_canonicalize(password, ctl->password, pwl);
545
546         snprintf(shroud, sizeof (shroud), "\"%s\"", password);
547         ok = gen_transact(sock, "LOGIN \"%s\" \"%s\"", remotename, password);
548         shroud[0] = '\0';
549         free(password);
550         free(remotename);
551 #ifdef SSL_ENABLE
552         /* this is for servers which claim to support TLS, but actually
553          * don't! */
554         if (did_stls && ok == PS_SOCKET && !ctl->sslproto && !ctl->wehaveauthed)
555         {
556             ctl->sslproto = xstrdup("");
557             /* repoll immediately */
558             ok = PS_REPOLL;
559         }
560 #endif
561         if (ok)
562         {
563             /* SASL cancellation of authentication */
564             gen_send(sock, "*");
565             if(ctl->server.authenticate != A_ANY)
566                 return ok;
567         }
568         else
569             return(ok);
570     }
571
572     return(ok);
573 }
574
575 static int internal_expunge(int sock)
576 /* ship an expunge, resetting associated counters */
577 {
578     int ok;
579
580     recentcount_ok = 0;
581
582     if ((ok = gen_transact(sock, "EXPUNGE")))
583         return(ok);
584
585     /* some servers do not report RECENT after an EXPUNGE. in this case, 
586      * the previous value of recentcount is just ignored. */
587     if (!recentcount_ok)
588         recentcount = 0;
589
590     expunged += deletions;
591     deletions = 0;
592
593 #ifdef IMAP_UID /* not used */
594     expunge_uids(ctl);
595 #endif /* IMAP_UID */
596
597     return(PS_SUCCESS);
598 }
599
600 static int imap_idle(int sock)
601 /* start an RFC2177 IDLE, or fake one if unsupported */
602 {
603     int ok;
604
605     stage = STAGE_IDLE;
606     saved_timeout = mytimeout;
607
608     if (has_idle) {
609         /* special timeout to terminate the IDLE and re-issue it
610          * at least every 28 minutes:
611          * (the server may have an inactivity timeout) */
612         mytimeout = 1680; /* 28 min */
613         /* enter IDLE mode */
614         ok = gen_transact(sock, "IDLE");
615
616         if (ok == PS_IDLETIMEOUT) {
617             /* send "DONE" continuation */
618             SockWrite(sock, "DONE\r\n", 6);
619             if (outlevel >= O_MONITOR)
620                 report(stdout, "IMAP> DONE\n");
621         } else
622             /* not idle timeout */
623             return ok;
624     } else {  /* no idle support, fake it */
625         /* when faking an idle, we can't assume the server will
626          * send us the new messages out of the blue (RFC2060);
627          * this timeout is potentially the delay before we notice
628          * new mail (can be small since NOOP checking is cheap) */
629         mytimeout = 28;
630         ok = gen_transact(sock, "NOOP");
631         /* if there's an error (not likely) or we just found mail (stage 
632          * has changed, timeout has also been restored), we're done */
633         if (ok != 0 || stage != STAGE_IDLE)
634             return(ok);
635
636         /* wait (briefly) for an unsolicited status update */
637         ok = imap_ok(sock, NULL);
638         /* again, this is new mail or an error */
639         if (ok != PS_IDLETIMEOUT)
640             return(ok);
641     }
642
643     /* restore normal timeout value */
644     mytimeout = saved_timeout;
645     stage = STAGE_FETCH;
646
647     /* get OK IDLE message */
648     if (has_idle)
649         return imap_ok(sock, NULL);
650
651     return PS_SUCCESS;
652 }
653
654 static int imap_getrange(int sock, 
655                          struct query *ctl, 
656                          const char *folder, 
657                          int *countp, int *newp, int *bytes)
658 /* get range of messages to be fetched */
659 {
660     int ok;
661     char buf[MSGBUFSIZE+1], *cp;
662
663     /* find out how many messages are waiting */
664     *bytes = -1;
665
666     if (pass > 1)
667     {
668         /* deleted mails have already been expunged by
669          * end_mailbox_poll().
670          *
671          * recentcount is already set here by the last imap command which
672          * returned RECENT on detecting new mail. if recentcount is 0, wait
673          * for new mail.
674          *
675          * this is a while loop because imap_idle() might return on other
676          * mailbox changes also */
677         while (recentcount == 0 && do_idle) {
678             smtp_close(ctl, 1);
679             ok = imap_idle(sock);
680             if (ok)
681             {
682                 report(stderr, GT_("re-poll failed\n"));
683                 return(ok);
684             }
685         }
686         /* if recentcount is 0, return no mail */
687         if (recentcount == 0)
688                 count = 0;
689         if (outlevel >= O_DEBUG)
690             report(stdout, ngettext("%d message waiting after re-poll\n",
691                                     "%d messages waiting after re-poll\n",
692                                     count), count);
693     }
694     else
695     {
696         count = 0;
697         ok = gen_transact(sock, 
698                           check_only ? "EXAMINE \"%s\"" : "SELECT \"%s\"",
699                           folder ? folder : "INBOX");
700         if (ok != 0)
701         {
702             report(stderr, GT_("mailbox selection failed\n"));
703             return(ok);
704         }
705         else if (outlevel >= O_DEBUG)
706             report(stdout, ngettext("%d message waiting after first poll\n",
707                                     "%d messages waiting after first poll\n",
708                                     count), count);
709
710         /* no messages?  then we may need to idle until we get some */
711         while (count == 0 && do_idle) {
712             ok = imap_idle(sock);
713             if (ok)
714             {
715                 report(stderr, GT_("re-poll failed\n"));
716                 return(ok);
717             }
718         }
719
720         /*
721          * We should have an expunge here to
722          * a) avoid fetching deleted mails during 'fetchall'
723          * b) getting a wrong count of mails during 'no fetchall'
724          */
725         if (!check_only && !ctl->keep && count > 0)
726         {
727             ok = internal_expunge(sock);
728             if (ok)
729             {
730                 report(stderr, GT_("expunge failed\n"));
731                 return(ok);
732             }
733             if (outlevel >= O_DEBUG)
734                 report(stdout, ngettext("%d message waiting after expunge\n",
735                                         "%d messages waiting after expunge\n",
736                                         count), count);
737         }
738     }
739
740     *countp = count;
741     recentcount = 0;
742     startcount = 1;
743
744     /* OK, now get a count of unseen messages and their indices */
745     if (!ctl->fetchall && count > 0)
746     {
747         if (unseen_messages)
748             free(unseen_messages);
749         unseen_messages = xmalloc(count * sizeof(unsigned int));
750         memset(unseen_messages, 0, count * sizeof(unsigned int));
751         unseen = 0;
752
753         /* don't count deleted messages, in case user enabled keep last time */
754         gen_send(sock, "SEARCH UNSEEN NOT DELETED");
755         do {
756             ok = gen_recv(sock, buf, sizeof(buf));
757             if (ok != 0)
758             {
759                 report(stderr, GT_("search for unseen messages failed\n"));
760                 return(PS_PROTOCOL);
761             }
762             else if ((cp = strstr(buf, "* SEARCH")))
763             {
764                 char    *ep;
765
766                 cp += 8;        /* skip "* SEARCH" */
767                 /* startcount is higher than count so that if there are no
768                  * unseen messages, imap_getsizes() will not need to do
769                  * anything! */
770                 startcount = count + 1;
771
772                 while (*cp && unseen < count)
773                 {
774                     /* skip whitespace */
775                     while (*cp && isspace((unsigned char)*cp))
776                         cp++;
777                     if (*cp) 
778                     {
779                         unsigned int um;
780                         /*
781                          * Message numbers are between 1 and 2^32 inclusive,
782                          * so unsigned int is large enough.
783                          */
784                         um=(unsigned int)strtol(cp,&ep,10);
785                         if (um <= count)
786                         {
787                             unseen_messages[unseen++] = um;
788                             if (outlevel >= O_DEBUG)
789                                 report(stdout, GT_("%u is unseen\n"), um);
790                             if (startcount > um)
791                                 startcount = um;
792                         }
793                         cp = ep;
794                     }
795                 }
796             }
797         } while
798             (tag[0] != '\0' && strncmp(buf, tag, strlen(tag)));
799
800         if (outlevel >= O_DEBUG && unseen > 0)
801             report(stdout, GT_("%u is first unseen\n"), startcount);
802     } else
803         unseen = -1;
804
805     *newp = unseen;
806     expunged = 0;
807     deletions = 0;
808
809     return(PS_SUCCESS);
810 }
811
812 static int imap_getpartialsizes(int sock, int first, int last, int *sizes)
813 /* capture the sizes of messages #first-#last */
814 {
815     char buf [MSGBUFSIZE+1];
816
817     /*
818      * Some servers (as in, PMDF5.1-9.1 under OpenVMS 6.1)
819      * won't accept 1:1 as valid set syntax.  Some implementors
820      * should be taken out and shot for excessive anality.
821      *
822      * Microsoft Exchange (brain-dead piece of crap that it is) 
823      * sometimes gets its knickers in a knot about bodiless messages.
824      * You may see responses like this:
825      *
826      *  fetchmail: IMAP> A0004 FETCH 1:9 RFC822.SIZE
827      *  fetchmail: IMAP< * 2 FETCH (RFC822.SIZE 1187)
828      *  fetchmail: IMAP< * 3 FETCH (RFC822.SIZE 3954)
829      *  fetchmail: IMAP< * 4 FETCH (RFC822.SIZE 1944)
830      *  fetchmail: IMAP< * 5 FETCH (RFC822.SIZE 2933)
831      *  fetchmail: IMAP< * 6 FETCH (RFC822.SIZE 1854)
832      *  fetchmail: IMAP< * 7 FETCH (RFC822.SIZE 34054)
833      *  fetchmail: IMAP< * 8 FETCH (RFC822.SIZE 5561)
834      *  fetchmail: IMAP< * 9 FETCH (RFC822.SIZE 1101)
835      *  fetchmail: IMAP< A0004 NO The requested item could not be found.
836      *
837      * This means message 1 has only headers.  For kicks and grins
838      * you can telnet in and look:
839      *  A003 FETCH 1 FULL
840      *  A003 NO The requested item could not be found.
841      *  A004 fetch 1 rfc822.header
842      *  A004 NO The requested item could not be found.
843      *  A006 FETCH 1 BODY
844      *  * 1 FETCH (BODY ("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 35 3))
845      *  A006 OK FETCH completed.
846      *
847      * To get around this, we terminate the read loop on a NO and count
848      * on the fact that the sizes array has been preinitialized with a
849      * known-bad size value.
850      */
851
852     /* expunges change the fetch numbers */
853     first -= expunged;
854     last -= expunged;
855
856     if (last == first)
857         gen_send(sock, "FETCH %d RFC822.SIZE", last);
858     else if (last > first)
859         gen_send(sock, "FETCH %d:%d RFC822.SIZE", first, last);
860     else /* no unseen messages! */
861         return(PS_SUCCESS);
862     for (;;)
863     {
864         unsigned int num, size;
865         int ok;
866         char *cp;
867
868         if ((ok = gen_recv(sock, buf, sizeof(buf))))
869             return(ok);
870         /* we want response matching to be case-insensitive */
871         for (cp = buf; *cp; cp++)
872             *cp = toupper((unsigned char)*cp);
873         /* an untagged NO means that a message was not readable */
874         if (strstr(buf, "* NO"))
875             ;
876         else if (strstr(buf, "OK") || strstr(buf, "NO"))
877             break;
878         else if (sscanf(buf, "* %u FETCH (RFC822.SIZE %u)", &num, &size) == 2) 
879         {
880             if (num >= first && num <= last)
881                 sizes[num - first] = size;
882             else
883                 report(stderr, "Warning: ignoring bogus data for message sizes returned by the server.\n");
884         }
885     }
886
887     return(PS_SUCCESS);
888 }
889
890 static int imap_getsizes(int sock, int count, int *sizes)
891 /* capture the sizes of all messages */
892 {
893     return imap_getpartialsizes(sock, 1, count, sizes);
894 }
895
896 static int imap_is_old(int sock, struct query *ctl, int number)
897 /* is the given message old? */
898 {
899     flag seen = TRUE;
900     int i;
901
902     /* 
903      * Expunges change the fetch numbers, but unseen_messages contains
904      * indices from before any expungees were done.  So neither the
905      * argument nor the values in message_sequence need to be decremented.
906      */
907
908     seen = TRUE;
909     for (i = 0; i < unseen; i++)
910         if (unseen_messages[i] == number)
911         {
912             seen = FALSE;
913             break;
914         }
915
916     return(seen);
917 }
918
919 static char *skip_token(char *ptr)
920 {
921     while(isspace((unsigned char)*ptr)) ptr++;
922     while(!isspace((unsigned char)*ptr) && !iscntrl((unsigned char)*ptr)) ptr++;
923     while(isspace((unsigned char)*ptr)) ptr++;
924     return(ptr);
925 }
926
927 static int imap_fetch_headers(int sock, struct query *ctl,int number,int *lenp)
928 /* request headers of nth message */
929 {
930     char buf [MSGBUFSIZE+1];
931     int num;
932
933     /* expunges change the fetch numbers */
934     number -= expunged;
935
936     /*
937      * This is blessed by RFC1176, RFC1730, RFC2060.
938      * According to the RFCs, it should *not* set the \Seen flag.
939      */
940     gen_send(sock, "FETCH %d RFC822.HEADER", number);
941
942     /* looking for FETCH response */
943     for (;;) 
944     {
945         int     ok;
946         char    *ptr;
947
948         if ((ok = gen_recv(sock, buf, sizeof(buf))))
949             return(ok);
950         ptr = skip_token(buf);  /* either "* " or "AXXXX " */
951         if (sscanf(ptr, "%d FETCH (%*s {%d}", &num, lenp) == 2)
952             break;
953         /* try to recover from chronically fucked-up M$ Exchange servers */
954         else if (!strncmp(ptr, "NO", 2))
955         {
956             /* wait for a tagged response */
957             if (strstr (buf, "* NO"))
958                 imap_ok (sock, 0);
959             return(PS_TRANSIENT);
960         }
961         else if (!strncmp(ptr, "BAD", 3))
962         {
963             /* wait for a tagged response */
964             if (strstr (buf, "* BAD"))
965                 imap_ok (sock, 0);
966             return(PS_TRANSIENT);
967         }
968     }
969
970     if (num != number)
971         return(PS_ERROR);
972     else
973         return(PS_SUCCESS);
974 }
975
976 static int imap_fetch_body(int sock, struct query *ctl, int number, int *lenp)
977 /* request body of nth message */
978 {
979     char buf [MSGBUFSIZE+1], *cp;
980     int num;
981
982     /* expunges change the fetch numbers */
983     number -= expunged;
984
985     /*
986      * If we're using IMAP4, we can fetch the message without setting its
987      * seen flag.  This is good!  It means that if the protocol exchange
988      * craps out during the message, it will still be marked `unseen' on
989      * the server.
990      *
991      * According to RFC2060, and Mark Crispin the IMAP maintainer,
992      * FETCH %d BODY[TEXT] and RFC822.TEXT are "functionally 
993      * equivalent".  However, we know of at least one server that
994      * treats them differently in the presence of MIME attachments;
995      * the latter form downloads the attachment, the former does not.
996      * The server is InterChange, and the fool who implemented this
997      * misfeature ought to be strung up by his thumbs.  
998      *
999      * When I tried working around this by disabling use of the 4rev1 form,
1000      * I found that doing this breaks operation with M$ Exchange.
1001      * Annoyingly enough, Exchange's refusal to cope is technically legal
1002      * under RFC2062.  Trust Microsoft, the Great Enemy of interoperability
1003      * standards, to find a way to make standards compliance irritating....
1004      */
1005     switch (imap_version)
1006     {
1007     case IMAP4rev1:     /* RFC 2060 */
1008         gen_send(sock, "FETCH %d BODY.PEEK[TEXT]", number);
1009         break;
1010
1011     case IMAP4:         /* RFC 1730 */
1012         gen_send(sock, "FETCH %d RFC822.TEXT.PEEK", number);
1013         break;
1014
1015     default:            /* RFC 1176 */
1016         gen_send(sock, "FETCH %d RFC822.TEXT", number);
1017         break;
1018     }
1019
1020     /* looking for FETCH response */
1021     do {
1022         int     ok;
1023
1024         if ((ok = gen_recv(sock, buf, sizeof(buf))))
1025             return(ok);
1026     } while
1027         (!strstr(buf+4, "FETCH") || sscanf(buf+2, "%d", &num) != 1);
1028
1029     if (num != number)
1030         return(PS_ERROR);
1031
1032     /*
1033      * Try to extract a length from the FETCH response.  RFC2060 requires
1034      * it to be present, but at least one IMAP server (Novell GroupWise)
1035      * botches this.  The overflow check is needed because of a broken
1036      * server called dbmail that returns huge garbage lengths.
1037      */
1038     if ((cp = strchr(buf, '{'))) {
1039         errno = 0;
1040         *lenp = (int)strtol(cp + 1, (char **)NULL, 10);
1041         if (errno == ERANGE && (*lenp == LONG_MAX || *lenp == LONG_MIN))
1042             *lenp = -1;    /* length is too big/small for us to handle */
1043     }
1044     else
1045         *lenp = -1;     /* missing length part in FETCH reponse */
1046
1047     return(PS_SUCCESS);
1048 }
1049
1050 static int imap_trail(int sock, struct query *ctl, int number, const char *tag)
1051 /* discard tail of FETCH response after reading message text */
1052 {
1053     /* expunges change the fetch numbers */
1054     /* number -= expunged; */
1055
1056     for (;;)
1057     {
1058         char buf[MSGBUFSIZE+1], *t;
1059         int ok;
1060
1061         if ((ok = gen_recv(sock, buf, sizeof(buf))))
1062             return(ok);
1063
1064         /* UW IMAP returns "OK FETCH", Cyrus returns "OK Completed" */
1065         if (strncmp(buf, tag, strlen(tag)) == 0) {
1066             t = buf + strlen(tag);
1067             t += strspn(t, " \t");
1068             if (strncmp(t, "OK", 2) == 0)
1069                 break;
1070         }
1071     }
1072
1073     return(PS_SUCCESS);
1074 }
1075
1076 static int imap_delete(int sock, struct query *ctl, int number)
1077 /* set delete flag for given message */
1078 {
1079     int ok;
1080
1081     /* expunges change the fetch numbers */
1082     number -= expunged;
1083
1084     /*
1085      * Use SILENT if possible as a minor throughput optimization.
1086      * Note: this has been dropped from IMAP4rev1.
1087      *
1088      * We set Seen because there are some IMAP servers (notably HP
1089      * OpenMail) that do message-receipt DSNs, but only when the seen
1090      * bit is set.  This is the appropriate time -- we get here right
1091      * after the local SMTP response that says delivery was
1092      * successful.
1093      */
1094     if ((ok = gen_transact(sock,
1095                         imap_version == IMAP4 
1096                                 ? "STORE %d +FLAGS.SILENT (\\Seen \\Deleted)"
1097                                 : "STORE %d +FLAGS (\\Seen \\Deleted)", 
1098                         number)))
1099         return(ok);
1100     else
1101         deletions++;
1102
1103     /*
1104      * We do an expunge after expunge_period messages, rather than
1105      * just before quit, so that a line hit during a long session
1106      * won't result in lots of messages being fetched again during
1107      * the next session.
1108      */
1109     if (NUM_NONZERO(expunge_period) && (deletions % expunge_period) == 0)
1110         internal_expunge(sock);
1111
1112     return(PS_SUCCESS);
1113 }
1114
1115 static int imap_mark_seen(int sock, struct query *ctl, int number)
1116 /* mark the given message as seen */
1117 {
1118     return(gen_transact(sock,
1119         imap_version == IMAP4
1120         ? "STORE %d +FLAGS.SILENT (\\Seen)"
1121         : "STORE %d +FLAGS (\\Seen)",
1122         number));
1123 }
1124
1125 static int imap_end_mailbox_poll(int sock, struct query *ctl)
1126 /* cleanup mailbox before we idle or switch to another one */
1127 {
1128     if (deletions)
1129         internal_expunge(sock);
1130     return(PS_SUCCESS);
1131 }
1132
1133 static int imap_logout(int sock, struct query *ctl)
1134 /* send logout command */
1135 {
1136     /* if any un-expunged deletions remain, ship an expunge now */
1137     if (deletions)
1138         internal_expunge(sock);
1139
1140 #ifdef USE_SEARCH
1141     /* Memory clean-up */
1142     if (unseen_messages)
1143         free(unseen_messages);
1144 #endif /* USE_SEARCH */
1145
1146     return(gen_transact(sock, "LOGOUT"));
1147 }
1148
1149 static const struct method imap =
1150 {
1151     "IMAP",             /* Internet Message Access Protocol */
1152     "imap",
1153     "imaps",
1154     TRUE,               /* this is a tagged protocol */
1155     FALSE,              /* no message delimiter */
1156     imap_ok,            /* parse command response */
1157     imap_getauth,       /* get authorization */
1158     imap_getrange,      /* query range of messages */
1159     imap_getsizes,      /* get sizes of messages (used for ESMTP SIZE option) */
1160     imap_getpartialsizes,       /* get sizes of subset of messages (used for ESMTP SIZE option) */
1161     imap_is_old,        /* no UID check */
1162     imap_fetch_headers, /* request given message headers */
1163     imap_fetch_body,    /* request given message body */
1164     imap_trail,         /* eat message trailer */
1165     imap_delete,        /* delete the message */
1166     imap_mark_seen,     /* how to mark a message as seen */
1167     imap_end_mailbox_poll,      /* end-of-mailbox processing */
1168     imap_logout,        /* expunge and exit */
1169     TRUE,               /* yes, we can re-poll */
1170 };
1171
1172 int doIMAP(struct query *ctl)
1173 /* retrieve messages using IMAP Version 2bis or Version 4 */
1174 {
1175     return(do_protocol(ctl, &imap));
1176 }
1177
1178 /* imap.c ends here */