925 lines
30 KiB
C++
925 lines
30 KiB
C++
/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1999
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Pierre Phaneuf <pp@ludusdesign.com>
|
|
* Seth Spitzer <sspitzer@netscape.com>
|
|
* Howard Chu <hyc@symas.com>
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
// this file implements the nsMsgFilter interface
|
|
|
|
#include "msgCore.h"
|
|
#include "nsMsgBaseCID.h"
|
|
#include "nsIMsgHdr.h"
|
|
#include "nsMsgFilterList.h" // for kFileVersion
|
|
#include "nsMsgFilter.h"
|
|
#include "nsMsgUtils.h"
|
|
#include "nsFileStream.h"
|
|
#include "nsMsgLocalSearch.h"
|
|
#include "nsMsgSearchTerm.h"
|
|
#include "nsXPIDLString.h"
|
|
#include "nsIMsgAccountManager.h"
|
|
#include "nsIMsgIncomingServer.h"
|
|
#include "nsMsgSearchValue.h"
|
|
#include "nsReadableUtils.h"
|
|
#include "nsEscape.h"
|
|
#include "nsMsgI18N.h"
|
|
#include "nsIImportService.h"
|
|
#include "nsISupportsObsolete.h"
|
|
#include "nsIOutputStream.h"
|
|
#include "nsIStringBundle.h"
|
|
#include "nsDateTimeFormatCID.h"
|
|
|
|
static const char *kImapPrefix = "//imap:";
|
|
static const char *kWhitespace = "\b\t\r\n ";
|
|
static NS_DEFINE_CID(kDateTimeFormatCID, NS_DATETIMEFORMAT_CID);
|
|
|
|
nsMsgRuleAction::nsMsgRuleAction()
|
|
{
|
|
}
|
|
|
|
nsMsgRuleAction::~nsMsgRuleAction()
|
|
{
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS1(nsMsgRuleAction, nsIMsgRuleAction)
|
|
|
|
NS_IMPL_GETSET(nsMsgRuleAction, Type, nsMsgRuleActionType, m_type)
|
|
|
|
NS_IMETHODIMP nsMsgRuleAction::SetPriority(nsMsgPriorityValue aPriority)
|
|
{
|
|
NS_ENSURE_TRUE(m_type == nsMsgFilterAction::ChangePriority,
|
|
NS_ERROR_ILLEGAL_VALUE);
|
|
m_priority = aPriority;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgRuleAction::GetPriority(nsMsgPriorityValue *aResult)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
NS_ENSURE_TRUE(m_type == nsMsgFilterAction::ChangePriority,
|
|
NS_ERROR_ILLEGAL_VALUE);
|
|
*aResult = m_priority;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgRuleAction::SetLabel(nsMsgLabelValue aLabel)
|
|
{
|
|
NS_ENSURE_TRUE(m_type == nsMsgFilterAction::Label,
|
|
NS_ERROR_ILLEGAL_VALUE);
|
|
m_label = aLabel;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgRuleAction::GetLabel(nsMsgLabelValue *aResult)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
NS_ENSURE_TRUE(m_type == nsMsgFilterAction::Label, NS_ERROR_ILLEGAL_VALUE);
|
|
*aResult = m_label;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgRuleAction::SetTargetFolderUri(const char *aUri)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aUri);
|
|
NS_ENSURE_TRUE(m_type == nsMsgFilterAction::MoveToFolder ||
|
|
m_type == nsMsgFilterAction::CopyToFolder,
|
|
NS_ERROR_ILLEGAL_VALUE);
|
|
m_folderUri = aUri;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgRuleAction::GetTargetFolderUri(char** aResult)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
NS_ENSURE_TRUE(m_type == nsMsgFilterAction::MoveToFolder ||
|
|
m_type == nsMsgFilterAction::CopyToFolder,
|
|
NS_ERROR_ILLEGAL_VALUE);
|
|
*aResult = ToNewCString(m_folderUri);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgRuleAction::SetJunkScore(PRInt32 aJunkScore)
|
|
{
|
|
NS_ENSURE_TRUE(m_type == nsMsgFilterAction::JunkScore && aJunkScore >= 0 && aJunkScore <= 100,
|
|
NS_ERROR_ILLEGAL_VALUE);
|
|
m_junkScore = aJunkScore;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgRuleAction::GetJunkScore(PRInt32 *aResult)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
NS_ENSURE_TRUE(m_type == nsMsgFilterAction::JunkScore, NS_ERROR_ILLEGAL_VALUE);
|
|
*aResult = m_junkScore;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgRuleAction::SetStrValue(const char *aStrValue)
|
|
{
|
|
m_strValue = aStrValue;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgRuleAction::GetStrValue(char **aStrValue)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aStrValue);
|
|
*aStrValue = ToNewCString(m_strValue);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsMsgFilter::nsMsgFilter():
|
|
m_temporary(PR_FALSE),
|
|
m_unparseable(PR_FALSE),
|
|
m_filterList(nsnull),
|
|
m_expressionTree(nsnull)
|
|
{
|
|
NS_NewISupportsArray(getter_AddRefs(m_termList));
|
|
NS_NewISupportsArray(getter_AddRefs(m_actionList));
|
|
|
|
m_type = nsMsgFilterType::InboxRule;
|
|
}
|
|
|
|
nsMsgFilter::~nsMsgFilter()
|
|
{
|
|
delete m_expressionTree;
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS1(nsMsgFilter, nsIMsgFilter)
|
|
|
|
NS_IMPL_GETSET(nsMsgFilter, FilterType, nsMsgFilterTypeType, m_type)
|
|
NS_IMPL_GETSET(nsMsgFilter, Enabled, PRBool, m_enabled)
|
|
NS_IMPL_GETSET(nsMsgFilter, Temporary, PRBool, m_temporary)
|
|
NS_IMPL_GETSET(nsMsgFilter, Unparseable, PRBool, m_unparseable)
|
|
|
|
NS_IMETHODIMP nsMsgFilter::GetFilterName(PRUnichar **name)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(name);
|
|
*name = ToNewUnicode(m_filterName);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgFilter::SetFilterName(const PRUnichar *name)
|
|
{
|
|
m_filterName.Assign(name);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgFilter::GetFilterDesc(char **description)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(description);
|
|
*description = ToNewCString(m_description);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgFilter::SetFilterDesc(const char *description)
|
|
{
|
|
m_description.Assign(description);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgFilter::GetUnparsedBuffer(char **unparsedBuffer)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(unparsedBuffer);
|
|
*unparsedBuffer = ToNewCString(m_unparsedBuffer);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgFilter::SetUnparsedBuffer(const char *unparsedBuffer)
|
|
{
|
|
m_unparsedBuffer.Assign(unparsedBuffer);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgFilter::AddTerm(
|
|
nsMsgSearchAttribValue attrib, /* attribute for this term */
|
|
nsMsgSearchOpValue op, /* operator e.g. opContains */
|
|
nsIMsgSearchValue *value, /* value e.g. "Dogbert" */
|
|
PRBool BooleanAND, /* PR_TRUE if AND is the boolean operator.
|
|
PR_FALSE if OR is the boolean operators */
|
|
const char * arbitraryHeader) /* arbitrary header specified by user.
|
|
ignored unless attrib = attribOtherHeader */
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgFilter::AppendTerm(nsIMsgSearchTerm * aTerm)
|
|
{
|
|
NS_ENSURE_TRUE(aTerm, NS_ERROR_NULL_POINTER);
|
|
// invalidate expression tree if we're changing the terms
|
|
delete m_expressionTree;
|
|
m_expressionTree = nsnull;
|
|
return m_termList->AppendElement(NS_STATIC_CAST(nsISupports*,aTerm));
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgFilter::CreateTerm(nsIMsgSearchTerm **aResult)
|
|
{
|
|
nsMsgSearchTerm *term = new nsMsgSearchTerm;
|
|
NS_ENSURE_TRUE(term, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
*aResult = NS_STATIC_CAST(nsIMsgSearchTerm*,term);
|
|
NS_ADDREF(*aResult);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgFilter::CreateAction(nsIMsgRuleAction **aAction)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aAction);
|
|
nsMsgRuleAction *action = new nsMsgRuleAction;
|
|
NS_ENSURE_TRUE(action, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
*aAction = NS_STATIC_CAST(nsIMsgRuleAction*,action);
|
|
NS_ADDREF(*aAction);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgFilter::GetSortedActionList(nsISupportsArray *actionList)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(actionList);
|
|
PRUint32 numActions;
|
|
nsresult err = m_actionList->Count(&numActions);
|
|
NS_ENSURE_SUCCESS(err, err);
|
|
PRBool insertedFinalAction = PR_FALSE;
|
|
PRUint32 front = 0;
|
|
|
|
for (PRUint32 index =0; index < numActions; index++)
|
|
{
|
|
nsCOMPtr<nsIMsgRuleAction> action;
|
|
err = m_actionList->QueryElementAt(index, NS_GET_IID(nsIMsgRuleAction), (void **)getter_AddRefs(action));
|
|
if (!action)
|
|
continue;
|
|
|
|
nsMsgRuleActionType actionType;
|
|
action->GetType(&actionType);
|
|
|
|
//we always want MoveToFolder action to be last (or delete to trash)
|
|
if (actionType == nsMsgFilterAction::MoveToFolder || actionType == nsMsgFilterAction::Delete)
|
|
{
|
|
err = actionList->AppendElement(action);
|
|
NS_ENSURE_SUCCESS(err, err);
|
|
insertedFinalAction = PR_TRUE;
|
|
}
|
|
// Copy is always last, except for move/delete
|
|
else if (actionType == nsMsgFilterAction::CopyToFolder)
|
|
{
|
|
if (!insertedFinalAction)
|
|
{
|
|
err = actionList->AppendElement(action);
|
|
NS_ENSURE_SUCCESS(err, err);
|
|
}
|
|
else
|
|
{
|
|
// If we already have a move/delete action in place, we want to
|
|
// place ourselves just before that final action.
|
|
PRUint32 count;
|
|
actionList->Count(&count);
|
|
err = actionList->InsertElementAt(action, count - 2);
|
|
NS_ENSURE_SUCCESS(err, err);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
actionList->InsertElementAt(action,front);
|
|
// we always want FetchBodyFromPop3Server to be first
|
|
if (actionType == nsMsgFilterAction::FetchBodyFromPop3Server)
|
|
front = 1;
|
|
}
|
|
}
|
|
return err;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgFilter::AppendAction(nsIMsgRuleAction *aAction)
|
|
{
|
|
return m_actionList->AppendElement(NS_STATIC_CAST(nsISupports*,aAction));
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgFilter::GetActionAt(PRInt32 aIndex, nsIMsgRuleAction **aAction)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aAction);
|
|
return m_actionList->QueryElementAt(aIndex, NS_GET_IID(nsIMsgRuleAction),
|
|
(void **) aAction);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgFilter::GetActionList(nsISupportsArray **actionList)
|
|
{
|
|
NS_IF_ADDREF(*actionList = m_actionList);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP //for editing a filter
|
|
nsMsgFilter::ClearActionList()
|
|
{
|
|
return m_actionList->Clear();
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgFilter::GetTerm(PRInt32 termIndex,
|
|
nsMsgSearchAttribValue *attrib, /* attribute for this term */
|
|
nsMsgSearchOpValue *op, /* operator e.g. opContains */
|
|
nsIMsgSearchValue **value, /* value e.g. "Dogbert" */
|
|
PRBool *booleanAnd, /* PR_TRUE if AND is the boolean operator. PR_FALSE if OR is the boolean operator */
|
|
char ** arbitraryHeader) /* arbitrary header specified by user.ignore unless attrib = attribOtherHeader */
|
|
{
|
|
nsresult rv;
|
|
nsCOMPtr<nsIMsgSearchTerm> term;
|
|
rv = m_termList->QueryElementAt(termIndex, NS_GET_IID(nsIMsgSearchTerm),
|
|
(void **)getter_AddRefs(term));
|
|
if (NS_SUCCEEDED(rv) && term)
|
|
{
|
|
if(attrib)
|
|
term->GetAttrib(attrib);
|
|
if(op)
|
|
term->GetOp(op);
|
|
if(value)
|
|
term->GetValue(value);
|
|
if(booleanAnd)
|
|
term->GetBooleanAnd(booleanAnd);
|
|
if (attrib && arbitraryHeader
|
|
&& *attrib > nsMsgSearchAttrib::OtherHeader
|
|
&& *attrib < nsMsgSearchAttrib::kNumMsgSearchAttributes)
|
|
term->GetArbitraryHeader(arbitraryHeader);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgFilter::GetSearchTerms(nsISupportsArray **aResult)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
// caller can change m_termList, which can invalidate m_expressionTree.
|
|
delete m_expressionTree;
|
|
m_expressionTree = nsnull;
|
|
NS_IF_ADDREF(*aResult = m_termList);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgFilter::SetSearchTerms(nsISupportsArray *aSearchList)
|
|
{
|
|
delete m_expressionTree;
|
|
m_expressionTree = nsnull;
|
|
m_termList = aSearchList;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgFilter::SetScope(nsIMsgSearchScopeTerm *aResult)
|
|
{
|
|
m_scope = aResult;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgFilter::GetScope(nsIMsgSearchScopeTerm **aResult)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
NS_IF_ADDREF(*aResult = m_scope);
|
|
return NS_OK;
|
|
}
|
|
|
|
#define LOG_ENTRY_START_TAG "<p>\n"
|
|
#define LOG_ENTRY_START_TAG_LEN (strlen(LOG_ENTRY_START_TAG))
|
|
#define LOG_ENTRY_END_TAG "</p>\n"
|
|
#define LOG_ENTRY_END_TAG_LEN (strlen(LOG_ENTRY_END_TAG))
|
|
|
|
NS_IMETHODIMP nsMsgFilter::LogRuleHit(nsIMsgRuleAction *aFilterAction, nsIMsgDBHdr *aMsgHdr)
|
|
{
|
|
NS_ENSURE_TRUE(m_filterList, NS_OK);
|
|
nsCOMPtr <nsIOutputStream> logStream;
|
|
nsresult rv = m_filterList->GetLogStream(getter_AddRefs(logStream));
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
PRTime date;
|
|
nsMsgRuleActionType actionType;
|
|
|
|
nsXPIDLString authorValue;
|
|
nsXPIDLString subjectValue;
|
|
nsXPIDLString filterName;
|
|
nsXPIDLString dateValue;
|
|
|
|
GetFilterName(getter_Copies(filterName));
|
|
aFilterAction->GetType(&actionType);
|
|
(void)aMsgHdr->GetDate(&date);
|
|
PRExplodedTime exploded;
|
|
PR_ExplodeTime(date, PR_LocalTimeParameters, &exploded);
|
|
|
|
if (!mDateFormatter)
|
|
{
|
|
mDateFormatter = do_CreateInstance(kDateTimeFormatCID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
if (!mDateFormatter)
|
|
{
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
}
|
|
mDateFormatter->FormatPRExplodedTime(nsnull, kDateFormatShort,
|
|
kTimeFormatSeconds, &exploded,
|
|
dateValue);
|
|
|
|
(void)aMsgHdr->GetMime2DecodedAuthor(getter_Copies(authorValue));
|
|
(void)aMsgHdr->GetMime2DecodedSubject(getter_Copies(subjectValue));
|
|
|
|
nsCString buffer;
|
|
// this is big enough to hold a log entry.
|
|
// do this so we avoid growing and copying as we append to the log.
|
|
buffer.SetCapacity(512);
|
|
|
|
nsCOMPtr<nsIStringBundleService> bundleService =
|
|
do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCOMPtr<nsIStringBundle> bundle;
|
|
rv = bundleService->CreateBundle("chrome://messenger/locale/filter.properties",
|
|
getter_AddRefs(bundle));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
const PRUnichar *filterLogDetectFormatStrings[4] = { filterName.get(), authorValue.get(), subjectValue.get(), dateValue.get() };
|
|
nsXPIDLString filterLogDetectStr;
|
|
rv = bundle->FormatStringFromName(
|
|
NS_LITERAL_STRING("filterLogDetectStr").get(),
|
|
filterLogDetectFormatStrings, 4,
|
|
getter_Copies(filterLogDetectStr));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
buffer += NS_ConvertUTF16toUTF8(filterLogDetectStr);
|
|
buffer += "\n";
|
|
|
|
if (actionType == nsMsgFilterAction::MoveToFolder ||
|
|
actionType == nsMsgFilterAction::CopyToFolder)
|
|
{
|
|
nsXPIDLCString actionFolderUri;
|
|
aFilterAction->GetTargetFolderUri(getter_Copies(actionFolderUri));
|
|
NS_ConvertASCIItoUTF16 actionFolderUriValue(actionFolderUri);
|
|
|
|
nsXPIDLCString msgId;
|
|
aMsgHdr->GetMessageId(getter_Copies(msgId));
|
|
NS_ConvertASCIItoUTF16 msgIdValue(msgId);
|
|
|
|
const PRUnichar *logMoveFormatStrings[2] = { msgIdValue.get(), actionFolderUriValue.get() };
|
|
nsXPIDLString logMoveStr;
|
|
rv = bundle->FormatStringFromName(
|
|
(actionType == nsMsgFilterAction::MoveToFolder) ?
|
|
NS_LITERAL_STRING("logMoveStr").get() :
|
|
NS_LITERAL_STRING("logCopyStr").get(),
|
|
logMoveFormatStrings, 2,
|
|
getter_Copies(logMoveStr));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
buffer += NS_ConvertUTF16toUTF8(logMoveStr);
|
|
}
|
|
else
|
|
{
|
|
nsXPIDLString actionValue;
|
|
nsAutoString filterActionID;
|
|
filterActionID = NS_LITERAL_STRING("filterAction");
|
|
filterActionID.AppendInt(actionType);
|
|
rv = bundle->GetStringFromName(filterActionID.get(), getter_Copies(actionValue));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
buffer += NS_ConvertUTF16toUTF8(actionValue);
|
|
}
|
|
buffer += "\n";
|
|
|
|
PRUint32 writeCount;
|
|
|
|
rv = logStream->Write(LOG_ENTRY_START_TAG, LOG_ENTRY_START_TAG_LEN, &writeCount);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
NS_ASSERTION(writeCount == LOG_ENTRY_START_TAG_LEN, "failed to write out start log tag");
|
|
|
|
// html escape the log for security reasons.
|
|
// we don't want some to send us a message with a subject with
|
|
// html tags, especially <script>
|
|
char *escapedBuffer = nsEscapeHTML(buffer.get());
|
|
if (!escapedBuffer)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
PRUint32 escapedBufferLen = strlen(escapedBuffer);
|
|
rv = logStream->Write(escapedBuffer, escapedBufferLen, &writeCount);
|
|
PR_Free(escapedBuffer);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
NS_ASSERTION(writeCount == escapedBufferLen, "failed to write out log hit");
|
|
|
|
rv = logStream->Write(LOG_ENTRY_END_TAG, LOG_ENTRY_END_TAG_LEN, &writeCount);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
NS_ASSERTION(writeCount == LOG_ENTRY_END_TAG_LEN, "failed to write out end log tag");
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgFilter::MatchHdr(nsIMsgDBHdr *msgHdr, nsIMsgFolder *folder,
|
|
nsIMsgDatabase *db, const char *headers,
|
|
PRUint32 headersSize, PRBool *pResult)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(folder);
|
|
NS_ENSURE_ARG_POINTER(msgHdr);
|
|
// use offlineMail because
|
|
nsXPIDLCString folderCharset;
|
|
folder->GetCharset(getter_Copies(folderCharset));
|
|
nsresult rv = nsMsgSearchOfflineMail::MatchTermsForFilter(msgHdr, m_termList,
|
|
folderCharset.get(), m_scope, db, headers, headersSize, &m_expressionTree, pResult);
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
nsMsgFilter::SetFilterList(nsIMsgFilterList *filterList)
|
|
{
|
|
// doesn't hold a ref.
|
|
m_filterList = filterList;
|
|
}
|
|
|
|
nsresult
|
|
nsMsgFilter::GetFilterList(nsIMsgFilterList **aResult)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
NS_IF_ADDREF(*aResult = m_filterList);
|
|
return NS_OK;
|
|
}
|
|
|
|
void nsMsgFilter::SetFilterScript(nsCString *fileName)
|
|
{
|
|
m_scriptFileName = *fileName;
|
|
}
|
|
|
|
nsresult nsMsgFilter::ConvertMoveOrCopyToFolderValue(nsIMsgRuleAction *filterAction, nsCString &moveValue)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(filterAction);
|
|
PRInt16 filterVersion = kFileVersion;
|
|
if (m_filterList)
|
|
m_filterList->GetVersion(&filterVersion);
|
|
if (filterVersion <= k60Beta1Version)
|
|
{
|
|
nsCOMPtr <nsIImportService> impSvc = do_GetService(NS_IMPORTSERVICE_CONTRACTID);
|
|
NS_ASSERTION(impSvc, "cannot get importService");
|
|
nsCOMPtr <nsIMsgFolder> rootFolder;
|
|
nsXPIDLCString folderUri;
|
|
|
|
m_filterList->GetFolder(getter_AddRefs(rootFolder));
|
|
|
|
// if relative path starts with kImap, this is a move to folder on the same server
|
|
if (moveValue.Find(kImapPrefix) == 0)
|
|
{
|
|
PRInt32 prefixLen = PL_strlen(kImapPrefix);
|
|
nsCAutoString originalServerPath;
|
|
moveValue.Mid(originalServerPath, prefixLen, moveValue.Length() - prefixLen);
|
|
if ( filterVersion == k45Version && impSvc)
|
|
{
|
|
nsAutoString unicodeStr;
|
|
impSvc->SystemStringToUnicode(originalServerPath.get(), unicodeStr);
|
|
nsresult rv = CopyUTF16toMUTF7(unicodeStr, originalServerPath);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
nsCOMPtr <nsIMsgFolder> destIFolder;
|
|
if (rootFolder)
|
|
{
|
|
rootFolder->FindSubFolder(originalServerPath, getter_AddRefs(destIFolder));
|
|
if (destIFolder)
|
|
{
|
|
destIFolder->GetURI(getter_Copies(folderUri));
|
|
filterAction->SetTargetFolderUri(folderUri);
|
|
moveValue.Assign(folderUri);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// start off leaving the value the same.
|
|
filterAction->SetTargetFolderUri(moveValue.get());
|
|
nsresult rv = NS_OK;
|
|
nsCOMPtr <nsIMsgFolder> localMailRoot;
|
|
rootFolder->GetURI(getter_Copies(folderUri));
|
|
// if the root folder is not imap, than the local mail root is the server root.
|
|
// otherwise, it's the migrated local folders.
|
|
if (nsCRT::strncmp("imap:", folderUri, 5))
|
|
{
|
|
localMailRoot = rootFolder;
|
|
}
|
|
else
|
|
{
|
|
nsCOMPtr<nsIMsgAccountManager> accountManager =
|
|
do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
nsCOMPtr <nsIMsgIncomingServer> server;
|
|
rv = accountManager->GetLocalFoldersServer(getter_AddRefs(server));
|
|
if (NS_SUCCEEDED(rv) && server)
|
|
{
|
|
rv = server->GetRootFolder(getter_AddRefs(localMailRoot));
|
|
}
|
|
}
|
|
}
|
|
if (NS_SUCCEEDED(rv) && localMailRoot)
|
|
{
|
|
nsXPIDLCString localRootURI;
|
|
nsCOMPtr <nsIMsgFolder> destIMsgFolder;
|
|
nsCOMPtr <nsIMsgFolder> localMailRootMsgFolder = do_QueryInterface(localMailRoot);
|
|
localMailRoot->GetURI(getter_Copies(localRootURI));
|
|
nsCString destFolderUri;
|
|
destFolderUri.Assign( localRootURI);
|
|
// need to remove ".sbd" from moveValue, and perhaps escape it.
|
|
moveValue.ReplaceSubstring(".sbd/", "/");
|
|
|
|
#if defined(XP_MAC) || defined(XP_MACOSX)
|
|
char *unescapedMoveValue = ToNewCString(moveValue);
|
|
nsUnescape(unescapedMoveValue);
|
|
moveValue.Assign(unescapedMoveValue);
|
|
nsCRT::free(unescapedMoveValue);
|
|
#endif
|
|
destFolderUri.Append('/');
|
|
if ( filterVersion == k45Version && impSvc)
|
|
{
|
|
nsAutoString unicodeStr;
|
|
impSvc->SystemStringToUnicode(moveValue.get(), unicodeStr);
|
|
rv = NS_MsgEscapeEncodeURLPath(unicodeStr, moveValue);
|
|
}
|
|
destFolderUri.Append(moveValue);
|
|
localMailRootMsgFolder->GetChildWithURI (destFolderUri.get(), PR_TRUE, PR_FALSE /*caseInsensitive*/, getter_AddRefs(destIMsgFolder));
|
|
|
|
if (destIMsgFolder)
|
|
{
|
|
destIMsgFolder->GetURI(getter_Copies(folderUri));
|
|
filterAction->SetTargetFolderUri(folderUri);
|
|
moveValue.Assign(folderUri);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
filterAction->SetTargetFolderUri(moveValue.get());
|
|
|
|
return NS_OK;
|
|
// set m_action.m_value.m_folderUri
|
|
}
|
|
|
|
nsresult nsMsgFilter::SaveToTextFile(nsIOFileStream *aStream)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aStream);
|
|
if (m_unparseable)
|
|
{
|
|
//we need to trim leading whitespaces before filing out
|
|
m_unparsedBuffer.Trim(kWhitespace, PR_TRUE /*leadingCharacters*/, PR_FALSE /*trailingCharacters*/);
|
|
*aStream << m_unparsedBuffer.get();
|
|
return NS_OK;
|
|
}
|
|
nsresult err = m_filterList->WriteWstrAttr(nsIMsgFilterList::attribName, m_filterName.get(), aStream);
|
|
err = m_filterList->WriteBoolAttr(nsIMsgFilterList::attribEnabled, m_enabled, aStream);
|
|
err = m_filterList->WriteStrAttr(nsIMsgFilterList::attribDescription, m_description.get(), aStream);
|
|
err = m_filterList->WriteIntAttr(nsIMsgFilterList::attribType, m_type, aStream);
|
|
if (IsScript())
|
|
err = m_filterList->WriteStrAttr(nsIMsgFilterList::attribScriptFile, m_scriptFileName.get(), aStream);
|
|
else
|
|
err = SaveRule(aStream);
|
|
return err;
|
|
}
|
|
|
|
nsresult nsMsgFilter::SaveRule(nsIOFileStream *aStream)
|
|
{
|
|
nsresult err = NS_OK;
|
|
nsCOMPtr<nsIMsgFilterList> filterList;
|
|
GetFilterList(getter_AddRefs(filterList));
|
|
nsCAutoString actionFilingStr;
|
|
|
|
PRUint32 numActions;
|
|
err = m_actionList->Count(&numActions);
|
|
NS_ENSURE_SUCCESS(err, err);
|
|
|
|
for (PRUint32 index =0; index < numActions; index++)
|
|
{
|
|
nsCOMPtr<nsIMsgRuleAction> action;
|
|
err = m_actionList->QueryElementAt(index, NS_GET_IID(nsIMsgRuleAction), (void **)getter_AddRefs(action));
|
|
if (!action)
|
|
continue;
|
|
|
|
nsMsgRuleActionType actionType;
|
|
action->GetType(&actionType);
|
|
GetActionFilingStr(actionType, actionFilingStr);
|
|
|
|
err = filterList->WriteStrAttr(nsIMsgFilterList::attribAction, actionFilingStr.get(), aStream);
|
|
NS_ENSURE_SUCCESS(err, err);
|
|
|
|
switch(actionType)
|
|
{
|
|
case nsMsgFilterAction::MoveToFolder:
|
|
case nsMsgFilterAction::CopyToFolder:
|
|
{
|
|
nsXPIDLCString imapTargetString;
|
|
action->GetTargetFolderUri(getter_Copies(imapTargetString));
|
|
err = filterList->WriteStrAttr(nsIMsgFilterList::attribActionValue, imapTargetString.get(), aStream);
|
|
}
|
|
break;
|
|
case nsMsgFilterAction::ChangePriority:
|
|
{
|
|
nsMsgPriorityValue priorityValue;
|
|
action->GetPriority(&priorityValue);
|
|
nsCAutoString priority;
|
|
NS_MsgGetUntranslatedPriorityName(priorityValue, priority);
|
|
err = filterList->WriteStrAttr(
|
|
nsIMsgFilterList::attribActionValue, priority.get(), aStream);
|
|
}
|
|
break;
|
|
case nsMsgFilterAction::Label:
|
|
{
|
|
nsMsgLabelValue label;
|
|
action->GetLabel(&label);
|
|
err = filterList->WriteIntAttr(nsIMsgFilterList::attribActionValue, label, aStream);
|
|
}
|
|
break;
|
|
case nsMsgFilterAction::JunkScore:
|
|
{
|
|
PRInt32 junkScore;
|
|
action->GetJunkScore(&junkScore);
|
|
err = filterList->WriteIntAttr(nsIMsgFilterList::attribActionValue, junkScore, aStream);
|
|
}
|
|
break;
|
|
case nsMsgFilterAction::AddTag:
|
|
case nsMsgFilterAction::Reply:
|
|
case nsMsgFilterAction::Forward:
|
|
{
|
|
nsXPIDLCString strValue;
|
|
action->GetStrValue(getter_Copies(strValue));
|
|
// strValue is e-mail address
|
|
err = filterList->WriteStrAttr(nsIMsgFilterList::attribActionValue, strValue.get(), aStream);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
// and here the fun begins - file out term list...
|
|
PRUint32 searchIndex;
|
|
nsCAutoString condition;
|
|
PRUint32 count;
|
|
m_termList->Count(&count);
|
|
for (searchIndex = 0; searchIndex < count && NS_SUCCEEDED(err);
|
|
searchIndex++)
|
|
{
|
|
nsCAutoString stream;
|
|
|
|
nsCOMPtr<nsIMsgSearchTerm> term;
|
|
m_termList->QueryElementAt(searchIndex, NS_GET_IID(nsIMsgSearchTerm),
|
|
(void **)getter_AddRefs(term));
|
|
if (!term)
|
|
continue;
|
|
|
|
if (condition.Length() > 1)
|
|
condition += ' ';
|
|
|
|
PRBool booleanAnd;
|
|
PRBool matchAll;
|
|
term->GetBooleanAnd(&booleanAnd);
|
|
term->GetMatchAll(&matchAll);
|
|
if (matchAll)
|
|
{
|
|
condition += "ALL";
|
|
break;
|
|
}
|
|
else if (booleanAnd)
|
|
condition += "AND (";
|
|
else
|
|
condition += "OR (";
|
|
|
|
nsresult searchError = term->GetTermAsString(stream);
|
|
if (NS_FAILED(searchError))
|
|
{
|
|
err = searchError;
|
|
break;
|
|
}
|
|
|
|
condition += stream;
|
|
condition += ')';
|
|
}
|
|
if (NS_SUCCEEDED(err))
|
|
err = filterList->WriteStrAttr(nsIMsgFilterList::attribCondition, condition.get(), aStream);
|
|
return err;
|
|
}
|
|
|
|
// for each action, this table encodes the filterTypes that support the action.
|
|
struct RuleActionsTableEntry
|
|
{
|
|
nsMsgRuleActionType action;
|
|
nsMsgFilterTypeType supportedTypes;
|
|
PRInt32 xp_strIndex;
|
|
const char *actionFilingStr; /* used for filing out filters, don't translate! */
|
|
};
|
|
|
|
static struct RuleActionsTableEntry ruleActionsTable[] =
|
|
{
|
|
{ nsMsgFilterAction::MoveToFolder, nsMsgFilterType::Inbox, 0, "Move to folder"},
|
|
{ nsMsgFilterAction::CopyToFolder, nsMsgFilterType::Inbox, 0, "Copy to folder"},
|
|
{ nsMsgFilterAction::ChangePriority, nsMsgFilterType::Inbox, 0, "Change priority"},
|
|
{ nsMsgFilterAction::Delete, nsMsgFilterType::All, 0, "Delete"},
|
|
{ nsMsgFilterAction::MarkRead, nsMsgFilterType::All, 0, "Mark read"},
|
|
{ nsMsgFilterAction::KillThread, nsMsgFilterType::All, 0, "Ignore thread"},
|
|
{ nsMsgFilterAction::WatchThread, nsMsgFilterType::All, 0, "Watch thread"},
|
|
{ nsMsgFilterAction::MarkFlagged, nsMsgFilterType::All, 0, "Mark flagged"},
|
|
{ nsMsgFilterAction::Label, nsMsgFilterType::All, 0, "Label"},
|
|
{ nsMsgFilterAction::Reply, nsMsgFilterType::All, 0, "Reply"},
|
|
{ nsMsgFilterAction::Forward, nsMsgFilterType::All, 0, "Forward"},
|
|
{ nsMsgFilterAction::StopExecution, nsMsgFilterType::All, 0, "Stop execution"},
|
|
{ nsMsgFilterAction::DeleteFromPop3Server, nsMsgFilterType::Inbox, 0, "Delete from Pop3 server"},
|
|
{ nsMsgFilterAction::LeaveOnPop3Server, nsMsgFilterType::Inbox, 0, "Leave on Pop3 server"},
|
|
{ nsMsgFilterAction::JunkScore, nsMsgFilterType::All, 0, "JunkScore"},
|
|
{ nsMsgFilterAction::FetchBodyFromPop3Server, nsMsgFilterType::Inbox, 0, "Fetch body from Pop3Server"},
|
|
{ nsMsgFilterAction::AddTag, nsMsgFilterType::All, 0, "AddTag"},
|
|
};
|
|
|
|
const char *nsMsgFilter::GetActionStr(nsMsgRuleActionType action)
|
|
{
|
|
int numActions = sizeof(ruleActionsTable) / sizeof(ruleActionsTable[0]);
|
|
|
|
for (int i = 0; i < numActions; i++)
|
|
{
|
|
if (action == ruleActionsTable[i].action)
|
|
return ruleActionsTable[i].actionFilingStr;
|
|
}
|
|
return "";
|
|
}
|
|
/*static */nsresult nsMsgFilter::GetActionFilingStr(nsMsgRuleActionType action, nsCString &actionStr)
|
|
{
|
|
int numActions = sizeof(ruleActionsTable) / sizeof(ruleActionsTable[0]);
|
|
|
|
for (int i = 0; i < numActions; i++)
|
|
{
|
|
if (action == ruleActionsTable[i].action)
|
|
{
|
|
actionStr = ruleActionsTable[i].actionFilingStr;
|
|
return NS_OK;
|
|
}
|
|
}
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
|
|
|
|
nsMsgRuleActionType nsMsgFilter::GetActionForFilingStr(nsCString &actionStr)
|
|
{
|
|
int numActions = sizeof(ruleActionsTable) / sizeof(ruleActionsTable[0]);
|
|
|
|
for (int i = 0; i < numActions; i++)
|
|
{
|
|
if (actionStr.Equals(ruleActionsTable[i].actionFilingStr))
|
|
return ruleActionsTable[i].action;
|
|
}
|
|
return nsMsgFilterAction::None;
|
|
}
|
|
|
|
PRInt16
|
|
nsMsgFilter::GetVersion()
|
|
{
|
|
if (!m_filterList) return 0;
|
|
PRInt16 version;
|
|
m_filterList->GetVersion(&version);
|
|
return version;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void nsMsgFilter::Dump()
|
|
{
|
|
nsCAutoString s;
|
|
CopyUCS2toASCII(m_filterName, s);
|
|
printf("filter %s type = %c desc = %s\n", s.get(), m_type + '0', m_description.get());
|
|
}
|
|
#endif
|
|
|