aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS1
-rw-r--r--driver.c10
-rw-r--r--fetchmail.h3
-rw-r--r--pop3.c117
-rw-r--r--uid.c35
5 files changed, 162 insertions, 4 deletions
diff --git a/NEWS b/NEWS
index bd3c2ac9..3b7780cf 100644
--- 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.
diff --git a/driver.c b/driver.c
index 5ec0f45a..3113513c 100644
--- 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;
diff --git a/fetchmail.h b/fetchmail.h
index 0db3f4b3..a3ded780 100644
--- a/fetchmail.h
+++ b/fetchmail.h
@@ -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 e5e26299..5b8f53b9 100644
--- 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 5bab716a..735bdbf8 100644
--- 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. */
{