1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 /*
7 nsImapBodyShell and associated classes
8 */
9 
10 #ifndef IMAPBODY_H
11 #define IMAPBODY_H
12 
13 #include "mozilla/Attributes.h"
14 #include "nsImapCore.h"
15 #include "nsString.h"
16 #include "nsRefPtrHashtable.h"
17 #include "nsTArray.h"
18 
19 class nsImapProtocol;
20 
21 typedef enum _nsIMAPBodypartType {
22   IMAP_BODY_MESSAGE_RFC822,
23   IMAP_BODY_MESSAGE_HEADER,
24   IMAP_BODY_LEAF,
25   IMAP_BODY_MULTIPART
26 } nsIMAPBodypartType;
27 
28 class nsImapBodyShell;
29 class nsIMAPBodypartMessage;
30 
31 class nsIMAPBodypart {
32  public:
33   // Construction
GetIsValid()34   virtual bool GetIsValid() { return m_isValid; }
35   virtual void SetIsValid(bool valid);
36   virtual nsIMAPBodypartType GetType() = 0;
37 
38   // Generation
39   // Generates an HTML representation of this part.  Returns content length
40   // generated, -1 if failed.
Generate(nsImapBodyShell * aShell,bool,bool)41   virtual int32_t Generate(nsImapBodyShell* aShell, bool /*stream*/,
42                            bool /* prefetch */) {
43     return -1;
44   }
45   virtual void AdoptPartDataBuffer(
46       char* buf);  // Adopts storage for part data buffer.  If NULL, sets
47                    // isValid to false.
48   virtual void AdoptHeaderDataBuffer(
49       char* buf);  // Adopts storage for header data buffer.  If NULL, sets
50                    // isValid to false.
ShouldFetchInline(nsImapBodyShell * aShell)51   virtual bool ShouldFetchInline(nsImapBodyShell* aShell) {
52     return true;
53   }  // returns true if this part should be fetched inline for generation.
PreflightCheckAllInline(nsImapBodyShell * aShell)54   virtual bool PreflightCheckAllInline(nsImapBodyShell* aShell) { return true; }
55 
56   virtual bool ShouldExplicitlyFetchInline();
57   virtual bool ShouldExplicitlyNotFetchInline();
IsLastTextPart(const char * partNumberString)58   virtual bool IsLastTextPart(const char* partNumberString) { return true; }
59 
60  protected:
61   // If stream is false, simply returns the content length that will be
62   // generated the body of the part itself
63   virtual int32_t GeneratePart(nsImapBodyShell* aShell, bool stream,
64                                bool prefetch);
65   // the MIME headers of the part
66   virtual int32_t GenerateMIMEHeader(nsImapBodyShell* aShell, bool stream,
67                                      bool prefetch);
68   // Generates the MIME boundary wrapper for this part.
69   virtual int32_t GenerateBoundary(nsImapBodyShell* aShell, bool stream,
70                                    bool prefetch, bool lastBoundary);
71   // lastBoundary indicates whether or not this should be the boundary for the
72   // final MIME part of the multipart message.
73   // Generates (possibly empty) filling for a part that won't be filled in
74   // inline.
75   virtual int32_t GenerateEmptyFilling(nsImapBodyShell* aShell, bool stream,
76                                        bool prefetch);
77 
78   // Part Numbers / Hierarchy
79  public:
GetPartNumberString()80   virtual char* GetPartNumberString() { return m_partNumberString; }
81   virtual nsIMAPBodypart* FindPartWithNumber(
82       const char* partNum);  // Returns the part object with the given number
GetParentPart()83   virtual nsIMAPBodypart* GetParentPart() {
84     return m_parentPart;
85   }  // Returns the parent of this part.
86      // We will define a part of type message/rfc822 to be the
87      // parent of its body and header.
88      // A multipart is a parent of its child parts.
89      // All other leafs do not have children.
90 
91   // Other / Helpers
92  public:
93   virtual ~nsIMAPBodypart();
GetnsIMAPBodypartMessage()94   virtual nsIMAPBodypartMessage* GetnsIMAPBodypartMessage() { return NULL; }
95 
GetBodyType()96   const char* GetBodyType() { return m_bodyType; }
GetBodySubType()97   const char* GetBodySubType() { return m_bodySubType; }
SetBoundaryData(char * boundaryData)98   void SetBoundaryData(char* boundaryData) { m_boundaryData = boundaryData; }
99 
100  protected:
101   virtual void QueuePrefetchMIMEHeader(nsImapBodyShell* aShell);
102   // virtual void  PrefetchMIMEHeader();  // Initiates a prefetch for the MIME
103   // header of this part.
104   nsIMAPBodypart(char* partNumber, nsIMAPBodypart* parentPart);
105 
106  protected:
107   bool m_isValid;            // If this part is valid.
108   char* m_partNumberString;  // string representation of this part's
109                              // full-hierarchy number.  Define 0 to be the
110                              // top-level message
111   char* m_partData;          // data for this part.  NULL if not filled in yet.
112   char* m_headerData;  // data for this part's MIME header.  NULL if not filled
113                        // in yet.
114   char* m_boundaryData;  // MIME boundary for this part
115   int32_t m_partLength;
116   int32_t m_contentLength;  // Total content length which will be Generate()'d.
117                             // -1 if not filled in yet.
118   nsIMAPBodypart* m_parentPart;  // Parent of this part
119 
120   // Fields - Filled in from parsed BODYSTRUCTURE response (as well as others)
121   char* m_contentType;  // constructed from m_bodyType and m_bodySubType
122   char* m_bodyType;
123   char* m_bodySubType;
124   char* m_bodyID;
125   char* m_bodyDescription;
126   char* m_bodyEncoding;
127   // we ignore extension data for now
128 };
129 
130 // Message headers
131 // A special type of nsIMAPBodypart
132 // These may be headers for the top-level message,
133 // or any body part of type message/rfc822.
134 class nsIMAPMessageHeaders : public nsIMAPBodypart {
135  public:
136   nsIMAPMessageHeaders(char* partNum, nsIMAPBodypart* parentPart);
137   virtual nsIMAPBodypartType GetType() override;
138   // Generates an HTML representation of this part.  Returns content length
139   // generated, -1 if failed.
140   virtual int32_t Generate(nsImapBodyShell* aShell, bool stream,
141                            bool prefetch) override;
142   virtual bool ShouldFetchInline(nsImapBodyShell* aShell) override;
143   virtual void QueuePrefetchMessageHeaders(nsImapBodyShell* aShell);
144 };
145 
146 class nsIMAPBodypartMultipart : public nsIMAPBodypart {
147  public:
148   nsIMAPBodypartMultipart(char* partNum, nsIMAPBodypart* parentPart);
149   virtual nsIMAPBodypartType GetType() override;
150   virtual ~nsIMAPBodypartMultipart();
151   virtual bool ShouldFetchInline(nsImapBodyShell* aShell) override;
152   virtual bool PreflightCheckAllInline(nsImapBodyShell* aShell) override;
153   // Generates an HTML representation of this part.  Returns content length
154   // generated, -1 if failed.
155   virtual int32_t Generate(nsImapBodyShell* aShell, bool stream,
156                            bool prefetch) override;
157   // Returns the part object with the given number
158   virtual nsIMAPBodypart* FindPartWithNumber(const char* partNum) override;
159   virtual bool IsLastTextPart(const char* partNumberString) override;
AppendPart(nsIMAPBodypart * part)160   void AppendPart(nsIMAPBodypart* part) { m_partList->AppendElement(part); }
161   void SetBodySubType(char* bodySubType);
162 
163  protected:
164   nsTArray<nsIMAPBodypart*>*
165       m_partList;  // An ordered list of top-level body parts for this shell
166 };
167 
168 // The name "leaf" is somewhat misleading, since a part of type message/rfc822
169 // is technically a leaf, even though it can contain other parts within it.
170 class nsIMAPBodypartLeaf : public nsIMAPBodypart {
171  public:
172   nsIMAPBodypartLeaf(char* partNum, nsIMAPBodypart* parentPart, char* bodyType,
173                      char* bodySubType, char* bodyID, char* bodyDescription,
174                      char* bodyEncoding, int32_t partLength,
175                      bool preferPlainText);
176   virtual nsIMAPBodypartType GetType() override;
177   // Generates an HTML representation of this part.  Returns content length
178   // generated, -1 if failed.
179   virtual int32_t Generate(nsImapBodyShell* aShell, bool stream,
180                            bool prefetch) override;
181   // returns true if this part should be fetched inline for generation.
182   virtual bool ShouldFetchInline(nsImapBodyShell* aShell) override;
183   virtual bool PreflightCheckAllInline(nsImapBodyShell* aShell) override;
184 
185  private:
186   bool mPreferPlainText;
187 };
188 
189 class nsIMAPBodypartMessage : public nsIMAPBodypartLeaf {
190  public:
191   nsIMAPBodypartMessage(char* partNum, nsIMAPBodypart* parentPart,
192                         bool topLevelMessage, char* bodyType, char* bodySubType,
193                         char* bodyID, char* bodyDescription, char* bodyEncoding,
194                         int32_t partLength, bool preferPlainText);
195   void SetBody(nsIMAPBodypart* body);
196   virtual nsIMAPBodypartType GetType() override;
197   virtual ~nsIMAPBodypartMessage();
198   virtual int32_t Generate(nsImapBodyShell* aShell, bool stream,
199                            bool prefetch) override;
200   virtual bool ShouldFetchInline(nsImapBodyShell* aShell) override;
201   virtual bool PreflightCheckAllInline(nsImapBodyShell* aShell) override;
202   // Returns the part object with the given number
203   virtual nsIMAPBodypart* FindPartWithNumber(const char* partNum) override;
204   void AdoptMessageHeaders(
205       char* headers);  // Fills in buffer (and adopts storage) for header object
206                        // partNum specifies the message part number to which the
207                        // headers correspond.  NULL indicates the top-level
208                        // message
GetnsIMAPBodypartMessage()209   virtual nsIMAPBodypartMessage* GetnsIMAPBodypartMessage() override {
210     return this;
211   }
GetIsTopLevelMessage()212   virtual bool GetIsTopLevelMessage() { return m_topLevelMessage; }
213 
214  protected:
215   nsIMAPMessageHeaders* m_headers;  // Every body shell should have headers
216   nsIMAPBodypart* m_body;
217   bool m_topLevelMessage;  // Whether or not this is the top-level message
218 };
219 
220 // MessagePartID and an array of them are used for pipelining prefetches.
221 
222 class nsIMAPMessagePartID {
223  public:
224   nsIMAPMessagePartID(nsIMAPeFetchFields fields, const char* partNumberString);
GetFields()225   nsIMAPeFetchFields GetFields() { return m_fields; }
GetPartNumberString()226   const char* GetPartNumberString() { return m_partNumberString; }
227 
228  protected:
229   const char* m_partNumberString;
230   nsIMAPeFetchFields m_fields;
231 };
232 
233 // We will refer to a Body "Shell" as a hierarchical object representation of a
234 // parsed BODYSTRUCTURE response.  A shell contains representations of Shell
235 // "Parts."  A Body Shell can undergo essentially two operations: Construction
236 // and Generation. Shell Construction occurs from a parsed a BODYSTRUCTURE
237 // response, split into empty parts. Shell Generation generates a "MIME Shell"
238 // of the message and streams it to libmime for display.  The MIME Shell has
239 // selected (inline) parts filled in, and leaves all others for on-demand
240 // retrieval through explicit part fetches.
241 
242 class nsImapBodyShell : public nsISupports {
243  public:
244   NS_DECL_THREADSAFE_ISUPPORTS
245   nsImapBodyShell(nsImapProtocol* protocolConnection,
246                   nsIMAPBodypartMessage* message, uint32_t UID,
247                   uint32_t UIDValidity, const char* folderName);
248   // To be used after a shell is uncached
SetConnection(nsImapProtocol * con)249   void SetConnection(nsImapProtocol* con) { m_protocolConnection = con; }
GetIsValid()250   virtual bool GetIsValid() { return m_isValid; }
251   virtual void SetIsValid(bool valid);
252 
253   // Prefetch
254   // Adds a message body part to the queue to be prefetched
255   // in a single, pipelined command
256   void AddPrefetchToQueue(nsIMAPeFetchFields, const char* partNum);
257   // Runs a single pipelined command which fetches all of the
258   // elements in the prefetch queue
259   void FlushPrefetchQueue();
260   // Fills in buffer (and adopts storage) for header object
261   // partNum specifies the message part number to which the
262   // headers correspond.  NULL indicates the top-level message
263   void AdoptMessageHeaders(char* headers, const char* partNum);
264   // Fills in buffer (and adopts storage) for MIME headers in appropriate
265   // object. If object can't be found, sets isValid to false.
266   void AdoptMimeHeader(const char* partNum, char* mimeHeader);
267 
268   // Generation
269   // Streams out an HTML representation of this IMAP message, going along and
270   // fetching parts it thinks it needs, and leaving empty shells for the parts
271   // it doesn't.
272   // Returns number of bytes generated, or -1 if invalid.
273   // If partNum is not NULL, then this works to generates a MIME part that
274   // hasn't been downloaded yet and leaves out all other parts.  By default, to
275   // generate a normal message, partNum should be NULL.
276   virtual int32_t Generate(char* partNum);
277 
278   // Returns TRUE if the user has the pref "Show Attachments Inline" set.
279   // Returns FALSE if the setting is "Show Attachments as Links"
280   virtual bool GetShowAttachmentsInline();
281   // Returns true if all parts are inline, false otherwise. Does not generate
282   // anything.
283   bool PreflightCheckAllInline();
284 
285   // Helpers
GetConnection()286   nsImapProtocol* GetConnection() { return m_protocolConnection; }
287   bool GetPseudoInterrupted();
288   bool DeathSignalReceived();
GetUID()289   nsCString& GetUID() { return m_UID; }
GetUID_validity()290   nsCString& GetUID_validity() { return m_UID_validity; }
GetFolderName()291   const char* GetFolderName() { return m_folderName; }
GetGeneratingPart()292   char* GetGeneratingPart() { return m_generatingPart; }
293   // Returns true if this is in the process of being generated,
294   // so we don't re-enter
IsBeingGenerated()295   bool IsBeingGenerated() { return m_isBeingGenerated; }
IsShellCached()296   bool IsShellCached() { return m_cached; }
SetIsCached(bool isCached)297   void SetIsCached(bool isCached) { m_cached = isCached; }
GetGeneratingWholeMessage()298   bool GetGeneratingWholeMessage() { return m_generatingWholeMessage; }
GetContentModified()299   IMAP_ContentModifiedType GetContentModified() { return m_contentModified; }
SetContentModified(IMAP_ContentModifiedType modType)300   void SetContentModified(IMAP_ContentModifiedType modType) {
301     m_contentModified = modType;
302   }
303 
304  protected:
305   virtual ~nsImapBodyShell();
306 
307   nsIMAPBodypartMessage* m_message;
308 
309   nsTArray<nsIMAPMessagePartID>
310       m_prefetchQueue;  // Array of pipelined part prefetches.
311 
312   bool m_isValid;
313   nsImapProtocol* m_protocolConnection;  // Connection, for filling in parts
314   nsCString m_UID;                       // UID of this message
315   nsCString m_UID_validity;  // appended UID and UID-validity of this message
316   char* m_folderName;        // folder that contains this message
317   char* m_generatingPart;  // If a specific part is being generated, this is it.
318                            // Otherwise, NULL.
319   bool m_isBeingGenerated;   // true if this body shell is in the process of
320                              // being generated
321   bool m_gotAttachmentPref;  // Whether or not m_showAttachmentsInline has been
322                              // initialized
323   bool m_showAttachmentsInline;   // Whether or not we should display attachment
324                                   // inline
325   bool m_cached;                  // Whether or not this shell is cached
326   bool m_generatingWholeMessage;  // whether or not we are generating the whole
327                                   // (non-MPOD) message Set to false if we are
328                                   // generating by parts
329   // under what conditions the content has been modified.
330   // Either IMAP_CONTENT_MODIFIED_VIEW_INLINE or
331   // IMAP_CONTENT_MODIFIED_VIEW_AS_LINKS
332   IMAP_ContentModifiedType m_contentModified;
333 };
334 
335 // This class caches shells, so we don't have to always go and re-fetch them.
336 // This does not cache any of the filled-in inline parts;  those are cached
337 // individually in the libnet memory cache.  (ugh, how will we do that?) Since
338 // we'll only be retrieving shells for messages over a given size, and since the
339 // shells themselves won't be very large, this cache will not grow very big
340 // (relatively) and should handle most common usage scenarios.
341 
342 // A body cache is associated with a given host, spanning folders, so
343 // it uses both UID and UIDVALIDITY .
344 
345 class nsImapBodyShellCache {
346  public:
347   static nsImapBodyShellCache* Create();
348   virtual ~nsImapBodyShellCache();
349 
350   // Adds shell to cache, possibly ejecting
351   // another entry based on scheme in EjectEntry().
352   bool AddShellToCache(nsImapBodyShell* shell);
353   // Looks up a shell in the cache given the message's UID.
354   nsImapBodyShell* FindShellForUID(nsCString& UID, const char* mailboxName,
355                                    IMAP_ContentModifiedType modType);
356   void Clear();
357 
358  protected:
359   nsImapBodyShellCache();
360   // Chooses an entry to eject;  deletes that entry;  and ejects it from the
361   // cache, clearing up a new space.  Returns true if it found an entry
362   // to eject, false otherwise.
363   bool EjectEntry();
GetSize()364   uint32_t GetSize() { return m_shellList->Length(); }
GetMaxSize()365   uint32_t GetMaxSize() { return 20; }
366   nsTArray<nsImapBodyShell*>* m_shellList;  // For maintenance
367   // For quick lookup based on UID
368   nsRefPtrHashtable<nsCStringHashKey, nsImapBodyShell> m_shellHash;
369 };
370 
371 #endif  // IMAPBODY_H
372