From: Brendan L. <bre...@ai...> - 2006-02-24 01:06:33
|
I've run across what I believe to be a bug in imap_idle() on servers not supporting the IDLE extension. There is code in imap_idle() that tries to simulate and idle() call with frequent NOOP calls: 624 } else { /* no idle support, fake it */ 625 /* when faking an idle, we can't assume the server will 626 * send us the new messages out of the blue (RFC2060); 627 * this timeout is potentially the delay before we notice 628 * new mail (can be small since NOOP checking is cheap) */ 629 mytimeout = 28; 630 ok = gen_transact(sock, "NOOP"); 631 /* if there's an error (not likely) or we just found mail (stage 632 * has changed, timeout has also been restored), we're done */ 633 if (ok != 0 || stage != STAGE_IDLE) 634 return(ok); 635 636 /* wait (briefly) for an unsolicited status update */ 637 ok = imap_ok(sock, NULL); 638 /* again, this is new mail or an error */ 639 if (ok != PS_IDLETIMEOUT) 640 return(ok); 641 } This code works fine until a notification of new mail is received (a "* 1 EXISTS" message is received). At this point normally one is in the "imap_ok()" routine called from line 637, and this correctly receives the notification message and parses it, updating the "count" variable. However it does not the return to imap_idle() routine, but instead reissues a recv call having set a much longer (300s) timeout and after the 300s have expired it then returns an error causing fetchmail to drop the IMAP connection (a main point of this idle code was to keep the connection open for subsequent message retrieval). Net result is that delivery of mail is delayed by 5 minutes and the server connection is dropped and reestablished each time a wait for mail occurs. The problem seems to be caused by the loop condition in imap_ok(): 151 } 152 } while 153 (tag[0] != '\0' && strncmp(buf, tag, strlen(tag))); 154 This assumes that tag[0] will be set to '\0' if one is not waiting for a tagged response. In this case the code should not be waiting for a tagged response (it is waiting for an unsolicited notification). However the 'tag' global character array is set by the gen_transact() at line 630 and is not cleared before the call to imap_ok() at line 637. The fix is a very simple one-line change to imap_idle (2 lines with comments): 630 ok = gen_transact(sock, "NOOP"); 631 /* if there's an error (not likely) or we just found mail (stage 632 * has changed, timeout has also been restored), we're done */ 633 if (ok != 0 || stage != STAGE_IDLE) 634 return(ok); 635 + 636 /* clear tag so imap_ok does not expect tagged response */ + 637 tag[0]='\0'; 638 /* wait (briefly) for an unsolicited status update */ 639 ok = imap_ok(sock, NULL); A second, more minor problem is that getting a "* RECENT" notification does not break a caller out of the imap_idle's imap_ok() call. This causes an extra 28second wait after being notified about a message before it is actually received. Diffs for the complete set of changes against 6.3.2 are attached to this email. I have seen this in fetchmail 6.2.5 and 6.3.2 on linux platforms, but this problem should be generic to all platforms. |