]> Pileus Git - ~andy/fetchmail/blob - imap.c
The great options massacre.
[~andy/fetchmail] / imap.c
1 /* Copyright 1996 by Eric S. Raymond
2  * All rights reserved.
3  * For license terms, see the file COPYING in this directory.
4  */
5
6 /***********************************************************************
7   module:       imap.c
8   project:      fetchmail
9   programmer:   Eric S. Raymond
10   description:  IMAP client code
11
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.  
16
17 > 2. I assume that I can pick an upper bound on message numbers from the EXISTS
18 >    reponse.
19
20 Correct.
21
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.
25
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.
29
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.
33
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
36 messages.
37
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.
44
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 -----------------------------------
49
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
54 this logic for now.
55
56  ***********************************************************************/
57
58 #include  <config.h>
59 #include  <stdio.h>
60 #include  "socket.h"
61 #include  "fetchmail.h"
62
63 /*********************************************************************
64
65  Method declarations for IMAP 
66
67  *********************************************************************/
68
69 static int count, first;
70 static int exists, unseen, recent;
71
72 int imap_ok (argbuf,socket)
73 /* parse command response */
74 char *argbuf;
75 int socket;
76 {
77   int ok;
78   char buf [POPBUFSIZE+1];
79   char *bufp;
80   int n;
81
82   do {
83     if (SockGets(socket, buf, sizeof(buf)) < 0)
84       return(PS_SOCKET);
85
86     if (outlevel == O_VERBOSE)
87       fprintf(stderr,"%s\n",buf);
88
89     /* interpret untagged status responses */
90     if (strstr(buf, "EXISTS"))
91         exists = atoi(buf+2);
92     if (strstr(buf, "RECENT"))
93         recent = atoi(buf+2);
94     if (sscanf(buf + 2, "OK [UNSEEN %d]", &n) == 1)
95         unseen = n;
96
97   } while
98       (tag[0] != '\0' && strncmp(buf, tag, strlen(tag)));
99
100   if (tag[0] == '\0') {
101     strcpy(argbuf, buf);
102     return(0); 
103   }
104   else {
105     if (strncmp(buf + TAGLEN + 1, "OK", 2) == 0) {
106       strcpy(argbuf, buf + TAGLEN);
107       return(0);
108     }
109     else if (strncmp(buf + TAGLEN + 1, "BAD", 2) == 0)
110       return(PS_ERROR);
111     else
112       return(PS_PROTOCOL);
113   }
114 }
115
116 int imap_getauth(socket, queryctl, buf)
117 /* apply for connection authorization */
118 int socket;
119 struct hostrec *queryctl;
120 char *buf;
121 {
122     /* try to get authorized */
123     return(gen_transact(socket,
124                   "LOGIN %s %s",
125                   queryctl->remotename, queryctl->password));
126 }
127
128 static imap_getrange(socket, queryctl, countp, firstp)
129 /* get range of messages to be fetched */
130 int socket;
131 struct hostrec *queryctl;
132 int *countp;
133 int *firstp;
134 {
135     int ok;
136
137     /* find out how many messages are waiting */
138     exists = unseen = recent = -1;
139     ok = gen_transact(socket,
140                   "SELECT %s",
141                   queryctl->remotefolder[0] ? queryctl->remotefolder : "INBOX");
142     if (ok != 0)
143         return(ok);
144
145     /* compute size of message run */
146     *countp = exists;
147     if (queryctl->fetchall)
148         *firstp = 1;
149     else {
150         if (exists > 0 && unseen == -1) {
151             fprintf(stderr,
152                     "no UNSEEN response; assuming all %d RECENT messages are unseen\n",
153                     recent);
154             *firstp = exists - recent + 1;
155         } else {
156             *firstp = unseen;
157         }
158     }
159
160     return(0);
161 }
162
163 static int imap_fetch(socket, number, lenp)
164 /* request nth message */
165 int socket;
166 int number;
167 int *lenp; 
168 {
169     char buf [POPBUFSIZE+1];
170     int num;
171
172     gen_send(socket, "FETCH %d RFC822", number);
173
174     /* looking for FETCH response */
175     do {
176         if (SockGets(socket, buf,sizeof(buf)) < 0)
177             return(PS_SOCKET);
178     } while
179             (sscanf(buf+2, "%d FETCH (RFC822 {%d}", &num, lenp) != 2);
180
181     if (num != number)
182         return(PS_ERROR);
183     else
184         return(0);
185 }
186
187 static imap_trail(socket, queryctl, number)
188 /* discard tail of FETCH response */
189 int socket;
190 struct hostrec *queryctl;
191 int number;
192 {
193     char buf [POPBUFSIZE+1];
194
195     if (SockGets(socket, buf,sizeof(buf)) < 0)
196         return(PS_SOCKET);
197     else
198         return(0);
199 }
200
201 static imap_delete(socket, queryctl, number)
202 /* set delete flag for given message */
203 int socket;
204 struct hostrec *queryctl;
205 int number;
206 {
207     return(socket, gen_transact("STORE %d +FLAGS (\\Deleted)", number));
208 }
209
210 static struct method imap =
211 {
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 */
225 };
226
227 int doIMAP (queryctl)
228 /* retrieve messages using IMAP Version 2bis or Version 4 */
229 struct hostrec *queryctl;
230 {
231     return(do_protocol(queryctl, &imap));
232 }
233
234