1 /* Copyright 1996 by Eric S. Raymond
3 * For license terms, see the file COPYING in this directory.
6 /***********************************************************************
9 programmer: Eric S. Raymond
10 description: IMAP client code
12 Chris Newman, one of the IMAP maintainers, criticized this as follows:
13 ------------------------------- CUT HERE -----------------------------------
14 On Wed, 18 Sep 1996, Eric S. Raymond wrote:
15 > 1. I do one one SELECT, at the beginning of the fetch.
17 > 2. I assume that I can pick an upper bound on message numbers from the EXISTS
22 > 3. If there is an UNSEEN nnn trailer on the OK response to SELECT, I assume
23 > that the unseen messages have message numbers which are the nnn consecutive
24 > integers up to and including the upper bound.
26 > 4. Otherwise, if the response included RECENT nnn, I assume that the unseen
27 > messages have message numbers which are the nnn consecutive integers up to
28 > and including the upper bound.
30 These will only work if your client is the only client that accesses the
31 INBOX. There is no requirement that the UNSEEN and RECENT messages are at
32 the end of the folder in general.
34 If you want to present all UNSEEN messages and flag all the messages you
35 download as SEEN, you could do a SEARCH UNSEEN and just fetch those
38 However, the proper thing to do if you want to present the messages when
39 disconnected from the server is to use UIDs. To do this, you remember the
40 highest UID you have (you can initialize to 0), and fetch everything with
41 a higher UID. Ideally, you shouldn't cause the SEEN flag to be set until
42 the user has actually seen the message. This requires STORE +FLAGS SEEN
43 for those messages which have been seen since the last update.
45 The key thing to remember is that in IMAP the server holds the
46 authoratative list of messages and the client just holds a cache. This is
47 a very different model from POP.
48 ------------------------------- CUT HERE -----------------------------------
50 A problem with this recommendation is that the UID commands don't exist
51 in IMAP2bis. Since we want to preserve IMAP2bis capability (so fetchmail
52 will continue to work with the pre-IMAP4 imapd) and we've warned the user
53 that multiple concurrent fetchmail runs are a Bad Idea, we'll stick with
56 ***********************************************************************/
61 #include "fetchmail.h"
63 /*********************************************************************
65 Method declarations for IMAP
67 *********************************************************************/
69 static int count, first;
70 static int exists, unseen, recent;
72 int imap_ok (argbuf,socket)
73 /* parse command response */
78 char buf [POPBUFSIZE+1];
83 if (SockGets(socket, buf, sizeof(buf)) < 0)
86 if (outlevel == O_VERBOSE)
87 fprintf(stderr,"%s\n",buf);
89 /* interpret untagged status responses */
90 if (strstr(buf, "EXISTS"))
92 if (strstr(buf, "RECENT"))
94 if (sscanf(buf + 2, "OK [UNSEEN %d]", &n) == 1)
98 (tag[0] != '\0' && strncmp(buf, tag, strlen(tag)));
100 if (tag[0] == '\0') {
105 if (strncmp(buf + TAGLEN + 1, "OK", 2) == 0) {
106 strcpy(argbuf, buf + TAGLEN);
109 else if (strncmp(buf + TAGLEN + 1, "BAD", 2) == 0)
116 int imap_getauth(socket, queryctl, buf)
117 /* apply for connection authorization */
119 struct hostrec *queryctl;
122 /* try to get authorized */
123 return(gen_transact(socket,
125 queryctl->remotename, queryctl->password));
128 static imap_getrange(socket, queryctl, countp, firstp)
129 /* get range of messages to be fetched */
131 struct hostrec *queryctl;
137 /* find out how many messages are waiting */
138 exists = unseen = recent = -1;
139 ok = gen_transact(socket,
141 queryctl->remotefolder[0] ? queryctl->remotefolder : "INBOX");
145 /* compute size of message run */
147 if (queryctl->fetchall)
150 if (exists > 0 && unseen == -1) {
152 "no UNSEEN response; assuming all %d RECENT messages are unseen\n",
154 *firstp = exists - recent + 1;
163 static int imap_fetch(socket, number, lenp)
164 /* request nth message */
169 char buf [POPBUFSIZE+1];
172 gen_send(socket, "FETCH %d RFC822", number);
174 /* looking for FETCH response */
176 if (SockGets(socket, buf,sizeof(buf)) < 0)
179 (sscanf(buf+2, "%d FETCH (RFC822 {%d}", &num, lenp) != 2);
187 static imap_trail(socket, queryctl, number)
188 /* discard tail of FETCH response */
190 struct hostrec *queryctl;
193 char buf [POPBUFSIZE+1];
195 if (SockGets(socket, buf,sizeof(buf)) < 0)
201 static imap_delete(socket, queryctl, number)
202 /* set delete flag for given message */
204 struct hostrec *queryctl;
207 return(socket, gen_transact("STORE %d +FLAGS (\\Deleted)", number));
210 static struct method imap =
212 "IMAP", /* Internet Message Access Protocol */
213 143, /* standard IMAP2bis/IMAP4 port */
214 1, /* this is a tagged protocol */
215 0, /* no message delimiter */
216 imap_ok, /* parse command response */
217 imap_getauth, /* get authorization */
218 imap_getrange, /* query range of messages */
219 NULL, /* no UID check */
220 imap_fetch, /* request given message */
221 imap_trail, /* eat message trailer */
222 imap_delete, /* set IMAP delete flag */
223 "EXPUNGE", /* the IMAP expunge command */
224 "LOGOUT", /* the IMAP exit command */
227 int doIMAP (queryctl)
228 /* retrieve messages using IMAP Version 2bis or Version 4 */
229 struct hostrec *queryctl;
231 return(do_protocol(queryctl, &imap));