From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- comm/mailnews/imap/src/ImapMessageService.jsm | 292 ++++++++++++++++++++++++++ 1 file changed, 292 insertions(+) create mode 100644 comm/mailnews/imap/src/ImapMessageService.jsm (limited to 'comm/mailnews/imap/src/ImapMessageService.jsm') 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}" +); -- cgit v1.2.3