]> Pileus Git - ~andy/fetchmail/blob - imap.c
ab446777d90c126f2f96e378b4ef190378a5d94a
[~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 #endif
385     (void)greeting;
386
387     /*
388      * Assumption: expunges are cheap, so we want to do them
389      * after every message unless user said otherwise.
390      */
391     if (NUM_SPECIFIED(ctl->expunge))
392         expunge_period = NUM_VALUE_OUT(ctl->expunge);
393     else
394         expunge_period = 1;
395
396     capa_probe(sock, ctl);
397
398     /* 
399      * If either (a) we saw a PREAUTH token in the greeting, or
400      * (b) the user specified ssh preauthentication, then we're done.
401      */
402     if (preauth || ctl->server.authenticate == A_SSH)
403     {
404         preauth = FALSE;  /* reset for the next session */
405         return(PS_SUCCESS);
406     }
407
408 #ifdef SSL_ENABLE
409     if (maybe_tls(ctl)) {
410         char *commonname;
411
412         commonname = ctl->server.pollname;
413         if (ctl->server.via)
414             commonname = ctl->server.via;
415         if (ctl->sslcommonname)
416             commonname = ctl->sslcommonname;
417
418         if (strstr(capabilities, "STARTTLS"))
419         {
420             /* Use "tls1" rather than ctl->sslproto because tls1 is the only
421              * protocol that will work with STARTTLS.  Don't need to worry
422              * whether TLS is mandatory or opportunistic unless SSLOpen() fails
423              * (see below). */
424             if (gen_transact(sock, "STARTTLS") == PS_SUCCESS
425                     && SSLOpen(sock, ctl->sslcert, ctl->sslkey, "tls1", ctl->sslcertck,
426                         ctl->sslcertpath, ctl->sslfingerprint, commonname,
427                         ctl->server.pollname, &ctl->remotename) != -1)
428             {
429                 /*
430                  * RFC 2595 says this:
431                  *
432                  * "Once TLS has been started, the client MUST discard cached
433                  * information about server capabilities and SHOULD re-issue the
434                  * CAPABILITY command.  This is necessary to protect against
435                  * man-in-the-middle attacks which alter the capabilities list prior
436                  * to STARTTLS.  The server MAY advertise different capabilities
437                  * after STARTTLS."
438                  *
439                  * Now that we're confident in our TLS connection we can
440                  * guarantee a secure capability re-probe.
441                  */
442                 got_tls = 1;
443                 capa_probe(sock, ctl);
444                 if (outlevel >= O_VERBOSE)
445                 {
446                     report(stdout, GT_("%s: upgrade to TLS succeeded.\n"), commonname);
447                 }
448             }
449         }
450
451         if (!got_tls) {
452             if (must_tls(ctl)) {
453                 /* Config required TLS but we couldn't guarantee it, so we must
454                  * stop. */
455                 report(stderr, GT_("%s: upgrade to TLS failed.\n"), commonname);
456                 return PS_SOCKET;
457             } else {
458                 if (outlevel >= O_VERBOSE) {
459                     report(stdout, GT_("%s: opportunistic upgrade to TLS failed, trying to continue\n"), commonname);
460                 }
461                 /* We don't know whether the connection is in a working state, so
462                  * test by issuing a NOOP. */
463                 if (gen_transact(sock, "NOOP") != PS_SUCCESS) {
464                     /* Not usable.  Empty sslproto to force an unencrypted
465                      * connection on the next attempt, and repoll. */
466                     ctl->sslproto = xstrdup("");
467                     return PS_REPOLL;
468                 }
469                 /* Usable.  Proceed with authenticating insecurely. */
470             }
471         }
472     }
473 #endif /* SSL_ENABLE */
474
475     /*
476      * Time to authenticate the user.
477      * Try the protocol variants that don't require passwords first.
478      */
479     ok = PS_AUTHFAIL;
480
481     if ((ctl->server.authenticate == A_ANY 
482          || ctl->server.authenticate == A_EXTERNAL)
483         && strstr(capabilities, "AUTH=EXTERNAL"))
484     {
485         ok = do_authcert(sock, "AUTHENTICATE", ctl->remotename);
486         if (ok)
487         {
488             /* SASL cancellation of authentication */
489             gen_send(sock, "*");
490             if (ctl->server.authenticate != A_ANY)
491                 return ok;
492         } else {
493             return ok;
494         }
495     }
496
497 #ifdef GSSAPI
498     if ((ctl->server.authenticate == A_ANY 
499          || ctl->server.authenticate == A_GSSAPI)
500         && strstr(capabilities, "AUTH=GSSAPI"))
501     {
502         if ((ok = do_gssauth(sock, "AUTHENTICATE", "imap",
503                         ctl->server.truename, ctl->remotename)))
504         {
505             /* SASL cancellation of authentication */
506             gen_send(sock, "*");
507             if (ctl->server.authenticate != A_ANY)
508                 return ok;
509         } else  {
510             return ok;
511         }
512     }
513 #endif /* GSSAPI */
514
515 #ifdef KERBEROS_V4
516     if ((ctl->server.authenticate == A_ANY 
517          || ctl->server.authenticate == A_KERBEROS_V4
518          || ctl->server.authenticate == A_KERBEROS_V5) 
519         && strstr(capabilities, "AUTH=KERBEROS_V4"))
520     {
521         if ((ok = do_rfc1731(sock, "AUTHENTICATE", ctl->server.truename)))
522         {
523             /* SASL cancellation of authentication */
524             gen_send(sock, "*");
525             if(ctl->server.authenticate != A_ANY)
526                 return ok;
527         }
528         else
529             return ok;
530     }
531 #endif /* KERBEROS_V4 */
532
533     /*
534      * No such luck.  OK, now try the variants that mask your password
535      * in a challenge-response.
536      */
537
538     if ((ctl->server.authenticate == A_ANY && strstr(capabilities, "AUTH=CRAM-MD5"))
539         || ctl->server.authenticate == A_CRAM_MD5)
540     {
541         if ((ok = do_cram_md5 (sock, "AUTHENTICATE", ctl, NULL)))
542         {
543             /* SASL cancellation of authentication */
544             gen_send(sock, "*");
545             if(ctl->server.authenticate != A_ANY)
546                 return ok;
547         }
548         else
549             return ok;
550     }
551
552 #ifdef OPIE_ENABLE
553     if ((ctl->server.authenticate == A_ANY 
554          || ctl->server.authenticate == A_OTP)
555         && strstr(capabilities, "AUTH=X-OTP")) {
556         if ((ok = do_otp(sock, "AUTHENTICATE", ctl)))
557         {
558             /* SASL cancellation of authentication */
559             gen_send(sock, "*");
560             if(ctl->server.authenticate != A_ANY)
561                 return ok;
562         } else {
563             return ok;
564         }
565     }
566 #else
567     if (ctl->server.authenticate == A_OTP)
568     {
569         report(stderr, 
570            GT_("Required OTP capability not compiled into fetchmail\n"));
571     }
572 #endif /* OPIE_ENABLE */
573
574 #ifdef NTLM_ENABLE
575     if ((ctl->server.authenticate == A_ANY 
576          || ctl->server.authenticate == A_NTLM) 
577         && strstr (capabilities, "AUTH=NTLM")) {
578         if ((ok = do_imap_ntlm(sock, ctl)))
579         {
580             /* SASL cancellation of authentication */
581             gen_send(sock, "*");
582             if(ctl->server.authenticate != A_ANY)
583                 return ok;
584         }
585         else
586             return(ok);
587     }
588 #else
589     if (ctl->server.authenticate == A_NTLM)
590     {
591         report(stderr, 
592            GT_("Required NTLM capability not compiled into fetchmail\n"));
593     }
594 #endif /* NTLM_ENABLE */
595
596 #ifdef __UNUSED__       /* The Cyrus IMAP4rev1 server chokes on this */
597     /* this handles either AUTH=LOGIN or AUTH-LOGIN */
598     if ((imap_version >= IMAP4rev1) && (!strstr(capabilities, "LOGIN")))
599     {
600         report(stderr, 
601                GT_("Required LOGIN capability not supported by server\n"));
602     }
603 #endif /* __UNUSED__ */
604
605     /* 
606      * We're stuck with sending the password en clair.
607      * The reason for this odd-looking logic is that some
608      * servers return LOGINDISABLED even though login 
609      * actually works.  So arrange things in such a way that
610      * setting auth passwd makes it ignore this capability.
611      */
612     if((ctl->server.authenticate==A_ANY&&!strstr(capabilities,"LOGINDISABLED"))
613         || ctl->server.authenticate == A_PASSWORD)
614     {
615         /* these sizes guarantee no buffer overflow */
616         char *remotename, *password;
617         size_t rnl, pwl;
618         rnl = 2 * strlen(ctl->remotename) + 1;
619         pwl = 2 * strlen(ctl->password) + 1;
620         remotename = xmalloc(rnl);
621         password = xmalloc(pwl);
622
623         imap_canonicalize(remotename, ctl->remotename, rnl);
624         imap_canonicalize(password, ctl->password, pwl);
625
626         snprintf(shroud, sizeof (shroud), "\"%s\"", password);
627         ok = gen_transact(sock, "LOGIN \"%s\" \"%s\"", remotename, password);
628         memset(shroud, 0x55, sizeof(shroud));
629         shroud[0] = '\0';
630         memset(password, 0x55, strlen(password));
631         free(password);
632         free(remotename);
633         if (ok)
634         {
635             /* SASL cancellation of authentication */
636             gen_send(sock, "*");
637             if(ctl->server.authenticate != A_ANY)
638                 return ok;
639         }
640         else
641             return(ok);
642     }
643
644     return(ok);
645 }
646
647 static int internal_expunge(int sock)
648 /* ship an expunge, resetting associated counters */
649 {
650     int ok;
651
652     actual_deletions = 0;
653
654     if ((ok = gen_transact(sock, "EXPUNGE")))
655         return(ok);
656
657     /* if there is a mismatch between the number of mails which should
658      * have been expunged and the number of mails actually expunged,
659      * another email client may be deleting mails. Quit here,
660      * otherwise fetchmail gets out-of-sync with the imap server,
661      * reports the wrong size to the SMTP server on MAIL FROM: and
662      * triggers a "message ... was not the expected length" error on
663      * every subsequent mail */
664     if (deletions > 0 && deletions != actual_deletions)
665     {
666         report(stderr,
667                 GT_("mail expunge mismatch (%d actual != %d expected)\n"),
668                 actual_deletions, deletions);
669         deletions = 0;
670         return(PS_ERROR);
671     }
672
673     expunged += deletions;
674     deletions = 0;
675
676 #ifdef IMAP_UID /* not used */
677     expunge_uids(ctl);
678 #endif /* IMAP_UID */
679
680     return(PS_SUCCESS);
681 }
682
683 static int imap_idle(int sock)
684 /* start an RFC2177 IDLE, or fake one if unsupported */
685 {
686     int ok;
687
688     saved_timeout = mytimeout;
689
690     if (has_idle) {
691         /* special timeout to terminate the IDLE and re-issue it
692          * at least every 28 minutes:
693          * (the server may have an inactivity timeout) */
694         mytimeout = idle_timeout = 1680; /* 28 min */
695         time(&idle_start_time);
696         stage = STAGE_IDLE;
697         /* enter IDLE mode */
698         ok = gen_transact(sock, "IDLE");
699
700         if (ok == PS_IDLETIMEOUT) {
701             /* send "DONE" continuation */
702             SockWrite(sock, "DONE\r\n", 6);
703             if (outlevel >= O_MONITOR)
704                 report(stdout, "IMAP> DONE\n");
705             /* reset stage and timeout here: we are not idling any more */
706             mytimeout = saved_timeout;
707             stage = STAGE_FETCH;
708             /* get OK IDLE message */
709             ok = imap_ok(sock, NULL);
710         }
711     } else {  /* no idle support, fake it */
712         /* Note: stage and timeout have not been changed here as NOOP
713          * does not idle */
714         ok = gen_transact(sock, "NOOP");
715
716         /* no error, but no new mail either */
717         if (ok == PS_SUCCESS && recentcount == 0)
718         {
719             /* There are some servers who do send new mail
720              * notification out of the blue. This is in compliance
721              * with RFC 2060 section 5.3. Wait for that with a low
722              * timeout */
723             mytimeout = idle_timeout = 28;
724             time(&idle_start_time);
725             stage = STAGE_IDLE;
726             /* We are waiting for notification; no tag needed */
727             tag[0] = '\0';
728             /* wait (briefly) for an unsolicited status update */
729             ok = imap_ok(sock, NULL);
730             if (ok == PS_IDLETIMEOUT) {
731                 /* no notification came; ok */
732                 ok = PS_SUCCESS;
733             }
734         }
735     }
736
737     /* restore normal timeout value */
738     set_timeout(0);
739     mytimeout = saved_timeout;
740     stage = STAGE_FETCH;
741
742     return(ok);
743 }
744
745 static int imap_getrange(int sock, 
746                          struct query *ctl, 
747                          const char *folder, 
748                          int *countp, int *newp, int *bytes)
749 /* get range of messages to be fetched */
750 {
751     int ok;
752     char buf[MSGBUFSIZE+1], *cp;
753
754     /* find out how many messages are waiting */
755     *bytes = -1;
756
757     if (pass > 1)
758     {
759         /* deleted mails have already been expunged by
760          * end_mailbox_poll().
761          *
762          * recentcount is already set here by the last imap command which
763          * returned EXISTS on detecting new mail. if recentcount is 0, wait
764          * for new mail.
765          *
766          * this is a while loop because imap_idle() might return on other
767          * mailbox changes also */
768         while (recentcount == 0 && do_idle) {
769             smtp_close(ctl, 1);
770             ok = imap_idle(sock);
771             if (ok)
772             {
773                 report(stderr, GT_("re-poll failed\n"));
774                 return(ok);
775             }
776         }
777         /* if recentcount is 0, return no mail */
778         if (recentcount == 0)
779                 count = 0;
780         if (outlevel >= O_DEBUG)
781             report(stdout, ngettext("%d message waiting after re-poll\n",
782                                     "%d messages waiting after re-poll\n",
783                                     count), count);
784     }
785     else
786     {
787         oldcount = count = 0;
788         ok = gen_transact(sock, 
789                           check_only ? "EXAMINE \"%s\"" : "SELECT \"%s\"",
790                           folder ? folder : "INBOX");
791         /* imap_ok returns PS_LOCKBUSY for READ-ONLY folders,
792          * which we can safely use in fetchall keep only */
793         if (ok == PS_LOCKBUSY && ctl->fetchall && ctl-> keep)
794             ok = 0;
795
796         if (ok != 0)
797         {
798             report(stderr, GT_("mailbox selection failed\n"));
799             return(ok);
800         }
801         else if (outlevel >= O_DEBUG)
802             report(stdout, ngettext("%d message waiting after first poll\n",
803                                     "%d messages waiting after first poll\n",
804                                     count), count);
805
806         /*
807          * We should have an expunge here to
808          * a) avoid fetching deleted mails during 'fetchall'
809          * b) getting a wrong count of mails during 'no fetchall'
810          */
811         if (!check_only && !ctl->keep && count > 0)
812         {
813             ok = internal_expunge(sock);
814             if (ok)
815             {
816                 report(stderr, GT_("expunge failed\n"));
817                 return(ok);
818             }
819             if (outlevel >= O_DEBUG)
820                 report(stdout, ngettext("%d message waiting after expunge\n",
821                                         "%d messages waiting after expunge\n",
822                                         count), count);
823         }
824
825         if (count == 0 && do_idle)
826         {
827             /* no messages?  then we may need to idle until we get some */
828             while (count == 0) {
829                 ok = imap_idle(sock);
830                 if (ok)
831                 {
832                     report(stderr, GT_("re-poll failed\n"));
833                     return(ok);
834                 }
835             }
836             if (outlevel >= O_DEBUG)
837                 report(stdout, ngettext("%d message waiting after re-poll\n",
838                                         "%d messages waiting after re-poll\n",
839                                         count), count);
840         }
841     }
842
843     *countp = oldcount = count;
844     recentcount = 0;
845     startcount = 1;
846
847     /* OK, now get a count of unseen messages and their indices */
848     if (!ctl->fetchall && count > 0)
849     {
850         if (unseen_messages)
851             free(unseen_messages);
852         unseen_messages = (unsigned int *)xmalloc(count * sizeof(unsigned int));
853         memset(unseen_messages, 0, count * sizeof(unsigned int));
854         unseen = 0;
855
856         /* don't count deleted messages, in case user enabled keep last time */
857         gen_send(sock, "SEARCH UNSEEN NOT DELETED");
858         do {
859             ok = gen_recv(sock, buf, sizeof(buf));
860             if (ok != 0)
861             {
862                 report(stderr, GT_("search for unseen messages failed\n"));
863                 return(PS_PROTOCOL);
864             }
865             else if ((cp = strstr(buf, "* SEARCH")))
866             {
867                 char    *ep;
868
869                 cp += 8;        /* skip "* SEARCH" */
870                 /* startcount is higher than count so that if there are no
871                  * unseen messages, imap_getsizes() will not need to do
872                  * anything! */
873                 startcount = count + 1;
874
875                 while (*cp && unseen < count)
876                 {
877                     /* skip whitespace */
878                     while (*cp && isspace((unsigned char)*cp))
879                         cp++;
880                     if (*cp) 
881                     {
882                         unsigned long um;
883
884                         errno = 0;
885                         um = strtoul(cp,&ep,10);
886                         if (errno == 0 && um <= UINT_MAX && um <= (unsigned)count)
887                         {
888                             unseen_messages[unseen++] = um;
889                             if (outlevel >= O_DEBUG)
890                                 report(stdout, GT_("%lu is unseen\n"), um);
891                             if (startcount > um)
892                                 startcount = um;
893                         }
894                         cp = ep;
895                     }
896                 }
897             }
898         } while
899             (tag[0] != '\0' && strncmp(buf, tag, strlen(tag)));
900
901         if (outlevel >= O_DEBUG && unseen > 0)
902             report(stdout, GT_("%u is first unseen\n"), startcount);
903     } else
904         unseen = -1;
905
906     *newp = unseen;
907     expunged = 0;
908     deletions = 0;
909
910     return(PS_SUCCESS);
911 }
912
913 static int imap_getpartialsizes(int sock, int first, int last, int *sizes)
914 /* capture the sizes of messages #first-#last */
915 {
916     char buf [MSGBUFSIZE+1];
917
918     /*
919      * Some servers (as in, PMDF5.1-9.1 under OpenVMS 6.1)
920      * won't accept 1:1 as valid set syntax.  Some implementors
921      * should be taken out and shot for excessive anality.
922      *
923      * Microsoft Exchange (brain-dead piece of crap that it is) 
924      * sometimes gets its knickers in a knot about bodiless messages.
925      * You may see responses like this:
926      *
927      *  fetchmail: IMAP> A0004 FETCH 1:9 RFC822.SIZE
928      *  fetchmail: IMAP< * 2 FETCH (RFC822.SIZE 1187)
929      *  fetchmail: IMAP< * 3 FETCH (RFC822.SIZE 3954)
930      *  fetchmail: IMAP< * 4 FETCH (RFC822.SIZE 1944)
931      *  fetchmail: IMAP< * 5 FETCH (RFC822.SIZE 2933)
932      *  fetchmail: IMAP< * 6 FETCH (RFC822.SIZE 1854)
933      *  fetchmail: IMAP< * 7 FETCH (RFC822.SIZE 34054)
934      *  fetchmail: IMAP< * 8 FETCH (RFC822.SIZE 5561)
935      *  fetchmail: IMAP< * 9 FETCH (RFC822.SIZE 1101)
936      *  fetchmail: IMAP< A0004 NO The requested item could not be found.
937      *
938      * This means message 1 has only headers.  For kicks and grins
939      * you can telnet in and look:
940      *  A003 FETCH 1 FULL
941      *  A003 NO The requested item could not be found.
942      *  A004 fetch 1 rfc822.header
943      *  A004 NO The requested item could not be found.
944      *  A006 FETCH 1 BODY
945      *  * 1 FETCH (BODY ("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 35 3))
946      *  A006 OK FETCH completed.
947      *
948      * To get around this, we terminate the read loop on a NO and count
949      * on the fact that the sizes array has been preinitialized with a
950      * known-bad size value.
951      */
952
953     /* expunges change the fetch numbers */
954     first -= expunged;
955     last -= expunged;
956
957     if (last == first)
958         gen_send(sock, "FETCH %d RFC822.SIZE", last);
959     else if (last > first)
960         gen_send(sock, "FETCH %d:%d RFC822.SIZE", first, last);
961     else /* no unseen messages! */
962         return(PS_SUCCESS);
963     for (;;)
964     {
965         unsigned int size;
966         int ok, num;
967         char *cp;
968
969         if ((ok = gen_recv(sock, buf, sizeof(buf))))
970             return(ok);
971         /* we want response matching to be case-insensitive */
972         for (cp = buf; *cp; cp++)
973             *cp = toupper((unsigned char)*cp);
974         /* an untagged NO means that a message was not readable */
975         if (strstr(buf, "* NO"))
976             ;
977         else if (strstr(buf, "OK") || strstr(buf, "NO"))
978             break;
979         else if (sscanf(buf, "* %d FETCH (RFC822.SIZE %u)", &num, &size) == 2
980         /* some servers (like mail.internode.on.net bld-mail04) return UID information here
981          *
982          * IMAP> A0005 FETCH 1 RFC822.SIZE
983          * IMAP< * 1 FETCH (UID 16 RFC822.SIZE 1447)
984          * IMAP< A0005 OK FETCH completed
985          *
986          */
987                 || sscanf(buf, "* %d FETCH (UID %*s RFC822.SIZE %u)", &num, &size) == 2)
988         {
989             if (num >= first && num <= last)
990                 sizes[num - first] = size;
991             else
992                 report(stderr,
993                         GT_("Warning: ignoring bogus data for message sizes returned by the server.\n"));
994         }
995     }
996
997     return(PS_SUCCESS);
998 }
999
1000 static int imap_getsizes(int sock, int count, int *sizes)
1001 /* capture the sizes of all messages */
1002 {
1003     return imap_getpartialsizes(sock, 1, count, sizes);
1004 }
1005
1006 static int imap_is_old(int sock, struct query *ctl, int number)
1007 /* is the given message old? */
1008 {
1009     flag seen = TRUE;
1010     int i;
1011
1012     (void)sock;
1013     (void)ctl;
1014     /* 
1015      * Expunges change the fetch numbers, but unseen_messages contains
1016      * indices from before any expungees were done.  So neither the
1017      * argument nor the values in message_sequence need to be decremented.
1018      */
1019
1020     seen = TRUE;
1021     for (i = 0; i < unseen; i++)
1022         if (unseen_messages[i] == (unsigned)number)
1023         {
1024             seen = FALSE;
1025             break;
1026         }
1027
1028     return(seen);
1029 }
1030
1031 static char *skip_token(char *ptr)
1032 {
1033     while(isspace((unsigned char)*ptr)) ptr++;
1034     while(!isspace((unsigned char)*ptr) && !iscntrl((unsigned char)*ptr)) ptr++;
1035     while(isspace((unsigned char)*ptr)) ptr++;
1036     return(ptr);
1037 }
1038
1039 static int imap_fetch_headers(int sock, struct query *ctl,int number,int *lenp)
1040 /* request headers of nth message */
1041 {
1042     char buf [MSGBUFSIZE+1];
1043     int num;
1044
1045     (void)ctl;
1046     /* expunges change the fetch numbers */
1047     number -= expunged;
1048
1049     /*
1050      * This is blessed by RFC1176, RFC1730, RFC2060.
1051      * According to the RFCs, it should *not* set the \Seen flag.
1052      */
1053     gen_send(sock, "FETCH %d RFC822.HEADER", number);
1054
1055     /* looking for FETCH response */
1056     for (;;) 
1057     {
1058         int     ok;
1059         char    *ptr;
1060
1061         if ((ok = gen_recv(sock, buf, sizeof(buf))))
1062             return(ok);
1063         ptr = skip_token(buf);  /* either "* " or "AXXXX " */
1064         if (sscanf(ptr, "%d FETCH (RFC822.HEADER {%d}", &num, lenp) == 2
1065         /* some servers (like mail.internode.on.net bld-mail04) return UID information here
1066          *
1067          * IMAP> A0006 FETCH 1 RFC822.HEADER
1068          * IMAP< * 1 FETCH (UID 16 RFC822.HEADER {1360}
1069          * ...
1070          * IMAP< )
1071          * IMAP< A0006 OK FETCH completed
1072          *
1073          */
1074                 || sscanf(ptr, "%d FETCH (UID %*s RFC822.HEADER {%d}", &num, lenp) == 2)
1075             break;
1076         /* try to recover from chronically fucked-up M$ Exchange servers */
1077         else if (!strncmp(ptr, "NO", 2))
1078         {
1079             /* wait for a tagged response */
1080             if (strstr (buf, "* NO"))
1081                 imap_ok (sock, 0);
1082             return(PS_TRANSIENT);
1083         }
1084         else if (!strncmp(ptr, "BAD", 3))
1085         {
1086             /* wait for a tagged response */
1087             if (strstr (buf, "* BAD"))
1088                 imap_ok (sock, 0);
1089             return(PS_TRANSIENT);
1090         }
1091     }
1092
1093     if (num != number)
1094         return(PS_ERROR);
1095     else
1096         return(PS_SUCCESS);
1097 }
1098
1099 static int imap_fetch_body(int sock, struct query *ctl, int number, int *lenp)
1100 /* request body of nth message */
1101 {
1102     char buf [MSGBUFSIZE+1], *cp;
1103     int num;
1104
1105     (void)ctl;
1106     /* expunges change the fetch numbers */
1107     number -= expunged;
1108
1109     /*
1110      * If we're using IMAP4, we can fetch the message without setting its
1111      * seen flag.  This is good!  It means that if the protocol exchange
1112      * craps out during the message, it will still be marked `unseen' on
1113      * the server.
1114      *
1115      * According to RFC2060, and Mark Crispin the IMAP maintainer,
1116      * FETCH %d BODY[TEXT] and RFC822.TEXT are "functionally 
1117      * equivalent".  However, we know of at least one server that
1118      * treats them differently in the presence of MIME attachments;
1119      * the latter form downloads the attachment, the former does not.
1120      * The server is InterChange, and the fool who implemented this
1121      * misfeature ought to be strung up by his thumbs.  
1122      *
1123      * When I tried working around this by disabling use of the 4rev1 form,
1124      * I found that doing this breaks operation with M$ Exchange.
1125      * Annoyingly enough, Exchange's refusal to cope is technically legal
1126      * under RFC2062.  Trust Microsoft, the Great Enemy of interoperability
1127      * standards, to find a way to make standards compliance irritating....
1128      */
1129     switch (imap_version)
1130     {
1131     case IMAP4rev1:     /* RFC 2060 */
1132         gen_send(sock, "FETCH %d BODY.PEEK[TEXT]", number);
1133         break;
1134
1135     case IMAP4:         /* RFC 1730 */
1136         gen_send(sock, "FETCH %d RFC822.TEXT.PEEK", number);
1137         break;
1138
1139     default:            /* RFC 1176 */
1140         gen_send(sock, "FETCH %d RFC822.TEXT", number);
1141         break;
1142     }
1143
1144     /* looking for FETCH response */
1145     do {
1146         int     ok;
1147
1148         if ((ok = gen_recv(sock, buf, sizeof(buf))))
1149             return(ok);
1150     } while
1151         (!strstr(buf+4, "FETCH") || sscanf(buf+2, "%d", &num) != 1);
1152
1153     if (num != number)
1154         return(PS_ERROR);
1155
1156     /*
1157      * Try to extract a length from the FETCH response.  RFC2060 requires
1158      * it to be present, but at least one IMAP server (Novell GroupWise)
1159      * botches this.  The overflow check is needed because of a broken
1160      * server called dbmail that returns huge garbage lengths.
1161      */
1162     if ((cp = strchr(buf, '{'))) {
1163         errno = 0;
1164         *lenp = (int)strtol(cp + 1, (char **)NULL, 10);
1165         if (errno == ERANGE || *lenp < 0)
1166             *lenp = -1;    /* length is too big/small for us to handle */
1167     }
1168     else
1169         *lenp = -1;     /* missing length part in FETCH reponse */
1170
1171     return(PS_SUCCESS);
1172 }
1173
1174 static int imap_trail(int sock, struct query *ctl, const char *tag)
1175 /* discard tail of FETCH response after reading message text */
1176 {
1177     /* expunges change the fetch numbers */
1178     /* number -= expunged; */
1179
1180     (void)ctl;
1181     for (;;)
1182     {
1183         char buf[MSGBUFSIZE+1], *t;
1184         int ok;
1185
1186         if ((ok = gen_recv(sock, buf, sizeof(buf))))
1187             return(ok);
1188
1189         /* UW IMAP returns "OK FETCH", Cyrus returns "OK Completed" */
1190         if (strncmp(buf, tag, strlen(tag)) == 0) {
1191             t = buf + strlen(tag);
1192             t += strspn(t, " \t");
1193             if (strncmp(t, "OK", 2) == 0)
1194                 break;
1195         }
1196     }
1197
1198     return(PS_SUCCESS);
1199 }
1200
1201 static int imap_delete(int sock, struct query *ctl, int number)
1202 /* set delete flag for given message */
1203 {
1204     int ok;
1205
1206     (void)ctl;
1207     /* expunges change the fetch numbers */
1208     number -= expunged;
1209
1210     /*
1211      * Use SILENT if possible as a minor throughput optimization.
1212      * Note: this has been dropped from IMAP4rev1.
1213      *
1214      * We set Seen because there are some IMAP servers (notably HP
1215      * OpenMail) that do message-receipt DSNs, but only when the seen
1216      * bit is set.  This is the appropriate time -- we get here right
1217      * after the local SMTP response that says delivery was
1218      * successful.
1219      */
1220     if ((ok = gen_transact(sock,
1221                         imap_version == IMAP4 
1222                                 ? "STORE %d +FLAGS.SILENT (\\Seen \\Deleted)"
1223                                 : "STORE %d +FLAGS (\\Seen \\Deleted)", 
1224                         number)))
1225         return(ok);
1226     else
1227         deletions++;
1228
1229     /*
1230      * We do an expunge after expunge_period messages, rather than
1231      * just before quit, so that a line hit during a long session
1232      * won't result in lots of messages being fetched again during
1233      * the next session.
1234      */
1235     if (NUM_NONZERO(expunge_period) && (deletions % expunge_period) == 0)
1236     {
1237         if ((ok = internal_expunge(sock)))
1238             return(ok);
1239     }
1240
1241     return(PS_SUCCESS);
1242 }
1243
1244 static int imap_mark_seen(int sock, struct query *ctl, int number)
1245 /* mark the given message as seen */
1246 {
1247     (void)ctl;
1248
1249     /* expunges change the message numbers */
1250     number -= expunged;
1251
1252     return(gen_transact(sock,
1253         imap_version == IMAP4
1254         ? "STORE %d +FLAGS.SILENT (\\Seen)"
1255         : "STORE %d +FLAGS (\\Seen)",
1256         number));
1257 }
1258
1259 static int imap_end_mailbox_poll(int sock, struct query *ctl)
1260 /* cleanup mailbox before we idle or switch to another one */
1261 {
1262     (void)ctl;
1263     if (deletions)
1264         internal_expunge(sock);
1265     return(PS_SUCCESS);
1266 }
1267
1268 static int imap_logout(int sock, struct query *ctl)
1269 /* send logout command */
1270 {
1271     (void)ctl;
1272     /* if any un-expunged deletions remain, ship an expunge now */
1273     if (deletions)
1274         internal_expunge(sock);
1275
1276 #ifdef USE_SEARCH
1277     /* Memory clean-up */
1278     if (unseen_messages)
1279         free(unseen_messages);
1280 #endif /* USE_SEARCH */
1281
1282     return(gen_transact(sock, "LOGOUT"));
1283 }
1284
1285 static const struct method imap =
1286 {
1287     "IMAP",             /* Internet Message Access Protocol */
1288     "imap",             /* service (plain and TLS) */
1289     "imaps",            /* service (SSL) */
1290     TRUE,               /* this is a tagged protocol */
1291     FALSE,              /* no message delimiter */
1292     imap_ok,            /* parse command response */
1293     imap_getauth,       /* get authorization */
1294     imap_getrange,      /* query range of messages */
1295     imap_getsizes,      /* get sizes of messages (used for ESMTP SIZE option) */
1296     imap_getpartialsizes,       /* get sizes of subset of messages (used for ESMTP SIZE option) */
1297     imap_is_old,        /* no UID check */
1298     imap_fetch_headers, /* request given message headers */
1299     imap_fetch_body,    /* request given message body */
1300     imap_trail,         /* eat message trailer */
1301     imap_delete,        /* delete the message */
1302     imap_mark_seen,     /* how to mark a message as seen */
1303     imap_end_mailbox_poll,      /* end-of-mailbox processing */
1304     imap_logout,        /* expunge and exit */
1305     TRUE,               /* yes, we can re-poll */
1306 };
1307
1308 int doIMAP(struct query *ctl)
1309 /* retrieve messages using IMAP Version 2bis or Version 4 */
1310 {
1311     return(do_protocol(ctl, &imap));
1312 }
1313
1314 /* imap.c ends here */