]> Pileus Git - ~andy/fetchmail/blob - imap.c
Minor tweaks.
[~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 #endif
15 #include  "fetchmail.h"
16 #include  "socket.h"
17
18 #ifdef KERBEROS_V4
19 #if defined (__bsdi__)
20 #include <des.h>
21 #define krb_get_err_text(e) (krb_err_txt[e])
22 #endif
23 #if defined (__FreeBSD__)
24 #define krb_get_err_text(e) (krb_err_txt[e])
25 #endif
26 #include <krb.h>
27 #endif /* KERBEROS_V4 */
28
29 #ifndef strstr          /* glibc-2.1 declares this as a macro */
30 extern char *strstr();  /* needed on sysV68 R3V7.1. */
31 #endif /* strstr */
32
33 /* imap_version values */
34 #define IMAP2           -1      /* IMAP2 or IMAP2BIS, RFC1176 */
35 #define IMAP4           0       /* IMAP4 rev 0, RFC1730 */
36 #define IMAP4rev1       1       /* IMAP4 rev 1, RFC2060 */
37
38 static int count, seen, recent, unseen, deletions,expunged, imap_version;
39
40 int imap_ok(int sock, char *argbuf)
41 /* parse command response */
42 {
43     char buf [POPBUFSIZE+1];
44
45     seen = 0;
46     do {
47         int     ok;
48
49         if ((ok = gen_recv(sock, buf, sizeof(buf))))
50             return(ok);
51
52         /* interpret untagged status responses */
53         if (strstr(buf, "EXISTS"))
54             count = atoi(buf+2);
55         if (strstr(buf, "RECENT"))
56             recent = atoi(buf+2);
57         if (strstr(buf, "UNSEEN"))
58         {
59             char        *cp;
60
61             /*
62              * Handle both "* 42 UNSEEN" (if tha ever happens) and 
63              * "* OK [UNSEEN 42] 42". Note that what this gets us is
64              * a minimum index, not a count.
65              */
66             unseen = 0;
67             for (cp = buf; *cp && !isdigit(*cp); cp++)
68                 continue;
69             unseen = atoi(cp);
70         }
71         if (strstr(buf, "FLAGS"))
72             seen = (strstr(buf, "Seen") != (char *)NULL);
73     } while
74         (tag[0] != '\0' && strncmp(buf, tag, strlen(tag)));
75
76     if (tag[0] == '\0')
77     {
78         if (argbuf)
79             strcpy(argbuf, buf);
80         return(PS_SUCCESS); 
81     }
82     else
83     {
84         char    *cp;
85
86         /* skip the tag */
87         for (cp = buf; !isspace(*cp); cp++)
88             continue;
89         while (isspace(*cp))
90             cp++;
91
92         if (strncmp(cp, "OK", 2) == 0)
93         {
94             if (argbuf)
95                 strcpy(argbuf, cp);
96             return(PS_SUCCESS);
97         }
98         else if (strncmp(cp, "BAD", 2) == 0)
99             return(PS_ERROR);
100         else
101             return(PS_PROTOCOL);
102     }
103 }
104
105 #ifdef KERBEROS_V4
106 #if SIZEOF_INT == 4
107 typedef int     int32;
108 #elif SIZEOF_SHORT == 4
109 typedef short   int32;
110 #elif SIZEOF_LONG == 4
111 typedef long    int32;
112 #else
113 #error Cannot deduce a 32-bit-type
114 #endif
115
116 static int do_rfc1731(int sock, char *truename)
117 /* authenticate as per RFC1731 -- note 32-bit integer requirement here */
118 {
119     int result = 0, len;
120     char buf1[4096], buf2[4096];
121     union {
122       int32 cint;
123       char cstr[4];
124     } challenge1, challenge2;
125     char srvinst[INST_SZ];
126     char *p;
127     char srvrealm[REALM_SZ];
128     KTEXT_ST authenticator;
129     CREDENTIALS credentials;
130     char tktuser[MAX_K_NAME_SZ+1+INST_SZ+1+REALM_SZ+1];
131     char tktinst[INST_SZ];
132     char tktrealm[REALM_SZ];
133     des_cblock session;
134     des_key_schedule schedule;
135
136     gen_send(sock, "AUTHENTICATE KERBEROS_V4");
137
138     /* The data encoded in the first ready response contains a random
139      * 32-bit number in network byte order.  The client should respond
140      * with a Kerberos ticket and an authenticator for the principal
141      * "imap.hostname@realm", where "hostname" is the first component
142      * of the host name of the server with all letters in lower case
143      * and where "realm" is the Kerberos realm of the server.  The
144      * encrypted checksum field included within the Kerberos
145      * authenticator should contain the server provided 32-bit number
146      * in network byte order.
147      */
148
149     if (result = gen_recv(sock, buf1, sizeof buf1)) {
150         return result;
151     }
152
153     len = from64tobits(challenge1.cstr, buf1);
154     if (len < 0) {
155         error(0, -1, "could not decode initial BASE64 challenge");
156         return PS_AUTHFAIL;
157     }
158
159     /* Client responds with a Kerberos ticket and an authenticator for
160      * the principal "imap.hostname@realm" where "hostname" is the
161      * first component of the host name of the server with all letters
162      * in lower case and where "realm" is the Kerberos realm of the
163      * server.  The encrypted checksum field included within the
164      * Kerberos authenticator should contain the server-provided
165      * 32-bit number in network byte order.
166      */
167
168     strncpy(srvinst, truename, (sizeof srvinst)-1);
169     srvinst[(sizeof srvinst)-1] = '\0';
170     for (p = srvinst; *p; p++) {
171       if (isupper(*p)) {
172         *p = tolower(*p);
173       }
174     }
175
176     strncpy(srvrealm, krb_realmofhost(srvinst), (sizeof srvrealm)-1);
177     srvrealm[(sizeof srvrealm)-1] = '\0';
178     if (p = strchr(srvinst, '.')) {
179       *p = '\0';
180     }
181
182     result = krb_mk_req(&authenticator, "imap", srvinst, srvrealm, 0);
183     if (result) {
184         error(0, -1, "krb_mq_req: %s", krb_get_err_text(result));
185         return PS_AUTHFAIL;
186     }
187
188     result = krb_get_cred("imap", srvinst, srvrealm, &credentials);
189     if (result) {
190         error(0, -1, "krb_get_cred: %s", krb_get_err_text(result));
191         return PS_AUTHFAIL;
192     }
193
194     memcpy(session, credentials.session, sizeof session);
195     memset(&credentials, 0, sizeof credentials);
196     des_key_sched(session, schedule);
197
198     result = krb_get_tf_fullname(TKT_FILE, tktuser, tktinst, tktrealm);
199     if (result) {
200         error(0, -1, "krb_get_tf_fullname: %s", krb_get_err_text(result));
201         return PS_AUTHFAIL;
202     }
203
204     if (strcmp(tktuser, user) != 0) {
205         error(0, -1, "principal %s in ticket does not match -u %s", tktuser,
206                 user);
207         return PS_AUTHFAIL;
208     }
209
210     if (tktinst[0]) {
211         error(0, 0, "non-null instance (%s) might cause strange behavior",
212                 tktinst);
213         strcat(tktuser, ".");
214         strcat(tktuser, tktinst);
215     }
216
217     if (strcmp(tktrealm, srvrealm) != 0) {
218         strcat(tktuser, "@");
219         strcat(tktuser, tktrealm);
220     }
221
222     result = krb_mk_req(&authenticator, "imap", srvinst, srvrealm,
223             challenge1.cint);
224     if (result) {
225         error(0, -1, "krb_mq_req: %s", krb_get_err_text(result));
226         return PS_AUTHFAIL;
227     }
228
229     to64frombits(buf1, authenticator.dat, authenticator.length);
230     if (outlevel == O_VERBOSE) {
231         error(0, 0, "IMAP> %s", buf1);
232     }
233     SockWrite(sock, buf1, strlen(buf1));
234     SockWrite(sock, "\r\n", 2);
235
236     /* Upon decrypting and verifying the ticket and authenticator, the
237      * server should verify that the contained checksum field equals
238      * the original server provided random 32-bit number.  Should the
239      * verification be successful, the server must add one to the
240      * checksum and construct 8 octets of data, with the first four
241      * octets containing the incremented checksum in network byte
242      * order, the fifth octet containing a bit-mask specifying the
243      * protection mechanisms supported by the server, and the sixth
244      * through eighth octets containing, in network byte order, the
245      * maximum cipher-text buffer size the server is able to receive.
246      * The server must encrypt the 8 octets of data in the session key
247      * and issue that encrypted data in a second ready response.  The
248      * client should consider the server authenticated if the first
249      * four octets the un-encrypted data is equal to one plus the
250      * checksum it previously sent.
251      */
252     
253     if (result = gen_recv(sock, buf1, sizeof buf1))
254         return result;
255
256     /* The client must construct data with the first four octets
257      * containing the original server-issued checksum in network byte
258      * order, the fifth octet containing the bit-mask specifying the
259      * selected protection mechanism, the sixth through eighth octets
260      * containing in network byte order the maximum cipher-text buffer
261      * size the client is able to receive, and the following octets
262      * containing a user name string.  The client must then append
263      * from one to eight octets so that the length of the data is a
264      * multiple of eight octets. The client must then PCBC encrypt the
265      * data with the session key and respond to the second ready
266      * response with the encrypted data.  The server decrypts the data
267      * and verifies the contained checksum.  The username field
268      * identifies the user for whom subsequent IMAP operations are to
269      * be performed; the server must verify that the principal
270      * identified in the Kerberos ticket is authorized to connect as
271      * that user.  After these verifications, the authentication
272      * process is complete.
273      */
274
275     len = from64tobits(buf2, buf1);
276     if (len < 0) {
277         error(0, -1, "could not decode BASE64 ready response");
278         return PS_AUTHFAIL;
279     }
280
281     des_ecb_encrypt((des_cblock *)buf2, (des_cblock *)buf2, schedule, 0);
282     memcpy(challenge2.cstr, buf2, 4);
283     if (ntohl(challenge2.cint) != challenge1.cint + 1) {
284         error(0, -1, "challenge mismatch");
285         return PS_AUTHFAIL;
286     }       
287
288     memset(authenticator.dat, 0, sizeof authenticator.dat);
289
290     result = htonl(challenge1.cint);
291     memcpy(authenticator.dat, &result, sizeof result);
292
293     /* The protection mechanisms and their corresponding bit-masks are as
294      * follows:
295      *
296      * 1 No protection mechanism
297      * 2 Integrity (krb_mk_safe) protection
298      * 4 Privacy (krb_mk_priv) protection
299      */
300     authenticator.dat[4] = 1;
301
302     len = strlen(tktuser);
303     strncpy(authenticator.dat+8, tktuser, len);
304     authenticator.length = len + 8 + 1;
305     while (authenticator.length & 7) {
306         authenticator.length++;
307     }
308     des_pcbc_encrypt((des_cblock *)authenticator.dat,
309             (des_cblock *)authenticator.dat, authenticator.length, schedule,
310             &session, 1);
311
312     to64frombits(buf1, authenticator.dat, authenticator.length);
313     if (outlevel == O_VERBOSE) {
314         error(0, 0, "IMAP> %s", buf1);
315     }
316     SockWrite(sock, buf1, strlen(buf1));
317     SockWrite(sock, "\r\n", 2);
318
319     if (result = gen_recv(sock, buf1, sizeof buf1))
320         return result;
321
322     if (strstr(buf1, "OK")) {
323         return PS_SUCCESS;
324     }
325     else {
326         return PS_AUTHFAIL;
327     }
328 }
329 #endif /* KERBEROS_V4 */
330
331 int imap_getauth(int sock, struct query *ctl, char *greeting)
332 /* apply for connection authorization */
333 {
334     char capabilities[POPBUFSIZE+1];
335     int ok = 0;
336
337     /* probe to see if we're running IMAP4 and can use RFC822.PEEK */
338     gen_send(sock, "CAPABILITY");
339     if ((ok = gen_recv(sock, capabilities, sizeof(capabilities))))
340         return(ok);
341     if (strstr(capabilities, "BAD"))
342     {
343         imap_version = IMAP2;
344         if (outlevel == O_VERBOSE)
345             error(0, 0, "Protocol identified as IMAP2 or IMAP2BIS");
346     }
347     /* UW-IMAP server 10.173 notifies in all caps */
348     else if (strstr(capabilities, "IMAP4rev1") || strstr(capabilities, "IMAP4REV1"))
349     {
350         imap_version = IMAP4rev1;
351         if (outlevel == O_VERBOSE)
352             error(0, 0, "Protocol identified as IMAP4 rev 1");
353     }
354     else
355     {
356         imap_version = IMAP4;
357         if (outlevel == O_VERBOSE)
358             error(0, 0, "Protocol identified as IMAP4 rev 0");
359     }
360
361     /* eat the tail of the CAPABILITY response (if any) */
362     if ((peek_capable = (imap_version >= IMAP4)))
363         if ((ok = gen_recv(sock, capabilities, sizeof(capabilities))))
364             return(ok);
365
366 #ifdef KERBEROS_V4
367     if (strstr(capabilities, "AUTH=KERBEROS_V4"))
368     {
369         if (outlevel == O_VERBOSE)
370             error(0, 0, "KERBEROS_V4 authentication is supported");
371
372         if (ctl->server.protocol == P_IMAP_K4)
373         {
374             if ((ok = do_rfc1731(sock, ctl->server.truename)))
375             {
376                 if (outlevel == O_VERBOSE)
377                     error(0, 0, "IMAP> *");
378                 SockWrite(sock, "*\r\n", 3);
379             }
380             
381             return(ok);
382         }
383         /* else fall through to ourdinary AUTH=LOGIN case */
384     }
385     else if (ctl->server.protocol == P_IMAP_K4)
386     {
387         error(0,-1, "Required KERBEROS_V4 capability not supported by server");
388         return(PS_AUTHFAIL);
389     }
390 #endif /* KERBEROS_V4 */
391
392     /* try to get authorized in the ordinary (AUTH=LOGIN) way */
393     ok = gen_transact(sock, "LOGIN %s \"%s\"", ctl->remotename, ctl->password);
394     if (ok)
395         return(ok);
396
397     return(PS_SUCCESS);
398 }
399
400 static int imap_getrange(int sock, 
401                          struct query *ctl, 
402                          const char *folder, 
403                          int *countp, int *newp)
404 /* get range of messages to be fetched */
405 {
406     int ok;
407
408     /* find out how many messages are waiting */
409     recent = unseen = -1;
410
411     if (pass > 1)
412     {
413         /* 
414          * We have to have an expunge here, otherwise the re-poll will
415          * infinite-loop picking up un-expunged message.
416          */
417         ok = 0;
418         if (deletions && ctl->expunge > 1)
419             ok = gen_transact(sock, "EXPUNGE");
420 #ifdef IMAP_UID /* not used */
421         if (!ok)
422             expunge_uids(ctl);
423 #endif /* IMAP_UID */
424         if (ok || gen_transact(sock, "NOOP"))
425         {
426             error(0, 0, "re-poll failed");
427             return(ok);
428         }
429     }
430     else
431     {
432         ok = gen_transact(sock, "SELECT %s", folder ? folder : "INBOX");
433         if (ok != 0)
434         {
435             error(0, 0, "mailbox selection failed");
436             return(ok);
437         }
438     }
439
440     *countp = count;
441
442     /*
443      * Note: because IMAP has an is_old method, this number is used
444      * only for the "X messages (Y unseen)" notification.  Accordingly
445      * it doesn't matter much that it can be wrong (e.g. if we see an
446      * UNSEEN response but not all messages above the first UNSEEN one
447      * are likewise).
448      */
449     if (unseen >= 0)            /* optional, but better if we see it */
450         *newp = count - unseen + 1;
451     else if (recent >= 0)       /* mandatory */
452         *newp = recent;
453     else
454         *newp = -1;             /* should never happen, RECENT is mandatory */ 
455
456     expunged = deletions = 0;
457
458     return(PS_SUCCESS);
459 }
460
461 static int imap_getsizes(int sock, int count, int *sizes)
462 /* capture the sizes of all messages */
463 {
464     char buf [POPBUFSIZE+1];
465     int i;
466
467     gen_send(sock, "FETCH 1:%d RFC822.SIZE", count);
468     for (;;)
469     {
470         int num, size, ok;
471
472         if ((ok = gen_recv(sock, buf, sizeof(buf))))
473             return(ok);
474         if (strstr(buf, "OK"))
475             break;
476         else if (sscanf(buf, "* %d FETCH (RFC822.SIZE %d)", &num, &size) == 2)
477             sizes[num - 1] = size;
478     }
479
480     return(PS_SUCCESS);
481 }
482
483 static int imap_is_old(int sock, struct query *ctl, int number)
484 /* is the given message old? */
485 {
486     int ok;
487
488     /* expunges change the fetch numbers */
489     number -= expunged;
490
491     if ((ok = gen_transact(sock, "FETCH %d FLAGS", number)) != 0)
492         return(PS_ERROR);
493
494     return(seen);
495 }
496
497 static int imap_fetch_headers(int sock, struct query *ctl,int number,int *lenp)
498 /* request headers of nth message */
499 {
500     char buf [POPBUFSIZE+1];
501     int num;
502
503     /* expunges change the fetch numbers */
504     number -= expunged;
505
506     /*
507      * This is blessed by RFC 1176, RFC1730, RFC2060.
508      * According to the RFCs, it should *not* set the \Seen flag.
509      */
510     gen_send(sock, "FETCH %d RFC822.HEADER", number);
511
512     /* looking for FETCH response */
513     do {
514         int     ok;
515
516         if ((ok = gen_recv(sock, buf, sizeof(buf))))
517             return(ok);
518     } while
519         (sscanf(buf+2, "%d FETCH (%*s {%d}", &num, lenp) != 2);
520
521     if (num != number)
522         return(PS_ERROR);
523     else
524         return(PS_SUCCESS);
525 }
526
527 static int imap_fetch_body(int sock, struct query *ctl, int number, int *lenp)
528 /* request body of nth message */
529 {
530     char buf [POPBUFSIZE+1], *cp;
531     int num;
532
533     /* expunges change the fetch numbers */
534     number -= expunged;
535
536     /*
537      * If we're using IMAP4, we can fetch the message without setting its
538      * seen flag.  This is good!  It means that if the protocol exchange
539      * craps out during the message, it will still be marked `unseen' on
540      * the server.
541      *
542      * However...*don't* do this if we're using keep to suppress deletion!
543      * In that case, marking the seen flag is the only way to prevent the
544      * message from being re-fetched on subsequent runs.
545      */
546     switch (imap_version)
547     {
548     case IMAP4rev1:     /* RFC 2060 */
549         if (!ctl->keep)
550             gen_send(sock, "FETCH %d BODY.PEEK[TEXT]", number);
551         else
552             gen_send(sock, "FETCH %d BODY[TEXT]", number);
553         break;
554
555     case IMAP4:         /* RFC 1730 */
556         if (!ctl->keep)
557             gen_send(sock, "FETCH %d RFC822.TEXT.PEEK", number);
558         else
559             gen_send(sock, "FETCH %d RFC822.TEXT", number);
560         break;
561
562     default:            /* RFC 1176 */
563         gen_send(sock, "FETCH %d RFC822.TEXT", number);
564         break;
565     }
566
567     /* looking for FETCH response */
568     do {
569         int     ok;
570
571         if ((ok = gen_recv(sock, buf, sizeof(buf))))
572             return(ok);
573     } while
574         (sscanf(buf+2, "%d FETCH", &num) != 1);
575
576     if (num != number)
577         return(PS_ERROR);
578
579     /* try to extract a length */
580     if ((cp = strchr(buf, '{')))
581         *lenp = atoi(cp + 1);
582     else
583         *lenp = 0;
584
585     return(PS_SUCCESS);
586 }
587
588 static int imap_trail(int sock, struct query *ctl, int number)
589 /* discard tail of FETCH response after reading message text */
590 {
591     /* expunges change the fetch numbers */
592     /* number -= expunged; */
593
594     for (;;)
595     {
596         char buf[POPBUFSIZE+1];
597         int ok;
598
599         if ((ok = gen_recv(sock, buf, sizeof(buf))))
600             return(ok);
601
602         /* UW IMAP returns "OK FETCH", Cyrus returns "OK Completed" */
603         if (strstr(buf, "OK"))
604             break;
605     }
606
607     return(PS_SUCCESS);
608 }
609
610 static int imap_delete(int sock, struct query *ctl, int number)
611 /* set delete flag for given message */
612 {
613     int ok;
614
615     /* expunges change the fetch numbers */
616     number -= expunged;
617
618     /*
619      * Use SILENT if possible as a minor throughput optimization.
620      * Note: this has been dropped from IMAP4rev1.
621      */
622     if ((ok = gen_transact(sock,
623                         imap_version == IMAP4 
624                                 ? "STORE %d +FLAGS.SILENT (\\Deleted)"
625                                 : "STORE %d +FLAGS (\\Deleted)", 
626                         number)))
627         return(ok);
628
629     /*
630      * We do an expunge after ctl->expunge messages, rather than
631      * just before quit, so that a line hit during a long session
632      * won't result in lots of messages being fetched again during
633      * the next session.
634      */
635     if (ctl->expunge > 0 && (++deletions % ctl->expunge) == 0)
636     {
637         if ((ok = gen_transact(sock, "EXPUNGE")))
638             return(ok);
639
640 #ifdef IMAP_UID /* not used */
641         expunge_uids(ctl);
642 #endif /* IMAP_UID */
643
644         expunged = deletions;
645     }
646
647     return(PS_SUCCESS);
648 }
649
650 static int imap_logout(int sock, struct query *ctl)
651 /* send logout command */
652 {
653     /* if expunges after deletion have been suppressed, ship one now */
654     if (ctl->expunge == 0 && deletions)
655     {
656         int     ok;
657
658         if ((ok = gen_transact(sock, "EXPUNGE")))
659             return(ok);
660
661 #ifdef IMAP_UID /* not used */
662         expunge_uids(ctl);
663 #endif /* IMAP_UID */
664     }
665
666     return(gen_transact(sock, "LOGOUT"));
667 }
668
669 const static struct method imap =
670 {
671     "IMAP",             /* Internet Message Access Protocol */
672     143,                /* standard IMAP2bis/IMAP4 port */
673     TRUE,               /* this is a tagged protocol */
674     FALSE,              /* no message delimiter */
675     imap_ok,            /* parse command response */
676     imap_getauth,       /* get authorization */
677     imap_getrange,      /* query range of messages */
678     imap_getsizes,      /* get sizes of messages (used for --limit option */
679     imap_is_old,        /* no UID check */
680     imap_fetch_headers, /* request given message headers */
681     imap_fetch_body,    /* request given message body */
682     imap_trail,         /* eat message trailer */
683     imap_delete,        /* delete the message */
684     imap_logout,        /* expunge and exit */
685     TRUE,               /* yes, we can re-poll */
686 };
687
688 int doIMAP(struct query *ctl)
689 /* retrieve messages using IMAP Version 2bis or Version 4 */
690 {
691     return(do_protocol(ctl, &imap));
692 }
693
694 /* imap.c ends here */