]> Pileus Git - ~andy/fetchmail/commitdiff
Wolfgang Wander's patch for better UIDL-only support.
authorEric S. Raymond <esr@thyrsus.com>
Sat, 16 Aug 1997 00:02:36 +0000 (00:02 -0000)
committerEric S. Raymond <esr@thyrsus.com>
Sat, 16 Aug 1997 00:02:36 +0000 (00:02 -0000)
svn path=/trunk/; revision=1272

NEWS
driver.c
fetchmail.h
pop3.c
uid.c

diff --git a/NEWS b/NEWS
index bd3c2ac918c1fc5c624ae80cf7faf59729996359..3b7780cf7f17067aa2dfdc92403415ca64155dbf 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -15,6 +15,7 @@
 fetchmail-4.1.1 ()
 * Fix an obvious bug in some snprintf calls (non-Linux systems only)
 * No more hard limit on number of destination headers
+* Wolfgang Wander's support for faster new-message detection using UIDL.
 
 fetchmail-4.1.0 (Mon Aug 11 17:19:57 EDT 1997)
 * Make the RPM depend on `smtpdaemon', which the sendmail package provides.
index 5ec0f45aa6f529273f81dabc07a0db60c95789c7..3113513ce179c7b6b18a1b42c29434c74fdd4287 100644 (file)
--- a/driver.c
+++ b/driver.c
@@ -702,6 +702,16 @@ int num;           /* index of message */
            from_offs = (line - headers);
        else if (!strncasecmp("Content-Transfer-Encoding:", line, 26))
            ctt_offs = (line - headers);
+       else if (!strncasecmp("Message-Id:", buf, 11 ))
+       {
+           if( ctl->server.uidl )
+           {
+               char id[IDLEN+1];
+               sscanf( buf+12, "%s", id);
+               if( !str_in_list( &ctl->newsaved, id ) )
+                   save_str(&ctl->newsaved, num, id );
+           }
+       }
 
        else if (!MULTIDROP(ctl))
            continue;
index 0db3f4b3ad483b1304220acdb717b9bab88effa3..a3ded7803645464149074f06ebb472513e758de9 100644 (file)
@@ -245,6 +245,9 @@ void save_str_pair(struct idlist **, const char *, const char *);
 void free_str_pair_list(struct idlist **);
 int delete_str(struct idlist **, int);
 int str_in_list(struct idlist **, const char *);
+int str_nr_in_list(struct idlist **, const char *);
+int count_list( struct idlist **idl );
+char *str_from_nr_list( struct idlist **idl, int number );
 char *str_find(struct idlist **, int);
 char *idpair_find(struct idlist **, const char *);
 void append_str_list(struct idlist **, struct idlist **);
diff --git a/pop3.c b/pop3.c
index e5e26299e6526208fa2e01c1d4e5484bb0c1a452..5b8f53b91c035d239677095ea337dcdced16a12d 100644 (file)
--- a/pop3.c
+++ b/pop3.c
@@ -199,6 +199,113 @@ int pop3_getauth(int sock, struct query *ctl, char *greeting)
     return(0);
 }
 
+static int
+pop3_gettopid( int sock, int num , char *id)
+{
+    int ok;
+    int got_it;
+    char buf [POPBUFSIZE+1];
+    sprintf( buf, "TOP %d 1", num );
+    if( (ok = gen_transact(sock, buf ) ) != 0 )
+       return ok; 
+    got_it = 0;
+    while((ok = gen_recv(sock, buf, sizeof(buf))) == 0) {
+       if( buf[0] == '.' )
+           break;
+       if( ! got_it && ! strncasecmp("Message-Id:", buf, 11 )) {
+           got_it = 1;
+           sscanf( buf+12, "%s", id);
+       }
+    }
+    return 0;
+}
+
+static int
+pop3_slowuidl( int sock,  struct query *ctl, int *countp, int *newp)
+{
+    /* This approach tries to get the message headers from the
+     * remote hosts and compares the message-id to the already known
+     * ones:
+     *  + if the first message containes a new id, all messages on
+     *    the server will be new
+     *  + if the first is known, try to estimate the last known message
+     *    on the server and check. If this works you know the total number
+     *    of messages to get.
+     *  + Otherwise run a binary search to determine the last known message
+     */
+    int ok, nolinear = 0;
+    int first_nr, list_len, try_id, try_nr, hop_id, add_id;
+    int num;
+    char id [IDLEN+1];
+    
+    if( (ok = pop3_gettopid( sock, 1, id )) != 0 )
+       return ok;
+    
+    if( ( first_nr = str_nr_in_list(&ctl->oldsaved, id) ) == -1 ) {
+       /* the first message is unknown -> all messages are new */
+       *newp = *countp;        
+       return 0;
+    }
+
+    /* check where we expect the latest known message */
+    list_len = count_list( &ctl->oldsaved );
+    try_id = list_len  - first_nr; /* -1 + 1 */
+    if( try_id > 1 ) {
+       if( try_id <= *countp ) {
+           if( (ok = pop3_gettopid( sock, try_id, id )) != 0 )
+               return ok;
+    
+           try_nr = str_nr_in_list(&ctl->oldsaved, id);
+       } else {
+           try_id = *countp+1;
+           try_nr = -1;
+       }
+       if( try_nr != list_len -1 ) {
+           /* some messages inbetween have been deleted... */
+           if( try_nr == -1 ) {
+               nolinear = 1;
+
+               for( add_id = 1<<30; add_id > try_id-1; add_id >>= 1 )
+                   ;
+               for( ; add_id; add_id >>= 1 ) {
+                   if( try_nr == -1 ) {
+                       if( try_id - add_id <= 1 ) {
+                           continue;
+                       }
+                       try_id -= add_id;
+                   } else 
+                       try_id += add_id;
+                   
+                   if( (ok = pop3_gettopid( sock, try_id, id )) != 0 )
+                       return ok;
+                   try_nr = str_nr_in_list(&ctl->oldsaved, id);
+               }
+               if( try_nr == -1 ) {
+                   try_id--;
+               }
+           } else {
+               error(0,0,"Messages inserted into list on server. "
+                     "Cannot handle this.");
+               return -1;
+           }
+       } 
+    }
+    /* The first try_id messages are known -> copy them to
+       the newsaved list */
+    for( num = first_nr; num < list_len; num++ )
+       save_str(&ctl->newsaved, num-first_nr + 1,
+                str_from_nr_list( &ctl->oldsaved, num ));
+
+    if( nolinear ) {
+       free_str_list(&ctl->oldsaved);
+       ctl->oldsaved = 0;
+       last = try_id;
+    }
+
+    *newp = *countp - try_id;
+    return 0;
+}
+
 static int pop3_getrange(int sock, 
                         struct query *ctl,
                         const char *folder,
@@ -243,9 +350,13 @@ static int pop3_getrange(int sock,
        }
        else
        {
-           /* grab the mailbox's UID list */
-           if ((ok = gen_transact(sock, "UIDL")) != 0)
-               PROTOCOL_ERROR
+           /* grab the mailbox's UID list */
+           if ((ok = gen_transact(sock, "UIDL")) != 0)
+           {
+               /* don't worry, yet! do it the slow way */
+               if((ok = pop3_slowuidl( sock, ctl, countp, newp))!=0)
+                   PROTOCOL_ERROR
+           }
            else
            {
                int     num;
diff --git a/uid.c b/uid.c
index 5bab716aa7cbc35e81895593395079eebd174c5e..735bdbf8d4225a811083a8fce3e6013303c96134 100644 (file)
--- a/uid.c
+++ b/uid.c
@@ -82,7 +82,8 @@ void initialize_saved_lists(struct query *hostlist, const char *idfile)
            {
                for (ctl = hostlist; ctl; ctl = ctl->next)
                {
-                   if (strcasecmp(host, ctl->server.truename) == 0
+                   if (ctl->server.truename &&
+                       strcasecmp(host, ctl->server.truename) == 0
                                && strcasecmp(user, ctl->remotename) == 0)
                    {
                        save_str(&ctl->oldsaved, -1, id);
@@ -172,6 +173,38 @@ int str_in_list(struct idlist **idl, const char *str)
        return(str_in_list(&(*idl)->next, str));
 }
 
+int str_nr_in_list( struct idlist **idl, const char *str )
+  /* return the position of str in idl */
+{
+    int nr;
+    struct idlist *walk;
+    if ( !str )
+        return -1;
+    for( walk = *idl, nr = 0; walk; nr ++, walk = walk->next )
+        if( strcasecmp( str, walk->id) == 0 )
+           return nr;
+    return -1;
+}
+
+int count_list( struct idlist **idl )
+  /* count the number of elements in the list */
+{
+  if( !*idl )
+    return 0;
+  return 1 + count_list( &(*idl)->next );
+}
+
+char* str_from_nr_list( struct idlist **idl, int number )
+  /* return the number'th string in idl */
+{
+    if( !*idl  || number < 0)
+        return 0;
+    if( number == 0 )
+        return (*idl)->id;
+    return str_from_nr_list(&(*idl)->next, number-1);
+}
+
+    
 char *str_find(struct idlist **idl, int number)
 /* return the id of the given number in the given list. */
 {