/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Netscape 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/NPL/ * * 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 * Seth Spitzer * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the NPL, 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 NPL, 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 "nsMsgSearchScopeTerm.h" #include "nsIMsgAccountManager.h" #include "nsIMsgIncomingServer.h" #include "nsMsgSearchValue.h" #include "nsReadableUtils.h" #include "nsEscape.h" #include "nsMsgUtf7Utils.h" #include "nsIImportService.h" #include "nsISupportsObsolete.h" #include "nsIOutputStream.h" #include "nsEscape.h" static const char *kImapPrefix = "//imap:"; static const char *kWhitespace = "\b\t\r\n "; 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, 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, 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; } nsMsgFilter::nsMsgFilter(): m_temporary(PR_FALSE), m_unparseable(PR_FALSE), m_filterList(nsnull) { NS_NewISupportsArray(getter_AddRefs(m_termList)); NS_NewISupportsArray(getter_AddRefs(m_actionList)); m_type = nsMsgFilterType::InboxRule; } nsMsgFilter::~nsMsgFilter() { } 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); 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); PRUint32 front = 0; for (PRUint32 index =0; index < numActions; index++) { nsCOMPtr action; err = m_actionList->QueryElementAt(index, NS_GET_IID(nsIMsgRuleAction), (void **)getter_AddRefs(action)); if (!action) continue; nsMsgRuleActionType actionType; action->GetType(&actionType); if (actionType == nsMsgFilterAction::MoveToFolder) //we always want MoveToFolder action to be last actionList->AppendElement(action); 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 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); NS_IF_ADDREF(*aResult = m_termList); return NS_OK; } NS_IMETHODIMP nsMsgFilter::SetSearchTerms(nsISupportsArray *aSearchList) { 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 "

\n" #define LOG_ENTRY_START_TAG_LEN (strlen(LOG_ENTRY_START_TAG)) #define LOG_ENTRY_END_TAG "

\n" #define LOG_ENTRY_END_TAG_LEN (strlen(LOG_ENTRY_END_TAG)) NS_IMETHODIMP nsMsgFilter::LogRuleHit(nsIMsgRuleAction *aFilterAction, nsIMsgDBHdr *aMsgHdr) { nsCOMPtr logStream; nsresult rv = m_filterList->GetLogStream(getter_AddRefs(logStream)); NS_ENSURE_SUCCESS(rv,rv); PRTime date; char dateStr[40]; /* 30 probably not enough */ nsMsgRuleActionType actionType; nsXPIDLCString author; nsXPIDLCString subject; nsXPIDLString filterName; GetFilterName(getter_Copies(filterName)); aFilterAction->GetType(&actionType); rv = aMsgHdr->GetDate(&date); PRExplodedTime exploded; PR_ExplodeTime(date, PR_LocalTimeParameters, &exploded); // XXX Temporary until logging is fully localized PR_FormatTimeUSEnglish(dateStr, sizeof(dateStr), "%Y-%m-%d %H:%M:%S", &exploded); aMsgHdr->GetAuthor(getter_Copies(author)); aMsgHdr->GetSubject(getter_Copies(subject)); 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); buffer = "Applied filter \""; buffer += NS_ConvertUCS2toUTF8(filterName).get(); buffer += "\" to message from "; buffer += (const char*)author; buffer += " - "; buffer += (const char *)subject; buffer += " at "; buffer += dateStr; buffer += "\n"; const char *actionStr = GetActionStr(actionType); buffer += "Action = "; buffer += actionStr; buffer += " "; if (actionType == nsMsgFilterAction::MoveToFolder) { nsXPIDLCString actionFolderUri; aFilterAction->GetTargetFolderUri(getter_Copies(actionFolderUri)); buffer += actionFolderUri.get(); } buffer += "\n"; if (actionType == nsMsgFilterAction::MoveToFolder) { nsXPIDLCString msgId; aMsgHdr->GetMessageId(getter_Copies(msgId)); buffer += " id = "; buffer += (const char*)msgId; 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