if ((gen_transact(sock, "USER %s", ctl->remotename)) != 0)
PROTOCOL_ERROR
-#ifdef HAVE_LIBOPIE
+#if defined(HAVE_LIBOPIE) && defined(OPIE_ENABLE)
/* see RFC1938: A One-Time Password System */
if (challenge = strstr(lastok, "otp-"))
{
char response[OPIE_RESPONSE_MAX+1];
+ /*
+ * Special case in case we're running Craig Metz's
+ * OPIE daemon. Code in opiegenerator() will detect this.
+ */
if (ctl->password && !strcmp(ctl->password, "opie"))
{
if (ok = opiegenerator(challenge, "", response))
ok = gen_transact(sock, "PASS %s", response);
}
else
-#else
+#endif /* defined(HAVE_LIBOPIE) && defined(OPIE_ENABLE) */
/* ordinary validation, no one-time password */
ok = gen_transact(sock, "PASS %s", ctl->password);
-#endif /* HAVE_LIBOPIE */
if (ok != 0)
{
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,
/* Ensure that the new list is properly empty */
ctl->newsaved = (struct idlist *)NULL;
+#ifdef MBOX
+ /* Alain Knaff suggests this, but it's not RFC standard */
+ if (folder)
+ if ((ok = gen_transact(sock, "MBOX %s", folder)))
+ return ok;
+#endif /* MBOX */
+
/* get the total message count */
gen_send(sock, "STAT");
ok = pop3_ok(sock, buf);
}
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;
if ((ok = pop3_ok(sock, buf)) != 0)
return(ok);
- /*
- * Look for "nnn octets" -- there may or may not be preceding cruft.
- * It's OK to punt and pass back -1 as a failure indication here, as
- * long as the force_getsizes flag has forced sizes to be preloaded.
- */
- if ((cp = strstr(buf, " octets")) == (char *)NULL)
- *lenp = -1;
- else
- {
- while (--cp >= buf && isdigit(*cp))
- continue;
- *lenp = atoi(++cp);
- }
+ *lenp = -1; /* we got sizes from the LIST response */
return(0);
}
static int pop3_delete(int sock, struct query *ctl, int number)
/* delete a given message */
{
+ /* actually, mark for deletion -- doesn't happen until QUIT time */
return(gen_transact(sock, "DELE %d", number));
}
110, /* standard POP3 port */
FALSE, /* this is not a tagged protocol */
TRUE, /* this uses a message delimiter */
- TRUE, /* RFC 1725 doesn't require a size field in fetch */
pop3_ok, /* parse command response */
pop3_getauth, /* get authorization */
pop3_getrange, /* query range of messages */
int doPOP3 (struct query *ctl)
/* retrieve messages using POP3 */
{
+#ifndef MBOX
if (ctl->mailboxes->id) {
fprintf(stderr,"Option --remote is not supported with POP3\n");
return(PS_SYNTAX);
}
+#endif /* MBOX */
peek_capable = FALSE;
return(do_protocol(ctl, &pop3));
}