-/* Copyright 1996 by Eric S. Raymond
+/*
+ * imap.c -- IMAP2bis protocol methods
+ *
+ * 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 <string.h>
+#include <ctype.h>
+#if defined(STDC_HEADERS)
+#include <stdlib.h>
+#endif
#include "socket.h"
#include "fetchmail.h"
-/*********************************************************************
-
- Method declarations for IMAP
-
- *********************************************************************/
-
-static int count, first;
-static int exists, unseen, recent;
+static int count, seen, recent, unseen;
-int imap_ok (argbuf,socket)
+int imap_ok (sockfp, argbuf)
/* parse command response */
char *argbuf;
-int socket;
+FILE *sockfp;
{
- 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);
+ char buf [POPBUFSIZE+1];
+
+ seen = 0;
+ do {
+ if (SockGets(buf, sizeof(buf), sockfp) < 0)
+ return(PS_SOCKET);
+
+ if (outlevel == O_VERBOSE)
+ fprintf(stderr,"%s\n",buf);
+
+ /* interpret untagged status responses */
+ if (strstr(buf, "EXISTS"))
+ count = atoi(buf+2);
+ if (strstr(buf, "RECENT"))
+ recent = atoi(buf+2);
+ if (strstr(buf, "UNSEEN"))
+ unseen = atoi(buf+2);
+ if (strstr(buf, "FLAGS"))
+ seen = (strstr(buf, "Seen") != (char *)NULL);
+ } while
+ (tag[0] != '\0' && strncmp(buf, tag, strlen(tag)));
+
+ if (tag[0] == '\0')
+ {
+ strcpy(argbuf, buf);
+ return(0);
}
- else if (strncmp(buf + TAGLEN + 1, "BAD", 2) == 0)
- return(PS_ERROR);
else
- return(PS_PROTOCOL);
- }
+ {
+ char *cp;
+
+ /* skip the tag */
+ for (cp = buf; !isspace(*cp); cp++)
+ continue;
+ while (isspace(*cp))
+ cp++;
+
+ if (strncmp(cp, "OK", 2) == 0)
+ {
+ strcpy(argbuf, cp);
+ return(0);
+ }
+ else if (strncmp(cp, "BAD", 2) == 0)
+ return(PS_ERROR);
+ else
+ return(PS_PROTOCOL);
+ }
}
-int imap_getauth(socket, queryctl, buf)
+int imap_getauth(sockfp, ctl, buf)
/* apply for connection authorization */
-int socket;
-struct hostrec *queryctl;
+FILE *sockfp;
+struct query *ctl;
char *buf;
{
/* try to get authorized */
- return(gen_transact(socket,
- "LOGIN %s %s",
- queryctl->remotename, queryctl->password));
+ return(gen_transact(sockfp,
+ "LOGIN %s \"%s\"",
+ ctl->remotename, ctl->password));
}
-static imap_getrange(socket, queryctl, countp, firstp)
+static int imap_getrange(sockfp, ctl, countp, newp)
/* get range of messages to be fetched */
-int socket;
-struct hostrec *queryctl;
-int *countp;
-int *firstp;
+FILE *sockfp;
+struct query *ctl;
+int *countp, *newp;
{
int ok;
/* find out how many messages are waiting */
- exists = unseen = recent = -1;
- ok = gen_transact(socket,
+ recent = unseen = 0;
+ ok = gen_transact(sockfp,
"SELECT %s",
- queryctl->remotefolder[0] ? queryctl->remotefolder : "INBOX");
+ ctl->mailbox[0] ? ctl->mailbox : "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;
- }
+ *countp = count;
+
+ if (unseen) /* optional response, but better if we see it */
+ *newp = unseen;
+ else if (recent) /* mandatory */
+ *newp = recent;
+ else
+ *newp = -1; /* should never happen, RECENT is mandatory */
+
+ return(0);
+}
+
+static int imap_getsizes(sockfp, count, sizes)
+/* capture the sizes of all messages */
+FILE *sockfp;
+int count;
+int *sizes;
+{
+ char buf [POPBUFSIZE+1];
+
+ gen_send(sockfp, "FETCH 1:%d RFC822.SIZE", count);
+ while (SockGets(buf, sizeof(buf), sockfp) >= 0)
+ {
+ int num, size;
+
+ if (outlevel == O_VERBOSE)
+ fprintf(stderr,"%s\n",buf);
+ if (strstr(buf, "OK"))
+ break;
+ else if (sscanf(buf, "* %d FETCH (RFC822.SIZE %d)", &num, &size) == 2)
+ sizes[num - 1] = size;
+ else
+ sizes[num - 1] = -1;
}
return(0);
}
-static int imap_fetch(socket, number, lenp)
+static int imap_is_old(sockfp, ctl, num)
+/* is the given message old? */
+FILE *sockfp;
+struct query *ctl;
+int num;
+{
+ int ok;
+
+ if ((ok = gen_transact(sockfp, "FETCH %d FLAGS", num)) != 0)
+ exit(PS_ERROR);
+
+ return(seen);
+}
+
+static int imap_fetch(sockfp, number, lenp)
/* request nth message */
-int socket;
+FILE *sockfp;
int number;
int *lenp;
{
char buf [POPBUFSIZE+1];
int num;
- gen_send(socket, "FETCH %d RFC822", number);
+ gen_send(sockfp, "FETCH %d RFC822", number);
/* looking for FETCH response */
do {
- if (SockGets(socket, buf,sizeof(buf)) < 0)
+ if (SockGets(buf, sizeof(buf), sockfp) < 0)
return(PS_SOCKET);
} while
(sscanf(buf+2, "%d FETCH (RFC822 {%d}", &num, lenp) != 2);
return(0);
}
-static imap_trail(socket, queryctl, number)
-/* discard tail of FETCH response */
-int socket;
-struct hostrec *queryctl;
+static int imap_trail(sockfp, ctl, number)
+/* discard tail of FETCH response after reading message text */
+FILE *sockfp;
+struct query *ctl;
int number;
{
char buf [POPBUFSIZE+1];
- if (SockGets(socket, buf,sizeof(buf)) < 0)
+ if (SockGets(buf, sizeof(buf), sockfp) < 0)
return(PS_SOCKET);
else
return(0);
}
-static imap_delete(socket, queryctl, number)
+static int imap_delete(sockfp, ctl, number)
/* set delete flag for given message */
-int socket;
-struct hostrec *queryctl;
+FILE *sockfp;
+struct query *ctl;
int number;
{
- return(socket, gen_transact("STORE %d +FLAGS (\\Deleted)", number));
+ return(gen_transact(sockfp, "STORE %d +FLAGS (\\Deleted)", number));
}
-static struct method imap =
+const static struct method imap =
{
"IMAP", /* Internet Message Access Protocol */
143, /* standard IMAP2bis/IMAP4 port */
imap_ok, /* parse command response */
imap_getauth, /* get authorization */
imap_getrange, /* query range of messages */
- NULL, /* no UID check */
+ imap_getsizes, /* grab message sizes */
+ imap_is_old, /* no UID check */
imap_fetch, /* request given message */
imap_trail, /* eat message trailer */
imap_delete, /* set IMAP delete flag */
"LOGOUT", /* the IMAP exit command */
};
-int doIMAP (queryctl)
+int doIMAP(ctl)
/* retrieve messages using IMAP Version 2bis or Version 4 */
-struct hostrec *queryctl;
+struct query *ctl;
{
- return(do_protocol(queryctl, &imap));
+ return(do_protocol(ctl, &imap));
}
-
+/* imap.c ends here */