aboutsummaryrefslogtreecommitdiffstats
path: root/imap.c
blob: 369b294469a16af66f2aa5eb53e1ba69e817ff0d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/* Copyright 1996 by Eric S. Raymond
 * All rights reserved.
 * For license terms, see the file COPYING in this directory.
 */

/***********************************************************************
  module:       imap.c
  project:      fetchmail
  programmer:   Eric S. Raymond
  description:  IMAP client code

Chris Newman, one of the IMAP maintainers, criticized this as follows:
------------------------------- CUT HERE -----------------------------------
On Wed, 18 Sep 1996, Eric S. Raymond wrote:
> 1. I do one one SELECT, at the beginning of the fetch.  
> 
> 2. I assume that I can pick an upper bound on message numbers from the EXISTS
>    reponse.

Correct.

> 3. If there is an UNSEEN nnn trailer on the OK response to SELECT, I assume
>    that the unseen messages have message numbers which are the nnn consecutive
>    integers up to and including the upper bound.
> 
> 4. Otherwise, if the response included RECENT nnn, I assume that the unseen
>    messages have message numbers which are the nnn consecutive integers up to
>    and including the upper bound.

These will only work if your client is the only client that accesses the
INBOX.  There is no requirement that the UNSEEN and RECENT messages are at
the end of the folder in general.

If you want to present all UNSEEN messages and flag all the messages you
download as SEEN, you could do a SEARCH UNSEEN and just fetch those
messages.

However, the proper thing to do if you want to present the messages when
disconnected from the server is to use UIDs.  To do this, you remember the
highest UID you have (you can initialize to 0), and fetch everything with
a higher UID.  Ideally, you shouldn't cause the SEEN flag to be set until
the user has actually seen the message.  This requires STORE +FLAGS SEEN
for those messages which have been seen since the last update.

The key thing to remember is that in IMAP the server holds the
authoratative list of messages and the client just holds a cache.  This is
a very different model from POP.
------------------------------- CUT HERE -----------------------------------

A problem with this recommendation is that the UID commands don't exist
in IMAP2bis.  Since we want to preserve IMAP2bis capability (so fetchmail
will continue to work with the pre-IMAP4 imapd) and we've warned the user
that multiple concurrent fetchmail runs are a Bad Idea, we'll stick with
this logic for now.

 ***********************************************************************/

#include  <config.h>
#include  <stdio.h>
#include  "socket.h"
#include  "fetchmail.h"

/*********************************************************************

 Method declarations for IMAP 

 *********************************************************************/

static int count, first;
static int exists, unseen, recent;

int imap_ok (argbuf,socket)
/* parse command response */
char *argbuf;
int socket;
{
  int ok;
  char buf [POPBUFSIZE+1];
  char *bufp;
  int n;

  do {
    if (SockGets(socket, buf, sizeof(buf)) < 0)
      return(PS_SOCKET);

    if (outlevel == O_VERBOSE)
      fprintf(stderr,"%s\n",buf);

    /* interpret untagged status responses */
    if (strstr(buf, "EXISTS"))
	exists = atoi(buf+2);
    if (strstr(buf, "RECENT"))
	recent = atoi(buf+2);
    if (sscanf(buf + 2, "OK [UNSEEN %d]", &n) == 1)
	unseen = n;

  } while
      (tag[0] != '\0' && strncmp(buf, tag, strlen(tag)));

  if (tag[0] == '\0') {
    strcpy(argbuf, buf);
    return(0); 
  }
  else {
    if (strncmp(buf + TAGLEN + 1, "OK", 2) == 0) {
      strcpy(argbuf, buf + TAGLEN);
      return(0);
    }
    else if (strncmp(buf + TAGLEN + 1, "BAD", 2) == 0)
      return(PS_ERROR);
    else
      return(PS_PROTOCOL);
  }
}

int imap_getauth(socket, queryctl, buf)
/* apply for connection authorization */
int socket;
struct hostrec *queryctl;
char *buf;
{
    /* try to get authorized */
    return(gen_transact(socket,
		  "LOGIN %s %s",
		  queryctl->remotename, queryctl->password));
}

static imap_getrange(socket, queryctl, countp, firstp)
/* get range of messages to be fetched */
int socket;
struct hostrec *queryctl;
int *countp;
int *firstp;
{
    int ok;

    /* find out how many messages are waiting */
    exists = unseen = recent = -1;
    ok = gen_transact(socket,
		  "SELECT %s",
		  queryctl->remotefolder[0] ? queryctl->remotefolder : "INBOX");
    if (ok != 0)
	return(ok);

    /* compute size of message run */
    *countp = exists;
    if (queryctl->fetchall)
	*firstp = 1;
    else {
	if (exists > 0 && unseen == -1) {
	    fprintf(stderr,
		    "no UNSEEN response; assuming all %d RECENT messages are unseen\n",
		    recent);
	    *firstp = exists - recent + 1;
	} else {
	    *firstp = unseen;
	}
    }

    return(0);
}

static int imap_fetch(socket, number, lenp)
/* request nth message */
int socket;
int number;
int *lenp; 
{
    char buf [POPBUFSIZE+1];
    int	num;

    gen_send(socket, "FETCH %d RFC822", number);

    /* looking for FETCH response */
    do {
	if (SockGets(socket, buf,sizeof(buf)) < 0)
	    return(PS_SOCKET);
    } while
	    (sscanf(buf+2, "%d FETCH (RFC822 {%d}", &num, lenp) != 2);

    if (num != number)
	return(PS_ERROR);
    else
	return(0);
}

static imap_trail(socket, queryctl, number)
/* discard tail of FETCH response */
int socket;
struct hostrec *queryctl;
int number;
{
    char buf [POPBUFSIZE+1];

    if (SockGets(socket, buf,sizeof(buf)) < 0)
	return(PS_SOCKET);
    else
	return(0);
}

static imap_delete(socket, queryctl, number)
/* set delete flag for given message */
int socket;
struct hostrec *queryctl;
int number;
{
    return(socket, gen_transact("STORE %d +FLAGS (\\Deleted)", number));
}

static struct method imap =
{
    "IMAP",		/* Internet Message Access Protocol */
    143,		/* standard IMAP2bis/IMAP4 port */
    1,			/* this is a tagged protocol */
    0,			/* no message delimiter */
    imap_ok,		/* parse command response */
    imap_getauth,	/* get authorization */
    imap_getrange,	/* query range of messages */
    NULL,		/* no UID check */
    imap_fetch,		/* request given message */
    imap_trail,		/* eat message trailer */
    imap_delete,	/* set IMAP delete flag */
    "EXPUNGE",		/* the IMAP expunge command */
    "LOGOUT",		/* the IMAP exit command */
};

int doIMAP (queryctl)
/* retrieve messages using IMAP Version 2bis or Version 4 */
struct hostrec *queryctl;
{
    return(do_protocol(queryctl, &imap));
}