]> Pileus Git - ~andy/fetchmail/commitdiff
With any luck, this is a correct implementation of UIDL support.
authorEric S. Raymond <esr@thyrsus.com>
Sat, 28 Sep 1996 05:02:42 +0000 (05:02 -0000)
committerEric S. Raymond <esr@thyrsus.com>
Sat, 28 Sep 1996 05:02:42 +0000 (05:02 -0000)
svn path=/trunk/; revision=168

Makefile.in
driver.c
fetchmail.c
fetchmail.h
imap.c
pop2.c
pop3.c

index 0ea85cf165c8ae7b6ba85579536d2c8f6a9ac1cf..e7073bbac33857c7d7933c3e6eee0f07eaa340dd 100644 (file)
@@ -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
index 7d5e58cbad5a986e5e092dd03d7420256a7c5093..2b295c3bc8ad571e334497ccaf2d2349e53ffae6 100644 (file)
--- 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;
                }
index 15cf8a0e36a924b027210c52bf1f582d1a50dcd3..5b59db476b8c5d0aab13868ca609bdf18c3c2f97 100644 (file)
@@ -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;
 
 }
 
-
index 8bb1c970dc187c84f328fe3b0380d5726844736a..12aaf11e34ecedeb9b112256261326778f47531a 100644 (file)
 
 #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 902688f05c145231d89189bbc640af1134b7e840..eb1553a47cfd095d17050aa3c26f46ac79b56667 100644 (file)
--- 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 e37baaa9cc7839d7b1997861df72e49aa8ffe7eb..dfb78772f9b95bd942a57e23954954f02abb1768 100644 (file)
--- 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 480b4fd7402bebc319e2ec8c9656386b6329d0dd..412c3e34b499f14e064abee2b1ee2091f473cda5 100644 (file)
--- 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)