]> Pileus Git - ~andy/fetchmail/blob - imap.c
Add multiple-folder support.
[~andy/fetchmail] / imap.c
1 /*
2  * imap.c -- IMAP2bis/IMAP4 protocol methods
3  *
4  * Copyright 1996 by Eric S. Raymond
5  * All rights reserved.
6  * For license terms, see the file COPYING in this directory.
7  */
8
9 #include  <config.h>
10 #include  <stdio.h>
11 #include  <string.h>
12 #include  <ctype.h>
13 #if defined(STDC_HEADERS)
14 #include  <stdlib.h>
15 #endif
16 #include  "fetchmail.h"
17 #include  "socket.h"
18
19 extern char *strstr();  /* needed on sysV68 R3V7.1. */
20
21 /* imap_version values */
22 #define IMAP2           -1      /* IMAP2 or IMAP2BIS, RFC1176 */
23 #define IMAP4           0       /* IMAP4 rev 0, RFC1730 */
24 #define IMAP4rev1       1       /* IMAP4 rev 1, RFC2060 */
25
26 static int count, seen, recent, unseen, deletecount, imap_version;
27
28 int imap_ok (int sock,  char *argbuf)
29 /* parse command response */
30 {
31     char buf [POPBUFSIZE+1];
32
33     seen = 0;
34     do {
35         int     ok;
36
37         if ((ok = gen_recv(sock, buf, sizeof(buf))))
38             return(ok);
39
40         /* interpret untagged status responses */
41         if (strstr(buf, "EXISTS"))
42             count = atoi(buf+2);
43         if (strstr(buf, "RECENT"))
44             recent = atoi(buf+2);
45         if (strstr(buf, "UNSEEN"))
46             unseen = atoi(buf+2);
47         if (strstr(buf, "FLAGS"))
48             seen = (strstr(buf, "Seen") != (char *)NULL);
49     } while
50         (tag[0] != '\0' && strncmp(buf, tag, strlen(tag)));
51
52     if (tag[0] == '\0')
53     {
54         strcpy(argbuf, buf);
55         return(PS_SUCCESS); 
56     }
57     else
58     {
59         char    *cp;
60
61         /* skip the tag */
62         for (cp = buf; !isspace(*cp); cp++)
63             continue;
64         while (isspace(*cp))
65             cp++;
66
67         if (strncmp(cp, "OK", 2) == 0)
68         {
69             strcpy(argbuf, cp);
70             return(PS_SUCCESS);
71         }
72         else if (strncmp(cp, "BAD", 2) == 0)
73             return(PS_ERROR);
74         else
75             return(PS_PROTOCOL);
76     }
77 }
78
79 int imap_getauth(int sock, struct query *ctl, char *buf)
80 /* apply for connection authorization */
81 {
82     char rbuf [POPBUFSIZE+1];
83
84     /* try to get authorized */
85     int ok = gen_transact(sock,
86                   "LOGIN %s \"%s\"",
87                   ctl->remotename, ctl->password);
88
89     if (ok)
90         return(ok);
91
92     /* probe to see if we're running IMAP4 and can use RFC822.PEEK */
93     gen_send(sock, "CAPABILITY");
94     if ((ok = gen_recv(sock, rbuf, sizeof(rbuf))))
95         return(ok);
96     if (strstr(rbuf, "BAD"))
97     {
98         imap_version = IMAP2;
99         if (outlevel == O_VERBOSE)
100             error(0, 0, "Protocol identified as IMAP2 or IMAP2BIS");
101     }
102     else if (strstr(rbuf, "IMAP4rev1"))
103     {
104         imap_version = IMAP4rev1;
105         if (outlevel == O_VERBOSE)
106             error(0, 0, "Protocol identified as IMAP4 rev 1");
107     }
108     else
109     {
110         imap_version = IMAP4;
111         if (outlevel == O_VERBOSE)
112             error(0, 0, "Protocol identified as IMAP4 rev 0");
113     }
114
115     peek_capable = (imap_version >= IMAP4);
116
117     return(PS_SUCCESS);
118 }
119
120 static int imap_getrange(int sock, 
121                          struct query *ctl, 
122                          const char *folder, 
123                          int *countp, int *newp)
124 /* get range of messages to be fetched */
125 {
126     int ok;
127
128     /* find out how many messages are waiting */
129     recent = unseen = 0;
130     ok = gen_transact(sock, "SELECT %s", folder ? folder : "INBOX");
131     if (ok != 0)
132     {
133         error(0, 0, "mailbox selection failed");
134         return(ok);
135     }
136
137     *countp = count;
138
139     if (unseen)         /* optional response, but better if we see it */
140         *newp = unseen;
141     else if (recent)    /* mandatory */
142         *newp = recent;
143     else
144         *newp = -1;     /* should never happen, RECENT is mandatory */ 
145
146     deletecount = 0;
147
148     return(PS_SUCCESS);
149 }
150
151 static int imap_getsizes(int sock, int count, int *sizes)
152 /* capture the sizes of all messages */
153 {
154     char buf [POPBUFSIZE+1];
155
156     gen_send(sock, "FETCH 1:%d RFC822.SIZE", count);
157     while (SockRead(sock, buf, sizeof(buf)))
158     {
159         int num, size, ok;
160
161         if ((ok = gen_recv(sock, buf, sizeof(buf))))
162             return(ok);
163         if (strstr(buf, "OK"))
164             break;
165         else if (sscanf(buf, "* %d FETCH (RFC822.SIZE %d)", &num, &size) == 2)
166             sizes[num - 1] = size;
167         else
168             sizes[num - 1] = -1;
169     }
170
171     return(PS_SUCCESS);
172 }
173
174 static int imap_is_old(int sock, struct query *ctl, int number)
175 /* is the given message old? */
176 {
177     int ok;
178
179     /* expunges change the fetch numbers */
180     number -= deletecount;
181
182     if ((ok = gen_transact(sock, "FETCH %d FLAGS", number)) != 0)
183         return(PS_ERROR);
184
185     return(seen);
186 }
187
188 static int imap_fetch(int sock, struct query *ctl, int number, int *lenp)
189 /* request nth message */
190 {
191     char buf [POPBUFSIZE+1];
192     int num;
193
194     /* expunges change the fetch numbers */
195     number -= deletecount;
196
197     /*
198      * If we're using IMAP4, we can fetch the message without setting its
199      * seen flag.  This is good!  It means that if the protocol exchange
200      * craps out during the message, it will still be marked `unseen' on
201      * the server.
202      *
203      * However...*don't* do this if we're using keep to suppress deletion!
204      * In that case, marking the seen flag is the only way to prevent the
205      * message from being re-fetched on subsequent runs.
206      */
207     switch (imap_version)
208     {
209     case IMAP4rev1:     /* RFC 2060 */
210         if (!ctl->keep)
211             gen_send(sock, "FETCH %d BODY.PEEK[]", number);
212         else
213             gen_send(sock, "FETCH %d BODY", number);
214         break;
215
216     case IMAP4:         /* RFC 1730 */
217         if (!ctl->keep)
218             gen_send(sock, "FETCH %d RFC822.PEEK", number);
219         else
220             gen_send(sock, "FETCH %d RFC822", number);
221         break;
222
223     default:            /* RFC 1176 */
224         gen_send(sock, "FETCH %d RFC822", number);
225         break;
226     }
227
228     /* looking for FETCH response */
229     do {
230         int     ok;
231
232         if ((ok = gen_recv(sock, buf, sizeof(buf))))
233             return(ok);
234     } while
235         /* third token can be "RFC822" or "BODY[]" */
236         (sscanf(buf+2, "%d FETCH (%*s {%d}", &num, lenp) != 2);
237
238     if (num != number)
239         return(PS_ERROR);
240     else
241         return(PS_SUCCESS);
242 }
243
244 static int imap_trail(int sock, struct query *ctl, int number)
245 /* discard tail of FETCH response after reading message text */
246 {
247     char buf [POPBUFSIZE+1];
248
249     /* expunges change the fetch numbers */
250     /* number -= deletecount; */
251
252     return(gen_recv(sock, buf, sizeof(buf)));
253 }
254
255 static int imap_delete(int sock, struct query *ctl, int number)
256 /* set delete flag for given message */
257 {
258     int ok;
259
260     /* expunges change the fetch numbers */
261     number -= deletecount;
262
263     /*
264      * Use SILENT if possible as a minor throughput optimization.
265      * Note: this has been dropped from IMAP4rev1.
266      */
267     if ((ok = gen_transact(sock,
268                         imap_version == IMAP4 
269                                 ? "STORE %d +FLAGS.SILENT (\\Deleted)"
270                                 : "STORE %d +FLAGS (\\Deleted)", 
271                         number)))
272         return(ok);
273
274     /*
275      * We do an expunge after each message, rather than just before quit,
276      * so that a line hit during a long session won't result in lots of
277      * messages being fetched again during the next session.
278      */
279     if ((ok = gen_transact(sock, "EXPUNGE")))
280         return(ok);
281
282     deletecount++;
283
284     return(PS_SUCCESS);
285 }
286
287 const static struct method imap =
288 {
289     "IMAP",             /* Internet Message Access Protocol */
290     143,                /* standard IMAP2bis/IMAP4 port */
291     1,                  /* this is a tagged protocol */
292     0,                  /* no message delimiter */
293     imap_ok,            /* parse command response */
294     imap_getauth,       /* get authorization */
295     imap_getrange,      /* query range of messages */
296     imap_getsizes,      /* grab message sizes */
297     imap_is_old,        /* no UID check */
298     imap_fetch,         /* request given message */
299     imap_trail,         /* eat message trailer */
300     imap_delete,        /* delete the message */
301     "LOGOUT",           /* the IMAP exit command */
302 };
303
304 int doIMAP(struct query *ctl)
305 /* retrieve messages using IMAP Version 2bis or Version 4 */
306 {
307     return(do_protocol(ctl, &imap));
308 }
309
310 /* imap.c ends here */