The IMAP protocol implementation is the most complicated in the mailnews code, though IMAP is by far the most complicated protocol as well.
The IMAP implementation is the only multi-threaded protocol in mailnews. The other protocols use necko to do the actual socket i/o on a separate thread, but everything else, including the parsing of the server responses, happens on the main UI thread. In IMAP, we spin up a new thread for every connection to the server, and read from the input nsPipe and parse the responses on that thread. This allows us to write code as if the sending of commands and the retrieving of responses was synchronous, e.g., this code here where we send the select mailbox command, and parse the server response in the next line of code. In contrast, POP3/NNTP/SMTP all send commands and receive responses using a state machine.
At the time the IMAP code was originally written, it was felt that the state machine approach would just be too complicated, and make the code hard to maintain. The trade-off is that we have to deal with thread safety issues, deadlock, etc. The biggest problem we have right now is that there are times when we try to shutdown connections from the UI thread, and we end up calling methods on the UI thread that should only be called from the IMAP thread.
Generally, almost all the code in nsImapProtocol, and all the parsing code (nsImapServerResponseParser, nsImapGenericParser, and nsImapBodyShell) is run on the imap connection thread. When an imap thread needs to communicate with the UI thread, the nsImapProtocol object uses one of three xpcom proxy sink objects - nsIImapMailFolderSink, nsIImapServerSink, and nsIImapMessageSink. These sinks allow the nsImapProtocol object to make synchronous method calls to the UI thread. nsImapIncomingServer implements the nsIImapServerSink, and nsImapMailFolder implements the nsIImapMailFolderSink and the nsIImapMessageSink.