mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-27 10:57:34 +09:00
Issue #1258 - Part 1: Import mailnews, ldap, and mork from comm-esr52.9.1
This commit is contained in:
parent
23e0d82436
commit
e400f4130a
1564 changed files with 510348 additions and 0 deletions
203
mailnews/base/search/content/CustomHeaders.js
Normal file
203
mailnews/base/search/content/CustomHeaders.js
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* 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/. */
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
var gAddButton;
|
||||
var gRemoveButton;
|
||||
var gHeaderInputElement;
|
||||
var gArrayHdrs;
|
||||
var gHdrsList;
|
||||
var gContainer;
|
||||
var gFilterBundle=null;
|
||||
var gCustomBundle=null;
|
||||
|
||||
function onLoad()
|
||||
{
|
||||
let hdrs = Services.prefs.getCharPref("mailnews.customHeaders");
|
||||
gHeaderInputElement = document.getElementById("headerInput");
|
||||
gHeaderInputElement.focus();
|
||||
|
||||
gHdrsList = document.getElementById("headerList");
|
||||
gArrayHdrs = new Array();
|
||||
gAddButton = document.getElementById("addButton");
|
||||
gRemoveButton = document.getElementById("removeButton");
|
||||
|
||||
initializeDialog(hdrs);
|
||||
updateAddButton(true);
|
||||
updateRemoveButton();
|
||||
}
|
||||
|
||||
function initializeDialog(hdrs)
|
||||
{
|
||||
if (hdrs)
|
||||
{
|
||||
hdrs = hdrs.replace(/\s+/g,''); //remove white spaces before splitting
|
||||
gArrayHdrs = hdrs.split(":");
|
||||
for (var i = 0; i < gArrayHdrs.length; i++)
|
||||
if (!gArrayHdrs[i])
|
||||
gArrayHdrs.splice(i,1); //remove any null elements
|
||||
initializeRows();
|
||||
}
|
||||
}
|
||||
|
||||
function initializeRows()
|
||||
{
|
||||
for (var i = 0; i < gArrayHdrs.length; i++)
|
||||
addRow(TrimString(gArrayHdrs[i]));
|
||||
}
|
||||
|
||||
function onTextInput()
|
||||
{
|
||||
// enable the add button if the user has started to type text
|
||||
updateAddButton( (gHeaderInputElement.value == "") );
|
||||
}
|
||||
|
||||
function onOk()
|
||||
{
|
||||
if (gArrayHdrs.length)
|
||||
{
|
||||
var hdrs;
|
||||
if (gArrayHdrs.length == 1)
|
||||
hdrs = gArrayHdrs;
|
||||
else
|
||||
hdrs = gArrayHdrs.join(": ");
|
||||
Services.prefs.setCharPref("mailnews.customHeaders", hdrs);
|
||||
// flush prefs to disk, in case we crash, to avoid dataloss and problems with filters that use the custom headers
|
||||
Services.prefs.savePrefFile(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
Services.prefs.clearUserPref("mailnews.customHeaders"); //clear the pref, no custom headers
|
||||
}
|
||||
|
||||
window.arguments[0].selectedVal = gHdrsList.selectedItem ? gHdrsList.selectedItem.label : null;
|
||||
return true;
|
||||
}
|
||||
|
||||
function customHeaderOverflow()
|
||||
{
|
||||
var nsMsgSearchAttrib = Components.interfaces.nsMsgSearchAttrib;
|
||||
if (gArrayHdrs.length >= (nsMsgSearchAttrib.kNumMsgSearchAttributes - nsMsgSearchAttrib.OtherHeader - 1))
|
||||
{
|
||||
if (!gFilterBundle)
|
||||
gFilterBundle = document.getElementById("bundle_filter");
|
||||
|
||||
var alertText = gFilterBundle.getString("customHeaderOverflow");
|
||||
Services.prompt.alert(window, null, alertText);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function onAddHeader()
|
||||
{
|
||||
var newHdr = TrimString(gHeaderInputElement.value);
|
||||
|
||||
if (!isRFC2822Header(newHdr)) // if user entered an invalid rfc822 header field name, bail out.
|
||||
{
|
||||
if (!gCustomBundle)
|
||||
gCustomBundle = document.getElementById("bundle_custom");
|
||||
|
||||
var alertText = gCustomBundle.getString("colonInHeaderName");
|
||||
Services.prompt.alert(window, null, alertText);
|
||||
return;
|
||||
}
|
||||
|
||||
gHeaderInputElement.value = "";
|
||||
if (!newHdr || customHeaderOverflow())
|
||||
return;
|
||||
if (!duplicateHdrExists(newHdr))
|
||||
{
|
||||
gArrayHdrs[gArrayHdrs.length] = newHdr;
|
||||
var newItem = addRow(newHdr);
|
||||
gHdrsList.selectItem (newItem); // make sure the new entry is selected in the tree
|
||||
// now disable the add button
|
||||
updateAddButton(true);
|
||||
gHeaderInputElement.focus(); // refocus the input field for the next custom header
|
||||
}
|
||||
}
|
||||
|
||||
function isRFC2822Header(hdr)
|
||||
{
|
||||
var charCode;
|
||||
for (var i = 0; i < hdr.length; i++)
|
||||
{
|
||||
charCode = hdr.charCodeAt(i);
|
||||
//58 is for colon and 33 and 126 are us-ascii bounds that should be used for header field name, as per rfc2822
|
||||
|
||||
if (charCode < 33 || charCode == 58 || charCode > 126)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function duplicateHdrExists(hdr)
|
||||
{
|
||||
for (var i = 0;i < gArrayHdrs.length; i++)
|
||||
{
|
||||
if (gArrayHdrs[i] == hdr)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function onRemoveHeader()
|
||||
{
|
||||
var listitem = gHdrsList.selectedItems[0]
|
||||
if (!listitem) return;
|
||||
listitem.remove();
|
||||
var selectedHdr = GetListItemAttributeStr(listitem);
|
||||
var j=0;
|
||||
for (var i = 0; i < gArrayHdrs.length; i++)
|
||||
{
|
||||
if (gArrayHdrs[i] == selectedHdr)
|
||||
{
|
||||
gArrayHdrs.splice(i,1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function GetListItemAttributeStr(listitem)
|
||||
{
|
||||
if (listitem)
|
||||
return TrimString(listitem.getAttribute("label"));
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
function addRow(newHdr)
|
||||
{
|
||||
var listitem = document.createElement("listitem");
|
||||
listitem.setAttribute("label", newHdr);
|
||||
gHdrsList.appendChild(listitem);
|
||||
return listitem;
|
||||
}
|
||||
|
||||
function updateAddButton(aDisable)
|
||||
{
|
||||
// only update the button if the disabled state changed
|
||||
if (aDisable == gAddButton.disabled)
|
||||
return;
|
||||
|
||||
gAddButton.disabled = aDisable;
|
||||
document.documentElement.defaultButton = aDisable ? "accept" : "extra1";
|
||||
}
|
||||
|
||||
function updateRemoveButton()
|
||||
{
|
||||
var headerSelected = (gHdrsList.selectedItems.length > 0);
|
||||
gRemoveButton.disabled = !headerSelected;
|
||||
if (gRemoveButton.disabled)
|
||||
gHeaderInputElement.focus();
|
||||
}
|
||||
|
||||
//Remove whitespace from both ends of a string
|
||||
function TrimString(string)
|
||||
{
|
||||
if (!string) return "";
|
||||
return string.trim();
|
||||
}
|
||||
57
mailnews/base/search/content/CustomHeaders.xul
Normal file
57
mailnews/base/search/content/CustomHeaders.xul
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- 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/. -->
|
||||
<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
|
||||
|
||||
<!DOCTYPE dialog SYSTEM "chrome://messenger/locale/CustomHeaders.dtd">
|
||||
<dialog id="customHeadersDialog"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="onLoad();"
|
||||
ondialogaccept="return onOk();"
|
||||
ondialogextra1="onAddHeader();"
|
||||
ondialogextra2="onRemoveHeader();"
|
||||
style="width: 30em; height: 25em;"
|
||||
persist="width height screenX screenY"
|
||||
title="&window.title;"
|
||||
buttons="accept,cancel,extra1,extra2">
|
||||
|
||||
<stringbundleset id="stringbundleset">
|
||||
<stringbundle id="bundle_filter" src="chrome://messenger/locale/filter.properties"/>
|
||||
<stringbundle id="bundle_custom" src="chrome://messenger/locale/custom.properties"/>
|
||||
</stringbundleset>
|
||||
|
||||
<script type="application/javascript" src="chrome://messenger/content/CustomHeaders.js"/>
|
||||
|
||||
<grid flex="1">
|
||||
<columns>
|
||||
<column flex="1"/>
|
||||
<column/>
|
||||
</columns>
|
||||
<rows>
|
||||
<row>
|
||||
<label accesskey="&newMsgHeader.accesskey;" control="headerInput" value="&newMsgHeader.label;"/>
|
||||
</row>
|
||||
<row>
|
||||
<textbox id="headerInput" onfocus="this.select();" oninput="onTextInput();"/>
|
||||
</row>
|
||||
|
||||
<row flex="1">
|
||||
<vbox>
|
||||
<listbox id="headerList" flex="1" onselect="updateRemoveButton();" />
|
||||
</vbox>
|
||||
|
||||
<vbox>
|
||||
<button id="addButton"
|
||||
label="&addButton.label;"
|
||||
accesskey="&addButton.accesskey;"
|
||||
dlgtype="extra1"/>
|
||||
<button id="removeButton"
|
||||
label="&removeButton.label;"
|
||||
accesskey="&removeButton.accesskey;"
|
||||
dlgtype="extra2"/>
|
||||
</vbox>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
</dialog>
|
||||
854
mailnews/base/search/content/FilterEditor.js
Normal file
854
mailnews/base/search/content/FilterEditor.js
Normal file
|
|
@ -0,0 +1,854 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource:///modules/mailServices.js");
|
||||
Components.utils.import("resource:///modules/MailUtils.js");
|
||||
|
||||
// The actual filter that we're editing if it is a _saved_ filter or prefill;
|
||||
// void otherwise.
|
||||
var gFilter;
|
||||
// cache the key elements we need
|
||||
var gFilterList;
|
||||
// The filter name as it appears in the "Filter Name" field of dialog.
|
||||
var gFilterNameElement;
|
||||
var gFilterTypeSelector;
|
||||
var gFilterBundle;
|
||||
var gPreFillName;
|
||||
var gSessionFolderListenerAdded = false;
|
||||
var gFilterActionList;
|
||||
var gCustomActions = null;
|
||||
var gFilterType;
|
||||
var gFilterPosition = 0;
|
||||
|
||||
var gFilterActionStrings = ["none", "movemessage", "setpriorityto", "deletemessage",
|
||||
"markasread", "ignorethread", "watchthread", "markasflagged",
|
||||
"label", "replytomessage", "forwardmessage", "stopexecution",
|
||||
"deletefrompopserver", "leaveonpopserver", "setjunkscore",
|
||||
"fetchfrompopserver", "copymessage", "addtagtomessage",
|
||||
"ignoresubthread", "markasunread"];
|
||||
|
||||
// A temporary filter with the current state of actions in the UI.
|
||||
var gTempFilter = null;
|
||||
// A nsIArray of the currently defined actions in the order they will be run.
|
||||
var gActionListOrdered = null;
|
||||
|
||||
var gFilterEditorMsgWindow = null;
|
||||
|
||||
var nsMsgFilterAction = Components.interfaces.nsMsgFilterAction;
|
||||
var nsMsgFilterType = Components.interfaces.nsMsgFilterType;
|
||||
var nsIMsgRuleAction = Components.interfaces.nsIMsgRuleAction;
|
||||
var nsMsgSearchScope = Components.interfaces.nsMsgSearchScope;
|
||||
|
||||
function filterEditorOnLoad()
|
||||
{
|
||||
getCustomActions();
|
||||
initializeSearchWidgets();
|
||||
initializeFilterWidgets();
|
||||
|
||||
gFilterBundle = document.getElementById("bundle_filter");
|
||||
|
||||
if ("arguments" in window && window.arguments[0])
|
||||
{
|
||||
var args = window.arguments[0];
|
||||
|
||||
if ("filterList" in args)
|
||||
{
|
||||
gFilterList = args.filterList;
|
||||
// the postPlugin filters cannot be applied to servers that are
|
||||
// deferred, (you must define them on the deferredTo server instead).
|
||||
let server = gFilterList.folder.server;
|
||||
if (server.rootFolder != server.rootMsgFolder)
|
||||
gFilterTypeSelector.disableDeferredAccount();
|
||||
}
|
||||
|
||||
if ("filterPosition" in args)
|
||||
{
|
||||
gFilterPosition = args.filterPosition;
|
||||
}
|
||||
|
||||
if ("filter" in args)
|
||||
{
|
||||
// editing a filter
|
||||
gFilter = window.arguments[0].filter;
|
||||
initializeDialog(gFilter);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gFilterList)
|
||||
setSearchScope(getScopeFromFilterList(gFilterList));
|
||||
// if doing prefill filter create a new filter and populate it.
|
||||
if ("filterName" in args)
|
||||
{
|
||||
gPreFillName = args.filterName;
|
||||
|
||||
// Passing null as the parameter to createFilter to keep the name empty
|
||||
// until later where we assign the name.
|
||||
gFilter = gFilterList.createFilter(null);
|
||||
|
||||
var term = gFilter.createTerm();
|
||||
|
||||
term.attrib = Components.interfaces.nsMsgSearchAttrib.Default;
|
||||
if (("fieldName" in args) && args.fieldName) {
|
||||
// fieldName should contain the name of the field in which to search,
|
||||
// from nsMsgSearchTerm.cpp::SearchAttribEntryTable, e.g. "to" or "cc"
|
||||
try {
|
||||
term.attrib = term.getAttributeFromString(args.fieldName);
|
||||
} catch (e) { /* Invalid string is fine, just ignore it. */ }
|
||||
}
|
||||
if (term.attrib == Components.interfaces.nsMsgSearchAttrib.Default)
|
||||
term.attrib = Components.interfaces.nsMsgSearchAttrib.Sender;
|
||||
|
||||
term.op = Components.interfaces.nsMsgSearchOp.Is;
|
||||
term.booleanAnd = gSearchBooleanRadiogroup.value == "and";
|
||||
|
||||
var termValue = term.value;
|
||||
termValue.attrib = term.attrib;
|
||||
termValue.str = gPreFillName;
|
||||
|
||||
term.value = termValue;
|
||||
|
||||
gFilter.appendTerm(term);
|
||||
|
||||
// the default action for news filters is Delete
|
||||
// for everything else, it's MoveToFolder
|
||||
var filterAction = gFilter.createAction();
|
||||
filterAction.type = (getScopeFromFilterList(gFilterList) ==
|
||||
nsMsgSearchScope.newsFilter) ?
|
||||
nsMsgFilterAction.Delete : nsMsgFilterAction.MoveToFolder;
|
||||
gFilter.appendAction(filterAction);
|
||||
initializeDialog(gFilter);
|
||||
}
|
||||
else if ("copiedFilter" in args)
|
||||
{
|
||||
// we are copying a filter
|
||||
var copiedFilter = args.copiedFilter;
|
||||
var copiedName = gFilterBundle.getFormattedString("copyToNewFilterName",
|
||||
[copiedFilter.filterName]);
|
||||
let newFilter = gFilterList.createFilter(copiedName);
|
||||
|
||||
// copy the actions
|
||||
for (let i = 0; i < copiedFilter.actionCount; i++)
|
||||
{
|
||||
let filterAction = copiedFilter.getActionAt(i);
|
||||
newFilter.appendAction(filterAction);
|
||||
}
|
||||
|
||||
// copy the search terms
|
||||
for (let i = 0; i < copiedFilter.searchTerms.Count(); i++)
|
||||
{
|
||||
var searchTerm = copiedFilter.searchTerms.QueryElementAt(i,
|
||||
Components.interfaces.nsIMsgSearchTerm);
|
||||
|
||||
var newTerm = newFilter.createTerm();
|
||||
newTerm.attrib = searchTerm.attrib;
|
||||
newTerm.op = searchTerm.op;
|
||||
newTerm.booleanAnd = searchTerm.booleanAnd;
|
||||
newTerm.value = searchTerm.value;
|
||||
newFilter.appendTerm(newTerm);
|
||||
};
|
||||
|
||||
gPreFillName = copiedName;
|
||||
gFilter = newFilter;
|
||||
|
||||
initializeDialog(gFilter);
|
||||
|
||||
// We reset the filter name, because otherwise the saveFilter()
|
||||
// function thinks we are editing a filter, and will thus skip the name
|
||||
// uniqueness check.
|
||||
gFilter.filterName = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
// fake the first more button press
|
||||
onMore(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!gFilter)
|
||||
{
|
||||
// This is a new filter. Set to both Incoming and Manual contexts.
|
||||
gFilterTypeSelector.setType(nsMsgFilterType.Incoming | nsMsgFilterType.Manual);
|
||||
}
|
||||
|
||||
// in the case of a new filter, we may not have an action row yet.
|
||||
ensureActionRow();
|
||||
gFilterType = gFilterTypeSelector.getType();
|
||||
|
||||
gFilterNameElement.select();
|
||||
// This call is required on mac and linux. It has no effect under win32. See bug 94800.
|
||||
gFilterNameElement.focus();
|
||||
}
|
||||
|
||||
function filterEditorOnUnload()
|
||||
{
|
||||
if (gSessionFolderListenerAdded)
|
||||
MailServices.mailSession.RemoveFolderListener(gFolderListener);
|
||||
}
|
||||
|
||||
function onEnterInSearchTerm(event)
|
||||
{
|
||||
if (event.ctrlKey || (Services.appinfo.OS == "Darwin" && event.metaKey)) {
|
||||
// If accel key (Ctrl on Win/Linux, Cmd on Mac) was held too, accept the dialog.
|
||||
document.getElementById("FilterEditor").acceptDialog();
|
||||
} else {
|
||||
// If only plain Enter was pressed, add a new rule line.
|
||||
onMore(event);
|
||||
}
|
||||
}
|
||||
|
||||
function onAccept()
|
||||
{
|
||||
try {
|
||||
if (!saveFilter())
|
||||
return false;
|
||||
} catch(e) {Components.utils.reportError(e); return false;}
|
||||
|
||||
// parent should refresh filter list..
|
||||
// this should REALLY only happen when some criteria changes that
|
||||
// are displayed in the filter dialog, like the filter name
|
||||
window.arguments[0].refresh = true;
|
||||
window.arguments[0].newFilter = gFilter;
|
||||
return true;
|
||||
}
|
||||
|
||||
// the folderListener object
|
||||
var gFolderListener = {
|
||||
OnItemAdded: function(parentItem, item) {},
|
||||
|
||||
OnItemRemoved: function(parentItem, item){},
|
||||
|
||||
OnItemPropertyChanged: function(item, property, oldValue, newValue) {},
|
||||
|
||||
OnItemIntPropertyChanged: function(item, property, oldValue, newValue) {},
|
||||
|
||||
OnItemBoolPropertyChanged: function(item, property, oldValue, newValue) {},
|
||||
|
||||
OnItemUnicharPropertyChanged: function(item, property, oldValue, newValue){},
|
||||
OnItemPropertyFlagChanged: function(item, property, oldFlag, newFlag) {},
|
||||
|
||||
OnItemEvent: function(folder, event)
|
||||
{
|
||||
var eventType = event.toString();
|
||||
|
||||
if (eventType == "FolderCreateCompleted")
|
||||
{
|
||||
gActionTargetElement.selectFolder(folder);
|
||||
SetBusyCursor(window, false);
|
||||
}
|
||||
else if (eventType == "FolderCreateFailed")
|
||||
SetBusyCursor(window, false);
|
||||
}
|
||||
}
|
||||
|
||||
function duplicateFilterNameExists(filterName)
|
||||
{
|
||||
if (gFilterList)
|
||||
for (var i = 0; i < gFilterList.filterCount; i++)
|
||||
if (filterName == gFilterList.getFilterAt(i).filterName)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function getScopeFromFilterList(filterList)
|
||||
{
|
||||
if (!filterList)
|
||||
{
|
||||
dump("yikes, null filterList\n");
|
||||
return nsMsgSearchScope.offlineMail;
|
||||
}
|
||||
return filterList.folder.server.filterScope;
|
||||
}
|
||||
|
||||
function getScope(filter)
|
||||
{
|
||||
return getScopeFromFilterList(filter.filterList);
|
||||
}
|
||||
|
||||
function initializeFilterWidgets()
|
||||
{
|
||||
gFilterNameElement = document.getElementById("filterName");
|
||||
gFilterActionList = document.getElementById("filterActionList");
|
||||
initializeFilterTypeSelector();
|
||||
}
|
||||
|
||||
function initializeFilterTypeSelector()
|
||||
{
|
||||
/**
|
||||
* This object controls code interaction with the widget allowing specifying
|
||||
* the filter type (event when the filter is run).
|
||||
*/
|
||||
gFilterTypeSelector = {
|
||||
checkBoxManual: document.getElementById("runManual"),
|
||||
checkBoxIncoming : document.getElementById("runIncoming"),
|
||||
|
||||
menulistIncoming: document.getElementById("pluginsRunOrder"),
|
||||
|
||||
menuitemBeforePlugins: document.getElementById("runBeforePlugins"),
|
||||
menuitemAfterPlugins: document.getElementById("runAfterPlugins"),
|
||||
|
||||
checkBoxArchive: document.getElementById("runArchive"),
|
||||
checkBoxOutgoing: document.getElementById("runOutgoing"),
|
||||
|
||||
/**
|
||||
* Returns the currently set filter type (checkboxes) in terms
|
||||
* of a Components.interfaces.nsMsgFilterType value.
|
||||
*/
|
||||
getType: function()
|
||||
{
|
||||
let type = nsMsgFilterType.None;
|
||||
|
||||
if (this.checkBoxManual.checked)
|
||||
type |= nsMsgFilterType.Manual;
|
||||
|
||||
if (this.checkBoxIncoming.checked) {
|
||||
if (this.menulistIncoming.selectedItem == this.menuitemAfterPlugins) {
|
||||
type |= nsMsgFilterType.PostPlugin;
|
||||
} else {
|
||||
// this.menuitemBeforePlugins selected
|
||||
if (getScopeFromFilterList(gFilterList) ==
|
||||
nsMsgSearchScope.newsFilter)
|
||||
type |= nsMsgFilterType.NewsRule;
|
||||
else
|
||||
type |= nsMsgFilterType.InboxRule;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.checkBoxArchive.checked)
|
||||
type |= nsMsgFilterType.Archive;
|
||||
|
||||
if (this.checkBoxOutgoing.checked)
|
||||
type |= nsMsgFilterType.PostOutgoing;
|
||||
|
||||
return type;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the checkboxes to represent the filter type passed in.
|
||||
*
|
||||
* @param aType the filter type to set in terms
|
||||
* of Components.interfaces.nsMsgFilterType values.
|
||||
*/
|
||||
setType: function(aType)
|
||||
{
|
||||
// If there is no type (event) requested, force "when manually run"
|
||||
if (aType == nsMsgFilterType.None)
|
||||
aType = nsMsgFilterType.Manual;
|
||||
|
||||
this.checkBoxManual.checked = aType & nsMsgFilterType.Manual;
|
||||
|
||||
this.checkBoxIncoming.checked = aType & (nsMsgFilterType.PostPlugin |
|
||||
nsMsgFilterType.Incoming);
|
||||
|
||||
this.menulistIncoming.selectedItem = aType & nsMsgFilterType.PostPlugin ?
|
||||
this.menuitemAfterPlugins : this.menuitemBeforePlugins;
|
||||
|
||||
this.checkBoxArchive.checked = aType & nsMsgFilterType.Archive;
|
||||
|
||||
this.checkBoxOutgoing.checked = aType & nsMsgFilterType.PostOutgoing;
|
||||
|
||||
this.updateClassificationMenu();
|
||||
},
|
||||
|
||||
/**
|
||||
* Enable the "before/after classification" menulist depending on
|
||||
* whether "run when incoming mail" is selected.
|
||||
*/
|
||||
updateClassificationMenu: function()
|
||||
{
|
||||
this.menulistIncoming.disabled = !this.checkBoxIncoming.checked;
|
||||
updateFilterType();
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable the options unsuitable for deferred accounts.
|
||||
*/
|
||||
disableDeferredAccount: function()
|
||||
{
|
||||
this.menuitemAfterPlugins.disabled = true;
|
||||
this.checkBoxOutgoing.disabled = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function initializeDialog(filter)
|
||||
{
|
||||
gFilterNameElement.value = filter.filterName;
|
||||
let filterType = filter.filterType;
|
||||
gFilterTypeSelector.setType(filter.filterType);
|
||||
|
||||
let numActions = filter.actionCount;
|
||||
for (let actionIndex = 0; actionIndex < numActions; actionIndex++)
|
||||
{
|
||||
let filterAction = filter.getActionAt(actionIndex);
|
||||
|
||||
var newActionRow = document.createElement('listitem');
|
||||
newActionRow.setAttribute('initialActionIndex', actionIndex);
|
||||
newActionRow.className = 'ruleaction';
|
||||
gFilterActionList.appendChild(newActionRow);
|
||||
newActionRow.setAttribute('value',
|
||||
filterAction.type == nsMsgFilterAction.Custom ?
|
||||
filterAction.customId : gFilterActionStrings[filterAction.type]);
|
||||
newActionRow.setAttribute('onfocus', 'this.storeFocus();');
|
||||
}
|
||||
|
||||
var gSearchScope = getFilterScope(getScope(filter), filter.filterType, filter.filterList);
|
||||
initializeSearchRows(gSearchScope, filter.searchTerms);
|
||||
setFilterScope(filter.filterType, filter.filterList);
|
||||
}
|
||||
|
||||
function ensureActionRow()
|
||||
{
|
||||
// make sure we have at least one action row visible to the user
|
||||
if (!gFilterActionList.getRowCount())
|
||||
{
|
||||
var newActionRow = document.createElement('listitem');
|
||||
newActionRow.className = 'ruleaction';
|
||||
gFilterActionList.appendChild(newActionRow);
|
||||
newActionRow.mRemoveButton.disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
// move to overlay
|
||||
function saveFilter()
|
||||
{
|
||||
// See if at least one filter type (activation event) is selected.
|
||||
if (gFilterType == nsMsgFilterType.None) {
|
||||
Services.prompt.alert(window,
|
||||
gFilterBundle.getString("mustHaveFilterTypeTitle"),
|
||||
gFilterBundle.getString("mustHaveFilterTypeMessage"));
|
||||
return false;
|
||||
}
|
||||
|
||||
let filterName = gFilterNameElement.value;
|
||||
// If we think have a duplicate, then we need to check that if we
|
||||
// have an original filter name (i.e. we are editing a filter), then
|
||||
// we must check that the original is not the current as that is what
|
||||
// the duplicateFilterNameExists function will have picked up.
|
||||
if ((!gFilter || gFilter.filterName != filterName) && duplicateFilterNameExists(filterName))
|
||||
{
|
||||
Services.prompt.alert(window,
|
||||
gFilterBundle.getString("cannotHaveDuplicateFilterTitle"),
|
||||
gFilterBundle.getString("cannotHaveDuplicateFilterMessage"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that all of the search attributes and operators are valid.
|
||||
function rule_desc(index, obj) {
|
||||
return (index + 1) + " (" + obj.searchattribute.label + ", " + obj.searchoperator.label + ")";
|
||||
}
|
||||
|
||||
let invalidRule = false;
|
||||
for (let index = 0; index < gSearchTerms.length; index++)
|
||||
{
|
||||
let obj = gSearchTerms[index].obj;
|
||||
// We don't need to check validity of matchAll terms
|
||||
if (obj.matchAll)
|
||||
continue;
|
||||
|
||||
// the term might be an offscreen one that we haven't initialized yet
|
||||
let searchTerm = obj.searchTerm;
|
||||
if (!searchTerm && !gSearchTerms[index].initialized)
|
||||
continue;
|
||||
|
||||
if (isNaN(obj.searchattribute.value)) // is this a custom term?
|
||||
{
|
||||
let customTerm = MailServices.filters.getCustomTerm(obj.searchattribute.value);
|
||||
if (!customTerm)
|
||||
{
|
||||
invalidRule = true;
|
||||
Components.utils.reportError("Filter not saved because custom search term '" +
|
||||
obj.searchattribute.value + "' in rule " + rule_desc(index, obj) + " not found");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!customTerm.getAvailable(obj.searchScope, obj.searchattribute.value))
|
||||
{
|
||||
invalidRule = true;
|
||||
Components.utils.reportError("Filter not saved because custom search term '" +
|
||||
customTerm.name + "' in rule " + rule_desc(index, obj) + " not available");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
let otherHeader = Components.interfaces.nsMsgSearchAttrib.OtherHeader;
|
||||
let attribValue = (obj.searchattribute.value > otherHeader) ?
|
||||
otherHeader : obj.searchattribute.value;
|
||||
if (!obj.searchattribute
|
||||
.validityTable
|
||||
.getAvailable(attribValue, obj.searchoperator.value))
|
||||
{
|
||||
invalidRule = true;
|
||||
Components.utils.reportError("Filter not saved because standard search term '" +
|
||||
attribValue + "' in rule " + rule_desc(index, obj) + " not available in this context");
|
||||
}
|
||||
}
|
||||
|
||||
if (invalidRule) {
|
||||
Services.prompt.alert(window,
|
||||
gFilterBundle.getString("searchTermsInvalidTitle"),
|
||||
gFilterBundle.getFormattedString("searchTermsInvalidRule",
|
||||
[obj.searchattribute.label,
|
||||
obj.searchoperator.label]));
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// before we go any further, validate each specified filter action, abort the save
|
||||
// if any of the actions is invalid...
|
||||
for (let index = 0; index < gFilterActionList.itemCount; index++)
|
||||
{
|
||||
var listItem = gFilterActionList.getItemAtIndex(index);
|
||||
if (!listItem.validateAction())
|
||||
return false;
|
||||
}
|
||||
|
||||
// if we made it here, all of the actions are valid, so go ahead and save the filter
|
||||
let isNewFilter;
|
||||
if (!gFilter)
|
||||
{
|
||||
// This is a new filter
|
||||
gFilter = gFilterList.createFilter(filterName);
|
||||
isNewFilter = true;
|
||||
gFilter.enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We are working with an existing filter object,
|
||||
// either editing or using prefill
|
||||
gFilter.filterName = filterName;
|
||||
//Prefilter is treated as a new filter.
|
||||
if (gPreFillName)
|
||||
{
|
||||
isNewFilter = true;
|
||||
gFilter.enabled = true;
|
||||
}
|
||||
else
|
||||
isNewFilter = false;
|
||||
|
||||
gFilter.clearActionList();
|
||||
}
|
||||
|
||||
// add each filteraction to the filter
|
||||
for (let index = 0; index < gFilterActionList.itemCount; index++)
|
||||
gFilterActionList.getItemAtIndex(index).saveToFilter(gFilter);
|
||||
|
||||
// If we do not have a filter name at this point, generate one.
|
||||
if (!gFilter.filterName)
|
||||
AssignMeaningfulName();
|
||||
|
||||
gFilter.filterType = gFilterType;
|
||||
saveSearchTerms(gFilter.searchTerms, gFilter);
|
||||
|
||||
if (isNewFilter)
|
||||
{
|
||||
// new filter - insert into gFilterList
|
||||
gFilterList.insertFilterAt(gFilterPosition, gFilter);
|
||||
}
|
||||
|
||||
// success!
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the list of actions the user created will be executed in a different order.
|
||||
* Exposes a note to the user if that is the case.
|
||||
*/
|
||||
function checkActionsReorder()
|
||||
{
|
||||
setTimeout(_checkActionsReorder, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be called from setTimeout otherwise some of the elements calling
|
||||
* may not be fully initialized yet (e.g. we get ".saveToFilter is not a function").
|
||||
* It is OK to schedule multiple timeouts with this function.
|
||||
*/
|
||||
function _checkActionsReorder() {
|
||||
// Create a temporary disposable filter and add current actions to it.
|
||||
if (!gTempFilter)
|
||||
gTempFilter = gFilterList.createFilter("");
|
||||
else
|
||||
gTempFilter.clearActionList();
|
||||
|
||||
for (let index = 0; index < gFilterActionList.itemCount; index++)
|
||||
gFilterActionList.getItemAtIndex(index).saveToFilter(gTempFilter);
|
||||
|
||||
// Now get the actions out of the filter in the order they will be executed in.
|
||||
gActionListOrdered = gTempFilter.sortedActionList;
|
||||
|
||||
// Compare the two lists.
|
||||
let statusBar = document.getElementById("statusbar");
|
||||
for (let index = 0; index < gActionListOrdered.length; index++) {
|
||||
if (index != gTempFilter.getActionIndex(
|
||||
gActionListOrdered.queryElementAt(index, nsIMsgRuleAction)))
|
||||
{
|
||||
// If the lists are not the same unhide the status bar and show warning.
|
||||
statusBar.style.visibility = "visible";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
statusBar.style.visibility = "hidden";
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a dialog with the ordered list of actions.
|
||||
* The fetching of action label and argument is separated from checkActionsReorder
|
||||
* function to make that one more lightweight. The list is built only upon
|
||||
* user request.
|
||||
*/
|
||||
function showActionsOrder()
|
||||
{
|
||||
// Fetch the actions and arguments as a string.
|
||||
let actionStrings = [];
|
||||
for (let index = 0; index < gFilterActionList.itemCount; index++)
|
||||
gFilterActionList.getItemAtIndex(index).getActionStrings(actionStrings);
|
||||
|
||||
// Present a nicely formatted list of action names and arguments.
|
||||
let actionList = gFilterBundle.getString("filterActionOrderExplanation");
|
||||
for (let i = 0; i < gActionListOrdered.length; i++) {
|
||||
let actionIndex = gTempFilter.getActionIndex(
|
||||
gActionListOrdered.queryElementAt(i, nsIMsgRuleAction));
|
||||
let action = actionStrings[actionIndex];
|
||||
actionList += gFilterBundle.getFormattedString("filterActionItem",
|
||||
[(i + 1), action.label, action.argument]);
|
||||
}
|
||||
|
||||
Services.prompt.confirmEx(window,
|
||||
gFilterBundle.getString("filterActionOrderTitle"),
|
||||
actionList, Services.prompt.BUTTON_TITLE_OK,
|
||||
null, null, null, null, {value:false});
|
||||
}
|
||||
|
||||
function AssignMeaningfulName()
|
||||
{
|
||||
// termRoot points to the first search object, which is the one we care about.
|
||||
let termRoot = gSearchTerms[0].obj;
|
||||
// stub is used as the base name for a filter.
|
||||
let stub;
|
||||
|
||||
// If this is a Match All Messages Filter, we already know the name to assign.
|
||||
if (termRoot.matchAll)
|
||||
stub = gFilterBundle.getString( "matchAllFilterName" );
|
||||
else
|
||||
{
|
||||
// Assign a name based on the first search term.
|
||||
let searchValue = termRoot.searchvalue;
|
||||
let selIndex = searchValue.getAttribute( "selectedIndex" );
|
||||
let children = document.getAnonymousNodes(searchValue);
|
||||
let activeItem = children[selIndex];
|
||||
let attribs = Components.interfaces.nsMsgSearchAttrib;
|
||||
|
||||
// Term, Operator and Value are the three parts of a filter match
|
||||
// Term and Operator are easy to retrieve
|
||||
let term = termRoot.searchattribute.label;
|
||||
let operator = termRoot.searchoperator.label;
|
||||
|
||||
// Values are either popup menu items or edit fields.
|
||||
// For popup menus use activeItem.label; for
|
||||
// edit fields, activeItem.value
|
||||
let value;
|
||||
switch (Number(termRoot.searchattribute.value))
|
||||
{
|
||||
case attribs.Priority:
|
||||
case attribs.MsgStatus:
|
||||
case attribs.Keywords:
|
||||
case attribs.HasAttachmentStatus:
|
||||
case attribs.JunkStatus:
|
||||
case attribs.JunkScoreOrigin:
|
||||
if (activeItem)
|
||||
value = activeItem.label;
|
||||
else
|
||||
value = "";
|
||||
break;
|
||||
|
||||
default:
|
||||
try
|
||||
{
|
||||
value = activeItem.value;
|
||||
}
|
||||
catch (ex)
|
||||
{
|
||||
// We should never get here, but for safety's sake,
|
||||
// let's name the filter "Untitled Filter".
|
||||
stub = gFilterBundle.getString( "untitledFilterName" );
|
||||
// Do not 'Return'. Instead fall through and deal with the untitled filter below.
|
||||
}
|
||||
break;
|
||||
}
|
||||
// We are now ready to name the filter.
|
||||
// If at this point stub is empty, we know that this is not a Match All Filter
|
||||
// and is not an "untitledFilterName" Filter, so assign it a name using
|
||||
// a string format from the Filter Bundle.
|
||||
if (!stub)
|
||||
stub = gFilterBundle.getFormattedString("filterAutoNameStr", [term, operator, value]);
|
||||
}
|
||||
|
||||
// Whatever name we have used, 'uniquify' it.
|
||||
let tempName = stub;
|
||||
let count = 1;
|
||||
while (duplicateFilterNameExists(tempName))
|
||||
{
|
||||
count++;
|
||||
tempName = stub + " " + count;
|
||||
}
|
||||
gFilter.filterName = tempName;
|
||||
}
|
||||
|
||||
|
||||
function GetFirstSelectedMsgFolder()
|
||||
{
|
||||
var selectedFolder = gActionTargetElement.getAttribute("uri");
|
||||
if (!selectedFolder)
|
||||
return null;
|
||||
|
||||
var msgFolder = MailUtils.getFolderForURI(selectedFolder, true);
|
||||
return msgFolder;
|
||||
}
|
||||
|
||||
function SearchNewFolderOkCallback(name, uri)
|
||||
{
|
||||
var msgFolder = MailUtils.getFolderForURI(uri, true);
|
||||
var imapFolder = null;
|
||||
try
|
||||
{
|
||||
imapFolder = msgFolder.QueryInterface(Components.interfaces.nsIMsgImapMailFolder);
|
||||
}
|
||||
catch(ex) {}
|
||||
if (imapFolder) //imapFolder creation is asynchronous.
|
||||
{
|
||||
if (!gSessionFolderListenerAdded) {
|
||||
try
|
||||
{
|
||||
let notifyFlags = Components.interfaces.nsIFolderListener.event;
|
||||
MailServices.mailSession.AddFolderListener(gFolderListener, notifyFlags);
|
||||
gSessionFolderListenerAdded = true;
|
||||
}
|
||||
catch (ex)
|
||||
{
|
||||
Components.utils.reportError("Error adding to session: " + ex + "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var msgWindow = GetFilterEditorMsgWindow();
|
||||
|
||||
if (imapFolder)
|
||||
SetBusyCursor(window, true);
|
||||
|
||||
msgFolder.createSubfolder(name, msgWindow);
|
||||
|
||||
if (!imapFolder)
|
||||
{
|
||||
var curFolder = uri+"/"+encodeURIComponent(name);
|
||||
let folder = MailUtils.getFolderForURI(curFolder);
|
||||
gActionTargetElement.selectFolder(folder);
|
||||
}
|
||||
}
|
||||
|
||||
function UpdateAfterCustomHeaderChange()
|
||||
{
|
||||
updateSearchAttributes();
|
||||
}
|
||||
|
||||
//if you use msgWindow, please make sure that destructor gets called when you close the "window"
|
||||
function GetFilterEditorMsgWindow()
|
||||
{
|
||||
if (!gFilterEditorMsgWindow)
|
||||
{
|
||||
var msgWindowContractID = "@mozilla.org/messenger/msgwindow;1";
|
||||
var nsIMsgWindow = Components.interfaces.nsIMsgWindow;
|
||||
gFilterEditorMsgWindow = Components.classes[msgWindowContractID].createInstance(nsIMsgWindow);
|
||||
gFilterEditorMsgWindow.domWindow = window;
|
||||
gFilterEditorMsgWindow.rootDocShell.appType = Components.interfaces.nsIDocShell.APP_TYPE_MAIL;
|
||||
}
|
||||
return gFilterEditorMsgWindow;
|
||||
}
|
||||
|
||||
function SetBusyCursor(window, enable)
|
||||
{
|
||||
// setCursor() is only available for chrome windows.
|
||||
// However one of our frames is the start page which
|
||||
// is a non-chrome window, so check if this window has a
|
||||
// setCursor method
|
||||
if ("setCursor" in window)
|
||||
{
|
||||
if (enable)
|
||||
window.setCursor("wait");
|
||||
else
|
||||
window.setCursor("auto");
|
||||
}
|
||||
}
|
||||
|
||||
function doHelpButton()
|
||||
{
|
||||
openHelp("mail-filters");
|
||||
}
|
||||
|
||||
function getCustomActions()
|
||||
{
|
||||
if (!gCustomActions)
|
||||
{
|
||||
gCustomActions = [];
|
||||
let customActionsEnum = MailServices.filters.getCustomActions();
|
||||
while (customActionsEnum.hasMoreElements())
|
||||
gCustomActions.push(customActionsEnum.getNext().QueryInterface(
|
||||
Components.interfaces.nsIMsgFilterCustomAction));
|
||||
}
|
||||
}
|
||||
|
||||
function updateFilterType()
|
||||
{
|
||||
gFilterType = gFilterTypeSelector.getType();
|
||||
setFilterScope(gFilterType, gFilterList);
|
||||
|
||||
// set valid actions
|
||||
var ruleActions = gFilterActionList.getElementsByAttribute('class', 'ruleaction');
|
||||
for (var i = 0; i < ruleActions.length; i++)
|
||||
ruleActions[i].mRuleActionType.hideInvalidActions();
|
||||
}
|
||||
|
||||
// Given a filter type, set the global search scope to the filter scope
|
||||
function setFilterScope(aFilterType, aFilterList)
|
||||
{
|
||||
let filterScope = getFilterScope(getScopeFromFilterList(aFilterList),
|
||||
aFilterType, aFilterList);
|
||||
setSearchScope(filterScope);
|
||||
}
|
||||
|
||||
//
|
||||
// Given the base filter scope for a server, and the filter
|
||||
// type, return the scope used for filter. This assumes a
|
||||
// hierarchy of contexts, with incoming the most restrictive,
|
||||
// followed by manual and post-plugin.
|
||||
function getFilterScope(aServerFilterScope, aFilterType, aFilterList)
|
||||
{
|
||||
if (aFilterType & nsMsgFilterType.Incoming)
|
||||
return aServerFilterScope;
|
||||
|
||||
// Manual or PostPlugin
|
||||
// local mail allows body and junk types
|
||||
if (aServerFilterScope == nsMsgSearchScope.offlineMailFilter)
|
||||
return nsMsgSearchScope.offlineMail;
|
||||
// IMAP and NEWS online don't allow body
|
||||
return nsMsgSearchScope.onlineManual;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-focus the action that was focused before focus was lost.
|
||||
*/
|
||||
function setLastActionFocus() {
|
||||
let lastAction = gFilterActionList.getAttribute("focusedAction");
|
||||
if (!lastAction || lastAction < 0)
|
||||
lastAction = 0;
|
||||
if (lastAction >= gFilterActionList.itemCount)
|
||||
lastAction = gFilterActionList.itemCount - 1;
|
||||
|
||||
gFilterActionList.getItemAtIndex(lastAction).mRuleActionType.menulist.focus();
|
||||
}
|
||||
125
mailnews/base/search/content/FilterEditor.xul
Normal file
125
mailnews/base/search/content/FilterEditor.xul
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://messenger/skin/filterDialog.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://messenger/skin/folderPane.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://messenger/skin/folderMenus.css" type="text/css"?>
|
||||
|
||||
<?xul-overlay href="chrome://messenger/content/searchTermOverlay.xul"?>
|
||||
|
||||
<!DOCTYPE dialog SYSTEM "chrome://messenger/locale/FilterEditor.dtd">
|
||||
|
||||
<dialog id="FilterEditor"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
title="&window.title;"
|
||||
style="&filterEditorDialog.dimensions;"
|
||||
windowtype="mailnews:filtereditor"
|
||||
persist="width height screenX screenY"
|
||||
buttons="accept,cancel"
|
||||
onload="filterEditorOnLoad();"
|
||||
onunload="filterEditorOnUnload();"
|
||||
ondialogaccept="return onAccept();">
|
||||
|
||||
<dummy class="usesMailWidgets"/>
|
||||
<stringbundleset id="stringbundleset">
|
||||
<stringbundle id="bundle_messenger" src="chrome://messenger/locale/messenger.properties"/>
|
||||
<stringbundle id="bundle_filter" src="chrome://messenger/locale/filter.properties"/>
|
||||
<stringbundle id="bundle_search" src="chrome://messenger/locale/search.properties"/>
|
||||
</stringbundleset>
|
||||
|
||||
<script type="application/javascript" src="chrome://messenger/content/mailWindowOverlay.js"/>
|
||||
<script type="application/javascript" src="chrome://messenger/content/mailCommands.js"/>
|
||||
<script type="application/javascript" src="chrome://messenger/content/FilterEditor.js"/>
|
||||
|
||||
<commandset>
|
||||
<command id="cmd_updateFilterType" oncommand="updateFilterType();"/>
|
||||
<command id="cmd_updateClassificationMenu" oncommand="gFilterTypeSelector.updateClassificationMenu();"/>
|
||||
</commandset>
|
||||
|
||||
<vbox>
|
||||
<hbox align="center">
|
||||
<label value="&filterName.label;" accesskey="&filterName.accesskey;" control="filterName"/>
|
||||
<textbox flex="1" id="filterName"/>
|
||||
<spacer flex="1"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
||||
<separator class="thin"/>
|
||||
|
||||
<vbox flex="1">
|
||||
<groupbox>
|
||||
<caption label="&contextDesc.label;"/>
|
||||
<grid>
|
||||
<columns>
|
||||
<column/>
|
||||
<column/>
|
||||
</columns>
|
||||
<rows>
|
||||
<row>
|
||||
<checkbox id="runManual"
|
||||
label="&contextManual.label;"
|
||||
accesskey="&contextManual.accesskey;"
|
||||
command="cmd_updateFilterType"/>
|
||||
</row>
|
||||
<row>
|
||||
<checkbox id="runIncoming"
|
||||
label="&contextIncomingMail.label;"
|
||||
accesskey="&contextIncomingMail.accesskey;"
|
||||
command="cmd_updateClassificationMenu"/>
|
||||
<menulist id="pluginsRunOrder"
|
||||
command="cmd_updateFilterType">
|
||||
<menupopup>
|
||||
<menuitem id="runBeforePlugins"
|
||||
label="&contextBeforeCls.label;"/>
|
||||
<menuitem id="runAfterPlugins"
|
||||
label="&contextAfterCls.label;"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
</row>
|
||||
<row>
|
||||
<checkbox id="runArchive"
|
||||
label="&contextArchive.label;"
|
||||
accesskey="&contextArchive.accesskey;"
|
||||
command="cmd_updateFilterType"/>
|
||||
</row>
|
||||
<row>
|
||||
<checkbox id="runOutgoing"
|
||||
label="&contextOutgoing.label;"
|
||||
accesskey="&contextOutgoing.accesskey;"
|
||||
command="cmd_updateFilterType"/>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
</groupbox>
|
||||
|
||||
<vbox id="searchTermListBox" flex="1"/>
|
||||
</vbox>
|
||||
|
||||
<splitter id="gray_horizontal_splitter" persist="state"/>
|
||||
|
||||
<vbox flex="1">
|
||||
<label value="&filterActionDesc.label;"
|
||||
accesskey="&filterActionDesc.accesskey;"
|
||||
control="filterActionList"/>
|
||||
<listbox id="filterActionList" flex="1" rows="4" minheight="35%"
|
||||
onfocus="setLastActionFocus();" focusedAction="0">
|
||||
<listcols>
|
||||
<listcol flex="&filterActionTypeFlexValue;"/>
|
||||
<listcol flex="&filterActionTargetFlexValue;"/>
|
||||
<listcol class="filler"/>
|
||||
</listcols>
|
||||
</listbox>
|
||||
</vbox>
|
||||
|
||||
<vbox id="statusbar" style="visibility: hidden;">
|
||||
<hbox align="center">
|
||||
<label>
|
||||
&filterActionOrderWarning.label;
|
||||
</label>
|
||||
<label class="text-link" onclick="showActionsOrder();">&filterActionOrder.label;</label>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</dialog>
|
||||
536
mailnews/base/search/content/searchTermOverlay.js
Normal file
536
mailnews/base/search/content/searchTermOverlay.js
Normal file
|
|
@ -0,0 +1,536 @@
|
|||
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
var gTotalSearchTerms=0;
|
||||
var gSearchTermList;
|
||||
var gSearchTerms = new Array;
|
||||
var gSearchRemovedTerms = new Array;
|
||||
var gSearchScope;
|
||||
var gSearchBooleanRadiogroup;
|
||||
|
||||
var gUniqueSearchTermCounter = 0; // gets bumped every time we add a search term so we can always
|
||||
// dynamically generate unique IDs for the terms.
|
||||
|
||||
// cache these so we don't have to hit the string bundle for them
|
||||
var gMoreButtonTooltipText;
|
||||
var gLessButtonTooltipText;
|
||||
var gLoading = true;
|
||||
|
||||
|
||||
function searchTermContainer() {}
|
||||
|
||||
searchTermContainer.prototype = {
|
||||
internalSearchTerm : '',
|
||||
internalBooleanAnd : '',
|
||||
|
||||
// this.searchTerm: the actual nsIMsgSearchTerm object
|
||||
get searchTerm() { return this.internalSearchTerm; },
|
||||
set searchTerm(val) {
|
||||
this.internalSearchTerm = val;
|
||||
|
||||
var term = val;
|
||||
// val is a nsIMsgSearchTerm
|
||||
var searchAttribute=this.searchattribute;
|
||||
var searchOperator=this.searchoperator;
|
||||
var searchValue=this.searchvalue;
|
||||
|
||||
// now reflect all attributes of the searchterm into the widgets
|
||||
if (searchAttribute)
|
||||
{
|
||||
// for custom, the value is the custom id, not the integer attribute
|
||||
if (term.attrib == Components.interfaces.nsMsgSearchAttrib.Custom)
|
||||
searchAttribute.value = term.customId;
|
||||
else
|
||||
searchAttribute.value = term.attrib;
|
||||
}
|
||||
if (searchOperator) searchOperator.value = val.op;
|
||||
if (searchValue) searchValue.value = term.value;
|
||||
|
||||
this.booleanAnd = val.booleanAnd;
|
||||
this.matchAll = val.matchAll;
|
||||
return val;
|
||||
},
|
||||
|
||||
// searchscope - just forward to the searchattribute
|
||||
get searchScope() {
|
||||
if (this.searchattribute)
|
||||
return this.searchattribute.searchScope;
|
||||
return undefined;
|
||||
},
|
||||
set searchScope(val) {
|
||||
var searchAttribute = this.searchattribute;
|
||||
if (searchAttribute) searchAttribute.searchScope=val;
|
||||
return val;
|
||||
},
|
||||
|
||||
saveId: function (element, slot) {
|
||||
this[slot] = element.id;
|
||||
},
|
||||
|
||||
getElement: function (slot) {
|
||||
return document.getElementById(this[slot]);
|
||||
},
|
||||
|
||||
// three well-defined properties:
|
||||
// searchattribute, searchoperator, searchvalue
|
||||
// the trick going on here is that we're storing the Element's Id,
|
||||
// not the element itself, because the XBL object may change out
|
||||
// from underneath us
|
||||
get searchattribute() { return this.getElement("internalSearchAttributeId"); },
|
||||
set searchattribute(val) {
|
||||
this.saveId(val, "internalSearchAttributeId");
|
||||
return val;
|
||||
},
|
||||
get searchoperator() { return this.getElement("internalSearchOperatorId"); },
|
||||
set searchoperator(val) {
|
||||
this.saveId(val, "internalSearchOperatorId");
|
||||
return val;
|
||||
},
|
||||
get searchvalue() { return this.getElement("internalSearchValueId"); },
|
||||
set searchvalue(val) {
|
||||
this.saveId(val, "internalSearchValueId");
|
||||
return val;
|
||||
},
|
||||
|
||||
booleanNodes: null,
|
||||
get booleanAnd() { return this.internalBooleanAnd; },
|
||||
set booleanAnd(val) {
|
||||
this.internalBooleanAnd = val;
|
||||
return val;
|
||||
},
|
||||
|
||||
save: function () {
|
||||
var searchTerm = this.searchTerm;
|
||||
var nsMsgSearchAttrib = Components.interfaces.nsMsgSearchAttrib;
|
||||
|
||||
if (isNaN(this.searchattribute.value)) // is this a custom term?
|
||||
{
|
||||
searchTerm.attrib = nsMsgSearchAttrib.Custom;
|
||||
searchTerm.customId = this.searchattribute.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
searchTerm.attrib = this.searchattribute.value;
|
||||
}
|
||||
|
||||
if (this.searchattribute.value > nsMsgSearchAttrib.OtherHeader && this.searchattribute.value < nsMsgSearchAttrib.kNumMsgSearchAttributes)
|
||||
searchTerm.arbitraryHeader = this.searchattribute.label;
|
||||
searchTerm.op = this.searchoperator.value;
|
||||
if (this.searchvalue.value)
|
||||
this.searchvalue.save();
|
||||
else
|
||||
this.searchvalue.saveTo(searchTerm.value);
|
||||
searchTerm.value = this.searchvalue.value;
|
||||
searchTerm.booleanAnd = this.booleanAnd;
|
||||
searchTerm.matchAll = this.matchAll;
|
||||
},
|
||||
// if you have a search term element with no search term
|
||||
saveTo: function(searchTerm) {
|
||||
this.internalSearchTerm = searchTerm;
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
|
||||
var nsIMsgSearchTerm = Components.interfaces.nsIMsgSearchTerm;
|
||||
|
||||
function initializeSearchWidgets()
|
||||
{
|
||||
gSearchBooleanRadiogroup = document.getElementById("booleanAndGroup");
|
||||
gSearchTermList = document.getElementById("searchTermList");
|
||||
|
||||
// initialize some strings
|
||||
var bundle = document.getElementById('bundle_search');
|
||||
gMoreButtonTooltipText = bundle.getString('moreButtonTooltipText');
|
||||
gLessButtonTooltipText = bundle.getString('lessButtonTooltipText');
|
||||
}
|
||||
|
||||
function initializeBooleanWidgets()
|
||||
{
|
||||
var booleanAnd = true;
|
||||
var matchAll = false;
|
||||
// get the boolean value from the first term
|
||||
var firstTerm = gSearchTerms[0].searchTerm;
|
||||
if (firstTerm)
|
||||
{
|
||||
// If there is a second term, it should actually define whether we're
|
||||
// using 'and' or not. Note that our UI is not as rich as the
|
||||
// underlying search model, so there's the potential to lose here when
|
||||
// grouping is involved.
|
||||
booleanAnd = (gSearchTerms.length > 1) ?
|
||||
gSearchTerms[1].searchTerm.booleanAnd : firstTerm.booleanAnd;
|
||||
matchAll = firstTerm.matchAll;
|
||||
}
|
||||
// target radio items have value="and" or value="or" or "all"
|
||||
gSearchBooleanRadiogroup.value = matchAll
|
||||
? "matchAll"
|
||||
: (booleanAnd ? "and" : "or")
|
||||
var searchTerms = document.getElementById("searchTermList");
|
||||
if (searchTerms)
|
||||
updateSearchTermsListbox(matchAll);
|
||||
}
|
||||
|
||||
function initializeSearchRows(scope, searchTerms)
|
||||
{
|
||||
for (var i = 0; i < searchTerms.Count(); i++) {
|
||||
var searchTerm = searchTerms.QueryElementAt(i, nsIMsgSearchTerm);
|
||||
createSearchRow(i, scope, searchTerm, false);
|
||||
gTotalSearchTerms++;
|
||||
}
|
||||
initializeBooleanWidgets();
|
||||
updateRemoveRowButton();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables/disables all the visible elements inside the search terms listbox.
|
||||
*
|
||||
* @param matchAllValue boolean value from the first search term
|
||||
*/
|
||||
function updateSearchTermsListbox(matchAllValue)
|
||||
{
|
||||
var searchTerms = document.getElementById("searchTermList");
|
||||
searchTerms.setAttribute("disabled", matchAllValue);
|
||||
var searchAttributeList = searchTerms.getElementsByTagName("searchattribute");
|
||||
var searchOperatorList = searchTerms.getElementsByTagName("searchoperator");
|
||||
var searchValueList = searchTerms.getElementsByTagName("searchvalue");
|
||||
for (var i = 0; i < searchAttributeList.length; i++) {
|
||||
searchAttributeList[i].setAttribute("disabled", matchAllValue);
|
||||
searchOperatorList[i].setAttribute("disabled", matchAllValue);
|
||||
searchValueList[i].setAttribute("disabled", matchAllValue);
|
||||
if (!matchAllValue)
|
||||
searchValueList[i].removeAttribute("disabled");
|
||||
}
|
||||
var moreOrLessButtonsList = searchTerms.getElementsByTagName("button");
|
||||
for (var i = 0; i < moreOrLessButtonsList.length; i++) {
|
||||
moreOrLessButtonsList[i].setAttribute("disabled", matchAllValue);
|
||||
}
|
||||
if (!matchAllValue)
|
||||
updateRemoveRowButton();
|
||||
}
|
||||
|
||||
// enables/disables the less button for the first row of search terms.
|
||||
function updateRemoveRowButton()
|
||||
{
|
||||
var firstListItem = gSearchTermList.getItemAtIndex(0);
|
||||
if (firstListItem)
|
||||
firstListItem.lastChild.lastChild.setAttribute("disabled", gTotalSearchTerms == 1);
|
||||
}
|
||||
|
||||
// Returns the actual list item row index in the list of search rows
|
||||
// that contains the passed in element id.
|
||||
function getSearchRowIndexForElement(aElement)
|
||||
{
|
||||
var listItem = aElement;
|
||||
|
||||
while (listItem && listItem.localName != "listitem")
|
||||
listItem = listItem.parentNode;
|
||||
|
||||
return gSearchTermList.getIndexOfItem(listItem);
|
||||
}
|
||||
|
||||
function onMore(event)
|
||||
{
|
||||
// if we have an event, extract the list row index and use that as the row number
|
||||
// for our insertion point. If there is no event, append to the end....
|
||||
var rowIndex;
|
||||
|
||||
if (event)
|
||||
rowIndex = getSearchRowIndexForElement(event.target) + 1;
|
||||
else
|
||||
rowIndex = gSearchTermList.getRowCount();
|
||||
|
||||
createSearchRow(rowIndex, gSearchScope, null, event != null);
|
||||
gTotalSearchTerms++;
|
||||
updateRemoveRowButton();
|
||||
|
||||
// the user just added a term, so scroll to it
|
||||
gSearchTermList.ensureIndexIsVisible(rowIndex);
|
||||
}
|
||||
|
||||
function onLess(event)
|
||||
{
|
||||
if (event && gTotalSearchTerms > 1)
|
||||
{
|
||||
removeSearchRow(getSearchRowIndexForElement(event.target));
|
||||
--gTotalSearchTerms;
|
||||
}
|
||||
|
||||
updateRemoveRowButton();
|
||||
}
|
||||
|
||||
// set scope on all visible searchattribute tags
|
||||
function setSearchScope(scope)
|
||||
{
|
||||
gSearchScope = scope;
|
||||
for (var i = 0; i < gSearchTerms.length; i++)
|
||||
{
|
||||
// don't set element attributes if XBL hasn't loaded
|
||||
if (!(gSearchTerms[i].obj.searchattribute.searchScope === undefined))
|
||||
{
|
||||
gSearchTerms[i].obj.searchattribute.searchScope = scope;
|
||||
// act like the user "selected" this, see bug #202848
|
||||
gSearchTerms[i].obj.searchattribute.onSelect(null /* no event */);
|
||||
}
|
||||
gSearchTerms[i].scope = scope;
|
||||
}
|
||||
}
|
||||
|
||||
function updateSearchAttributes()
|
||||
{
|
||||
for (var i=0; i<gSearchTerms.length; i++)
|
||||
gSearchTerms[i].obj.searchattribute.refreshList();
|
||||
}
|
||||
|
||||
function booleanChanged(event) {
|
||||
// when boolean changes, we have to update all the attributes on the search terms
|
||||
var newBoolValue = (event.target.getAttribute("value") == "and");
|
||||
var matchAllValue = (event.target.getAttribute("value") == "matchAll");
|
||||
if (document.getElementById("abPopup")) {
|
||||
var selectedAB = document.getElementById("abPopup").selectedItem.value;
|
||||
setSearchScope(GetScopeForDirectoryURI(selectedAB));
|
||||
}
|
||||
for (var i=0; i<gSearchTerms.length; i++) {
|
||||
let searchTerm = gSearchTerms[i].obj;
|
||||
// If term is not yet initialized in the UI, change the original object.
|
||||
if (!searchTerm || !gSearchTerms[i].initialized)
|
||||
searchTerm = gSearchTerms[i].searchTerm;
|
||||
|
||||
searchTerm.booleanAnd = newBoolValue;
|
||||
searchTerm.matchAll = matchAllValue;
|
||||
}
|
||||
var searchTerms = document.getElementById("searchTermList");
|
||||
if (searchTerms)
|
||||
{
|
||||
if (!matchAllValue && searchTerms.hidden && !gTotalSearchTerms)
|
||||
onMore(null); // fake to get empty row.
|
||||
updateSearchTermsListbox(matchAllValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new search row with all the needed elements.
|
||||
*
|
||||
* @param index index of the position in the menulist where to add the row
|
||||
* @param scope a nsMsgSearchScope constant indicating scope of this search rule
|
||||
* @param searchTerm nsIMsgSearchTerm object to hold the search term
|
||||
* @param aUserAdded boolean indicating if the row addition was initiated by the user
|
||||
* (e.g. via the '+' button)
|
||||
*/
|
||||
function createSearchRow(index, scope, searchTerm, aUserAdded)
|
||||
{
|
||||
var searchAttr = document.createElement("searchattribute");
|
||||
var searchOp = document.createElement("searchoperator");
|
||||
var searchVal = document.createElement("searchvalue");
|
||||
|
||||
var moreButton = document.createElement("button");
|
||||
var lessButton = document.createElement("button");
|
||||
moreButton.setAttribute("class", "small-button");
|
||||
moreButton.setAttribute("oncommand", "onMore(event);");
|
||||
moreButton.setAttribute('label', '+');
|
||||
moreButton.setAttribute('tooltiptext', gMoreButtonTooltipText);
|
||||
lessButton.setAttribute("class", "small-button");
|
||||
lessButton.setAttribute("oncommand", "onLess(event);");
|
||||
lessButton.setAttribute('label', '\u2212');
|
||||
lessButton.setAttribute('tooltiptext', gLessButtonTooltipText);
|
||||
|
||||
// now set up ids:
|
||||
searchAttr.id = "searchAttr" + gUniqueSearchTermCounter;
|
||||
searchOp.id = "searchOp" + gUniqueSearchTermCounter;
|
||||
searchVal.id = "searchVal" + gUniqueSearchTermCounter;
|
||||
|
||||
searchAttr.setAttribute("for", searchOp.id + "," + searchVal.id);
|
||||
searchOp.setAttribute("opfor", searchVal.id);
|
||||
|
||||
var rowdata = [searchAttr, searchOp, searchVal,
|
||||
[moreButton, lessButton] ];
|
||||
var searchrow = constructRow(rowdata);
|
||||
searchrow.id = "searchRow" + gUniqueSearchTermCounter;
|
||||
|
||||
var searchTermObj = new searchTermContainer;
|
||||
searchTermObj.searchattribute = searchAttr;
|
||||
searchTermObj.searchoperator = searchOp;
|
||||
searchTermObj.searchvalue = searchVal;
|
||||
|
||||
// now insert the new search term into our list of terms
|
||||
gSearchTerms.splice(index, 0, {obj:searchTermObj, scope:scope, searchTerm:searchTerm, initialized:false});
|
||||
|
||||
var editFilter = null;
|
||||
try { editFilter = gFilter; } catch(e) { }
|
||||
|
||||
var editMailView = null;
|
||||
try { editMailView = gMailView; } catch(e) { }
|
||||
|
||||
if ((!editFilter && !editMailView) ||
|
||||
(editFilter && index == gTotalSearchTerms) ||
|
||||
(editMailView && index == gTotalSearchTerms))
|
||||
gLoading = false;
|
||||
|
||||
// index is index of new row
|
||||
// gTotalSearchTerms has not been updated yet
|
||||
if (gLoading || index == gTotalSearchTerms) {
|
||||
gSearchTermList.appendChild(searchrow);
|
||||
}
|
||||
else {
|
||||
var currentItem = gSearchTermList.getItemAtIndex(index);
|
||||
gSearchTermList.insertBefore(searchrow, currentItem);
|
||||
}
|
||||
|
||||
// If this row was added by user action, focus the value field.
|
||||
if (aUserAdded) {
|
||||
document.commandDispatcher.advanceFocusIntoSubtree(searchVal);
|
||||
searchrow.setAttribute("highlight", "true");
|
||||
}
|
||||
|
||||
// bump our unique search term counter
|
||||
gUniqueSearchTermCounter++;
|
||||
}
|
||||
|
||||
function initializeTermFromId(id)
|
||||
{
|
||||
initializeTermFromIndex(getSearchRowIndexForElement(document.getElementById(id)));
|
||||
}
|
||||
|
||||
function initializeTermFromIndex(index)
|
||||
{
|
||||
var searchTermObj = gSearchTerms[index].obj;
|
||||
|
||||
searchTermObj.searchScope = gSearchTerms[index].scope;
|
||||
// the search term will initialize the searchTerm element, including
|
||||
// .booleanAnd
|
||||
if (gSearchTerms[index].searchTerm)
|
||||
searchTermObj.searchTerm = gSearchTerms[index].searchTerm;
|
||||
// here, we don't have a searchTerm, so it's probably a new element -
|
||||
// we'll initialize the .booleanAnd from the existing setting in
|
||||
// the UI
|
||||
else
|
||||
{
|
||||
searchTermObj.booleanAnd = (gSearchBooleanRadiogroup.value == "and");
|
||||
if (index)
|
||||
{
|
||||
// If we weren't pre-initialized with a searchTerm then steal the
|
||||
// search attribute and operator from the previous row.
|
||||
searchTermObj.searchattribute.value = gSearchTerms[index - 1].obj.searchattribute.value;
|
||||
searchTermObj.searchoperator.value = gSearchTerms[index - 1].obj.searchoperator.value;
|
||||
}
|
||||
}
|
||||
|
||||
gSearchTerms[index].initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <listitem> using the array children as the children
|
||||
* of each listcell.
|
||||
* @param aChildren An array of XUL elements to put into the listitem.
|
||||
* Each array member is put into a separate listcell.
|
||||
* If the member itself is an array of elements,
|
||||
* all of them are put into the same listcell.
|
||||
*/
|
||||
function constructRow(aChildren)
|
||||
{
|
||||
let listitem = document.createElement("listitem");
|
||||
listitem.setAttribute("allowevents", "true");
|
||||
for (let i = 0; i < aChildren.length; i++) {
|
||||
let listcell = document.createElement("listcell");
|
||||
let child = aChildren[i];
|
||||
|
||||
if (child instanceof Array) {
|
||||
for (let j = 0; j < child.length; j++)
|
||||
listcell.appendChild(child[j]);
|
||||
} else {
|
||||
child.setAttribute("flex", "1");
|
||||
listcell.appendChild(child);
|
||||
}
|
||||
listitem.appendChild(listcell);
|
||||
}
|
||||
return listitem;
|
||||
}
|
||||
|
||||
function removeSearchRow(index)
|
||||
{
|
||||
var searchTermObj = gSearchTerms[index].obj;
|
||||
if (!searchTermObj) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if it is an existing (but offscreen) term,
|
||||
// make sure it is initialized before we remove it.
|
||||
if (!gSearchTerms[index].searchTerm && !gSearchTerms[index].initialized)
|
||||
initializeTermFromIndex(index);
|
||||
|
||||
// need to remove row from list, so walk upwards from the
|
||||
// searchattribute to find the first <listitem>
|
||||
var listitem = searchTermObj.searchattribute;
|
||||
|
||||
while (listitem) {
|
||||
if (listitem.localName == "listitem") break;
|
||||
listitem = listitem.parentNode;
|
||||
}
|
||||
|
||||
if (!listitem) {
|
||||
dump("Error: couldn't find parent listitem!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (searchTermObj.searchTerm) {
|
||||
gSearchRemovedTerms[gSearchRemovedTerms.length] = searchTermObj.searchTerm;
|
||||
} else {
|
||||
//dump("That wasn't real. ignoring \n");
|
||||
}
|
||||
|
||||
listitem.remove();
|
||||
|
||||
// now remove the item from our list of terms
|
||||
gSearchTerms.splice(index, 1);
|
||||
}
|
||||
|
||||
// save the search terms from the UI back to the actual search terms
|
||||
// searchTerms: nsISupportsArray of terms
|
||||
// termOwner: object which can contain and create the terms
|
||||
// (will be unnecessary if we just make terms creatable
|
||||
// via XPCOM)
|
||||
function saveSearchTerms(searchTerms, termOwner)
|
||||
{
|
||||
var matchAll = gSearchBooleanRadiogroup.value == 'matchAll';
|
||||
var i;
|
||||
for (i = 0; i < gSearchRemovedTerms.length; i++)
|
||||
searchTerms.RemoveElement(gSearchRemovedTerms[i]);
|
||||
|
||||
for (i = 0; i < gSearchTerms.length; i++) {
|
||||
try {
|
||||
gSearchTerms[i].obj.matchAll = matchAll;
|
||||
var searchTerm = gSearchTerms[i].obj.searchTerm;
|
||||
if (searchTerm) {
|
||||
gSearchTerms[i].obj.save();
|
||||
} else if (!gSearchTerms[i].initialized) {
|
||||
// the term might be an offscreen one we haven't initialized yet
|
||||
searchTerm = gSearchTerms[i].searchTerm;
|
||||
} else {
|
||||
// need to create a new searchTerm, and somehow save it to that
|
||||
searchTerm = termOwner.createTerm();
|
||||
gSearchTerms[i].obj.saveTo(searchTerm);
|
||||
// this might not be the right place for the term,
|
||||
// but we need to make the array longer anyway
|
||||
termOwner.appendTerm(searchTerm);
|
||||
}
|
||||
searchTerms.SetElementAt(i, searchTerm);
|
||||
} catch (ex) {
|
||||
dump("** Error saving element " + i + ": " + ex + "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onReset(event)
|
||||
{
|
||||
while (gTotalSearchTerms>0)
|
||||
removeSearchRow(--gTotalSearchTerms);
|
||||
onMore(null);
|
||||
}
|
||||
|
||||
function hideMatchAllItem()
|
||||
{
|
||||
var allItems = document.getElementById('matchAllItem');
|
||||
if (allItems)
|
||||
allItems.hidden = true;
|
||||
}
|
||||
66
mailnews/base/search/content/searchTermOverlay.xul
Normal file
66
mailnews/base/search/content/searchTermOverlay.xul
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!-- 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/. -->
|
||||
|
||||
<!DOCTYPE overlay SYSTEM "chrome://messenger/locale/searchTermOverlay.dtd">
|
||||
|
||||
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://messenger/content/searchTermOverlay.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://messenger/content/dateFormat.js"/>
|
||||
|
||||
<vbox id="searchTermListBox">
|
||||
|
||||
<radiogroup id="booleanAndGroup" orient="horizontal" value="and"
|
||||
oncommand="booleanChanged(event);">
|
||||
<radio value="and" label="&matchAll.label;"
|
||||
accesskey="&matchAll.accesskey;"/>
|
||||
<radio value="or" label="&matchAny.label;"
|
||||
accesskey="&matchAny.accesskey;"/>
|
||||
<radio value="matchAll" id="matchAllItem" label="&matchAllMsgs.label;"
|
||||
accesskey="&matchAllMsgs.accesskey;"/>
|
||||
</radiogroup>
|
||||
|
||||
<hbox flex="1">
|
||||
<hbox id="searchterms"/>
|
||||
<listbox flex="1" id="searchTermList" rows="4" minheight="35%">
|
||||
<listcols>
|
||||
<listcol flex="&searchTermListAttributesFlexValue;"/>
|
||||
<listcol flex="&searchTermListOperatorsFlexValue;"/>
|
||||
<listcol flex="&searchTermListValueFlexValue;"/>
|
||||
<listcol class="filler"/>
|
||||
</listcols>
|
||||
|
||||
<!-- this is what the listitems will look like:
|
||||
<listitem id="searchListItem">
|
||||
<listcell allowevents="true">
|
||||
<searchattribute id="searchAttr1" for="searchOp1,searchValue1" flex="1"/>
|
||||
</listcell>
|
||||
<listcell allowevents="true">
|
||||
<searchoperator id="searchOp1" opfor="searchValue1" flex="1"/>
|
||||
</listcell>
|
||||
<listcell allowevents="true" >
|
||||
<searchvalue id="searchValue1" flex="1"/>
|
||||
</listcell>
|
||||
<listcell>
|
||||
<button label="add"/>
|
||||
<button label="remove"/>
|
||||
</listcell>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<listcell label="the.."/>
|
||||
<listcell label="contains.."/>
|
||||
<listcell label="text here"/>
|
||||
<listcell label="+/-"/>
|
||||
</listitem>
|
||||
-->
|
||||
</listbox>
|
||||
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
||||
</overlay>
|
||||
738
mailnews/base/search/content/searchWidgets.xml
Normal file
738
mailnews/base/search/content/searchWidgets.xml
Normal file
|
|
@ -0,0 +1,738 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!-- 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/. -->
|
||||
|
||||
<!--
|
||||
This file has the following external dependencies:
|
||||
-gFilterActionStrings from FilterEditor.js
|
||||
-gFilterList from FilterEditor.js
|
||||
-gFilter from FilterEditor.js
|
||||
-gCustomActions from FilterEditor.js
|
||||
-gFilterType from FilterEditor.js
|
||||
-checkActionsReorder from FilterEditor.js
|
||||
-->
|
||||
|
||||
<!DOCTYPE dialog [
|
||||
<!ENTITY % filterEditorDTD SYSTEM "chrome://messenger/locale/FilterEditor.dtd" >
|
||||
%filterEditorDTD;
|
||||
<!ENTITY % messengerDTD SYSTEM "chrome://messenger/locale/messenger.dtd" >
|
||||
%messengerDTD;
|
||||
]>
|
||||
|
||||
<bindings id="filterBindings"
|
||||
xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:nc="http://home.netscape.com/NC-rdf#"
|
||||
xmlns:xbl="http://www.mozilla.org/xbl">
|
||||
|
||||
<binding id="ruleactiontype-menulist">
|
||||
<content>
|
||||
<xul:menulist class="ruleaction-type">
|
||||
<xul:menupopup>
|
||||
<xul:menuitem label="&moveMessage.label;" value="movemessage" enablefornews="false"/>
|
||||
<xul:menuitem label="©Message.label;" value="copymessage"/>
|
||||
<xul:menuseparator enablefornews="false"/>
|
||||
<xul:menuitem label="&forwardTo.label;" value="forwardmessage" enablefornews="false"/>
|
||||
<xul:menuitem label="&replyWithTemplate.label;" value="replytomessage" enablefornews="false"/>
|
||||
<xul:menuseparator/>
|
||||
<xul:menuitem label="&markMessageRead.label;" value="markasread"/>
|
||||
<xul:menuitem label="&markMessageUnread.label;" value="markasunread"/>
|
||||
<xul:menuitem label="&markMessageStarred.label;" value="markasflagged"/>
|
||||
<xul:menuitem label="&setPriority.label;" value="setpriorityto"/>
|
||||
<xul:menuitem label="&addTag.label;" value="addtagtomessage"/>
|
||||
<xul:menuitem label="&setJunkScore.label;" value="setjunkscore" enablefornews="false"/>
|
||||
<xul:menuseparator enableforpop3="true"/>
|
||||
<xul:menuitem label="&deleteMessage.label;" value="deletemessage"/>
|
||||
<xul:menuitem label="&deleteFromPOP.label;" value="deletefrompopserver" enableforpop3="true"/>
|
||||
<xul:menuitem label="&fetchFromPOP.label;" value="fetchfrompopserver" enableforpop3="true"/>
|
||||
<xul:menuseparator/>
|
||||
<xul:menuitem label="&ignoreThread.label;" value="ignorethread"/>
|
||||
<xul:menuitem label="&ignoreSubthread.label;" value="ignoresubthread"/>
|
||||
<xul:menuitem label="&watchThread.label;" value="watchthread"/>
|
||||
<xul:menuseparator/>
|
||||
<xul:menuitem label="&stopExecution.label;" value="stopexecution"/>
|
||||
</xul:menupopup>
|
||||
</xul:menulist>
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<constructor>
|
||||
<![CDATA[
|
||||
this.addCustomActions();
|
||||
this.hideInvalidActions();
|
||||
// differentiate between creating a new, next available action,
|
||||
// and creating a row which will be initialized with an action
|
||||
if (!this.parentNode.hasAttribute('initialActionIndex'))
|
||||
{
|
||||
var unavailableActions = this.usedActionsList();
|
||||
// select the first one that's not in the list
|
||||
for (var index = 0; index < this.menuitems.length; index++)
|
||||
{
|
||||
var menu = this.menuitems[index];
|
||||
if (!(menu.value in unavailableActions) && !menu.hidden)
|
||||
{
|
||||
this.menulist.value = menu.value;
|
||||
this.parentNode.setAttribute('value', menu.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.parentNode.mActionTypeInitialized = true;
|
||||
this.parentNode.clearInitialActionIndex();
|
||||
}
|
||||
]]>
|
||||
</constructor>
|
||||
|
||||
<field name="menulist">document.getAnonymousNodes(this)[0]</field>
|
||||
<field name="menuitems">this.menulist.getElementsByTagNameNS(this.menulist.namespaceURI, 'menuitem')</field>
|
||||
|
||||
<method name="hideInvalidActions">
|
||||
<body>
|
||||
<![CDATA[
|
||||
let menupopup = this.menulist.menupopup;
|
||||
let scope = getScopeFromFilterList(gFilterList);
|
||||
|
||||
// walk through the list of filter actions and hide any actions which aren't valid
|
||||
// for our given scope (news, imap, pop, etc) and context
|
||||
let elements, i;
|
||||
|
||||
// disable / enable all elements in the "filteractionlist"
|
||||
// based on the scope and the "enablefornews" attribute
|
||||
elements = menupopup.getElementsByAttribute("enablefornews", "true");
|
||||
for (i = 0; i < elements.length; i++)
|
||||
elements[i].hidden = scope != Components.interfaces.nsMsgSearchScope.newsFilter;
|
||||
|
||||
elements = menupopup.getElementsByAttribute("enablefornews", "false");
|
||||
for (i = 0; i < elements.length; i++)
|
||||
elements[i].hidden = scope == Components.interfaces.nsMsgSearchScope.newsFilter;
|
||||
|
||||
elements = menupopup.getElementsByAttribute("enableforpop3", "true");
|
||||
for (i = 0; i < elements.length; i++)
|
||||
elements[i].hidden = !((gFilterList.folder.server.type == "pop3") ||
|
||||
(gFilterList.folder.server.type == "none"));
|
||||
|
||||
elements = menupopup.getElementsByAttribute("isCustom", "true");
|
||||
// Note there might be an additional element here as a placeholder
|
||||
// for a missing action, so we iterate over the known actions
|
||||
// instead of the elements.
|
||||
for (i = 0; i < gCustomActions.length; i++)
|
||||
elements[i].hidden = !gCustomActions[i]
|
||||
.isValidForType(gFilterType, scope);
|
||||
|
||||
// Disable "Reply with Template" if there are no templates.
|
||||
if (!this.getTemplates(false)) {
|
||||
elements = menupopup.getElementsByAttribute("value", "replytomessage");
|
||||
if (elements.length == 1)
|
||||
elements[0].hidden = true;
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="addCustomActions">
|
||||
<body>
|
||||
<![CDATA[
|
||||
var menupopup = this.menulist.menupopup;
|
||||
for (var i = 0; i < gCustomActions.length; i++)
|
||||
{
|
||||
var customAction = gCustomActions[i];
|
||||
var menuitem = document.createElementNS(
|
||||
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
|
||||
"xul:menuitem");
|
||||
menuitem.setAttribute("label", customAction.name);
|
||||
menuitem.setAttribute("value", customAction.id);
|
||||
menuitem.setAttribute("isCustom", "true");
|
||||
menupopup.appendChild(menuitem);
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<!-- returns a hash containing all of the filter actions which are currently being used by other filteractionrows -->
|
||||
<method name="usedActionsList">
|
||||
<body>
|
||||
<![CDATA[
|
||||
var usedActions = {};
|
||||
var currentFilterActionRow = this.parentNode;
|
||||
var listBox = currentFilterActionRow.mListBox; // need to account for the list item
|
||||
// now iterate over each list item in the list box
|
||||
for (var index = 0; index < listBox.getRowCount(); index++)
|
||||
{
|
||||
var filterActionRow = listBox.getItemAtIndex(index);
|
||||
if (filterActionRow != currentFilterActionRow)
|
||||
{
|
||||
var actionValue = filterActionRow.getAttribute('value');
|
||||
|
||||
// let custom actions decide if dups are allowed
|
||||
var isCustom = false;
|
||||
for (var i = 0; i < gCustomActions.length; i++)
|
||||
{
|
||||
if (gCustomActions[i].id == actionValue)
|
||||
{
|
||||
isCustom = true;
|
||||
if (!gCustomActions[i].allowDuplicates)
|
||||
usedActions[actionValue] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isCustom) {
|
||||
// The following actions can appear more than once in a single filter
|
||||
// so do not set them as already used.
|
||||
if (actionValue != 'addtagtomessage' &&
|
||||
actionValue != 'forwardmessage' &&
|
||||
actionValue != 'copymessage')
|
||||
usedActions[actionValue] = true;
|
||||
// If either Delete message or Move message exists, disable the other one.
|
||||
// It does not make sense to apply both to the same message.
|
||||
if (actionValue == 'deletemessage')
|
||||
usedActions['movemessage'] = true;
|
||||
else if (actionValue == 'movemessage')
|
||||
usedActions['deletemessage'] = true;
|
||||
// The same with Mark as read/Mark as Unread.
|
||||
else if (actionValue == 'markasread')
|
||||
usedActions['markasunread'] = true;
|
||||
else if (actionValue == 'markasunread')
|
||||
usedActions['markasread'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return usedActions;
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
- Check if there exist any templates in this account.
|
||||
-
|
||||
- @param populateTemplateList If true, create menuitems representing
|
||||
- the found templates.
|
||||
- @param templateMenuList The menulist element to create items in.
|
||||
-
|
||||
- @return True if at least one template was found, otherwise false.
|
||||
-->
|
||||
<method name="getTemplates">
|
||||
<parameter name="populateTemplateList"/>
|
||||
<parameter name="templateMenuList"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
Components.utils.import("resource:///modules/iteratorUtils.jsm", this);
|
||||
let identitiesRaw = MailServices.accounts
|
||||
.getIdentitiesForServer(gFilterList.folder.server);
|
||||
let identities = Array.from(this.fixIterator(identitiesRaw,
|
||||
Components.interfaces.nsIMsgIdentity));
|
||||
|
||||
if (!identities.length) // typically if this is Local Folders
|
||||
identities.push(MailServices.accounts.defaultAccount.defaultIdentity);
|
||||
|
||||
let templateFound = false;
|
||||
let foldersScanned = [];
|
||||
|
||||
for (let identity of identities) {
|
||||
let enumerator = null;
|
||||
let msgFolder;
|
||||
try {
|
||||
msgFolder = Components.classes["@mozilla.org/rdf/rdf-service;1"]
|
||||
.getService(Components.interfaces.nsIRDFService)
|
||||
.GetResource(identity.stationeryFolder)
|
||||
.QueryInterface(Components.interfaces.nsIMsgFolder);
|
||||
// If we already processed this folder, do not set enumerator
|
||||
// so that we skip this identity.
|
||||
if (foldersScanned.indexOf(msgFolder) == -1) {
|
||||
foldersScanned.push(msgFolder);
|
||||
enumerator = msgFolder.msgDatabase.EnumerateMessages();
|
||||
}
|
||||
} catch (e) {
|
||||
// The Templates folder may not exist, that is OK.
|
||||
}
|
||||
|
||||
if (!enumerator)
|
||||
continue;
|
||||
|
||||
while (enumerator.hasMoreElements()) {
|
||||
let header = enumerator.getNext();
|
||||
if (header instanceof Components.interfaces.nsIMsgDBHdr) {
|
||||
templateFound = true;
|
||||
if (!populateTemplateList)
|
||||
return true;
|
||||
let msgTemplateUri = msgFolder.URI + "?messageId=" +
|
||||
header.messageId + '&subject=' + header.mime2DecodedSubject;
|
||||
let newItem = templateMenuList.appendItem(header.mime2DecodedSubject,
|
||||
msgTemplateUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return templateFound;
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
</implementation>
|
||||
|
||||
<handlers>
|
||||
<handler event="command">
|
||||
<![CDATA[
|
||||
this.parentNode.setAttribute('value', this.menulist.value);
|
||||
checkActionsReorder();
|
||||
]]>
|
||||
</handler>
|
||||
|
||||
<handler event="popupshowing">
|
||||
<![CDATA[
|
||||
var unavailableActions = this.usedActionsList();
|
||||
for (var index = 0; index < this.menuitems.length; index++)
|
||||
{
|
||||
var menu = this.menuitems[index];
|
||||
menu.setAttribute('disabled', menu.value in unavailableActions);
|
||||
}
|
||||
]]>
|
||||
</handler>
|
||||
</handlers>
|
||||
</binding>
|
||||
|
||||
<!-- This binding exists to disable the default binding of a listitem
|
||||
in the search terms. -->
|
||||
<binding id="listitem">
|
||||
<implementation>
|
||||
<method name="_fireEvent">
|
||||
<parameter name="aName"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
/* This provides a dummy _fireEvent function that
|
||||
the listbox expects to be able to call.
|
||||
See bug 202036. */
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="ruleaction" extends="#listitem">
|
||||
<content allowevents="true">
|
||||
<xul:listcell class="ruleactiontype"
|
||||
orient="vertical" align="stretch" pack="center"/>
|
||||
<xul:listcell class="ruleactiontarget" xbl:inherits="type=value"
|
||||
orient="vertical" align="stretch" pack="center"/>
|
||||
<xul:listcell>
|
||||
<xul:button class="small-button"
|
||||
label="+"
|
||||
tooltiptext="&addAction.tooltip;"
|
||||
oncommand="this.parentNode.parentNode.addRow();"/>
|
||||
<xul:button class="small-button"
|
||||
label="−"
|
||||
tooltiptext="&removeAction.tooltip;"
|
||||
oncommand="this.parentNode.parentNode.removeRow();"
|
||||
anonid="removeButton"/>
|
||||
</xul:listcell>
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<field name="mListBox">this.parentNode</field>
|
||||
<field name="mRemoveButton">document.getAnonymousElementByAttribute(this, "anonid", "removeButton")</field>
|
||||
<field name="mActionTypeInitialized">false</field>
|
||||
<field name="mRuleActionTargetInitialized">false</field>
|
||||
<field name="mRuleActionType">document.getAnonymousNodes(this)[0]</field>
|
||||
|
||||
<method name="clearInitialActionIndex">
|
||||
<body>
|
||||
<![CDATA[
|
||||
// we should only remove the initialActionIndex after we have been told that
|
||||
// both the rule action type and the rule action target have both been built since they both need
|
||||
// this piece of information. This complication arises because both of these child elements are getting
|
||||
// bound asynchronously after the search row has been constructed
|
||||
|
||||
if (this.mActionTypeInitialized && this.mRuleActionTargetInitialized)
|
||||
this.removeAttribute('initialActionIndex');
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="initWithAction">
|
||||
<parameter name="aFilterAction"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
var filterActionStr;
|
||||
var actionTarget = document.getAnonymousNodes(this)[1];
|
||||
var actionItem = document.getAnonymousNodes(actionTarget);
|
||||
var nsMsgFilterAction = Components.interfaces.nsMsgFilterAction;
|
||||
switch (aFilterAction.type)
|
||||
{
|
||||
case nsMsgFilterAction.Custom:
|
||||
filterActionStr = aFilterAction.customId;
|
||||
if (actionItem)
|
||||
actionItem[0].value = aFilterAction.strValue;
|
||||
|
||||
// Make sure the custom action has been added. If not, it
|
||||
// probably was from an extension that has been removed. We'll
|
||||
// show a dummy menuitem to warn the user.
|
||||
var needCustomLabel = true;
|
||||
for (var i = 0; i < gCustomActions.length; i++)
|
||||
{
|
||||
if (gCustomActions[i].id == filterActionStr)
|
||||
{
|
||||
needCustomLabel = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (needCustomLabel)
|
||||
{
|
||||
var menuitem = document.createElementNS(
|
||||
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
|
||||
"xul:menuitem");
|
||||
menuitem.setAttribute("label",
|
||||
gFilterBundle.getString("filterMissingCustomAction"));
|
||||
menuitem.setAttribute("value", filterActionStr);
|
||||
menuitem.disabled = true;
|
||||
this.mRuleActionType.menulist.menupopup.appendChild(menuitem);
|
||||
var scriptError = Components.classes["@mozilla.org/scripterror;1"]
|
||||
.createInstance(Components.interfaces.nsIScriptError);
|
||||
scriptError.init("Missing custom action " + filterActionStr,
|
||||
null, null, 0, 0,
|
||||
Components.interfaces.nsIScriptError.errorFlag,
|
||||
"component javascript");
|
||||
Services.console.logMessage(scriptError);
|
||||
}
|
||||
break;
|
||||
case nsMsgFilterAction.MoveToFolder:
|
||||
case nsMsgFilterAction.CopyToFolder:
|
||||
actionItem[0].value = aFilterAction.targetFolderUri;
|
||||
break;
|
||||
case nsMsgFilterAction.Reply:
|
||||
case nsMsgFilterAction.Forward:
|
||||
actionItem[0].value = aFilterAction.strValue;
|
||||
break;
|
||||
case nsMsgFilterAction.Label:
|
||||
actionItem[0].value = aFilterAction.label;
|
||||
break;
|
||||
case nsMsgFilterAction.ChangePriority:
|
||||
actionItem[0].value = aFilterAction.priority;
|
||||
break;
|
||||
case nsMsgFilterAction.JunkScore:
|
||||
actionItem[0].value = aFilterAction.junkScore;
|
||||
break;
|
||||
case nsMsgFilterAction.AddTag:
|
||||
actionItem[0].value = aFilterAction.strValue;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (aFilterAction.type != nsMsgFilterAction.Custom)
|
||||
filterActionStr = gFilterActionStrings[aFilterAction.type];
|
||||
document.getAnonymousNodes(this.mRuleActionType)[0]
|
||||
.value = filterActionStr;
|
||||
this.mRuleActionTargetInitialized = true;
|
||||
this.clearInitialActionIndex();
|
||||
checkActionsReorder();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="validateAction">
|
||||
<body>
|
||||
<![CDATA[
|
||||
// returns true if this row represents a valid filter action and false otherwise.
|
||||
// This routine also prompts the user.
|
||||
Components.utils.import("resource:///modules/MailUtils.js", this);
|
||||
var filterActionString = this.getAttribute('value');
|
||||
var actionTarget = document.getAnonymousNodes(this)[1];
|
||||
var errorString, customError;
|
||||
|
||||
switch (filterActionString)
|
||||
{
|
||||
case "movemessage":
|
||||
case "copymessage":
|
||||
let msgFolder = document.getAnonymousNodes(actionTarget)[0].value ?
|
||||
this.MailUtils.getFolderForURI(document.getAnonymousNodes(actionTarget)[0].value) : null;
|
||||
if (!msgFolder || !msgFolder.canFileMessages)
|
||||
errorString = "mustSelectFolder";
|
||||
break;
|
||||
case "forwardmessage":
|
||||
if (document.getAnonymousNodes(actionTarget)[0].value.length < 3 ||
|
||||
document.getAnonymousNodes(actionTarget)[0].value.indexOf('@') < 1)
|
||||
errorString = "enterValidEmailAddress";
|
||||
break;
|
||||
case "replytomessage":
|
||||
if (!document.getAnonymousNodes(actionTarget)[0].selectedItem)
|
||||
errorString = "pickTemplateToReplyWith";
|
||||
break;
|
||||
default:
|
||||
// some custom actions have no action value node
|
||||
if (!document.getAnonymousNodes(actionTarget))
|
||||
return true;
|
||||
// locate the correct custom action, and check validity
|
||||
for (var i = 0; i < gCustomActions.length; i++)
|
||||
if (gCustomActions[i].id == filterActionString)
|
||||
{
|
||||
customError =
|
||||
gCustomActions[i].validateActionValue(
|
||||
document.getAnonymousNodes(actionTarget)[0].value,
|
||||
gFilterList.folder, gFilterType);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
errorString = errorString ?
|
||||
gFilterBundle.getString(errorString) :
|
||||
customError;
|
||||
if (errorString)
|
||||
Services.prompt.alert(window, null, errorString);
|
||||
|
||||
return !errorString;
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="saveToFilter">
|
||||
<parameter name="aFilter"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
// create a new filter action, fill it in, and then append it to the filter
|
||||
var filterAction = aFilter.createAction();
|
||||
var filterActionString = this.getAttribute('value');
|
||||
filterAction.type = gFilterActionStrings.indexOf(filterActionString);
|
||||
var actionTarget = document.getAnonymousNodes(this)[1];
|
||||
var actionItem = document.getAnonymousNodes(actionTarget);
|
||||
var nsMsgFilterAction = Components.interfaces.nsMsgFilterAction;
|
||||
switch (filterAction.type)
|
||||
{
|
||||
case nsMsgFilterAction.Label:
|
||||
filterAction.label = actionItem[0].getAttribute("value");
|
||||
break;
|
||||
case nsMsgFilterAction.ChangePriority:
|
||||
filterAction.priority = actionItem[0].getAttribute("value");
|
||||
break;
|
||||
case nsMsgFilterAction.MoveToFolder:
|
||||
case nsMsgFilterAction.CopyToFolder:
|
||||
filterAction.targetFolderUri = actionItem[0].value;
|
||||
break;
|
||||
case nsMsgFilterAction.JunkScore:
|
||||
filterAction.junkScore = actionItem[0].value;
|
||||
break;
|
||||
case nsMsgFilterAction.Custom:
|
||||
filterAction.customId = filterActionString;
|
||||
// fall through to set the value
|
||||
default:
|
||||
if (actionItem)
|
||||
filterAction.strValue = actionItem[0].value;
|
||||
break;
|
||||
}
|
||||
aFilter.appendAction(filterAction);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="getActionStrings">
|
||||
<parameter name="aActionStrings"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
// Collect the action names and arguments in a plain string form.
|
||||
let actionTarget = document.getAnonymousNodes(this)[1];
|
||||
let actionItem = document.getAnonymousNodes(actionTarget);
|
||||
|
||||
aActionStrings.push({
|
||||
label: document.getAnonymousNodes(this.mRuleActionType)[0].label,
|
||||
argument: actionItem ?
|
||||
(actionItem[0].label ?
|
||||
actionItem[0].label : actionItem[0].value) : ""
|
||||
});
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="updateRemoveButton">
|
||||
<body>
|
||||
<![CDATA[
|
||||
// if we only have one row of actions, then disable the remove button for that row
|
||||
this.mListBox.getItemAtIndex(0).mRemoveButton.disabled = this.mListBox.getRowCount() == 1;
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="addRow">
|
||||
<body>
|
||||
<![CDATA[
|
||||
let listItem = document.createElement('listitem');
|
||||
listItem.className = 'ruleaction';
|
||||
listItem.setAttribute('onfocus','this.storeFocus();');
|
||||
this.mListBox.insertBefore(listItem, this.nextSibling);
|
||||
this.mListBox.ensureElementIsVisible(listItem);
|
||||
|
||||
// make sure the first remove button is enabled
|
||||
this.updateRemoveButton();
|
||||
checkActionsReorder();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="removeRow">
|
||||
<body>
|
||||
<![CDATA[
|
||||
// this.mListBox will fail after the row is removed, so save it
|
||||
let listBox = this.mListBox;
|
||||
if (listBox.getRowCount() > 1)
|
||||
this.remove();
|
||||
// can't use 'this' as it is destroyed now
|
||||
listBox.getItemAtIndex(0).updateRemoveButton();
|
||||
checkActionsReorder();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="storeFocus">
|
||||
<body>
|
||||
<![CDATA[
|
||||
// When this action row is focused, store its index in the parent listbox.
|
||||
this.mListBox.setAttribute("focusedAction", this.mListBox.getIndexOfItem(this));
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="ruleactiontarget-base">
|
||||
<implementation>
|
||||
<constructor>
|
||||
<![CDATA[
|
||||
if (this.parentNode.hasAttribute('initialActionIndex'))
|
||||
{
|
||||
let actionIndex = this.parentNode.getAttribute('initialActionIndex');
|
||||
let filterAction = gFilter.getActionAt(actionIndex);
|
||||
this.parentNode.initWithAction(filterAction);
|
||||
}
|
||||
this.parentNode.updateRemoveButton();
|
||||
]]>
|
||||
</constructor>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="ruleactiontarget-tag" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
|
||||
<content>
|
||||
<xul:menulist class="ruleactionitem">
|
||||
<xul:menupopup>
|
||||
</xul:menupopup>
|
||||
</xul:menulist>
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<constructor>
|
||||
<![CDATA[
|
||||
let menuPopup = document.getAnonymousNodes(this)[0].menupopup;
|
||||
let tagArray = MailServices.tags.getAllTags({});
|
||||
for (let i = 0; i < tagArray.length; ++i)
|
||||
{
|
||||
var taginfo = tagArray[i];
|
||||
var newMenuItem = document.createElement('menuitem');
|
||||
newMenuItem.setAttribute('label', taginfo.tag);
|
||||
newMenuItem.setAttribute('value', taginfo.key);
|
||||
menuPopup.appendChild(newMenuItem);
|
||||
}
|
||||
// propagating a pre-existing hack to make the tag get displayed correctly in the menulist
|
||||
// now that we've changed the tags for each menu list. We need to use the current selectedIndex
|
||||
// (if its defined) to handle the case where we were initialized with a filter action already.
|
||||
var currentItem = document.getAnonymousNodes(this)[0].selectedItem;
|
||||
document.getAnonymousNodes(this)[0].selectedItem = null;
|
||||
document.getAnonymousNodes(this)[0].selectedItem = currentItem;
|
||||
]]>
|
||||
</constructor>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="ruleactiontarget-priority" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
|
||||
<content>
|
||||
<xul:menulist class="ruleactionitem">
|
||||
<xul:menupopup>
|
||||
<xul:menuitem value="6" label="&highestPriorityCmd.label;"/>
|
||||
<xul:menuitem value="5" label="&highPriorityCmd.label;"/>
|
||||
<xul:menuitem value="4" label="&normalPriorityCmd.label;"/>
|
||||
<xul:menuitem value="3" label="&lowPriorityCmd.label;"/>
|
||||
<xul:menuitem value="2" label="&lowestPriorityCmd.label;"/>
|
||||
</xul:menupopup>
|
||||
</xul:menulist>
|
||||
</content>
|
||||
</binding>
|
||||
|
||||
<binding id="ruleactiontarget-junkscore" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
|
||||
<content>
|
||||
<xul:menulist class="ruleactionitem">
|
||||
<xul:menupopup>
|
||||
<xul:menuitem value="100" label="&junk.label;"/>
|
||||
<xul:menuitem value="0" label="¬Junk.label;"/>
|
||||
</xul:menupopup>
|
||||
</xul:menulist>
|
||||
</content>
|
||||
</binding>
|
||||
|
||||
<binding id="ruleactiontarget-replyto" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
|
||||
<content>
|
||||
<xul:menulist class="ruleactionitem">
|
||||
<xul:menupopup>
|
||||
</xul:menupopup>
|
||||
</xul:menulist>
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<constructor>
|
||||
<![CDATA[
|
||||
document.getAnonymousElementByAttribute(
|
||||
this.parentNode, "class", "ruleactiontype")
|
||||
.getTemplates(true, document.getAnonymousNodes(this)[0]);
|
||||
]]>
|
||||
</constructor>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="ruleactiontarget-forwardto" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
|
||||
<content>
|
||||
<xul:textbox class="ruleactionitem" flex="1"/>
|
||||
</content>
|
||||
</binding>
|
||||
|
||||
<binding id="ruleactiontarget-folder" extends="chrome://messenger/content/searchWidgets.xml#ruleactiontarget-base">
|
||||
<content>
|
||||
<xul:menulist class="ruleactionitem folderMenuItem"
|
||||
displayformat="verbose"
|
||||
oncommand="this.parentNode.setPicker(event);">
|
||||
<xul:menupopup type="folder"
|
||||
mode="filing"
|
||||
class="menulist-menupopup"
|
||||
showRecent="true"
|
||||
recentLabel="&recentFolders.label;"
|
||||
showFileHereLabel="true"/>
|
||||
</xul:menulist>
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<constructor>
|
||||
<![CDATA[
|
||||
Components.utils.import("resource:///modules/MailUtils.js", this);
|
||||
let folder = this.menulist.value ?
|
||||
this.MailUtils.getFolderForURI(this.menulist.value) :
|
||||
gFilterList.folder;
|
||||
// An account folder is not a move/copy target; show "Choose Folder".
|
||||
folder = folder.isServer ? null : folder;
|
||||
let menupopup = this.menulist.menupopup;
|
||||
// The menupopup constructor needs to finish first.
|
||||
setTimeout(function() { menupopup.selectFolder(folder); }, 0);
|
||||
]]>
|
||||
</constructor>
|
||||
|
||||
<field name="menulist">document.getAnonymousNodes(this)[0]</field>
|
||||
<method name="setPicker">
|
||||
<parameter name="aEvent"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
this.menulist.menupopup.selectFolder(aEvent.target._folder);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
</bindings>
|
||||
36
mailnews/base/search/content/viewLog.js
Normal file
36
mailnews/base/search/content/viewLog.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/* 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/. */
|
||||
|
||||
var gFilterList;
|
||||
var gLogFilters;
|
||||
var gLogView;
|
||||
|
||||
function onLoad()
|
||||
{
|
||||
gFilterList = window.arguments[0].filterList;
|
||||
|
||||
gLogFilters = document.getElementById("logFilters");
|
||||
gLogFilters.checked = gFilterList.loggingEnabled;
|
||||
|
||||
gLogView = document.getElementById("logView");
|
||||
|
||||
// for security, disable JS
|
||||
gLogView.docShell.allowJavascript = false;
|
||||
|
||||
gLogView.setAttribute("src", gFilterList.logURL);
|
||||
}
|
||||
|
||||
function toggleLogFilters()
|
||||
{
|
||||
gFilterList.loggingEnabled = gLogFilters.checked;
|
||||
}
|
||||
|
||||
function clearLog()
|
||||
{
|
||||
gFilterList.clearLog();
|
||||
|
||||
// reload the newly truncated file
|
||||
gLogView.reload();
|
||||
}
|
||||
|
||||
49
mailnews/base/search/content/viewLog.xul
Normal file
49
mailnews/base/search/content/viewLog.xul
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!-- 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/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://messenger/skin/messenger.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE dialog SYSTEM "chrome://messenger/locale/viewLog.dtd">
|
||||
|
||||
<dialog id="viewLogWindow"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="onLoad();"
|
||||
title="&viewLog.title;"
|
||||
windowtype="mailnews:filterlog"
|
||||
buttons="accept"
|
||||
buttonlabelaccept="&closeLog.label;"
|
||||
buttonaccesskeyaccept="&closeLog.accesskey;"
|
||||
ondialogaccept="window.close();"
|
||||
persist="screenX screenY width height"
|
||||
style="width: 40em; height: 25em;">
|
||||
|
||||
<script type="application/javascript" src="chrome://messenger/content/viewLog.js"/>
|
||||
|
||||
<vbox flex="1">
|
||||
<description>&viewLogInfo.text;</description>
|
||||
<hbox>
|
||||
<checkbox id="logFilters"
|
||||
label="&enableLog.label;"
|
||||
accesskey="&enableLog.accesskey;"
|
||||
oncommand="toggleLogFilters();"/>
|
||||
<spacer flex="1"/>
|
||||
<button label="&clearLog.label;"
|
||||
accesskey="&clearLog.accesskey;"
|
||||
oncommand="clearLog();"/>
|
||||
</hbox>
|
||||
<separator class="thin"/>
|
||||
<hbox flex="1">
|
||||
<browser id="logView"
|
||||
class="inset"
|
||||
type="content"
|
||||
disablehistory="true"
|
||||
disablesecurity="true"
|
||||
src="about:blank"
|
||||
autofind="false"
|
||||
flex="1"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</dialog>
|
||||
38
mailnews/base/search/public/moz.build
Normal file
38
mailnews/base/search/public/moz.build
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIMsgFilter.idl',
|
||||
'nsIMsgFilterCustomAction.idl',
|
||||
'nsIMsgFilterHitNotify.idl',
|
||||
'nsIMsgFilterList.idl',
|
||||
'nsIMsgFilterPlugin.idl',
|
||||
'nsIMsgFilterService.idl',
|
||||
'nsIMsgOperationListener.idl',
|
||||
'nsIMsgSearchAdapter.idl',
|
||||
'nsIMsgSearchCustomTerm.idl',
|
||||
'nsIMsgSearchNotify.idl',
|
||||
'nsIMsgSearchScopeTerm.idl',
|
||||
'nsIMsgSearchSession.idl',
|
||||
'nsIMsgSearchTerm.idl',
|
||||
'nsIMsgSearchValidityManager.idl',
|
||||
'nsIMsgSearchValidityTable.idl',
|
||||
'nsIMsgSearchValue.idl',
|
||||
'nsIMsgTraitService.idl',
|
||||
'nsMsgFilterCore.idl',
|
||||
'nsMsgSearchCore.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'msgsearch'
|
||||
|
||||
EXPORTS += [
|
||||
'nsMsgBodyHandler.h',
|
||||
'nsMsgResultElement.h',
|
||||
'nsMsgSearchAdapter.h',
|
||||
'nsMsgSearchBoolExpression.h',
|
||||
'nsMsgSearchScopeTerm.h',
|
||||
'nsMsgSearchTerm.h',
|
||||
]
|
||||
|
||||
142
mailnews/base/search/public/nsIMsgFilter.idl
Normal file
142
mailnews/base/search/public/nsIMsgFilter.idl
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsMsgFilterCore.idl"
|
||||
// Disable deprecation warnings generated by nsISupportsArray and associated
|
||||
// classes.
|
||||
%{C++
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning (disable : 4996)
|
||||
#endif
|
||||
%}
|
||||
interface nsISupportsArray;
|
||||
|
||||
interface nsIArray;
|
||||
interface nsIOutputStream;
|
||||
interface nsIMsgFilterCustomAction;
|
||||
interface nsIMsgFilterList;
|
||||
interface nsIMsgSearchScopeTerm;
|
||||
interface nsIMsgSearchValue;
|
||||
interface nsIMsgSearchTerm;
|
||||
|
||||
[scriptable, uuid(36d2748e-9246-44f3-bb74-46cbb0b8c23a)]
|
||||
interface nsIMsgRuleAction : nsISupports {
|
||||
|
||||
attribute nsMsgRuleActionType type;
|
||||
|
||||
// target priority.. throws an exception if the action is not priority
|
||||
attribute nsMsgPriorityValue priority;
|
||||
|
||||
// target folder.. throws an exception if the action is not move to folder
|
||||
attribute ACString targetFolderUri;
|
||||
|
||||
// target label. throws an exception if the action is not label
|
||||
attribute nsMsgLabelValue label;
|
||||
|
||||
attribute long junkScore;
|
||||
|
||||
attribute AUTF8String strValue;
|
||||
|
||||
// action id if type is Custom
|
||||
attribute ACString customId;
|
||||
|
||||
// custom action associated with customId
|
||||
// (which must be set prior to reading this attribute)
|
||||
readonly attribute nsIMsgFilterCustomAction customAction;
|
||||
|
||||
};
|
||||
|
||||
[scriptable, uuid(d304fcfc-b588-11e4-981c-770e1e5d46b0)]
|
||||
interface nsIMsgFilter : nsISupports {
|
||||
attribute nsMsgFilterTypeType filterType;
|
||||
/**
|
||||
* some filters are "temporary". For example, the filters we create when the user
|
||||
* filters return receipts to the Sent folder.
|
||||
* we don't show temporary filters in the UI
|
||||
* and we don't write them to disk.
|
||||
*/
|
||||
attribute boolean temporary;
|
||||
attribute boolean enabled;
|
||||
attribute AString filterName;
|
||||
attribute ACString filterDesc;
|
||||
attribute ACString unparsedBuffer; //holds the entire filter if we don't know how to handle it
|
||||
attribute boolean unparseable; //whether we could parse the filter or not
|
||||
|
||||
attribute nsIMsgFilterList filterList; // owning filter list
|
||||
|
||||
void AddTerm(in nsMsgSearchAttribValue attrib,
|
||||
in nsMsgSearchOpValue op,
|
||||
in nsIMsgSearchValue value,
|
||||
in boolean BooleanAND,
|
||||
in ACString arbitraryHeader);
|
||||
|
||||
void GetTerm(in long termIndex,
|
||||
out nsMsgSearchAttribValue attrib,
|
||||
out nsMsgSearchOpValue op,
|
||||
out nsIMsgSearchValue value, // bad! using shared structure
|
||||
out boolean BooleanAND,
|
||||
out ACString arbitraryHeader);
|
||||
|
||||
void appendTerm(in nsIMsgSearchTerm term);
|
||||
|
||||
nsIMsgSearchTerm createTerm();
|
||||
|
||||
attribute nsISupportsArray searchTerms;
|
||||
|
||||
attribute nsIMsgSearchScopeTerm scope;
|
||||
|
||||
// Marking noscript because "headers" is actually a null-separated
|
||||
// list of headers, which is not scriptable.
|
||||
[noscript] void MatchHdr(in nsIMsgDBHdr msgHdr, in nsIMsgFolder folder,
|
||||
in nsIMsgDatabase db,
|
||||
in string headers,
|
||||
// [array, size_is(headerSize)] in string headers,
|
||||
in unsigned long headerSize, out boolean result);
|
||||
|
||||
|
||||
/*
|
||||
* Report that Rule was matched and executed when filter logging is enabled.
|
||||
*
|
||||
* @param aFilterAction The filter rule that was invoked.
|
||||
* @param aHeader The header information of the message acted on by
|
||||
* the filter.
|
||||
*/
|
||||
void logRuleHit(in nsIMsgRuleAction aFilterAction,
|
||||
in nsIMsgDBHdr aHeader);
|
||||
|
||||
/* Report that filtering failed for some reason when filter logging is enabled.
|
||||
*
|
||||
* @param aFilterAction Filter rule that was invoked.
|
||||
* @param aHeader Header of the message acted on by the filter.
|
||||
* @param aRcode Error code returned by low-level routine that
|
||||
* led to the filter failure.
|
||||
* @param aErrmsg Error message
|
||||
*/
|
||||
void logRuleHitFail(in nsIMsgRuleAction aFilterAction,
|
||||
in nsIMsgDBHdr aHeader,
|
||||
in nsresult aRcode,
|
||||
in string aErrmsg );
|
||||
|
||||
nsIMsgRuleAction createAction();
|
||||
|
||||
nsIMsgRuleAction getActionAt(in unsigned long aIndex);
|
||||
|
||||
long getActionIndex(in nsIMsgRuleAction aAction);
|
||||
|
||||
void appendAction(in nsIMsgRuleAction action);
|
||||
|
||||
readonly attribute unsigned long actionCount;
|
||||
|
||||
void clearActionList();
|
||||
|
||||
// Returns the action list in the order it will be really executed in.
|
||||
readonly attribute nsIArray sortedActionList;
|
||||
|
||||
void SaveToTextFile(in nsIOutputStream aStream);
|
||||
};
|
||||
90
mailnews/base/search/public/nsIMsgFilterCustomAction.idl
Normal file
90
mailnews/base/search/public/nsIMsgFilterCustomAction.idl
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/* 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/. */
|
||||
|
||||
#include "nsMsgFilterCore.idl"
|
||||
|
||||
interface nsIArray;
|
||||
interface nsIMsgCopyServiceListener;
|
||||
interface nsIMsgWindow;
|
||||
|
||||
/**
|
||||
* describes a custom action added to a message filter
|
||||
*/
|
||||
[scriptable,uuid(4699C41E-3671-436e-B6AE-4FD8106747E4)]
|
||||
interface nsIMsgFilterCustomAction : nsISupports
|
||||
{
|
||||
/* globally unique string to identify this filter action.
|
||||
* recommended form: ExtensionName@example.com#ActionName
|
||||
*/
|
||||
readonly attribute ACString id;
|
||||
|
||||
/* action name to display in action list. This should be localized. */
|
||||
readonly attribute AString name;
|
||||
|
||||
/**
|
||||
* Is this custom action valid for a particular filter type?
|
||||
*
|
||||
* @param type the filter type
|
||||
* @param scope the search scope
|
||||
*
|
||||
* @return true if valid
|
||||
*/
|
||||
boolean isValidForType(in nsMsgFilterTypeType type, in nsMsgSearchScopeValue scope);
|
||||
|
||||
/**
|
||||
* After the user inputs a particular action value for the action, determine
|
||||
* if that value is valid.
|
||||
*
|
||||
* @param actionValue The value entered.
|
||||
* @param actionFolder Folder in the filter list
|
||||
* @param filterType Filter Type (Manual, OfflineMail, etc.)
|
||||
*
|
||||
* @return errorMessage A localized message to display if invalid
|
||||
* Set to null if the actionValue is valid
|
||||
*/
|
||||
AUTF8String validateActionValue(in AUTF8String actionValue,
|
||||
in nsIMsgFolder actionFolder,
|
||||
in nsMsgFilterTypeType filterType);
|
||||
|
||||
/* allow duplicate actions in the same filter list? Default No. */
|
||||
attribute boolean allowDuplicates;
|
||||
|
||||
/*
|
||||
* The custom action itself
|
||||
*
|
||||
* Generally for the apply method, folder-based methods give correct
|
||||
* results and are preferred if available. Otherwise, be careful
|
||||
* that the action does correct notifications to maintain counts, and correct
|
||||
* manipulations of both IMAP and local non-database storage of message
|
||||
* metadata.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Apply the custom action to an array of messages
|
||||
*
|
||||
* @param msgHdrs array of nsIMsgDBHdr objects of messages
|
||||
* @param actionValue user-set value to use in the action
|
||||
* @param copyListener calling method (filterType Manual only)
|
||||
* @param filterType type of filter being applied
|
||||
* @param msgWindow message window
|
||||
*/
|
||||
|
||||
void apply(in nsIArray msgHdrs /* nsIMsgDBHdr array */,
|
||||
in AUTF8String actionValue,
|
||||
in nsIMsgCopyServiceListener copyListener,
|
||||
in nsMsgFilterTypeType filterType,
|
||||
in nsIMsgWindow msgWindow);
|
||||
|
||||
/* does this action start an async action? If so, a copy listener must
|
||||
* be used to continue filter processing after the action. This only
|
||||
* applies to after-the-fact (manual) filters. Call OnStopCopy when done
|
||||
* using the copyListener to continue.
|
||||
*/
|
||||
readonly attribute boolean isAsync;
|
||||
|
||||
/// Does this action need the message body?
|
||||
readonly attribute boolean needsBody;
|
||||
};
|
||||
|
||||
|
||||
27
mailnews/base/search/public/nsIMsgFilterHitNotify.idl
Normal file
27
mailnews/base/search/public/nsIMsgFilterHitNotify.idl
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIMsgFilter;
|
||||
interface nsIMsgWindow;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// nsIMsgFilterHitNotify is an interface designed to make evaluating filters
|
||||
// easier. Clients typically open a filter list and ask the filter list to
|
||||
// evaluate the filters for a particular message, and pass in an
|
||||
// interface pointer to be notified of hits. The filter list will call the
|
||||
// ApplyFilterHit method on the interface pointer in case of hits, along with
|
||||
// the desired action and value.
|
||||
// return value is used to indicate whether the
|
||||
// filter list should continue trying to apply filters or not.
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[scriptable, uuid(c9f15174-1f3f-11d3-a51b-0060b0fc04b7)]
|
||||
interface nsIMsgFilterHitNotify : nsISupports {
|
||||
boolean applyFilterHit(in nsIMsgFilter filter, in nsIMsgWindow msgWindow);
|
||||
};
|
||||
|
||||
108
mailnews/base/search/public/nsIMsgFilterList.idl
Normal file
108
mailnews/base/search/public/nsIMsgFilterList.idl
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIMsgFilterHitNotify.idl"
|
||||
#include "nsMsgFilterCore.idl"
|
||||
|
||||
interface nsIFile;
|
||||
interface nsIOutputStream;
|
||||
interface nsIMsgFilter;
|
||||
interface nsIMsgFolder;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// The Msg Filter List is an interface designed to make accessing filter lists
|
||||
// easier. Clients typically open a filter list and either enumerate the filters,
|
||||
// or add new filters, or change the order around...
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef long nsMsgFilterFileAttribValue;
|
||||
|
||||
[scriptable, uuid(5d0ec03e-7e2f-49e9-b58a-b274c85f279e)]
|
||||
interface nsIMsgFilterList : nsISupports {
|
||||
|
||||
const nsMsgFilterFileAttribValue attribNone = 0;
|
||||
const nsMsgFilterFileAttribValue attribVersion = 1;
|
||||
const nsMsgFilterFileAttribValue attribLogging = 2;
|
||||
const nsMsgFilterFileAttribValue attribName = 3;
|
||||
const nsMsgFilterFileAttribValue attribEnabled = 4;
|
||||
const nsMsgFilterFileAttribValue attribDescription = 5;
|
||||
const nsMsgFilterFileAttribValue attribType = 6;
|
||||
const nsMsgFilterFileAttribValue attribScriptFile = 7;
|
||||
const nsMsgFilterFileAttribValue attribAction = 8;
|
||||
const nsMsgFilterFileAttribValue attribActionValue = 9;
|
||||
const nsMsgFilterFileAttribValue attribCondition = 10;
|
||||
const nsMsgFilterFileAttribValue attribCustomId = 11;
|
||||
|
||||
attribute nsIMsgFolder folder;
|
||||
readonly attribute short version;
|
||||
readonly attribute ACString arbitraryHeaders;
|
||||
readonly attribute boolean shouldDownloadAllHeaders;
|
||||
readonly attribute unsigned long filterCount;
|
||||
nsIMsgFilter getFilterAt(in unsigned long filterIndex);
|
||||
nsIMsgFilter getFilterNamed(in AString filterName);
|
||||
|
||||
void setFilterAt(in unsigned long filterIndex, in nsIMsgFilter filter);
|
||||
void removeFilter(in nsIMsgFilter filter);
|
||||
void removeFilterAt(in unsigned long filterIndex);
|
||||
|
||||
void moveFilterAt(in unsigned long filterIndex,
|
||||
in nsMsgFilterMotionValue motion);
|
||||
void moveFilter(in nsIMsgFilter filter,
|
||||
in nsMsgFilterMotionValue motion);
|
||||
|
||||
void insertFilterAt(in unsigned long filterIndex, in nsIMsgFilter filter);
|
||||
|
||||
attribute boolean loggingEnabled;
|
||||
|
||||
nsIMsgFilter createFilter(in AString name);
|
||||
|
||||
void saveToFile(in nsIOutputStream stream);
|
||||
|
||||
void parseCondition(in nsIMsgFilter aFilter, in string condition);
|
||||
// this is temporary so that we can save the filterlist to disk
|
||||
// without knowing where the filters were read from intially
|
||||
// (such as the filter list dialog)
|
||||
attribute nsIFile defaultFile;
|
||||
void saveToDefaultFile();
|
||||
|
||||
|
||||
// marking noscript because headers is a null-separated list
|
||||
// of strings, which is not scriptable
|
||||
[noscript]
|
||||
void applyFiltersToHdr(in nsMsgFilterTypeType filterType,
|
||||
in nsIMsgDBHdr msgHdr,
|
||||
in nsIMsgFolder folder,
|
||||
in nsIMsgDatabase db,
|
||||
in string headers,
|
||||
//[array, size_is(headerSize)] in string headers,
|
||||
in unsigned long headerSize,
|
||||
in nsIMsgFilterHitNotify listener,
|
||||
in nsIMsgWindow msgWindow);
|
||||
|
||||
// IO routines, used by filter object filing code.
|
||||
void writeIntAttr(in nsMsgFilterFileAttribValue attrib, in long value, in nsIOutputStream stream);
|
||||
void writeStrAttr(in nsMsgFilterFileAttribValue attrib, in string value, in nsIOutputStream stream);
|
||||
void writeWstrAttr(in nsMsgFilterFileAttribValue attrib, in wstring value, in nsIOutputStream stream);
|
||||
void writeBoolAttr(in nsMsgFilterFileAttribValue attrib, in boolean value, in nsIOutputStream stream);
|
||||
boolean matchOrChangeFilterTarget(in ACString oldUri, in ACString newUri, in boolean caseInsensitive);
|
||||
|
||||
// for filter logging
|
||||
// If both attributes are fetched successfully, they guarantee
|
||||
// the log file exists and is set up with a header.
|
||||
attribute nsIOutputStream logStream;
|
||||
readonly attribute ACString logURL;
|
||||
void clearLog();
|
||||
void flushLogIfNecessary();
|
||||
};
|
||||
|
||||
|
||||
/* these longs are all actually of type nsMsgFilterMotionValue */
|
||||
[scriptable, uuid(d067b528-304e-11d3-a0e1-00a0c900d445)]
|
||||
interface nsMsgFilterMotion {
|
||||
const long up = 0;
|
||||
const long down = 1;
|
||||
};
|
||||
350
mailnews/base/search/public/nsIMsgFilterPlugin.idl
Normal file
350
mailnews/base/search/public/nsIMsgFilterPlugin.idl
Normal file
|
|
@ -0,0 +1,350 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "MailNewsTypes2.idl"
|
||||
|
||||
interface nsIMsgWindow;
|
||||
interface nsIFile;
|
||||
|
||||
/**
|
||||
* This interface is still very much under development, and is not yet stable.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(e2e56690-a676-11d6-80c9-00008646b737)]
|
||||
interface nsIMsgFilterPlugin : nsISupports
|
||||
{
|
||||
/**
|
||||
* Do any necessary cleanup: flush and close any open files, etc.
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* Some protocols (ie IMAP) can, as an optimization, avoid
|
||||
* downloading all message header lines. If your plugin doesn't need
|
||||
* any more than the minimal set, it can return false for this attribute.
|
||||
*/
|
||||
readonly attribute boolean shouldDownloadAllHeaders;
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* These interfaces typically implement a Bayesian classifier of messages.
|
||||
*
|
||||
* Two sets of interfaces may be used: the older junk-only interfaces, and
|
||||
* the newer trait-oriented interfaces that treat junk classification as
|
||||
* one of a set of classifications to accomplish.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(b15a0f9c-df07-4af0-9ba8-80dca68ac35d)]
|
||||
interface nsIJunkMailClassificationListener : nsISupports
|
||||
{
|
||||
/**
|
||||
* Inform a listener of a message's classification as junk. At the end
|
||||
* of a batch of classifications, signify end of batch by calling with
|
||||
* null aMsgURI (other parameters are don't care)
|
||||
*
|
||||
* @param aMsgURI URI of the message that was classified.
|
||||
* @param aClassification classification of message as UNCLASSIFIED, GOOD,
|
||||
* or JUNK.
|
||||
* @param aJunkPercent indicator of degree of uncertainty, with 100 being
|
||||
* probably junk, and 0 probably good
|
||||
*/
|
||||
void onMessageClassified(in string aMsgURI,
|
||||
in nsMsgJunkStatus aClassification,
|
||||
in uint32_t aJunkPercent);
|
||||
};
|
||||
|
||||
[scriptable, uuid(AF247D07-72F0-482d-9EAB-5A786407AA4C)]
|
||||
interface nsIMsgTraitClassificationListener : nsISupports
|
||||
{
|
||||
/**
|
||||
* Inform a listener of a message's match to traits. The list
|
||||
* of traits being matched is in aTraits. Corresponding
|
||||
* indicator of match (percent) is in aPercents. At the end
|
||||
* of a batch of classifications, signify end of batch by calling with
|
||||
* null aMsgURI (other parameters are don't care)
|
||||
*
|
||||
* @param aMsgURI URI of the message that was classified
|
||||
* @param aTraitCount length of aTraits and aPercents arrays
|
||||
* @param aTraits array of matched trait ids
|
||||
* @param aPercents array of percent match (0 is unmatched, 100 is fully
|
||||
* matched) of the trait with the corresponding array
|
||||
* index in aTraits
|
||||
*/
|
||||
void onMessageTraitsClassified(in string aMsgURI,
|
||||
in unsigned long aTraitCount,
|
||||
[array, size_is(aTraitCount)] in unsigned long aTraits,
|
||||
[array, size_is(aTraitCount)] in unsigned long aPercents);
|
||||
};
|
||||
|
||||
[scriptable, uuid(12667532-88D1-44a7-AD48-F73719BE5C92)]
|
||||
interface nsIMsgTraitDetailListener : nsISupports
|
||||
{
|
||||
/**
|
||||
* Inform a listener of details of a message's match to traits.
|
||||
* This returns the tokens that were used in the calculation,
|
||||
* the calculated percent probability that each token matches the trait,
|
||||
* and a running estimate (starting with the strongest tokens) of the
|
||||
* combined total probability that a message matches the trait, when
|
||||
* only tokens stronger than the current token are used.
|
||||
*
|
||||
* @param aMsgURI URI of the message that was classified
|
||||
* @param aProTrait trait id of pro trait for the calculation
|
||||
* @param tokenCount length of arrays that follow
|
||||
* @param tokenStrings the string for a particular token
|
||||
* @param tokenPercents calculated probability that a message with that token
|
||||
* matches the trait
|
||||
* @param runningPercents calculated probability that the message matches the
|
||||
* trait, accounting for this token and all stronger tokens.
|
||||
*/
|
||||
void onMessageTraitDetails(in string aMsgUri,
|
||||
in unsigned long aProTrait,
|
||||
in unsigned long tokenCount,
|
||||
[array, size_is(tokenCount)] in wstring tokenStrings,
|
||||
[array, size_is(tokenCount)] in unsigned long tokenPercents,
|
||||
[array, size_is(tokenCount)] in unsigned long runningPercents);
|
||||
};
|
||||
|
||||
[scriptable, uuid(8EA5BBCA-F735-4d43-8541-D203D8E2FF2F)]
|
||||
interface nsIJunkMailPlugin : nsIMsgFilterPlugin
|
||||
{
|
||||
/**
|
||||
* Message classifications.
|
||||
*/
|
||||
const nsMsgJunkStatus UNCLASSIFIED = 0;
|
||||
const nsMsgJunkStatus GOOD = 1;
|
||||
const nsMsgJunkStatus JUNK = 2;
|
||||
|
||||
/**
|
||||
* Message junk score constants. Junkscore can only be one of these two
|
||||
* values (or not set).
|
||||
*/
|
||||
const nsMsgJunkScore IS_SPAM_SCORE = 100; // junk
|
||||
const nsMsgJunkScore IS_HAM_SCORE = 0; // not junk
|
||||
|
||||
/**
|
||||
* Trait ids for junk analysis. These values are fixed to ensure
|
||||
* backwards compatibility with existing junk-oriented classification
|
||||
* code.
|
||||
*/
|
||||
|
||||
const unsigned long GOOD_TRAIT = 1; // good
|
||||
const unsigned long JUNK_TRAIT = 2; // junk
|
||||
|
||||
/**
|
||||
* Given a message URI, determine what its current classification is
|
||||
* according to the current training set.
|
||||
*/
|
||||
void classifyMessage(in string aMsgURI, in nsIMsgWindow aMsgWindow,
|
||||
in nsIJunkMailClassificationListener aListener);
|
||||
|
||||
void classifyMessages(in unsigned long aCount,
|
||||
[array, size_is(aCount)] in string aMsgURIs,
|
||||
in nsIMsgWindow aMsgWindow,
|
||||
in nsIJunkMailClassificationListener aListener);
|
||||
|
||||
/**
|
||||
* Given a message URI, evaluate its relative match to a list of
|
||||
* traits according to the current training set.
|
||||
*
|
||||
* @param aMsgURI URI of the message to be evaluated
|
||||
* @param aTraitCount length of aProTraits, aAntiTraits arrays
|
||||
* @param aProTraits array of trait ids for trained messages that
|
||||
* match the tested trait (for example,
|
||||
* JUNK_TRAIT if testing for junk)
|
||||
* @param aAntiTraits array of trait ids for trained messages that
|
||||
* do not match the tested trait (for example,
|
||||
* GOOD_TRAIT if testing for junk)
|
||||
* @param aTraitListener trait-oriented callback listener (may be null)
|
||||
* @param aMsgWindow current message window (may be null)
|
||||
* @param aJunkListener junk-oriented callback listener (may be null)
|
||||
*/
|
||||
|
||||
void classifyTraitsInMessage(
|
||||
in string aMsgURI,
|
||||
in unsigned long aTraitCount,
|
||||
[array, size_is(aTraitCount)] in unsigned long aProTraits,
|
||||
[array, size_is(aTraitCount)] in unsigned long aAntiTraits,
|
||||
in nsIMsgTraitClassificationListener aTraitListener,
|
||||
[optional] in nsIMsgWindow aMsgWindow,
|
||||
[optional] in nsIJunkMailClassificationListener aJunkListener);
|
||||
|
||||
/**
|
||||
* Given an array of message URIs, evaluate their relative match to a
|
||||
* list of traits according to the current training set.
|
||||
*
|
||||
* @param aCount Number of messages to evaluate
|
||||
* @param aMsgURIs array of URIs of the messages to be evaluated
|
||||
* @param aTraitCount length of aProTraits, aAntiTraits arrays
|
||||
* @param aProTraits array of trait ids for trained messages that
|
||||
* match the tested trait (for example,
|
||||
* JUNK_TRAIT if testing for junk)
|
||||
* @param aAntiTraits array of trait ids for trained messages that
|
||||
* do not match the tested trait (for example,
|
||||
* GOOD_TRAIT if testing for junk)
|
||||
* @param aTraitListener trait-oriented callback listener (may be null)
|
||||
* @param aMsgWindow current message window (may be null)
|
||||
* @param aJunkListener junk-oriented callback listener (may be null)
|
||||
*/
|
||||
|
||||
void classifyTraitsInMessages(
|
||||
in unsigned long aCount,
|
||||
[array, size_is(aCount)] in string aMsgURIs,
|
||||
in unsigned long aTraitCount,
|
||||
[array, size_is(aTraitCount)] in unsigned long aProTraits,
|
||||
[array, size_is(aTraitCount)] in unsigned long aAntiTraits,
|
||||
in nsIMsgTraitClassificationListener aTraitListener,
|
||||
[optional] in nsIMsgWindow aMsgWindow,
|
||||
[optional] in nsIJunkMailClassificationListener aJunkListener);
|
||||
|
||||
/**
|
||||
* Called when a user forces the classification of a message. Should
|
||||
* cause the training set to be updated appropriately.
|
||||
*
|
||||
* @arg aMsgURI URI of the message to be classified
|
||||
* @arg aOldUserClassification Was it previous manually classified
|
||||
* by the user? If so, how?
|
||||
* @arg aNewClassification New manual classification.
|
||||
* @arg aListener Callback (may be null)
|
||||
*/
|
||||
void setMessageClassification(
|
||||
in string aMsgURI, in nsMsgJunkStatus aOldUserClassification,
|
||||
in nsMsgJunkStatus aNewClassification,
|
||||
in nsIMsgWindow aMsgWindow,
|
||||
in nsIJunkMailClassificationListener aListener);
|
||||
|
||||
/**
|
||||
* Called when a user forces a change in the classification of a message.
|
||||
* Should cause the training set to be updated appropriately.
|
||||
*
|
||||
* @param aMsgURI URI of the message to be classified
|
||||
* @param aOldCount length of aOldTraits array
|
||||
* @param aOldTraits array of trait IDs of the old
|
||||
* message classification(s), if any
|
||||
* @param aNewCount length of aNewTraits array
|
||||
* @param aNewTraits array of trait IDs of the new
|
||||
* message classification(s), if any
|
||||
* @param aTraitListener trait-oriented listener (may be null)
|
||||
* @param aMsgWindow current message window (may be null)
|
||||
* @param aJunkListener junk-oriented listener (may be null)
|
||||
*/
|
||||
void setMsgTraitClassification(
|
||||
in string aMsgURI,
|
||||
in unsigned long aOldCount,
|
||||
[array, size_is(aOldCount)] in unsigned long aOldTraits,
|
||||
in unsigned long aNewCount,
|
||||
[array, size_is(aNewCount)] in unsigned long aNewTraits,
|
||||
[optional] in nsIMsgTraitClassificationListener aTraitListener,
|
||||
[optional] in nsIMsgWindow aMsgWindow,
|
||||
[optional] in nsIJunkMailClassificationListener aJunkListener);
|
||||
|
||||
readonly attribute boolean userHasClassified;
|
||||
|
||||
/** Removes the training file and clears out any in memory training tokens.
|
||||
User must retrain after doing this.
|
||||
**/
|
||||
void resetTrainingData();
|
||||
|
||||
/**
|
||||
* Given a message URI, return a list of tokens and their contribution to
|
||||
* the analysis of a message's match to a trait according to the
|
||||
* current training set.
|
||||
*
|
||||
* @param aMsgURI URI of the message to be evaluated
|
||||
* @param aProTrait trait id for trained messages that match the
|
||||
* tested trait (for example, JUNK_TRAIT if testing
|
||||
* for junk)
|
||||
* @param aAntiTrait trait id for trained messages that do not match
|
||||
* the tested trait (for example, GOOD_TRAIT
|
||||
* if testing for junk)
|
||||
* @param aListener callback listener for results
|
||||
* @param aMsgWindow current message window (may be null)
|
||||
*/
|
||||
void detailMessage(
|
||||
in string aMsgURI,
|
||||
in unsigned long aProTrait,
|
||||
in unsigned long aAntiTrait,
|
||||
in nsIMsgTraitDetailListener aListener,
|
||||
[optional] in nsIMsgWindow aMsgWindow);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The nsIMsgCorpus interface manages a corpus of mail data used for
|
||||
* statistical analysis of messages.
|
||||
*/
|
||||
[scriptable, uuid(70BAD26F-DFD4-41bd-8FAB-4C09B9C1E845)]
|
||||
interface nsIMsgCorpus : nsISupports
|
||||
{
|
||||
/**
|
||||
* Clear the corpus data for a trait id.
|
||||
*
|
||||
* @param aTrait trait id
|
||||
*/
|
||||
void clearTrait(in unsigned long aTrait);
|
||||
|
||||
/**
|
||||
* Update corpus data from a file.
|
||||
*
|
||||
* @param aFile the file with the data, in the format:
|
||||
*
|
||||
* Format of the trait file for version 1:
|
||||
* [0xFCA93601] (the 01 is the version)
|
||||
* for each trait to write:
|
||||
* [id of trait to write] (0 means end of list)
|
||||
* [number of messages per trait]
|
||||
* for each token with non-zero count
|
||||
* [count]
|
||||
* [length of word]word
|
||||
*
|
||||
* @param aIsAdd should the data be added, or removed? True if
|
||||
* adding, false if removing.
|
||||
*
|
||||
* @param aRemapCount number of items in the parallel arrays aFromTraits,
|
||||
* aToTraits. These arrays allow conversion of the
|
||||
* trait id stored in the file (which may be originated
|
||||
* externally) to the trait id used in the local corpus
|
||||
* (which is defined locally using nsIMsgTraitService, and
|
||||
* mapped by that interface to a globally unique trait
|
||||
* id string).
|
||||
*
|
||||
* @param aFromTraits array of trait ids used in aFile. If aFile contains
|
||||
* trait ids that are not in this array, they are not
|
||||
* remapped, but assummed to be local trait ids.
|
||||
*
|
||||
* @param aToTraits array of trait ids, corresponding to elements of
|
||||
* aFromTraits, that represent the local trait ids to
|
||||
* be used in storing data from aFile into the local corpus.
|
||||
*/
|
||||
void updateData(in nsIFile aFile, in boolean aIsAdd,
|
||||
[optional] in unsigned long aRemapCount,
|
||||
[optional, array, size_is(aRemapCount)] in unsigned long aFromTraits,
|
||||
[optional, array, size_is(aRemapCount)] in unsigned long aToTraits);
|
||||
|
||||
/**
|
||||
* Get the corpus count for a token as a string.
|
||||
*
|
||||
* @param aWord string of characters representing the token
|
||||
* @param aTrait trait id
|
||||
*
|
||||
* @return count of that token in the corpus
|
||||
*
|
||||
*/
|
||||
unsigned long getTokenCount(in AUTF8String aWord, in unsigned long aTrait);
|
||||
|
||||
/**
|
||||
* Gives information on token and message count information in the
|
||||
* training data corpus.
|
||||
*
|
||||
* @param aTrait trait id (may be null)
|
||||
* @param aMessageCount count of messages that have been trained with aTrait
|
||||
*
|
||||
* @return token count for all traits
|
||||
*/
|
||||
|
||||
unsigned long corpusCounts(in unsigned long aTrait, out unsigned long aMessageCount);
|
||||
};
|
||||
95
mailnews/base/search/public/nsIMsgFilterService.idl
Normal file
95
mailnews/base/search/public/nsIMsgFilterService.idl
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsMsgFilterCore.idl"
|
||||
|
||||
interface nsIMsgFilterList;
|
||||
interface nsIMsgWindow;
|
||||
interface nsIMsgFilterCustomAction;
|
||||
interface nsISimpleEnumerator;
|
||||
interface nsIFile;
|
||||
interface nsIMsgFolder;
|
||||
interface nsIMsgSearchCustomTerm;
|
||||
interface nsIArray;
|
||||
interface nsIMsgOperationListener;
|
||||
|
||||
[scriptable, uuid(78a74023-1692-4567-8d72-9ca58fbbd427)]
|
||||
interface nsIMsgFilterService : nsISupports {
|
||||
|
||||
nsIMsgFilterList OpenFilterList(in nsIFile filterFile, in nsIMsgFolder rootFolder, in nsIMsgWindow msgWindow);
|
||||
void CloseFilterList(in nsIMsgFilterList filterList);
|
||||
|
||||
void SaveFilterList(in nsIMsgFilterList filterList,
|
||||
in nsIFile filterFile);
|
||||
|
||||
void CancelFilterList(in nsIMsgFilterList filterList);
|
||||
nsIMsgFilterList getTempFilterList(in nsIMsgFolder aFolder);
|
||||
void applyFiltersToFolders(in nsIMsgFilterList aFilterList,
|
||||
in nsIArray aFolders,
|
||||
in nsIMsgWindow aMsgWindow,
|
||||
[optional] in nsIMsgOperationListener aCallback);
|
||||
|
||||
/*
|
||||
* Apply filters to a specific list of messages in a folder.
|
||||
* @param aFilterType The type of filter to match against
|
||||
* @param aMsgHdrList The list of message headers (nsIMsgDBHdr objects)
|
||||
* @param aFolder The folder the messages belong to
|
||||
* @param aMsgWindow A UI window for attaching progress/dialogs
|
||||
* @param aCallback A listener that gets notified of any filtering error
|
||||
*/
|
||||
void applyFilters(in nsMsgFilterTypeType aFilterType,
|
||||
in nsIArray aMsgHdrList,
|
||||
in nsIMsgFolder aFolder,
|
||||
in nsIMsgWindow aMsgWindow,
|
||||
[optional] in nsIMsgOperationListener aCallback);
|
||||
|
||||
/**
|
||||
* add a custom filter action
|
||||
*
|
||||
* @param aAction the custom action to add
|
||||
*/
|
||||
void addCustomAction(in nsIMsgFilterCustomAction aAction);
|
||||
|
||||
/**
|
||||
* get the list of custom actions
|
||||
*
|
||||
* @return enumerator of nsIMsgFilterCustomAction objects
|
||||
*/
|
||||
nsISimpleEnumerator getCustomActions();
|
||||
|
||||
/**
|
||||
* lookup a custom action given its id
|
||||
*
|
||||
* @param id unique identifier for a particular custom action
|
||||
*
|
||||
* @return the custom action, or null if not found
|
||||
*/
|
||||
nsIMsgFilterCustomAction getCustomAction(in ACString id);
|
||||
|
||||
/**
|
||||
* add a custom search term
|
||||
*
|
||||
* @param aTerm the custom term to add
|
||||
*/
|
||||
void addCustomTerm(in nsIMsgSearchCustomTerm aTerm);
|
||||
|
||||
/**
|
||||
* get the list of custom search terms
|
||||
*
|
||||
* @return enumerator of nsIMsgSearchCustomTerm objects
|
||||
*/
|
||||
nsISimpleEnumerator getCustomTerms();
|
||||
|
||||
/**
|
||||
* lookup a custom search term given its id
|
||||
*
|
||||
* @param id unique identifier for a particular custom search term
|
||||
*
|
||||
* @return the custom search term, or null if not found
|
||||
*/
|
||||
nsIMsgSearchCustomTerm getCustomTerm(in ACString id);
|
||||
|
||||
};
|
||||
17
mailnews/base/search/public/nsIMsgOperationListener.idl
Normal file
17
mailnews/base/search/public/nsIMsgOperationListener.idl
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
// Listener used to notify when an operation has completed.
|
||||
[scriptable, uuid(bdaef6ff-0909-435b-8fcd-76525dd2364c)]
|
||||
interface nsIMsgOperationListener : nsISupports {
|
||||
/**
|
||||
* Called when the operation stops (possibly with errors)
|
||||
*
|
||||
* @param aStatus Success or failure of the operation
|
||||
*/
|
||||
void onStopOperation(in nsresult aStatus);
|
||||
};
|
||||
42
mailnews/base/search/public/nsIMsgSearchAdapter.idl
Normal file
42
mailnews/base/search/public/nsIMsgSearchAdapter.idl
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIMsgSearchScopeTerm.idl"
|
||||
|
||||
[ptr] native nsMsgResultElement(nsMsgResultElement);
|
||||
|
||||
%{C++
|
||||
class nsMsgResultElement;
|
||||
%}
|
||||
|
||||
[scriptable, uuid(0b09078b-e0cd-440a-afee-01f45808ee74)]
|
||||
interface nsIMsgSearchAdapter : nsISupports {
|
||||
void ValidateTerms();
|
||||
void Search(out boolean done);
|
||||
void SendUrl();
|
||||
void CurrentUrlDone(in nsresult exitCode);
|
||||
|
||||
void AddHit(in nsMsgKey key);
|
||||
void AddResultElement(in nsIMsgDBHdr aHdr);
|
||||
|
||||
[noscript] void OpenResultElement(in nsMsgResultElement element);
|
||||
[noscript] void ModifyResultElement(in nsMsgResultElement element,
|
||||
in nsMsgSearchValue value);
|
||||
|
||||
readonly attribute string encoding;
|
||||
|
||||
[noscript] nsIMsgFolder FindTargetFolder([const] in nsMsgResultElement
|
||||
element);
|
||||
void Abort();
|
||||
void getSearchCharsets(out AString srcCharset, out AString destCharset);
|
||||
/*
|
||||
* Clear the saved scope reference. This is used when deleting scope, which is not
|
||||
* reference counted in nsMsgSearchSession
|
||||
*/
|
||||
void clearScope();
|
||||
};
|
||||
|
||||
79
mailnews/base/search/public/nsIMsgSearchCustomTerm.idl
Normal file
79
mailnews/base/search/public/nsIMsgSearchCustomTerm.idl
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/* 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/. */
|
||||
|
||||
#include "nsMsgSearchCore.idl"
|
||||
|
||||
/**
|
||||
* describes a custom term added to a message search or filter
|
||||
*/
|
||||
[scriptable,uuid(925DB5AA-21AF-494c-8652-984BC7BAD13A)]
|
||||
interface nsIMsgSearchCustomTerm : nsISupports
|
||||
{
|
||||
/**
|
||||
* globally unique string to identify this search term.
|
||||
* recommended form: ExtensionName@example.com#TermName
|
||||
* Commas and quotes are not allowed, the id must not
|
||||
* parse to an integer, and names of standard search
|
||||
* attributes in SearchAttribEntryTable in nsMsgSearchTerm.cpp
|
||||
* are not allowed.
|
||||
*/
|
||||
readonly attribute ACString id;
|
||||
|
||||
/// name to display in term list. This should be localized. */
|
||||
readonly attribute AString name;
|
||||
|
||||
/// Does this term need the message body?
|
||||
readonly attribute boolean needsBody;
|
||||
|
||||
/**
|
||||
* Is this custom term enabled?
|
||||
*
|
||||
* @param scope search scope (nsMsgSearchScope)
|
||||
* @param op search operator (nsMsgSearchOp). If null, determine
|
||||
* if term is available for any operator.
|
||||
*
|
||||
* @return true if enabled
|
||||
*/
|
||||
boolean getEnabled(in nsMsgSearchScopeValue scope,
|
||||
in nsMsgSearchOpValue op);
|
||||
|
||||
/**
|
||||
* Is this custom term available?
|
||||
*
|
||||
* @param scope search scope (nsMsgSearchScope)
|
||||
* @param op search operator (nsMsgSearchOp). If null, determine
|
||||
* if term is available for any operator.
|
||||
*
|
||||
* @return true if available
|
||||
*/
|
||||
boolean getAvailable(in nsMsgSearchScopeValue scope,
|
||||
in nsMsgSearchOpValue op);
|
||||
|
||||
/**
|
||||
* List the valid operators for this term.
|
||||
*
|
||||
* @param scope search scope (nsMsgSearchScope)
|
||||
* @param length object to hold array length
|
||||
*
|
||||
* @return array of operators
|
||||
*/
|
||||
void getAvailableOperators(in nsMsgSearchScopeValue scope,
|
||||
out unsigned long length,
|
||||
[retval, array, size_is(length)]
|
||||
out nsMsgSearchOpValue operators);
|
||||
|
||||
/**
|
||||
* Apply the custom search term to a message
|
||||
*
|
||||
* @param msgHdr header database reference representing the message
|
||||
* @param searchValue user-set value to use in the search
|
||||
* @param searchOp search operator (Contains, IsHigherThan, etc.)
|
||||
*
|
||||
* @return true if the term matches the message, else false
|
||||
*/
|
||||
|
||||
boolean match(in nsIMsgDBHdr msgHdr,
|
||||
in AUTF8String searchValue,
|
||||
in nsMsgSearchOpValue searchOp);
|
||||
};
|
||||
31
mailnews/base/search/public/nsIMsgSearchNotify.idl
Normal file
31
mailnews/base/search/public/nsIMsgSearchNotify.idl
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIMsgDBHdr;
|
||||
interface nsIMsgSearchSession;
|
||||
interface nsIMsgFolder;
|
||||
|
||||
// when a search is run, this interface is passed in as a listener
|
||||
// on the search.
|
||||
[scriptable, uuid(ca37784d-352b-4c39-8ccb-0abc1a93f681)]
|
||||
interface nsIMsgSearchNotify : nsISupports
|
||||
{
|
||||
void onSearchHit(in nsIMsgDBHdr header, in nsIMsgFolder folder);
|
||||
|
||||
// notification that a search has finished.
|
||||
void onSearchDone(in nsresult status);
|
||||
/*
|
||||
* until we can encode searches with a URI, this will be an
|
||||
* out-of-bound way to connect a set of search terms to a datasource
|
||||
*/
|
||||
|
||||
/*
|
||||
* called when a new search begins
|
||||
*/
|
||||
void onNewSearch();
|
||||
};
|
||||
|
||||
20
mailnews/base/search/public/nsIMsgSearchScopeTerm.idl
Normal file
20
mailnews/base/search/public/nsIMsgSearchScopeTerm.idl
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsIMsgSearchSession.idl"
|
||||
|
||||
interface nsIMsgFolder;
|
||||
interface nsIMsgDBHdr;
|
||||
interface nsILineInputStream;
|
||||
interface nsIInputStream;
|
||||
|
||||
[scriptable, uuid(934672c3-9b8f-488a-935d-87b4023fa0be)]
|
||||
interface nsIMsgSearchScopeTerm : nsISupports {
|
||||
nsIInputStream getInputStream(in nsIMsgDBHdr aHdr);
|
||||
void closeInputStream();
|
||||
readonly attribute nsIMsgFolder folder;
|
||||
readonly attribute nsIMsgSearchSession searchSession;
|
||||
};
|
||||
|
||||
147
mailnews/base/search/public/nsIMsgSearchSession.idl
Normal file
147
mailnews/base/search/public/nsIMsgSearchSession.idl
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIMsgSearchValue.idl"
|
||||
|
||||
interface nsIMsgSearchAdapter;
|
||||
interface nsIMsgSearchTerm;
|
||||
interface nsIMsgSearchNotify;
|
||||
interface nsIMsgHdr;
|
||||
interface nsIMsgDatabase;
|
||||
// Disable deprecation warnings generated by nsISupportsArray and associated
|
||||
// classes.
|
||||
%{C++
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning (disable : 4996)
|
||||
#endif
|
||||
%}
|
||||
interface nsISupportsArray;
|
||||
interface nsIMsgWindow;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// The Msg Search Session is an interface designed to make constructing
|
||||
// searches easier. Clients typically build up search terms, and then run
|
||||
// the search
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
[scriptable, uuid(1ed69bbf-7983-4602-9a9b-2f2263a78878)]
|
||||
interface nsIMsgSearchSession : nsISupports {
|
||||
|
||||
/**
|
||||
* add a search term to the search session
|
||||
*
|
||||
* @param attrib search attribute (e.g. nsMsgSearchAttrib::Subject)
|
||||
* @param op search operator (e.g. nsMsgSearchOp::Contains)
|
||||
* @param value search value (e.g. "Dogbert", see nsIMsgSearchValue)
|
||||
* @param BooleanAND set to true if associated boolean operator is AND
|
||||
* @param customString if attrib > nsMsgSearchAttrib::OtherHeader,
|
||||
* a user defined arbitrary header
|
||||
* if attrib == nsMsgSearchAttrib::Custom, the custom id
|
||||
* otherwise ignored
|
||||
*/
|
||||
void addSearchTerm(in nsMsgSearchAttribValue attrib,
|
||||
in nsMsgSearchOpValue op,
|
||||
in nsIMsgSearchValue value,
|
||||
in boolean BooleanAND,
|
||||
in string customString);
|
||||
|
||||
readonly attribute nsISupportsArray searchTerms;
|
||||
|
||||
nsIMsgSearchTerm createTerm ();
|
||||
void appendTerm(in nsIMsgSearchTerm term);
|
||||
|
||||
/**
|
||||
* @name Search notification flags
|
||||
* These flags determine which notifications will be sent.
|
||||
* @{
|
||||
*/
|
||||
/// search started notification
|
||||
const long onNewSearch = 0x1;
|
||||
|
||||
/// search finished notification
|
||||
const long onSearchDone = 0x2;
|
||||
|
||||
/// search hit notification
|
||||
const long onSearchHit = 0x4;
|
||||
|
||||
const long allNotifications = 0x7;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* Add a listener to get notified of search starts, stops, and hits.
|
||||
*
|
||||
* @param aListener listener
|
||||
* @param aNotifyFlags which notifications to send. Defaults to all
|
||||
*/
|
||||
void registerListener (in nsIMsgSearchNotify aListener,
|
||||
[optional] in long aNotifyFlags);
|
||||
void unregisterListener (in nsIMsgSearchNotify listener);
|
||||
|
||||
readonly attribute unsigned long numSearchTerms;
|
||||
|
||||
readonly attribute nsIMsgSearchAdapter runningAdapter;
|
||||
|
||||
void getNthSearchTerm(in long whichTerm,
|
||||
in nsMsgSearchAttribValue attrib,
|
||||
in nsMsgSearchOpValue op,
|
||||
in nsIMsgSearchValue value); // wrong, should be out
|
||||
|
||||
long countSearchScopes();
|
||||
|
||||
void getNthSearchScope(in long which,out nsMsgSearchScopeValue scopeId, out nsIMsgFolder folder);
|
||||
|
||||
/* add a scope (e.g. a mail folder) to the search */
|
||||
void addScopeTerm(in nsMsgSearchScopeValue scope,
|
||||
in nsIMsgFolder folder);
|
||||
|
||||
void addDirectoryScopeTerm(in nsMsgSearchScopeValue scope);
|
||||
|
||||
void clearScopes();
|
||||
|
||||
/* Call this function everytime the scope changes! It informs the FE if
|
||||
the current scope support custom header use. FEs should not display the
|
||||
custom header dialog if custom headers are not supported */
|
||||
[noscript] boolean ScopeUsesCustomHeaders(in nsMsgSearchScopeValue scope,
|
||||
/* could be a folder or server based on scope */
|
||||
in voidPtr selection,
|
||||
in boolean forFilters);
|
||||
|
||||
/* use this to determine if your attribute is a string attrib */
|
||||
boolean IsStringAttribute(in nsMsgSearchAttribValue attrib);
|
||||
|
||||
/* add all scopes of a given type to the search */
|
||||
void AddAllScopes(in nsMsgSearchScopeValue attrib);
|
||||
|
||||
void search(in nsIMsgWindow aWindow);
|
||||
void interruptSearch();
|
||||
|
||||
// these two methods are used when the search session is using
|
||||
// a timer to do local search, and the search adapter needs
|
||||
// to run a url (e.g., to reparse a local folder) and wants to
|
||||
// pause the timer while running the url. This will fail if the
|
||||
// current adapter is not using a timer.
|
||||
void pauseSearch();
|
||||
void resumeSearch();
|
||||
|
||||
[noscript] readonly attribute voidPtr searchParam;
|
||||
readonly attribute nsMsgSearchType searchType;
|
||||
|
||||
[noscript] nsMsgSearchType SetSearchParam(in nsMsgSearchType type,
|
||||
in voidPtr param);
|
||||
|
||||
boolean MatchHdr(in nsIMsgDBHdr aMsgHdr, in nsIMsgDatabase aDatabase);
|
||||
|
||||
void addSearchHit(in nsIMsgDBHdr header, in nsIMsgFolder folder);
|
||||
|
||||
readonly attribute long numResults;
|
||||
attribute nsIMsgWindow window;
|
||||
|
||||
/* these longs are all actually of type nsMsgSearchBooleanOp */
|
||||
const long BooleanOR=0;
|
||||
const long BooleanAND=1;
|
||||
};
|
||||
156
mailnews/base/search/public/nsIMsgSearchTerm.idl
Normal file
156
mailnews/base/search/public/nsIMsgSearchTerm.idl
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsMsgSearchCore.idl"
|
||||
#include "nsIMsgSearchValue.idl"
|
||||
|
||||
interface nsIMsgDBHdr;
|
||||
interface nsIMsgDatabase;
|
||||
interface nsIMsgSearchScopeTerm;
|
||||
|
||||
[scriptable, uuid(705a2b5a-5efc-495c-897a-bef1161cd3c0)]
|
||||
interface nsIMsgSearchTerm : nsISupports {
|
||||
attribute nsMsgSearchAttribValue attrib;
|
||||
attribute nsMsgSearchOpValue op;
|
||||
attribute nsIMsgSearchValue value;
|
||||
|
||||
attribute boolean booleanAnd;
|
||||
attribute ACString arbitraryHeader;
|
||||
/**
|
||||
* Not to be confused with arbitraryHeader, which is a header in the
|
||||
* rfc822 message. This is a property of the nsIMsgDBHdr, and may have
|
||||
* nothing to do the message headers, e.g., gloda-id.
|
||||
* value.str will be compared with nsIMsgHdr::GetProperty(hdrProperty).
|
||||
*/
|
||||
attribute ACString hdrProperty;
|
||||
|
||||
/// identifier for a custom id used for this term, if any.
|
||||
attribute ACString customId;
|
||||
|
||||
attribute boolean beginsGrouping;
|
||||
attribute boolean endsGrouping;
|
||||
|
||||
/**
|
||||
* Match the value against one of the emails found in the incoming
|
||||
* 2047-encoded string.
|
||||
*/
|
||||
boolean matchRfc822String(in ACString aString, in string charset);
|
||||
/**
|
||||
* Match the current header value against the incoming 2047-encoded string.
|
||||
*
|
||||
* This method will first apply the nsIMimeConverter decoding to the string
|
||||
* (using the supplied parameters) and will then match the value against the
|
||||
* decoded result.
|
||||
*/
|
||||
boolean matchRfc2047String(in ACString aString, in string charset, in boolean charsetOverride);
|
||||
boolean matchDate(in PRTime aTime);
|
||||
boolean matchStatus(in unsigned long aStatus);
|
||||
boolean matchPriority(in nsMsgPriorityValue priority);
|
||||
boolean matchAge(in PRTime days);
|
||||
boolean matchSize(in unsigned long size);
|
||||
boolean matchLabel(in nsMsgLabelValue aLabelValue);
|
||||
boolean matchJunkStatus(in string aJunkScore);
|
||||
/*
|
||||
* Test search term match for junkpercent
|
||||
*
|
||||
* @param aJunkPercent junkpercent for message (0-100, 100 is junk)
|
||||
* @return true if matches
|
||||
*/
|
||||
boolean matchJunkPercent(in unsigned long aJunkPercent);
|
||||
/*
|
||||
* Test search term match for junkscoreorigin
|
||||
* @param aJunkScoreOrigin Who set junk score? Possible values:
|
||||
* plugin filter imapflag user whitelist
|
||||
* @return true if matches
|
||||
*/
|
||||
boolean matchJunkScoreOrigin(in string aJunkScoreOrigin);
|
||||
|
||||
/**
|
||||
* Test if the body of the passed in message matches "this" search term.
|
||||
* @param aScopeTerm scope of search
|
||||
* @param aOffset offset of message in message store.
|
||||
* @param aLength length of message.
|
||||
* @param aCharset folder charset.
|
||||
* @param aMsg db msg hdr of message to match.
|
||||
* @param aDB db containing msg header.
|
||||
*/
|
||||
boolean matchBody(in nsIMsgSearchScopeTerm aScopeTerm,
|
||||
in unsigned long long aOffset,
|
||||
in unsigned long aLength,
|
||||
in string aCharset,
|
||||
in nsIMsgDBHdr aMsg,
|
||||
in nsIMsgDatabase aDb);
|
||||
|
||||
/**
|
||||
* Test if the arbitrary header specified by this search term
|
||||
* matches the corresponding header in the passed in message.
|
||||
*
|
||||
* @param aScopeTerm scope of search
|
||||
* @param aLength length of message
|
||||
* @param aCharset The charset to apply to un-labeled non-UTF-8 data.
|
||||
* @param aCharsetOverride If true, aCharset is used instead of any
|
||||
* charset labeling other than UTF-8.
|
||||
*
|
||||
* N.B. This is noscript because headers is a null-separated list of
|
||||
* strings, which is not scriptable.
|
||||
*/
|
||||
[noscript]
|
||||
boolean matchArbitraryHeader(in nsIMsgSearchScopeTerm aScopeTerm,
|
||||
in unsigned long aLength,
|
||||
in string aCharset,
|
||||
in boolean aCharsetOverride,
|
||||
in nsIMsgDBHdr aMsg,
|
||||
in nsIMsgDatabase aDb,
|
||||
//[array, size_is(headerLength)] in string headers,
|
||||
in string aHeaders,
|
||||
in unsigned long aHeaderLength,
|
||||
in boolean aForFilters);
|
||||
|
||||
/**
|
||||
* Compares value.str with nsIMsgHdr::GetProperty(hdrProperty).
|
||||
* @param msg msg to match db hdr property of.
|
||||
*
|
||||
* @returns true if msg matches property, false otherwise.
|
||||
*/
|
||||
boolean matchHdrProperty(in nsIMsgDBHdr msg);
|
||||
|
||||
/**
|
||||
* Compares value.status with nsIMsgHdr::GetUint32Property(hdrProperty).
|
||||
* @param msg msg to match db hdr property of.
|
||||
*
|
||||
* @returns true if msg matches property, false otherwise.
|
||||
*/
|
||||
boolean matchUint32HdrProperty(in nsIMsgDBHdr msg);
|
||||
|
||||
/**
|
||||
* Compares value.status with the folder flags of the msg's folder.
|
||||
* @param msg msgHdr whose folder's flag we want to compare.
|
||||
*
|
||||
* @returns true if folder's flags match value.status, false otherwise.
|
||||
*/
|
||||
boolean matchFolderFlag(in nsIMsgDBHdr msg);
|
||||
|
||||
readonly attribute boolean matchAllBeforeDeciding;
|
||||
|
||||
readonly attribute ACString termAsString;
|
||||
boolean matchKeyword(in ACString keyword); // used for tag searches
|
||||
attribute boolean matchAll;
|
||||
/**
|
||||
* Does the message match the custom search term?
|
||||
*
|
||||
* @param msg message database object representing the message
|
||||
*
|
||||
* @return true if message matches
|
||||
*/
|
||||
boolean matchCustom(in nsIMsgDBHdr msg);
|
||||
|
||||
/**
|
||||
* Returns a nsMsgSearchAttribValue value corresponding to a field string from
|
||||
* the nsMsgSearchTerm.cpp::SearchAttribEntryTable table.
|
||||
* Does not handle custom attributes yet.
|
||||
*/
|
||||
nsMsgSearchAttribValue getAttributeFromString(in string aAttribName);
|
||||
};
|
||||
26
mailnews/base/search/public/nsIMsgSearchValidityManager.idl
Normal file
26
mailnews/base/search/public/nsIMsgSearchValidityManager.idl
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIMsgSearchValidityTable.idl"
|
||||
|
||||
typedef long nsMsgSearchValidityScope;
|
||||
|
||||
[scriptable, uuid(6A352055-DE6E-49d2-A256-89E0B9EC405E)]
|
||||
interface nsIMsgSearchValidityManager : nsISupports {
|
||||
nsIMsgSearchValidityTable getTable(in nsMsgSearchValidityScope scope);
|
||||
|
||||
/**
|
||||
* Given a search attribute (which is an internal numerical id), return
|
||||
* the string name that you can use as a key to look up the localized
|
||||
* string in the search-attributes.properties file.
|
||||
*
|
||||
* @param aSearchAttribute attribute type from interface nsMsgSearchAttrib
|
||||
*
|
||||
* @return localization-friendly string representation
|
||||
* of the attribute
|
||||
*/
|
||||
AString getAttributeProperty(in nsMsgSearchAttribValue aSearchAttribute);
|
||||
};
|
||||
50
mailnews/base/search/public/nsIMsgSearchValidityTable.idl
Normal file
50
mailnews/base/search/public/nsIMsgSearchValidityTable.idl
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsMsgSearchCore.idl"
|
||||
// Disable deprecation warnings generated by nsISupportsArray and associated
|
||||
// classes.
|
||||
%{C++
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning (disable : 4996)
|
||||
#endif
|
||||
%}
|
||||
interface nsISupportsArray;
|
||||
|
||||
[scriptable, uuid(b07f1cb6-fae9-4d92-9edb-03f9ad249c66)]
|
||||
interface nsIMsgSearchValidityTable : nsISupports {
|
||||
|
||||
void setAvailable(in nsMsgSearchAttribValue attrib,
|
||||
in nsMsgSearchOpValue op, in boolean active);
|
||||
void setEnabled(in nsMsgSearchAttribValue attrib,
|
||||
in nsMsgSearchOpValue op, in boolean enabled);
|
||||
void setValidButNotShown(in nsMsgSearchAttribValue attrib,
|
||||
in nsMsgSearchOpValue op, in boolean valid);
|
||||
|
||||
boolean getAvailable(in nsMsgSearchAttribValue attrib,
|
||||
in nsMsgSearchOpValue op);
|
||||
boolean getEnabled(in nsMsgSearchAttribValue attrib,
|
||||
in nsMsgSearchOpValue op);
|
||||
boolean getValidButNotShown(in nsMsgSearchAttribValue attrib,
|
||||
in nsMsgSearchOpValue op);
|
||||
|
||||
[noscript] void validateTerms(in nsISupportsArray terms);
|
||||
|
||||
readonly attribute long numAvailAttribs;
|
||||
|
||||
void getAvailableAttributes(out unsigned long length,
|
||||
[retval, array, size_is(length)]
|
||||
out nsMsgSearchAttribValue attrs);
|
||||
|
||||
void getAvailableOperators(in nsMsgSearchAttribValue attrib,
|
||||
out unsigned long length,
|
||||
[retval, array, size_is(length)]
|
||||
out nsMsgSearchOpValue operators);
|
||||
|
||||
void setDefaultAttrib(in nsMsgSearchAttribValue defaultAttrib);
|
||||
};
|
||||
36
mailnews/base/search/public/nsIMsgSearchValue.idl
Normal file
36
mailnews/base/search/public/nsIMsgSearchValue.idl
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsMsgSearchCore.idl"
|
||||
|
||||
interface nsIMsgFolder;
|
||||
|
||||
[scriptable, uuid(783758a0-cdb5-11dc-95ff-0800200c9a66)]
|
||||
interface nsIMsgSearchValue : nsISupports {
|
||||
// type of object
|
||||
attribute nsMsgSearchAttribValue attrib;
|
||||
|
||||
// accessing these will throw an exception if the above
|
||||
// attribute does not match the type!
|
||||
attribute AString str;
|
||||
attribute nsMsgPriorityValue priority;
|
||||
attribute PRTime date;
|
||||
// see nsMsgMessageFlags.idl and nsMsgFolderFlags.idl
|
||||
attribute unsigned long status;
|
||||
attribute unsigned long size;
|
||||
attribute nsMsgKey msgKey;
|
||||
attribute long age; // in days
|
||||
attribute nsIMsgFolder folder;
|
||||
attribute nsMsgLabelValue label;
|
||||
attribute nsMsgJunkStatus junkStatus;
|
||||
/*
|
||||
* junkPercent is set by the message filter plugin, and is approximately
|
||||
* proportional to the probability that a message is junk.
|
||||
* (range 0-100, 100 is junk)
|
||||
*/
|
||||
attribute unsigned long junkPercent;
|
||||
|
||||
AString toString();
|
||||
};
|
||||
174
mailnews/base/search/public/nsIMsgTraitService.idl
Normal file
174
mailnews/base/search/public/nsIMsgTraitService.idl
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
/* 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/. */
|
||||
|
||||
/**
|
||||
* This interface provides management of traits that are used to categorize
|
||||
* messages. A trait is some characteristic of a message, such as being "junk"
|
||||
* or "personal", that may be discoverable by analysis of the message.
|
||||
*
|
||||
* Traits are described by a universal identifier "id" as a string, as well
|
||||
* as a local integer identifer "index". One purpose of this service is to
|
||||
* provide the mapping between those forms.
|
||||
*
|
||||
* Recommended (but not required) format for id:
|
||||
* "extensionName@example.org#traitName"
|
||||
*/
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
[scriptable, uuid(2CB15FB0-A912-40d3-8882-F2765C75655F)]
|
||||
interface nsIMsgTraitService : nsISupports
|
||||
{
|
||||
/**
|
||||
* the highest ever index for a registered trait. The first trait is 1,
|
||||
* == 0 means no traits are defined
|
||||
*/
|
||||
readonly attribute long lastIndex;
|
||||
|
||||
/**
|
||||
* Register a trait. May be called multiple times, but subsequent
|
||||
* calls do not register the trait
|
||||
*
|
||||
* @param id the trait universal identifier
|
||||
*
|
||||
* @return the internal index for the registered trait if newly
|
||||
* registered, else 0
|
||||
*/
|
||||
unsigned long registerTrait(in ACString id);
|
||||
|
||||
/**
|
||||
* Unregister a trait.
|
||||
*
|
||||
* @param id the trait universal identifier
|
||||
*/
|
||||
void unRegisterTrait(in ACString id);
|
||||
|
||||
/**
|
||||
* is a trait registered?
|
||||
*
|
||||
* @param id the trait universal identifier
|
||||
*
|
||||
* @return true if registered
|
||||
*/
|
||||
boolean isRegistered(in ACString id);
|
||||
|
||||
/**
|
||||
* set the trait name, which is an optional short description of the trait
|
||||
*
|
||||
* @param id the trait universal identifier
|
||||
* @param name description of the trait.
|
||||
*/
|
||||
void setName(in ACString id, in ACString name);
|
||||
|
||||
/**
|
||||
* get the trait name, which is an optional short description of the trait
|
||||
*
|
||||
* @param id the trait universal identifier
|
||||
*
|
||||
* @return description of the trait
|
||||
*/
|
||||
ACString getName(in ACString id);
|
||||
|
||||
/**
|
||||
* get the internal index number for the trait.
|
||||
*
|
||||
* @param id the trait universal identifier
|
||||
*
|
||||
* @return internal index number for the trait
|
||||
*/
|
||||
unsigned long getIndex(in ACString id);
|
||||
|
||||
/**
|
||||
* get the trait universal identifier for an internal trait index
|
||||
*
|
||||
* @param index the internal identifier for the trait
|
||||
*
|
||||
* @return trait universal identifier
|
||||
*/
|
||||
ACString getId(in unsigned long index);
|
||||
|
||||
/**
|
||||
* enable the trait for analysis. Each enabled trait will be analyzed by
|
||||
* the bayesian code. The enabled trait is the "pro" trait that represents
|
||||
* messages matching the trait. Each enabled trait also needs a corresponding
|
||||
* anti trait defined, which represents messages that do not match the trait.
|
||||
* The anti trait does not need to be enabled
|
||||
*
|
||||
* @param id the trait universal identifier
|
||||
* @param enabled should this trait be processed by the bayesian analyzer?
|
||||
*/
|
||||
void setEnabled(in ACString id, in boolean enabled);
|
||||
|
||||
/**
|
||||
* Should this trait be processed by the bayes analyzer?
|
||||
*
|
||||
* @param id the trait universal identifier
|
||||
*
|
||||
* @return true if this is a "pro" trait to process
|
||||
*/
|
||||
boolean getEnabled(in ACString id);
|
||||
|
||||
/**
|
||||
* set the anti trait, which indicates messages that have been marked as
|
||||
* NOT matching a particular trait.
|
||||
*
|
||||
* @param id the trait universal identifier
|
||||
* @param antiId trait id for messages marked as not matching the trait
|
||||
*/
|
||||
void setAntiId(in ACString id, in ACString antiId);
|
||||
|
||||
/**
|
||||
* get the id of traits that do not match a particular trait
|
||||
*
|
||||
* @param id the trait universal identifier for a "pro" trait
|
||||
*
|
||||
* @return universal trait identifier for an "anti" trait that does not
|
||||
* match the "pro" trait messages
|
||||
*/
|
||||
ACString getAntiId(in ACString id);
|
||||
|
||||
/**
|
||||
* get an array of traits to be analyzed by the bayesian code. This is
|
||||
* a pair of traits: a "pro" trait of messages that match the trait (and is
|
||||
* set enabled) and an "anti" trait of messages that do not match the trait.
|
||||
*
|
||||
* @param count length of proIndices and antiIndices arrays
|
||||
* @param proIndices trait internal index for "pro" trait to analyze
|
||||
* @param antiIndices trait internal index for corresponding "anti" traits
|
||||
*/
|
||||
void getEnabledIndices(out unsigned long count,
|
||||
[array, size_is(count)] out unsigned long proIndices,
|
||||
[array, size_is(count)] out unsigned long antiIndices);
|
||||
|
||||
/**
|
||||
* Add a trait as an alias of another trait. An alias is a trait whose
|
||||
* counts will be combined with the aliased trait. This allows multiple sets
|
||||
* of corpus data to be used to provide information on a single message
|
||||
* characteristic, while allowing each individual set of corpus data to
|
||||
* retain its own identity.
|
||||
*
|
||||
* @param aTraitIndex the internal identifier for the aliased trait
|
||||
* @param aTraitAlias the internal identifier for the alias to add
|
||||
*/
|
||||
void addAlias(in unsigned long aTraitIndex, in unsigned long aTraitAlias);
|
||||
|
||||
/**
|
||||
* Removes a trait as an alias of another trait.
|
||||
*
|
||||
* @param aTraitIndex the internal identifier for the aliased trait
|
||||
* @param aTraitAlias the internal identifier for the alias to remove
|
||||
*/
|
||||
void removeAlias(in unsigned long aTraitIndex, in unsigned long aTraitAlias);
|
||||
|
||||
/**
|
||||
* Get an array of trait aliases for a trait index, if any
|
||||
*
|
||||
* @param aTraitIndex the internal identifier for the aliased trait
|
||||
* @param aLength length of array of aliases
|
||||
* @param aAliases array of internal identifiers for aliases
|
||||
*/
|
||||
void getAliases(in unsigned long aTraitIndex, out unsigned long aLength,
|
||||
[retval, array, size_is(aLength)] out unsigned long aAliases);
|
||||
|
||||
};
|
||||
112
mailnews/base/search/public/nsMsgBodyHandler.h
Normal file
112
mailnews/base/search/public/nsMsgBodyHandler.h
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
/* 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/. */
|
||||
|
||||
|
||||
#ifndef __nsMsgBodyHandler_h
|
||||
#define __nsMsgBodyHandler_h
|
||||
|
||||
#include "nsIMsgSearchScopeTerm.h"
|
||||
#include "nsILineInputStream.h"
|
||||
#include "nsIMsgDatabase.h"
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// nsMsgBodyHandler: used to retrieve lines from POP and IMAP offline messages.
|
||||
// This is a helper class used by nsMsgSearchTerm::MatchBody
|
||||
//---------------------------------------------------------------------------
|
||||
class nsMsgBodyHandler
|
||||
{
|
||||
public:
|
||||
nsMsgBodyHandler (nsIMsgSearchScopeTerm *,
|
||||
uint32_t length,
|
||||
nsIMsgDBHdr * msg,
|
||||
nsIMsgDatabase * db);
|
||||
|
||||
// we can also create a body handler when doing arbitrary header
|
||||
// filtering...we need the list of headers and the header size as well
|
||||
// if we are doing filtering...if ForFilters is false, headers and
|
||||
// headersSize is ignored!!!
|
||||
nsMsgBodyHandler (nsIMsgSearchScopeTerm *,
|
||||
uint32_t length, nsIMsgDBHdr * msg, nsIMsgDatabase * db,
|
||||
const char * headers /* NULL terminated list of headers */,
|
||||
uint32_t headersSize, bool ForFilters);
|
||||
|
||||
virtual ~nsMsgBodyHandler();
|
||||
|
||||
// Returns next message line in buf and the applicable charset, if found.
|
||||
// The return value is the length of 'buf' or -1 for EOF.
|
||||
int32_t GetNextLine(nsCString &buf, nsCString &charset);
|
||||
|
||||
// Transformations
|
||||
void SetStripHtml (bool strip) { m_stripHtml = strip; }
|
||||
void SetStripHeaders (bool strip) { m_stripHeaders = strip; }
|
||||
|
||||
protected:
|
||||
void Initialize(); // common initialization code
|
||||
|
||||
// filter related methods. For filtering we always use the headers
|
||||
// list instead of the database...
|
||||
bool m_Filtering;
|
||||
int32_t GetNextFilterLine(nsCString &buf);
|
||||
// pointer into the headers list in the original message hdr db...
|
||||
const char * m_headers;
|
||||
uint32_t m_headersSize;
|
||||
uint32_t m_headerBytesRead;
|
||||
|
||||
// local / POP related methods
|
||||
void OpenLocalFolder();
|
||||
|
||||
// goes through the mail folder
|
||||
int32_t GetNextLocalLine(nsCString &buf);
|
||||
|
||||
nsIMsgSearchScopeTerm *m_scope;
|
||||
nsCOMPtr <nsILineInputStream> m_fileLineStream;
|
||||
nsCOMPtr <nsIFile> m_localFile;
|
||||
|
||||
/**
|
||||
* The number of lines in the message. If |m_lineCountInBodyLines| then this
|
||||
* is the number of body lines, otherwise this is the entire number of lines
|
||||
* in the message. This is important so we know when to stop reading the file
|
||||
* without accidentally reading part of the next message.
|
||||
*/
|
||||
uint32_t m_numLocalLines;
|
||||
/**
|
||||
* When true, |m_numLocalLines| is the number of body lines in the message,
|
||||
* when false it is the entire number of lines in the message.
|
||||
*
|
||||
* When a message is an offline IMAP or news message, then the number of lines
|
||||
* will be the entire number of lines, so this should be false. When the
|
||||
* message is a local message, the number of lines will be the number of body
|
||||
* lines.
|
||||
*/
|
||||
bool m_lineCountInBodyLines;
|
||||
|
||||
// Offline IMAP related methods & state
|
||||
|
||||
|
||||
nsCOMPtr<nsIMsgDBHdr> m_msgHdr;
|
||||
nsCOMPtr<nsIMsgDatabase> m_db;
|
||||
|
||||
// Transformations
|
||||
// With the exception of m_isMultipart, these all apply to the various parts
|
||||
bool m_stripHeaders; // true if we're supposed to strip of message headers
|
||||
bool m_stripHtml; // true if we're supposed to strip off HTML tags
|
||||
bool m_pastMsgHeaders; // true if we've already skipped over the message headers
|
||||
bool m_pastPartHeaders; // true if we've already skipped over the part headers
|
||||
bool m_partIsHtml; // true if the Content-type header claims text/html
|
||||
bool m_base64part; // true if the current part is in base64
|
||||
bool m_isMultipart; // true if the message is a multipart/* message
|
||||
bool m_partIsText; // true if the current part is text/*
|
||||
bool m_inMessageAttachment; // true if current part is message/*
|
||||
|
||||
nsTArray<nsCString> m_boundaries; // The boundary strings to look for
|
||||
nsCString m_partCharset; // The charset found in the part
|
||||
|
||||
// See implementation for comments
|
||||
int32_t ApplyTransformations (const nsCString &line, int32_t length,
|
||||
bool &returnThisLine, nsCString &buf);
|
||||
void SniffPossibleMIMEHeader (const nsCString &line);
|
||||
static void StripHtml (nsCString &buf);
|
||||
static void Base64Decode (nsCString &buf);
|
||||
};
|
||||
#endif
|
||||
63
mailnews/base/search/public/nsMsgFilterCore.idl
Normal file
63
mailnews/base/search/public/nsMsgFilterCore.idl
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsMsgSearchCore.idl"
|
||||
|
||||
typedef long nsMsgFilterTypeType;
|
||||
|
||||
[scriptable,uuid(b963a9c6-3a75-4d91-9f79-7186418d4d2d)]
|
||||
interface nsMsgFilterType {
|
||||
/* these longs are all actually of type nsMsgFilterTypeType */
|
||||
const long None = 0x00;
|
||||
const long InboxRule = 0x01;
|
||||
const long InboxJavaScript = 0x02;
|
||||
const long Inbox = InboxRule | InboxJavaScript;
|
||||
const long NewsRule = 0x04;
|
||||
const long NewsJavaScript = 0x08;
|
||||
const long News = NewsRule | NewsJavaScript;
|
||||
const long Incoming = Inbox | News;
|
||||
const long Manual = 0x10;
|
||||
const long PostPlugin = 0x20; // After bayes filtering
|
||||
const long PostOutgoing = 0x40; // After sending
|
||||
const long Archive = 0x80; // Before archiving
|
||||
const long All = Incoming | Manual;
|
||||
};
|
||||
|
||||
typedef long nsMsgFilterMotionValue;
|
||||
|
||||
typedef long nsMsgFilterIndex;
|
||||
|
||||
typedef long nsMsgRuleActionType;
|
||||
|
||||
[scriptable, uuid(7726FE79-AFA3-4a39-8292-733AEE288737)]
|
||||
interface nsMsgFilterAction {
|
||||
|
||||
// Custom Action.
|
||||
const long Custom=-1;
|
||||
/* if you change these, you need to update filter.properties,
|
||||
look for filterActionX */
|
||||
/* these longs are all actually of type nsMsgFilterActionType */
|
||||
const long None=0; /* uninitialized state */
|
||||
const long MoveToFolder=1;
|
||||
const long ChangePriority=2;
|
||||
const long Delete=3;
|
||||
const long MarkRead=4;
|
||||
const long KillThread=5;
|
||||
const long WatchThread=6;
|
||||
const long MarkFlagged=7;
|
||||
const long Label=8;
|
||||
const long Reply=9;
|
||||
const long Forward=10;
|
||||
const long StopExecution=11;
|
||||
const long DeleteFromPop3Server=12;
|
||||
const long LeaveOnPop3Server=13;
|
||||
const long JunkScore=14;
|
||||
const long FetchBodyFromPop3Server=15;
|
||||
const long CopyToFolder=16;
|
||||
const long AddTag=17;
|
||||
const long KillSubthread=18;
|
||||
const long MarkUnread=19;
|
||||
};
|
||||
|
||||
40
mailnews/base/search/public/nsMsgResultElement.h
Normal file
40
mailnews/base/search/public/nsMsgResultElement.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef __nsMsgResultElement_h
|
||||
#define __nsMsgResultElement_h
|
||||
|
||||
#include "nsMsgSearchCore.h"
|
||||
#include "nsIMsgSearchAdapter.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
// nsMsgResultElement specifies a single search hit.
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// nsMsgResultElement is a list of attribute/value pairs which are used to
|
||||
// represent a search hit without requiring a message header or server connection
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
class nsMsgResultElement
|
||||
{
|
||||
public:
|
||||
nsMsgResultElement (nsIMsgSearchAdapter *);
|
||||
virtual ~nsMsgResultElement ();
|
||||
|
||||
static nsresult AssignValues (nsIMsgSearchValue *src, nsMsgSearchValue *dst);
|
||||
nsresult GetValue (nsMsgSearchAttribValue, nsMsgSearchValue **) const;
|
||||
nsresult AddValue (nsIMsgSearchValue*);
|
||||
nsresult AddValue (nsMsgSearchValue*);
|
||||
|
||||
nsresult GetPrettyName (nsMsgSearchValue**);
|
||||
nsresult Open (void *window);
|
||||
|
||||
nsTArray<nsCOMPtr<nsIMsgSearchValue> > m_valueList;
|
||||
nsIMsgSearchAdapter *m_adapter;
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
#endif
|
||||
218
mailnews/base/search/public/nsMsgSearchAdapter.h
Normal file
218
mailnews/base/search/public/nsMsgSearchAdapter.h
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef _nsMsgSearchAdapter_H_
|
||||
#define _nsMsgSearchAdapter_H_
|
||||
|
||||
#include "nsMsgSearchCore.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsStringGlue.h"
|
||||
#include "nsIMsgSearchAdapter.h"
|
||||
#include "nsIMsgSearchValidityTable.h"
|
||||
#include "nsIMsgSearchValidityManager.h"
|
||||
#include "nsIMsgSearchTerm.h"
|
||||
#include "nsINntpIncomingServer.h"
|
||||
|
||||
class nsIMsgSearchScopeTerm;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// These Adapter classes contain the smarts to convert search criteria from
|
||||
// the canonical structures in msg_srch.h into whatever format is required
|
||||
// by their protocol.
|
||||
//
|
||||
// There is a separate Adapter class for area (pop, imap, nntp, ldap) to contain
|
||||
// the special smarts for that protocol.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsMsgSearchAdapter : public nsIMsgSearchAdapter
|
||||
{
|
||||
public:
|
||||
nsMsgSearchAdapter (nsIMsgSearchScopeTerm*, nsISupportsArray *);
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIMSGSEARCHADAPTER
|
||||
|
||||
nsIMsgSearchScopeTerm *m_scope;
|
||||
nsCOMPtr<nsISupportsArray> m_searchTerms; /* linked list of criteria terms */
|
||||
|
||||
bool m_abortCalled;
|
||||
nsString m_defaultCharset;
|
||||
bool m_forceAsciiSearch;
|
||||
|
||||
static nsresult EncodeImap (char **ppEncoding,
|
||||
nsISupportsArray *searchTerms,
|
||||
const char16_t *srcCharset,
|
||||
const char16_t *destCharset,
|
||||
bool reallyDredd = false);
|
||||
|
||||
static nsresult EncodeImapValue(char *encoding, const char *value, bool useQuotes, bool reallyDredd);
|
||||
|
||||
static char *GetImapCharsetParam(const char16_t *destCharset);
|
||||
static char16_t *EscapeSearchUrl (const char16_t *nntpCommand);
|
||||
static char16_t *EscapeImapSearchProtocol(const char16_t *imapCommand);
|
||||
static char16_t *EscapeQuoteImapSearchProtocol(const char16_t *imapCommand);
|
||||
static char *UnEscapeSearchUrl (const char *commandSpecificData);
|
||||
// This stuff lives in the base class because the IMAP search syntax
|
||||
// is used by the Dredd SEARCH command as well as IMAP itself
|
||||
static const char *m_kImapBefore;
|
||||
static const char *m_kImapBody;
|
||||
static const char *m_kImapCC;
|
||||
static const char *m_kImapFrom;
|
||||
static const char *m_kImapNot;
|
||||
static const char *m_kImapOr;
|
||||
static const char *m_kImapSince;
|
||||
static const char *m_kImapSubject;
|
||||
static const char *m_kImapTo;
|
||||
static const char *m_kImapHeader;
|
||||
static const char *m_kImapAnyText;
|
||||
static const char *m_kImapKeyword;
|
||||
static const char *m_kNntpKeywords;
|
||||
static const char *m_kImapSentOn;
|
||||
static const char *m_kImapSeen;
|
||||
static const char *m_kImapAnswered;
|
||||
static const char *m_kImapNotSeen;
|
||||
static const char *m_kImapNotAnswered;
|
||||
static const char *m_kImapCharset;
|
||||
static const char *m_kImapUnDeleted;
|
||||
static const char *m_kImapSizeSmaller;
|
||||
static const char *m_kImapSizeLarger;
|
||||
static const char *m_kImapNew;
|
||||
static const char *m_kImapNotNew;
|
||||
static const char *m_kImapFlagged;
|
||||
static const char *m_kImapNotFlagged;
|
||||
protected:
|
||||
virtual ~nsMsgSearchAdapter();
|
||||
typedef enum _msg_TransformType
|
||||
{
|
||||
kOverwrite, /* "John Doe" -> "John*Doe", simple contains */
|
||||
kInsert, /* "John Doe" -> "John* Doe", name completion */
|
||||
kSurround /* "John Doe" -> "John* *Doe", advanced contains */
|
||||
} msg_TransformType;
|
||||
|
||||
char *TransformSpacesToStars (const char *, msg_TransformType transformType);
|
||||
nsresult OpenNewsResultInUnknownGroup (nsMsgResultElement*);
|
||||
|
||||
static nsresult EncodeImapTerm (nsIMsgSearchTerm *, bool reallyDredd, const char16_t *srcCharset, const char16_t *destCharset, char **ppOutTerm);
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Validity checking for attrib/op pairs. We need to know what operations are
|
||||
// legal in three places:
|
||||
// 1. when the FE brings up the dialog box and needs to know how to build
|
||||
// the menus and enable their items
|
||||
// 2. when the FE fires off a search, we need to check their lists for
|
||||
// correctness
|
||||
// 3. for on-the-fly capability negotion e.g. with XSEARCH-capable news
|
||||
// servers
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsMsgSearchValidityTable final : public nsIMsgSearchValidityTable
|
||||
{
|
||||
public:
|
||||
nsMsgSearchValidityTable ();
|
||||
NS_DECL_NSIMSGSEARCHVALIDITYTABLE
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
protected:
|
||||
int m_numAvailAttribs; // number of rows with at least one available operator
|
||||
typedef struct vtBits
|
||||
{
|
||||
uint16_t bitEnabled : 1;
|
||||
uint16_t bitAvailable : 1;
|
||||
uint16_t bitValidButNotShown : 1;
|
||||
} vtBits;
|
||||
vtBits m_table [nsMsgSearchAttrib::kNumMsgSearchAttributes][nsMsgSearchOp::kNumMsgSearchOperators];
|
||||
private:
|
||||
~nsMsgSearchValidityTable() {}
|
||||
nsMsgSearchAttribValue m_defaultAttrib;
|
||||
};
|
||||
|
||||
// Using getters and setters seems a little nicer then dumping the 2-D array
|
||||
// syntax all over the code
|
||||
#define CHECK_AO if (a < 0 || \
|
||||
a >= nsMsgSearchAttrib::kNumMsgSearchAttributes || \
|
||||
o < 0 || \
|
||||
o >= nsMsgSearchOp::kNumMsgSearchOperators) \
|
||||
return NS_ERROR_ILLEGAL_VALUE;
|
||||
inline nsresult nsMsgSearchValidityTable::SetAvailable (int a, int o, bool b)
|
||||
{ CHECK_AO; m_table [a][o].bitAvailable = b; return NS_OK;}
|
||||
inline nsresult nsMsgSearchValidityTable::SetEnabled (int a, int o, bool b)
|
||||
{ CHECK_AO; m_table [a][o].bitEnabled = b; return NS_OK; }
|
||||
inline nsresult nsMsgSearchValidityTable::SetValidButNotShown (int a, int o, bool b)
|
||||
{ CHECK_AO; m_table [a][o].bitValidButNotShown = b; return NS_OK;}
|
||||
|
||||
inline nsresult nsMsgSearchValidityTable::GetAvailable (int a, int o, bool *aResult)
|
||||
{ CHECK_AO; *aResult = m_table [a][o].bitAvailable; return NS_OK;}
|
||||
inline nsresult nsMsgSearchValidityTable::GetEnabled (int a, int o, bool *aResult)
|
||||
{ CHECK_AO; *aResult = m_table [a][o].bitEnabled; return NS_OK;}
|
||||
inline nsresult nsMsgSearchValidityTable::GetValidButNotShown (int a, int o, bool *aResult)
|
||||
{ CHECK_AO; *aResult = m_table [a][o].bitValidButNotShown; return NS_OK;}
|
||||
#undef CHECK_AO
|
||||
|
||||
class nsMsgSearchValidityManager : public nsIMsgSearchValidityManager
|
||||
{
|
||||
public:
|
||||
nsMsgSearchValidityManager ();
|
||||
|
||||
protected:
|
||||
virtual ~nsMsgSearchValidityManager ();
|
||||
|
||||
public:
|
||||
NS_DECL_NSIMSGSEARCHVALIDITYMANAGER
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
nsresult GetTable (int, nsMsgSearchValidityTable**);
|
||||
|
||||
protected:
|
||||
|
||||
// There's one global validity manager that everyone uses. You *could* do
|
||||
// this with static members of the adapter classes, but having a dedicated
|
||||
// object makes cleanup of these tables (at shutdown-time) automagic.
|
||||
|
||||
nsCOMPtr<nsIMsgSearchValidityTable> m_offlineMailTable;
|
||||
nsCOMPtr<nsIMsgSearchValidityTable> m_offlineMailFilterTable;
|
||||
nsCOMPtr<nsIMsgSearchValidityTable> m_onlineMailTable;
|
||||
nsCOMPtr<nsIMsgSearchValidityTable> m_onlineMailFilterTable;
|
||||
nsCOMPtr<nsIMsgSearchValidityTable> m_onlineManualFilterTable;
|
||||
|
||||
nsCOMPtr<nsIMsgSearchValidityTable> m_newsTable; // online news
|
||||
|
||||
// Local news tables, used for local news searching or offline.
|
||||
nsCOMPtr<nsIMsgSearchValidityTable> m_localNewsTable; // base table
|
||||
nsCOMPtr<nsIMsgSearchValidityTable> m_localNewsJunkTable; // base + junk
|
||||
nsCOMPtr<nsIMsgSearchValidityTable> m_localNewsBodyTable; // base + body
|
||||
nsCOMPtr<nsIMsgSearchValidityTable> m_localNewsJunkBodyTable; // base + junk + body
|
||||
nsCOMPtr<nsIMsgSearchValidityTable> m_ldapTable;
|
||||
nsCOMPtr<nsIMsgSearchValidityTable> m_ldapAndTable;
|
||||
nsCOMPtr<nsIMsgSearchValidityTable> m_localABTable;
|
||||
nsCOMPtr<nsIMsgSearchValidityTable> m_localABAndTable;
|
||||
nsCOMPtr<nsIMsgSearchValidityTable> m_newsFilterTable;
|
||||
|
||||
nsresult NewTable (nsIMsgSearchValidityTable **);
|
||||
|
||||
nsresult InitOfflineMailTable();
|
||||
nsresult InitOfflineMailFilterTable();
|
||||
nsresult InitOnlineMailTable();
|
||||
nsresult InitOnlineMailFilterTable();
|
||||
nsresult InitOnlineManualFilterTable();
|
||||
nsresult InitNewsTable();
|
||||
nsresult InitLocalNewsTable();
|
||||
nsresult InitLocalNewsJunkTable();
|
||||
nsresult InitLocalNewsBodyTable();
|
||||
nsresult InitLocalNewsJunkBodyTable();
|
||||
nsresult InitNewsFilterTable();
|
||||
|
||||
//set the custom headers in the table, changes whenever "mailnews.customHeaders" pref changes.
|
||||
nsresult SetOtherHeadersInTable(nsIMsgSearchValidityTable *table, const char *customHeaders);
|
||||
|
||||
nsresult InitLdapTable();
|
||||
nsresult InitLdapAndTable();
|
||||
nsresult InitLocalABTable();
|
||||
nsresult InitLocalABAndTable();
|
||||
nsresult SetUpABTable(nsIMsgSearchValidityTable *aTable, bool isOrTable);
|
||||
nsresult EnableDirectoryAttribute(nsIMsgSearchValidityTable *table, nsMsgSearchAttribValue aSearchAttrib);
|
||||
};
|
||||
|
||||
#endif
|
||||
107
mailnews/base/search/public/nsMsgSearchBoolExpression.h
Normal file
107
mailnews/base/search/public/nsMsgSearchBoolExpression.h
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsMsgSearchCore.h"
|
||||
|
||||
#ifndef __nsMsgSearchBoolExpression_h
|
||||
#define __nsMsgSearchBoolExpression_h
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsMsgSearchBoolExpression is a class added to provide AND/OR terms in search queries.
|
||||
// A nsMsgSearchBoolExpression contains either a search term or two nsMsgSearchBoolExpressions and
|
||||
// a boolean operator.
|
||||
// I (mscott) am placing it here for now....
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/* CBoolExpression --> encapsulates one or more search terms by internally
|
||||
representing the search terms and their boolean operators as a binary
|
||||
expression tree. Each node in the tree consists of either
|
||||
(1) a boolean operator and two nsMsgSearchBoolExpressions or
|
||||
(2) if the node is a leaf node then it contains a search term.
|
||||
With each search term that is part of the expression we may also keep
|
||||
a character string. The character
|
||||
string is used to store the IMAP/NNTP encoding of the search term. This
|
||||
makes generating a search encoding (for online) easier.
|
||||
|
||||
For IMAP/NNTP: nsMsgSearchBoolExpression has/assumes knowledge about how
|
||||
AND and OR search terms are combined according to IMAP4 and NNTP protocol.
|
||||
That is the only piece of IMAP/NNTP knowledge it is aware of.
|
||||
|
||||
Order of Evaluation: Okay, the way in which the boolean expression tree
|
||||
is put together directly effects the order of evaluation. We currently
|
||||
support left to right evaluation.
|
||||
Supporting other order of evaluations involves adding new internal add
|
||||
term methods.
|
||||
*/
|
||||
|
||||
class nsMsgSearchBoolExpression
|
||||
{
|
||||
public:
|
||||
|
||||
// create a leaf node expression
|
||||
nsMsgSearchBoolExpression(nsIMsgSearchTerm * aNewTerm,
|
||||
char * aEncodingString = NULL);
|
||||
|
||||
// create a non-leaf node expression containing 2 expressions
|
||||
// and a boolean operator
|
||||
nsMsgSearchBoolExpression(nsMsgSearchBoolExpression *,
|
||||
nsMsgSearchBoolExpression *,
|
||||
nsMsgSearchBooleanOperator boolOp);
|
||||
|
||||
nsMsgSearchBoolExpression();
|
||||
~nsMsgSearchBoolExpression(); // recursively destroys all sub
|
||||
// expressions as well
|
||||
|
||||
// accessors
|
||||
|
||||
// Offline
|
||||
static nsMsgSearchBoolExpression * AddSearchTerm (nsMsgSearchBoolExpression * aOrigExpr, nsIMsgSearchTerm * aNewTerm, char * aEncodingStr); // IMAP/NNTP
|
||||
static nsMsgSearchBoolExpression * AddExpressionTree(nsMsgSearchBoolExpression * aOrigExpr, nsMsgSearchBoolExpression * aExpression, bool aBoolOp);
|
||||
|
||||
// parses the expression tree and all
|
||||
// expressions underneath this node to
|
||||
// determine if the end result is true or false.
|
||||
bool OfflineEvaluate(nsIMsgDBHdr *msgToMatch,
|
||||
const char *defaultCharset, nsIMsgSearchScopeTerm *scope,
|
||||
nsIMsgDatabase *db, const char *headers, uint32_t headerSize,
|
||||
bool Filtering);
|
||||
|
||||
// assuming the expression is for online
|
||||
// searches, determine the length of the
|
||||
// resulting IMAP/NNTP encoding string
|
||||
int32_t CalcEncodeStrSize();
|
||||
|
||||
// fills pre-allocated
|
||||
// memory in buffer with
|
||||
// the IMAP/NNTP encoding for the expression
|
||||
void GenerateEncodeStr(nsCString * buffer);
|
||||
|
||||
// if we are not a leaf node, then we have two other expressions
|
||||
// and a boolean operator
|
||||
nsMsgSearchBoolExpression * m_leftChild;
|
||||
nsMsgSearchBoolExpression * m_rightChild;
|
||||
nsMsgSearchBooleanOperator m_boolOp;
|
||||
|
||||
protected:
|
||||
// if we are a leaf node, all we have is a search term
|
||||
|
||||
nsIMsgSearchTerm * m_term;
|
||||
|
||||
// store IMAP/NNTP encoding for the search term if applicable
|
||||
nsCString m_encodingStr;
|
||||
|
||||
// internal methods
|
||||
|
||||
// the idea is to separate the public interface for adding terms to
|
||||
// the expression tree from the order of evaluation which influences
|
||||
// how we internally construct the tree. Right now, we are supporting
|
||||
// left to right evaluation so the tree is constructed to represent
|
||||
// that by calling leftToRightAddTerm. If future forms of evaluation
|
||||
// need to be supported, add new methods here for proper tree construction.
|
||||
nsMsgSearchBoolExpression * leftToRightAddTerm(nsIMsgSearchTerm * newTerm,
|
||||
char * encodingStr);
|
||||
};
|
||||
|
||||
#endif
|
||||
222
mailnews/base/search/public/nsMsgSearchCore.idl
Normal file
222
mailnews/base/search/public/nsMsgSearchCore.idl
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "MailNewsTypes2.idl"
|
||||
|
||||
interface nsIMsgFolder;
|
||||
|
||||
interface nsIMsgDatabase;
|
||||
interface nsIMsgDBHdr;
|
||||
|
||||
[scriptable, uuid(6e893e59-af98-4f62-a326-0f00f32147cd)]
|
||||
|
||||
interface nsMsgSearchScope {
|
||||
const nsMsgSearchScopeValue offlineMail = 0;
|
||||
const nsMsgSearchScopeValue offlineMailFilter = 1;
|
||||
const nsMsgSearchScopeValue onlineMail = 2;
|
||||
const nsMsgSearchScopeValue onlineMailFilter = 3;
|
||||
/// offline news, base table, no body or junk
|
||||
const nsMsgSearchScopeValue localNews = 4;
|
||||
const nsMsgSearchScopeValue news = 5;
|
||||
const nsMsgSearchScopeValue newsEx = 6;
|
||||
const nsMsgSearchScopeValue LDAP = 7;
|
||||
const nsMsgSearchScopeValue LocalAB = 8;
|
||||
const nsMsgSearchScopeValue allSearchableGroups = 9;
|
||||
const nsMsgSearchScopeValue newsFilter = 10;
|
||||
const nsMsgSearchScopeValue LocalABAnd = 11;
|
||||
const nsMsgSearchScopeValue LDAPAnd = 12;
|
||||
// IMAP and NEWS, searched using local headers
|
||||
const nsMsgSearchScopeValue onlineManual = 13;
|
||||
/// local news + junk
|
||||
const nsMsgSearchScopeValue localNewsJunk = 14;
|
||||
/// local news + body
|
||||
const nsMsgSearchScopeValue localNewsBody = 15;
|
||||
/// local news + junk + body
|
||||
const nsMsgSearchScopeValue localNewsJunkBody = 16;
|
||||
};
|
||||
|
||||
typedef long nsMsgSearchAttribValue;
|
||||
|
||||
/**
|
||||
* Definitions of search attribute types. The numerical order
|
||||
* from here will also be used to determine the order that the
|
||||
* attributes display in the filter editor.
|
||||
*/
|
||||
[scriptable, uuid(a83ca7e8-4591-4111-8fb8-fd76ac73c866)]
|
||||
interface nsMsgSearchAttrib {
|
||||
const nsMsgSearchAttribValue Custom = -2; /* a custom term, see nsIMsgSearchCustomTerm */
|
||||
const nsMsgSearchAttribValue Default = -1;
|
||||
const nsMsgSearchAttribValue Subject = 0; /* mail and news */
|
||||
const nsMsgSearchAttribValue Sender = 1;
|
||||
const nsMsgSearchAttribValue Body = 2;
|
||||
const nsMsgSearchAttribValue Date = 3;
|
||||
|
||||
const nsMsgSearchAttribValue Priority = 4; /* mail only */
|
||||
const nsMsgSearchAttribValue MsgStatus = 5;
|
||||
const nsMsgSearchAttribValue To = 6;
|
||||
const nsMsgSearchAttribValue CC = 7;
|
||||
const nsMsgSearchAttribValue ToOrCC = 8;
|
||||
const nsMsgSearchAttribValue AllAddresses = 9;
|
||||
|
||||
const nsMsgSearchAttribValue Location = 10; /* result list only */
|
||||
const nsMsgSearchAttribValue MessageKey = 11; /* message result elems */
|
||||
const nsMsgSearchAttribValue AgeInDays = 12;
|
||||
const nsMsgSearchAttribValue FolderInfo = 13; /* for "view thread context" from result */
|
||||
const nsMsgSearchAttribValue Size = 14;
|
||||
const nsMsgSearchAttribValue AnyText = 15;
|
||||
const nsMsgSearchAttribValue Keywords = 16; // keywords are the internal representation of tags.
|
||||
|
||||
const nsMsgSearchAttribValue Name = 17;
|
||||
const nsMsgSearchAttribValue DisplayName = 18;
|
||||
const nsMsgSearchAttribValue Nickname = 19;
|
||||
const nsMsgSearchAttribValue ScreenName = 20;
|
||||
const nsMsgSearchAttribValue Email = 21;
|
||||
const nsMsgSearchAttribValue AdditionalEmail = 22;
|
||||
const nsMsgSearchAttribValue PhoneNumber = 23;
|
||||
const nsMsgSearchAttribValue WorkPhone = 24;
|
||||
const nsMsgSearchAttribValue HomePhone = 25;
|
||||
const nsMsgSearchAttribValue Fax = 26;
|
||||
const nsMsgSearchAttribValue Pager = 27;
|
||||
const nsMsgSearchAttribValue Mobile = 28;
|
||||
const nsMsgSearchAttribValue City = 29;
|
||||
const nsMsgSearchAttribValue Street = 30;
|
||||
const nsMsgSearchAttribValue Title = 31;
|
||||
const nsMsgSearchAttribValue Organization = 32;
|
||||
const nsMsgSearchAttribValue Department = 33;
|
||||
|
||||
// 34 - 43, reserved for ab / LDAP;
|
||||
const nsMsgSearchAttribValue HasAttachmentStatus = 44;
|
||||
const nsMsgSearchAttribValue JunkStatus = 45;
|
||||
const nsMsgSearchAttribValue JunkPercent = 46;
|
||||
const nsMsgSearchAttribValue JunkScoreOrigin = 47;
|
||||
const nsMsgSearchAttribValue Label = 48; /* mail only...can search by label */
|
||||
const nsMsgSearchAttribValue HdrProperty = 49; // uses nsIMsgSearchTerm::hdrProperty
|
||||
const nsMsgSearchAttribValue FolderFlag = 50; // uses nsIMsgSearchTerm::status
|
||||
const nsMsgSearchAttribValue Uint32HdrProperty = 51; // uses nsIMsgSearchTerm::hdrProperty
|
||||
|
||||
// 52 is for showing customize - in ui headers start from 53 onwards up until 99.
|
||||
|
||||
/** OtherHeader MUST ALWAYS BE LAST attribute since
|
||||
* we can have an arbitrary # of these. The number can be changed,
|
||||
* however, because we never persist AttribValues as integers.
|
||||
*/
|
||||
const nsMsgSearchAttribValue OtherHeader = 52;
|
||||
// must be last attribute
|
||||
const nsMsgSearchAttribValue kNumMsgSearchAttributes = 100;
|
||||
};
|
||||
|
||||
typedef long nsMsgSearchOpValue;
|
||||
|
||||
[scriptable, uuid(9160b196-6fcb-4eba-aaaf-6c806c4ee420)]
|
||||
interface nsMsgSearchOp {
|
||||
const nsMsgSearchOpValue Contains = 0; /* for text attributes */
|
||||
const nsMsgSearchOpValue DoesntContain = 1;
|
||||
const nsMsgSearchOpValue Is = 2; /* is and isn't also apply to some non-text attrs */
|
||||
const nsMsgSearchOpValue Isnt = 3;
|
||||
const nsMsgSearchOpValue IsEmpty = 4;
|
||||
|
||||
const nsMsgSearchOpValue IsBefore = 5; /* for date attributes */
|
||||
const nsMsgSearchOpValue IsAfter = 6;
|
||||
|
||||
const nsMsgSearchOpValue IsHigherThan = 7; /* for priority. Is also applies */
|
||||
const nsMsgSearchOpValue IsLowerThan = 8;
|
||||
|
||||
const nsMsgSearchOpValue BeginsWith = 9;
|
||||
const nsMsgSearchOpValue EndsWith = 10;
|
||||
|
||||
const nsMsgSearchOpValue SoundsLike = 11; /* for LDAP phoenetic matching */
|
||||
const nsMsgSearchOpValue LdapDwim = 12; /* Do What I Mean for simple search */
|
||||
|
||||
const nsMsgSearchOpValue IsGreaterThan = 13;
|
||||
const nsMsgSearchOpValue IsLessThan = 14;
|
||||
|
||||
const nsMsgSearchOpValue NameCompletion = 15; /* Name Completion operator...as the name implies =) */
|
||||
const nsMsgSearchOpValue IsInAB = 16;
|
||||
const nsMsgSearchOpValue IsntInAB = 17;
|
||||
const nsMsgSearchOpValue IsntEmpty = 18; /* primarily for tags */
|
||||
const nsMsgSearchOpValue Matches = 19; /* generic term for use by custom terms */
|
||||
const nsMsgSearchOpValue DoesntMatch = 20; /* generic term for use by custom terms */
|
||||
const nsMsgSearchOpValue kNumMsgSearchOperators = 21; /* must be last operator */
|
||||
};
|
||||
|
||||
typedef long nsMsgSearchWidgetValue;
|
||||
|
||||
/* FEs use this to help build the search dialog box */
|
||||
[scriptable,uuid(903dd2e8-304e-11d3-92e6-00a0c900d445)]
|
||||
interface nsMsgSearchWidget {
|
||||
const nsMsgSearchWidgetValue Text = 0;
|
||||
const nsMsgSearchWidgetValue Date = 1;
|
||||
const nsMsgSearchWidgetValue Menu = 2;
|
||||
const nsMsgSearchWidgetValue Int = 3; /* added to account for age in days which requires an integer field */
|
||||
const nsMsgSearchWidgetValue None = 4;
|
||||
};
|
||||
|
||||
typedef long nsMsgSearchTypeValue;
|
||||
|
||||
|
||||
/* Used to specify type of search to be performed */
|
||||
[scriptable,uuid(964b7f32-304e-11d3-ae13-00a0c900d445)]
|
||||
interface nsMsgSearchType {
|
||||
const nsMsgSearchTypeValue None = 0;
|
||||
const nsMsgSearchTypeValue RootDSE = 1;
|
||||
const nsMsgSearchTypeValue Normal = 2;
|
||||
const nsMsgSearchTypeValue LdapVLV = 3;
|
||||
const nsMsgSearchTypeValue NameCompletion = 4;
|
||||
};
|
||||
|
||||
typedef long nsMsgSearchBooleanOperator;
|
||||
|
||||
[scriptable, uuid(a37f3f4a-304e-11d3-8f94-00a0c900d445)]
|
||||
interface nsMsgSearchBooleanOp {
|
||||
const nsMsgSearchBooleanOperator BooleanOR = 0;
|
||||
const nsMsgSearchBooleanOperator BooleanAND = 1;
|
||||
};
|
||||
|
||||
/* Use this to specify the value of a search term */
|
||||
|
||||
[ptr] native nsMsgSearchValue(nsMsgSearchValue);
|
||||
|
||||
%{C++
|
||||
#include "nsStringGlue.h"
|
||||
|
||||
typedef struct nsMsgSearchValue
|
||||
{
|
||||
nsMsgSearchAttribValue attribute;
|
||||
union
|
||||
{
|
||||
nsMsgPriorityValue priority;
|
||||
PRTime date;
|
||||
uint32_t msgStatus; /* see MSG_FLAG in msgcom.h */
|
||||
uint32_t size;
|
||||
nsMsgKey key;
|
||||
int32_t age; /* in days */
|
||||
nsIMsgFolder *folder;
|
||||
nsMsgLabelValue label;
|
||||
uint32_t junkStatus;
|
||||
uint32_t junkPercent;
|
||||
} u;
|
||||
char *string;
|
||||
nsString utf16String;
|
||||
} nsMsgSearchValue;
|
||||
%}
|
||||
|
||||
[ptr] native nsMsgSearchTerm(nsMsgSearchTerm);
|
||||
|
||||
// Please note the ! at the start of this macro, which means the macro
|
||||
// needs to enumerate the non-string attributes.
|
||||
%{C++
|
||||
#define IS_STRING_ATTRIBUTE(_a) \
|
||||
(!(_a == nsMsgSearchAttrib::Priority || _a == nsMsgSearchAttrib::Date || \
|
||||
_a == nsMsgSearchAttrib::MsgStatus || _a == nsMsgSearchAttrib::MessageKey || \
|
||||
_a == nsMsgSearchAttrib::Size || _a == nsMsgSearchAttrib::AgeInDays || \
|
||||
_a == nsMsgSearchAttrib::FolderInfo || _a == nsMsgSearchAttrib::Location || \
|
||||
_a == nsMsgSearchAttrib::Label || _a == nsMsgSearchAttrib::JunkStatus || \
|
||||
_a == nsMsgSearchAttrib::FolderFlag || _a == nsMsgSearchAttrib::Uint32HdrProperty || \
|
||||
_a == nsMsgSearchAttrib::JunkPercent || _a == nsMsgSearchAttrib::HasAttachmentStatus))
|
||||
%}
|
||||
|
||||
[ptr] native nsSearchMenuItem(nsSearchMenuItem);
|
||||
|
||||
45
mailnews/base/search/public/nsMsgSearchScopeTerm.h
Normal file
45
mailnews/base/search/public/nsMsgSearchScopeTerm.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef __nsMsgSearchScopeTerm_h
|
||||
#define __nsMsgSearchScopeTerm_h
|
||||
|
||||
#include "nsMsgSearchCore.h"
|
||||
#include "nsMsgSearchScopeTerm.h"
|
||||
#include "nsIMsgSearchAdapter.h"
|
||||
#include "nsIMsgFolder.h"
|
||||
#include "nsIMsgSearchAdapter.h"
|
||||
#include "nsIMsgSearchSession.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIWeakReference.h"
|
||||
#include "nsIWeakReferenceUtils.h"
|
||||
|
||||
class nsMsgSearchScopeTerm : public nsIMsgSearchScopeTerm
|
||||
{
|
||||
public:
|
||||
nsMsgSearchScopeTerm (nsIMsgSearchSession *, nsMsgSearchScopeValue, nsIMsgFolder *);
|
||||
nsMsgSearchScopeTerm ();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIMSGSEARCHSCOPETERM
|
||||
|
||||
nsresult TimeSlice (bool *aDone);
|
||||
nsresult InitializeAdapter (nsISupportsArray *termList);
|
||||
|
||||
char *GetStatusBarName ();
|
||||
|
||||
nsMsgSearchScopeValue m_attribute;
|
||||
char *m_name;
|
||||
nsCOMPtr <nsIMsgFolder> m_folder;
|
||||
nsCOMPtr <nsIMsgSearchAdapter> m_adapter;
|
||||
nsCOMPtr <nsIInputStream> m_inputStream; // for message bodies
|
||||
nsWeakPtr m_searchSession;
|
||||
bool m_searchServer;
|
||||
|
||||
private:
|
||||
virtual ~nsMsgSearchScopeTerm();
|
||||
};
|
||||
|
||||
#endif
|
||||
85
mailnews/base/search/public/nsMsgSearchTerm.h
Normal file
85
mailnews/base/search/public/nsMsgSearchTerm.h
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
|
||||
#ifndef __nsMsgSearchTerm_h
|
||||
#define __nsMsgSearchTerm_h
|
||||
//---------------------------------------------------------------------------
|
||||
// nsMsgSearchTerm specifies one criterion, e.g. name contains phil
|
||||
//---------------------------------------------------------------------------
|
||||
#include "nsIMsgSearchSession.h"
|
||||
#include "nsIMsgSearchScopeTerm.h"
|
||||
#include "nsIMsgSearchTerm.h"
|
||||
#include "nsIMsgSearchCustomTerm.h"
|
||||
|
||||
// needed to search for addresses in address books
|
||||
#include "nsIAbDirectory.h"
|
||||
|
||||
#define EMPTY_MESSAGE_LINE(buf) (buf[0] == '\r' || buf[0] == '\n' || buf[0] == '\0')
|
||||
|
||||
class nsMsgSearchTerm : public nsIMsgSearchTerm
|
||||
{
|
||||
public:
|
||||
nsMsgSearchTerm();
|
||||
nsMsgSearchTerm (nsMsgSearchAttribValue, nsMsgSearchOpValue, nsIMsgSearchValue *, nsMsgSearchBooleanOperator, const char * arbitraryHeader);
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIMSGSEARCHTERM
|
||||
|
||||
nsresult DeStream (char *, int16_t length);
|
||||
nsresult DeStreamNew (char *, int16_t length);
|
||||
|
||||
nsresult GetLocalTimes (PRTime, PRTime, PRExplodedTime &, PRExplodedTime &);
|
||||
|
||||
bool IsBooleanOpAND() { return m_booleanOp == nsMsgSearchBooleanOp::BooleanAND ? true : false;}
|
||||
nsMsgSearchBooleanOperator GetBooleanOp() {return m_booleanOp;}
|
||||
// maybe should return nsString & ??
|
||||
const char * GetArbitraryHeader() {return m_arbitraryHeader.get();}
|
||||
|
||||
static char * EscapeQuotesInStr(const char *str);
|
||||
|
||||
nsMsgSearchAttribValue m_attribute;
|
||||
nsMsgSearchOpValue m_operator;
|
||||
nsMsgSearchValue m_value;
|
||||
|
||||
// boolean operator to be applied to this search term and the search term which precedes it.
|
||||
nsMsgSearchBooleanOperator m_booleanOp;
|
||||
|
||||
// user specified string for the name of the arbitrary header to be used in the search
|
||||
// only has a value when m_attribute = OtherHeader!!!!
|
||||
nsCString m_arbitraryHeader;
|
||||
|
||||
// db hdr property name to use - used when m_attribute = HdrProperty.
|
||||
nsCString m_hdrProperty;
|
||||
bool m_matchAll; // does this term match all headers?
|
||||
nsCString m_customId; // id of custom search term
|
||||
|
||||
protected:
|
||||
virtual ~nsMsgSearchTerm();
|
||||
|
||||
nsresult MatchString(const nsACString &stringToMatch, const char *charset,
|
||||
bool *pResult);
|
||||
nsresult MatchString(const nsAString &stringToMatch, bool *pResult);
|
||||
nsresult OutputValue(nsCString &outputStr);
|
||||
nsresult ParseAttribute(char *inStream, nsMsgSearchAttribValue *attrib);
|
||||
nsresult ParseOperator(char *inStream, nsMsgSearchOpValue *value);
|
||||
nsresult ParseValue(char *inStream);
|
||||
/**
|
||||
* Switch a string to lower case, except for special database rows
|
||||
* that are not headers, but could be headers
|
||||
*
|
||||
* @param aValue the string to switch
|
||||
*/
|
||||
void ToLowerCaseExceptSpecials(nsACString &aValue);
|
||||
nsresult InitializeAddressBook();
|
||||
nsresult MatchInAddressBook(const nsAString &aAddress, bool *pResult);
|
||||
// fields used by search in address book
|
||||
nsCOMPtr <nsIAbDirectory> mDirectory;
|
||||
|
||||
bool mBeginsGrouping;
|
||||
bool mEndsGrouping;
|
||||
};
|
||||
|
||||
#endif
|
||||
14
mailnews/base/search/src/Bogofilter.sfd
Normal file
14
mailnews/base/search/src/Bogofilter.sfd
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
version="9"
|
||||
logging="yes"
|
||||
name="BogofilterYes"
|
||||
enabled="yes"
|
||||
type="17"
|
||||
action="JunkScore"
|
||||
actionValue="100"
|
||||
condition="OR (\"X-Bogosity\",begins with,Spam) OR (\"X-Bogosity\",begins with,Y)"
|
||||
name="BogofilterNo"
|
||||
enabled="yes"
|
||||
type="17"
|
||||
action="JunkScore"
|
||||
actionValue="0"
|
||||
condition="OR (\"X-Bogosity\",begins with,Ham) OR (\"X-Bogosity\",begins with,N)"
|
||||
14
mailnews/base/search/src/DSPAM.sfd
Normal file
14
mailnews/base/search/src/DSPAM.sfd
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
version="9"
|
||||
logging="yes"
|
||||
name="DSPAMYes"
|
||||
enabled="yes"
|
||||
type="17"
|
||||
action="JunkScore"
|
||||
actionValue="100"
|
||||
condition="OR (\"X-DSPAM-Result\",begins with,Spam)"
|
||||
name="DSPAMNo"
|
||||
enabled="yes"
|
||||
type="17"
|
||||
action="JunkScore"
|
||||
actionValue="0"
|
||||
condition="OR (\"X-DSPAM-Result\",begins with,Innocent)"
|
||||
8
mailnews/base/search/src/Habeas.sfd
Normal file
8
mailnews/base/search/src/Habeas.sfd
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
version="8"
|
||||
logging="yes"
|
||||
name="HabeasNo"
|
||||
enabled="yes"
|
||||
type="1"
|
||||
action="JunkScore"
|
||||
actionValue="0"
|
||||
condition="OR (\"X-Habeas-SWE-3\",is,\"like Habeas SWE (tm)\")"
|
||||
14
mailnews/base/search/src/POPFile.sfd
Normal file
14
mailnews/base/search/src/POPFile.sfd
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
version="9"
|
||||
logging="yes"
|
||||
name="POPFileYes"
|
||||
enabled="yes"
|
||||
type="17"
|
||||
action="JunkScore"
|
||||
actionValue="100"
|
||||
condition="OR (\"X-Text-Classification\",begins with,spam)"
|
||||
name="POPFileNo"
|
||||
enabled="yes"
|
||||
type="17"
|
||||
action="JunkScore"
|
||||
actionValue="0"
|
||||
condition="OR (\"X-Text-Classification\",begins with,inbox) OR (\"X-Text-Classification\",begins with,allowed)"
|
||||
14
mailnews/base/search/src/SpamAssassin.sfd
Normal file
14
mailnews/base/search/src/SpamAssassin.sfd
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
version="9"
|
||||
logging="yes"
|
||||
name="SpamAssassinYes"
|
||||
enabled="yes"
|
||||
type="17"
|
||||
action="JunkScore"
|
||||
actionValue="100"
|
||||
condition="OR (\"X-Spam-Status\",begins with,Yes) OR (\"X-Spam-Flag\",begins with,YES) OR (subject,begins with,***SPAM***)"
|
||||
name="SpamAssassinNo"
|
||||
enabled="yes"
|
||||
type="17"
|
||||
action="JunkScore"
|
||||
actionValue="0"
|
||||
condition="OR (\"X-Spam-Status\",begins with,No)"
|
||||
14
mailnews/base/search/src/SpamCatcher.sfd
Normal file
14
mailnews/base/search/src/SpamCatcher.sfd
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
version="9"
|
||||
logging="yes"
|
||||
name="SpamCatcherNo"
|
||||
enabled="yes"
|
||||
type="17"
|
||||
action="JunkScore"
|
||||
actionValue="0"
|
||||
condition="OR (\"x-SpamCatcher\",begins with,No)"
|
||||
name="SpamCatcherYes"
|
||||
enabled="yes"
|
||||
type="17"
|
||||
action="JunkScore"
|
||||
actionValue="100"
|
||||
condition="OR (\"x-SpamCatcher\",begins with,Yes)"
|
||||
14
mailnews/base/search/src/SpamPal.sfd
Normal file
14
mailnews/base/search/src/SpamPal.sfd
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
version="9"
|
||||
logging="yes"
|
||||
name="SpamPalNo"
|
||||
enabled="yes"
|
||||
type="17"
|
||||
action="JunkScore"
|
||||
actionValue="0"
|
||||
condition="OR (\"X-SpamPal\",begins with,PASS)"
|
||||
name="SpamPalYes"
|
||||
enabled="yes"
|
||||
type="17"
|
||||
action="JunkScore"
|
||||
actionValue="100"
|
||||
condition="OR (\"X-SpamPal\",begins with,SPAM)"
|
||||
33
mailnews/base/search/src/moz.build
Normal file
33
mailnews/base/search/src/moz.build
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
SOURCES += [
|
||||
'nsMsgBodyHandler.cpp',
|
||||
'nsMsgFilter.cpp',
|
||||
'nsMsgFilterList.cpp',
|
||||
'nsMsgFilterService.cpp',
|
||||
'nsMsgImapSearch.cpp',
|
||||
'nsMsgLocalSearch.cpp',
|
||||
'nsMsgSearchAdapter.cpp',
|
||||
'nsMsgSearchNews.cpp',
|
||||
'nsMsgSearchSession.cpp',
|
||||
'nsMsgSearchTerm.cpp',
|
||||
'nsMsgSearchValue.cpp',
|
||||
]
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'nsMsgTraitService.js',
|
||||
'nsMsgTraitService.manifest',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'mail'
|
||||
|
||||
FINAL_TARGET_FILES.isp += [
|
||||
'Bogofilter.sfd',
|
||||
'DSPAM.sfd',
|
||||
'POPFile.sfd',
|
||||
'SpamAssassin.sfd',
|
||||
'SpamPal.sfd',
|
||||
]
|
||||
487
mailnews/base/search/src/nsMsgBodyHandler.cpp
Normal file
487
mailnews/base/search/src/nsMsgBodyHandler.cpp
Normal file
|
|
@ -0,0 +1,487 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "msgCore.h"
|
||||
#include "nsMsgSearchCore.h"
|
||||
#include "nsMsgUtils.h"
|
||||
#include "nsMsgBodyHandler.h"
|
||||
#include "nsMsgSearchTerm.h"
|
||||
#include "nsIMsgHdr.h"
|
||||
#include "nsMsgMessageFlags.h"
|
||||
#include "nsISeekableStream.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsIFile.h"
|
||||
#include "plbase64.h"
|
||||
#include "prmem.h"
|
||||
#include "nsMimeTypes.h"
|
||||
|
||||
nsMsgBodyHandler::nsMsgBodyHandler (nsIMsgSearchScopeTerm * scope,
|
||||
uint32_t numLines,
|
||||
nsIMsgDBHdr* msg, nsIMsgDatabase * db)
|
||||
{
|
||||
m_scope = scope;
|
||||
m_numLocalLines = numLines;
|
||||
uint32_t flags;
|
||||
m_lineCountInBodyLines = NS_SUCCEEDED(msg->GetFlags(&flags)) ?
|
||||
!(flags & nsMsgMessageFlags::Offline) : true;
|
||||
// account for added x-mozilla-status lines, and envelope line.
|
||||
if (!m_lineCountInBodyLines)
|
||||
m_numLocalLines += 3;
|
||||
m_msgHdr = msg;
|
||||
m_db = db;
|
||||
|
||||
// the following are variables used when the body handler is handling stuff from filters....through this constructor, that is not the
|
||||
// case so we set them to NULL.
|
||||
m_headers = NULL;
|
||||
m_headersSize = 0;
|
||||
m_Filtering = false; // make sure we set this before we call initialize...
|
||||
|
||||
Initialize(); // common initialization stuff
|
||||
OpenLocalFolder();
|
||||
}
|
||||
|
||||
nsMsgBodyHandler::nsMsgBodyHandler(nsIMsgSearchScopeTerm * scope,
|
||||
uint32_t numLines,
|
||||
nsIMsgDBHdr* msg, nsIMsgDatabase* db,
|
||||
const char * headers, uint32_t headersSize,
|
||||
bool Filtering)
|
||||
{
|
||||
m_scope = scope;
|
||||
m_numLocalLines = numLines;
|
||||
uint32_t flags;
|
||||
m_lineCountInBodyLines = NS_SUCCEEDED(msg->GetFlags(&flags)) ?
|
||||
!(flags & nsMsgMessageFlags::Offline) : true;
|
||||
// account for added x-mozilla-status lines, and envelope line.
|
||||
if (!m_lineCountInBodyLines)
|
||||
m_numLocalLines += 3;
|
||||
m_msgHdr = msg;
|
||||
m_db = db;
|
||||
m_headersSize = headersSize;
|
||||
m_Filtering = Filtering;
|
||||
|
||||
Initialize();
|
||||
|
||||
if (m_Filtering)
|
||||
m_headers = headers;
|
||||
else
|
||||
OpenLocalFolder(); // if nothing else applies, then we must be a POP folder file
|
||||
}
|
||||
|
||||
void nsMsgBodyHandler::Initialize()
|
||||
// common initialization code regardless of what body type we are handling...
|
||||
{
|
||||
// Default transformations for local message search and MAPI access
|
||||
m_stripHeaders = true;
|
||||
m_stripHtml = true;
|
||||
m_partIsHtml = false;
|
||||
m_base64part = false;
|
||||
m_isMultipart = false;
|
||||
m_partIsText = true; // Default is text/plain, maybe proven otherwise later.
|
||||
m_pastMsgHeaders = false;
|
||||
m_pastPartHeaders = false;
|
||||
m_inMessageAttachment = false;
|
||||
m_headerBytesRead = 0;
|
||||
}
|
||||
|
||||
nsMsgBodyHandler::~nsMsgBodyHandler()
|
||||
{
|
||||
}
|
||||
|
||||
int32_t nsMsgBodyHandler::GetNextLine (nsCString &buf, nsCString &charset)
|
||||
{
|
||||
int32_t length = -1; // length of incoming line or -1 eof
|
||||
int32_t outLength = -1; // length of outgoing line or -1 eof
|
||||
bool eatThisLine = true;
|
||||
nsAutoCString nextLine;
|
||||
|
||||
while (eatThisLine) {
|
||||
// first, handle the filtering case...this is easy....
|
||||
if (m_Filtering)
|
||||
length = GetNextFilterLine(nextLine);
|
||||
else
|
||||
{
|
||||
// 3 cases: Offline IMAP, POP, or we are dealing with a news message....
|
||||
// Offline cases should be same as local mail cases, since we're going
|
||||
// to store offline messages in berkeley format folders.
|
||||
if (m_db)
|
||||
{
|
||||
length = GetNextLocalLine (nextLine); // (2) POP
|
||||
}
|
||||
}
|
||||
|
||||
if (length < 0)
|
||||
break; // eof in
|
||||
|
||||
outLength = ApplyTransformations(nextLine, length, eatThisLine, buf);
|
||||
}
|
||||
|
||||
if (outLength < 0)
|
||||
return -1; // eof out
|
||||
|
||||
// For non-multipart messages, the entire message minus headers is encoded
|
||||
// ApplyTransformations can only decode a part
|
||||
if (!m_isMultipart && m_base64part)
|
||||
{
|
||||
Base64Decode(buf);
|
||||
m_base64part = false;
|
||||
// And reapply our transformations...
|
||||
outLength = ApplyTransformations(buf, buf.Length(), eatThisLine, buf);
|
||||
}
|
||||
|
||||
charset = m_partCharset;
|
||||
return outLength;
|
||||
}
|
||||
|
||||
void nsMsgBodyHandler::OpenLocalFolder()
|
||||
{
|
||||
nsCOMPtr <nsIInputStream> inputStream;
|
||||
nsresult rv = m_scope->GetInputStream(m_msgHdr, getter_AddRefs(inputStream));
|
||||
// Warn and return if GetInputStream fails
|
||||
NS_ENSURE_SUCCESS_VOID(rv);
|
||||
m_fileLineStream = do_QueryInterface(inputStream);
|
||||
}
|
||||
|
||||
int32_t nsMsgBodyHandler::GetNextFilterLine(nsCString &buf)
|
||||
{
|
||||
// m_nextHdr always points to the next header in the list....the list is NULL terminated...
|
||||
uint32_t numBytesCopied = 0;
|
||||
if (m_headersSize > 0)
|
||||
{
|
||||
// #mscott. Ugly hack! filter headers list have CRs & LFs inside the NULL delimited list of header
|
||||
// strings. It is possible to have: To NULL CR LF From. We want to skip over these CR/LFs if they start
|
||||
// at the beginning of what we think is another header.
|
||||
|
||||
while (m_headersSize > 0 && (m_headers[0] == '\r' || m_headers[0] == '\n' || m_headers[0] == ' ' || m_headers[0] == '\0'))
|
||||
{
|
||||
m_headers++; // skip over these chars...
|
||||
m_headersSize--;
|
||||
}
|
||||
|
||||
if (m_headersSize > 0)
|
||||
{
|
||||
numBytesCopied = strlen(m_headers) + 1 ;
|
||||
buf.Assign(m_headers);
|
||||
m_headers += numBytesCopied;
|
||||
// be careful...m_headersSize is unsigned. Don't let it go negative or we overflow to 2^32....*yikes*
|
||||
if (m_headersSize < numBytesCopied)
|
||||
m_headersSize = 0;
|
||||
else
|
||||
m_headersSize -= numBytesCopied; // update # bytes we have read from the headers list
|
||||
|
||||
return (int32_t) numBytesCopied;
|
||||
}
|
||||
}
|
||||
else if (m_headersSize == 0) {
|
||||
buf.Truncate();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// return -1 if no more local lines, length of next line otherwise.
|
||||
|
||||
int32_t nsMsgBodyHandler::GetNextLocalLine(nsCString &buf)
|
||||
// returns number of bytes copied
|
||||
{
|
||||
if (m_numLocalLines)
|
||||
{
|
||||
// I the line count is in body lines, only decrement once we have
|
||||
// processed all the headers. Otherwise the line is not in body
|
||||
// lines and we want to decrement for every line.
|
||||
if (m_pastMsgHeaders || !m_lineCountInBodyLines)
|
||||
m_numLocalLines--;
|
||||
// do we need to check the return value here?
|
||||
if (m_fileLineStream)
|
||||
{
|
||||
bool more = false;
|
||||
nsresult rv = m_fileLineStream->ReadLine(buf, &more);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
return buf.Length();
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method applies a sequence of transformations to the line.
|
||||
*
|
||||
* It applies the following sequences in order
|
||||
* * Removes headers if the searcher doesn't want them
|
||||
* (sets m_past*Headers)
|
||||
* * Determines the current MIME type.
|
||||
* (via SniffPossibleMIMEHeader)
|
||||
* * Strips any HTML if the searcher doesn't want it
|
||||
* * Strips non-text parts
|
||||
* * Decodes any base64 part
|
||||
* (resetting part variables: m_base64part, m_pastPartHeaders, m_partIsHtml,
|
||||
* m_partIsText)
|
||||
*
|
||||
* @param line (in) the current line
|
||||
* @param length (in) the length of said line
|
||||
* @param eatThisLine (out) whether or not to ignore this line
|
||||
* @param buf (inout) if m_base64part, the current part as needed for
|
||||
* decoding; else, it is treated as an out param (a
|
||||
* redundant version of line).
|
||||
* @return the length of the line after applying transformations
|
||||
*/
|
||||
int32_t nsMsgBodyHandler::ApplyTransformations (const nsCString &line, int32_t length,
|
||||
bool &eatThisLine, nsCString &buf)
|
||||
{
|
||||
eatThisLine = false;
|
||||
|
||||
if (!m_pastPartHeaders) // line is a line from the part headers
|
||||
{
|
||||
if (m_stripHeaders)
|
||||
eatThisLine = true;
|
||||
|
||||
// We have already grabbed all worthwhile information from the headers,
|
||||
// so there is no need to keep track of the current lines
|
||||
buf.Assign(line);
|
||||
|
||||
SniffPossibleMIMEHeader(buf);
|
||||
|
||||
if (buf.IsEmpty() || buf.First() == '\r' || buf.First() == '\n') {
|
||||
if (!m_inMessageAttachment) {
|
||||
m_pastPartHeaders = true;
|
||||
} else {
|
||||
// We're in a message attachment and have just read past the
|
||||
// part header for the attached message. We now need to read
|
||||
// the message headers and any part headers.
|
||||
// We can now forget about the special handling of attached messages.
|
||||
m_inMessageAttachment = false;
|
||||
}
|
||||
}
|
||||
|
||||
// We set m_pastMsgHeaders to 'true' only once.
|
||||
if (m_pastPartHeaders)
|
||||
m_pastMsgHeaders = true;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// Check to see if this is one of our boundary strings.
|
||||
bool matchedBoundary = false;
|
||||
if (m_isMultipart && m_boundaries.Length() > 0) {
|
||||
for (int32_t i = (int32_t)m_boundaries.Length() - 1; i >= 0; i--) {
|
||||
if (StringBeginsWith(line, m_boundaries[i])) {
|
||||
matchedBoundary = true;
|
||||
// If we matched a boundary, we won't need the nested/later ones any more.
|
||||
m_boundaries.SetLength(i+1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (matchedBoundary)
|
||||
{
|
||||
if (m_base64part && m_partIsText)
|
||||
{
|
||||
Base64Decode(buf);
|
||||
// Work on the parsed string
|
||||
if (!buf.Length())
|
||||
{
|
||||
NS_WARNING("Trying to transform an empty buffer");
|
||||
eatThisLine = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// It is wrong to call ApplyTransformations() here since this will
|
||||
// lead to the buffer being doubled-up at |buf.Append(line.get());| below.
|
||||
// ApplyTransformations(buf, buf.Length(), eatThisLine, buf);
|
||||
// Avoid spurious failures
|
||||
eatThisLine = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buf.Truncate();
|
||||
eatThisLine = true; // We have no content...
|
||||
}
|
||||
|
||||
// Reset all assumed headers
|
||||
m_base64part = false;
|
||||
// Get ready to sniff new part headers, but do not reset m_pastMsgHeaders
|
||||
// since it will screw the body line count.
|
||||
m_pastPartHeaders = false;
|
||||
m_partIsHtml = false;
|
||||
// If we ever see a multipart message, each part needs to set 'm_partIsText',
|
||||
// so no more defaulting to 'true' when the part is done.
|
||||
m_partIsText = false;
|
||||
|
||||
return buf.Length();
|
||||
}
|
||||
|
||||
if (!m_partIsText)
|
||||
{
|
||||
// Ignore non-text parts
|
||||
buf.Truncate();
|
||||
eatThisLine = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (m_base64part)
|
||||
{
|
||||
// We need to keep track of all lines to parse base64encoded...
|
||||
buf.Append(line.get());
|
||||
eatThisLine = true;
|
||||
return buf.Length();
|
||||
}
|
||||
|
||||
// ... but there's no point if we're not parsing base64.
|
||||
buf.Assign(line);
|
||||
if (m_stripHtml && m_partIsHtml)
|
||||
{
|
||||
StripHtml (buf);
|
||||
}
|
||||
|
||||
return buf.Length();
|
||||
}
|
||||
|
||||
void nsMsgBodyHandler::StripHtml (nsCString &pBufInOut)
|
||||
{
|
||||
char *pBuf = (char*) PR_Malloc (pBufInOut.Length() + 1);
|
||||
if (pBuf)
|
||||
{
|
||||
char *pWalk = pBuf;
|
||||
|
||||
char *pWalkInOut = (char *) pBufInOut.get();
|
||||
bool inTag = false;
|
||||
while (*pWalkInOut) // throw away everything inside < >
|
||||
{
|
||||
if (!inTag)
|
||||
if (*pWalkInOut == '<')
|
||||
inTag = true;
|
||||
else
|
||||
*pWalk++ = *pWalkInOut;
|
||||
else
|
||||
if (*pWalkInOut == '>')
|
||||
inTag = false;
|
||||
pWalkInOut++;
|
||||
}
|
||||
*pWalk = 0; // null terminator
|
||||
|
||||
pBufInOut.Adopt(pBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the MIME type, if present, from the current line.
|
||||
*
|
||||
* m_partIsHtml, m_isMultipart, m_partIsText, m_base64part, and boundary are
|
||||
* all set by this method at various points in time.
|
||||
*
|
||||
* @param line (in) a header line that may contain a MIME header
|
||||
*/
|
||||
void nsMsgBodyHandler::SniffPossibleMIMEHeader(const nsCString &line)
|
||||
{
|
||||
// Some parts of MIME are case-sensitive and other parts are case-insensitive;
|
||||
// specifically, the headers are all case-insensitive and the values we care
|
||||
// about are also case-insensitive, with the sole exception of the boundary
|
||||
// string, so we can't just take the input line and make it lower case.
|
||||
nsCString lowerCaseLine(line);
|
||||
ToLowerCase(lowerCaseLine);
|
||||
|
||||
if (StringBeginsWith(lowerCaseLine, NS_LITERAL_CSTRING("content-type:")))
|
||||
{
|
||||
if (lowerCaseLine.Find("text/html", CaseInsensitiveCompare) != -1)
|
||||
{
|
||||
m_partIsText = true;
|
||||
m_partIsHtml = true;
|
||||
}
|
||||
else if (lowerCaseLine.Find("multipart/", CaseInsensitiveCompare) != -1)
|
||||
{
|
||||
if (m_isMultipart)
|
||||
{
|
||||
// Nested multipart, get ready for new headers.
|
||||
m_base64part = false;
|
||||
m_pastPartHeaders = false;
|
||||
m_partIsHtml = false;
|
||||
m_partIsText = false;
|
||||
}
|
||||
m_isMultipart = true;
|
||||
m_partCharset.Truncate();
|
||||
}
|
||||
else if (lowerCaseLine.Find("message/", CaseInsensitiveCompare) != -1)
|
||||
{
|
||||
// Initialise again.
|
||||
m_base64part = false;
|
||||
m_pastPartHeaders = false;
|
||||
m_partIsHtml = false;
|
||||
m_partIsText = true; // Default is text/plain, maybe proven otherwise later.
|
||||
m_inMessageAttachment = true;
|
||||
}
|
||||
else if (lowerCaseLine.Find("text/", CaseInsensitiveCompare) != -1)
|
||||
m_partIsText = true;
|
||||
else if (lowerCaseLine.Find("text/", CaseInsensitiveCompare) == -1)
|
||||
m_partIsText = false; // We have disproven our assumption.
|
||||
}
|
||||
|
||||
int32_t start;
|
||||
if (m_isMultipart &&
|
||||
(start = lowerCaseLine.Find("boundary=", CaseInsensitiveCompare)) != -1)
|
||||
{
|
||||
start += 9; // strlen("boundary=")
|
||||
if (line[start] == '\"')
|
||||
start++;
|
||||
int32_t end = line.RFindChar('\"');
|
||||
if (end == -1)
|
||||
end = line.Length();
|
||||
|
||||
// Collect all boundaries. Since we only react to crossing a boundary,
|
||||
// we can simply collect the boundaries instead of forming a tree
|
||||
// structure from the message. Keep it simple ;-)
|
||||
nsCString boundary;
|
||||
boundary.Assign("--");
|
||||
boundary.Append(Substring(line, start, end-start));
|
||||
if (!m_boundaries.Contains(boundary))
|
||||
m_boundaries.AppendElement(boundary);
|
||||
}
|
||||
|
||||
if (m_isMultipart &&
|
||||
(start = lowerCaseLine.Find("charset=", CaseInsensitiveCompare)) != -1)
|
||||
{
|
||||
start += 8; // strlen("charset=")
|
||||
bool foundQuote = false;
|
||||
if (line[start] == '\"') {
|
||||
start++;
|
||||
foundQuote = true;
|
||||
}
|
||||
int32_t end = line.FindChar(foundQuote ? '\"' : ';', start);
|
||||
if (end == -1)
|
||||
end = line.Length();
|
||||
|
||||
m_partCharset.Assign(Substring(line, start, end-start));
|
||||
}
|
||||
|
||||
if (StringBeginsWith(lowerCaseLine,
|
||||
NS_LITERAL_CSTRING("content-transfer-encoding:")) &&
|
||||
lowerCaseLine.Find(ENCODING_BASE64, CaseInsensitiveCompare) != kNotFound)
|
||||
m_base64part = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the given base64 string.
|
||||
*
|
||||
* It returns its decoded string in its input.
|
||||
*
|
||||
* @param pBufInOut (inout) a buffer of the string
|
||||
*/
|
||||
void nsMsgBodyHandler::Base64Decode (nsCString &pBufInOut)
|
||||
{
|
||||
char *decodedBody = PL_Base64Decode(pBufInOut.get(), pBufInOut.Length(), nullptr);
|
||||
if (decodedBody)
|
||||
pBufInOut.Adopt(decodedBody);
|
||||
|
||||
int32_t offset = pBufInOut.FindChar('\n');
|
||||
while (offset != -1) {
|
||||
pBufInOut.Replace(offset, 1, ' ');
|
||||
offset = pBufInOut.FindChar('\n', offset);
|
||||
}
|
||||
offset = pBufInOut.FindChar('\r');
|
||||
while (offset != -1) {
|
||||
pBufInOut.Replace(offset, 1, ' ');
|
||||
offset = pBufInOut.FindChar('\r', offset);
|
||||
}
|
||||
}
|
||||
|
||||
1057
mailnews/base/search/src/nsMsgFilter.cpp
Normal file
1057
mailnews/base/search/src/nsMsgFilter.cpp
Normal file
File diff suppressed because it is too large
Load diff
103
mailnews/base/search/src/nsMsgFilter.h
Normal file
103
mailnews/base/search/src/nsMsgFilter.h
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef _nsMsgFilter_H_
|
||||
#define _nsMsgFilter_H_
|
||||
|
||||
#include "nscore.h"
|
||||
#include "nsISupports.h"
|
||||
#include "nsIMsgFilter.h"
|
||||
#include "nsIMsgSearchScopeTerm.h"
|
||||
#include "nsMsgSearchBoolExpression.h"
|
||||
#include "nsIDateTimeFormat.h"
|
||||
#include "nsIMsgFilterCustomAction.h"
|
||||
|
||||
class nsMsgRuleAction : public nsIMsgRuleAction
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
nsMsgRuleAction();
|
||||
|
||||
NS_DECL_NSIMSGRULEACTION
|
||||
|
||||
private:
|
||||
virtual ~nsMsgRuleAction();
|
||||
|
||||
nsMsgRuleActionType m_type;
|
||||
// this used to be a union - why bother?
|
||||
nsMsgPriorityValue m_priority; /* priority to set rule to */
|
||||
nsMsgLabelValue m_label; /* label to set rule to */
|
||||
nsCString m_folderUri;
|
||||
int32_t m_junkScore; /* junk score (or arbitrary int value?) */
|
||||
// arbitrary string value. Currently, email address to forward to
|
||||
nsCString m_strValue;
|
||||
nsCString m_customId;
|
||||
nsCOMPtr<nsIMsgFilterCustomAction> m_customAction;
|
||||
} ;
|
||||
|
||||
|
||||
class nsMsgFilter : public nsIMsgFilter
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
nsMsgFilter();
|
||||
|
||||
NS_DECL_NSIMSGFILTER
|
||||
|
||||
nsMsgFilterTypeType GetType() {return m_type;}
|
||||
void SetType(nsMsgFilterTypeType type) {m_type = type;}
|
||||
bool GetEnabled() {return m_enabled;}
|
||||
void SetFilterScript(nsCString *filterName);
|
||||
|
||||
bool IsScript() {return (m_type &
|
||||
(nsMsgFilterType::InboxJavaScript |
|
||||
nsMsgFilterType::NewsJavaScript)) != 0;}
|
||||
|
||||
// filing routines.
|
||||
nsresult SaveRule(nsIOutputStream *aStream);
|
||||
|
||||
int16_t GetVersion();
|
||||
#ifdef DEBUG
|
||||
void Dump();
|
||||
#endif
|
||||
|
||||
nsresult ConvertMoveOrCopyToFolderValue(nsIMsgRuleAction *filterAction, nsCString &relativePath);
|
||||
static const char *GetActionStr(nsMsgRuleActionType action);
|
||||
static nsresult GetActionFilingStr(nsMsgRuleActionType action, nsCString &actionStr);
|
||||
static nsMsgRuleActionType GetActionForFilingStr(nsCString &actionStr);
|
||||
protected:
|
||||
|
||||
/*
|
||||
* Reporting function for filtering success/failure.
|
||||
* Logging has to be enabled for the message to appear.
|
||||
*/
|
||||
nsresult LogRuleHitGeneric(nsIMsgRuleAction *aFilterAction,
|
||||
nsIMsgDBHdr *aMsgHdr,
|
||||
nsresult aRcode,
|
||||
const char *aErrmsg);
|
||||
|
||||
virtual ~nsMsgFilter();
|
||||
|
||||
nsMsgFilterTypeType m_type;
|
||||
nsString m_filterName;
|
||||
nsCString m_scriptFileName; // iff this filter is a script.
|
||||
nsCString m_description;
|
||||
nsCString m_unparsedBuffer;
|
||||
|
||||
bool m_enabled;
|
||||
bool m_temporary;
|
||||
bool m_unparseable;
|
||||
nsIMsgFilterList *m_filterList; /* owning filter list */
|
||||
nsCOMPtr<nsISupportsArray> m_termList; /* linked list of criteria terms */
|
||||
nsCOMPtr<nsIMsgSearchScopeTerm> m_scope; /* default for mail rules is inbox, but news rules could
|
||||
have a newsgroup - LDAP would be invalid */
|
||||
nsTArray<nsCOMPtr<nsIMsgRuleAction> > m_actionList;
|
||||
nsMsgSearchBoolExpression *m_expressionTree;
|
||||
nsCOMPtr<nsIDateTimeFormat> mDateFormatter;
|
||||
};
|
||||
|
||||
#endif
|
||||
1198
mailnews/base/search/src/nsMsgFilterList.cpp
Normal file
1198
mailnews/base/search/src/nsMsgFilterList.cpp
Normal file
File diff suppressed because it is too large
Load diff
75
mailnews/base/search/src/nsMsgFilterList.h
Normal file
75
mailnews/base/search/src/nsMsgFilterList.h
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef _nsMsgFilterList_H_
|
||||
#define _nsMsgFilterList_H_
|
||||
|
||||
#include "nscore.h"
|
||||
#include "nsIMsgFolder.h"
|
||||
#include "nsIMsgFilterList.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsIOutputStream.h"
|
||||
|
||||
const int16_t kFileVersion = 9;
|
||||
const int16_t kManualContextVersion = 9;
|
||||
const int16_t k60Beta1Version = 7;
|
||||
const int16_t k45Version = 6;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// The Msg Filter List is an interface designed to make accessing filter lists
|
||||
// easier. Clients typically open a filter list and either enumerate the filters,
|
||||
// or add new filters, or change the order around...
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class nsIMsgFilter;
|
||||
class nsMsgFilter;
|
||||
|
||||
class nsMsgFilterList : public nsIMsgFilterList
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIMSGFILTERLIST
|
||||
|
||||
nsMsgFilterList();
|
||||
|
||||
nsresult Close();
|
||||
nsresult LoadTextFilters(nsIInputStream *aStream);
|
||||
|
||||
bool m_temporaryList;
|
||||
|
||||
protected:
|
||||
virtual ~nsMsgFilterList();
|
||||
|
||||
nsresult ComputeArbitraryHeaders();
|
||||
nsresult SaveTextFilters(nsIOutputStream *aStream);
|
||||
// file streaming methods
|
||||
int ReadChar(nsIInputStream *aStream);
|
||||
int SkipWhitespace(nsIInputStream *aStream);
|
||||
bool StrToBool(nsCString &str);
|
||||
int LoadAttrib(nsMsgFilterFileAttribValue &attrib, nsIInputStream *aStream);
|
||||
const char *GetStringForAttrib(nsMsgFilterFileAttribValue attrib);
|
||||
nsresult LoadValue(nsCString &value, nsIInputStream *aStream);
|
||||
int16_t m_fileVersion;
|
||||
bool m_loggingEnabled;
|
||||
bool m_startWritingToBuffer; //tells us when to start writing one whole filter to m_unparsedBuffer
|
||||
nsCOMPtr<nsIMsgFolder> m_folder;
|
||||
nsMsgFilter *m_curFilter; // filter we're filing in or out(?)
|
||||
nsCString m_filterFileName;
|
||||
nsTArray<nsCOMPtr<nsIMsgFilter> > m_filters;
|
||||
nsCString m_arbitraryHeaders;
|
||||
nsCOMPtr<nsIFile> m_defaultFile;
|
||||
nsCString m_unparsedFilterBuffer; //holds one entire filter unparsed
|
||||
|
||||
private:
|
||||
nsresult TruncateLog();
|
||||
nsresult GetLogFile(nsIFile **aFile);
|
||||
nsresult EnsureLogFile(nsIFile *file);
|
||||
nsCOMPtr<nsIOutputStream> m_logStream;
|
||||
};
|
||||
|
||||
#endif
|
||||
1216
mailnews/base/search/src/nsMsgFilterService.cpp
Normal file
1216
mailnews/base/search/src/nsMsgFilterService.cpp
Normal file
File diff suppressed because it is too large
Load diff
46
mailnews/base/search/src/nsMsgFilterService.h
Normal file
46
mailnews/base/search/src/nsMsgFilterService.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef _nsMsgFilterService_H_
|
||||
#define _nsMsgFilterService_H_
|
||||
|
||||
#include "nsIMsgFilterService.h"
|
||||
#include "nsCOMArray.h"
|
||||
|
||||
class nsIMsgWindow;
|
||||
class nsIStringBundle;
|
||||
|
||||
|
||||
|
||||
// The filter service is used to acquire and manipulate filter lists.
|
||||
|
||||
class nsMsgFilterService : public nsIMsgFilterService
|
||||
{
|
||||
|
||||
public:
|
||||
nsMsgFilterService();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIMSGFILTERSERVICE
|
||||
/* clients call OpenFilterList to get a handle to a FilterList, of existing nsMsgFilter *.
|
||||
These are manipulated by the front end as a result of user interaction
|
||||
with dialog boxes. To apply the new list call MSG_CloseFilterList.
|
||||
*/
|
||||
nsresult BackUpFilterFile(nsIFile *aFilterFile, nsIMsgWindow *aMsgWindow);
|
||||
nsresult AlertBackingUpFilterFile(nsIMsgWindow *aMsgWindow);
|
||||
nsresult ThrowAlertMsg(const char*aMsgName, nsIMsgWindow *aMsgWindow);
|
||||
nsresult GetStringFromBundle(const char *aMsgName, char16_t **aResult);
|
||||
nsresult GetFilterStringBundle(nsIStringBundle **aBundle);
|
||||
|
||||
protected:
|
||||
virtual ~nsMsgFilterService();
|
||||
|
||||
nsCOMArray<nsIMsgFilterCustomAction> mCustomActions; // defined custom action list
|
||||
nsCOMArray<nsIMsgSearchCustomTerm> mCustomTerms; // defined custom term list
|
||||
|
||||
};
|
||||
|
||||
#endif // _nsMsgFilterService_H_
|
||||
|
||||
1004
mailnews/base/search/src/nsMsgImapSearch.cpp
Normal file
1004
mailnews/base/search/src/nsMsgImapSearch.cpp
Normal file
File diff suppressed because it is too large
Load diff
1022
mailnews/base/search/src/nsMsgLocalSearch.cpp
Normal file
1022
mailnews/base/search/src/nsMsgLocalSearch.cpp
Normal file
File diff suppressed because it is too large
Load diff
104
mailnews/base/search/src/nsMsgLocalSearch.h
Normal file
104
mailnews/base/search/src/nsMsgLocalSearch.h
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef _nsMsgLocalSearch_H
|
||||
#define _nsMsgLocalSearch_H
|
||||
|
||||
// inherit interface here
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsIMsgSearchAdapter.h"
|
||||
#include "nsIUrlListener.h"
|
||||
|
||||
// inherit base implementation
|
||||
#include "nsMsgSearchAdapter.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
|
||||
|
||||
class nsIMsgDBHdr;
|
||||
class nsIMsgSearchScopeTerm;
|
||||
class nsIMsgFolder;
|
||||
class nsMsgSearchBoolExpression;
|
||||
|
||||
class nsMsgSearchOfflineMail : public nsMsgSearchAdapter, public nsIUrlListener
|
||||
{
|
||||
public:
|
||||
nsMsgSearchOfflineMail (nsIMsgSearchScopeTerm*, nsISupportsArray *);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
NS_DECL_NSIURLLISTENER
|
||||
|
||||
NS_IMETHOD ValidateTerms () override;
|
||||
NS_IMETHOD Search (bool *aDone) override;
|
||||
NS_IMETHOD Abort () override;
|
||||
NS_IMETHOD AddResultElement (nsIMsgDBHdr *) override;
|
||||
|
||||
static nsresult MatchTermsForFilter(nsIMsgDBHdr * msgToMatch,
|
||||
nsISupportsArray *termList,
|
||||
const char *defaultCharset,
|
||||
nsIMsgSearchScopeTerm *scope,
|
||||
nsIMsgDatabase * db,
|
||||
const char * headers,
|
||||
uint32_t headerSize,
|
||||
nsMsgSearchBoolExpression ** aExpressionTree,
|
||||
bool *pResult);
|
||||
|
||||
static nsresult MatchTermsForSearch(nsIMsgDBHdr * msgTomatch,
|
||||
nsISupportsArray * termList,
|
||||
const char *defaultCharset,
|
||||
nsIMsgSearchScopeTerm *scope,
|
||||
nsIMsgDatabase *db,
|
||||
nsMsgSearchBoolExpression ** aExpressionTree,
|
||||
bool *pResult);
|
||||
|
||||
virtual nsresult OpenSummaryFile ();
|
||||
|
||||
static nsresult ProcessSearchTerm(nsIMsgDBHdr *msgToMatch,
|
||||
nsIMsgSearchTerm * aTerm,
|
||||
const char *defaultCharset,
|
||||
nsIMsgSearchScopeTerm * scope,
|
||||
nsIMsgDatabase * db,
|
||||
const char * headers,
|
||||
uint32_t headerSize,
|
||||
bool Filtering,
|
||||
bool *pResult);
|
||||
protected:
|
||||
virtual ~nsMsgSearchOfflineMail();
|
||||
static nsresult MatchTerms(nsIMsgDBHdr *msgToMatch,
|
||||
nsISupportsArray *termList,
|
||||
const char *defaultCharset,
|
||||
nsIMsgSearchScopeTerm *scope,
|
||||
nsIMsgDatabase * db,
|
||||
const char * headers,
|
||||
uint32_t headerSize,
|
||||
bool ForFilters,
|
||||
nsMsgSearchBoolExpression ** aExpressionTree,
|
||||
bool *pResult);
|
||||
|
||||
static nsresult ConstructExpressionTree(nsISupportsArray * termList,
|
||||
uint32_t termCount,
|
||||
uint32_t &aStartPosInList,
|
||||
nsMsgSearchBoolExpression ** aExpressionTree);
|
||||
|
||||
nsCOMPtr <nsIMsgDatabase> m_db;
|
||||
nsCOMPtr<nsISimpleEnumerator> m_listContext;
|
||||
void CleanUpScope();
|
||||
};
|
||||
|
||||
|
||||
class nsMsgSearchOfflineNews : public nsMsgSearchOfflineMail
|
||||
{
|
||||
public:
|
||||
nsMsgSearchOfflineNews (nsIMsgSearchScopeTerm*, nsISupportsArray *);
|
||||
virtual ~nsMsgSearchOfflineNews ();
|
||||
NS_IMETHOD ValidateTerms () override;
|
||||
|
||||
virtual nsresult OpenSummaryFile () override;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
1332
mailnews/base/search/src/nsMsgSearchAdapter.cpp
Normal file
1332
mailnews/base/search/src/nsMsgSearchAdapter.cpp
Normal file
File diff suppressed because it is too large
Load diff
37
mailnews/base/search/src/nsMsgSearchImap.h
Normal file
37
mailnews/base/search/src/nsMsgSearchImap.h
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef _nsMsgSearchImap_h__
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsMsgSearchAdapter.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//---------- Adapter class for searching online (IMAP) folders ----------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsMsgSearchOnlineMail : public nsMsgSearchAdapter
|
||||
{
|
||||
public:
|
||||
nsMsgSearchOnlineMail (nsMsgSearchScopeTerm *scope, nsISupportsArray *termList);
|
||||
virtual ~nsMsgSearchOnlineMail ();
|
||||
|
||||
NS_IMETHOD ValidateTerms () override;
|
||||
NS_IMETHOD Search (bool *aDone) override;
|
||||
NS_IMETHOD GetEncoding (char **result) override;
|
||||
NS_IMETHOD AddResultElement (nsIMsgDBHdr *) override;
|
||||
|
||||
static nsresult Encode (nsCString& ppEncoding,
|
||||
nsISupportsArray *searchTerms,
|
||||
const char16_t *destCharset);
|
||||
|
||||
|
||||
protected:
|
||||
nsCString m_encoding;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
511
mailnews/base/search/src/nsMsgSearchNews.cpp
Normal file
511
mailnews/base/search/src/nsMsgSearchNews.cpp
Normal file
|
|
@ -0,0 +1,511 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
#include "msgCore.h"
|
||||
#include "nsMsgSearchAdapter.h"
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "nsMsgSearchScopeTerm.h"
|
||||
#include "nsMsgResultElement.h"
|
||||
#include "nsMsgSearchTerm.h"
|
||||
#include "nsIMsgHdr.h"
|
||||
#include "nsMsgSearchNews.h"
|
||||
#include "nsIDBFolderInfo.h"
|
||||
#include "prprf.h"
|
||||
#include "nsIMsgDatabase.h"
|
||||
#include "nsMemory.h"
|
||||
#include <ctype.h>
|
||||
// Disable deprecation warnings generated by nsISupportsArray and associated
|
||||
// classes.
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning (disable : 4996)
|
||||
#endif
|
||||
#include "nsISupportsArray.h"
|
||||
|
||||
// Implementation of search for IMAP mail folders
|
||||
|
||||
|
||||
// Implementation of search for newsgroups
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//----------- Adapter class for searching XPAT-capable news servers -----------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
const char *nsMsgSearchNews::m_kNntpFrom = "FROM ";
|
||||
const char *nsMsgSearchNews::m_kNntpSubject = "SUBJECT ";
|
||||
const char *nsMsgSearchNews::m_kTermSeparator = "/";
|
||||
|
||||
|
||||
nsMsgSearchNews::nsMsgSearchNews (nsMsgSearchScopeTerm *scope, nsISupportsArray *termList) : nsMsgSearchAdapter (scope, termList)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
nsMsgSearchNews::~nsMsgSearchNews ()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
nsresult nsMsgSearchNews::ValidateTerms ()
|
||||
{
|
||||
nsresult err = nsMsgSearchAdapter::ValidateTerms ();
|
||||
if (NS_OK == err)
|
||||
{
|
||||
err = Encode (&m_encoding);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
nsresult nsMsgSearchNews::Search (bool *aDone)
|
||||
{
|
||||
// the state machine runs in the news: handler
|
||||
nsresult err = NS_ERROR_NOT_IMPLEMENTED;
|
||||
return err;
|
||||
}
|
||||
|
||||
char16_t *nsMsgSearchNews::EncodeToWildmat (const char16_t *value)
|
||||
{
|
||||
// Here we take advantage of XPAT's use of the wildmat format, which allows
|
||||
// a case-insensitive match by specifying each case possibility for each character
|
||||
// So, "FooBar" is encoded as "[Ff][Oo][Bb][Aa][Rr]"
|
||||
|
||||
char16_t *caseInsensitiveValue = (char16_t*) moz_xmalloc(sizeof(char16_t) * ((4 * NS_strlen(value)) + 1));
|
||||
if (caseInsensitiveValue)
|
||||
{
|
||||
char16_t *walkValue = caseInsensitiveValue;
|
||||
while (*value)
|
||||
{
|
||||
if (isalpha(*value))
|
||||
{
|
||||
*walkValue++ = (char16_t)'[';
|
||||
*walkValue++ = ToUpperCase((char16_t)*value);
|
||||
*walkValue++ = ToLowerCase((char16_t)*value);
|
||||
*walkValue++ = (char16_t)']';
|
||||
}
|
||||
else
|
||||
*walkValue++ = *value;
|
||||
value++;
|
||||
}
|
||||
*walkValue = 0;
|
||||
}
|
||||
return caseInsensitiveValue;
|
||||
}
|
||||
|
||||
|
||||
char *nsMsgSearchNews::EncodeTerm (nsIMsgSearchTerm *term)
|
||||
{
|
||||
// Develop an XPAT-style encoding for the search term
|
||||
|
||||
NS_ASSERTION(term, "null term");
|
||||
if (!term)
|
||||
return nullptr;
|
||||
|
||||
// Find a string to represent the attribute
|
||||
const char *attribEncoding = nullptr;
|
||||
nsMsgSearchAttribValue attrib;
|
||||
|
||||
term->GetAttrib(&attrib);
|
||||
|
||||
switch (attrib)
|
||||
{
|
||||
case nsMsgSearchAttrib::Sender:
|
||||
attribEncoding = m_kNntpFrom;
|
||||
break;
|
||||
case nsMsgSearchAttrib::Subject:
|
||||
attribEncoding = m_kNntpSubject;
|
||||
break;
|
||||
default:
|
||||
nsCString header;
|
||||
term->GetArbitraryHeader(header);
|
||||
if (header.IsEmpty())
|
||||
{
|
||||
NS_ASSERTION(false,"malformed search"); // malformed search term?
|
||||
return nullptr;
|
||||
}
|
||||
attribEncoding = header.get();
|
||||
}
|
||||
|
||||
// Build a string to represent the string pattern
|
||||
bool leadingStar = false;
|
||||
bool trailingStar = false;
|
||||
nsMsgSearchOpValue op;
|
||||
term->GetOp(&op);
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case nsMsgSearchOp::Contains:
|
||||
leadingStar = true;
|
||||
trailingStar = true;
|
||||
break;
|
||||
case nsMsgSearchOp::Is:
|
||||
break;
|
||||
case nsMsgSearchOp::BeginsWith:
|
||||
trailingStar = true;
|
||||
break;
|
||||
case nsMsgSearchOp::EndsWith:
|
||||
leadingStar = true;
|
||||
break;
|
||||
default:
|
||||
NS_ASSERTION(false,"malformed search"); // malformed search term?
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// ### i18N problem Get the csid from FE, which is the correct csid for term
|
||||
// int16 wincsid = INTL_GetCharSetID(INTL_DefaultTextWidgetCsidSel);
|
||||
|
||||
// Do INTL_FormatNNTPXPATInRFC1522Format trick for non-ASCII string
|
||||
// unsigned char *intlNonRFC1522Value = INTL_FormatNNTPXPATInNonRFC1522Format (wincsid, (unsigned char*)term->m_value.u.string);
|
||||
nsCOMPtr <nsIMsgSearchValue> searchValue;
|
||||
|
||||
nsresult rv = term->GetValue(getter_AddRefs(searchValue));
|
||||
if (NS_FAILED(rv) || !searchValue)
|
||||
return nullptr;
|
||||
|
||||
|
||||
nsString intlNonRFC1522Value;
|
||||
rv = searchValue->GetStr(intlNonRFC1522Value);
|
||||
if (NS_FAILED(rv) || intlNonRFC1522Value.IsEmpty())
|
||||
return nullptr;
|
||||
|
||||
char16_t *caseInsensitiveValue = EncodeToWildmat (intlNonRFC1522Value.get());
|
||||
if (!caseInsensitiveValue)
|
||||
return nullptr;
|
||||
|
||||
// TO DO: Do INTL_FormatNNTPXPATInRFC1522Format trick for non-ASCII string
|
||||
// Unfortunately, we currently do not handle xxx or xxx search in XPAT
|
||||
// Need to add the INTL_FormatNNTPXPATInRFC1522Format call after we can do that
|
||||
// so we should search a string in either RFC1522 format and non-RFC1522 format
|
||||
|
||||
char16_t *escapedValue = EscapeSearchUrl (caseInsensitiveValue);
|
||||
free(caseInsensitiveValue);
|
||||
if (!escapedValue)
|
||||
return nullptr;
|
||||
|
||||
nsAutoCString pattern;
|
||||
|
||||
if (leadingStar)
|
||||
pattern.Append('*');
|
||||
pattern.Append(NS_ConvertUTF16toUTF8(escapedValue));
|
||||
if (trailingStar)
|
||||
pattern.Append('*');
|
||||
|
||||
// Combine the XPAT command syntax with the attribute and the pattern to
|
||||
// form the term encoding
|
||||
const char xpatTemplate[] = "XPAT %s 1- %s";
|
||||
int termLength = (sizeof(xpatTemplate) - 1) + strlen(attribEncoding) + pattern.Length() + 1;
|
||||
char *termEncoding = new char [termLength];
|
||||
if (termEncoding)
|
||||
PR_snprintf (termEncoding, termLength, xpatTemplate, attribEncoding, pattern.get());
|
||||
|
||||
return termEncoding;
|
||||
}
|
||||
|
||||
nsresult nsMsgSearchNews::GetEncoding(char **result)
|
||||
{
|
||||
NS_ENSURE_ARG(result);
|
||||
*result = ToNewCString(m_encoding);
|
||||
return (*result) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
nsresult nsMsgSearchNews::Encode (nsCString *outEncoding)
|
||||
{
|
||||
NS_ASSERTION(outEncoding, "no out encoding");
|
||||
if (!outEncoding)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
nsresult err = NS_OK;
|
||||
|
||||
uint32_t numTerms;
|
||||
|
||||
m_searchTerms->Count(&numTerms);
|
||||
char **intermediateEncodings = new char * [numTerms];
|
||||
if (intermediateEncodings)
|
||||
{
|
||||
// Build an XPAT command for each term
|
||||
int encodingLength = 0;
|
||||
uint32_t i;
|
||||
for (i = 0; i < numTerms; i++)
|
||||
{
|
||||
nsCOMPtr<nsIMsgSearchTerm> pTerm;
|
||||
m_searchTerms->QueryElementAt(i, NS_GET_IID(nsIMsgSearchTerm),
|
||||
(void **)getter_AddRefs(pTerm));
|
||||
// set boolean OR term if any of the search terms are an OR...this only works if we are using
|
||||
// homogeneous boolean operators.
|
||||
bool isBooleanOpAnd;
|
||||
pTerm->GetBooleanAnd(&isBooleanOpAnd);
|
||||
m_ORSearch = !isBooleanOpAnd;
|
||||
|
||||
intermediateEncodings[i] = EncodeTerm (pTerm);
|
||||
if (intermediateEncodings[i])
|
||||
encodingLength += strlen(intermediateEncodings[i]) + strlen(m_kTermSeparator);
|
||||
}
|
||||
encodingLength += strlen("?search");
|
||||
// Combine all the term encodings into one big encoding
|
||||
char *encoding = new char [encodingLength + 1];
|
||||
if (encoding)
|
||||
{
|
||||
PL_strcpy (encoding, "?search");
|
||||
|
||||
m_searchTerms->Count(&numTerms);
|
||||
|
||||
for (i = 0; i < numTerms; i++)
|
||||
{
|
||||
if (intermediateEncodings[i])
|
||||
{
|
||||
PL_strcat (encoding, m_kTermSeparator);
|
||||
PL_strcat (encoding, intermediateEncodings[i]);
|
||||
delete [] intermediateEncodings[i];
|
||||
}
|
||||
}
|
||||
*outEncoding = encoding;
|
||||
}
|
||||
else
|
||||
err = NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
else
|
||||
err = NS_ERROR_OUT_OF_MEMORY;
|
||||
delete [] intermediateEncodings;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgSearchNews::AddHit(nsMsgKey key)
|
||||
{
|
||||
m_candidateHits.AppendElement(key);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* void CurrentUrlDone (in nsresult exitCode); */
|
||||
NS_IMETHODIMP nsMsgSearchNews::CurrentUrlDone(nsresult exitCode)
|
||||
{
|
||||
CollateHits();
|
||||
ReportHits();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
#if 0 // need to switch this to a notify stop loading handler, I think.
|
||||
void nsMsgSearchNews::PreExitFunction (URL_Struct * /*url*/, int status, MWContext *context)
|
||||
{
|
||||
MSG_SearchFrame *frame = MSG_SearchFrame::FromContext (context);
|
||||
nsMsgSearchNews *adapter = (nsMsgSearchNews*) frame->GetRunningAdapter();
|
||||
adapter->CollateHits();
|
||||
adapter->ReportHits();
|
||||
|
||||
if (status == MK_INTERRUPTED)
|
||||
{
|
||||
adapter->Abort();
|
||||
frame->EndCylonMode();
|
||||
}
|
||||
else
|
||||
{
|
||||
frame->m_idxRunningScope++;
|
||||
if (frame->m_idxRunningScope >= frame->m_scopeList.Count())
|
||||
frame->EndCylonMode();
|
||||
}
|
||||
}
|
||||
#endif // 0
|
||||
|
||||
void nsMsgSearchNews::CollateHits()
|
||||
{
|
||||
// Since the XPAT commands are processed one at a time, the result set for the
|
||||
// entire query is the intersection of results for each XPAT command if an AND search,
|
||||
// otherwise we want the union of all the search hits (minus the duplicates of course).
|
||||
|
||||
uint32_t size = m_candidateHits.Length();
|
||||
if (!size)
|
||||
return;
|
||||
|
||||
// Sort the article numbers first, so it's easy to tell how many hits
|
||||
// on a given article we got
|
||||
m_candidateHits.Sort();
|
||||
|
||||
// For an OR search we only need to count the first occurrence of a candidate.
|
||||
uint32_t termCount = 1;
|
||||
if (!m_ORSearch)
|
||||
{
|
||||
// We have a traditional AND search which must be collated. In order to
|
||||
// get promoted into the hits list, a candidate article number must appear
|
||||
// in the results of each XPAT command. So if we fire 3 XPAT commands (one
|
||||
// per search term), the article number must appear 3 times. If it appears
|
||||
// fewer than 3 times, it matched some search terms, but not all.
|
||||
m_searchTerms->Count(&termCount);
|
||||
}
|
||||
uint32_t candidateCount = 0;
|
||||
uint32_t candidate = m_candidateHits[0];
|
||||
for (uint32_t index = 0; index < size; ++index)
|
||||
{
|
||||
uint32_t possibleCandidate = m_candidateHits[index];
|
||||
if (candidate == possibleCandidate)
|
||||
{
|
||||
++candidateCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
candidateCount = 1;
|
||||
candidate = possibleCandidate;
|
||||
}
|
||||
if (candidateCount == termCount)
|
||||
m_hits.AppendElement(candidate);
|
||||
}
|
||||
}
|
||||
|
||||
void nsMsgSearchNews::ReportHits ()
|
||||
{
|
||||
nsCOMPtr <nsIMsgDatabase> db;
|
||||
nsCOMPtr <nsIDBFolderInfo> folderInfo;
|
||||
nsCOMPtr <nsIMsgFolder> scopeFolder;
|
||||
|
||||
nsresult err = m_scope->GetFolder(getter_AddRefs(scopeFolder));
|
||||
if (NS_SUCCEEDED(err) && scopeFolder)
|
||||
{
|
||||
err = scopeFolder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
|
||||
}
|
||||
|
||||
if (db)
|
||||
{
|
||||
uint32_t size = m_hits.Length();
|
||||
for (uint32_t i = 0; i < size; ++i)
|
||||
{
|
||||
nsCOMPtr <nsIMsgDBHdr> header;
|
||||
|
||||
db->GetMsgHdrForKey(m_hits.ElementAt(i), getter_AddRefs(header));
|
||||
if (header)
|
||||
ReportHit(header, scopeFolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ### this should take an nsIMsgFolder instead of a string location.
|
||||
void nsMsgSearchNews::ReportHit (nsIMsgDBHdr *pHeaders, nsIMsgFolder *folder)
|
||||
{
|
||||
// this is totally filched from msg_SearchOfflineMail until I decide whether the
|
||||
// right thing is to get them from the db or from NNTP
|
||||
nsCOMPtr<nsIMsgSearchSession> session;
|
||||
nsCOMPtr<nsIMsgFolder> scopeFolder;
|
||||
m_scope->GetFolder(getter_AddRefs(scopeFolder));
|
||||
m_scope->GetSearchSession(getter_AddRefs(session));
|
||||
if (session)
|
||||
session->AddSearchHit (pHeaders, scopeFolder);
|
||||
}
|
||||
|
||||
nsresult nsMsgSearchValidityManager::InitNewsTable()
|
||||
{
|
||||
NS_ASSERTION (nullptr == m_newsTable,"don't call this twice!");
|
||||
nsresult rv = NewTable (getter_AddRefs(m_newsTable));
|
||||
|
||||
if (NS_SUCCEEDED(rv))
|
||||
{
|
||||
m_newsTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Contains, 1);
|
||||
m_newsTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Contains, 1);
|
||||
m_newsTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Is, 1);
|
||||
m_newsTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Is, 1);
|
||||
m_newsTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::BeginsWith, 1);
|
||||
m_newsTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::BeginsWith, 1);
|
||||
m_newsTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::EndsWith, 1);
|
||||
m_newsTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::EndsWith, 1);
|
||||
|
||||
m_newsTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Contains, 1);
|
||||
m_newsTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Contains, 1);
|
||||
m_newsTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Is, 1);
|
||||
m_newsTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Is, 1);
|
||||
m_newsTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::BeginsWith, 1);
|
||||
m_newsTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::BeginsWith, 1);
|
||||
m_newsTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::EndsWith, 1);
|
||||
m_newsTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::EndsWith, 1);
|
||||
|
||||
#if 0
|
||||
// Size should be handled after the fact...
|
||||
m_newsTable->SetAvailable (nsMsgSearchAttrib::Size, nsMsgSearchOp::IsGreaterThan, 1);
|
||||
m_newsTable->SetEnabled (nsMsgSearchAttrib::Size, nsMsgSearchOp::IsGreaterThan, 1);
|
||||
m_newsTable->SetAvailable (nsMsgSearchAttrib::Size, nsMsgSearchOp::IsLessThan, 1);
|
||||
m_newsTable->SetEnabled (nsMsgSearchAttrib::Size, nsMsgSearchOp::IsLessThan, 1);
|
||||
#endif
|
||||
|
||||
m_newsTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Contains, 1);
|
||||
m_newsTable->SetEnabled (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Contains, 1);
|
||||
m_newsTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Is, 1);
|
||||
m_newsTable->SetEnabled (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Is, 1);
|
||||
m_newsTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::BeginsWith, 1);
|
||||
m_newsTable->SetEnabled (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::BeginsWith, 1);
|
||||
m_newsTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::EndsWith, 1);
|
||||
m_newsTable->SetEnabled (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::EndsWith, 1);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult nsMsgSearchValidityManager::InitNewsFilterTable()
|
||||
{
|
||||
NS_ASSERTION (nullptr == m_newsFilterTable, "news filter table already initted");
|
||||
nsresult rv = NewTable (getter_AddRefs(m_newsFilterTable));
|
||||
|
||||
if (NS_SUCCEEDED(rv))
|
||||
{
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Contains, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Contains, 1);
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::DoesntContain, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::DoesntContain, 1);
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Is, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Is, 1);
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Isnt, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Isnt, 1);
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::BeginsWith, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::BeginsWith, 1);
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::EndsWith, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::EndsWith, 1);
|
||||
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::IsInAB, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::IsInAB, 1);
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::IsntInAB, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::IsntInAB, 1);
|
||||
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Contains, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Contains, 1);
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::DoesntContain, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::DoesntContain, 1);
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Is, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Is, 1);
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Isnt, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Isnt, 1);
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::BeginsWith, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::BeginsWith, 1);
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::EndsWith, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::EndsWith, 1);
|
||||
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Date, nsMsgSearchOp::IsBefore, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Date, nsMsgSearchOp::IsBefore, 1);
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Date, nsMsgSearchOp::IsAfter, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Date, nsMsgSearchOp::IsAfter, 1);
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Date, nsMsgSearchOp::Is, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Date, nsMsgSearchOp::Is, 1);
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Date, nsMsgSearchOp::Isnt, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Date, nsMsgSearchOp::Isnt, 1);
|
||||
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Size, nsMsgSearchOp::IsGreaterThan, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Size, nsMsgSearchOp::IsGreaterThan, 1);
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Size, nsMsgSearchOp::IsLessThan, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Size, nsMsgSearchOp::IsLessThan, 1);
|
||||
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Contains, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Contains, 1);
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::DoesntContain, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::DoesntContain, 1);
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Is, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Is, 1);
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Isnt, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Isnt, 1);
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::BeginsWith, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::BeginsWith, 1);
|
||||
m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::EndsWith, 1);
|
||||
m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::EndsWith, 1);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
49
mailnews/base/search/src/nsMsgSearchNews.h
Normal file
49
mailnews/base/search/src/nsMsgSearchNews.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef _nsMsgSearchNews_h__
|
||||
#include "nsMsgSearchAdapter.h"
|
||||
#include "MailNewsTypes.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//---------- Adapter class for searching online (news) folders ----------------
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class nsMsgSearchNews : public nsMsgSearchAdapter
|
||||
{
|
||||
public:
|
||||
nsMsgSearchNews (nsMsgSearchScopeTerm *scope, nsISupportsArray *termList);
|
||||
virtual ~nsMsgSearchNews ();
|
||||
|
||||
NS_IMETHOD ValidateTerms () override;
|
||||
NS_IMETHOD Search (bool *aDone) override;
|
||||
NS_IMETHOD GetEncoding (char **result) override;
|
||||
NS_IMETHOD AddHit(nsMsgKey key) override;
|
||||
NS_IMETHOD CurrentUrlDone(nsresult exitCode) override;
|
||||
|
||||
virtual nsresult Encode (nsCString *outEncoding);
|
||||
virtual char *EncodeTerm (nsIMsgSearchTerm *);
|
||||
char16_t *EncodeToWildmat (const char16_t *);
|
||||
|
||||
void ReportHits ();
|
||||
void CollateHits ();
|
||||
void ReportHit (nsIMsgDBHdr *pHeaders, nsIMsgFolder *folder);
|
||||
|
||||
protected:
|
||||
nsCString m_encoding;
|
||||
bool m_ORSearch; // set to true if any of the search terms contains an OR for a boolean operator.
|
||||
|
||||
nsTArray<nsMsgKey> m_candidateHits;
|
||||
nsTArray<nsMsgKey> m_hits;
|
||||
|
||||
static const char *m_kNntpFrom;
|
||||
static const char *m_kNntpSubject;
|
||||
static const char *m_kTermSeparator;
|
||||
static const char *m_kUrlPrefix;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
675
mailnews/base/search/src/nsMsgSearchSession.cpp
Normal file
675
mailnews/base/search/src/nsMsgSearchSession.cpp
Normal file
|
|
@ -0,0 +1,675 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "msgCore.h"
|
||||
#include "nsMsgSearchCore.h"
|
||||
#include "nsMsgSearchAdapter.h"
|
||||
#include "nsMsgSearchBoolExpression.h"
|
||||
#include "nsMsgSearchSession.h"
|
||||
#include "nsMsgResultElement.h"
|
||||
#include "nsMsgSearchTerm.h"
|
||||
#include "nsMsgSearchScopeTerm.h"
|
||||
#include "nsIMsgMessageService.h"
|
||||
#include "nsMsgUtils.h"
|
||||
#include "nsIMsgSearchNotify.h"
|
||||
#include "nsIMsgMailSession.h"
|
||||
#include "nsMsgBaseCID.h"
|
||||
#include "nsMsgFolderFlags.h"
|
||||
#include "nsMsgLocalSearch.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsMsgSearchSession, nsIMsgSearchSession, nsIUrlListener,
|
||||
nsISupportsWeakReference)
|
||||
|
||||
nsMsgSearchSession::nsMsgSearchSession()
|
||||
{
|
||||
m_sortAttribute = nsMsgSearchAttrib::Sender;
|
||||
m_idxRunningScope = 0;
|
||||
m_handlingError = false;
|
||||
m_expressionTree = nullptr;
|
||||
m_searchPaused = false;
|
||||
nsresult rv = NS_NewISupportsArray(getter_AddRefs(m_termList));
|
||||
if (NS_FAILED(rv))
|
||||
NS_ASSERTION(false, "Failed to allocate a nsISupportsArray for nsMsgFilter");
|
||||
}
|
||||
|
||||
nsMsgSearchSession::~nsMsgSearchSession()
|
||||
{
|
||||
InterruptSearch();
|
||||
delete m_expressionTree;
|
||||
DestroyScopeList();
|
||||
DestroyTermList();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMsgSearchSession::AddSearchTerm(nsMsgSearchAttribValue attrib,
|
||||
nsMsgSearchOpValue op,
|
||||
nsIMsgSearchValue *value,
|
||||
bool BooleanANDp,
|
||||
const char *customString)
|
||||
{
|
||||
// stupid gcc
|
||||
nsMsgSearchBooleanOperator boolOp;
|
||||
if (BooleanANDp)
|
||||
boolOp = (nsMsgSearchBooleanOperator)nsMsgSearchBooleanOp::BooleanAND;
|
||||
else
|
||||
boolOp = (nsMsgSearchBooleanOperator)nsMsgSearchBooleanOp::BooleanOR;
|
||||
nsMsgSearchTerm *pTerm = new nsMsgSearchTerm(attrib, op, value,
|
||||
boolOp, customString);
|
||||
NS_ENSURE_TRUE(pTerm, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
m_termList->AppendElement(pTerm);
|
||||
// force the expression tree to rebuild whenever we change the terms
|
||||
delete m_expressionTree;
|
||||
m_expressionTree = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMsgSearchSession::AppendTerm(nsIMsgSearchTerm *aTerm)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aTerm);
|
||||
NS_ENSURE_TRUE(m_termList, NS_ERROR_NOT_INITIALIZED);
|
||||
delete m_expressionTree;
|
||||
m_expressionTree = nullptr;
|
||||
return m_termList->AppendElement(aTerm);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMsgSearchSession::GetSearchTerms(nsISupportsArray **aResult)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aResult);
|
||||
*aResult = m_termList;
|
||||
NS_ADDREF(*aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMsgSearchSession::CreateTerm(nsIMsgSearchTerm **aResult)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aResult);
|
||||
nsMsgSearchTerm *term = new nsMsgSearchTerm;
|
||||
NS_ENSURE_TRUE(term, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
*aResult = static_cast<nsIMsgSearchTerm*>(term);
|
||||
NS_ADDREF(*aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgSearchSession::RegisterListener(nsIMsgSearchNotify *aListener,
|
||||
int32_t aNotifyFlags)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aListener);
|
||||
m_listenerList.AppendElement(aListener);
|
||||
m_listenerFlagList.AppendElement(aNotifyFlags);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgSearchSession::UnregisterListener(nsIMsgSearchNotify *aListener)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aListener);
|
||||
size_t listenerIndex = m_listenerList.IndexOf(aListener);
|
||||
if (listenerIndex != m_listenerList.NoIndex)
|
||||
{
|
||||
m_listenerList.RemoveElementAt(listenerIndex);
|
||||
m_listenerFlagList.RemoveElementAt(listenerIndex);
|
||||
|
||||
// Adjust our iterator if it is active.
|
||||
// Removal of something at a higher index than the iterator does not affect
|
||||
// it; we only care if the the index we were pointing at gets shifted down,
|
||||
// in which case we also want to shift down.
|
||||
if (m_iListener != -1 && (signed)listenerIndex <= m_iListener)
|
||||
m_iListener--;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgSearchSession::GetNumSearchTerms(uint32_t *aNumSearchTerms)
|
||||
{
|
||||
NS_ENSURE_ARG(aNumSearchTerms);
|
||||
return m_termList->Count(aNumSearchTerms);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMsgSearchSession::GetNthSearchTerm(int32_t whichTerm,
|
||||
nsMsgSearchAttribValue attrib,
|
||||
nsMsgSearchOpValue op,
|
||||
nsIMsgSearchValue *value)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgSearchSession::CountSearchScopes(int32_t *_retval)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(_retval);
|
||||
*_retval = m_scopeList.Length();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMsgSearchSession::GetNthSearchScope(int32_t which,
|
||||
nsMsgSearchScopeValue *scopeId,
|
||||
nsIMsgFolder **folder)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(scopeId);
|
||||
NS_ENSURE_ARG_POINTER(folder);
|
||||
|
||||
nsMsgSearchScopeTerm *scopeTerm = m_scopeList.SafeElementAt(which, nullptr);
|
||||
NS_ENSURE_ARG(scopeTerm);
|
||||
|
||||
*scopeId = scopeTerm->m_attribute;
|
||||
*folder = scopeTerm->m_folder;
|
||||
NS_IF_ADDREF(*folder);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMsgSearchSession::AddScopeTerm(nsMsgSearchScopeValue scope,
|
||||
nsIMsgFolder *folder)
|
||||
{
|
||||
if (scope != nsMsgSearchScope::allSearchableGroups)
|
||||
{
|
||||
NS_ASSERTION(folder, "need folder if not searching all groups");
|
||||
NS_ENSURE_TRUE(folder, NS_ERROR_NULL_POINTER);
|
||||
}
|
||||
|
||||
nsMsgSearchScopeTerm *pScopeTerm = new nsMsgSearchScopeTerm(this, scope, folder);
|
||||
NS_ENSURE_TRUE(pScopeTerm, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
m_scopeList.AppendElement(pScopeTerm);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMsgSearchSession::AddDirectoryScopeTerm(nsMsgSearchScopeValue scope)
|
||||
{
|
||||
nsMsgSearchScopeTerm *pScopeTerm = new nsMsgSearchScopeTerm(this, scope, nullptr);
|
||||
NS_ENSURE_TRUE(pScopeTerm, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
m_scopeList.AppendElement(pScopeTerm);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgSearchSession::ClearScopes()
|
||||
{
|
||||
DestroyScopeList();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMsgSearchSession::ScopeUsesCustomHeaders(nsMsgSearchScopeValue scope,
|
||||
void *selection,
|
||||
bool forFilters,
|
||||
bool *_retval)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMsgSearchSession::IsStringAttribute(nsMsgSearchAttribValue attrib,
|
||||
bool *_retval)
|
||||
{
|
||||
// Is this check needed?
|
||||
NS_ENSURE_ARG(_retval);
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMsgSearchSession::AddAllScopes(nsMsgSearchScopeValue attrib)
|
||||
{
|
||||
// don't think this is needed.
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgSearchSession::Search(nsIMsgWindow *aWindow)
|
||||
{
|
||||
nsresult rv = Initialize();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIMsgSearchNotify> listener;
|
||||
m_iListener = 0;
|
||||
while (m_iListener != -1 && m_iListener < (signed)m_listenerList.Length())
|
||||
{
|
||||
listener = m_listenerList[m_iListener];
|
||||
int32_t listenerFlags = m_listenerFlagList[m_iListener++];
|
||||
if (!listenerFlags || (listenerFlags & nsIMsgSearchSession::onNewSearch))
|
||||
listener->OnNewSearch();
|
||||
}
|
||||
m_iListener = -1;
|
||||
|
||||
m_msgWindowWeak = do_GetWeakReference(aWindow);
|
||||
|
||||
return BeginSearching();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgSearchSession::InterruptSearch()
|
||||
{
|
||||
nsCOMPtr<nsIMsgWindow> msgWindow(do_QueryReferent(m_msgWindowWeak));
|
||||
if (msgWindow)
|
||||
{
|
||||
EnableFolderNotifications(true);
|
||||
if (m_idxRunningScope < m_scopeList.Length())
|
||||
msgWindow->StopUrls();
|
||||
|
||||
while (m_idxRunningScope < m_scopeList.Length())
|
||||
{
|
||||
ReleaseFolderDBRef();
|
||||
m_idxRunningScope++;
|
||||
}
|
||||
//m_idxRunningScope = m_scopeList.Length() so it will make us not run another url
|
||||
}
|
||||
if (m_backgroundTimer)
|
||||
{
|
||||
m_backgroundTimer->Cancel();
|
||||
NotifyListenersDone(NS_MSG_SEARCH_INTERRUPTED);
|
||||
|
||||
m_backgroundTimer = nullptr;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgSearchSession::PauseSearch()
|
||||
{
|
||||
if (m_backgroundTimer)
|
||||
{
|
||||
m_backgroundTimer->Cancel();
|
||||
m_searchPaused = true;
|
||||
return NS_OK;
|
||||
}
|
||||
else
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgSearchSession::ResumeSearch()
|
||||
{
|
||||
if (m_searchPaused)
|
||||
{
|
||||
m_searchPaused = false;
|
||||
return StartTimer();
|
||||
}
|
||||
else
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgSearchSession::GetSearchParam(void **aSearchParam)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgSearchSession::GetSearchType(nsMsgSearchType **aSearchType)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgSearchSession::SetSearchParam(nsMsgSearchType *type,
|
||||
void *param,
|
||||
nsMsgSearchType **_retval)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgSearchSession::GetNumResults(int32_t *aNumResults)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgSearchSession::SetWindow(nsIMsgWindow *aWindow)
|
||||
{
|
||||
m_msgWindowWeak = do_GetWeakReference(aWindow);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgSearchSession::GetWindow(nsIMsgWindow **aWindow)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aWindow);
|
||||
*aWindow = nullptr;
|
||||
nsCOMPtr<nsIMsgWindow> msgWindow(do_QueryReferent(m_msgWindowWeak));
|
||||
msgWindow.swap(*aWindow);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgSearchSession::OnStartRunningUrl(nsIURI *url)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgSearchSession::OnStopRunningUrl(nsIURI *url, nsresult aExitCode)
|
||||
{
|
||||
nsCOMPtr<nsIMsgSearchAdapter> runningAdapter;
|
||||
|
||||
nsresult rv = GetRunningAdapter(getter_AddRefs(runningAdapter));
|
||||
// tell the current adapter that the current url has run.
|
||||
if (NS_SUCCEEDED(rv) && runningAdapter)
|
||||
{
|
||||
runningAdapter->CurrentUrlDone(aExitCode);
|
||||
EnableFolderNotifications(true);
|
||||
ReleaseFolderDBRef();
|
||||
}
|
||||
if (++m_idxRunningScope < m_scopeList.Length())
|
||||
DoNextSearch();
|
||||
else
|
||||
NotifyListenersDone(aExitCode);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsresult nsMsgSearchSession::Initialize()
|
||||
{
|
||||
// Loop over scope terms, initializing an adapter per term. This
|
||||
// architecture is necessitated by two things:
|
||||
// 1. There might be more than one kind of adapter per if online
|
||||
// *and* offline mail mail folders are selected, or if newsgroups
|
||||
// belonging to Dredd *and* INN are selected
|
||||
// 2. Most of the protocols are only capable of searching one scope at a
|
||||
// time, so we'll do each scope in a separate adapter on the client
|
||||
|
||||
nsMsgSearchScopeTerm *scopeTerm = nullptr;
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
uint32_t numTerms;
|
||||
m_termList->Count(&numTerms);
|
||||
// Ensure that the FE has added scopes and terms to this search
|
||||
NS_ASSERTION(numTerms > 0, "no terms to search!");
|
||||
if (numTerms == 0)
|
||||
return NS_MSG_ERROR_NO_SEARCH_VALUES;
|
||||
|
||||
// if we don't have any search scopes to search, return that code.
|
||||
if (m_scopeList.Length() == 0)
|
||||
return NS_MSG_ERROR_INVALID_SEARCH_SCOPE;
|
||||
|
||||
m_runningUrl.Truncate(); // clear out old url, if any.
|
||||
m_idxRunningScope = 0;
|
||||
|
||||
// If this term list (loosely specified here by the first term) should be
|
||||
// scheduled in parallel, build up a list of scopes to do the round-robin scheduling
|
||||
for (uint32_t i = 0; i < m_scopeList.Length() && NS_SUCCEEDED(rv); i++)
|
||||
{
|
||||
scopeTerm = m_scopeList.ElementAt(i);
|
||||
// NS_ASSERTION(scopeTerm->IsValid());
|
||||
|
||||
rv = scopeTerm->InitializeAdapter(m_termList);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult nsMsgSearchSession::BeginSearching()
|
||||
{
|
||||
// Here's a sloppy way to start the URL, but I don't really have time to
|
||||
// unify the scheduling mechanisms. If the first scope is a newsgroup, and
|
||||
// it's not Dredd-capable, we build the URL queue. All other searches can be
|
||||
// done with one URL
|
||||
nsCOMPtr<nsIMsgWindow> msgWindow(do_QueryReferent(m_msgWindowWeak));
|
||||
if (msgWindow)
|
||||
msgWindow->SetStopped(false);
|
||||
return DoNextSearch();
|
||||
}
|
||||
|
||||
nsresult nsMsgSearchSession::DoNextSearch()
|
||||
{
|
||||
nsMsgSearchScopeTerm *scope = m_scopeList.ElementAt(m_idxRunningScope);
|
||||
if (scope->m_attribute == nsMsgSearchScope::onlineMail ||
|
||||
(scope->m_attribute == nsMsgSearchScope::news && scope->m_searchServer))
|
||||
{
|
||||
nsCOMPtr<nsIMsgSearchAdapter> adapter = do_QueryInterface(scope->m_adapter);
|
||||
if (adapter)
|
||||
{
|
||||
m_runningUrl.Truncate();
|
||||
adapter->GetEncoding(getter_Copies(m_runningUrl));
|
||||
}
|
||||
NS_ENSURE_STATE(!m_runningUrl.IsEmpty());
|
||||
return GetNextUrl();
|
||||
}
|
||||
else
|
||||
{
|
||||
return SearchWOUrls();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult nsMsgSearchSession::GetNextUrl()
|
||||
{
|
||||
nsCOMPtr<nsIMsgMessageService> msgService;
|
||||
|
||||
bool stopped = false;
|
||||
nsCOMPtr<nsIMsgWindow> msgWindow(do_QueryReferent(m_msgWindowWeak));
|
||||
if (msgWindow)
|
||||
msgWindow->GetStopped(&stopped);
|
||||
if (stopped)
|
||||
return NS_OK;
|
||||
|
||||
nsMsgSearchScopeTerm *currentTerm = GetRunningScope();
|
||||
NS_ENSURE_TRUE(currentTerm, NS_ERROR_NULL_POINTER);
|
||||
EnableFolderNotifications(false);
|
||||
nsCOMPtr<nsIMsgFolder> folder = currentTerm->m_folder;
|
||||
if (folder)
|
||||
{
|
||||
nsCString folderUri;
|
||||
folder->GetURI(folderUri);
|
||||
nsresult rv = GetMessageServiceFromURI(folderUri, getter_AddRefs(msgService));
|
||||
|
||||
if (NS_SUCCEEDED(rv) && msgService && currentTerm)
|
||||
msgService->Search(this, msgWindow, currentTerm->m_folder, m_runningUrl.get());
|
||||
return rv;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void nsMsgSearchSession::TimerCallback(nsITimer *aTimer, void *aClosure)
|
||||
{
|
||||
NS_ENSURE_TRUE_VOID(aClosure);
|
||||
nsMsgSearchSession *searchSession = (nsMsgSearchSession *) aClosure;
|
||||
bool done;
|
||||
bool stopped = false;
|
||||
|
||||
searchSession->TimeSlice(&done);
|
||||
nsCOMPtr<nsIMsgWindow> msgWindow(do_QueryReferent(searchSession->m_msgWindowWeak));
|
||||
if (msgWindow)
|
||||
msgWindow->GetStopped(&stopped);
|
||||
|
||||
if (done || stopped)
|
||||
{
|
||||
if (aTimer)
|
||||
aTimer->Cancel();
|
||||
searchSession->m_backgroundTimer = nullptr;
|
||||
if (searchSession->m_idxRunningScope < searchSession->m_scopeList.Length())
|
||||
searchSession->DoNextSearch();
|
||||
else
|
||||
searchSession->NotifyListenersDone(NS_OK);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult nsMsgSearchSession::StartTimer()
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
m_backgroundTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
m_backgroundTimer->InitWithFuncCallback(TimerCallback, (void *) this, 0,
|
||||
nsITimer::TYPE_REPEATING_SLACK);
|
||||
TimerCallback(m_backgroundTimer, this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsMsgSearchSession::SearchWOUrls()
|
||||
{
|
||||
EnableFolderNotifications(false);
|
||||
return StartTimer();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMsgSearchSession::GetRunningAdapter(nsIMsgSearchAdapter **aSearchAdapter)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aSearchAdapter);
|
||||
*aSearchAdapter = nullptr;
|
||||
nsMsgSearchScopeTerm *scope = GetRunningScope();
|
||||
if (scope)
|
||||
{
|
||||
NS_IF_ADDREF(*aSearchAdapter = scope->m_adapter);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgSearchSession::AddSearchHit(nsIMsgDBHdr *aHeader,
|
||||
nsIMsgFolder *aFolder)
|
||||
{
|
||||
nsCOMPtr<nsIMsgSearchNotify> listener;
|
||||
m_iListener = 0;
|
||||
while (m_iListener != -1 && m_iListener < (signed)m_listenerList.Length())
|
||||
{
|
||||
listener = m_listenerList[m_iListener];
|
||||
int32_t listenerFlags = m_listenerFlagList[m_iListener++];
|
||||
if (!listenerFlags || (listenerFlags & nsIMsgSearchSession::onSearchHit))
|
||||
listener->OnSearchHit(aHeader, aFolder);
|
||||
}
|
||||
m_iListener = -1;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsMsgSearchSession::NotifyListenersDone(nsresult aStatus)
|
||||
{
|
||||
// need to stabilize "this" in case one of the listeners releases the last
|
||||
// reference to us.
|
||||
RefPtr<nsIMsgSearchSession> kungFuDeathGrip(this);
|
||||
|
||||
nsCOMPtr<nsIMsgSearchNotify> listener;
|
||||
m_iListener = 0;
|
||||
while (m_iListener != -1 && m_iListener < (signed)m_listenerList.Length())
|
||||
{
|
||||
listener = m_listenerList[m_iListener];
|
||||
int32_t listenerFlags = m_listenerFlagList[m_iListener++];
|
||||
if (!listenerFlags || (listenerFlags & nsIMsgSearchSession::onSearchDone))
|
||||
listener->OnSearchDone(aStatus);
|
||||
}
|
||||
m_iListener = -1;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void nsMsgSearchSession::DestroyScopeList()
|
||||
{
|
||||
nsMsgSearchScopeTerm *scope = nullptr;
|
||||
|
||||
for (int32_t i = m_scopeList.Length() - 1; i >= 0; i--)
|
||||
{
|
||||
scope = m_scopeList.ElementAt(i);
|
||||
// NS_ASSERTION (scope->IsValid(), "invalid search scope");
|
||||
if (scope->m_adapter)
|
||||
scope->m_adapter->ClearScope();
|
||||
}
|
||||
m_scopeList.Clear();
|
||||
}
|
||||
|
||||
|
||||
void nsMsgSearchSession::DestroyTermList()
|
||||
{
|
||||
m_termList->Clear();
|
||||
}
|
||||
|
||||
nsMsgSearchScopeTerm *nsMsgSearchSession::GetRunningScope()
|
||||
{
|
||||
return m_scopeList.SafeElementAt(m_idxRunningScope, nullptr);
|
||||
}
|
||||
|
||||
nsresult nsMsgSearchSession::TimeSlice(bool *aDone)
|
||||
{
|
||||
// we only do serial for now.
|
||||
return TimeSliceSerial(aDone);
|
||||
}
|
||||
|
||||
void nsMsgSearchSession::ReleaseFolderDBRef()
|
||||
{
|
||||
nsMsgSearchScopeTerm *scope = GetRunningScope();
|
||||
if (!scope)
|
||||
return;
|
||||
|
||||
bool isOpen = false;
|
||||
uint32_t flags;
|
||||
nsCOMPtr<nsIMsgFolder> folder;
|
||||
scope->GetFolder(getter_AddRefs(folder));
|
||||
nsCOMPtr<nsIMsgMailSession> mailSession = do_GetService(NS_MSGMAILSESSION_CONTRACTID);
|
||||
if (!mailSession || !folder)
|
||||
return;
|
||||
|
||||
mailSession->IsFolderOpenInWindow(folder, &isOpen);
|
||||
folder->GetFlags(&flags);
|
||||
|
||||
/*we don't null out the db reference for inbox because inbox is like the "main" folder
|
||||
and performance outweighs footprint */
|
||||
if (!isOpen && !(nsMsgFolderFlags::Inbox & flags))
|
||||
folder->SetMsgDatabase(nullptr);
|
||||
}
|
||||
nsresult nsMsgSearchSession::TimeSliceSerial(bool *aDone)
|
||||
{
|
||||
// This version of TimeSlice runs each scope term one at a time, and waits until one
|
||||
// scope term is finished before starting another one. When we're searching the local
|
||||
// disk, this is the fastest way to do it.
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aDone);
|
||||
|
||||
nsMsgSearchScopeTerm *scope = GetRunningScope();
|
||||
if (!scope)
|
||||
{
|
||||
*aDone = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv = scope->TimeSlice(aDone);
|
||||
if (*aDone || NS_FAILED(rv))
|
||||
{
|
||||
EnableFolderNotifications(true);
|
||||
ReleaseFolderDBRef();
|
||||
m_idxRunningScope++;
|
||||
EnableFolderNotifications(false);
|
||||
// check if the next scope is an online search; if so,
|
||||
// set *aDone to true so that we'll try to run the next
|
||||
// search in TimerCallback.
|
||||
scope = GetRunningScope();
|
||||
if (scope && (scope->m_attribute == nsMsgSearchScope::onlineMail ||
|
||||
(scope->m_attribute == nsMsgSearchScope::news && scope->m_searchServer)))
|
||||
{
|
||||
*aDone = true;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
*aDone = false;
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
nsMsgSearchSession::EnableFolderNotifications(bool aEnable)
|
||||
{
|
||||
nsMsgSearchScopeTerm *scope = GetRunningScope();
|
||||
if (scope)
|
||||
{
|
||||
nsCOMPtr<nsIMsgFolder> folder;
|
||||
scope->GetFolder(getter_AddRefs(folder));
|
||||
if (folder) //enable msg count notifications
|
||||
folder->EnableNotifications(nsIMsgFolder::allMessageCountNotifications, aEnable, false);
|
||||
}
|
||||
}
|
||||
|
||||
//this method is used for adding new hdrs to quick search view
|
||||
NS_IMETHODIMP
|
||||
nsMsgSearchSession::MatchHdr(nsIMsgDBHdr *aMsgHdr, nsIMsgDatabase *aDatabase, bool *aResult)
|
||||
{
|
||||
nsMsgSearchScopeTerm *scope = m_scopeList.SafeElementAt(0, nullptr);
|
||||
if (scope)
|
||||
{
|
||||
if (!scope->m_adapter)
|
||||
scope->InitializeAdapter(m_termList);
|
||||
if (scope->m_adapter)
|
||||
{
|
||||
nsAutoString nullCharset, folderCharset;
|
||||
scope->m_adapter->GetSearchCharsets(nullCharset, folderCharset);
|
||||
NS_ConvertUTF16toUTF8 charset(folderCharset.get());
|
||||
nsMsgSearchOfflineMail::MatchTermsForSearch(aMsgHdr, m_termList,
|
||||
charset.get(), scope, aDatabase, &m_expressionTree, aResult);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
98
mailnews/base/search/src/nsMsgSearchSession.h
Normal file
98
mailnews/base/search/src/nsMsgSearchSession.h
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef nsMsgSearchSession_h___
|
||||
#define nsMsgSearchSession_h___
|
||||
|
||||
#include "nscore.h"
|
||||
#include "nsMsgSearchCore.h"
|
||||
#include "nsIMsgSearchSession.h"
|
||||
#include "nsIUrlListener.h"
|
||||
#include "nsIMsgWindow.h"
|
||||
#include "nsITimer.h"
|
||||
// Disable deprecation warnings generated by nsISupportsArray and associated
|
||||
// classes.
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning (disable : 4996)
|
||||
#endif
|
||||
#include "nsISupportsArray.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsTObserverArray.h"
|
||||
|
||||
class nsMsgSearchAdapter;
|
||||
class nsMsgSearchBoolExpression;
|
||||
class nsMsgSearchScopeTerm;
|
||||
|
||||
class nsMsgSearchSession : public nsIMsgSearchSession, public nsIUrlListener, public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIMSGSEARCHSESSION
|
||||
NS_DECL_NSIURLLISTENER
|
||||
|
||||
nsMsgSearchSession();
|
||||
|
||||
protected:
|
||||
virtual ~nsMsgSearchSession();
|
||||
|
||||
nsWeakPtr m_msgWindowWeak;
|
||||
nsresult Initialize();
|
||||
nsresult StartTimer();
|
||||
nsresult TimeSlice (bool *aDone);
|
||||
nsMsgSearchScopeTerm *GetRunningScope();
|
||||
void StopRunning();
|
||||
nsresult BeginSearching();
|
||||
nsresult DoNextSearch();
|
||||
nsresult SearchWOUrls ();
|
||||
nsresult GetNextUrl();
|
||||
nsresult NotifyListenersDone(nsresult status);
|
||||
void EnableFolderNotifications(bool aEnable);
|
||||
void ReleaseFolderDBRef();
|
||||
|
||||
nsTArray<RefPtr<nsMsgSearchScopeTerm>> m_scopeList;
|
||||
nsCOMPtr <nsISupportsArray> m_termList;
|
||||
|
||||
nsTArray<nsCOMPtr<nsIMsgSearchNotify> > m_listenerList;
|
||||
nsTArray<int32_t> m_listenerFlagList;
|
||||
/**
|
||||
* Iterator index for m_listenerList/m_listenerFlagList. We used to use an
|
||||
* nsTObserverArray for m_listenerList but its auto-adjusting iterator was
|
||||
* not helping us keep our m_listenerFlagList iterator correct.
|
||||
*
|
||||
* We are making the simplifying assumption that our notifications are
|
||||
* non-reentrant. In the exceptional case that it turns out they are
|
||||
* reentrant, we assume that this is the result of canceling a search while
|
||||
* the session is active and initiating a new one. In that case, we assume
|
||||
* the outer iteration can safely be abandoned.
|
||||
*
|
||||
* This value is defined to be the index of the next listener we will process.
|
||||
* This allows us to use the sentinel value of -1 to convey that no iteration
|
||||
* is in progress (and the iteration process to abort if the value transitions
|
||||
* to -1, which we always set on conclusion of our loop).
|
||||
*/
|
||||
int32_t m_iListener;
|
||||
|
||||
void DestroyTermList ();
|
||||
void DestroyScopeList ();
|
||||
|
||||
static void TimerCallback(nsITimer *aTimer, void *aClosure);
|
||||
// support for searching multiple scopes in serial
|
||||
nsresult TimeSliceSerial (bool *aDone);
|
||||
nsresult TimeSliceParallel ();
|
||||
|
||||
nsMsgSearchAttribValue m_sortAttribute;
|
||||
uint32_t m_idxRunningScope;
|
||||
nsMsgSearchType m_searchType;
|
||||
bool m_handlingError;
|
||||
nsCString m_runningUrl; // The url for the current search
|
||||
nsCOMPtr <nsITimer> m_backgroundTimer;
|
||||
bool m_searchPaused;
|
||||
nsMsgSearchBoolExpression *m_expressionTree;
|
||||
};
|
||||
|
||||
#endif
|
||||
2088
mailnews/base/search/src/nsMsgSearchTerm.cpp
Normal file
2088
mailnews/base/search/src/nsMsgSearchTerm.cpp
Normal file
File diff suppressed because it is too large
Load diff
117
mailnews/base/search/src/nsMsgSearchValue.cpp
Normal file
117
mailnews/base/search/src/nsMsgSearchValue.cpp
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#include "MailNewsTypes.h"
|
||||
#include "nsMsgSearchValue.h"
|
||||
#include "nsIMsgFolder.h"
|
||||
#include "nsMsgUtils.h"
|
||||
#include "nsStringGlue.h"
|
||||
|
||||
nsMsgSearchValueImpl::nsMsgSearchValueImpl(nsMsgSearchValue *aInitialValue)
|
||||
{
|
||||
mValue = *aInitialValue;
|
||||
if (IS_STRING_ATTRIBUTE(aInitialValue->attribute) && aInitialValue->string)
|
||||
{
|
||||
mValue.string = NS_strdup(aInitialValue->string);
|
||||
CopyUTF8toUTF16(mValue.string, mValue.utf16String);
|
||||
}
|
||||
else
|
||||
mValue.string = 0;
|
||||
}
|
||||
|
||||
nsMsgSearchValueImpl::~nsMsgSearchValueImpl()
|
||||
{
|
||||
if (IS_STRING_ATTRIBUTE(mValue.attribute))
|
||||
NS_Free(mValue.string);
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsMsgSearchValueImpl, nsIMsgSearchValue)
|
||||
|
||||
NS_IMPL_GETSET(nsMsgSearchValueImpl, Priority, nsMsgPriorityValue, mValue.u.priority)
|
||||
NS_IMPL_GETSET(nsMsgSearchValueImpl, Status, uint32_t, mValue.u.msgStatus)
|
||||
NS_IMPL_GETSET(nsMsgSearchValueImpl, Size, uint32_t, mValue.u.size)
|
||||
NS_IMPL_GETSET(nsMsgSearchValueImpl, MsgKey, nsMsgKey, mValue.u.key)
|
||||
NS_IMPL_GETSET(nsMsgSearchValueImpl, Age, int32_t, mValue.u.age)
|
||||
NS_IMPL_GETSET(nsMsgSearchValueImpl, Date, PRTime, mValue.u.date)
|
||||
NS_IMPL_GETSET(nsMsgSearchValueImpl, Attrib, nsMsgSearchAttribValue, mValue.attribute)
|
||||
NS_IMPL_GETSET(nsMsgSearchValueImpl, Label, nsMsgLabelValue, mValue.u.label)
|
||||
NS_IMPL_GETSET(nsMsgSearchValueImpl, JunkStatus, uint32_t, mValue.u.junkStatus)
|
||||
NS_IMPL_GETSET(nsMsgSearchValueImpl, JunkPercent, uint32_t, mValue.u.junkPercent)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMsgSearchValueImpl::GetFolder(nsIMsgFolder* *aResult)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aResult);
|
||||
NS_ENSURE_TRUE(mValue.attribute == nsMsgSearchAttrib::FolderInfo, NS_ERROR_ILLEGAL_VALUE);
|
||||
*aResult = mValue.u.folder;
|
||||
NS_IF_ADDREF(*aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMsgSearchValueImpl::SetFolder(nsIMsgFolder* aValue)
|
||||
{
|
||||
NS_ENSURE_TRUE(mValue.attribute == nsMsgSearchAttrib::FolderInfo, NS_ERROR_ILLEGAL_VALUE);
|
||||
mValue.u.folder = aValue;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMsgSearchValueImpl::GetStr(nsAString &aResult)
|
||||
{
|
||||
NS_ENSURE_TRUE(IS_STRING_ATTRIBUTE(mValue.attribute), NS_ERROR_ILLEGAL_VALUE);
|
||||
aResult = mValue.utf16String;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMsgSearchValueImpl::SetStr(const nsAString &aValue)
|
||||
{
|
||||
NS_ENSURE_TRUE(IS_STRING_ATTRIBUTE(mValue.attribute), NS_ERROR_ILLEGAL_VALUE);
|
||||
if (mValue.string)
|
||||
NS_Free(mValue.string);
|
||||
mValue.string = ToNewUTF8String(aValue);
|
||||
mValue.utf16String = aValue;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMsgSearchValueImpl::ToString(nsAString &aResult)
|
||||
{
|
||||
aResult.AssignLiteral("[nsIMsgSearchValue: ");
|
||||
if (IS_STRING_ATTRIBUTE(mValue.attribute)) {
|
||||
aResult.Append(mValue.utf16String);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
switch (mValue.attribute) {
|
||||
|
||||
case nsMsgSearchAttrib::Priority:
|
||||
case nsMsgSearchAttrib::Date:
|
||||
case nsMsgSearchAttrib::MsgStatus:
|
||||
case nsMsgSearchAttrib::MessageKey:
|
||||
case nsMsgSearchAttrib::Size:
|
||||
case nsMsgSearchAttrib::AgeInDays:
|
||||
case nsMsgSearchAttrib::FolderInfo:
|
||||
case nsMsgSearchAttrib::Label:
|
||||
case nsMsgSearchAttrib::JunkStatus:
|
||||
case nsMsgSearchAttrib::JunkPercent:
|
||||
{
|
||||
nsAutoString tempInt;
|
||||
tempInt.AppendInt(mValue.attribute);
|
||||
|
||||
aResult.AppendLiteral("type=");
|
||||
aResult.Append(tempInt);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
NS_ERROR("Unknown search value type");
|
||||
}
|
||||
|
||||
aResult.AppendLiteral("]");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
26
mailnews/base/search/src/nsMsgSearchValue.h
Normal file
26
mailnews/base/search/src/nsMsgSearchValue.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef __nsMsgSearchValue_h
|
||||
#define __nsMsgSearchValue_h
|
||||
|
||||
#include "nsIMsgSearchValue.h"
|
||||
#include "nsMsgSearchCore.h"
|
||||
|
||||
class nsMsgSearchValueImpl : public nsIMsgSearchValue {
|
||||
public:
|
||||
nsMsgSearchValueImpl(nsMsgSearchValue *aInitialValue);
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIMSGSEARCHVALUE
|
||||
|
||||
private:
|
||||
virtual ~nsMsgSearchValueImpl();
|
||||
|
||||
nsMsgSearchValue mValue;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
239
mailnews/base/search/src/nsMsgTraitService.js
Normal file
239
mailnews/base/search/src/nsMsgTraitService.js
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
/* 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/. */
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
// local static variables
|
||||
|
||||
var _lastIndex = 0; // the first index will be one
|
||||
var _traits = {};
|
||||
|
||||
var traitsBranch = Services.prefs.getBranch("mailnews.traits.");
|
||||
|
||||
function _registerTrait(aId, aIndex)
|
||||
{
|
||||
var trait = {};
|
||||
trait.enabled = false;
|
||||
trait.name = "";
|
||||
trait.antiId = "";
|
||||
trait.index = aIndex;
|
||||
_traits[aId] = trait;
|
||||
return;
|
||||
}
|
||||
|
||||
function nsMsgTraitService() {}
|
||||
|
||||
nsMsgTraitService.prototype =
|
||||
{
|
||||
// Component setup
|
||||
classID: Components.ID("{A2E95F4F-DA72-4a41-9493-661AD353C00A}"),
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Components.interfaces.nsIMsgTraitService]),
|
||||
|
||||
// nsIMsgTraitService implementation
|
||||
|
||||
get lastIndex()
|
||||
{
|
||||
return _lastIndex;
|
||||
},
|
||||
|
||||
registerTrait: function(aId)
|
||||
{
|
||||
if (_traits[aId])
|
||||
return 0; // meaning already registered
|
||||
_registerTrait(aId, ++_lastIndex);
|
||||
traitsBranch.setBoolPref("enabled." + _lastIndex, false);
|
||||
traitsBranch.setCharPref("id." + _lastIndex, aId);
|
||||
return _lastIndex;
|
||||
},
|
||||
|
||||
unRegisterTrait: function(aId)
|
||||
{
|
||||
if (_traits[aId])
|
||||
{
|
||||
var index = _traits[aId].index;
|
||||
_traits[aId] = null;
|
||||
traitsBranch.clearUserPref("id." + index);
|
||||
traitsBranch.clearUserPref("enabled." + index);
|
||||
traitsBranch.clearUserPref("antiId." + index);
|
||||
traitsBranch.clearUserPref("name." + index);
|
||||
}
|
||||
return;
|
||||
},
|
||||
|
||||
isRegistered: function(aId)
|
||||
{
|
||||
return _traits[aId] ? true : false;
|
||||
},
|
||||
|
||||
setName: function(aId, aName)
|
||||
{
|
||||
traitsBranch.setCharPref("name." + _traits[aId].index, aName);
|
||||
_traits[aId].name = aName;
|
||||
},
|
||||
|
||||
getName: function(aId)
|
||||
{
|
||||
return _traits[aId].name;
|
||||
},
|
||||
|
||||
getIndex: function(aId)
|
||||
{
|
||||
return _traits[aId].index;
|
||||
},
|
||||
|
||||
getId: function(aIndex)
|
||||
{
|
||||
for (let id in _traits)
|
||||
if (_traits[id].index == aIndex)
|
||||
return id;
|
||||
return null;
|
||||
},
|
||||
|
||||
setEnabled: function(aId, aEnabled)
|
||||
{
|
||||
traitsBranch.setBoolPref("enabled." + _traits[aId].index, aEnabled);
|
||||
_traits[aId].enabled = aEnabled;
|
||||
},
|
||||
|
||||
getEnabled: function(aId)
|
||||
{
|
||||
return _traits[aId].enabled;
|
||||
},
|
||||
|
||||
setAntiId: function(aId, aAntiId)
|
||||
{
|
||||
traitsBranch.setCharPref("antiId." + _traits[aId].index, aAntiId);
|
||||
_traits[aId].antiId = aAntiId;
|
||||
},
|
||||
|
||||
getAntiId: function(aId)
|
||||
{
|
||||
return _traits[aId].antiId;
|
||||
},
|
||||
|
||||
getEnabledIndices: function(aCount, aProIndices, aAntiIndices)
|
||||
{
|
||||
let proIndices = [];
|
||||
let antiIndices = [];
|
||||
for (let id in _traits)
|
||||
if (_traits[id].enabled)
|
||||
{
|
||||
proIndices.push(_traits[id].index);
|
||||
antiIndices.push(_traits[_traits[id].antiId].index);
|
||||
}
|
||||
aCount.value = proIndices.length;
|
||||
aProIndices.value = proIndices;
|
||||
aAntiIndices.value = antiIndices;
|
||||
return;
|
||||
},
|
||||
|
||||
addAlias: function addAlias(aTraitIndex, aTraitAliasIndex)
|
||||
{
|
||||
let aliasesString = "";
|
||||
try {
|
||||
aliasesString = traitsBranch.getCharPref("aliases." + aTraitIndex);
|
||||
}
|
||||
catch (e) {}
|
||||
let aliases;
|
||||
if (aliasesString.length)
|
||||
aliases = aliasesString.split(",");
|
||||
else
|
||||
aliases = [];
|
||||
if (aliases.indexOf(aTraitAliasIndex.toString()) == -1)
|
||||
{
|
||||
aliases.push(aTraitAliasIndex);
|
||||
traitsBranch.setCharPref("aliases." + aTraitIndex, aliases.join());
|
||||
}
|
||||
},
|
||||
|
||||
removeAlias: function removeAlias(aTraitIndex, aTraitAliasIndex)
|
||||
{
|
||||
let aliasesString = "";
|
||||
try {
|
||||
aliasesString = traitsBranch.getCharPref("aliases." + aTraitIndex);
|
||||
}
|
||||
catch (e) {
|
||||
return;
|
||||
}
|
||||
let aliases;
|
||||
if (aliasesString.length)
|
||||
aliases = aliasesString.split(",");
|
||||
else
|
||||
aliases = [];
|
||||
let location;
|
||||
if ((location = aliases.indexOf(aTraitAliasIndex.toString())) != -1)
|
||||
{
|
||||
aliases.splice(location, 1);
|
||||
traitsBranch.setCharPref("aliases." + aTraitIndex, aliases.join());
|
||||
}
|
||||
},
|
||||
|
||||
getAliases: function getAliases(aTraitIndex, aLength)
|
||||
{
|
||||
let aliasesString = "";
|
||||
try {
|
||||
aliasesString = traitsBranch.getCharPref("aliases." + aTraitIndex);
|
||||
}
|
||||
catch (e) {}
|
||||
|
||||
let aliases;
|
||||
if (aliasesString.length)
|
||||
aliases = aliasesString.split(",");
|
||||
else
|
||||
aliases = [];
|
||||
aLength.value = aliases.length;
|
||||
return aliases;
|
||||
},
|
||||
};
|
||||
|
||||
// initialization
|
||||
|
||||
_init();
|
||||
|
||||
function _init()
|
||||
{
|
||||
// get existing traits
|
||||
var idBranch = Services.prefs.getBranch("mailnews.traits.id.");
|
||||
var nameBranch = Services.prefs.getBranch("mailnews.traits.name.");
|
||||
var enabledBranch = Services.prefs.getBranch("mailnews.traits.enabled.");
|
||||
var antiIdBranch = Services.prefs.getBranch("mailnews.traits.antiId.");
|
||||
_lastIndex = Services.prefs.getBranch("mailnews.traits.").getIntPref("lastIndex");
|
||||
var ids = idBranch.getChildList("");
|
||||
for (var i = 0; i < ids.length; i++)
|
||||
{
|
||||
var id = idBranch.getCharPref(ids[i]);
|
||||
var index = parseInt(ids[i]);
|
||||
_registerTrait(id, index, false);
|
||||
|
||||
// Read in values, ignore errors since that usually means the
|
||||
// value does not exist
|
||||
try {
|
||||
_traits[id].name = nameBranch.getCharPref(ids[i]);
|
||||
}
|
||||
catch (e) {}
|
||||
|
||||
try {
|
||||
_traits[id].enabled = enabledBranch.getBoolPref(ids[i]);
|
||||
}
|
||||
catch (e) {}
|
||||
|
||||
try {
|
||||
_traits[id].antiId = antiIdBranch.getCharPref(ids[i]);
|
||||
}
|
||||
catch (e) {}
|
||||
|
||||
if (_lastIndex < index)
|
||||
_lastIndex = index;
|
||||
}
|
||||
|
||||
//for (traitId in _traits)
|
||||
// dump("\nindex of " + traitId + " is " + _traits[traitId].index);
|
||||
//dump("\n");
|
||||
}
|
||||
|
||||
var components = [nsMsgTraitService];
|
||||
var NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
|
||||
2
mailnews/base/search/src/nsMsgTraitService.manifest
Normal file
2
mailnews/base/search/src/nsMsgTraitService.manifest
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
component {A2E95F4F-DA72-4a41-9493-661AD353C00A} nsMsgTraitService.js
|
||||
contract @mozilla.org/msg-trait-service;1 {A2E95F4F-DA72-4a41-9493-661AD353C00A}
|
||||
Loading…
Add table
Add a link
Reference in a new issue