This repository has been archived by the owner. It is now read-only.
Permalink
Cannot retrieve contributors at this time
359 lines (329 sloc)
9.63 KB
| /* 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 https://mozilla.org/MPL/2.0/. */ | |
| /** | |
| * @fileoverview A whole bunch of utility functions that will abstract away | |
| * various low-level nsIMsgDbHdr operations. The idea is to save time by not | |
| * having to lookup how to do simple actions. | |
| * @author Jonathan Protzenko | |
| */ | |
| var EXPORTED_SYMBOLS = [ | |
| // Low-level XPCOM boring stuff | |
| "msgHdrToMessageBody", | |
| "msgHdrToNeckoURL", | |
| "msgUriToMsgHdr", | |
| "msgHdrGetUri", | |
| // Quickly identify a message | |
| "msgHdrIsDraft", | |
| "msgHdrIsSent", | |
| "msgHdrIsArchive", | |
| "msgHdrIsInbox", | |
| "msgHdrIsRss", | |
| "msgHdrIsNntp", | |
| // Actions on a set of message headers | |
| "msgHdrsArchive", | |
| "msgHdrsDelete", | |
| // Doesn't really belong here | |
| "getMail3Pane", | |
| // Higher-level functions | |
| "msgHdrGetHeaders", | |
| ]; | |
| // from mailnews/base/public/nsMsgFolderFlags.idl | |
| const nsMsgFolderFlags_SentMail = 0x00000200; | |
| const nsMsgFolderFlags_Drafts = 0x00000400; | |
| const nsMsgFolderFlags_Archive = 0x00004000; | |
| const nsMsgFolderFlags_Inbox = 0x00001000; | |
| const { XPCOMUtils } = ChromeUtils.import( | |
| "resource://gre/modules/XPCOMUtils.jsm" | |
| ); | |
| XPCOMUtils.defineLazyModuleGetters(this, { | |
| MailServices: "resource:///modules/MailServices.jsm", | |
| MailUtils: "resource:///modules/MailUtils.jsm", | |
| MessageArchiver: "resource:///modules/MessageArchiver.jsm", | |
| Services: "resource://gre/modules/Services.jsm", | |
| }); | |
| XPCOMUtils.defineLazyGetter(this, "MimeMessage", () => { | |
| let tmp = {}; | |
| try { | |
| ChromeUtils.import("resource:///modules/gloda/mimemsg.js", tmp); | |
| } catch (ex) { | |
| ChromeUtils.import("resource:///modules/gloda/MimeMessage.jsm", tmp); | |
| } | |
| return tmp.MimeMessage; | |
| }); | |
| XPCOMUtils.defineLazyGetter(this, "MsgHdrToMimeMessage", () => { | |
| let tmp = {}; | |
| try { | |
| ChromeUtils.import("resource:///modules/gloda/mimemsg.js", tmp); | |
| } catch (ex) { | |
| ChromeUtils.import("resource:///modules/gloda/MimeMessage.jsm", tmp); | |
| } | |
| return tmp.MsgHdrToMimeMessage; | |
| }); | |
| // Adding a messenger lazy getter to the MailServices even though it's not a service | |
| XPCOMUtils.defineLazyGetter(MailServices, "messenger", function() { | |
| return Cc["@mozilla.org/messenger;1"].createInstance(Ci.nsIMessenger); | |
| }); | |
| /** | |
| * Get a given message header's uri. | |
| * @param {nsIMsgDbHdr} aMsg The message | |
| * @return {String} | |
| */ | |
| function msgHdrGetUri(aMsg) { | |
| return aMsg.folder.getUriForMsg(aMsg); | |
| } | |
| /** | |
| * Get a msgHdr from a message URI (msgHdr.URI). | |
| * @param {String} aUri The URI of the message | |
| * @return {nsIMsgDbHdr} | |
| */ | |
| function msgUriToMsgHdr(aUri) { | |
| try { | |
| let messageService = MailServices.messenger.messageServiceFromURI(aUri); | |
| return messageService.messageURIToMsgHdr(aUri); | |
| } catch (e) { | |
| dump("Unable to get " + aUri + " — returning null instead"); | |
| return null; | |
| } | |
| } | |
| /** | |
| * Tells if the message is in the account's inbox | |
| * @param {nsIMsgDbHdr} msgHdr The message header to examine | |
| * @return {bool} | |
| */ | |
| function msgHdrIsInbox(msgHdr) { | |
| return msgHdr.folder.getFlag(nsMsgFolderFlags_Inbox); | |
| } | |
| /** | |
| * Tells if the message is a draft message | |
| * @param {nsIMsgDbHdr} msgHdr The message header to examine | |
| * @return {bool} | |
| */ | |
| function msgHdrIsDraft(msgHdr) { | |
| return msgHdr.folder.getFlag(nsMsgFolderFlags_Drafts); | |
| } | |
| /** | |
| * Tells if the message is a sent message | |
| * @param {nsIMsgDbHdr} msgHdr The message header to examine | |
| * @return {bool} | |
| */ | |
| function msgHdrIsSent(msgHdr) { | |
| return msgHdr.folder.getFlag(nsMsgFolderFlags_SentMail); | |
| } | |
| /** | |
| * Tells if the message is an archived message | |
| * @param {nsIMsgDbHdr} msgHdr The message header to examine | |
| * @return {bool} | |
| */ | |
| function msgHdrIsArchive(msgHdr) { | |
| return msgHdr.folder.getFlag(nsMsgFolderFlags_Archive); | |
| } | |
| /** | |
| * Get a string containing the body of a messsage. | |
| * @param {nsIMsgDbHdr} aMessageHeader The message header | |
| * @param {bool} aStripHtml Keep html? | |
| * @return {string} | |
| */ | |
| function msgHdrToMessageBody(aMessageHeader, aStripHtml, aLength) { | |
| let messenger = Cc["@mozilla.org/messenger;1"].createInstance( | |
| Ci.nsIMessenger | |
| ); | |
| let listener = Cc[ | |
| "@mozilla.org/network/sync-stream-listener;1" | |
| ].createInstance(Ci.nsISyncStreamListener); | |
| let uri = aMessageHeader.folder.getUriForMsg(aMessageHeader); | |
| messenger | |
| .messageServiceFromURI(uri) | |
| .streamMessage(uri, listener, null, null, false, ""); | |
| let folder = aMessageHeader.folder; | |
| /* | |
| * AUTF8String getMsgTextFromStream(in nsIInputStream aStream, in ACString aCharset, | |
| in unsigned long aBytesToRead, in unsigned long aMaxOutputLen, | |
| in boolean aCompressQuotes, in boolean aStripHTMLTags, | |
| out ACString aContentType); | |
| */ | |
| return folder.getMsgTextFromStream( | |
| listener.inputStream, | |
| aMessageHeader.Charset, | |
| 2 * aLength, | |
| aLength, | |
| false, | |
| aStripHtml, | |
| {} | |
| ); | |
| } | |
| /** | |
| * Get a nsIURI from a nsIMsgDBHdr | |
| * @param {nsIMsgDbHdr} aMsgHdr The message header | |
| * @return {nsIURI} | |
| */ | |
| function msgHdrToNeckoURL(aMsgHdr) { | |
| let uri = aMsgHdr.folder.getUriForMsg(aMsgHdr); | |
| let neckoURL = {}; | |
| let msgService = MailServices.messenger.messageServiceFromURI(uri); | |
| msgService.GetUrlForUri(uri, neckoURL, null); | |
| return neckoURL.value; | |
| } | |
| /** | |
| * Delete a set of messages. | |
| * @param {nsIMsgDbHdr array} msgHdrs The message headers | |
| */ | |
| function msgHdrsDelete(msgHdrs) { | |
| let pending = {}; | |
| for (let msgHdr of msgHdrs) { | |
| if (!pending[msgHdr.folder.URI]) { | |
| pending[msgHdr.folder.URI] = { | |
| folder: msgHdr.folder, | |
| msgs: Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray), | |
| }; | |
| } | |
| pending[msgHdr.folder.URI].msgs.appendElement(msgHdr); | |
| } | |
| for (let [, { folder, msgs }] of Object.entries(pending)) { | |
| folder.deleteMessages( | |
| msgs, | |
| getMail3Pane().msgWindow, | |
| false, | |
| false, | |
| null, | |
| true | |
| ); | |
| folder.msgDatabase = null; /* don't leak */ | |
| } | |
| } | |
| /** | |
| * Get the main Thunderbird window. Used heavily to get a reference to globals | |
| * that are defined in mail/base/content/. | |
| * @return The window object for the main window. | |
| */ | |
| function getMail3Pane() { | |
| return Services.wm.getMostRecentWindow("mail:3pane"); | |
| } | |
| /** | |
| * Archive a set of messages | |
| * @param {nsIMsgDbHdr array} msgHdrs The message headers | |
| */ | |
| function msgHdrsArchive(msgHdrs) { | |
| const archiver = new MessageArchiver(); | |
| archiver.archiveMessages( | |
| msgHdrs.filter( | |
| x => | |
| !msgHdrIsArchive(x) && MailUtils.getIdentityForHeader(x).archiveEnabled | |
| ) | |
| ); | |
| } | |
| /** | |
| * Tell if a message is an RSS feed iteme | |
| * @param {nsIMsgDbHdr} msgHdr The message header | |
| * @return {Bool} | |
| */ | |
| function msgHdrIsRss(msgHdr) { | |
| return msgHdr.folder.server instanceof Ci.nsIRssIncomingServer; | |
| } | |
| /** | |
| * Tell if a message is a NNTP message | |
| * @param {nsIMsgDbHdr} msgHdr The message header | |
| * @return {Bool} | |
| */ | |
| function msgHdrIsNntp(msgHdr) { | |
| return msgHdr.folder.server instanceof Ci.nsINntpIncomingServer; | |
| } | |
| /** | |
| * Recycling the HeaderHandlerBase from mimemsg.js | |
| */ | |
| function HeaderHandler(aHeaders) { | |
| this.headers = aHeaders; | |
| } | |
| HeaderHandler.prototype = { | |
| __proto__: MimeMessage.prototype.__proto__, // == HeaderHandlerBase | |
| }; | |
| /** | |
| * Creates a stream listener that will call k once done, passing it the string | |
| * that has been read. | |
| */ | |
| function createStreamListener(k) { | |
| return { | |
| _data: "", | |
| _stream: null, | |
| QueryInterface: ChromeUtils.generateQI([ | |
| Ci.nsIStreamListener, | |
| Ci.nsIRequestObserver, | |
| ]), | |
| // nsIRequestObserver | |
| onStartRequest(aRequest) {}, | |
| onStopRequest(aRequest, aStatusCode) { | |
| try { | |
| k(this._data); | |
| } catch (e) { | |
| dump("Error inside stream listener:\n" + e + "\n"); | |
| } | |
| }, | |
| // nsIStreamListener | |
| onDataAvailable(aRequest, aInputStream, aOffset, aCount) { | |
| if (this._stream == null) { | |
| this._stream = Cc[ | |
| "@mozilla.org/scriptableinputstream;1" | |
| ].createInstance(Ci.nsIScriptableInputStream); | |
| this._stream.init(aInputStream); | |
| } | |
| this._data += this._stream.read(aCount); | |
| }, | |
| }; | |
| } | |
| /** | |
| * @param aMsgHdr The message header whose headers you want | |
| * @param k A function that takes a HeaderHandler object (see mimemsg.js). | |
| * Such an object has a get function, a has function. It has a header property, | |
| * whose keys are lowercased header names, and whose values are list of | |
| * strings corresponding to the multiple entries found for that header. | |
| */ | |
| function msgHdrGetHeaders(aMsgHdr, k) { | |
| let uri = msgHdrGetUri(aMsgHdr); | |
| let messageService = MailServices.messenger.messageServiceFromURI(uri); | |
| let fallback = () => | |
| MsgHdrToMimeMessage( | |
| aMsgHdr, | |
| null, | |
| function(aMsgHdr, aMimeMsg) { | |
| k(aMimeMsg); | |
| }, | |
| true, | |
| { | |
| partsOnDemand: true, | |
| } | |
| ); | |
| // This is intentionally disabled because there's a bug in Thunderbird that | |
| // renders the supposedly-useful streamHeaders function unusable. | |
| if (false && "streamHeaders" in messageService) { | |
| try { | |
| messageService.streamHeaders( | |
| uri, | |
| createStreamListener(aRawString => { | |
| let re = /\r?\n\s+/g; | |
| let str = aRawString.replace(re, " "); | |
| let lines = str.split(/\r?\n/); | |
| let obj = {}; | |
| for (let line of lines) { | |
| let i = line.indexOf(":"); | |
| if (i < 0) { | |
| continue; | |
| } | |
| let k = line.substring(0, i).toLowerCase(); | |
| let v = line.substring(i + 1).trim(); | |
| if (!(k in obj)) { | |
| obj[k] = []; | |
| } | |
| obj[k].push(v); | |
| } | |
| k(new HeaderHandler(obj)); | |
| }), | |
| null, | |
| true | |
| ); | |
| } catch (e) { | |
| fallback(); | |
| } | |
| } else { | |
| fallback(); | |
| } | |
| } |