Bug 1762688 - Implement expunge/onlineCopy in ImapService.jsm. r=mkmelin

- Remove unused return value of nsIImapService.expunge
- Implement streamMessage in ImapMessageService.jsm
- Enable test_compactOfflineStore.

Differential Revision: https://phabricator.services.mozilla.com/D150883

Depends on D150313

--HG--
extra : rebase_source : 3ae28eb16d0784b73529dae293abdf660a731353
This commit is contained in:
Ping Chen 2022-07-12 05:54:12 +00:00
Родитель 627353f83c
Коммит af0394f26d
12 изменённых файлов: 184 добавлений и 20 удалений

Просмотреть файл

@ -88,13 +88,10 @@ interface nsIImapService : nsISupports
* @param aImapMailFolder the folder to expunge
* @param aUrlListener url listener, can be null
* @param aMsgWindow msg window url is running in, can be null
*
* @returns the url created to run the expunge.
*/
void expunge(in nsIMsgFolder aImapMailFolder,
in nsIUrlListener aUrlListener,
in nsIMsgWindow aMsgWindow,
out nsIURI aURL);
in nsIMsgWindow aMsgWindow);
/**
* Issue a STATUS on the target folder.

Просмотреть файл

@ -78,7 +78,9 @@ class ImapChannel {
if (this._readFromLocalCache()) {
return;
}
} catch (e) {}
} catch (e) {
this._logger.warn(e);
}
this._readFromServer();
}
@ -96,7 +98,7 @@ class ImapChannel {
return false;
}
let hdr = this.URI.folder.getMessageHeader(this._msgKey);
let hdr = this.URI.folder.GetMessageHeader(this._msgKey);
let stream = this.URI.folder.getLocalMsgStream(hdr);
let pump = Cc["@mozilla.org/network/input-stream-pump;1"].createInstance(
Ci.nsIInputStreamPump

Просмотреть файл

@ -213,6 +213,7 @@ class ImapClient {
* @param {number} flags - The internal flags number to update.
*/
updateMesageFlags(action, folder, urlListener, messageIds, flags) {
this._urlListener = urlListener;
let getCommand = () => {
// _supportedFlags is available after _actionSelectResponse.
let flagsStr = ImapUtils.flagsToString(flags, this._supportedFlags);
@ -232,6 +233,39 @@ class ImapClient {
}
}
/**
* Send EXPUNGE command to a folder.
* @param {nsIMsgFolder} folder - The associated folder.
*/
expunge(folder) {
this._actionFolderCommand(folder, () => {
this._nextAction = () => this._actionDone();
this._sendTagged("EXPUNGE");
});
}
/**
* Move or copy messages from a folder to another folder.
* @param {nsIMsgFolder} folder - The source folder.
* @param {nsIMsgFolder} folder - The target folder.
* @param {string} messageIds - The message identifiers.
* @param {boolean} idsAreUids - If true messageIds are UIDs, otherwise,
* messageIds are sequences.
* @param {boolean} isMove - If true, use MOVE command when supported.
*/
copy(folder, dstFolder, messageIds, idsAreUids, isMove) {
let command = idsAreUids ? "UID " : "";
command +=
isMove && this._capabilities.includes("MOVE")
? "MOVE " // rfc6851
: "COPY ";
command += messageIds + ` "${this._getServerFolderName(dstFolder)}"`;
this._actionFolderCommand(folder, () => {
this._nextAction = () => this._actionDone();
this._sendTagged(command);
});
}
/**
* Send IDLE command to the server.
*/
@ -672,6 +706,25 @@ class ImapClient {
}
this._send(token, true);
};
/**
* Execute an action with a folder selected.
* @param {nsIMsgFolder} folder - The folder to select.
* @param {function} actionInFolder - The action to execute.
*/
_actionFolderCommand(folder, actionInFolder) {
if (this.folder == folder) {
// If already in the folder, execute the action now.
actionInFolder();
} else {
// Send the SELECT command and queue the action.
this.folder = folder;
this._actionAfterSelectFolder = actionInFolder;
this._nextAction = this._actionSelectResponse;
this._sendTagged(`SELECT "${this._getServerFolderName(folder)}"`);
}
}
/**
* Send LSUB or LIST command depending on the server capabilities.
*/

Просмотреть файл

@ -2,7 +2,7 @@
* 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"];
const EXPORTED_SYMBOLS = ["ImapMessageService", "ImapMessageMessageService"];
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
@ -21,7 +21,7 @@ XPCOMUtils.defineLazyModuleGetters(lazy, {
/**
* @implements {nsIMsgMessageService}
*/
class ImapMessageService {
class BaseMessageService {
QueryInterface = ChromeUtils.generateQI(["nsIMsgMessageService"]);
SaveMessageToDisk(
@ -75,6 +75,46 @@ class ImapMessageService {
return imapUrl;
}
streamMessage(messageUri, consumer, msgWindow, urlListener, localOnly) {
let { host, folderName, key } = this._decomposeMessageUri(messageUri);
let imapUrl = Services.io
.newURI(`imap://${host}/fetch>UID>/${folderName}>${key}`)
.QueryInterface(Ci.nsIImapUrl);
imapUrl.localFetchOnly = localOnly;
let folder = lazy.MailUtils.getOrCreateFolder(
`imap://${host}/${folderName}`
);
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,
false,
{}
);
}
messageURIToMsgHdr(messageUri) {
let { host, folderName, key } = this._decomposeMessageUri(messageUri);
let folder = lazy.MailUtils.getOrCreateFolder(
`imap://${host}/${folderName}`
);
return folder.GetMessageHeader(key);
}
/**
* Parse a message uri to hostname, folder and message key.
* @param {string} uri - The imap-message:// url to parse.
@ -88,6 +128,20 @@ class ImapMessageService {
}
}
/**
* 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}"
);

Просмотреть файл

@ -36,6 +36,12 @@ var imapJSModules = [
"@mozilla.org/messenger/messageservice;1?type=imap",
"ImapMessageService",
],
[
"ImapMessageMessageService",
"{2532ae4f-a852-4c96-be45-1308ba23d62e}",
"@mozilla.org/messenger/messageservice;1?type=imap-message",
"ImapMessageService",
],
[
"ImapProtocolHandler",
"{ebb06c58-6ccd-4bde-9087-40663e0388ae}",
@ -56,9 +62,15 @@ ImapModuleLoader.prototype = {
Ci.nsIComponentRegistrar
);
for (let [moduleName, interfaceId, contractId] of imapJSModules) {
for (let [
moduleName,
interfaceId,
contractId,
fileName,
] of imapJSModules) {
fileName = fileName || moduleName;
// Load a module.
let scope = ChromeUtils.import(`resource:///modules/${moduleName}.jsm`);
let scope = ChromeUtils.import(`resource:///modules/${fileName}.jsm`);
// Register a module.
let classId = Components.ID(interfaceId);

Просмотреть файл

@ -135,6 +135,49 @@ class ImapService {
channel.asyncOpen(streamListener);
}
}
expunge(folder, urlListener, msgWindow) {
this._withClient(folder, client => {
client.startRunningUrl(urlListener, msgWindow);
client.onReady = () => {
client.expunge(folder);
};
});
}
onlineMessageCopy(
folder,
messageIds,
dstFolder,
idsAreUids,
isMove,
urlListener,
outURL,
copyState,
msgWindow
) {
this._withClient(folder, client => {
let runningUrl = client.startRunningUrl(urlListener, msgWindow);
runningUrl.QueryInterface(Ci.nsIImapUrl).imapAction = isMove
? Ci.nsIImapUrl.nsImapOnlineMove
: Ci.nsIImapUrl.nsImapOnlineCopy;
client.onReady = () => {
client.copy(folder, dstFolder, messageIds, idsAreUids, isMove);
};
});
}
/**
* Do some actions with a connection.
* @param {nsIMsgFolder} folder - The associated folder.
* @param {Function} handler - A callback function to take a ImapClient
* instance, and do some actions.
*/
_withClient(folder, handler) {
let server = folder.QueryInterface(Ci.nsIMsgImapMailFolder)
.imapIncomingServer;
server.wrappedJSObject.withClient(handler);
}
}
ImapService.prototype.classID = Components.ID(

Просмотреть файл

@ -1300,7 +1300,7 @@ NS_IMETHODIMP nsImapMailFolder::Expunge(nsIUrlListener* aListener,
do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
return imapService->Expunge(this, aListener, aMsgWindow, nullptr);
return imapService->Expunge(this, aListener, aMsgWindow);
}
NS_IMETHODIMP nsImapMailFolder::CompactAll(nsIUrlListener* aListener,
@ -6970,10 +6970,11 @@ nsImapMailFolder::CopyMessages(
allMessageCountNotifications,
false); // disable message count notification
nsCOMPtr<nsIURI> resultUrl;
nsCOMPtr<nsISupports> copySupport = do_QueryInterface(m_copyState);
rv = imapService->OnlineMessageCopy(srcFolder, messageIds, this, true,
isMove, urlListener, nullptr,
copySupport, msgWindow);
rv = imapService->OnlineMessageCopy(
srcFolder, messageIds, this, true, isMove, urlListener,
getter_AddRefs(resultUrl), copySupport, msgWindow);
if (NS_SUCCEEDED(rv) && m_copyState->m_allowUndo) {
RefPtr<nsImapMoveCopyMsgTxn> undoMsgTxn = new nsImapMoveCopyMsgTxn;
if (!undoMsgTxn ||

Просмотреть файл

@ -1445,11 +1445,11 @@ NS_IMETHODIMP nsImapService::UpdateFolderStatus(nsIMsgFolder* aImapMailFolder,
// Expunge, used to "compress" an imap folder,removes deleted messages.
NS_IMETHODIMP nsImapService::Expunge(nsIMsgFolder* aImapMailFolder,
nsIUrlListener* aUrlListener,
nsIMsgWindow* aMsgWindow, nsIURI** aURL) {
nsIMsgWindow* aMsgWindow) {
NS_ENSURE_ARG_POINTER(aImapMailFolder);
return FolderCommand(aImapMailFolder, aUrlListener, "/Expunge>",
nsIImapUrl::nsImapExpungeFolder, aMsgWindow, aURL);
nsIImapUrl::nsImapExpungeFolder, aMsgWindow, nullptr);
}
/* old-stle biff that doesn't download headers */

Просмотреть файл

@ -7,6 +7,7 @@
* and returns success.
*/
var { setTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm");
var { MessageGenerator } = ChromeUtils.import(
"resource://testing-common/mailnews/MessageGenerator.jsm"
);
@ -161,9 +162,11 @@ add_task(async function compactOfflineStore() {
let listener = new PromiseTestUtils.PromiseUrlListener();
gRootFolder.compactAll(listener, null);
await listener.promise;
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve => setTimeout(resolve, 300));
});
add_task(function test_checkCompactionResult() {
add_task(function test_checkCompactionResult1() {
checkOfflineStore(gImapInboxOfflineStoreSize);
});
@ -176,7 +179,7 @@ add_task(async function pendingRemoval() {
await listener.promise;
});
add_task(function test_checkCompactionResult() {
add_task(function test_checkCompactionResult2() {
let tmpFile = gRootFolder.filePath;
tmpFile.append("nstmp");
Assert.ok(!tmpFile.exists());

Просмотреть файл

@ -6,7 +6,6 @@ run-sequentially =
prefs =
mailnews.imap.jsmodule=false
[test_compactOfflineStore.js]
[test_copyThenMove.js]
[test_customCommandReturnsFetchResponse.js]
[test_dod.js]

Просмотреть файл

@ -2,6 +2,7 @@
[test_bccProperty.js]
[test_bug460636.js]
[test_chunkLastLF.js]
[test_compactOfflineStore.js]
[test_converterImap.js]
[test_mailboxes.js]
[test_imapPasswordFailure.js]

Просмотреть файл

@ -6,7 +6,6 @@ run-sequentially =
prefs =
mailnews.imap.jsmodule=false
[test_compactOfflineStore.js]
[test_copyThenMove.js]
[test_customCommandReturnsFetchResponse.js]
[test_dontStatNoSelect.js]