summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/imap/src/nsImapBodyShell.h
blob: c2ab8c5898e26672703e0371f0cb19031cde1ae9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
nsImapBodyShell and associated classes
*/

#ifndef IMAPBODY_H
#define IMAPBODY_H

#include "mozilla/Attributes.h"
#include "nsImapCore.h"
#include "nsString.h"
#include "nsRefPtrHashtable.h"
#include "nsTArray.h"

class nsImapProtocol;

typedef enum _nsIMAPBodypartType {
  IMAP_BODY_MESSAGE_RFC822,
  IMAP_BODY_MESSAGE_HEADER,
  IMAP_BODY_LEAF,
  IMAP_BODY_MULTIPART
} nsIMAPBodypartType;

class nsImapBodyShell;
class nsIMAPBodypartMessage;

class nsIMAPBodypart {
 public:
  // Construction
  virtual bool GetIsValid() { return m_isValid; }
  virtual void SetIsValid(bool valid);
  virtual nsIMAPBodypartType GetType() = 0;

  // Generation
  // Generates an HTML representation of this part.  Returns content length
  // generated, -1 if failed.
  virtual int32_t Generate(nsImapBodyShell* aShell, nsImapProtocol* conn,
                           bool /*stream*/, bool /* prefetch */) {
    return -1;
  }
  virtual void AdoptPartDataBuffer(
      char* buf);  // Adopts storage for part data buffer.  If NULL, sets
                   // isValid to false.
  virtual void AdoptHeaderDataBuffer(
      char* buf);  // Adopts storage for header data buffer.  If NULL, sets
                   // isValid to false.
  virtual bool ShouldFetchInline(nsImapBodyShell* aShell) {
    return true;
  }  // returns true if this part should be fetched inline for generation.
  virtual bool PreflightCheckAllInline(nsImapBodyShell* aShell) { return true; }

  virtual bool ShouldExplicitlyFetchInline();
  virtual bool ShouldExplicitlyNotFetchInline();
  virtual bool IsLastTextPart(const char* partNumberString) { return true; }

 protected:
  // If stream is false, simply returns the content length that will be
  // generated the body of the part itself
  virtual int32_t GeneratePart(nsImapBodyShell* aShell, nsImapProtocol* conn,
                               bool stream, bool prefetch);
  // the MIME headers of the part
  virtual int32_t GenerateMIMEHeader(nsImapBodyShell* aShell,
                                     nsImapProtocol* conn, bool stream,
                                     bool prefetch);
  // Generates the MIME boundary wrapper for this part.
  virtual int32_t GenerateBoundary(nsImapBodyShell* aShell,
                                   nsImapProtocol* conn, bool stream,
                                   bool prefetch, bool lastBoundary);
  // lastBoundary indicates whether or not this should be the boundary for the
  // final MIME part of the multipart message.
  // Generates (possibly empty) filling for a part that won't be filled in
  // inline.
  virtual int32_t GenerateEmptyFilling(nsImapBodyShell* aShell,
                                       nsImapProtocol* conn, bool stream,
                                       bool prefetch);

  // Part Numbers / Hierarchy
 public:
  virtual char* GetPartNumberString() { return m_partNumberString; }
  virtual nsIMAPBodypart* FindPartWithNumber(
      const char* partNum);  // Returns the part object with the given number
  virtual nsIMAPBodypart* GetParentPart() {
    return m_parentPart;
  }  // Returns the parent of this part.
     // We will define a part of type message/rfc822 to be the
     // parent of its body and header.
     // A multipart is a parent of its child parts.
     // All other leafs do not have children.

  // Other / Helpers
 public:
  virtual ~nsIMAPBodypart();
  virtual nsIMAPBodypartMessage* GetnsIMAPBodypartMessage() { return NULL; }

  const char* GetBodyType() { return m_bodyType; }
  const char* GetBodySubType() { return m_bodySubType; }
  void SetBoundaryData(char* boundaryData) { m_boundaryData = boundaryData; }

 protected:
  virtual void QueuePrefetchMIMEHeader(nsImapBodyShell* aShell);
  // virtual void  PrefetchMIMEHeader();  // Initiates a prefetch for the MIME
  // header of this part.
  nsIMAPBodypart(char* partNumber, nsIMAPBodypart* parentPart);

 protected:
  bool m_isValid;            // If this part is valid.
  char* m_partNumberString;  // string representation of this part's
                             // full-hierarchy number.  Define 0 to be the
                             // top-level message
  char* m_partData;          // data for this part.  NULL if not filled in yet.
  char* m_headerData;  // data for this part's MIME header.  NULL if not filled
                       // in yet.
  char* m_boundaryData;  // MIME boundary for this part
  int32_t m_partLength;
  int32_t m_contentLength;  // Total content length which will be Generate()'d.
                            // -1 if not filled in yet.
  nsIMAPBodypart* m_parentPart;  // Parent of this part

  // Fields - Filled in from parsed BODYSTRUCTURE response (as well as others)
  char* m_contentType;  // constructed from m_bodyType and m_bodySubType
  char* m_bodyType;
  char* m_bodySubType;
  char* m_bodyID;
  char* m_bodyDescription;
  char* m_bodyEncoding;
  // we ignore extension data for now
};

// Message headers
// A special type of nsIMAPBodypart
// These may be headers for the top-level message,
// or any body part of type message/rfc822.
class nsIMAPMessageHeaders : public nsIMAPBodypart {
 public:
  nsIMAPMessageHeaders(char* partNum, nsIMAPBodypart* parentPart);
  virtual nsIMAPBodypartType GetType() override;
  // Generates an HTML representation of this part.  Returns content length
  // generated, -1 if failed.
  virtual int32_t Generate(nsImapBodyShell* aShell, nsImapProtocol* conn,
                           bool stream, bool prefetch) override;
  virtual bool ShouldFetchInline(nsImapBodyShell* aShell) override;
  virtual void QueuePrefetchMessageHeaders(nsImapBodyShell* aShell);
};

class nsIMAPBodypartMultipart : public nsIMAPBodypart {
 public:
  nsIMAPBodypartMultipart(char* partNum, nsIMAPBodypart* parentPart);
  virtual nsIMAPBodypartType GetType() override;
  virtual ~nsIMAPBodypartMultipart();
  virtual bool ShouldFetchInline(nsImapBodyShell* aShell) override;
  virtual bool PreflightCheckAllInline(nsImapBodyShell* aShell) override;
  // Generates an HTML representation of this part.  Returns content length
  // generated, -1 if failed.
  virtual int32_t Generate(nsImapBodyShell* aShell, nsImapProtocol* conn,
                           bool stream, bool prefetch) override;
  // Returns the part object with the given number
  virtual nsIMAPBodypart* FindPartWithNumber(const char* partNum) override;
  virtual bool IsLastTextPart(const char* partNumberString) override;
  void AppendPart(nsIMAPBodypart* part) { m_partList.AppendElement(part); }
  void SetBodySubType(char* bodySubType);

 protected:
  // An ordered list of top-level body parts for this shell
  nsTArray<nsIMAPBodypart*> m_partList;
};

// The name "leaf" is somewhat misleading, since a part of type message/rfc822
// is technically a leaf, even though it can contain other parts within it.
class nsIMAPBodypartLeaf : public nsIMAPBodypart {
 public:
  nsIMAPBodypartLeaf(char* partNum, nsIMAPBodypart* parentPart, char* bodyType,
                     char* bodySubType, char* bodyID, char* bodyDescription,
                     char* bodyEncoding, int32_t partLength,
                     bool preferPlainText);
  virtual nsIMAPBodypartType GetType() override;
  // Generates an HTML representation of this part.  Returns content length
  // generated, -1 if failed.
  virtual int32_t Generate(nsImapBodyShell* aShell, nsImapProtocol* conn,
                           bool stream, bool prefetch) override;
  // returns true if this part should be fetched inline for generation.
  virtual bool ShouldFetchInline(nsImapBodyShell* aShell) override;
  virtual bool PreflightCheckAllInline(nsImapBodyShell* aShell) override;

 private:
  bool mPreferPlainText;
};

class nsIMAPBodypartMessage : public nsIMAPBodypartLeaf {
 public:
  nsIMAPBodypartMessage(char* partNum, nsIMAPBodypart* parentPart,
                        bool topLevelMessage, char* bodyType, char* bodySubType,
                        char* bodyID, char* bodyDescription, char* bodyEncoding,
                        int32_t partLength, bool preferPlainText);
  void SetBody(nsIMAPBodypart* body);
  virtual nsIMAPBodypartType GetType() override;
  virtual ~nsIMAPBodypartMessage();
  virtual int32_t Generate(nsImapBodyShell* aShell, nsImapProtocol* conn,
                           bool stream, bool prefetch) override;
  virtual bool ShouldFetchInline(nsImapBodyShell* aShell) override;
  virtual bool PreflightCheckAllInline(nsImapBodyShell* aShell) override;
  // Returns the part object with the given number
  virtual nsIMAPBodypart* FindPartWithNumber(const char* partNum) override;
  void AdoptMessageHeaders(
      char* headers);  // Fills in buffer (and adopts storage) for header object
                       // partNum specifies the message part number to which the
                       // headers correspond.  NULL indicates the top-level
                       // message
  virtual nsIMAPBodypartMessage* GetnsIMAPBodypartMessage() override {
    return this;
  }
  virtual bool GetIsTopLevelMessage() { return m_topLevelMessage; }

 protected:
  nsIMAPMessageHeaders* m_headers;  // Every body shell should have headers
  nsIMAPBodypart* m_body;
  bool m_topLevelMessage;  // Whether or not this is the top-level message
};

// MessagePartID and an array of them are used for pipelining prefetches.

class nsIMAPMessagePartID {
 public:
  nsIMAPMessagePartID(nsIMAPeFetchFields fields, const char* partNumberString);
  nsIMAPeFetchFields GetFields() { return m_fields; }
  const char* GetPartNumberString() { return m_partNumberString; }

 protected:
  const char* m_partNumberString;
  nsIMAPeFetchFields m_fields;
};

// We will refer to a Body "Shell" as a hierarchical object representation of a
// parsed BODYSTRUCTURE response.  A shell contains representations of Shell
// "Parts."  A Body Shell can undergo essentially two operations: Construction
// and Generation. Shell Construction occurs from a parsed a BODYSTRUCTURE
// response, split into empty parts. Shell Generation generates a "MIME Shell"
// of the message and streams it to libmime for display.  The MIME Shell has
// selected (inline) parts filled in, and leaves all others for on-demand
// retrieval through explicit part fetches.

class nsImapBodyShell : public nsISupports {
 public:
  NS_DECL_THREADSAFE_ISUPPORTS
  nsImapBodyShell(nsIMAPBodypartMessage* message, uint32_t UID,
                  uint32_t UIDValidity, const char* folderName,
                  bool showAttachmentsInline);
  // To be used after a shell is uncached
  bool GetIsValid() { return m_isValid; }
  void SetIsValid(bool valid);

  // Prefetch
  // Adds a message body part to the queue to be prefetched
  // in a single, pipelined command
  void AddPrefetchToQueue(nsIMAPeFetchFields, const char* partNum);
  // Fills in buffer (and adopts storage) for header object
  // partNum specifies the message part number to which the
  // headers correspond.  NULL indicates the top-level message
  void AdoptMessageHeaders(char* headers, const char* partNum);
  // Fills in buffer (and adopts storage) for MIME headers in appropriate
  // object. If object can't be found, sets isValid to false.
  void AdoptMimeHeader(const char* partNum, char* mimeHeader);

  // Generation
  // Streams out an HTML representation of this IMAP message, going along and
  // fetching parts it thinks it needs, and leaving empty shells for the parts
  // it doesn't.
  // Returns number of bytes generated, or -1 if invalid.
  // If partNum is not NULL, then this works to generates a MIME part that
  // hasn't been downloaded yet and leaves out all other parts.  By default, to
  // generate a normal message, partNum should be NULL.
  int32_t Generate(nsImapProtocol* conn, char* partNum);

  // Returns TRUE if the user has the pref "Show Attachments Inline" set.
  // Returns FALSE if the setting is "Show Attachments as Links"
  bool GetShowAttachmentsInline();
  // Returns true if all parts are inline, false otherwise. Does not generate
  // anything.
  bool PreflightCheckAllInline();

  // Helpers
  nsCString& GetUID() { return m_UID; }
  nsCString& GetUID_validity() { return m_UID_validity; }
  nsCString const& GetFolderName() const { return m_folderName; }
  char* GetGeneratingPart() { return m_generatingPart; }
  // Returns true if this is in the process of being generated,
  // so we don't re-enter
  bool IsBeingGenerated() { return m_isBeingGenerated; }
  bool IsShellCached() { return m_cached; }
  void SetIsCached(bool isCached) { m_cached = isCached; }
  bool GetGeneratingWholeMessage() { return m_generatingWholeMessage; }
  IMAP_ContentModifiedType GetContentModified() { return m_contentModified; }

 protected:
  virtual ~nsImapBodyShell();

  nsIMAPBodypartMessage* m_message;

  // Array of pipelined part prefetches.
  nsTArray<nsIMAPMessagePartID> m_prefetchQueue;

  bool m_isValid;
  nsCString m_UID;           // UID of this message
  nsCString m_UID_validity;  // appended UID and UID-validity of this message
  nsCString m_folderName;    // folder that contains this message
  char* m_generatingPart;  // If a specific part is being generated, this is it.
                           // Otherwise, NULL.
  bool m_isBeingGenerated;  // true if this body shell is in the process of
                            // being generated
  bool m_cached;            // Whether or not this shell is cached
  bool m_generatingWholeMessage;  // whether or not we are generating the whole
                                  // (non-MPOD) message Set to false if we are
                                  // generating by parts
  // under what conditions the content has been modified.
  // Either IMAP_CONTENT_MODIFIED_VIEW_INLINE or
  // IMAP_CONTENT_MODIFIED_VIEW_AS_LINKS
  IMAP_ContentModifiedType m_contentModified;
};

// This class caches shells, so we don't have to always go and re-fetch them.
// This does not cache any of the filled-in inline parts;  those are cached
// individually in the libnet memory cache.  (ugh, how will we do that?) Since
// we'll only be retrieving shells for messages over a given size, and since the
// shells themselves won't be very large, this cache will not grow very big
// (relatively) and should handle most common usage scenarios.

// A body cache is associated with a given host, spanning folders, so
// it uses both UID and UIDVALIDITY .

class nsImapBodyShellCache {
 public:
  nsImapBodyShellCache();

  // Adds shell to cache, possibly ejecting
  // another entry based on scheme in EjectEntry().
  void AddShellToCache(nsImapBodyShell* shell);
  // Looks up a shell in the cache given the message's UID.
  nsImapBodyShell* FindShellForUID(nsACString const& UID,
                                   nsACString const& mailboxName,
                                   IMAP_ContentModifiedType modType);
  void Clear();

 protected:
  static constexpr int kMaxEntries = 20;
  // Chooses an entry to eject;  deletes that entry;  and ejects it from the
  // cache, clearing up a new space.
  void EjectEntry();

  nsTArray<nsImapBodyShell*> m_shellList;
  // For quick lookup based on UID
  nsRefPtrHashtable<nsCStringHashKey, nsImapBodyShell> m_shellHash;
};

#endif  // IMAPBODY_H