Bug 193625 Option to mark incoming junk mail as read p=eyalroz@technion.ac.il r=me sr=bienvenu

This commit is contained in:
neil%parkwaycc.co.uk 2004-04-16 10:24:19 +00:00
Родитель 9d2e5b35dc
Коммит ffbd23982c
15 изменённых файлов: 503 добавлений и 690 удалений

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

@ -55,6 +55,7 @@ interface nsISpamSettings: nsISupports {
attribute long level;
attribute boolean moveOnSpam;
attribute boolean markAsReadOnSpam;
/**
* Most consumers will just use spamFolderURI rather than accessing any of

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

@ -1,266 +0,0 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Seth Spitzer <sspitzer@netscape.com>
* Dan Mosedale <dmose@netscape.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
var gSpamSettings = {};
var gCurrentServer;
var gMessengerBundle;
const kJunkOnLocalFolderURI = "mailbox://nobody@Local%20Folders/Junk";
function onJunkMailLoad()
{
gMessengerBundle = document.getElementById("bundle_messenger");
if (window.arguments && window.arguments[0]) {
setupForAccountFromFolder(window.arguments[0].folder ? window.arguments[0].folder.URI : null);
}
}
function onServerClick(event)
{
if (gCurrentServer.serverURI == event.target.id)
return;
// before we set the UI for the new server,
// save off the old one
storeSettings(gSpamSettings[gCurrentServer.key].settings, gCurrentServer.spamSettings.loggingEnabled);
// set up the UI for the server
setupForAccountFromFolder(event.target.id);
}
function setupForAccountFromFolder(aURI)
{
try {
if (!aURI)
throw "this can happen if no folder is selected in the folder pane"
var msgFolder = GetMsgFolderFromUri(aURI, false);
gCurrentServer = msgFolder.server;
if (gCurrentServer.type == "nntp")
throw "this can happen if the selected folder (or account) doesn't have junk controls (news)"
}
catch (ex) {
// get server for default account
// XXX TODO
// edge cases to worry about later:
// what if there is no default account?
// what if default account is of a type where canGetIncomingMessages == true?
// what if no accounts are of a type where canGetIncomingMessages == true?
var accountManager = Components.classes["@mozilla.org/messenger/account-manager;1"]
.getService(Components.interfaces.nsIMsgAccountManager);
var account = accountManager.defaultAccount;
gCurrentServer = account.incomingServer;
}
var obj;
var key = gCurrentServer.key;
if (key in gSpamSettings) {
obj = gSpamSettings[key];
}
else {
// get and clone spam settings for this server
// we clone because if the users cancels we are going to throw away the changes
var settings = Components.classes["@mozilla.org/messenger/spamsettings;1"].createInstance(Components.interfaces.nsISpamSettings);
settings.clone(gCurrentServer.spamSettings);
obj = {server: gCurrentServer, settings: settings};
gSpamSettings[key] = obj;
}
// select server in the menulist
var serverList = document.getElementById("server");
var menuitems = serverList.getElementsByAttribute("id", obj.server.serverURI);
serverList.selectedItem = menuitems[0];
// set up the UI for this server
// set up the level checkbox
document.getElementById("level").checked = (obj.settings.level > 0);
// set up the junk mail folder picker
document.getElementById("moveOnSpam").checked = obj.settings.moveOnSpam;
document.getElementById("moveTargetMode").selectedItem = document.getElementById("moveTargetMode" + obj.settings.moveTargetMode);
// the default account should be the current account
// unless you can't create a folder on that server
// or search on that account (for purge)
// in which case, use Local Folders
var defaultAccountURI = obj.server.canCreateFoldersOnServer && obj.server.canSearchMessages ? obj.server.serverURI : "mailbox://nobody@Local%20Folders";
// if there is a target account, use it, else use the default account
SetFolderPicker(obj.settings.actionTargetAccount ? obj.settings.actionTargetAccount : defaultAccountURI, "actionTargetAccount");
// if there is a target account, use it, else use Junk on Local Folders
if (obj.settings.actionTargetFolder)
SetFolderPicker(obj.settings.actionTargetFolder, "actionTargetFolder");
else {
// note, this folder might not exist, but better this than ""
// and, we'll create it if we try to use it.
SetFolderPicker(kJunkOnLocalFolderURI, "actionTargetFolder");
}
// set up the purge UI
document.getElementById("purge").checked = obj.settings.purge;
document.getElementById("purgeInterval").value = obj.settings.purgeInterval;
// set up the whitelist UI
document.getElementById("useWhiteList").checked = obj.settings.useWhiteList;
var abList = document.getElementById("whiteListAbURI");
menuitems = abList.getElementsByAttribute("id", obj.settings.whiteListAbURI);
abList.selectedItem = menuitems[0];
// set up the manual mark UI
document.getElementById("manualMark").checked = obj.settings.manualMark;
document.getElementById("manualMarkMode").selectedItem = document.getElementById("manualMarkMode" + obj.settings.manualMarkMode);
conditionallyEnableUI(null);
}
function junkLog()
{
// pass in the "real" spam settings, as it's the one with the logStream
var args = {spamSettings: gCurrentServer.spamSettings};
window.openDialog("chrome://messenger/content/junkLog.xul", "junkLog", "chrome,modal,titlebar,resizable,centerscreen", args);
}
function onAccept()
{
// store the current changes
storeSettings(gSpamSettings[gCurrentServer.key].settings, gCurrentServer.spamSettings.loggingEnabled);
for (var key in gSpamSettings) {
try {
// if they hit ok, set the "real" server's spam settings.
// this will set prefs.
gSpamSettings[key].server.spamSettings = gSpamSettings[key].settings;
}
catch (ex) {
dump("spam setting not saved: " + ex);
}
}
return true;
}
function storeSettings(aSettings, aLoggingEnabled)
{
aSettings.level = document.getElementById("level").checked ? 100 : 0;
aSettings.moveOnSpam = document.getElementById("moveOnSpam").checked;
aSettings.moveTargetMode = document.getElementById("moveTargetMode").value;
aSettings.actionTargetAccount = document.getElementById("actionTargetAccount").getAttribute("uri");
var targetFolderURI = document.getElementById("actionTargetFolder").getAttribute("uri");
if (targetFolderURI)
aSettings.actionTargetFolder = targetFolderURI;
else {
// note, this folder might not exist, but better this than ""
// and, we'll create it if we try to use it.
aSettings.actionTargetFolder = kJunkOnLocalFolderURI;
}
aSettings.purge = document.getElementById("purge").checked;
aSettings.purgeInterval = document.getElementById("purgeInterval").value;
aSettings.useWhiteList = document.getElementById("useWhiteList").checked;
aSettings.whiteListAbURI = document.getElementById("whiteListAbURI").selectedItem.getAttribute("id");
aSettings.loggingEnabled = aLoggingEnabled;
aSettings.manualMark = document.getElementById("manualMark").checked;
aSettings.manualMarkMode = document.getElementById("manualMarkMode").value;
}
function conditionallyEnableUI(id)
{
if (!document.getElementById("level").checked) {
document.getElementById("useWhiteList").disabled = true;
document.getElementById("whiteListAbURI").disabled = true;
document.getElementById("moveOnSpam").disabled = true;
document.getElementById("moveTargetMode").disabled = true;
document.getElementById("actionTargetAccount").disabled = true;
document.getElementById("actionTargetFolder").disabled = true;
document.getElementById("purge").disabled = true;
document.getElementById("purgeInterval").disabled = true;
document.getElementById("purgeLabel").disabled = true;
document.getElementById("manualMark").disabled = true;
document.getElementById("manualMarkMode").disabled = true;
return;
}
document.getElementById("useWhiteList").disabled = false;
document.getElementById("moveOnSpam").disabled = false;
document.getElementById("manualMark").disabled = false;
var enabled;
if (!id || id == "manualMark") {
enabled = document.getElementById("manualMark").checked;
// need to enable manualMarkMode before we enable manualMarkMode0
document.getElementById("manualMarkMode").disabled = !enabled;
}
if (!id || id == "moveOnSpam") {
enabled = document.getElementById("moveOnSpam").checked;
var choice = document.getElementById("moveTargetMode").value;
document.getElementById("moveTargetMode").disabled = !enabled;
document.getElementById("actionTargetAccount").disabled = !enabled || (choice == 1);
document.getElementById("actionTargetFolder").disabled = !enabled || (choice == 0);
var checked = document.getElementById("purge").checked;
document.getElementById("purge").disabled = !enabled;
document.getElementById("purgeInterval").disabled = !enabled || !checked;
document.getElementById("purgeLabel").disabled = !enabled;
if (!document.getElementById("manualMarkMode").disabled)
document.getElementById("manualMarkMode0").disabled = !enabled;
}
if (id == "purge") {
enabled = document.getElementById("purge").checked;
document.getElementById("purgeInterval").disabled = !enabled;
}
if (!id || id == "useWhiteList") {
enabled = document.getElementById("useWhiteList").checked;
document.getElementById("whiteListAbURI").disabled = !enabled;
}
}
function doHelpButton()
{
openHelp("mail-junk-controls");
}

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

@ -150,6 +150,8 @@
</hbox>
</vbox>
<checkbox id="markAsReadOnSpam" oncommand="conditionallyEnableUI('markAsReadOnSpam')" label="&markAsReadOnSpam.label;"/>
<vbox align="start">
<checkbox id="manualMark" oncommand="conditionallyEnableUI('manualMark')" label="&manualMark.label;"/>
<hbox>

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

@ -611,7 +611,7 @@ var DefaultController =
MsgApplyFilters(null);
return;
case "cmd_runJunkControls":
analyzeFolderForJunk();
filterFolderForJunk();
return;
case "cmd_deleteJunk":
deleteJunkInFolder();

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

@ -480,68 +480,71 @@ const nsIJunkMailPlugin = Components.interfaces.nsIJunkMailPlugin;
const nsIMsgDBHdr = Components.interfaces.nsIMsgDBHdr;
var gJunkmailComponent;
var gJunkKeys = [];
var gJunkTargetFolder;
function saveJunkMsgForAction(aServer, aMsgURI, aClassification)
function determineActionsForJunkMsgs(aView, aIndices, aActionParams)
{
// we only care when the message gets marked as junk
if (aClassification == nsIJunkMailPlugin.GOOD)
// we use some arbitrary message to determine the
// message server
var msgURI = aView.getURIForViewIndex(aIndices[0]);
var msgHdr = messenger.messageServiceFromURI(msgURI).messageURIToMsgHdr(msgURI);
var server = msgHdr.folder.server;
var spamSettings = server.spamSettings;
// note we will do moves/marking as read even if the spam
// feature is disabled, since the user has asked to use it
// despite the disabling
// note also that we will only act on messages which
// _the_current_run_ of the classifier has classified as
// junk, rather than on all junk messages in the folder
aActionParams.markRead = spamSettings.markAsReadOnSpam;
aActionParams.junkTargetFolder = null;
if (!spamSettings.moveOnSpam)
return;
var spamSettings = aServer.spamSettings
// if the spam feature is disabled,
// or if the move functionality is turned off, bail out.
// the user could still run the JMC manually,
// but let's not move in that scenario
if (!spamSettings.level || !spamSettings.moveOnSpam)
return;
var msgHdr = messenger.messageServiceFromURI(aMsgURI).messageURIToMsgHdr(aMsgURI);
// don't move if we are already in the junk folder
if (msgHdr.folder.flags & MSG_FOLDER_FLAG_JUNK)
return;
var spamFolderURI = spamSettings.spamFolderURI;
if (!spamFolderURI)
return;
var spamFolder = GetMsgFolderFromUri(spamFolderURI);
if (spamFolder)
{
gJunkKeys[gJunkKeys.length] = msgHdr.messageKey;
gJunkTargetFolder = spamFolder;
dump('no spam folder!');
return;
}
aActionParams.junkTargetFolder = GetMsgFolderFromUri(spamFolderURI);
}
function performActionOnJunkMsgs()
function performActionsOnJunkMsgs(aIndices)
{
if (!gJunkKeys.length)
{
gJunkTargetFolder = [];
return;
}
var indices = new Array(gJunkKeys.length);
for (var i=0;i<gJunkKeys.length;i++)
indices[i] = gDBView.findIndexFromKey(gJunkKeys[i], true /* expand */);
var treeView = gDBView.QueryInterface(Components.interfaces.nsITreeView);
var actionParams = { markRead: false, junkTargetFolder: null };
determineActionsForJunkMsgs(treeView,aIndices,actionParams);
if (!aIndices.length)
return;
var treeSelection = treeView.selection;
treeSelection.clearSelection();
// select the messages
for (i=0;i<indices.length;i++)
treeSelection.rangedSelect(indices[i], indices[i], true /* augment */);
for (i=0;i<aIndices.length;i++)
treeSelection.rangedSelect(aIndices[i], aIndices[i], true /* augment */);
SetNextMessageAfterDelete();
gDBView.doCommandWithFolder(nsMsgViewCommandType.moveMessages, gJunkTargetFolder);
gJunkKeys = [];
gJunkTargetFolder = null;
if (actionParams.markRead)
MarkSelectedMessagesRead(true);
if (actionParams.junkTargetFolder)
{
SetNextMessageAfterDelete();
gDBView.doCommandWithFolder(nsMsgViewCommandType.moveMessages, actionParams.junkTargetFolder);
}
treeSelection.clearSelection();
}
function getJunkmailComponent()
@ -551,19 +554,19 @@ function getJunkmailComponent()
}
}
function analyze(aMsgHdr, aNextFunction)
function analyzeMessageForJunk(aMsgHdr, aMsgIndex, aJunkMsgIndices, aLastMessage, aWhiteListDirectory)
{
var listener = {
onMessageClassified: function(aMsgURI, aClassification)
onMessageClassified: function(aClassifiedMsgURI, aClassification)
{
// XXX todo
// XXX TODO
// update status bar, or a progress dialog
// running junk mail controls manually, on a large folder
// can take a while, and the user doesn't know when we are done.
dump(aMsgURI + ' is '
+ (aClassification == nsIJunkMailPlugin.JUNK
? 'JUNK' : 'GOOD') + '\n');
// XXX TODO, make the cut off 50, like in nsMsgSearchTerm.cpp
// XXX TODO
// make the cut off 50, like in nsMsgSearchTerm.cpp
var score =
aClassification == nsIJunkMailPlugin.JUNK ? "100" : "0";
@ -574,26 +577,28 @@ function analyze(aMsgHdr, aNextFunction)
db.setStringProperty(aMsgHdr.messageKey, "junkscore", score);
db.setStringProperty(aMsgHdr.messageKey, "junkscoreorigin",
"plugin");
saveJunkMsgForAction(aMsgHdr.folder.server, aMsgURI, aClassification);
aNextFunction();
if (aClassification == nsIJunkMailPlugin.JUNK)
aJunkMsgIndices.push(aMsgIndex);
if (aLastMessage)
{
gJunkmailComponent.endBatch();
performActionsOnJunkMsgs(aJunkMsgIndices);
}
}
};
// if we are whitelisting, check if the email address is in the whitelist addressbook.
var spamSettings = aMsgHdr.folder.server.spamSettings;
if (spamSettings.useWhiteList && spamSettings.whiteListAbURI)
if (aWhiteListDirectory)
{
var whiteListDirectory = RDF.GetResource(spamSettings.whiteListAbURI).QueryInterface(Components.interfaces.nsIAbMDBDirectory);
var headerParser = Components.classes["@mozilla.org/messenger/headerparser;1"].getService(Components.interfaces.nsIMsgHeaderParser);
var authorEmailAddress = headerParser.extractHeaderAddressMailboxes(null, aMsgHdr.author);
if (whiteListDirectory.hasCardForEmailAddress(authorEmailAddress))
{
if (aWhiteListDirectory.hasCardForEmailAddress(authorEmailAddress))
// skip over this message, like we do on incoming mail
// the difference is it could be marked as junk from previous analysis
// or from being manually marked by the user.
aNextFunction();
return;
}
}
var messageURI = aMsgHdr.folder.generateMessageURI(aMsgHdr.messageKey)
@ -601,7 +606,7 @@ function analyze(aMsgHdr, aNextFunction)
gJunkmailComponent.classifyMessage(messageURI, msgWindow, listener);
}
function analyzeFolderForJunk()
function filterFolderForJunk()
{
MsgJunkMailInfo(true);
var view = GetDBView();
@ -614,42 +619,24 @@ function analyzeFolderForJunk()
if (!count)
return;
var messages = new Array(count)
var junkMsgsArray = new Array;
var tmpMsgURI = view.getURIForViewIndex(0);
var tmpMsgHdr = messenger.messageServiceFromURI(tmpMsgURI).messageURIToMsgHdr(tmpMsgURI);
var spamSettings = tmpMsgHdr.folder.server.spamSettings;
var whiteListDirectory;
if (spamSettings.useWhiteList && spamSettings.whiteListAbURI)
whiteListDirectory = RDF.GetResource(spamSettings.whiteListAbURI).QueryInterface(Components.interfaces.nsIAbMDBDirectory);
getJunkmailComponent();
gJunkmailComponent.startBatch();
for (var i = 0; i < count; i++) {
messages[i] = view.getURIForViewIndex(i);
var msgIndex = i;
var msgURI = view.getURIForViewIndex(i);
var msgHdr = messenger.messageServiceFromURI(msgURI).messageURIToMsgHdr(msgURI);
analyzeMessageForJunk(msgHdr,msgIndex,junkMsgsArray,(i == count-1),whiteListDirectory);
}
analyzeMessages(messages);
}
// not used yet, but soon
function analyzeMessagesForJunk()
{
var messages = GetSelectedMessages();
analyzeMessages(messages);
}
function analyzeMessages(messages)
{
function processNext()
{
if (counter < messages.length) {
var messageUri = messages[counter];
var message = messenger.messageServiceFromURI(messageUri).messageURIToMsgHdr(messageUri);
++counter;
analyze(message, processNext);
}
else {
dump('[bayesian filter message analysis complete.]\n');
gJunkmailComponent.endBatch();
performActionOnJunkMsgs();
}
}
getJunkmailComponent();
var counter = 0;
gJunkmailComponent.startBatch();
dump('[bayesian filter message analysis begins.]\n');
processNext();
}
function JunkSelectedMessages(setAsJunk)
@ -657,6 +644,9 @@ function JunkSelectedMessages(setAsJunk)
MsgJunkMailInfo(true);
gDBView.doCommand(setAsJunk ? nsMsgViewCommandType.junk
: nsMsgViewCommandType.unjunk);
// XXX TODO
// consider whether these messages should be
// marked as read (if markAsReadOnSpam is set)
}
function deleteJunkInFolder()

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

@ -1,20 +0,0 @@
<!ENTITY window.title "Junk Mail Controls">
<!ENTITY account.label "Account:">
<!ENTITY account.accesskey "A">
<!ENTITY viewLog.label "Junk Mail Log">
<!ENTITY viewLog.accesskey "j">
<!ENTITY info1.label "Junk mail controls evaluate your incoming messages and identify those that are most likely to be junk mail, or unsolicited mail. A junk icon is displayed if the message is identified as junk mail.">
<!ENTITY info2.label "Junk mail controls can be fine-tuned by using the Junk Mail toolbar button to mark junk messages appropriately.">
<!ENTITY level.label "Enable junk mail controls">
<!ENTITY level.accesskey "E">
<!ENTITY move.label "Move incoming messages determined to be junk mail to:">
<!ENTITY otherFolder.label "Other:">
<!ENTITY junkFolderOn.label "&quot;Junk&quot; folder on:">
<!ENTITY purge1.label "Automatically delete junk messages older than">
<!ENTITY purge1.accesskey "u">
<!ENTITY purge2.label "days from this folder">
<!ENTITY whitelist.label "Do not mark messages as junk mail if the sender is in my address book:">
<!ENTITY whitelist.accesskey "D">
<!ENTITY manualMark.label "When I manually mark messages as Junk:">
<!ENTITY manualMarkModeMove.label "Move them to the &quot;Junk&quot; folder">
<!ENTITY manualMarkModeDelete.label "Delete them">

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

@ -141,8 +141,9 @@ nsMsgDBView::nsMsgDBView()
mIsNews = PR_FALSE;
mDeleteModel = nsMsgImapDeleteModels::MoveToTrash;
m_deletingRows = PR_FALSE;
mOutstandingJunkBatches = 0;
mJunkIndices = nsnull;
mNumJunkIndices = 0;
/* mCommandsNeedDisablingBecauseOffline - A boolean that tell us if we needed to disable commands because we're offline w/o a downloaded msg select */
mCommandsNeedDisablingBecauseOffline = PR_FALSE;
@ -2229,185 +2230,206 @@ nsresult
nsMsgDBView::ApplyCommandToIndices(nsMsgViewCommandTypeValue command, nsMsgViewIndex* indices,
PRInt32 numIndices)
{
nsresult rv = NS_OK;
nsMsgKeyArray imapUids;
// if numIndices == 0, return quietly, just in case
if (numIndices == 0)
return NS_OK;
NS_ASSERTION(numIndices >= 0, "nsMsgDBView::ApplyCommandToIndices(): "
"numIndices is negative!");
nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(m_folder);
PRBool thisIsImapFolder = (imapFolder != nsnull);
if (numIndices == 0)
return NS_OK; // return quietly, just in case
if (command == nsMsgViewCommandType::deleteMsg)
rv = DeleteMessages(mMsgWindow, indices, numIndices, PR_FALSE);
else if (command == nsMsgViewCommandType::deleteNoTrash)
rv = DeleteMessages(mMsgWindow, indices, numIndices, PR_TRUE);
else
return DeleteMessages(mMsgWindow, indices, numIndices, PR_FALSE);
if (command == nsMsgViewCommandType::deleteNoTrash)
return DeleteMessages(mMsgWindow, indices, numIndices, PR_TRUE);
nsMsgKeyArray imapUids;
nsresult rv = NS_OK;
nsCOMPtr <nsIMsgImapMailFolder> imapFolder = do_QueryInterface(m_folder);
PRBool thisIsImapFolder = (imapFolder != nsnull);
nsCOMPtr<nsIJunkMailPlugin> junkPlugin;
// if this is a junk command, start a batch.
//
if ( command == nsMsgViewCommandType::junk
|| command == nsMsgViewCommandType::unjunk )
{
nsCOMPtr<nsIJunkMailPlugin> junkPlugin;
// get the folder from the first item; we assume that
// all messages in the view are from the same folder (no
// more junk status column in the 'search messages' dialog
// like in earlier versions...)
//
nsCOMPtr<nsIMsgFolder> folder;
rv = GetFolderForViewIndex(indices[0], getter_AddRefs(folder));
NS_ENSURE_SUCCESS(rv, rv);
// if this is a junk command, start a batch. The batch will be ended
// in the last callback.
//
if ( command == nsMsgViewCommandType::junk
|| command == nsMsgViewCommandType::unjunk )
nsCOMPtr<nsIMsgIncomingServer> server;
rv = folder->GetServer(getter_AddRefs(server));
NS_ENSURE_SUCCESS(rv, rv);
if (command == nsMsgViewCommandType::junk)
{
// get the folder from the first item (if it's the search view,
// only one item can be touched at a time; if a regular folder view,
// all items will have the same folder).
//
nsCOMPtr<nsIMsgFolder> folder;
rv = GetFolderForViewIndex(indices[0], getter_AddRefs(folder));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMsgIncomingServer> server;
rv = folder->GetServer(getter_AddRefs(server));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMsgFilterPlugin> filterPlugin;
rv = server->GetSpamFilterPlugin(getter_AddRefs(filterPlugin));
NS_ENSURE_SUCCESS(rv, rv);
junkPlugin = do_QueryInterface(filterPlugin, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = junkPlugin->StartBatch();
NS_ENSURE_SUCCESS(rv, rv);
mOutstandingJunkBatches++;
// append this batch of junk message indices to the
// array of junk message indices to be acted upon
// once OnMessageClassified() is run for the last message
//
// note: although message classification is done
// asynchronously, it is not done in a different thread,
// so the manipulations of mJunkIndices here and in
// OnMessageClassified() cannot interrupt each other
//
mNumJunkIndices += numIndices;
mJunkIndices = (nsMsgViewIndex *)nsMemory::Realloc(mJunkIndices, mNumJunkIndices * sizeof(nsMsgViewIndex));
memcpy(mJunkIndices + (mNumJunkIndices - numIndices), indices, numIndices * sizeof(nsMsgViewIndex));
// save the last URI, so that OnMessageClassified()
// will know when the classification it runs after
// is the last one; if the classification of previously-marked
// messages has not been completed, we replace here the
// previous 'last URI' with a new 'last URI',
// causing the batches to be coalesced
//
rv = GetURIForViewIndex(indices[numIndices-1], getter_Copies(mLastJunkURIInBatch));
NS_ENSURE_SUCCESS(rv, rv);
}
m_folder->EnableNotifications(nsIMsgFolder::allMessageCountNotifications, PR_FALSE, PR_TRUE /*dbBatching*/);
nsCOMPtr<nsIMsgFilterPlugin> filterPlugin;
rv = server->GetSpamFilterPlugin(getter_AddRefs(filterPlugin));
NS_ENSURE_SUCCESS(rv, rv);
for (int32 i = 0; i < numIndices; i++)
junkPlugin = do_QueryInterface(filterPlugin, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = junkPlugin->StartBatch();
NS_ENSURE_SUCCESS(rv, rv);
}
m_folder->EnableNotifications(nsIMsgFolder::allMessageCountNotifications, PR_FALSE, PR_TRUE /*dbBatching*/);
for (int32 i = 0; i < numIndices; i++)
{
if (thisIsImapFolder && command != nsMsgViewCommandType::markThreadRead)
imapUids.Add(GetAt(indices[i]));
switch (command)
{
if (thisIsImapFolder && command != nsMsgViewCommandType::markThreadRead)
imapUids.Add(GetAt(indices[i]));
switch (command)
{
case nsMsgViewCommandType::markMessagesRead:
rv = SetReadByIndex(indices[i], PR_TRUE);
break;
case nsMsgViewCommandType::markMessagesUnread:
rv = SetReadByIndex(indices[i], PR_FALSE);
break;
case nsMsgViewCommandType::toggleMessageRead:
rv = ToggleReadByIndex(indices[i]);
break;
case nsMsgViewCommandType::flagMessages:
rv = SetFlaggedByIndex(indices[i], PR_TRUE);
break;
case nsMsgViewCommandType::unflagMessages:
rv = SetFlaggedByIndex(indices[i], PR_FALSE);
break;
case nsMsgViewCommandType::markThreadRead:
rv = SetThreadOfMsgReadByIndex(indices[i], imapUids, PR_TRUE);
break;
case nsMsgViewCommandType::label0:
case nsMsgViewCommandType::label1:
case nsMsgViewCommandType::label2:
case nsMsgViewCommandType::label3:
case nsMsgViewCommandType::label4:
case nsMsgViewCommandType::label5:
rv = SetLabelByIndex(indices[i], (command - nsMsgViewCommandType::label0));
break;
case nsMsgViewCommandType::junk:
rv = SetJunkScoreByIndex(junkPlugin.get(), indices[i],
nsIJunkMailPlugin::JUNK, (i == numIndices-1));
break;
case nsMsgViewCommandType::unjunk:
rv = SetJunkScoreByIndex(junkPlugin.get(), indices[i],
nsIJunkMailPlugin::GOOD, (i == numIndices-1));
break;
case nsMsgViewCommandType::undeleteMsg:
break; // this is completely handled in the imap code below.
default:
NS_ASSERTION(PR_FALSE, "unhandled command");
break;
}
}
m_folder->EnableNotifications(nsIMsgFolder::allMessageCountNotifications, PR_TRUE, PR_TRUE /*dbBatching*/);
if (thisIsImapFolder)
{
imapMessageFlagsType flags = kNoImapMsgFlag;
PRBool commandIsLabelSet = PR_FALSE;
PRBool addFlags = PR_FALSE;
PRBool isRead = PR_FALSE;
switch (command)
{
case nsMsgViewCommandType::markThreadRead:
case nsMsgViewCommandType::markMessagesRead:
flags |= kImapMsgSeenFlag;
addFlags = PR_TRUE;
break;
case nsMsgViewCommandType::markMessagesUnread:
flags |= kImapMsgSeenFlag;
addFlags = PR_FALSE;
break;
case nsMsgViewCommandType::toggleMessageRead:
{
flags |= kImapMsgSeenFlag;
m_db->IsRead(GetAt(indices[0]), &isRead);
if (isRead)
addFlags = PR_TRUE;
else
addFlags = PR_FALSE;
}
break;
case nsMsgViewCommandType::flagMessages:
flags |= kImapMsgFlaggedFlag;
addFlags = PR_TRUE;
break;
case nsMsgViewCommandType::unflagMessages:
flags |= kImapMsgFlaggedFlag;
addFlags = PR_FALSE;
break;
case nsMsgViewCommandType::label0:
case nsMsgViewCommandType::label1:
case nsMsgViewCommandType::label2:
case nsMsgViewCommandType::label3:
case nsMsgViewCommandType::label4:
case nsMsgViewCommandType::label5:
flags |= ((command - nsMsgViewCommandType::label0) << 9);
addFlags = (command != nsMsgViewCommandType::label0);
commandIsLabelSet = PR_TRUE;
break;
case nsMsgViewCommandType::undeleteMsg:
flags = kImapMsgDeletedFlag;
addFlags = PR_FALSE;
break;
case nsMsgViewCommandType::junk:
return imapFolder->StoreCustomKeywords(mMsgWindow,
"Junk",
"NonJunk",
imapUids.GetArray(), imapUids.GetSize(),
nsnull);
case nsMsgViewCommandType::unjunk:
return imapFolder->StoreCustomKeywords(mMsgWindow,
"NonJunk",
"Junk",
imapUids.GetArray(), imapUids.GetSize(),
nsnull);
default:
break;
}
if (flags != kNoImapMsgFlag || commandIsLabelSet) // can't get here without thisIsImapThreadPane == TRUE
imapFolder->StoreImapFlags(flags, addFlags, imapUids.GetArray(), imapUids.GetSize());
case nsMsgViewCommandType::markMessagesRead:
rv = SetReadByIndex(indices[i], PR_TRUE);
break;
case nsMsgViewCommandType::markMessagesUnread:
rv = SetReadByIndex(indices[i], PR_FALSE);
break;
case nsMsgViewCommandType::toggleMessageRead:
rv = ToggleReadByIndex(indices[i]);
break;
case nsMsgViewCommandType::flagMessages:
rv = SetFlaggedByIndex(indices[i], PR_TRUE);
break;
case nsMsgViewCommandType::unflagMessages:
rv = SetFlaggedByIndex(indices[i], PR_FALSE);
break;
case nsMsgViewCommandType::markThreadRead:
rv = SetThreadOfMsgReadByIndex(indices[i], imapUids, PR_TRUE);
break;
case nsMsgViewCommandType::label0:
case nsMsgViewCommandType::label1:
case nsMsgViewCommandType::label2:
case nsMsgViewCommandType::label3:
case nsMsgViewCommandType::label4:
case nsMsgViewCommandType::label5:
rv = SetLabelByIndex(indices[i], (command - nsMsgViewCommandType::label0));
break;
case nsMsgViewCommandType::junk:
rv = SetAsJunkByIndex(junkPlugin.get(), indices[i],
nsIJunkMailPlugin::JUNK);
break;
case nsMsgViewCommandType::unjunk:
rv = SetAsJunkByIndex(junkPlugin.get(), indices[i],
nsIJunkMailPlugin::GOOD);
break;
case nsMsgViewCommandType::undeleteMsg:
break; // this is completely handled in the imap code below.
default:
NS_ASSERTION(PR_FALSE, "unhandled command");
break;
}
}
m_folder->EnableNotifications(nsIMsgFolder::allMessageCountNotifications, PR_TRUE, PR_TRUE /*dbBatching*/);
if (thisIsImapFolder)
{
imapMessageFlagsType flags = kNoImapMsgFlag;
PRBool commandIsLabelSet = PR_FALSE;
PRBool addFlags = PR_FALSE;
PRBool isRead = PR_FALSE;
switch (command)
{
case nsMsgViewCommandType::markThreadRead:
case nsMsgViewCommandType::markMessagesRead:
flags |= kImapMsgSeenFlag;
addFlags = PR_TRUE;
break;
case nsMsgViewCommandType::markMessagesUnread:
flags |= kImapMsgSeenFlag;
addFlags = PR_FALSE;
break;
case nsMsgViewCommandType::toggleMessageRead:
{
flags |= kImapMsgSeenFlag;
m_db->IsRead(GetAt(indices[0]), &isRead);
if (isRead)
addFlags = PR_TRUE;
else
addFlags = PR_FALSE;
}
break;
case nsMsgViewCommandType::flagMessages:
flags |= kImapMsgFlaggedFlag;
addFlags = PR_TRUE;
break;
case nsMsgViewCommandType::unflagMessages:
flags |= kImapMsgFlaggedFlag;
addFlags = PR_FALSE;
break;
case nsMsgViewCommandType::label0:
case nsMsgViewCommandType::label1:
case nsMsgViewCommandType::label2:
case nsMsgViewCommandType::label3:
case nsMsgViewCommandType::label4:
case nsMsgViewCommandType::label5:
flags |= ((command - nsMsgViewCommandType::label0) << 9);
addFlags = (command != nsMsgViewCommandType::label0);
commandIsLabelSet = PR_TRUE;
break;
case nsMsgViewCommandType::undeleteMsg:
flags = kImapMsgDeletedFlag;
addFlags = PR_FALSE;
break;
case nsMsgViewCommandType::junk:
return imapFolder->StoreCustomKeywords(mMsgWindow,
"Junk",
"NonJunk",
imapUids.GetArray(), imapUids.GetSize(),
nsnull);
case nsMsgViewCommandType::unjunk:
return imapFolder->StoreCustomKeywords(mMsgWindow,
"NonJunk",
"Junk",
imapUids.GetArray(), imapUids.GetSize(),
nsnull);
default:
break;
}
if (flags != kNoImapMsgFlag || commandIsLabelSet) // can't get here without thisIsImapThreadPane == TRUE
imapFolder->StoreImapFlags(flags, addFlags, imapUids.GetArray(), imapUids.GetSize());
}
return rv;
}
// view modifications methods by index
// This method just removes the specified line from the view. It does
@ -2621,12 +2643,10 @@ nsresult nsMsgDBView::SetStringPropertyByIndex(nsMsgViewIndex index, const char
return rv;
}
nsresult nsMsgDBView::SetJunkScoreByIndex(nsIJunkMailPlugin *aJunkPlugin,
nsresult nsMsgDBView::SetAsJunkByIndex(nsIJunkMailPlugin *aJunkPlugin,
nsMsgViewIndex aIndex,
nsMsgJunkStatus aNewClassification,
PRBool aIsLastInBatch)
nsMsgJunkStatus aNewClassification)
{
// get the message header (need this to get string properties)
//
nsCOMPtr <nsIMsgDBHdr> msgHdr;
@ -2667,13 +2687,6 @@ nsresult nsMsgDBView::SetJunkScoreByIndex(nsIJunkMailPlugin *aJunkPlugin,
rv = GetURIForViewIndex(aIndex, getter_Copies(uri));
NS_ENSURE_SUCCESS(rv, rv);
if ( aIsLastInBatch ) {
// if there's already a batch in progress, just replace the URI,
// thus causing the batches to be coalesced
//
mLastJunkUriInBatch = uri;
}
// tell the plugin about this change, so that it can (potentially)
// adjust its database appropriately
//
@ -2709,106 +2722,185 @@ NS_IMETHODIMP
nsMsgDBView::OnMessageClassified(const char *aMsgURI,
nsMsgJunkStatus aClassification)
{
// we can't just use m_folder
// as this might be from a cross folder search
// see bug #180477
nsCOMPtr <nsIMsgFolder> folder;
nsresult rv = GetFolderFromMsgURI(aMsgURI, getter_AddRefs(folder));
NS_ENSURE_SUCCESS(rv,rv);
// Note: we know all messages in a batch have the same
// classification, since unlike OnMessageClassified
// methods in other classes (such as nsLocalMailFolder
// and nsImapMailFolder), this class, nsMsgDBView, currently
// only triggers message classifications due to a command to
// mark some of the messages in the view as junk, or as not
// junk - so the classification is dictated to the filter,
// not suggested by it.
//
// for this reason the only thing we (may) have to do is
// perform the action on all of the junk messages
//
// this check is necessary because it is theoretically
// possible for a message to be flagged as non-junk,
// and before the classifier can finish with it, for it
// to be again classified as junk, and thus to have
// mLastJunkURIInBatch set with its URI - and it should
// not be considered the last junk messages during
// the first call of this function
if (aClassification == nsIJunkMailPlugin::GOOD)
return NS_OK;
NS_ASSERTION(mJunkIndices != nsnull, "the classification of a manually-marked junk message has been classified as junk, yet there seem to be no such outstanding messages");
nsCOMPtr<nsIMsgIncomingServer> server;
rv = folder->GetServer(getter_AddRefs(server));
NS_ENSURE_SUCCESS(rv, rv);
if ( mLastJunkURIInBatch.Equals(aMsgURI) )
{
nsCOMPtr<nsIMsgFolder> folder;
nsresult rv = GetFolderForViewIndex(mJunkIndices[0], getter_AddRefs(folder));
NS_ENSURE_SUCCESS(rv, rv);
// save off the msg hdr, if we need to
rv = SaveJunkMsgForAction(server, aMsgURI, aClassification);
NS_ENSURE_SUCCESS(rv,rv);
// it seems EndBatch must be called here, rather
// than after the last message has been dispatched for
// classification. One would think when the UI tells the
// classifier "I am done with sending you this batch of
// messages" the classifer should say "ok, now as soon
// as I finish classification I should flush my data file"
// ... but that's not the way it works. If that were
// to change you could move the EndBatch() call to
// ApplyCommandToIndices and avoid the redundant
// lines of code which get the pointer to the junk plugin
// object.
nsCOMPtr<nsIMsgIncomingServer> server;
rv = folder->GetServer(getter_AddRefs(server));
NS_ENSURE_SUCCESS(rv, rv);
// is this the last url in the batch?
if (mLastJunkUriInBatch.Equals(aMsgURI))
{
// get the filter, and QI to the interface we want
nsCOMPtr<nsIMsgFilterPlugin> filterPlugin;
rv = server->GetSpamFilterPlugin(getter_AddRefs(filterPlugin));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIJunkMailPlugin> junkPlugin = do_QueryInterface(filterPlugin, &rv);
nsCOMPtr<nsIJunkMailPlugin> junkPlugin;
junkPlugin = do_QueryInterface(filterPlugin, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = junkPlugin->EndBatch();
NS_ENSURE_SUCCESS(rv, rv);
// close out existing coalesced junk batches
for ( ; mOutstandingJunkBatches > 0 ; --mOutstandingJunkBatches )
{
// tell the plugin that all outstanding batches from us
// have finished.
rv = junkPlugin->EndBatch();
NS_ENSURE_SUCCESS(rv, rv);
if ( mNumJunkIndices > 0 )
{
PerformActionsOnJunkMsgs();
nsMemory::Free(mJunkIndices);
mJunkIndices = nsnull;
mNumJunkIndices = 0;
mLastJunkURIInBatch.Truncate();
}
rv = PerformActionOnJunkMsgs();
NS_ENSURE_SUCCESS(rv,rv);
}
return NS_OK;
}
nsresult
nsMsgDBView::PerformActionOnJunkMsgs()
nsMsgDBView::PerformActionsOnJunkMsgs()
{
PRUint32 numIndices = mJunkKeys.GetSize();
// nothing to do, bail out
if (!numIndices)
{
mJunkTargetFolder = nsnull; // just to be safe
return NS_OK;
}
PRBool movingJunkMessages,markingJunkMessagesRead;
nsCOMPtr <nsIMsgFolder> junkTargetFolder;
nsMsgViewIndex *indices = (nsMsgViewIndex *)nsMemory::Alloc(numIndices * sizeof(nsMsgViewIndex));
if (!indices)
return NS_ERROR_OUT_OF_MEMORY;
for (PRUint32 i=0;i<numIndices;i++)
indices[i] = FindKey(mJunkKeys.GetAt(i), PR_TRUE /* expand */); // what if we don't find the index?
// tell the FE to call SetNextMessageAfterDelete() because a delete is coming
nsresult rv = mCommandUpdater->UpdateNextMessageAfterDelete();
NS_ENSURE_SUCCESS(rv,rv);
// question: is it possible for the junk mail move/mark as read
// options to change after we've handled some of the batches but
// before we've handled the last one? if so, we can decide when
// handling each batch whether to save its indices or forget
// them, and then perform the known action when handling the
// last batch; however if the options can change between batches
// we may have to remember in separate arrays the indices to
// mark as read and the indices to move
//
// for now, we assume the options do not change between batches
//
if (numIndices > 1)
NS_QuickSort(indices, numIndices, sizeof(nsMsgViewIndex), CompareViewIndices, nsnull);
NoteStartChange(nsMsgViewNotificationCode::none, 0, 0);
if (mJunkTargetFolder)
rv = ApplyCommandToIndicesWithFolder(nsMsgViewCommandType::moveMessages, indices, numIndices, mJunkTargetFolder);
else
rv = ApplyCommandToIndices(nsMsgViewCommandType::deleteMsg, indices, numIndices);
NoteEndChange(nsMsgViewNotificationCode::none, 0, 0);
nsresult rv = DetermineActionsForJunkMsgs(&movingJunkMessages, &markingJunkMessagesRead, getter_AddRefs(junkTargetFolder));
NS_ENSURE_SUCCESS(rv,rv);
// nothing to do, bail out
if (!(movingJunkMessages || markingJunkMessagesRead))
return NS_OK;
mJunkKeys.RemoveAll();
mJunkTargetFolder = nsnull;
nsMemory::Free(indices);
NS_ASSERTION( (mNumJunkIndices > 0), "no indices of marked-as-junk messages to act on");
NS_ASSERTION(NS_SUCCEEDED(rv), "move or delete failed");
if (mNumJunkIndices > 1)
NS_QuickSort(mJunkIndices, mNumJunkIndices, sizeof(nsMsgViewIndex), CompareViewIndices, nsnull);
if (markingJunkMessagesRead)
{
// notes on marking junk as read:
// 1. there are 2 occasions on which junk messages are marked as
// read: here (after a manual marking) as well as after automatic
// marking by the bayesian filter (see code for local mail folders
// and for imap mail folders); it is perhaps worth considering
// making these two separate options... but for now they are
// controlled by a single preference
// 2. even though move/delete on manual mark may be
// turned off, we might still need to mark as read
NoteStartChange(nsMsgViewNotificationCode::none, 0, 0);
rv = ApplyCommandToIndices(nsMsgViewCommandType::markMessagesRead, mJunkIndices, mNumJunkIndices);
NoteEndChange(nsMsgViewNotificationCode::none, 0, 0);
NS_ASSERTION(NS_SUCCEEDED(rv), "marking marked-as-junk messages as read failed");
}
if (movingJunkMessages)
{
// tell the FE to call SetNextMessageAfterDelete() because a delete is coming
rv = mCommandUpdater->UpdateNextMessageAfterDelete();
NS_ENSURE_SUCCESS(rv,rv);
NoteStartChange(nsMsgViewNotificationCode::none, 0, 0);
if (junkTargetFolder)
rv = ApplyCommandToIndicesWithFolder(nsMsgViewCommandType::moveMessages, mJunkIndices, mNumJunkIndices, junkTargetFolder);
else
rv = ApplyCommandToIndices(nsMsgViewCommandType::deleteMsg, mJunkIndices, mNumJunkIndices);
NoteEndChange(nsMsgViewNotificationCode::none, 0, 0);
NS_ASSERTION(NS_SUCCEEDED(rv), "move or deletion of marked-as-junk messages failed");
}
return rv;
}
nsresult
nsMsgDBView::SaveJunkMsgForAction(nsIMsgIncomingServer *aServer, const char *aMsgURI, nsMsgJunkStatus aClassification)
nsMsgDBView::DetermineActionsForJunkMsgs(PRBool* movingJunkMessages, PRBool* markingJunkMessagesRead, nsIMsgFolder** junkTargetFolder)
{
// we only care when the message gets marked as junk
if (aClassification == nsIJunkMailPlugin::GOOD)
return NS_OK;
// there are two possible actions which may be performed
// on messages marked as spam: marking as read and moving
// somewhere...
*movingJunkMessages = false;
*markingJunkMessagesRead = false;
// ... the 'somewhere', junkTargetFolder, can be a folder,
// but if it remains null we'll delete the messages
*junkTargetFolder = nsnull;
nsCOMPtr<nsIMsgFolder> folder;
nsresult rv = GetFolderForViewIndex(mJunkIndices[0], getter_AddRefs(folder));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMsgIncomingServer> server;
rv = folder->GetServer(getter_AddRefs(server));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr <nsISpamSettings> spamSettings;
nsresult rv = aServer->GetSpamSettings(getter_AddRefs(spamSettings));
rv = server->GetSpamSettings(getter_AddRefs(spamSettings));
NS_ENSURE_SUCCESS(rv, rv);
// if the spam feature is disabled, do nothing
// the user could still manually mark spam if the feature is disabled
// but let's not move or delete in that scenario
// if the spam system is completely disabled we won't do anything
// question: is this a valid choice?
PRInt32 spamLevel;
(void)spamSettings->GetLevel(&spamLevel);
if (!spamLevel)
return NS_OK;
// if the manual mark functionality is turned off, bail out.
// now let's determine whether we'll be taking the first action,
// marking as read
(void)spamSettings->GetMarkAsReadOnSpam(markingJunkMessagesRead);
// now let's determine whether we'll be taking the second action,
// the move / deletion (and also determine which of these two)
PRBool manualMark;
(void)spamSettings->GetManualMark(&manualMark);
if (!manualMark)
@ -2816,28 +2908,14 @@ nsMsgDBView::SaveJunkMsgForAction(nsIMsgIncomingServer *aServer, const char *aMs
PRInt32 manualMarkMode;
(void)spamSettings->GetManualMarkMode(&manualMarkMode);
NS_ASSERTION(manualMarkMode == nsISpamSettings::MANUAL_MARK_MODE_MOVE
|| manualMarkMode == nsISpamSettings::MANUAL_MARK_MODE_DELETE,
"bad manual mark mode");
nsCOMPtr <nsIMsgMessageService> msgMessageService;
rv = GetMessageServiceFromURI(aMsgURI, getter_AddRefs(msgMessageService));
NS_ENSURE_SUCCESS(rv,rv);
nsCOMPtr <nsIMsgDBHdr> msgHdr;
rv = msgMessageService->MessageURIToMsgHdr(aMsgURI, getter_AddRefs(msgHdr));
NS_ENSURE_SUCCESS(rv,rv);
nsCOMPtr <nsIMsgFolder> srcFolder;
rv = msgHdr->GetFolder(getter_AddRefs(srcFolder));
NS_ENSURE_SUCCESS(rv,rv);
nsMsgKey msgKey;
rv = msgHdr->GetMessageKey(&msgKey);
NS_ENSURE_SUCCESS(rv,rv);
// we can execute the move or delete
// the folder must allow us to execute the move (or the deletion)
PRUint32 folderFlags;
srcFolder->GetFlags(&folderFlags);
folder->GetFlags(&folderFlags);
NS_ASSERTION(manualMarkMode == nsISpamSettings::MANUAL_MARK_MODE_MOVE || manualMarkMode == nsISpamSettings::MANUAL_MARK_MODE_DELETE, "bad mode");
if (manualMarkMode == nsISpamSettings::MANUAL_MARK_MODE_MOVE)
{
PRBool moveOnSpam;
@ -2856,48 +2934,25 @@ nsMsgDBView::SaveJunkMsgForAction(nsIMsgIncomingServer *aServer, const char *aMs
rv = spamSettings->GetSpamFolderURI(getter_Copies(spamFolderURI));
NS_ENSURE_SUCCESS(rv,rv);
NS_ASSERTION(!spamFolderURI.IsEmpty(), "spam folder is empty, can't move");
NS_ASSERTION(!spamFolderURI.IsEmpty(), "spam folder URI is empty, can't move");
if (!spamFolderURI.IsEmpty())
{
nsCOMPtr<nsIMsgFolder> destFolder;
rv = GetExistingFolder(spamFolderURI.get(), getter_AddRefs(destFolder));
if (NS_SUCCEEDED(rv) && destFolder)
{
#ifdef DEBUG
// double check the assumptions
if (mJunkKeys.GetSize())
{
NS_ASSERTION(mJunkTargetFolder, "should have a junk folder at this point");
NS_ASSERTION(mJunkTargetFolder.get() == destFolder.get(), "junk folder doesn't match");
}
else
NS_ASSERTION(mJunkTargetFolder == nsnull, "junk folder should be null, no keys yet");
#endif
// save off msg key and folder
mJunkKeys.Add(msgKey);
if (!mJunkTargetFolder)
mJunkTargetFolder = destFolder;
}
//nsCOMPtr<nsIMsgFolder> destFolder;
rv = GetExistingFolder(spamFolderURI.get(), junkTargetFolder);
NS_ENSURE_SUCCESS(rv,rv);
*movingJunkMessages = true;
}
return NS_OK;
}
else // manualMarkMode == nsISpamSettings::MANUAL_MARK_MODE_DELETE)
{
// if this is in the trash, don't delete?
if (folderFlags & MSG_FOLDER_FLAG_TRASH)
return NS_OK;
// we can't delete, bail out
PRBool canDelete;
(void)srcFolder->GetCanDeleteMessages(&canDelete);
if (!canDelete)
return NS_OK;
// save off msg key
mJunkKeys.Add(msgKey);
NS_ASSERTION(mJunkTargetFolder == nsnull, "should be null");
mJunkTargetFolder = nsnull; // should already be null
}
return NS_OK;
// at this point manualMarkMode == nsISpamSettings::MANUAL_MARK_MODE_DELETE)
// if this is in the trash, let's not delete
if (folderFlags & MSG_FOLDER_FLAG_TRASH)
return NS_OK;
return folder->GetCanDeleteMessages(movingJunkMessages);
}
// reversing threads involves reversing the threads but leaving the

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

@ -250,10 +250,9 @@ protected:
virtual nsresult CopyMessages(nsIMsgWindow *window, nsMsgViewIndex *indices, PRInt32 numIndices, PRBool isMove, nsIMsgFolder *destFolder);
virtual nsresult DeleteMessages(nsIMsgWindow *window, nsMsgViewIndex *indices, PRInt32 numIndices, PRBool deleteStorage);
nsresult SetStringPropertyByIndex(nsMsgViewIndex index, const char *aProperty, const char *aValue);
nsresult SetJunkScoreByIndex(nsIJunkMailPlugin *aJunkPlugin,
nsresult SetAsJunkByIndex(nsIJunkMailPlugin *aJunkPlugin,
nsMsgViewIndex aIndex,
nsMsgJunkStatus aNewClassification,
PRBool aIsLastInBatch);
nsMsgJunkStatus aNewClassification);
nsresult ToggleReadByIndex(nsMsgViewIndex index);
nsresult SetReadByIndex(nsMsgViewIndex index, PRBool read);
nsresult SetThreadOfMsgReadByIndex(nsMsgViewIndex index, nsMsgKeyArray &keysMarkedRead, PRBool read);
@ -360,12 +359,21 @@ protected:
// used to cache the atoms created for each color to be displayed
static nsIAtom* mLabelPrefColorAtoms[PREF_LABELS_MAX];
// used to know to finish out the junk mail classification batch when the
// last classification callback happens
nsCString mLastJunkUriInBatch;
PRUint8 mOutstandingJunkBatches;
// comparing against this value, the classifier
// callback function, OnMessageClassified(), can know
// when the classified message is the last one in the
// batch/series of batches (in which case the
// appropriate action is taken for all the messages)
nsXPIDLCString mLastJunkURIInBatch;
// these are the indices of the messages in the current
// batch/series of batches of messages manually marked
// as junk
nsMsgViewIndex *mJunkIndices;
PRUint32 mNumJunkIndices;
nsUInt32Array mIndicesToNoteChange;
protected:
static nsresult InitDisplayFormats();
@ -375,10 +383,8 @@ private:
static nsDateFormatSelector m_dateFormatToday;
PRBool ServerSupportsFilterAfterTheFact();
nsMsgKeyArray mJunkKeys;
nsCOMPtr <nsIMsgFolder> mJunkTargetFolder;
nsresult PerformActionOnJunkMsgs();
nsresult SaveJunkMsgForAction(nsIMsgIncomingServer *aServer, const char *aMsgURI, nsMsgJunkStatus aClassification);
nsresult PerformActionsOnJunkMsgs();
nsresult DetermineActionsForJunkMsgs(PRBool* movingJunkMessages, PRBool* markingJunkMessagesRead, nsIMsgFolder** junkTargetFolder);
};

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

@ -58,6 +58,7 @@ nsSpamSettings::nsSpamSettings()
{
mLevel = 0;
mMoveOnSpam = PR_FALSE;
mMarkAsReadOnSpam = PR_FALSE;
mMoveTargetMode = nsISpamSettings::MOVE_TARGET_MODE_ACCOUNT;
mPurge = PR_FALSE;
mPurgeInterval = 14; // 14 days
@ -123,6 +124,7 @@ NS_IMETHODIMP nsSpamSettings::SetManualMarkMode(PRInt32 aManualMarkMode)
NS_IMPL_GETSET(nsSpamSettings, LoggingEnabled, PRBool, mLoggingEnabled)
NS_IMPL_GETSET(nsSpamSettings, MoveOnSpam, PRBool, mMoveOnSpam)
NS_IMPL_GETSET(nsSpamSettings, MarkAsReadOnSpam, PRBool, mMarkAsReadOnSpam)
NS_IMPL_GETSET(nsSpamSettings, Purge, PRBool, mPurge)
NS_IMPL_GETSET(nsSpamSettings, UseWhiteList, PRBool, mUseWhiteList)
NS_IMPL_GETSET(nsSpamSettings, ManualMark, PRBool, mManualMark)
@ -349,6 +351,7 @@ NS_IMETHODIMP nsSpamSettings::Clone(nsISpamSettings *aSpamSettings)
NS_ENSURE_SUCCESS(rv,rv);
(void)aSpamSettings->GetMoveOnSpam(&mMoveOnSpam);
(void)aSpamSettings->GetMarkAsReadOnSpam(&mMarkAsReadOnSpam);
(void)aSpamSettings->GetManualMark(&mManualMark);
(void)aSpamSettings->GetManualMarkMode(&mManualMarkMode);
(void)aSpamSettings->GetPurge(&mPurge);

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

@ -72,6 +72,7 @@ private:
PRBool mPurge;
PRBool mUseWhiteList;
PRBool mMoveOnSpam;
PRBool mMarkAsReadOnSpam;
nsCString mActionTargetAccount;
nsCString mActionTargetFolder;

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

@ -2101,6 +2101,10 @@ nsMsgIncomingServer::SetSpamSettings(nsISpamSettings *aSpamSettings)
(void)mSpamSettings->GetMoveOnSpam(&moveOnSpam);
(void)SetBoolValue("moveOnSpam", moveOnSpam);
PRBool markAsReadOnSpam;
(void)mSpamSettings->GetMarkAsReadOnSpam(&markAsReadOnSpam);
(void)SetBoolValue("markAsReadOnSpam", markAsReadOnSpam);
PRInt32 moveTargetMode;
(void)mSpamSettings->GetMoveTargetMode(&moveTargetMode);
(void)SetIntValue("moveTargetMode", moveTargetMode);
@ -2213,6 +2217,12 @@ nsMsgIncomingServer::GetSpamSettings(nsISpamSettings **aSpamSettings)
rv = mSpamSettings->SetMoveOnSpam(moveOnSpam);
NS_ENSURE_SUCCESS(rv,rv);
PRBool markAsReadOnSpam;
rv = GetBoolValue("markAsReadOnSpam", &markAsReadOnSpam);
NS_ENSURE_SUCCESS(rv,rv);
rv = mSpamSettings->SetMarkAsReadOnSpam(markAsReadOnSpam);
NS_ENSURE_SUCCESS(rv,rv);
PRInt32 moveTargetMode;
rv = GetIntValue("moveTargetMode", &moveTargetMode);
NS_ENSURE_SUCCESS(rv,rv);

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

@ -7512,6 +7512,15 @@ nsImapMailFolder::OnMessageClassified(const char *aMsgURI, nsMsgJunkStatus aClas
rv = server->GetSpamSettings(getter_AddRefs(spamSettings));
NS_ENSURE_SUCCESS(rv, rv);
PRBool markAsReadOnSpam;
(void)spamSettings->GetMarkAsReadOnSpam(&markAsReadOnSpam);
if (markAsReadOnSpam)
{
if (!m_junkMessagesToMarkAsRead)
NS_NewISupportsArray(getter_AddRefs(m_junkMessagesToMarkAsRead));
m_junkMessagesToMarkAsRead->AppendElement(msgHdr);
}
PRBool willMoveMessage = PR_FALSE;
// don't do the move when we are opening up
@ -7562,6 +7571,17 @@ nsImapMailFolder::OnMessageClassified(const char *aMsgURI, nsMsgJunkStatus aClas
}
if (--m_numFilterClassifyRequests == 0)
{
if (m_junkMessagesToMarkAsRead)
{
PRUint32 count;
m_junkMessagesToMarkAsRead->Count(&count);
if (count > 0)
{
rv = MarkMessagesRead(m_junkMessagesToMarkAsRead, true);
NS_ENSURE_SUCCESS(rv,rv);
m_junkMessagesToMarkAsRead->SizeTo(0);
}
}
PlaybackCoalescedOperations();
// If we are performing biff for this folder, tell the server object
if (m_performingBiff)

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

@ -447,6 +447,7 @@ protected:
PRInt32 m_numFilterClassifyRequests;
PRBool m_msgMovedByFilter;
nsImapMoveCoalescer *m_moveCoalescer; // strictly owned by the nsImapMailFolder
nsCOMPtr <nsISupportsArray> m_junkMessagesToMarkAsRead;
nsMsgKey m_curMsgUid;
PRUint32 m_uidValidity;
PRInt32 m_numStatusRecentMessages; // used to store counts from Status command

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

@ -3429,6 +3429,15 @@ nsMsgLocalMailFolder::OnMessageClassified(const char *aMsgURI, nsMsgJunkStatus a
if (aClassification == nsIJunkMailPlugin::JUNK)
{
PRBool markAsReadOnSpam;
(void)spamSettings->GetMarkAsReadOnSpam(&markAsReadOnSpam);
if (markAsReadOnSpam)
{
rv = mDatabase->MarkRead(msgKey, true, this);
if (!NS_SUCCEEDED(rv))
NS_WARNING("failed marking spam message as read");
}
PRBool willMoveMessage = PR_FALSE;
// don't do the move when we are opening up

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

@ -378,6 +378,7 @@ pref("mail.server.default.use_idle", true);
// for spam
pref("mail.server.default.spamLevel",100); // 0 off, 100 on. not doing bool since we might have real levels one day.
pref("mail.server.default.moveOnSpam",false);
pref("mail.server.default.markAsReadOnSpam",false);
pref("mail.server.default.moveTargetMode",0); // 0 == "Junk" on server, 1 == specific folder
pref("mail.server.default.spamActionTargetAccount","");
pref("mail.server.default.spamActionTargetFolder","");