1 /* Copyright (C) 2006 - 2014 Jan Kundrát <jkt@flaska.net>
2 
3    This file is part of the Trojita Qt IMAP e-mail client,
4    http://trojita.flaska.net/
5 
6    This program is free software; you can redistribute it and/or
7    modify it under the terms of the GNU General Public License as
8    published by the Free Software Foundation; either version 2 of
9    the License or (at your option) version 3 or any later version
10    accepted by the membership of KDE e.V. (or its successor approved
11    by the membership of KDE e.V.), which shall act as a proxy
12    defined in Section 14 of version 3 of the license.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22 
23 #ifndef IMAP_MAILBOXTREE_H
24 #define IMAP_MAILBOXTREE_H
25 
26 #include <memory>
27 #include <QList>
28 #include <QModelIndex>
29 #include <QPointer>
30 #include <QString>
31 #include "../Parser/Response.h"
32 #include "../Parser/Message.h"
33 #include "MailboxMetadata.h"
34 
35 namespace Imap
36 {
37 
38 namespace Mailbox
39 {
40 
41 class Model;
42 class MailboxModel;
43 class KeepMailboxOpenTask;
44 class ListChildMailboxesTask;
45 
46 class TreeItem
47 {
48     friend class Model; // for m_loading and m_fetched
49     TreeItem(const TreeItem &); // don't implement
50     void operator=(const TreeItem &);  // don't implement
51     friend class DeleteMailboxTask; // for direct access to m_children
52     friend class ObtainSynchronizedMailboxTask;
53     friend class KeepMailboxOpenTask; // for direct access to m_children
54     friend class ListChildMailboxesTask; // setStatus() in case of failure
55     friend class MsgListModel; // for direct access to m_children
56     friend class ThreadingMsgListModel; // for direct access to m_children
57     friend class UpdateFlagsOfAllMessagesTask; // for direct access to m_children
58 
59 protected:
60     /** @short Availability of an item */
61     enum FetchingState {
62         NONE, /**< @short No attempt to download an item has been made yet */
63         UNAVAILABLE, /**< @short Item isn't cached and remote requests are disabled */
64         LOADING, /**< @short Download of an item is already scheduled */
65         DONE /**< @short Item is available right now */
66     };
67 
68 public:
69     typedef enum {
70         /** @short Full body of an e-mail stored on the IMAP server
71 
72           This one really makes sense on a TreeItemMessage and TreeItemPart, and
73           are used
74         */
75         /** @short The HEADER fetch modifier for the current item */
76         OFFSET_HEADER=1,
77         /** @short The TEXT fetch modifier for the current item */
78         OFFSET_TEXT=2,
79         /** @short The MIME fetch modifier for individual message parts
80 
81           In constrast to OFFSET_HEADER and OFFSET_TEXT, this one applies
82           only to TreeItemPart, simply because using the MIME modifier on
83           a top-level message is not allowed as per RFC 3501.
84         */
85         OFFSET_MIME=3,
86         /** @short Obtain the raw data without any kind of Content-Transfer-Encoding decoding */
87         OFFSET_RAW_CONTENTS = 4
88     } PartModifier;
89 
90 protected:
91     static const intptr_t TagMask = 0x3;
92     static const intptr_t PointerMask = ~TagMask;
93     union {
94         TreeItem *m_parent;
95         intptr_t m_parentAsBits;
96     };
97     TreeItemChildrenList m_children;
98 
accessFetchStatus()99     FetchingState accessFetchStatus() const
100     {
101         return static_cast<FetchingState>(m_parentAsBits & TagMask);
102     }
setFetchStatus(const FetchingState fetchStatus)103     void setFetchStatus(const FetchingState fetchStatus)
104     {
105         m_parentAsBits = reinterpret_cast<intptr_t>(parent()) | fetchStatus;
106     }
107 public:
108     explicit TreeItem(TreeItem *parent);
parent()109     TreeItem *parent() const
110     {
111         return reinterpret_cast<TreeItem *>(m_parentAsBits & PointerMask);
112     }
113     virtual int row() const;
114 
115     virtual ~TreeItem();
116     virtual unsigned int childrenCount(Model *const model);
117     virtual TreeItem *child(const int offset, Model *const model);
118     virtual TreeItemChildrenList setChildren(const TreeItemChildrenList &items);
119     virtual void fetch(Model *const model) = 0;
120     virtual unsigned int rowCount(Model *const model) = 0;
121     virtual unsigned int columnCount();
122     virtual QVariant data(Model *const model, int role) = 0;
123     virtual bool hasChildren(Model *const model) = 0;
fetched()124     virtual bool fetched() const { return accessFetchStatus() == DONE; }
loading()125     virtual bool loading() const { return accessFetchStatus() == LOADING; }
126     virtual bool isUnavailable() const;
127     virtual TreeItem *specialColumnPtr(int row, int column) const;
128     virtual QModelIndex toIndex(Model *const model) const;
129 };
130 
131 class TreeItemPart;
132 class TreeItemMessage;
133 
134 class TreeItemMailbox: public TreeItem
135 {
136     void operator=(const TreeItem &);  // don't implement
137     MailboxMetadata m_metadata;
138     friend class Model; // needs access to maintianingTask
139     friend class MailboxModel;
140     friend class DeleteMailboxTask; // for direct access to maintainingTask
141     friend class KeepMailboxOpenTask; // needs access to maintainingTask
142     friend class SubscribeUnsubscribeTask; // needs access to m_metadata.flags
143     static QLatin1String flagNoInferiors;
144     static QLatin1String flagHasNoChildren;
145     static QLatin1String flagHasChildren;
146 public:
147     explicit TreeItemMailbox(TreeItem *parent);
148     TreeItemMailbox(TreeItem *parent, Responses::List);
149     ~TreeItemMailbox();
150 
151     static TreeItemMailbox *fromMetadata(TreeItem *parent, const MailboxMetadata &metadata);
152 
153     virtual TreeItemChildrenList setChildren(const TreeItemChildrenList &items);
154     virtual void fetch(Model *const model);
155     virtual void fetchWithCacheControl(Model *const model, bool forceReload);
156     virtual unsigned int rowCount(Model *const model);
157     virtual QVariant data(Model *const model, int role);
158     virtual bool hasChildren(Model *const model);
159     virtual TreeItem *child(const int offset, Model *const model);
160 
161     SyncState syncState;
162 
163     /** @short Returns true if this mailbox has child mailboxes
164 
165     This function might access the network if the answer can't be decided, for example on basis of mailbox flags.
166     */
167     bool hasChildMailboxes(Model *const model);
168     /** @short Return true if the mailbox is already known to not have any child mailboxes
169 
170     No network activity will be caused. If the answer is not known for sure, we return false (meaning "don't know").
171     */
172     bool hasNoChildMailboxesAlreadyKnown();
173 
mailbox()174     QString mailbox() const { return m_metadata.mailbox; }
separator()175     QString separator() const { return m_metadata.separator; }
mailboxMetadata()176     const MailboxMetadata &mailboxMetadata() const { return m_metadata; }
177     /** @short Update internal tree with the results of a FETCH response
178 
179       If \a changedPart is not null, it will be updated to point to the message
180       part whose content got fetched.
181     */
182     void handleFetchResponse(Model *const model,
183                              const Responses::Fetch &response,
184                              QList<TreeItemPart *> &changedParts,
185                              TreeItemMessage *&changedMessage,
186                              bool usingQresync);
187     void rescanForChildMailboxes(Model *const model);
188     void handleExpunge(Model *const model, const Responses::NumberResponse &resp);
189     void handleExists(Model *const model, const Responses::NumberResponse &resp);
190     void handleVanished(Model *const model, const Responses::Vanished &resp);
191     bool isSelectable() const;
192 
193     void saveSyncStateAndUids(Model *model);
194 
195 private:
196     TreeItemPart *partIdToPtr(Model *model, TreeItemMessage *message, const QByteArray &msgId);
197 
198     /** @short ImapTask which is currently responsible for well-being of this mailbox */
199     QPointer<KeepMailboxOpenTask> maintainingTask;
200 };
201 
202 class TreeItemMsgList: public TreeItem
203 {
204     void operator=(const TreeItem &);  // don't implement
205     friend class TreeItemMailbox;
206     friend class TreeItemMessage; // for maintaining the m_unreadMessageCount
207     friend class Model;
208     friend class ObtainSynchronizedMailboxTask;
209     friend class KeepMailboxOpenTask;
210     FetchingState m_numberFetchingStatus;
211     int m_totalMessageCount;
212     int m_unreadMessageCount;
213     int m_recentMessageCount;
214 public:
215     explicit TreeItemMsgList(TreeItem *parent);
216 
217     virtual void fetch(Model *const model);
218     virtual unsigned int rowCount(Model *const model);
219     virtual QVariant data(Model *const model, int role);
220     virtual bool hasChildren(Model *const model);
221 
222     int totalMessageCount(Model *const model);
223     int unreadMessageCount(Model *const model);
224     int recentMessageCount(Model *const model);
225     void fetchNumbers(Model *const model);
226     void recalcVariousMessageCounts(Model *model);
227     void recalcVariousMessageCountsOnExpunge(Model *model, TreeItemMessage *expungedMessage);
228     void resetWasUnreadState();
229     bool numbersFetched() const;
230 };
231 
232 class MessageDataPayload
233 {
234 public:
235     MessageDataPayload();
236 
237     const Message::Envelope &envelope() const;
238     void setEnvelope(const Message::Envelope &envelope);
239     const QDateTime &internalDate() const;
240     void setInternalDate(const QDateTime &internalDate);
241     quint64 size() const;
242     void setSize(const quint64 size);
243     const QList<QByteArray> &hdrReferences() const;
244     void setHdrReferences(const QList<QByteArray> &hdrReferences);
245     const QList<QUrl> &hdrListPost() const;
246     void setHdrListPost(const QList<QUrl> &hdrListPost);
247     bool hdrListPostNo() const;
248     void setHdrListPostNo(const bool hdrListPostNo);
249     const QByteArray &rememberedBodyStructure() const;
250     void setRememberedBodyStructure(const QByteArray &blob);
251 
252     TreeItemPart *partHeader() const;
253     void setPartHeader(std::unique_ptr<TreeItemPart> part);
254     TreeItemPart *partText() const;
255     void setPartText(std::unique_ptr<TreeItemPart> part);
256 
257     bool isComplete() const;
258 
259     bool gotEnvelope() const;
260     bool gotInternalDate() const;
261     bool gotSize() const;
262     bool gotHdrReferences() const;
263     bool gotHdrListPost() const;
264     bool gotRemeberedBodyStructure() const;
265 
266 private:
267     Message::Envelope m_envelope;
268     QDateTime m_internalDate;
269     quint64 m_size;
270     QList<QByteArray> m_hdrReferences;
271     QList<QUrl> m_hdrListPost;
272     QByteArray m_rememberedBodyStructure;
273     bool m_hdrListPostNo;
274     std::unique_ptr<TreeItemPart> m_partHeader;
275     std::unique_ptr<TreeItemPart> m_partText;
276 
277     bool m_gotEnvelope : 1;
278     bool m_gotInternalDate : 1;
279     bool m_gotSize : 1;
280     bool m_gotBodystructure : 1;
281     bool m_gotHdrReferences : 1;
282     bool m_gotHdrListPost : 1;
283 };
284 
285 class TreeItemMessage: public TreeItem
286 {
287     void operator=(const TreeItem &);  // don't implement
288     friend class TreeItemMailbox;
289     friend class TreeItemMsgList;
290     friend class Model;
291     friend class ObtainSynchronizedMailboxTask; // needs access to m_offset
292     friend class KeepMailboxOpenTask; // needs access to m_offset
293     friend class UpdateFlagsTask; // needs access to m_flags
294     friend class UpdateFlagsOfAllMessagesTask; // needs access to m_flags
295     int m_offset;
296     uint m_uid;
297     mutable MessageDataPayload *m_data;
298     QStringList m_flags;
299     bool m_flagsHandled;
300     bool m_wasUnread;
301     /** @short Set FLAGS and maintain the unread message counter */
302     void setFlags(TreeItemMsgList *list, const QStringList &flags);
303     void processAdditionalHeaders(Model *model, const QByteArray &rawHeaders);
304     static bool hasNestedAttachments(Model *const model, TreeItemPart *part);
305 
data()306     MessageDataPayload *data() const
307     {
308         return m_data ? m_data : (m_data = new MessageDataPayload());
309     }
310 
311 public:
312     explicit TreeItemMessage(TreeItem *parent);
313     ~TreeItemMessage();
314 
315     virtual int row() const;
316     virtual void fetch(Model *const model);
317     virtual unsigned int rowCount(Model *const model);
318     virtual unsigned int columnCount();
319     virtual QVariant data(Model *const model, int role);
hasChildren(Model * const model)320     virtual bool hasChildren(Model *const model) { Q_UNUSED(model); return true; }
321     virtual TreeItemChildrenList setChildren(const TreeItemChildrenList &items);
322     Message::Envelope envelope(Model *const model);
323     QDateTime internalDate(Model *const model);
324     quint64 size(Model *const model);
325     bool isMarkedAsDeleted() const;
326     bool isMarkedAsRead() const;
327     bool isMarkedAsReplied() const;
328     bool isMarkedAsForwarded() const;
329     bool isMarkedAsRecent() const;
330     bool isMarkedAsFlagged() const;
331     bool isMarkedAsJunk() const;
332     bool isMarkedAsNotJunk() const;
333     void checkFlagsReadRecent(bool &isRead, bool &isRecent) const;
334     uint uid() const;
335     virtual TreeItem *specialColumnPtr(int row, int column) const;
336     bool hasAttachments(Model *const model);
337 
338     static QVariantList addresListToQVariant(const QList<Imap::Message::MailAddress> &addressList);
339 };
340 
341 class TreeItemPart: public TreeItem
342 {
343     void operator=(const TreeItem &);  // don't implement
344     friend class TreeItemMailbox; // needs access to m_data
345     friend class Model; // dtto
346     QByteArray m_mimeType;
347     QByteArray m_charset;
348     QByteArray m_contentFormat;
349     QByteArray m_delSp;
350     QByteArray m_encoding;
351     QByteArray m_data;
352     QByteArray m_bodyFldId;
353     QByteArray m_bodyDisposition;
354     QString m_fileName;
355     quint64 m_octets;
356     QByteArray m_multipartRelatedStartPart;
357     Imap::Message::AbstractMessage::bodyFldParam_t m_bodyFldParam;
358     mutable TreeItemPart *m_partMime;
359     mutable TreeItemPart *m_partRaw;
360 public:
361     TreeItemPart(TreeItem *parent, const QByteArray &mimeType);
362     ~TreeItemPart();
363 
364     virtual unsigned int childrenCount(Model *const model);
365     virtual TreeItem *child(const int offset, Model *const model);
366     virtual TreeItemChildrenList setChildren(const TreeItemChildrenList &items);
367 
368     virtual void fetchFromCache(Model *const model);
369     virtual void fetch(Model *const model);
370     virtual unsigned int rowCount(Model *const model);
371     virtual unsigned int columnCount();
372     virtual QVariant data(Model *const model, int role);
373     virtual bool hasChildren(Model *const model);
374 
375     virtual QByteArray partId() const;
376 
377     /** @short Shall we use RFC3516 BINARY for fetching message parts or not */
378     typedef enum {
379         /** @short Use the baseline IMAP feature, the BODY[...], from RFC 3501 */
380         FETCH_PART_IMAP,
381         /** @short Fetch via the RFC3516's BINARY extension */
382         FETCH_PART_BINARY
383     } PartFetchingMode;
384 
385     virtual QByteArray partIdForFetch(const PartFetchingMode fetchingMode) const;
386     virtual QByteArray pathToPart() const;
387     TreeItemMessage *message() const;
388 
389     /** @short Provide access to the internal buffer holding data
390 
391         It is safe to access the obtained pointer as long as this object is not
392         deleted. This function violates the classic concept of object
393         encapsulation, but is really useful for the implementation of
394         Imap::Network::MsgPartNetworkReply.
395      */
396     QByteArray *dataPtr();
mimeType()397     QByteArray mimeType() const { return m_mimeType; }
charset()398     QByteArray charset() const { return m_charset; }
setCharset(const QByteArray & ch)399     void setCharset(const QByteArray &ch) { m_charset = ch; }
setContentFormat(const QByteArray & format)400     void setContentFormat(const QByteArray &format) { m_contentFormat = format; }
setContentDelSp(const QByteArray & delSp)401     void setContentDelSp(const QByteArray &delSp) { m_delSp = delSp; }
setEncoding(const QByteArray & encoding)402     void setEncoding(const QByteArray &encoding) { m_encoding = encoding; }
encoding()403     QByteArray encoding() const { return m_encoding; }
setBodyFldId(const QByteArray & id)404     void setBodyFldId(const QByteArray &id) { m_bodyFldId = id; }
bodyFldId()405     QByteArray bodyFldId() const { return m_bodyFldId; }
setBodyDisposition(const QByteArray & disposition)406     void setBodyDisposition(const QByteArray &disposition) { m_bodyDisposition = disposition; }
bodyDisposition()407     QByteArray bodyDisposition() const { return m_bodyDisposition; }
setFileName(const QString & name)408     void setFileName(const QString &name) { m_fileName = name; }
fileName()409     QString fileName() const { return m_fileName; }
setOctets(const quint64 size)410     void setOctets(const quint64 size) { m_octets = size; }
411     /** @short Return the downloadable size of the message part */
octets()412     quint64 octets() const { return m_octets; }
multipartRelatedStartPart()413     QByteArray multipartRelatedStartPart() const { return m_multipartRelatedStartPart; }
setMultipartRelatedStartPart(const QByteArray & start)414     void setMultipartRelatedStartPart(const QByteArray &start) { m_multipartRelatedStartPart = start; }
setBodyFldParam(const Imap::Message::AbstractMessage::bodyFldParam_t & bodyFldParam)415     void setBodyFldParam(const Imap::Message::AbstractMessage::bodyFldParam_t &bodyFldParam) { m_bodyFldParam = bodyFldParam; }
bodyFldParam()416     Imap::Message::AbstractMessage::bodyFldParam_t bodyFldParam() const { return m_bodyFldParam; }
417     virtual TreeItem *specialColumnPtr(int row, int column) const;
418     virtual bool isTopLevelMultiPart() const;
419 
420     virtual void silentlyReleaseMemoryRecursive();
421 protected:
422     TreeItemPart(TreeItem *parent);
423 };
424 
425 /** @short A message part with a modifier
426 
427 This item hanldes fetching of message parts with an attached modifier (like TEXT, HEADER or MIME).
428 */
429 class TreeItemModifiedPart: public TreeItemPart
430 {
431     PartModifier m_modifier;
432 public:
433     TreeItemModifiedPart(TreeItem *parent, const PartModifier kind);
434     virtual int row() const;
435     virtual unsigned int columnCount();
436     virtual QByteArray partId() const;
437     virtual QByteArray pathToPart() const;
438     virtual TreeItem *specialColumnPtr(int row, int column) const;
439     PartModifier kind() const;
440     virtual QModelIndex toIndex(Model *const model) const;
441     virtual QByteArray partIdForFetch(const PartFetchingMode fetchingMode) const;
442 protected:
443     virtual bool isTopLevelMultiPart() const;
444 private:
445     QByteArray modifierToByteArray() const;
446 };
447 
448 /** @short Specialization of TreeItemPart for parts holding a multipart/message */
449 class TreeItemPartMultipartMessage: public TreeItemPart
450 {
451     Message::Envelope m_envelope;
452     mutable TreeItemPart *m_partHeader;
453     mutable TreeItemPart *m_partText;
454 public:
455     TreeItemPartMultipartMessage(TreeItem *parent, const Message::Envelope &envelope);
456     virtual ~TreeItemPartMultipartMessage();
457     virtual QVariant data(Model * const model, int role);
458     virtual TreeItem *specialColumnPtr(int row, int column) const;
459     virtual void silentlyReleaseMemoryRecursive();
460 };
461 
462 }
463 
464 }
465 
466 Q_DECLARE_METATYPE(QByteArray*)
467 
468 #endif // IMAP_MAILBOXTREE_H
469