summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/imap/src/ImapMessageService.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/imap/src/ImapMessageService.jsm')
-rw-r--r--comm/mailnews/imap/src/ImapMessageService.jsm292
1 files changed, 292 insertions, 0 deletions
diff --git a/comm/mailnews/imap/src/ImapMessageService.jsm b/comm/mailnews/imap/src/ImapMessageService.jsm
new file mode 100644
index 0000000000..e88e0f8090
--- /dev/null
+++ b/comm/mailnews/imap/src/ImapMessageService.jsm
@@ -0,0 +1,292 @@
+/* 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/. */
+
+const EXPORTED_SYMBOLS = ["ImapMessageService", "ImapMessageMessageService"];
+
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+const { MailServices } = ChromeUtils.import(
+ "resource:///modules/MailServices.jsm"
+);
+const { ImapUtils } = ChromeUtils.import("resource:///modules/ImapUtils.jsm");
+
+const lazy = {};
+
+XPCOMUtils.defineLazyModuleGetters(lazy, {
+ MailUtils: "resource:///modules/MailUtils.jsm",
+});
+
+/**
+ * @implements {nsIMsgMessageService}
+ */
+class BaseMessageService {
+ QueryInterface = ChromeUtils.generateQI(["nsIMsgMessageService"]);
+
+ _logger = ImapUtils.logger;
+
+ copyMessage(messageUri, copyListener, moveMessage, urlListener, msgWindow) {
+ this._logger.debug("copyMessage", messageUri, moveMessage);
+ let { serverURI, folder, folderName, key } =
+ this._decomposeMessageUri(messageUri);
+ let imapUrl = Services.io
+ .newURI(`${serverURI}/fetch>UID>/${folderName}>${key}`)
+ .QueryInterface(Ci.nsIImapUrl);
+
+ if (urlListener) {
+ imapUrl
+ .QueryInterface(Ci.nsIMsgMailNewsUrl)
+ .RegisterListener(urlListener);
+ }
+
+ return MailServices.imap.fetchMessage(
+ imapUrl,
+ moveMessage
+ ? Ci.nsIImapUrl.nsImapOnlineToOfflineMove
+ : Ci.nsIImapUrl.nsImapOnlineToOfflineCopy,
+ folder,
+ folder.QueryInterface(Ci.nsIImapMessageSink),
+ msgWindow,
+ copyListener,
+ key,
+ false,
+ {}
+ );
+ }
+
+ loadMessage(
+ messageUri,
+ displayConsumer,
+ msgWindow,
+ urlListener,
+ autodetectCharset
+ ) {
+ this._logger.debug("loadMessage", messageUri);
+ let { serverURI, folder, folderName, key } =
+ this._decomposeMessageUri(messageUri);
+ let imapUrl = Services.io
+ .newURI(`${serverURI}/fetch>UID>/${folderName}>${key}`)
+ .QueryInterface(Ci.nsIImapUrl);
+
+ let mailnewsUrl = imapUrl.QueryInterface(Ci.nsIMsgMailNewsUrl);
+ if (urlListener) {
+ mailnewsUrl.RegisterListener(urlListener);
+ }
+
+ return MailServices.imap.fetchMessage(
+ imapUrl,
+ Ci.nsIImapUrl.nsImapMsgFetch,
+ folder,
+ folder.QueryInterface(Ci.nsIImapMessageSink),
+ msgWindow,
+ displayConsumer,
+ key,
+ false,
+ {}
+ );
+ }
+
+ SaveMessageToDisk(
+ messageUri,
+ file,
+ addDummyEnvelope,
+ urlListener,
+ outUrl,
+ canonicalLineEnding,
+ msgWindow
+ ) {
+ this._logger.debug("SaveMessageToDisk", messageUri);
+ let { serverURI, folder, folderName, key } =
+ this._decomposeMessageUri(messageUri);
+ let imapUrl = Services.io
+ .newURI(`${serverURI}/fetch>UID>/${folderName}>${key}`)
+ .QueryInterface(Ci.nsIImapUrl);
+
+ let msgUrl = imapUrl.QueryInterface(Ci.nsIMsgMessageUrl);
+ msgUrl.messageFile = file;
+ msgUrl.AddDummyEnvelope = addDummyEnvelope;
+ msgUrl.canonicalLineEnding = canonicalLineEnding;
+ let mailnewsUrl = imapUrl.QueryInterface(Ci.nsIMsgMailNewsUrl);
+ mailnewsUrl.RegisterListener(urlListener);
+ mailnewsUrl.msgIsInLocalCache = folder.hasMsgOffline(key, null, 10);
+
+ return MailServices.imap.fetchMessage(
+ imapUrl,
+ Ci.nsIImapUrl.nsImapSaveMessageToDisk,
+ folder,
+ folder.QueryInterface(Ci.nsIImapMessageSink),
+ msgWindow,
+ mailnewsUrl.getSaveAsListener(addDummyEnvelope, file),
+ key,
+ false,
+ {}
+ );
+ }
+
+ getUrlForUri(messageUri, msgWindow) {
+ if (messageUri.includes("&type=application/x-message-display")) {
+ return Services.io.newURI(messageUri);
+ }
+
+ let { serverURI, folder, folderName, key } =
+ this._decomposeMessageUri(messageUri);
+ let delimiter =
+ folder.QueryInterface(Ci.nsIMsgImapMailFolder).hierarchyDelimiter || "/";
+ let imapUrl = Services.io
+ .newURI(
+ `${serverURI}:${folder.server.port}/fetch>UID>${delimiter}${folderName}>${key}`
+ )
+ .QueryInterface(Ci.nsIImapUrl);
+
+ return imapUrl;
+ }
+
+ streamMessage(
+ messageUri,
+ consumer,
+ msgWindow,
+ urlListener,
+ convertData,
+ additionalHeader,
+ localOnly
+ ) {
+ this._logger.debug("streamMessage", messageUri);
+ let { serverURI, folder, folderName, key } =
+ this._decomposeMessageUri(messageUri);
+ let url = `${serverURI}/fetch>UID>/${folderName}>${key}`;
+ if (additionalHeader) {
+ url += `?header=${additionalHeader}`;
+ }
+ let imapUrl = Services.io.newURI(url).QueryInterface(Ci.nsIImapUrl);
+ imapUrl.localFetchOnly = localOnly;
+
+ let mailnewsUrl = imapUrl.QueryInterface(Ci.nsIMsgMailNewsUrl);
+ mailnewsUrl.folder = folder;
+ mailnewsUrl.msgWindow = msgWindow;
+ mailnewsUrl.msgIsInLocalCache = folder.hasMsgOffline(key);
+ if (urlListener) {
+ mailnewsUrl.RegisterListener(urlListener);
+ }
+
+ return MailServices.imap.fetchMessage(
+ imapUrl,
+ Ci.nsIImapUrl.nsImapMsgFetchPeek,
+ folder,
+ folder.QueryInterface(Ci.nsIImapMessageSink),
+ msgWindow,
+ consumer,
+ key,
+ convertData,
+ {}
+ );
+ }
+
+ streamHeaders(messageUri, consumer, urlListener, localOnly) {
+ this._logger.debug("streamHeaders", messageUri);
+ let { folder, key } = this._decomposeMessageUri(messageUri);
+
+ let hasMsgOffline = folder.hasMsgOffline(key);
+ if (!hasMsgOffline) {
+ return;
+ }
+
+ let localMsgStream = folder.getLocalMsgStream(folder.GetMessageHeader(key));
+ let sstream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(
+ Ci.nsIScriptableInputStream
+ );
+ sstream.init(localMsgStream);
+ let headers = "";
+ let str = "";
+ do {
+ str = sstream.read(4096);
+ let index = str.indexOf("\r\n\r\n");
+ if (index != -1) {
+ headers += str.slice(0, index) + "\r\n";
+ break;
+ } else {
+ headers += str;
+ }
+ } while (str.length);
+
+ let headersStream = Cc[
+ "@mozilla.org/io/string-input-stream;1"
+ ].createInstance(Ci.nsIStringInputStream);
+ headersStream.setData(headers, headers.length);
+ let pump = Cc["@mozilla.org/network/input-stream-pump;1"].createInstance(
+ Ci.nsIInputStreamPump
+ );
+ pump.init(headersStream, 0, 0, true);
+ pump.asyncRead(consumer);
+ }
+
+ /**
+ * Go from message uri to go nsIMsgDBHdr.
+ *
+ * @param {string} uri - A message uri to get the nsIMsgDBHdr for.
+ * @returns {?nsIMsgDBHdr} Hdr for the uri, or or null if failed.
+ */
+ messageURIToMsgHdr(uri) {
+ try {
+ let { folder, key } = this._decomposeMessageUri(uri);
+ return folder.GetMessageHeader(key);
+ } catch (e) {
+ return null;
+ }
+ }
+
+ Search(searchSession, msgWindow, folder, searchUri) {
+ let server = folder.server.QueryInterface(Ci.nsIMsgIncomingServer);
+ server.wrappedJSObject.withClient(folder, client => {
+ client.startRunningUrl(
+ searchSession.QueryInterface(Ci.nsIUrlListener),
+ msgWindow
+ );
+ client.onReady = () => {
+ client.search(folder, searchUri);
+ };
+ client.onData = uids => {
+ for (let uid of uids) {
+ let msgHdr = folder.msgDatabase.getMsgHdrForKey(uid);
+ searchSession.runningAdapter.AddResultElement(msgHdr);
+ }
+ };
+ });
+ }
+
+ /**
+ * Parse a message uri to hostname, folder and message key.
+ *
+ * @param {string} uri - The imap-message:// url to parse.
+ * @returns {serverURI: string, folder: nsIMsgFolder, folderName: string, key: string}
+ */
+ _decomposeMessageUri(messageUri) {
+ let matches = /imap-message:\/\/([^:/]+)\/(.+)#(\d+)/.exec(messageUri);
+ if (!matches) {
+ throw new Error(`Unexpected IMAP URL: ${messageUri}`);
+ }
+ let [, host, folderName, key] = matches;
+ let folder = lazy.MailUtils.getOrCreateFolder(
+ `imap://${host}/${folderName}`
+ );
+ return { serverURI: folder.server.serverURI, folder, folderName, key };
+ }
+}
+
+/**
+ * A message service for imap://.
+ */
+class ImapMessageService extends BaseMessageService {}
+
+ImapMessageService.prototype.classID = Components.ID(
+ "{d63af753-c2f3-4f1d-b650-9d12229de8ad}"
+);
+
+/**
+ * A message service for imap-message://.
+ */
+class ImapMessageMessageService extends BaseMessageService {}
+
+ImapMessageMessageService.prototype.classID = Components.ID(
+ "{2532ae4f-a852-4c96-be45-1308ba23d62e}"
+);