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