diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/mailnews/news/src/NntpMessageService.jsm | |
parent | Initial commit. (diff) | |
download | thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/mailnews/news/src/NntpMessageService.jsm')
-rw-r--r-- | comm/mailnews/news/src/NntpMessageService.jsm | 272 |
1 files changed, 272 insertions, 0 deletions
diff --git a/comm/mailnews/news/src/NntpMessageService.jsm b/comm/mailnews/news/src/NntpMessageService.jsm new file mode 100644 index 0000000000..dcfac7570a --- /dev/null +++ b/comm/mailnews/news/src/NntpMessageService.jsm @@ -0,0 +1,272 @@ +/* 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 = ["NntpMessageService", "NewsMessageService"]; + +const { XPCOMUtils } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); +const { MailServices } = ChromeUtils.import( + "resource:///modules/MailServices.jsm" +); + +const lazy = {}; +XPCOMUtils.defineLazyModuleGetters(lazy, { + NntpChannel: "resource:///modules/NntpChannel.jsm", + NntpUtils: "resource:///modules/NntpUtils.jsm", +}); + +/** + * A message service for news-message://, mainly used for displaying messages. + * + * @implements {nsIMsgMessageService} + * @implements {nsIMsgMessageFetchPartService} + */ +class BaseMessageService { + QueryInterface = ChromeUtils.generateQI([ + "nsIMsgMessageService", + "nsIMsgMessageFetchPartService", + ]); + + _logger = lazy.NntpUtils.logger; + + /** @see nsIMsgMessageService */ + loadMessage( + messageURI, + displayConsumer, + msgWindow, + urlListener, + autodetectCharset + ) { + this._logger.debug("loadMessage", messageURI); + + let uri = this.getUrlForUri(messageURI, msgWindow); + if (urlListener) { + uri.RegisterListener(urlListener); + } + if (displayConsumer instanceof Ci.nsIDocShell) { + uri.loadURI( + displayConsumer.QueryInterface(Ci.nsIDocShell), + Ci.nsIWebNavigation.LOAD_FLAGS_NONE + ); + } else { + let streamListener = displayConsumer.QueryInterface(Ci.nsIStreamListener); + let channel = new lazy.NntpChannel(uri); + channel.asyncOpen(streamListener); + } + } + + /** + * @param {string} messageURI - Message URI. + * @param {?nsIMsgWindow} [msgWindow] - Message window. + * @returns {nsIURI} + */ + getUrlForUri(messageURI, msgWindow) { + let uri = Services.io + .newURI(this._createMessageIdUrl(messageURI)) + .QueryInterface(Ci.nsIMsgMailNewsUrl); + uri.msgWindow = msgWindow; + uri.QueryInterface(Ci.nsIMsgMessageUrl).originalSpec = messageURI; + uri.QueryInterface(Ci.nsINntpUrl).newsAction = + Ci.nsINntpUrl.ActionFetchArticle; + return uri; + } + + /** + * @param {string} uri - The message URI. + * @returns {?nsIMsgDBHdr} The message for the URI, or null. + */ + messageURIToMsgHdr(uri) { + let [folder, key] = this._decomposeNewsMessageURI(uri); + return folder?.GetMessageHeader(key); + } + + copyMessage(messageUri, copyListener, moveMessage, urlListener, msgWindow) { + this._logger.debug("copyMessage", messageUri); + this.loadMessage(messageUri, copyListener, msgWindow, urlListener, false); + } + + SaveMessageToDisk( + messageUri, + file, + addDummyEnvelope, + urlListener, + outUrl, + canonicalLineEnding, + msgWindow + ) { + this._logger.debug("SaveMessageToDisk", messageUri); + let url = this.getUrlForUri(messageUri, msgWindow); + if (urlListener) { + url.RegisterListener(urlListener); + } + url.newsAction = Ci.nsINntpUrl.ActionSaveMessageToDisk; + url.AddDummyEnvelope = addDummyEnvelope; + url.canonicalLineEnding = canonicalLineEnding; + + let [folder, key] = this._decomposeNewsMessageURI(messageUri); + if (folder && folder.QueryInterface(Ci.nsIMsgNewsFolder)) { + url.msgIsInLocalCache = folder.hasMsgOffline(key); + } + + this.loadMessage( + messageUri, + url.getSaveAsListener(addDummyEnvelope, file), + msgWindow, + urlListener, + false + ); + } + + Search(searchSession, msgWindow, msgFolder, searchUri) { + let slashIndex = searchUri.indexOf("/"); + let xpatLines = searchUri.slice(slashIndex + 1).split("/"); + let server = msgFolder.server.QueryInterface(Ci.nsINntpIncomingServer); + + server.wrappedJSObject.withClient(client => { + client.startRunningUrl( + searchSession.QueryInterface(Ci.nsIUrlListener), + msgWindow + ); + client.onOpen = () => { + client.search(msgFolder.name, xpatLines); + }; + + client.onData = line => { + searchSession.runningAdapter.AddHit(line.split(" ")[0]); + }; + }); + } + + streamMessage( + messageUri, + consumer, + msgWindow, + urlListener, + convertData, + additionalHeader + ) { + this._logger.debug("streamMessage", messageUri); + let [folder, key] = this._decomposeNewsMessageURI(messageUri); + + let uri = this.getUrlForUri(messageUri, msgWindow); + if (additionalHeader) { + // NOTE: jsmimeemitter relies on this. + let url = new URL(uri.spec); + let params = new URLSearchParams(`?header=${additionalHeader}`); + for (let [key, value] of params.entries()) { + url.searchParams.set(key, value); + } + uri = uri.mutate().setQuery(url.search).finalize(); + } + + uri = uri.QueryInterface(Ci.nsIMsgMailNewsUrl); + uri.msgIsInLocalCache = folder.hasMsgOffline(key); + if (urlListener) { + uri.RegisterListener(urlListener); + } + + let streamListener = consumer.QueryInterface(Ci.nsIStreamListener); + let channel = new lazy.NntpChannel(uri.QueryInterface(Ci.nsINntpUrl)); + let listener = streamListener; + if (convertData) { + let converter = Cc["@mozilla.org/streamConverters;1"].getService( + Ci.nsIStreamConverterService + ); + listener = converter.asyncConvertData( + "message/rfc822", + "*/*", + streamListener, + channel + ); + } + channel.asyncOpen(listener); + return uri; + } + + /** + * Parse a message uri to folder and message key. + * + * @param {string} uri - The news-message:// url to parse. + * @returns {[nsIMsgFolder, string]} - The folder and message key. + */ + _decomposeNewsMessageURI(uri) { + let host, groupName, key; + if (uri.startsWith("news-message://")) { + let matches = /news-message:\/\/([^:]+)\/(.+)#(\d+)/.exec(uri); + if (!matches) { + throw Components.Exception( + `Failed to parse message url: ${uri}`, + Cr.NS_ERROR_ILLEGAL_VALUE + ); + } + [, host, groupName, key] = matches; + if (host.includes("@")) { + host = host.slice(host.indexOf("@") + 1); + } + } else { + let url = new URL(uri); + host = url.hostname; + groupName = url.searchParams.get("group"); + key = url.searchParams.get("key"); + } + groupName = groupName ? decodeURIComponent(groupName) : null; + let server = MailServices.accounts + .findServer("", host, "nntp") + .QueryInterface(Ci.nsINntpIncomingServer); + let folder; + if (groupName) { + folder = server.rootFolder + .getChildNamed(groupName) + .QueryInterface(Ci.nsIMsgNewsFolder); + } + return [folder, key]; + } + + /** + * Create a news:// url from a news-message:// url. + * + * @param {string} messageURI - The news-message:// url. + * @returns {string} The news:// url. + */ + _createMessageIdUrl(messageURI) { + if (messageURI.startsWith("news://")) { + return messageURI; + } + let [folder, key] = this._decomposeNewsMessageURI(messageURI); + let host = folder.rootFolder.URI; + let messageId = folder.getMessageIdForKey(key); + let url = new URL(`${host}/${encodeURIComponent(messageId)}`); + url.searchParams.set("group", folder.name); + url.searchParams.set("key", key); + if (!url.port) { + url.port = folder.server.port; + } + return url.toString(); + } + + /** @see nsIMsgMessageFetchPartService */ + fetchMimePart(uri, messageUri, displayConsumer, msgWindow, urlListener) { + this._logger.debug("fetchMimePart", uri.spec); + this.loadMessage(uri.spec, displayConsumer, msgWindow, urlListener, false); + } +} + +/** + * A message service for news-message://, mainly for displaying messages. + */ +class NntpMessageService extends BaseMessageService {} + +NntpMessageService.prototype.classID = Components.ID( + "{9cefbe67-5966-4f8a-b7b0-cedd60a02c8e}" +); + +/** + * A message service for news://, mainly for handling attachments. + */ +class NewsMessageService extends BaseMessageService {} + +NewsMessageService.prototype.classID = Components.ID( + "{4cae5569-2c72-4910-9f3d-774f9e939df8}" +); |