|
From: Brendan L. <bre...@ai...> - 2006-03-07 15:07:40
|
Your patch works too. I cannot say the code is clearer than my fix, but
you are no doubt a lot more familiar with this code than I am. The key
fix (beyond not waiting for a tagged response when no tagged command had
been issued) in your fix is at line 595, where you do not attempt to
enter the pseudo-idle wait if a RECENT message has been received
(recentcount != 0).
Thanks,
Brendan
sh...@bo... wrote:
>Quoting from Brendan Lynch's mail on Thu, Mar 02, 2006:
>
>
>>However I believe the servers is behaving correctly according to the
>>IMAP spec: I have attached an explanation of the secondary problem I am
>>seeing (extra 28 s wait) that I sent to Casper; let me know if you need
>>more info.
>>
>>
>
>Yes, I have now read RFC 3501 section 5.3. This behaviour is correct.
>
>
>
>>The key point is that the imap_idle() code when 'faking' IDLE support
>>calls imap_ok() (at line 637) without having issued any tagged command:
>>
>>
>
>...
>
>
>
>>Since there is no tagged command outstanding the server will not (and
>>should not) ever send a tagged response. If we set imap_ok to only
>>return after a tagged response we are guaranteeing it will always only
>>return after a timeout.
>>
>>
>
>...
>
>
>
>>This again first does a gen_transact() which results in sending "A0130
>>NOOP" at 17:00:49. A "RECENT" update is already on its way form the
>>server, so we receive it before the "A0130 NOOP completed" at 17:00:49.
>>In imap_ok() we first process the "* 1 RECENT" notification at line 117,
>>setting recentcount, then (since we ARE waiting a tag) loop and process
>>the NOOP ok. we then return to gen_transact(), which in turn returns to
>>imap_idle().
>>
>>Since setting "recentcount" does not also change the stage, we fail the
>>test at line 633 of imap_idle() and continue to do an extra imap_ok()
>>which sets a 28 second timer at 17:00:49 and does a recv() which times
>>out at 17:01:17. We then return from imap_idle() to imap_getrange()
>>line 679. At this point recentcount is set and we exit the "while
>>(recentcount == 0 && do_idle)" loop.
>>
>>The wait between 17:00:49 and 17:01:17 was unnecessary, as already had
>>our RECENT notification. The second part of my fix causes imap_ok() to
>>change the stage to STAGE_FETCH on receipt of a RECENT notification,
>>just as it already did on receipt of a EXISTS notification. This causes
>>the if condition "if (ok != 0 || stage != STAGE_IDLE)" to be met at line
>>633 of imap_idle() so it immediately exits and does not do the extra
>>unneeded imap_ok() call.
>>
>>
>
>Yes, you have diagnosed the problem correctly and your patch is also
>correct. Based on your patch, I have prepared a new patch which does
>not modify imap_ok(), but cleans up the code in imap_idle(). Could you
>test this patch and see if this patch is acceptable to you? This patch
>is identical to the patch I had sent to Casper Gripenberg, except that
>comments relating to compliance with RFCs have been updated.
>
>
>
>------------------------------------------------------------------------
>
>Index: fetchmail-6.3/imap.c
>===================================================================
>--- fetchmail-6.3/imap.c (revision 4705)
>+++ fetchmail-6.3/imap.c (working copy)
>@@ -621,7 +621,6 @@
> {
> int ok;
>
>- stage = STAGE_IDLE;
> saved_timeout = mytimeout;
>
> if (has_idle) {
>@@ -629,6 +628,7 @@
> * at least every 28 minutes:
> * (the server may have an inactivity timeout) */
> mytimeout = 1680; /* 28 min */
>+ stage = STAGE_IDLE;
> /* enter IDLE mode */
> ok = gen_transact(sock, "IDLE");
>
>@@ -637,37 +637,43 @@
> SockWrite(sock, "DONE\r\n", 6);
> if (outlevel >= O_MONITOR)
> report(stdout, "IMAP> DONE\n");
>- } else
>- /* not idle timeout */
>- return ok;
>+ /* reset stage and timeout here: we are not idling any more */
>+ mytimeout = saved_timeout;
>+ stage = STAGE_FETCH;
>+ /* get OK IDLE message */
>+ ok = imap_ok(sock, NULL);
>+ }
> } else { /* no idle support, fake it */
>- /* when faking an idle, we can't assume the server will
>- * send us the new messages out of the blue (RFC2060);
>- * this timeout is potentially the delay before we notice
>- * new mail (can be small since NOOP checking is cheap) */
>- mytimeout = 28;
>+ /* Note: stage and timeout have not been changed here as NOOP
>+ * does not idle */
> ok = gen_transact(sock, "NOOP");
>- /* if there's an error (not likely) or we just found mail (stage
>- * has changed, timeout has also been restored), we're done */
>- if (ok != 0 || stage != STAGE_IDLE)
>- return(ok);
>
>- /* wait (briefly) for an unsolicited status update */
>- ok = imap_ok(sock, NULL);
>- /* again, this is new mail or an error */
>- if (ok != PS_IDLETIMEOUT)
>- return(ok);
>+ /* 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 = 28;
>+ 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_FETCH;
>
>- /* get OK IDLE message */
>- if (has_idle)
>- return imap_ok(sock, NULL);
>-
>- return PS_SUCCESS;
>+ return(ok);
> }
>
> static int imap_getrange(int sock,
>
>
|