Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
master
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
/* 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();
}
}