/* -*- 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 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 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 m_shellList; // For quick lookup based on UID nsRefPtrHashtable m_shellHash; }; #endif // IMAPBODY_H