diff options
Diffstat (limited to 'pop3.c')
-rw-r--r-- | pop3.c | 117 |
1 files changed, 114 insertions, 3 deletions
@@ -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; |