mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-08-27 01:38:39 +09:00
456 lines
15 KiB
JavaScript
456 lines
15 KiB
JavaScript
/* 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/. */
|
|
|
|
/* functions use for junk processing commands
|
|
*
|
|
* TODO: These functions make the false assumption that a view only contains
|
|
* a single folder. This is not true for XF saved searches.
|
|
*
|
|
* globals prerequisites used:
|
|
*
|
|
* window.MsgStatusFeedback
|
|
*
|
|
* One of:
|
|
* GetSelectedIndices(view) (in suite)
|
|
* gFolderDisplay (in mail)
|
|
*
|
|
* messenger
|
|
* gMessengerBundle
|
|
* gDBView
|
|
* either gMsgFolderSelected or gFolderDisplay
|
|
* MsgJunkMailInfo(aCheckFirstUse)
|
|
* SetNextMessageAfterDelete()
|
|
* pref
|
|
* msgWindow
|
|
*/
|
|
|
|
Components.utils.import("resource:///modules/mailServices.js");
|
|
Components.utils.import("resource:///modules/MailUtils.js");
|
|
Components.utils.import("resource://gre/modules/Services.jsm");
|
|
|
|
/*
|
|
* determineActionsForJunkMsgs
|
|
*
|
|
* Determines the actions that should be carried out on the messages
|
|
* that are being marked as junk
|
|
*
|
|
* @param aFolder
|
|
* the folder with messages being marked as junk
|
|
*
|
|
* @return an object with two properties: 'markRead' (boolean) indicating
|
|
* whether the messages should be marked as read, and 'junkTargetFolder'
|
|
* (nsIMsgFolder) specifying where the messages should be moved, or
|
|
* null if they should not be moved.
|
|
*/
|
|
function determineActionsForJunkMsgs(aFolder)
|
|
{
|
|
var actions = { markRead: false, junkTargetFolder: null };
|
|
var spamSettings = aFolder.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
|
|
|
|
actions.markRead = spamSettings.markAsReadOnSpam;
|
|
actions.junkTargetFolder = null;
|
|
|
|
// move only when the corresponding setting is activated
|
|
// and the currently viewed folder is not the junk folder.
|
|
if (spamSettings.moveOnSpam &&
|
|
!(aFolder.flags & Components.interfaces.nsMsgFolderFlags.Junk))
|
|
{
|
|
var spamFolderURI = spamSettings.spamFolderURI;
|
|
if (!spamFolderURI)
|
|
{
|
|
// XXX TODO
|
|
// we should use nsIPromptService to inform the user of the problem,
|
|
// e.g. when the junk folder was accidentally deleted.
|
|
dump('determineActionsForJunkMsgs: no spam folder found, not moving.');
|
|
}
|
|
else
|
|
actions.junkTargetFolder = MailUtils.getFolderForURI(spamFolderURI);
|
|
}
|
|
|
|
return actions;
|
|
}
|
|
|
|
/**
|
|
* performActionsOnJunkMsgs
|
|
*
|
|
* Performs required operations on a list of newly-classified junk messages
|
|
*
|
|
* @param aFolder
|
|
* the folder with messages being marked as junk
|
|
*
|
|
* @param aJunkMsgHdrs
|
|
* nsIArray containing headers (nsIMsgDBHdr) of new junk messages
|
|
*
|
|
* @param aGoodMsgHdrs
|
|
* nsIArray containing headers (nsIMsgDBHdr) of new good messages
|
|
*/
|
|
function performActionsOnJunkMsgs(aFolder, aJunkMsgHdrs, aGoodMsgHdrs)
|
|
{
|
|
if (aFolder instanceof Components.interfaces.nsIMsgImapMailFolder) // need to update IMAP custom flags
|
|
{
|
|
if (aJunkMsgHdrs.length)
|
|
{
|
|
var junkMsgKeys = new Array();
|
|
for (var i = 0; i < aJunkMsgHdrs.length; i++)
|
|
junkMsgKeys[i] = aJunkMsgHdrs.queryElementAt(i, Components.interfaces.nsIMsgDBHdr).messageKey;
|
|
aFolder.storeCustomKeywords(null, "Junk", "NonJunk", junkMsgKeys, junkMsgKeys.length);
|
|
}
|
|
|
|
if (aGoodMsgHdrs.length)
|
|
{
|
|
var goodMsgKeys = new Array();
|
|
for (var i = 0; i < aGoodMsgHdrs.length; i++)
|
|
goodMsgKeys[i] = aGoodMsgHdrs.queryElementAt(i, Components.interfaces.nsIMsgDBHdr).messageKey;
|
|
aFolder.storeCustomKeywords(null, "NonJunk", "Junk", goodMsgKeys, goodMsgKeys.length);
|
|
}
|
|
}
|
|
|
|
if (aJunkMsgHdrs.length)
|
|
{
|
|
var actionParams = determineActionsForJunkMsgs(aFolder);
|
|
if (actionParams.markRead)
|
|
aFolder.markMessagesRead(aJunkMsgHdrs, true);
|
|
|
|
if (actionParams.junkTargetFolder)
|
|
MailServices.copy
|
|
.CopyMessages(aFolder, aJunkMsgHdrs, actionParams.junkTargetFolder,
|
|
true /* isMove */, null, msgWindow, true /* allow undo */);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* MessageClassifier
|
|
*
|
|
* Helper object storing the list of pending messages to process,
|
|
* and implementing junk processing callback
|
|
*
|
|
* @param aFolder
|
|
* the folder with messages to be analyzed for junk
|
|
* @param aTotalMessages
|
|
* Number of messages to process, used for progress report only
|
|
*/
|
|
|
|
function MessageClassifier(aFolder, aTotalMessages)
|
|
{
|
|
this.mFolder = aFolder;
|
|
this.mJunkMsgHdrs = Components.classes["@mozilla.org/array;1"]
|
|
.createInstance(Components.interfaces.nsIMutableArray);
|
|
this.mGoodMsgHdrs = Components.classes["@mozilla.org/array;1"]
|
|
.createInstance(Components.interfaces.nsIMutableArray);
|
|
this.mMessages = new Object();
|
|
this.mMessageQueue = new Array();
|
|
this.mTotalMessages = aTotalMessages;
|
|
this.mProcessedMessages = 0;
|
|
this.firstMessage = true;
|
|
this.lastStatusTime = Date.now();
|
|
}
|
|
|
|
MessageClassifier.prototype =
|
|
{
|
|
/**
|
|
* analyzeMessage
|
|
*
|
|
* Starts the message classification process for a message. If the message
|
|
* sender's address is whitelisted, the message is skipped.
|
|
*
|
|
* @param aMsgHdr
|
|
* The header (nsIMsgDBHdr) of the message to classify.
|
|
* @param aSpamSettings
|
|
* nsISpamSettings object with information about whitelists
|
|
*/
|
|
analyzeMessage: function(aMsgHdr, aSpamSettings)
|
|
{
|
|
var junkscoreorigin = aMsgHdr.getStringProperty("junkscoreorigin");
|
|
if (junkscoreorigin == "user") // don't override user-set junk status
|
|
return;
|
|
|
|
// check whitelisting
|
|
if (aSpamSettings.checkWhiteList(aMsgHdr))
|
|
{
|
|
// message is ham from whitelist
|
|
var db = aMsgHdr.folder.msgDatabase;
|
|
db.setStringProperty(aMsgHdr.messageKey, "junkscore",
|
|
Components.interfaces.nsIJunkMailPlugin.IS_HAM_SCORE);
|
|
db.setStringProperty(aMsgHdr.messageKey, "junkscoreorigin", "whitelist");
|
|
this.mGoodMsgHdrs.appendElement(aMsgHdr, false);
|
|
return;
|
|
}
|
|
|
|
var messageURI = aMsgHdr.folder.generateMessageURI(aMsgHdr.messageKey) + "?fetchCompleteMessage=true";
|
|
this.mMessages[messageURI] = aMsgHdr;
|
|
if (this.firstMessage)
|
|
{
|
|
this.firstMessage = false;
|
|
MailServices.junk.classifyMessage(messageURI, msgWindow, this);
|
|
}
|
|
else
|
|
this.mMessageQueue.push(messageURI);
|
|
},
|
|
|
|
/*
|
|
* nsIJunkMailClassificationListener implementation
|
|
* onMessageClassified
|
|
*
|
|
* Callback function from nsIJunkMailPlugin with classification results
|
|
*
|
|
* @param aClassifiedMsgURI
|
|
* URI of classified message
|
|
* @param aClassification
|
|
* Junk classification (0: UNCLASSIFIED, 1: GOOD, 2: JUNK)
|
|
* @param aJunkPercent
|
|
* 0 - 100 indicator of junk likelihood, with 100 meaning probably junk
|
|
*/
|
|
onMessageClassified: function(aClassifiedMsgURI, aClassification, aJunkPercent)
|
|
{
|
|
if (!aClassifiedMsgURI)
|
|
return; // ignore end of batch
|
|
var nsIJunkMailPlugin = Components.interfaces.nsIJunkMailPlugin;
|
|
var score = (aClassification == nsIJunkMailPlugin.JUNK) ?
|
|
nsIJunkMailPlugin.IS_SPAM_SCORE : nsIJunkMailPlugin.IS_HAM_SCORE;
|
|
const statusDisplayInterval = 1000; // milliseconds between status updates
|
|
|
|
// set these props via the db (instead of the message header
|
|
// directly) so that the nsMsgDBView knows to update the UI
|
|
//
|
|
var msgHdr = this.mMessages[aClassifiedMsgURI];
|
|
var db = msgHdr.folder.msgDatabase;
|
|
db.setStringProperty(msgHdr.messageKey, "junkscore", score);
|
|
db.setStringProperty(msgHdr.messageKey, "junkscoreorigin", "plugin");
|
|
db.setStringProperty(msgHdr.messageKey, "junkpercent", aJunkPercent);
|
|
|
|
if (aClassification == nsIJunkMailPlugin.JUNK)
|
|
this.mJunkMsgHdrs.appendElement(msgHdr, false);
|
|
else if (aClassification == nsIJunkMailPlugin.GOOD)
|
|
this.mGoodMsgHdrs.appendElement(msgHdr, false);
|
|
|
|
var nextMsgURI = this.mMessageQueue.shift();
|
|
if (nextMsgURI)
|
|
{
|
|
++this.mProcessedMessages;
|
|
if (Date.now() > this.lastStatusTime + statusDisplayInterval)
|
|
{
|
|
this.lastStatusTime = Date.now();
|
|
var percentDone = 0;
|
|
if (this.mTotalMessages)
|
|
percentDone = Math.round(this.mProcessedMessages * 100 / this.mTotalMessages);
|
|
var percentStr = percentDone + "%";
|
|
window.MsgStatusFeedback.showStatusString(
|
|
document.getElementById("bundle_messenger")
|
|
.getFormattedString("junkAnalysisPercentComplete",
|
|
[percentStr]));
|
|
}
|
|
|
|
MailServices.junk.classifyMessage(nextMsgURI, msgWindow, this);
|
|
}
|
|
else
|
|
{
|
|
window.MsgStatusFeedback.showStatusString(
|
|
document.getElementById("bundle_messenger")
|
|
.getString("processingJunkMessages"));
|
|
performActionsOnJunkMsgs(this.mFolder, this.mJunkMsgHdrs, this.mGoodMsgHdrs);
|
|
window.MsgStatusFeedback.showStatusString("");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* filterFolderForJunk
|
|
*
|
|
* Filter all messages in the current folder for junk
|
|
*/
|
|
function filterFolderForJunk() { processFolderForJunk(true); }
|
|
|
|
/*
|
|
* analyzeMessagesForJunk
|
|
*
|
|
* Filter selected messages in the current folder for junk
|
|
*/
|
|
function analyzeMessagesForJunk() { processFolderForJunk(false); }
|
|
|
|
/*
|
|
* processFolderForJunk
|
|
*
|
|
* Filter messages in the current folder for junk
|
|
*
|
|
* @param aAll: true to filter all messages, else filter selection
|
|
*/
|
|
function processFolderForJunk(aAll)
|
|
{
|
|
MsgJunkMailInfo(true);
|
|
|
|
if (aAll)
|
|
{
|
|
// need to expand all threads, so we analyze everything
|
|
gDBView.doCommand(nsMsgViewCommandType.expandAll);
|
|
var treeView = gDBView.QueryInterface(Components.interfaces.nsITreeView);
|
|
var count = treeView.rowCount;
|
|
if (!count)
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// suite uses GetSelectedIndices, mail uses gFolderDisplay.selectedMessages
|
|
var indices = typeof GetSelectedIndices != "undefined" ?
|
|
GetSelectedIndices(gDBView) :
|
|
gFolderDisplay.selectedIndices;
|
|
if (!indices || !indices.length)
|
|
return;
|
|
}
|
|
var totalMessages = aAll ? count : indices.length;
|
|
|
|
// retrieve server and its spam settings via the header of an arbitrary message
|
|
for (var i = 0; i < totalMessages; i++)
|
|
{
|
|
var index = aAll ? i : indices[i];
|
|
try
|
|
{
|
|
var tmpMsgURI = gDBView.getURIForViewIndex(index);
|
|
break;
|
|
}
|
|
catch (e)
|
|
{
|
|
// dummy headers will fail, so look for another
|
|
continue;
|
|
}
|
|
}
|
|
if (!tmpMsgURI)
|
|
return;
|
|
|
|
var tmpMsgHdr = messenger.messageServiceFromURI(tmpMsgURI).messageURIToMsgHdr(tmpMsgURI);
|
|
var spamSettings = tmpMsgHdr.folder.server.spamSettings;
|
|
|
|
// create a classifier instance to classify messages in the folder.
|
|
var msgClassifier = new MessageClassifier(tmpMsgHdr.folder, totalMessages);
|
|
|
|
for ( i = 0; i < totalMessages; i++)
|
|
{
|
|
var index = aAll ? i : indices[i];
|
|
try
|
|
{
|
|
var msgURI = gDBView.getURIForViewIndex(index);
|
|
var msgHdr = messenger.messageServiceFromURI(msgURI).messageURIToMsgHdr(msgURI);
|
|
msgClassifier.analyzeMessage(msgHdr, spamSettings);
|
|
}
|
|
catch (ex)
|
|
{
|
|
// blow off errors here - dummy headers will fail
|
|
var msgURI = null;
|
|
}
|
|
}
|
|
if (msgClassifier.firstMessage) // the async plugin was not used, maybe all whitelisted?
|
|
performActionsOnJunkMsgs(msgClassifier.mFolder,
|
|
msgClassifier.mJunkMsgHdrs,
|
|
msgClassifier.mGoodMsgHdrs);
|
|
}
|
|
|
|
function JunkSelectedMessages(setAsJunk)
|
|
{
|
|
MsgJunkMailInfo(true);
|
|
|
|
// When the user explicitly marks a message as junk, we can mark it as read,
|
|
// too. This is independent of the "markAsReadOnSpam" pref, which applies
|
|
// only to automatically-classified messages.
|
|
// Note that this behaviour should match the one in the back end for marking
|
|
// as junk via clicking the 'junk' column.
|
|
|
|
if (setAsJunk && Services.prefs.getBoolPref("mailnews.ui.junk.manualMarkAsJunkMarksRead"))
|
|
MarkSelectedMessagesRead(true);
|
|
|
|
gDBView.doCommand(setAsJunk ? nsMsgViewCommandType.junk
|
|
: nsMsgViewCommandType.unjunk);
|
|
}
|
|
|
|
/**
|
|
* Delete junk messages in the current folder. This provides the guarantee that
|
|
* the method will be synchronous if no messages are deleted.
|
|
*
|
|
* @returns The number of messages deleted.
|
|
*/
|
|
function deleteJunkInFolder()
|
|
{
|
|
MsgJunkMailInfo(true);
|
|
|
|
// use direct folder commands if possible so we don't mess with the selection
|
|
let selectedFolder = gFolderDisplay.displayedFolder;
|
|
if ( !(selectedFolder.flags & Components.interfaces.nsMsgFolderFlags.Virtual) )
|
|
{
|
|
var junkMsgHdrs = Components.classes["@mozilla.org/array;1"]
|
|
.createInstance(Components.interfaces.nsIMutableArray);
|
|
var enumerator = gDBView.msgFolder.messages;
|
|
while (enumerator.hasMoreElements())
|
|
{
|
|
var msgHdr = enumerator.getNext().QueryInterface(Components.interfaces.nsIMsgDBHdr);
|
|
var junkScore = msgHdr.getStringProperty("junkscore");
|
|
if (junkScore == Components.interfaces.nsIJunkMailPlugin.IS_SPAM_SCORE)
|
|
junkMsgHdrs.appendElement(msgHdr, false);
|
|
}
|
|
|
|
if (junkMsgHdrs.length)
|
|
gDBView.msgFolder.deleteMessages(junkMsgHdrs, msgWindow, false, false, null, true);
|
|
return junkMsgHdrs.length;
|
|
}
|
|
|
|
// Folder is virtual, let the view do the work (but we lose selection)
|
|
|
|
// need to expand all threads, so we find everything
|
|
gDBView.doCommand(nsMsgViewCommandType.expandAll);
|
|
|
|
var treeView = gDBView.QueryInterface(Components.interfaces.nsITreeView);
|
|
var count = treeView.rowCount;
|
|
if (!count)
|
|
return 0;
|
|
|
|
var treeSelection = treeView.selection;
|
|
|
|
var clearedSelection = false;
|
|
|
|
// select the junk messages
|
|
var messageUri;
|
|
let numMessagesDeleted = 0;
|
|
for (var i = 0; i < count; ++i)
|
|
{
|
|
try {
|
|
messageUri = gDBView.getURIForViewIndex(i);
|
|
}
|
|
catch (ex) {continue;} // blow off errors for dummy rows
|
|
var msgHdr = messenger.messageServiceFromURI(messageUri).messageURIToMsgHdr(messageUri);
|
|
var junkScore = msgHdr.getStringProperty("junkscore");
|
|
var isJunk = (junkScore == Components.interfaces.nsIJunkMailPlugin.IS_SPAM_SCORE);
|
|
// if the message is junk, select it.
|
|
if (isJunk)
|
|
{
|
|
// only do this once
|
|
if (!clearedSelection)
|
|
{
|
|
// clear the current selection
|
|
// since we will be deleting all selected messages
|
|
treeSelection.clearSelection();
|
|
clearedSelection = true;
|
|
treeSelection.selectEventsSuppressed = true;
|
|
}
|
|
treeSelection.rangedSelect(i, i, true /* augment */);
|
|
numMessagesDeleted++;
|
|
}
|
|
}
|
|
|
|
// if we didn't clear the selection
|
|
// there was no junk, so bail.
|
|
if (!clearedSelection)
|
|
return 0;
|
|
|
|
treeSelection.selectEventsSuppressed = false;
|
|
// delete the selected messages
|
|
//
|
|
// We'll leave no selection after the delete
|
|
gNextMessageViewIndexAfterDelete = nsMsgViewIndex_None;
|
|
gDBView.doCommand(nsMsgViewCommandType.deleteMsg);
|
|
treeSelection.clearSelection();
|
|
ClearMessagePane();
|
|
return numMessagesDeleted;
|
|
}
|
|
|