/** * \file uid.c -- UIDL handling for POP3 servers without LAST * * For license terms, see the file COPYING in this directory. */ #include "config.h" #include #include #include #include #if defined(STDC_HEADERS) #include #include #endif #if defined(HAVE_UNISTD_H) #include #endif #include "fetchmail.h" #include "i18n.h" #include "sdump.h" /* * Machinery for handling UID lists live here. This is mainly to support * RFC1725/RFC1939-conformant POP3 servers without a LAST command, but may also * be useful for making the IMAP4 querying logic UID-oriented, if a future * revision of IMAP forces me to. * * These functions are also used by the rest of the code to maintain * string lists. * * Here's the theory: * * At start of a query, we have a (possibly empty) list of UIDs to be * considered seen in `oldsaved'. 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 initially set up by initialize_saved_list() from the * .fetchids file. * * Early in the query, during the execution of the protocol-specific * getrange code, the driver expects that the host's `newsaved' member * will be filled with a list of UIDs and message numbers representing * the 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 * `oldsaved' list to see if it is old. * * Each time a message-id is seen, we mark it with MARK_SEEN. * * Each time a message is deleted, we mark its id UID_DELETED in the * `newsaved' member. When we want to assert that an expunge has been * done on the server, we call expunge_uid() to register that all * deleted messages are gone by marking them UID_EXPUNGED. * * At the end of the query, the `newsaved' member becomes the * `oldsaved' list. The old `oldsaved' list is freed. * * At the end of the fetchmail run, seen and non-EXPUNGED members of all * current `oldsaved' lists are flushed out to the .fetchids file to * be picked up by the next run. If there are no un-expunged * messages, the file is deleted. * * One disadvantage of UIDL is that all the UIDs have to be downloaded * before a search for new messages can be done. Typically, new messages * are appended to mailboxes. Hence, downloading all UIDs just to download * a few new mails is a waste of bandwidth. If new messages are always at * the end of the mailbox, fast UIDL will decrease the time required to * download new mails. * * During fast UIDL, the UIDs of all messages are not downloaded! The first * unseen message is searched for by using a binary search on UIDs. UIDs * after the first unseen message are downloaded as and when needed. * * The advantages of fast UIDL are (this is noticeable only when the * mailbox has too many mails): * * - There is no need to download the UIDs of all mails right at the start. * - There is no need to save all the UIDs in memory separately in * `newsaved' list. * - There is no need to download the UIDs of seen mail (except for the * first binary search). * - The first new mail is downloaded considerably faster. * * The disadvantages are: * * - Since all UIDs are not downloaded, it is not possible to swap old and * new list. The current state of the mailbox is essentially a merged state * of old and new mails. * - If an intermediate mail has been temporarily refused (say, due to 4xx * code from the smtp server), this mail may not get downloaded. * - If 'flush' is used, such intermediate mails will also get deleted. * * The first two disadvantages can be overcome by doing a linear search * once in a while (say, every 10th poll). Also, with flush, fast UIDL * should be disabled. * * Note: some comparisons (those used for DNS address lists) are caseblind! */ int dofastuidl = 0; #ifdef POP3_ENABLE /** UIDs associated with un-queried hosts */ static struct idlist *scratchlist; /** Read saved IDs from \a idfile and attach to each host in \a hostlist. */ void initialize_saved_lists(struct query *hostlist, const char *idfile) { struct stat statbuf; FILE *tmpfp; struct query *ctl; /* make sure lists are initially empty */ for (ctl = hostlist; ctl; ctl = ctl->next) { ctl->skipped = (struct idlist *)NULL; ctl->oldsaved = (struct idlist *)NULL; ctl->newsaved = (struct idlist *)NULL; ctl->oldsavedend =
			 fetchmail README

Fetchmail is a free, full-featured, robust, well-documented remote
mail retrieval and forwarding utility intended to be used over
on-demand TCP/IP links (such as SLIP or PPP connections).  It
retrieves mail from remote mail servers and forwards it to your local
(client) machine's delivery system, so it can then be be read by
normal mail user agents such as elm(1) or Mail(1).

Fetchmail supports all standard mail-retrieval protocols in use on the
Internet: POP2, POP3, RPOP, APOP, KPOP, IMAP2bis, IMAP4, IMAP4rev1
ESMTP ETRN, and ODMR.  Fetchmail also fully supports authentication
via GSSAPI, Kerberos 4 and 5, RFC1938 one-time passwords, Compuserve's
POP3 with RPA, Microsoft's NTLM, Demon Internet's SDPS, or CRAM-MD5
authentication a la RFC2195.  Fetchmail also supports end-to-end
encryption with OpenSSL.

The fetchmail code was developed under Linux, but has also been
extensively tested under the BSD variants, AIX, HP-UX versions 9 and
10, SunOS, Solaris, NEXTSTEP, OSF 3.2, IRIX, and Rhapsody.

It should be readily portable to other Unix variants (it uses GNU
autoconf).  It has been ported to LynxOS and BeOS and will build there
without special action.  It has also been ported to QNX; to build
under QNX, see the header comments in the Makefile.  It is reported to
build and run under AmigaOS.

See the distribution files FEATURES for a full list of features, NEWS
for detailed information on recent changes, NOTES for design notes, and
TODO for a list of things that still need doing.

The fetchmail code appears to be stable and free of bugs affecting
normal operation (that is, retrieving from POP3 or IMAP in single-drop
mode and forwarding via SMTP to sendmail).  It will probably undergo
substantial change only if and when support for a new retrieval
protocol or authentication mode is added.

If you want to hack on this code, a list of known bugs and to-do items
can be found in the file todo.html.

You can easily fetch the latest version of fetchmail via FTP from the
following FTP directory:

	ftp://ftp.ccil.org/pub/esr/fetchmail

Or you can get it from the fetchmail home page:

	http://www.tuxedo.org/~esr/fetchmail

Enjoy!

							-- esr
idp = ctl->oldsaved; idp; idp = idp->next) idp->val.status.num = 0; } /** Write list of seen messages, at end of run. */ void write_saved_lists(struct query *hostlist, const char *idfile) { long idcount; FILE *tmpfp; struct query *ctl; struct idlist *idp; /* if all lists are empty, nuke the file */ idcount = 0; for (ctl = hostlist; ctl; ctl = ctl->next) { for (idp = ctl->oldsaved; idp; idp = idp->next) if (idp->val.status.mark == UID_SEEN || idp->val.status.mark == UID_DELETED) idcount++; } /* either nuke the file or write updated last-seen IDs */ if (!idcount && !scratchlist) { if (outlevel >= O_DEBUG) { if (access(idfile, F_OK) == 0) report(stdout, GT_("Deleting fetchids file.\n")); } if (unlink(idfile) && errno != ENOENT) report(stderr, GT_("Error deleting %s: %s\n"), idfile, strerror(errno)); } else { char *newnam = (char *)xmalloc(strlen(idfile) + 2); strcpy(newnam, idfile); strcat(newnam, "_"); if (outlevel >= O_DEBUG) report(stdout, GT_("Writing fetchids file.\n")); (void)unlink(newnam); /* remove file/link first */ if ((tmpfp = fopen(newnam, "w")) != (FILE *)NULL) { int errflg; for (ctl = hostlist; ctl; ctl = ctl->next) { for (idp = ctl->oldsaved; idp; idp = idp->next) if (idp->val.status.mark == UID_SEEN || idp->val.status.mark == UID_DELETED) fprintf(tmpfp, "%s@%s %s\n", ctl->remotename, ctl->server.queryname, idp->id); } for (idp = scratchlist; idp; idp = idp->next) fputs(idp->id, tmpfp); fflush(tmpfp); errflg = ferror(tmpfp); fclose(tmpfp); /* if we could write successfully, move into place; * otherwise, drop */ if (errflg) { report(stderr, GT_("Error writing to fetchids file %s, old file left in place.\n"), newnam); unlink(newnam); } else { if (rename(newnam, idfile)) { report(stderr, GT_("Cannot rename fetchids file %s to %s: %s\n"), newnam, idfile, strerror(errno)); } } } else { report(stderr, GT_("Cannot open fetchids file %s for writing: %s\n"), newnam, strerror(errno)); } free(newnam); } } #endif /* POP3_ENABLE */ /* uid.c ends here */