aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.in5
-rw-r--r--driver.c11
-rw-r--r--fetchmail.c66
-rw-r--r--fetchmail.h74
-rw-r--r--imap.c34
-rw-r--r--pop2.c3
-rw-r--r--pop3.c175
7 files changed, 214 insertions, 154 deletions
diff --git a/Makefile.in b/Makefile.in
index 0ea85cf1..e7073bba 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -72,14 +72,15 @@ ETAGS = etags -tw
CTAGS = ctags -tw
popobjs = socket.o getpass.o pop2.o pop3.o imap.o fetchmail.o options.o \
- rcfile_l.o rcfile_y.o rcfile.o daemon.o driver.o smtp.o xmalloc.o
+ rcfile_l.o rcfile_y.o rcfile.o daemon.o driver.o smtp.o xmalloc.o uid.o
objs = $(popobjs) $(EXTRAOBJ) $(extras)
srcs = $(srcdir)/socket.c $(srcdir)/getpass.c $(srcdir)/pop2.c \
$(srcdir)/pop3.c $(srcdir)/imap.c $(srcdir)/fetchmail.c \
$(srcdir)/options.c $(srcdir)/rcfile.c $(srcdir)/daemon.c \
- $(srcdir)/driver.c $(srcdir)/smtp.c $(srcdir)/xmalloc.c $(EXTRASRC)
+ $(srcdir)/driver.c $(srcdir)/smtp.c $(srcdir)/xmalloc.c \
+ $(srcdir)/uid.c $(EXTRASRC)
.SUFFIXES:
.SUFFIXES: .o .c .h .y .l .ps .dvi .info .texi
diff --git a/driver.c b/driver.c
index 7d5e58cb..2b295c3b 100644
--- a/driver.c
+++ b/driver.c
@@ -592,7 +592,7 @@ struct method *proto;
if (ok != 0)
goto cleanUp;
- /* compute count and first */
+ /* compute count and first, and get UID list if possible */
if ((*protocol->getrange)(socket, queryctl, &count, &first) != 0)
goto cleanUp;
@@ -651,6 +651,11 @@ struct method *proto;
ok = 0; /* retrieval suppressed */
else
{
+ /* we may want to reject this message based on its UID */
+ if (!queryctl->fetchall && *protocol->is_old)
+ if ((*protocol->is_old)(socket, queryctl, num))
+ continue;
+
/* request a message */
(*protocol->fetch)(socket, num, linelimit, &len);
if (outlevel == O_VERBOSE)
@@ -697,12 +702,12 @@ struct method *proto;
}
/* maybe we delete this message now? */
- if (protocol->delete_cmd)
+ if (protocol->delete)
{
if ((num < first && queryctl->flush) || !queryctl->keep) {
if (outlevel > O_SILENT && outlevel < O_VERBOSE)
fprintf(stderr,"flushing message %d\n", num);
- ok = gen_transact(socket, protocol->delete_cmd, num);
+ ok = (protocol->delete)(socket, queryctl, num);
if (ok != 0)
goto cleanUp;
}
diff --git a/fetchmail.c b/fetchmail.c
index 15cf8a0e..5b59db47 100644
--- a/fetchmail.c
+++ b/fetchmail.c
@@ -128,7 +128,6 @@ char **argv;
prc_mergeoptions(servername, &cmd_opts, &def_opts, hostp);
strcpy(hostp->servername, servername);
parseMDAargs(hostp);
- hostp->lastid[0] = '\0';
hostp->next = hostlist;
hostlist = hostp;
@@ -146,6 +145,12 @@ char **argv;
strcat(tmpbuf, user);
}
+ /* initialize UID handling */
+ if ((st = prc_filecheck(idfile)) != 0)
+ exit(st);
+ else
+ initialize_saved_lists(hostlist, idfile);
+
/* perhaps we just want to check options? */
if (versioninfo) {
printf("Taking options from command line and %s\n", rcfile);
@@ -206,23 +211,6 @@ char **argv;
return(PS_EXCLUDE);
}
- /* let's get stored message IDs from previous transactions */
- if ((st = prc_filecheck(idfile)) != 0) {
- return (st);
- } else if ((tmpfp = fopen(idfile, "r")) != (FILE *)NULL) {
- char buf[POPBUFSIZE+1], host[HOSTLEN+1], id[IDLEN+1];
-
- while (fgets(buf, POPBUFSIZE, tmpfp) != (char *)NULL) {
- if ((st = sscanf(buf, "%s %s\n", host, id)) == 2) {
- for (hostp = hostlist; hostp; hostp = hostp->next) {
- if (strcmp(host, hostp->servername) == 0)
- strcpy(hostp->lastid, id);
- }
- }
- }
- fclose(tmpfp);
- }
-
/* pick up interactively any passwords we need but don't have */
for (hostp = hostlist; hostp; hostp = hostp->next)
if (!(implicitmode && hostp->skip) && !hostp->password[0])
@@ -259,7 +247,10 @@ char **argv;
do {
for (hostp = hostlist; hostp; hostp = hostp->next) {
if (!implicitmode || !hostp->skip)
+ {
popstatus = query_host(hostp);
+ update_uid_lists(hostp);
+ }
}
sleep(poll_interval);
@@ -274,28 +265,12 @@ char **argv;
}
void termhook(int sig)
+/* to be executed on normal or signal-induced termination */
{
- FILE *tmpfp;
- int idcount = 0;
-
if (sig != 0)
fprintf(stderr, "terminated with signal %d\n", sig);
- for (hostp = hostlist; hostp; hostp = hostp->next) {
- if (hostp->lastid[0])
- idcount++;
- }
-
- /* write updated last-seen IDs */
- if (!idcount)
- unlink(idfile);
- else if ((tmpfp = fopen(idfile, "w")) != (FILE *)NULL) {
- for (hostp = hostlist; hostp; hostp = hostp->next) {
- if (hostp->lastid[0])
- fprintf(tmpfp, "%s %s\n", hostp->servername, hostp->lastid);
- }
- fclose(tmpfp);
- }
+ write_saved_lists(hostlist, idfile);
unlink(lockfile);
exit(popstatus);
@@ -478,8 +453,22 @@ struct hostrec *queryctl;
else
printf(" Text retrieved per message will be at most %d bytes.\n",
linelimit);
- if (queryctl->lastid[0])
- printf(" ID of last message retrieved %s\n", queryctl->lastid);
+ if (queryctl->protocol > P_POP2)
+ if (!queryctl->saved)
+ printf(" No UIDs saved from this host.\n");
+ else
+ {
+ struct idlist *idp;
+ int count = 0;
+
+ for (idp = hostp->saved; idp; idp = idp->next)
+ ++count;
+
+ printf(" %d UIDs saved.\n", count);
+ if (outlevel == O_VERBOSE)
+ for (idp = hostp->saved; idp; idp = idp->next)
+ fprintf(stderr, "\t%s %s\n", hostp->servername, idp->id);
+ }
}
/*********************************************************************
@@ -688,4 +677,3 @@ struct hostrec *queryctl;
}
-
diff --git a/fetchmail.h b/fetchmail.h
index 8bb1c970..12aaf11e 100644
--- a/fetchmail.h
+++ b/fetchmail.h
@@ -56,34 +56,43 @@
#define SIZETICKER 1024 /* print 1 dot per this many bytes */
+struct idlist
+{
+ int num;
+ char *id;
+ struct idlist *next;
+};
+
struct hostrec
{
- char servername [HOSTLEN+1];
- char localname [USERNAMELEN+1];
- char remotename [USERNAMELEN+1];
- char password [PASSWORDLEN+1];
- char userfolder [FOLDERLEN+1];
- char remotefolder [FOLDERLEN];
- char smtphost[HOSTLEN+1];
- char mda [MDALEN+1];
- int keep;
- int protocol;
- int fetchall;
- int flush;
- int norewrite;
- int skip;
- int port;
-
- /* state used for tracking UIDL ids */
- char lastid [IDLEN+1];
-
- /* dependent on the above members */
- int output;
- struct hostrec *next;
-
- /* internal use only */
+ /* per-host data */
+ char servername [HOSTLEN+1];
+ char localname [USERNAMELEN+1];
+ char remotename [USERNAMELEN+1];
+ char password [PASSWORDLEN+1];
+ char userfolder [FOLDERLEN+1];
+ char remotefolder [FOLDERLEN];
+ char smtphost[HOSTLEN+1];
+ char mda [MDALEN+1];
+ int protocol;
+ int port;
+
+ /* control flags */
+ int output;
+ int keep;
+ int fetchall;
+ int flush;
+ int norewrite;
+ int skip;
+
+ /* previous state of mailbox (initially from .fetchids) */
+ struct idlist *saved;
+
+ /* internal use */
+ struct hostrec *next; /* next host in chain */
+ struct idlist *mailbox; /* current state of mailbox */
#if defined(HAVE_APOP_SUPPORT)
- char digest [DIGESTLEN];
+ char digest [DIGESTLEN];
#endif
};
@@ -96,9 +105,10 @@ struct method
int (*parse_response)(); /* response_parsing function */
int (*getauth)(); /* authorization fetcher */
int (*getrange)(); /* get message range to fetch */
+ int (*is_old)(); /* check for old message */
int (*fetch)(); /* fetch a given message */
int (*trail)(); /* eat trailer of a message */
- char *delete_cmd; /* delete command */
+ int (*delete)(); /* delete method */
char *expunge_cmd; /* expunge command */
char *exit_cmd; /* exit command */
};
@@ -123,14 +133,22 @@ extern int versioninfo; /* emit only version info */
#ifdef HAVE_PROTOTYPES
-int gen_ok (char *buf, int socket);
+/* prototypes for globally callable functions */
void gen_send ();
int gen_transact ();
-/* prototypes for globally callable functions */
int doPOP2 (struct hostrec *);
int doPOP3 (struct hostrec *);
int doIMAP (struct hostrec *);
+
+void initialize_saved_lists(struct hostrec *, char *);
+void save_uid(struct idlist **, int, char *);
+void free_uid_list(struct idlist **);
+int delete_uid(struct idlist **, char *);
+int uid_in_list(struct idlist **, char *);
+void update_uid_lists(struct hostrec *);
+void write_saved_lists(struct hostrec *, char *);
+
int parsecmdline (int, char **, struct hostrec *);
int setdefaults (struct hostrec *);
char *getnextserver (int argc, char **, int *);
diff --git a/imap.c b/imap.c
index 902688f0..eb1553a4 100644
--- a/imap.c
+++ b/imap.c
@@ -206,20 +206,30 @@ int number;
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 */
- imap_fetch, /* request given message */
- imap_trail, /* eat message trailer */
- "STORE %d +FLAGS (\\Deleted)", /* set IMAP delete flag */
- "EXPUNGE", /* the IMAP expunge command */
- "LOGOUT", /* the IMAP exit command */
+ "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)
diff --git a/pop2.c b/pop2.c
index e37baaa9..dfb78772 100644
--- a/pop2.c
+++ b/pop2.c
@@ -137,9 +137,10 @@ static struct method pop2 =
pop2_ok, /* parse command response */
pop2_getauth, /* get authorization */
pop2_getrange, /* query range of messages */
+ NULL, /* no UID check */
pop2_fetch, /* request given message */
pop2_trail, /* eat message trailer */
- NULL, /* no POP2 delete command */
+ NULL, /* no POP2 delete method */
NULL, /* no POP2 expunge command */
"QUIT", /* the POP2 exit command */
};
diff --git a/pop3.c b/pop3.c
index 480b4fd7..412c3e34 100644
--- a/pop3.c
+++ b/pop3.c
@@ -151,62 +151,87 @@ struct hostrec *queryctl;
int *countp;
int *firstp;
{
- int ok;
- char buf [POPBUFSIZE+1];
+ int ok;
+ char buf [POPBUFSIZE+1];
- /* get the total message count */
- gen_send(socket, "STAT");
- ok = pop3_ok(buf,socket);
- if (ok == 0)
- sscanf(buf,"%d %*d", countp);
- else
- return(ok);
-
- /*
- * Ask for number of last message retrieved.
- * Newer, RFC-1760-conformant POP servers may not have the LAST command.
- * Therefore we don't croak if we get a nonzero return. Instead, send
- * UIDL and try to find the last received ID stored for this host in
- * the list we get back.
- */
- *firstp = 1;
- use_uidl = 0;
- if (*countp > 0 && !queryctl->fetchall) {
- char id [IDLEN+1];
- int num;
-
- /* try LAST first */
- gen_send(socket,"LAST");
+ /* get the total message count */
+ gen_send(socket, "STAT");
ok = pop3_ok(buf,socket);
- if (ok == 0 && sscanf(buf, "%d", firstp) == 0)
- return(PS_ERROR);
-
- use_uidl = (ok != 0);
-
- /* otherwise, if we have a stored last ID for this host,
- * send UIDL and search the returned list for it
- */
- if (use_uidl && queryctl->lastid[0]) {
- gen_send("UIDL");
- if ((ok = pop3_ok(buf, socket)) == 0) {
- while (SockGets(socket, buf, sizeof(buf)) >= 0) {
- if (outlevel == O_VERBOSE)
- fprintf(stderr,"%s\n",buf);
- if (strcmp(buf, ".\n") == 0) {
- break;
+ if (ok == 0)
+ sscanf(buf,"%d %*d", countp);
+ else
+ return(ok);
+
+ /*
+ * Ask for number of last message retrieved.
+ * Newer, RFC-1760-conformant POP servers may not have the LAST command.
+ * Therefore we don't croak if we get a nonzero return. Instead, send
+ * UIDL and try to find the last received ID stored for this host in
+ * the list we get back.
+ */
+ *firstp = 1;
+ use_uidl = 0;
+ if (*countp > 0 && !queryctl->fetchall) {
+ char id [IDLEN+1];
+ int num;
+
+ /* try LAST first */
+ gen_send(socket,"LAST");
+ ok = pop3_ok(buf,socket);
+ if (ok == 0 && sscanf(buf, "%d", &num) == 0)
+ return(PS_ERROR);
+
+ use_uidl = (ok != 0);
+
+ if (!use_uidl)
+ *firstp = num + 1;
+ else
+ {
+ /* grab the mailbox's UID list */
+ gen_send(socket, "UIDL");
+ if ((ok = pop3_ok(buf, socket)) == 0) {
+ while (SockGets(socket, buf, sizeof(buf)) >= 0) {
+ if (outlevel == O_VERBOSE)
+ fprintf(stderr,"%s\n",buf);
+ if (strcmp(buf, ".\n") == 0) {
+ break;
+ }
+ if (sscanf(buf, "%d %s\n", &num, id) == 2)
+ save_uid(&queryctl->mailbox, num, id);
+ }
}
- if (sscanf(buf, "%d %s\n", &num, id) == 2)
- if (strcmp(id, queryctl->lastid) == 0)
- *firstp = num;
- }
- }
+ }
}
- if (ok == 0)
- (*firstp)++;
- }
+ return(0);
+}
+
+static pop3_is_old(socket, queryctl, number)
+/* check a given message for age by UID */
+int socket;
+struct hostrec *queryctl;
+int number;
+{
+ if (!use_uidl)
+ return(0);
+ else
+ {
+ char buf [POPBUFSIZE+1];
+ int ok;
+
+ gen_send(socket, "UIDL %d", number);
+ if ((ok = pop3_ok(socket, buf)) != 0)
+ return(ok);
+ else
+ {
+ char id[IDLEN+1];
- return(0);
+ if (sscanf(buf, "%*d %s", id) == 2)
+ return(uid_in_list(&queryctl->saved, id));
+ else
+ return(0);
+ }
+ }
}
static int pop3_fetch(socket, number, limit, lenp)
@@ -223,25 +248,36 @@ int *lenp;
return(gen_transact(socket, "RETR %d", number));
}
-static pop3_trail(socket, queryctl, number)
-/* update the last-seen field for this host */
+static pop3_delete(socket, queryctl, number)
+/* delete a given message */
int socket;
struct hostrec *queryctl;
int number;
{
- if (!use_uidl)
- return(0);
- else
+ int ok;
+
+ /* send the deletion request */
+ if ((ok = gen_transact(socket, "DELE %d", number)) != 0)
+ return(ok);
+
+ /* we may need to nuke the message's UID out of the mailbox list */
+ if (use_uidl)
{
char buf [POPBUFSIZE+1];
- int ok;
+ /*
+ * This isn't strictly necessary. We should probably just
+ * stash the UID result from the last is_old call.
+ */
gen_send(socket, "UIDL %d", number);
if ((ok = pop3_ok(socket, buf)) != 0)
return(ok);
else
{
- sscanf(buf, "%*d %s", queryctl->lastid);
+ char id[IDLEN+1];
+
+ if (sscanf(buf, "%*d %s", id) == 2)
+ delete_uid(&queryctl->mailbox, id);
return(0);
}
}
@@ -249,18 +285,19 @@ int number;
static struct method pop3 =
{
- "POP3", /* Post Office Protocol v3 */
- 110, /* standard POP3 port */
- 0, /* this is not a tagged protocol */
- 1, /* this uses a message delimiter */
- pop3_ok, /* parse command response */
- pop3_getauth, /* get authorization */
- pop3_getrange, /* query range of messages */
- pop3_fetch, /* request given message */
- pop3_trail, /* eat message trailer */
- "DELE %d", /* set POP3 delete flag */
- NULL, /* the POP3 expunge command */
- "QUIT", /* the POP3 exit command */
+ "POP3", /* Post Office Protocol v3 */
+ 110, /* standard POP3 port */
+ 0, /* this is not a tagged protocol */
+ 1, /* this uses a message delimiter */
+ pop3_ok, /* parse command response */
+ pop3_getauth, /* get authorization */
+ pop3_getrange, /* query range of messages */
+ pop3_is_old, /* check for age by UID */
+ pop3_fetch, /* request given message */
+ NULL, /* no message trailer */
+ pop3_delete, /* how to delete a message */
+ NULL, /* no POP3 expunge command */
+ "QUIT", /* the POP3 exit command */
};
int doPOP3 (queryctl)