diff options
-rw-r--r-- | uid.c | 207 |
1 files changed, 207 insertions, 0 deletions
@@ -0,0 +1,207 @@ +/* Copyright 1993-95 by Carl Harris, Jr. Copyright 1996 by Eric S. Raymond + * All rights reserved. + * For license terms, see the file COPYING in this directory. + */ + +/*********************************************************************** + module: uid.c + project: fetchmail + programmer: Eric S. Raymond + description: UID list handling + + ***********************************************************************/ + +#include <config.h> + +#include <stdio.h> + +#if defined(STDC_HEADERS) +#include <stdlib.h> +#include <string.h> +#endif + +#if defined(HAVE_UNISTD_H) +#include <unistd.h> +#endif + +#include "fetchmail.h" + +/* + * Machinery for handling UID lists live here. This is mainly to support + * RFC1725-conformant POP3 servers without a LAST command, but may also be + * useful for making the IMAP4 querying logic UID-oriented, if I feel + * sufficiently motivated at some point. + * + * Here's the theory: + * + * At start of a query, we have a (possibly empty) list of UIDs to be + * considered `already seen'. These are messages that were left in + * the mailbox and *not deleted* on previous queries (we don't need to + * remember the UIDs of deleted messages because ... well, they're gone!). + * This list is set up by initialized_saved_list() from the .fetchids + * file and hangs off the host's `saved' member. + * + * Early in the query, during the execution of the protocol-specific + * getrange code, the driver expects that the host's `listed' member + * will be filled with a list of UIDs and message numbers representing + * the current mailbox state. If this list is empty, the server did + * not respond to the request for a UID listing. + * + * Each time a message is fetched, we can check its UID against the + * `saved' list to see if it is old. If not, it should be downloaded + * (and possibly deleted). It should be downloaded anyway if --all + * is on. It should not be deleted if --keep is on. + * + * Each time a message is deleted, we remove its id from the `listed' + * member. + * + * At the end of the query, whatever remains in the `listed' member + * (because it was not deleted) becomes the `saved' list. The old + * `saved' list is freed. + */ + +/* UIDs associated with un-queried hosts */ +static struct idlist *scratchlist; + +void initialize_saved_lists(hostlist, idfile) +/* read file of saved IDs and attach to each host */ +struct hostrec *hostlist; +char *idfile; +{ + int st; + FILE *tmpfp; + struct hostrec *hostp; + + /* make sure lists are initially empty */ + for (hostp = hostlist; hostp; hostp = hostp->next) + hostp->saved = hostp->mailbox = (struct idlist *)NULL; + + /* let's get stored message UIDs from previous queries */ + 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) + { + save_uid(&hostp->saved, -1, id); + break; + } + } + + /* if it's not in a host we're querying, save it anyway */ + if (hostp == (struct hostrec *)NULL) + save_uid(&scratchlist, -1, buf); + } + } + fclose(tmpfp); + } +} + +void save_uid(idl, num, str) +/* save a number/UID pair on the given UID list */ +struct idlist **idl; +int num; +char *str; +{ + struct idlist *new; + + new = (struct idlist *)xmalloc(sizeof(struct idlist)); + new->num = num; + new->id = strdup(str); + new->next = *idl; + *idl = new; +} + +void free_uid_list(idl) +/* free the given UID list */ +struct idlist **idl; +{ + if (*idl == (struct idlist *)NULL) + return; + + free_uid_list(&(*idl)->next); + free ((*idl)->id); + free(*idl); + *idl = (struct idlist *)NULL; +} + +int uid_in_list(idl, str) +/* is a given ID in the given list? */ +struct idlist **idl; +char *str; +{ + if (*idl == (struct idlist *)NULL) + return(0); + else if (strcmp(str, (*idl)->id) == 0) + return(1); + else + return(uid_in_list(&(*idl)->next, str)); +} + +int delete_uid(idl, str) +/* delete given UID from given list */ +struct idlist **idl; +char *str; +{ + if (*idl == (struct idlist *)NULL) + return(0); + else if (strcmp((*idl)->id, str) == 0) + { + struct idlist *next = (*idl)->next; + + free ((*idl)->id); + free(*idl); + *idl = next; + return(1); + } + else + return(delete_uid(&(*idl)->next, str)); + return(0); +} + +void update_uid_lists(hostp) +/* perform end-of-query actions on UID lists */ +struct hostrec *hostp; +{ + /* + * Replace `saved' list with `mailbox' list as modified by deletions. + */ + free_uid_list(&hostp->saved); + hostp->saved = hostp->mailbox; +} + +void write_saved_lists(hostlist, idfile) +struct hostrec *hostlist; +char *idfile; +{ + int st, idcount; + FILE *tmpfp; + struct hostrec *hostp; + struct idlist *idp; + + /* if all lists are empty, nuke the file */ + idcount = 0; + for (hostp = hostlist; hostp; hostp = hostp->next) { + if (hostp->saved) + idcount++; + } + + /* either nuke the file or write updated last-seen IDs */ + if (!idcount) + unlink(idfile); + else + if ((tmpfp = fopen(idfile, "w")) != (FILE *)NULL) { + for (hostp = hostlist; hostp; hostp = hostp->next) { + for (idp = hostp->saved; idp; idp = idp->next) + fprintf(tmpfp, "%s %s\n", hostp->servername, idp->id); + } + for (idp = scratchlist; idp; idp = idp->next) + fputs(idp->id, tmpfp); + fclose(tmpfp); + } +} |