Source code

Revision control

Copy as Markdown

Other Tools

/* -*- 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 "nsImapCore.h"
#include "nsRefCountedHashtable.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