1 The trojita IMAP Client
2 -----------------------
7 At the time I started to work with e-mail on a more advanced level than a free
8 e-mail provider could offer, my quest for an advanced and standards-compliant
9 "Mail Client" (MUA) begun. At first, I have no idea about various mail-related
10 protocols, but during the process, I learned the meaning of abbreviations like
11 SMTP, POP and IMAP. As I'm a fan of open solutions, I have immediately put
12 proprietary protocols out of consideration. This left me with essentially just
13 two choices for accessing my mail collection, either POP3 or IMAP4.
15 POP3 is an older protocol. While it certainly has its advantages (it's really
16 simple to implement, for one), it wasn't designed for managing mail from
17 multiple places, and it doesn't really support multiple mailboxes. As I used to
18 read my e-mails from at least four computers, I went on and had a closer look on
19 the only remaining protocol, the IMAP4.
21 Unlike POP, which was designed for retrieval of messages and wasn't designed
22 for use-cases where they remain on the central server, the IMAP is built in a
23 server-centric way. The only place where the messages are kept permanently is
24 the IMAP server. The protocol, defined in RFC 3501, offers a rich suite of
25 commands for both data access and data manipulation. It is no longer needed to
26 download a whole 5MB mail that has three lines of text and a JPEG attachment
27 just because you wanted to see the text. IMAP offers a comprehensive set of
28 commands for determining message structure, partial message retrieval and even
29 server-side message sorting based on message threading.
31 This power of IMAP is, however, often not exploited by today's IMAP clients. In
32 fact, most of them are just dumb POP-clients upgraded to speak the IMAP
33 language. Such clients are more vulnerable to various attacks (one
34 badly-formatted mail is parsed not only by the IMAP server, but then by the MUA
35 again, increasing the risk of buffer overflows and other security-related issue)
36 and are also wasting system resources.
38 The aim of the trojita MUA is to create a nice and efficient IMAP-only mail
39 client that makes use of advanced concepts found in the IMAP protocol (and
40 its numerous extensions) and in modern programming libraries. If I create a
41 tool for working with mail that I find comfortable, then I'll call trojita a
48 Connecting to an IMAP server, working with mailboxes ("folders") and messages
49 contained therein. Being able to display as many data formats as possible and
50 convenient, while staying secure (beware of HTML stuff with remote images etc).
51 Support for various forms of cryptographic contents (X.509 and PGP
52 signatures/encrypted messages) is planned for later releases. Being able to
53 compose mail (again, with the cryptographic stuff) and send it either via a SMTP
54 connection or possibly through a local sendmail-compatible interface. Being
55 able to utilize a not-so-common way of talking to the IMAP server via a UNIX
56 pipe (handy for IMAP-over-SSH for example).
62 Respecting all related RFC standards. Using the IMAP protocol in an efficient
63 way. Making use of convenient and effective programming technologies and modern
64 libraries. Being cross-platform friendly, providing a graphical user interface.
65 Utilizing a unit-testing framework for early regressions detection.
71 Due to the emphasis on modern libraries, portability and the joy of programming,
72 I've chosen the Qt library. When combined with the C++ STL, it provides an
73 extremely rich toolbox that is essential for writing large applications in a
80 On the very lowest layer of trojita, we can find the library that handles the
81 low-level layer of the IMAP protocol. It is used to parse bytes flowing from
82 the IMAP server to slightly higher objects like "atoms", "strings", "numbers"
83 and "lists". It is pretty simple and can be found in the Imap::LowLevelParser
84 namespace (files Imap/LowLevelParser.* with corresponding unit tests in
85 test/test_Imap_LowLevelParser.* ).
87 Building on the top of the Low Level Parser, we find the Parser. It is the
88 point of interaction with the IMAP server. It is being fed with IMAP commands
89 (via calling the appropriate member functions of the Imap::Parser class
90 instance) and emits Qt signals when it receives and parses a response from the
93 (Due to the nature of C++, where working with virtual functions de facto
94 forces the programmer to use pointers or references, this layer makes heavy use
95 of the std::tr1::shared_ptr template as defined in the Technical Report 1. The
96 build system of trojita tries to use system-provided tr1/memory header, but will
97 fall back to the one provided by Boost in case the system compiler doesn't
100 So, now we have an interface that we can feed with IMAP commands and which
101 returns us parsed data back. This is how a typical conversation looks like:
103 me: perform the SELECT operation on mailbox INBOX
104 parser: queued as command y001
105 imap: response: total message count, number: 18
106 imap: response: defined flags, list: (\Answered \Recent \Deleted \Seen \Draft)
107 imap: response: # of recent messages, number: 2
108 imap: untagged reply: OK, response: first unseen message, number: 13
109 imap: untagged reply: OK, response: UIDVALIDITY, number: 1337666
110 imap: command y001 completed successfully, message: bla bla bla
111 me: give me the envelope of the 12th message in this mailbox
114 While this is certainly cool feature, it isn't really useful for humans. We
115 usually prefer to see a nice graphical list of messages that we can click at,
116 shuffle them around and otherwise mess with them, and we want that window with a
117 quoted text to appear when we click the "reply" button. Here's where the next
120 We have a choice to make here -- either we can use a traditional IMAP-like
121 approach and treat mailboxes as a completely separated hierarchy, or we can
122 deal with the IMAP server as a whole. The former is what older development
123 versions (as of end of May 2008) used, the latter is what the Right Thing (TM)
124 is :). In this new approach, there's only one tree-like model representing all
125 mailboxes on the IMAP server, as one user sees it. All views interact with this
126 huge model only, and each of these views might be tied to a subtree of the whole
129 While this design choice has numerous advantages (like simplified sharing of
130 Parser instances, which in turn enables working with servers that don't
131 particularly like dozens of open connections per one user), it has to be
132 carefully thought-out before implementation. In the following, we'll discuss
133 how we employ the whole Interview framework of Qt in trojitá.
135 There's only one top-level item in our view.
137 Children of this top-level item represent mailbox hierarchy, possibly in
138 different namespaces (a namespace is a term from IMAP, see RFC 3501 for
139 details). A mailbox can hold number of other mailboxes (properly nested), or a
140 list of messages, or a combination of thereof.
142 A message itself is hierarchically structured. The structure matches closely
143 the MIME structure of the real email message. In addition, there are some
144 metadata (like IMAP flags or envelope) that are also part of the message
145 representation in the view.
147 This scenario has a problem that needs to be solved -- if a mailbox can contain
148 both messages and other mailboxes, how does one distinguish among them?
150 The purpose of the Imap::Mailbox::Model is to provide a nice high-level
151 overview of an IMAP mailbox. This is implemented using a standard Qt
152 model-view-architecture interface. This means that it is extremely easy to add
153 a graphical view to the application, as the Qt library already provides one.
154 The idea here is that all the views could be extremely stupid (they indeed are),
155 and all the IMAP logic like caching is to be provided by the lower layers.
157 FIXME: describe (and implement :) ) Imap::Mailbox::Cache and
158 (yet-to-be-thought-about) Imap::Mailbox::ParserPool for multiplexing of Parsers
159 among multiple Models.