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