From: Sunil S. <sh...@bo...> - 2009-07-23 12:31:51
|
Quoting from Matthias Andree's mail on Thu, Jul 23, 2009: > > ==================================================================== > > Case 1: After fetchmail has sent the initial MAIL FROM: to the SMTP > > server/invoked the MDA with %F substitution: > > > > I am assuming here that the SMTP server/MDA is doing a DNS > > lookup/basic spam testing on the sender address. > > > > So, in the SMTP server case, fetchmail is waiting for a response from > > the server after sending the initial MAIL FROM: while the SMTP server > > is doing the DNS lookup/basic spam testing. > > > > In the MDA case, fetchmail goes ahead with popen() and starts writing > > the body to the MDA. As the MDA is still doing the DNS lookup/basic > > spam testing and has not yet started reading the body, the pipe buffer > > will get full and fetchmail will get blocked on an fwrite() later. > > > > While fetchmail is blocked, the POP3 mailserver is waiting for > > fetchmail to read the entire mail body. > > > > Note that the write() timeout for the POP3 mailserver may be shorter > > than the read() timeout. This means that the POP3 mailserver is more > > likely to timeout faster if it finds that the body is not getting > > drained at all in a reasonable amount of time. > > ==================================================================== > > Case 2: After fetchmail has sent the entire mail to the SMTP server/MDA: > > > > Here, the remote mailserver is waiting for a command from fetchmail, > > while fetchmail is waiting for a response from the SMTP server/exit > > code from the MDA. > > > > As mentioned above, the read() timeout may be longer for the POP3 > > mailserver and so it may not mind waiting for the next command. > > ==================================================================== > > I'm not sure if I want to design fetchmail around guesses if read() or > write() timeouts on the server are set differently. What I mean to say is that Case 1 above must be occurring more frequently than reported. Hence, it should be tackled first. Of course, I could be wrong and so Case 2 should be tackled first. > >> a. queue downloaded messages before handing them off for delivery. This > >> avoids timeouts that originate in the SMTP/LMTP server or MDA that > >> fetchmail forwards to. > > > > This should work. Of course, fetchmail will have to work entirely with > > UIDs as it will have to reconnect later and mark delivered mails for > > deletion. > > Yup. That's what I want to do anyways in the next after-6.3.N releases (if > I'll call them 6.4 or 6.5 or 7.0, I'll decide later). > > So we'd have to polish the existing UID patches for IMAP to support > UIDVALIDITY (not a major issue, once you detect UIDVALIDITY changes, you > discard all stored UIDs) - OTOH if fetchmail deals cleanly with the > existing \Seen and \Deleted flags, we don't even need that for IMAP. I > need to check the IMAP4r1 transaction model though. Ok. > >> Fixing 2 is sort of a requisite for solving 1 in way a or c - we need to > >> track more state. This does entail changing the .fetchids format as > >> discussed in 2006, but the UID parser appeared very tolerant even at > >> that > >> time, so that an extension would be possible and backwards compatible. I > >> would feel more comfortable checking that again, but I think I checked > >> thoroughly in 2006 already. Even if we must change the .fetchids > >> format/layout, I'm open to it. > > > > Well, changing the .fetchids format is anyway a must. If you can > > incorporate the UID parser, it will be great. If I remember correctly, > > I'm not sure what you mean by "incorporate" here. I mean, please merge my UID parser code patch in fetchmail, if it is still suitable. > > the UID parser also had an option to mark bad mails. This would be > > used in such cases where there is a repeated delivery failure on the > > same mail. Once a certain bad count is reached, fetchmail will stop > > attempting to download the mail. > > I don't think fetchmail has such a feature in the baseline code. The > internal uid data structure is: From my UID parser code patch: struct uidlist { union { unsigned char *id; long int nid; /* IMAP ids are integers! */ }; int num; int mark; /* UID-index information */ #define UID_UNSEEN 0 /* hasn't been seen */ #define UID_SEEN 1 /* seen, but not deleted */ #define UID_DELETED 2 /* this message has been deleted */ #define UID_EXPUNGED 3 /* this message has been expunged */ #define UID_OVERSIZED 4 /* this message is oversized */ #define UID_SKIPPED 5 /* this message has been skipped */ #define UID_OLD_DELETED 6 /* this message was deleted but not expunged! */ #define UID_ERROR 99 /* processing error */ time_t dltime; /* time of delivery */ int errcount; /* count of errors while downloading this mail */ int size; /* size of the mail */ struct uidlist *next; }; > I'm considering a general solution that doesn't require such an analysis, > but solves all of the issues at the same time. > > WRT tracking the DELE/QUIT races in POP3, I am wondering about the > handling of the QUIT. Can we see a difference between "server hasn't > received QUIT" and "we haven't seen the answer"? In other words, will the > server's TCP stack hand the QUIT command to the server application > software even if TCP couldn't send the ACK? I think it will, because the > ACK itself needn't be ACKed and the server often won't care if we don't > see the +OK after QUIT... > > The other option is top track UIDs with "to be deleted" and "deleted, QUIT > +OK pending" and "deleted, QUIT acknowledged": > > - "QUIT acknowledged" is easy, we don't save that state per UID, but just > drop the corresponding UID as the server will do the same. > > - "to be deleted" means we're positively sure that the transaction was > rolled back (because we haven't sent the QUIT command) - we need a > workaround server option though, because some servers can be configured to > spoil the protocol dangerously and commit DELEtes on loss of connection > unless there's a RSET. We can assume the server won't reassign the UID > until the next cycle (*) > > - "deleted, QUIT +OK pending" is for your borderline case, we've sent the > QUIT to the TCP/IP stack but haven't seen the +OK response. If we see more > than half of the UIDs marked QUIT +OK pending in the next cycle, we'll > mark them "to be deleted", if it's less than half, we'll forget them and > re-fetch. The other option is to hash a subset of whitespace-normalized > message headers (Received, Message-ID, perhaps others, making sure to > avoid X-Status or other mutable headers) to accompany the UID. We could > hash headers as they pass by in forwarding and only re-fetch them in your > "we send QUIT but don't see +OK" case if we don't trust the UID. I wonder > if we should do that. Well, the 'UID_OLD_DELETED' mark mentioned above was meant to address these cases. > WRT getting stuck on one message, we could record message UIDs and mark > them as "fetch attempted before", perhaps with a counter. We'd set this > state if we ever send a TOP or RETR for a new message and keep this state > for the next poll cycle. This would be less than "seen". On the next poll > cycle, we'll fetch new messages before those marked "fetch attempted > before". This would allow new mail to be fetched even if we get stuck on > particular messages through server, fetchmail, or MTA/MDA bugs. If we add > a counter, we can mark a message "broken" if the counter exceeds a > threshold and give up on it without deleting it, and request manual > intervention from the postmaster (in multidrop) or addressee (in > singledrop). The 'errcount' field mentioned above was meant for this purpose. -- Sunil Shetye. |