+ p += strspn(p, POSIX_space);
+ strlcpy(id, p, idsize);
+ trim(id);
+ }
+ }
+ /* XXX FIXME: do not return success here if no Message-ID header was
+ * found. */
+ return 0;
+}
+
+/** Parse the UID response (leading +OK must have been
+ * stripped off) in buf, store the number in gotnum, and store the ID
+ * into the caller-provided buffer "id" of size "idsize".
+ * Returns PS_SUCCESS or PS_PROTOCOL for failure. */
+static int parseuid(const char *buf, unsigned long *gotnum, char *id, size_t idsize)
+{
+ const char *i;
+ char *j;
+
+ /* skip leading blanks ourselves */
+ i = buf;
+ i += strspn(i, POSIX_space);
+ errno = 0;
+ *gotnum = strtoul(i, &j, 10);
+ if (j == i || !*j || errno || NULL == strchr(POSIX_space, *j)) {
+ report(stderr, GT_("Cannot handle UIDL response from upstream server.\n"));
+ return PS_PROTOCOL;
+ }
+ j += strspn(j, POSIX_space);
+ strlcpy(id, j, idsize);
+ trim(id);
+ return PS_SUCCESS;
+}
+
+/** request UIDL for single message \a num and stuff the result into the
+ * buffer \a id which can hold \a idsize bytes */
+static int pop3_getuidl(int sock, int num, char *id /** output */, size_t idsize)
+{
+ int ok;
+ char buf [POPBUFSIZE+1];
+ unsigned long gotnum;
+
+ gen_send(sock, "UIDL %d", num);
+ if ((ok = pop3_ok(sock, buf)) != 0)
+ return(ok);
+ if ((ok = parseuid(buf, &gotnum, id, idsize)))
+ return ok;
+ if (gotnum != (unsigned long)num) {
+ report(stderr, GT_("Server responded with UID for wrong message.\n"));
+ return PS_PROTOCOL;
+ }
+ return(PS_SUCCESS);
+}
+
+static int pop3_fastuidl( int sock, struct query *ctl, unsigned int count, int *newp)
+{
+ int ok;
+ unsigned int first_nr, last_nr, try_nr;
+ char id [IDLEN+1];
+ struct idlist *savep = NULL; /** pointer to cache save_str result, speeds up saves */
+
+ first_nr = 0;
+ last_nr = count + 1;
+ while (first_nr < last_nr - 1)
+ {
+ struct idlist *newl;
+
+ try_nr = (first_nr + last_nr) / 2;
+ if ((ok = pop3_getuidl(sock, try_nr, id, sizeof(id))) != 0)
+ return ok;
+ if ((newl = str_in_list(&ctl->oldsaved, id, FALSE)))
+ {
+ flag mark = newl->val.status.mark;
+ if (mark == UID_DELETED || mark == UID_EXPUNGED)
+ {
+ if (outlevel >= O_VERBOSE)
+ report(stderr, GT_("id=%s (num=%u) was deleted, but is still present!\n"), id, try_nr);
+ /* just mark it as seen now! */
+ newl->val.status.mark = mark = UID_SEEN;
+ }
+
+ /* narrow the search region! */
+ if (mark == UID_UNSEEN)
+ {
+ if (outlevel >= O_DEBUG)
+ report(stdout, GT_("%u is unseen\n"), try_nr);
+ last_nr = try_nr;
+ }
+ else
+ first_nr = try_nr;
+
+ /* save the number */
+ newl->val.status.num = try_nr;
+ }
+ else
+ {
+ if (outlevel >= O_DEBUG)
+ report(stdout, GT_("%u is unseen\n"), try_nr);
+ last_nr = try_nr;
+
+ /* save it */
+ savep = save_str(savep ? &savep : &ctl->oldsaved, id, UID_UNSEEN);
+ savep->val.status.num = try_nr;