+ if (has_idle) {
+ /* special timeout to terminate the IDLE and re-issue it
+ * at least every 28 minutes:
+ * (the server may have an inactivity timeout) */
+ mytimeout = idle_timeout = 1680; /* 28 min */
+ time(&idle_start_time);
+ stage = STAGE_IDLE;
+ /* enter IDLE mode */
+ ok = gen_transact(sock, "IDLE");
+
+ if (ok == PS_IDLETIMEOUT) {
+ /* send "DONE" continuation */
+ SockWrite(sock, "DONE\r\n", 6);
+ if (outlevel >= O_MONITOR)
+ report(stdout, "IMAP> DONE\n");
+ /* reset stage and timeout here: we are not idling any more */
+ mytimeout = saved_timeout;
+ stage = STAGE_GETRANGE;
+ /* get OK IDLE message */
+ ok = imap_ok(sock, NULL);
+ }
+ } else { /* no idle support, fake it */
+ /* Note: stage and timeout have not been changed here as NOOP
+ * does not idle */
+ ok = gen_transact(sock, "NOOP");
+
+ /* no error, but no new mail either */
+ if (ok == PS_SUCCESS && recentcount == 0)
+ {
+ /* There are some servers who do send new mail
+ * notification out of the blue. This is in compliance
+ * with RFC 2060 section 5.3. Wait for that with a low
+ * timeout */
+ mytimeout = idle_timeout = 28;
+ time(&idle_start_time);
+ stage = STAGE_IDLE;
+ /* We are waiting for notification; no tag needed */
+ tag[0] = '\0';
+ /* wait (briefly) for an unsolicited status update */
+ ok = imap_ok(sock, NULL);
+ if (ok == PS_IDLETIMEOUT) {
+ /* no notification came; ok */
+ ok = PS_SUCCESS;
+ }
+ }
+ }
+
+ /* restore normal timeout value */
+ set_timeout(0);
+ mytimeout = saved_timeout;
+ stage = STAGE_GETRANGE;
+
+ return(ok);
+}
+
+static int imap_search(int sock, struct query *ctl, int count)
+/* search for unseen messages */
+{
+ int ok;
+ char buf[MSGBUFSIZE+1], *cp;
+
+ /* Don't count deleted messages. Enabled only for IMAP4 servers or
+ * higher and only when keeping mails. This flag will have an
+ * effect only when user has marked some unread mails for deletion
+ * using another e-mail client. */
+ flag skipdeleted = (imap_version >= IMAP4) && ctl->keep;
+ const char *undeleted;
+
+ /* structure to keep the end portion of the incomplete response */
+ struct RecvSplit rs;
+
+ /* startcount is higher than count so that if there are no
+ * unseen messages, imap_getsizes() will not need to do
+ * anything! */
+ startcount = count + 1;
+
+ for (;;)
+ {
+ undeleted = (skipdeleted ? " UNDELETED" : "");
+ gen_send(sock, "SEARCH UNSEEN%s", undeleted);
+ gen_recv_split_init("* SEARCH", &rs);
+ while ((ok = imap_response(sock, buf, &rs)) == PS_UNTAGGED)
+ {
+ if ((cp = strstr(buf, "* SEARCH")))
+ {
+ char *ep;
+
+ cp += 8; /* skip "* SEARCH" */
+ while (*cp && unseen < count)
+ {
+ /* skip whitespace */
+ while (*cp && isspace((unsigned char)*cp))
+ cp++;
+ if (*cp)
+ {
+ unsigned long um;
+
+ errno = 0;
+ um = strtoul(cp,&ep,10);
+ if (errno == 0 && ep > cp
+ && um <= INT_MAX && um <= (unsigned)count)
+ {
+ unseen_messages[unseen++] = um;
+ if (outlevel >= O_DEBUG)
+ report(stdout, GT_("%lu is unseen\n"), um);
+ if (startcount > um)
+ startcount = um;
+ }
+ cp = ep;
+ }
+ }
+ }
+ }
+ if (ok != PS_ERROR) /* success or non-protocol error */
+ return(ok);
+
+ /* there is a protocol error. try a different search command. */
+ if (skipdeleted)
+ {
+ /* retry with "SEARCH UNSEEN" */
+ skipdeleted = FALSE;
+ continue;
+ }
+ /* try with "FETCH 1:n FLAGS" */
+ break;
+ }
+
+ if (count == 1)
+ gen_send(sock, "FETCH %d FLAGS", count);
+ else
+ gen_send(sock, "FETCH %d:%d FLAGS", 1, count);
+ while ((ok = imap_response(sock, buf, NULL)) == PS_UNTAGGED)
+ {
+ unsigned int num;
+ int consumed;
+
+ /* expected response format:
+ * IMAP< * 1 FETCH (FLAGS (\Seen))
+ * IMAP< * 2 FETCH (FLAGS (\Seen \Deleted))
+ * IMAP< * 3 FETCH (FLAGS ())
+ * IMAP< * 4 FETCH (FLAGS (\Recent))
+ * IMAP< * 5 FETCH (UID 10 FLAGS (\Recent))
+ */
+ if (unseen < count
+ && sscanf(buf, "* %u %n", &num, &consumed) == 1
+ && 0 == strncasecmp(buf+consumed, "FETCH", 5)
+ && isspace((unsigned char)buf[consumed+5])
+ && num >= 1 && num <= (unsigned)count
+ && strstr(buf, "FLAGS ")
+ && !strstr(buf, "\\SEEN")
+ && !strstr(buf, "\\DELETED"))
+ {
+ unseen_messages[unseen++] = num;
+ if (outlevel >= O_DEBUG)
+ report(stdout, GT_("%u is unseen\n"), num);
+ if (startcount > num)
+ startcount = num;
+ }
+ }
+ return(ok);