diff options
-rw-r--r-- | Makefile.in | 5 | ||||
-rw-r--r-- | driver.c | 11 | ||||
-rw-r--r-- | fetchmail.c | 66 | ||||
-rw-r--r-- | fetchmail.h | 74 | ||||
-rw-r--r-- | imap.c | 34 | ||||
-rw-r--r-- | pop2.c | 3 | ||||
-rw-r--r-- | pop3.c | 175 |
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 @@ -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 *); @@ -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) @@ -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 */ }; @@ -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) |