IDLE again

exarkun at twistedmatrix.com exarkun at twistedmatrix.com
Fri Dec 4 06:32:27 GMT 2009


On 05:45 am, dom.lobue at gmail.com wrote:
>
>Jean-Paul,
>
>I read over the IMAP IDLE RFC and went through the IMAP4 twisted
>library and I've sketched out a rough outline of how to implement
>IDLE. I've run into some things in Twisted however that I don't really
>understand well, and I'm hoping you can point me in the correct
>direction.

Cool.  That was quick. :)  Before I get into things, since this thread 
is likely to go into a lot of Twisted-specific details which may not be 
generally interesting, if there's anyone who'd like off the cc list, 
please speak up. :)
>First, just to verify my understanding: the IMAP4Client class is a
>Protocol class.

Yep.  And to expand on that, instances of Protocol classes typically 
have a one-to-one relationship with a connection.
>All IMAP commands are represented by at least two
>methods in the IMAP4Client class - one for what to do when the command
>is received from the server, and one for when the command is sent to
>the server.

Generally, though there are some exceptions.  For example, AUTHENTICATE 
is implemented with one method that starts by possibly sending a 
CAPABILITY (IMAP4Client.authenticate) command, then another method which 
will actually send AUTHENTICATE (IMAP4Client.__cbAuthenticate), then two 
more methods for dealing with the response to the AUTHENTICATE 
(IMAP4Client.__cbContinueAuth and IMAP4Client.__cbAuthTLS).

Another way to look at it is like this.  For each supported protocol 
action, there is at least one public method on IMAP4Client to initiate 
this action by sending some bytes to the server.  All bytes received by 
IMAP4Client from the server are parsed according to the state the client 
is in, what commands are outstanding, etc.  Depending on the state and 
the bytes, callbacks might be invoked as a result of this, possibly 
delivering the results of a protocol action initiated earlier to the 
calling application code.

This doesn't disagree with what you said too much, it just re-states it 
in slightly more general terms.
>Assuming this assertion to be true, the broad strokes of
>the IDLE implementation is as follows:
>
>-IDLE is engaged: command is sent to server turning on IDLE, schedule
>an IDLE reset in 29 minutes, and an attribute ( _IDLE_Enabled for
>example ) is set to True.

Basically, yes.  One subtle point, though - since the server might 
reject the IDLE command, the client shouldn't assume it has entered the 
IDLE state until it receives a positive acknowledgement of the command 
from the server (eg the "+ idling" line from the RFC).
>-In the command dispatcher code: checks if IDLE is enabled or not. If
>enabled, it appends IDLE to all incoming commands and sends a DONE to
>the server before any new commands are sent. (On the incoming commands
>part: what I mean is if the method originally to be called was
>"incoming_exists", instead it would go to "incoming_existsIDLE".)

This part could probably bear some elaboration.  I think the question 
here is how the unsolicited information should best be made available to 
the application code which caused the IDLE to be issued (if I've 
misunderstood what you were getting at here, let me know).  One 
possibility is the existing IMailboxListener interface - if you look at 
the very end of the implementation of IMAP4Client, you'll find three no- 
op methods, modeChanged, flagsChanged, and newMessages.  These are 
intended for subclasses to override and are already called by 
IMAP4Client when unsolicited information is given by the server in 
response to a command.  It may make sense to direct data provided during 
an IDLE to these callbacks, or others similar to them.
>-When IDLE is confirmed off by server: delete scheduled IDLE reset.

Yep.
>And that's basically it I think. Fancy stuff like downloading the
>messages that IDLE notifies you about are handled in the
>ClientFactory, right?

Perhaps by a factory, or perhaps by something else.  When I've said 
"application code" above, this is what I'm talking about - the code that 
someone else has written which uses IMAP4Client somehow in order to do 
something IMAP4 related.  In our case, offlineimap would be the 
application code. :)  It doesn't make much difference to the IMAP4Client 
implementation who or what is using it, so it could be a ClientFactory 
or another protocol or a GUI or any number of other things.
>Some things that I'm not all that clear on and could use your help to
>understand:
>How do you cancel a previously queued/scheduled callback?

It would probably make sense to be more specific here.  "Callback" might 
mean a lot of things.

Deferreds, the central callback-management API used in Twisted, don't 
directly support cancellation (though we consider adding such support 
from time to time).  Generally APIs which want to offer cancellation do 
it by some other means separate from the Deferred they return.  For 
example, a number of APIs accept a "timeout" parameter which is a form 
of cancellation.  These APIs internally use the timed call features of 
the Twisted reactor to make the operation fail if it does not complete 
within the given time frame, resulting in an "errback" on the Deferred 
(just a callback for errors).

Actually canceling an operation depends on what the operation is and how 
it's implemented.  For example, the IMAP4 protocol itself offers no 
mechanism for canceling a command which the server has already received 
and begun processing, aside from prematurely closing the connection.  So 
if you issue a FETCH, you don't have much of a way to avoid receiving 
the results.

I'm not sure with what aim you bring up cancellation, so I don't think I 
can be any more specific than this now.  Let me know if I didn't 
actually answer your question.
>How are multiple connections to the same server handled? And more
>importantly: how do you have a command in one session use a callback
>on another already-open connection?

This is simpler than it seems.  The answer is mostly what you'd expect 
if you asked it about a non-Twisted-based app.  If you want protocol 
instance A to do something to protocol instance B, you make sure A has a 
reference to B and then you have A call a method on B.  There are lots 
of approaches to making sure that reference is available, but you don't 
really need anything fancier than an attribute somewhere - using the 
factory is a common approach, since  ClientFactory sets itself as the 
"factory" attribute on each protocol instance it creates.

Hope that was helpful,
Jean-Paul




More information about the OfflineIMAP-project mailing list