Back to index

lightning-sunbird  0.9+nobinonly
nsMsgFilter.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *   Seth Spitzer <sspitzer@netscape.com>
00025  *   Howard Chu <hyc@symas.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 // this file implements the nsMsgFilter interface 
00042 
00043 #include "msgCore.h"
00044 #include "nsMsgBaseCID.h"
00045 #include "nsIMsgHdr.h"
00046 #include "nsMsgFilterList.h"    // for kFileVersion
00047 #include "nsMsgFilter.h"
00048 #include "nsMsgUtils.h"
00049 #include "nsFileStream.h"
00050 #include "nsMsgLocalSearch.h"
00051 #include "nsMsgSearchTerm.h"
00052 #include "nsXPIDLString.h"
00053 #include "nsIMsgAccountManager.h"
00054 #include "nsIMsgIncomingServer.h"
00055 #include "nsMsgSearchValue.h"
00056 #include "nsReadableUtils.h"
00057 #include "nsEscape.h"
00058 #include "nsMsgI18N.h"
00059 #include "nsIImportService.h"
00060 #include "nsISupportsObsolete.h"
00061 #include "nsIOutputStream.h"
00062 #include "nsIStringBundle.h"
00063 #include "nsDateTimeFormatCID.h"
00064 
00065 static const char *kImapPrefix = "//imap:";
00066 static const char *kWhitespace = "\b\t\r\n ";
00067 static NS_DEFINE_CID(kDateTimeFormatCID,    NS_DATETIMEFORMAT_CID);
00068 
00069 nsMsgRuleAction::nsMsgRuleAction()
00070 {
00071 }
00072 
00073 nsMsgRuleAction::~nsMsgRuleAction()
00074 {
00075 }
00076 
00077 NS_IMPL_ISUPPORTS1(nsMsgRuleAction, nsIMsgRuleAction)
00078 
00079 NS_IMPL_GETSET(nsMsgRuleAction, Type, nsMsgRuleActionType, m_type)
00080 
00081 NS_IMETHODIMP nsMsgRuleAction::SetPriority(nsMsgPriorityValue aPriority)
00082 {
00083   NS_ENSURE_TRUE(m_type == nsMsgFilterAction::ChangePriority, 
00084                 NS_ERROR_ILLEGAL_VALUE);
00085   m_priority = aPriority;
00086   return NS_OK;
00087 }
00088 
00089 NS_IMETHODIMP
00090 nsMsgRuleAction::GetPriority(nsMsgPriorityValue *aResult)
00091 {
00092   NS_ENSURE_ARG_POINTER(aResult);
00093   NS_ENSURE_TRUE(m_type == nsMsgFilterAction::ChangePriority,
00094                  NS_ERROR_ILLEGAL_VALUE);
00095   *aResult = m_priority;
00096   return NS_OK;
00097 }
00098 
00099 NS_IMETHODIMP 
00100 nsMsgRuleAction::SetLabel(nsMsgLabelValue aLabel)
00101 {
00102   NS_ENSURE_TRUE(m_type == nsMsgFilterAction::Label,
00103                  NS_ERROR_ILLEGAL_VALUE);
00104   m_label = aLabel;
00105   return NS_OK;
00106 }
00107 
00108 NS_IMETHODIMP
00109 nsMsgRuleAction::GetLabel(nsMsgLabelValue *aResult)
00110 {
00111   NS_ENSURE_ARG_POINTER(aResult);
00112   NS_ENSURE_TRUE(m_type == nsMsgFilterAction::Label, NS_ERROR_ILLEGAL_VALUE);
00113   *aResult = m_label;
00114   return NS_OK;
00115 }
00116 
00117 NS_IMETHODIMP
00118 nsMsgRuleAction::SetTargetFolderUri(const char *aUri)
00119 {
00120   NS_ENSURE_ARG_POINTER(aUri);
00121   NS_ENSURE_TRUE(m_type == nsMsgFilterAction::MoveToFolder ||
00122                  m_type == nsMsgFilterAction::CopyToFolder,
00123                  NS_ERROR_ILLEGAL_VALUE);
00124   m_folderUri = aUri;
00125   return NS_OK;
00126 }
00127 
00128 NS_IMETHODIMP
00129 nsMsgRuleAction::GetTargetFolderUri(char** aResult)
00130 {
00131   NS_ENSURE_ARG_POINTER(aResult);
00132   NS_ENSURE_TRUE(m_type == nsMsgFilterAction::MoveToFolder ||
00133                  m_type == nsMsgFilterAction::CopyToFolder,
00134                  NS_ERROR_ILLEGAL_VALUE);
00135   *aResult = ToNewCString(m_folderUri);
00136   return NS_OK;
00137 }
00138 
00139 NS_IMETHODIMP 
00140 nsMsgRuleAction::SetJunkScore(PRInt32 aJunkScore)
00141 {
00142   NS_ENSURE_TRUE(m_type == nsMsgFilterAction::JunkScore && aJunkScore >= 0 && aJunkScore <= 100,
00143                  NS_ERROR_ILLEGAL_VALUE);
00144   m_junkScore = aJunkScore;
00145   return NS_OK;
00146 }
00147 
00148 NS_IMETHODIMP
00149 nsMsgRuleAction::GetJunkScore(PRInt32 *aResult)
00150 {
00151   NS_ENSURE_ARG_POINTER(aResult);
00152   NS_ENSURE_TRUE(m_type == nsMsgFilterAction::JunkScore, NS_ERROR_ILLEGAL_VALUE);
00153   *aResult = m_junkScore;
00154   return NS_OK;
00155 }
00156 
00157 NS_IMETHODIMP
00158 nsMsgRuleAction::SetStrValue(const char *aStrValue)
00159 {
00160   m_strValue = aStrValue;
00161   return NS_OK;
00162 }
00163 
00164 NS_IMETHODIMP
00165 nsMsgRuleAction::GetStrValue(char **aStrValue)
00166 {
00167   NS_ENSURE_ARG_POINTER(aStrValue);
00168   *aStrValue = ToNewCString(m_strValue);
00169   return NS_OK;
00170 }
00171 
00172 nsMsgFilter::nsMsgFilter():
00173     m_temporary(PR_FALSE),
00174     m_unparseable(PR_FALSE),
00175     m_filterList(nsnull),
00176     m_expressionTree(nsnull)
00177 {
00178   NS_NewISupportsArray(getter_AddRefs(m_termList));
00179   NS_NewISupportsArray(getter_AddRefs(m_actionList));
00180 
00181   m_type = nsMsgFilterType::InboxRule;
00182 }
00183 
00184 nsMsgFilter::~nsMsgFilter()
00185 {
00186   delete m_expressionTree;
00187 }
00188 
00189 NS_IMPL_ISUPPORTS1(nsMsgFilter, nsIMsgFilter)
00190 
00191 NS_IMPL_GETSET(nsMsgFilter, FilterType, nsMsgFilterTypeType, m_type)
00192 NS_IMPL_GETSET(nsMsgFilter, Enabled, PRBool, m_enabled)
00193 NS_IMPL_GETSET(nsMsgFilter, Temporary, PRBool, m_temporary)
00194 NS_IMPL_GETSET(nsMsgFilter, Unparseable, PRBool, m_unparseable)
00195 
00196 NS_IMETHODIMP nsMsgFilter::GetFilterName(PRUnichar **name)
00197 {
00198   NS_ENSURE_ARG_POINTER(name);  
00199   *name = ToNewUnicode(m_filterName);
00200   return NS_OK;
00201 }
00202 
00203 NS_IMETHODIMP nsMsgFilter::SetFilterName(const PRUnichar *name)
00204 {
00205   m_filterName.Assign(name);
00206   return NS_OK;
00207 }
00208 
00209 NS_IMETHODIMP nsMsgFilter::GetFilterDesc(char **description)
00210 {
00211   NS_ENSURE_ARG_POINTER(description);
00212   *description = ToNewCString(m_description);
00213   return NS_OK;
00214 }
00215 
00216 NS_IMETHODIMP nsMsgFilter::SetFilterDesc(const char *description)
00217 {
00218   m_description.Assign(description);
00219   return NS_OK;
00220 }
00221 
00222 NS_IMETHODIMP nsMsgFilter::GetUnparsedBuffer(char **unparsedBuffer)
00223 {
00224   NS_ENSURE_ARG_POINTER(unparsedBuffer);
00225   *unparsedBuffer = ToNewCString(m_unparsedBuffer);
00226   return NS_OK;
00227 }
00228 
00229 NS_IMETHODIMP nsMsgFilter::SetUnparsedBuffer(const char *unparsedBuffer)
00230 {
00231   m_unparsedBuffer.Assign(unparsedBuffer);
00232   return NS_OK;
00233 }
00234 
00235 NS_IMETHODIMP nsMsgFilter::AddTerm(     
00236                                    nsMsgSearchAttribValue attrib,    /* attribute for this term          */
00237                                    nsMsgSearchOpValue op,         /* operator e.g. opContains           */
00238                                    nsIMsgSearchValue *value,        /* value e.g. "Dogbert"               */
00239                                   PRBool BooleanAND,        /* PR_TRUE if AND is the boolean operator.
00240                                                             PR_FALSE if OR is the boolean operators */
00241                                   const char * arbitraryHeader)  /* arbitrary header specified by user.
00242                                   ignored unless attrib = attribOtherHeader */
00243 {
00244   return NS_OK;
00245 }
00246 
00247 NS_IMETHODIMP nsMsgFilter::AppendTerm(nsIMsgSearchTerm * aTerm)
00248 {
00249     NS_ENSURE_TRUE(aTerm, NS_ERROR_NULL_POINTER);
00250     // invalidate expression tree if we're changing the terms
00251     delete m_expressionTree;
00252     m_expressionTree = nsnull;
00253     return m_termList->AppendElement(NS_STATIC_CAST(nsISupports*,aTerm));
00254 }
00255 
00256 NS_IMETHODIMP
00257 nsMsgFilter::CreateTerm(nsIMsgSearchTerm **aResult)
00258 {
00259     nsMsgSearchTerm *term = new nsMsgSearchTerm;
00260     NS_ENSURE_TRUE(term, NS_ERROR_OUT_OF_MEMORY);
00261 
00262     *aResult = NS_STATIC_CAST(nsIMsgSearchTerm*,term);
00263     NS_ADDREF(*aResult);
00264     return NS_OK;
00265 }
00266 
00267 NS_IMETHODIMP
00268 nsMsgFilter::CreateAction(nsIMsgRuleAction **aAction)
00269 {
00270   NS_ENSURE_ARG_POINTER(aAction);
00271   nsMsgRuleAction *action = new nsMsgRuleAction;
00272   NS_ENSURE_TRUE(action, NS_ERROR_OUT_OF_MEMORY);
00273 
00274   *aAction = NS_STATIC_CAST(nsIMsgRuleAction*,action);
00275   NS_ADDREF(*aAction);
00276   return NS_OK;
00277 }
00278 
00279 NS_IMETHODIMP 
00280 nsMsgFilter::GetSortedActionList(nsISupportsArray *actionList)
00281 {
00282   NS_ENSURE_ARG_POINTER(actionList);
00283   PRUint32 numActions;
00284   nsresult err = m_actionList->Count(&numActions);
00285   NS_ENSURE_SUCCESS(err, err);
00286   PRBool insertedFinalAction = PR_FALSE;
00287   PRUint32 front = 0;
00288 
00289   for (PRUint32 index =0; index < numActions; index++)
00290   {
00291     nsCOMPtr<nsIMsgRuleAction> action;
00292     err = m_actionList->QueryElementAt(index, NS_GET_IID(nsIMsgRuleAction), (void **)getter_AddRefs(action));
00293     if (!action)
00294       continue;
00295  
00296     nsMsgRuleActionType actionType;
00297     action->GetType(&actionType);
00298     
00299     //we always want MoveToFolder action to be last (or delete to trash)
00300     if (actionType == nsMsgFilterAction::MoveToFolder || actionType == nsMsgFilterAction::Delete)
00301     {
00302       err = actionList->AppendElement(action);
00303       NS_ENSURE_SUCCESS(err, err);
00304       insertedFinalAction = PR_TRUE;
00305     }
00306     // Copy is always last, except for move/delete
00307     else if (actionType == nsMsgFilterAction::CopyToFolder)
00308     {
00309       if (!insertedFinalAction)
00310       {
00311         err = actionList->AppendElement(action);
00312         NS_ENSURE_SUCCESS(err, err);
00313       }
00314       else
00315       {
00316         // If we already have a move/delete action in place, we want to
00317         // place ourselves just before that final action.
00318         PRUint32 count;
00319         actionList->Count(&count);
00320         err = actionList->InsertElementAt(action, count - 2);
00321         NS_ENSURE_SUCCESS(err, err);
00322       }
00323     }
00324     else
00325     {
00326       actionList->InsertElementAt(action,front);
00327       // we always want FetchBodyFromPop3Server to be first
00328       if (actionType == nsMsgFilterAction::FetchBodyFromPop3Server)
00329         front = 1;
00330     }
00331   }
00332   return err;
00333 }
00334 
00335 NS_IMETHODIMP
00336 nsMsgFilter::AppendAction(nsIMsgRuleAction *aAction)
00337 {
00338   return m_actionList->AppendElement(NS_STATIC_CAST(nsISupports*,aAction));
00339 }
00340 
00341 NS_IMETHODIMP
00342 nsMsgFilter::GetActionAt(PRInt32 aIndex, nsIMsgRuleAction **aAction)
00343 {
00344   NS_ENSURE_ARG_POINTER(aAction);
00345   return m_actionList->QueryElementAt(aIndex, NS_GET_IID(nsIMsgRuleAction), 
00346                                        (void **) aAction);
00347 }
00348 
00349 NS_IMETHODIMP
00350 nsMsgFilter::GetActionList(nsISupportsArray **actionList)
00351 {
00352   NS_IF_ADDREF(*actionList = m_actionList);
00353   return NS_OK;
00354 }
00355 
00356 NS_IMETHODIMP  //for editing a filter
00357 nsMsgFilter::ClearActionList()
00358 {
00359   return m_actionList->Clear();
00360 }
00361 
00362 NS_IMETHODIMP nsMsgFilter::GetTerm(PRInt32 termIndex, 
00363                                    nsMsgSearchAttribValue *attrib,    /* attribute for this term          */
00364                                    nsMsgSearchOpValue *op,         /* operator e.g. opContains           */
00365                                    nsIMsgSearchValue **value,         /* value e.g. "Dogbert"               */
00366                                    PRBool *booleanAnd, /* PR_TRUE if AND is the boolean operator. PR_FALSE if OR is the boolean operator */
00367                                    char ** arbitraryHeader) /* arbitrary header specified by user.ignore unless attrib = attribOtherHeader */
00368 {
00369   nsresult rv;
00370   nsCOMPtr<nsIMsgSearchTerm> term;
00371   rv = m_termList->QueryElementAt(termIndex, NS_GET_IID(nsIMsgSearchTerm),
00372                                     (void **)getter_AddRefs(term));
00373   if (NS_SUCCEEDED(rv) && term)
00374   {
00375     if(attrib)
00376       term->GetAttrib(attrib);
00377     if(op)
00378       term->GetOp(op);
00379     if(value)
00380       term->GetValue(value);
00381     if(booleanAnd)
00382       term->GetBooleanAnd(booleanAnd);
00383     if (attrib && arbitraryHeader 
00384         && *attrib > nsMsgSearchAttrib::OtherHeader 
00385         && *attrib < nsMsgSearchAttrib::kNumMsgSearchAttributes)
00386       term->GetArbitraryHeader(arbitraryHeader);
00387   }
00388   return NS_OK;
00389 }
00390 
00391 NS_IMETHODIMP nsMsgFilter::GetSearchTerms(nsISupportsArray **aResult)
00392 {
00393     NS_ENSURE_ARG_POINTER(aResult);
00394     // caller can change m_termList, which can invalidate m_expressionTree.
00395     delete m_expressionTree;
00396     m_expressionTree = nsnull;
00397     NS_IF_ADDREF(*aResult = m_termList);
00398     return NS_OK;
00399 }
00400 
00401 NS_IMETHODIMP nsMsgFilter::SetSearchTerms(nsISupportsArray *aSearchList)
00402 {
00403     delete m_expressionTree;
00404     m_expressionTree = nsnull;
00405     m_termList = aSearchList;
00406     return NS_OK;
00407 }
00408 
00409 NS_IMETHODIMP nsMsgFilter::SetScope(nsIMsgSearchScopeTerm *aResult)
00410 {
00411     m_scope = aResult;
00412     return NS_OK;
00413 }
00414 
00415 NS_IMETHODIMP nsMsgFilter::GetScope(nsIMsgSearchScopeTerm **aResult)
00416 {
00417     NS_ENSURE_ARG_POINTER(aResult);
00418     NS_IF_ADDREF(*aResult = m_scope);
00419     return NS_OK;
00420 }
00421 
00422 #define LOG_ENTRY_START_TAG "<p>\n"
00423 #define LOG_ENTRY_START_TAG_LEN (strlen(LOG_ENTRY_START_TAG))
00424 #define LOG_ENTRY_END_TAG "</p>\n"
00425 #define LOG_ENTRY_END_TAG_LEN (strlen(LOG_ENTRY_END_TAG))
00426 
00427 NS_IMETHODIMP nsMsgFilter::LogRuleHit(nsIMsgRuleAction *aFilterAction, nsIMsgDBHdr *aMsgHdr)
00428 {
00429     NS_ENSURE_TRUE(m_filterList, NS_OK);
00430     nsCOMPtr <nsIOutputStream> logStream;
00431     nsresult rv = m_filterList->GetLogStream(getter_AddRefs(logStream));
00432     NS_ENSURE_SUCCESS(rv,rv);
00433   
00434     PRTime date;
00435     nsMsgRuleActionType actionType;
00436     
00437     nsXPIDLString authorValue;
00438     nsXPIDLString subjectValue;
00439     nsXPIDLString filterName;
00440     nsXPIDLString dateValue;
00441 
00442     GetFilterName(getter_Copies(filterName));
00443     aFilterAction->GetType(&actionType);
00444     (void)aMsgHdr->GetDate(&date);
00445     PRExplodedTime exploded;
00446     PR_ExplodeTime(date, PR_LocalTimeParameters, &exploded);
00447 
00448     if (!mDateFormatter)
00449     {
00450       mDateFormatter = do_CreateInstance(kDateTimeFormatCID, &rv);
00451       NS_ENSURE_SUCCESS(rv, rv);
00452       if (!mDateFormatter)
00453       {
00454         return NS_ERROR_FAILURE;
00455       }
00456     }
00457     mDateFormatter->FormatPRExplodedTime(nsnull, kDateFormatShort,
00458                                          kTimeFormatSeconds, &exploded, 
00459                                          dateValue);
00460   
00461     (void)aMsgHdr->GetMime2DecodedAuthor(getter_Copies(authorValue));
00462     (void)aMsgHdr->GetMime2DecodedSubject(getter_Copies(subjectValue));
00463 
00464     nsCString buffer;
00465     // this is big enough to hold a log entry.  
00466     // do this so we avoid growing and copying as we append to the log.
00467     buffer.SetCapacity(512);  
00468     
00469     nsCOMPtr<nsIStringBundleService> bundleService =
00470       do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
00471     NS_ENSURE_SUCCESS(rv, rv);
00472     
00473     nsCOMPtr<nsIStringBundle> bundle;
00474     rv = bundleService->CreateBundle("chrome://messenger/locale/filter.properties",
00475       getter_AddRefs(bundle));
00476     NS_ENSURE_SUCCESS(rv, rv);
00477     
00478     const PRUnichar *filterLogDetectFormatStrings[4] = { filterName.get(), authorValue.get(), subjectValue.get(), dateValue.get() };
00479     nsXPIDLString filterLogDetectStr;
00480     rv = bundle->FormatStringFromName(
00481       NS_LITERAL_STRING("filterLogDetectStr").get(),
00482       filterLogDetectFormatStrings, 4,
00483       getter_Copies(filterLogDetectStr));
00484     NS_ENSURE_SUCCESS(rv, rv);
00485     
00486     buffer += NS_ConvertUTF16toUTF8(filterLogDetectStr);
00487     buffer +=  "\n";
00488         
00489     if (actionType == nsMsgFilterAction::MoveToFolder ||
00490         actionType == nsMsgFilterAction::CopyToFolder)
00491     {
00492       nsXPIDLCString actionFolderUri;
00493       aFilterAction->GetTargetFolderUri(getter_Copies(actionFolderUri));
00494       NS_ConvertASCIItoUTF16 actionFolderUriValue(actionFolderUri);
00495          
00496       nsXPIDLCString msgId;
00497       aMsgHdr->GetMessageId(getter_Copies(msgId));
00498       NS_ConvertASCIItoUTF16 msgIdValue(msgId);
00499 
00500       const PRUnichar *logMoveFormatStrings[2] = { msgIdValue.get(), actionFolderUriValue.get() };
00501       nsXPIDLString logMoveStr;
00502       rv = bundle->FormatStringFromName(
00503         (actionType == nsMsgFilterAction::MoveToFolder) ?
00504           NS_LITERAL_STRING("logMoveStr").get() :
00505           NS_LITERAL_STRING("logCopyStr").get(),
00506         logMoveFormatStrings, 2,
00507         getter_Copies(logMoveStr));
00508       NS_ENSURE_SUCCESS(rv, rv);
00509       
00510       buffer += NS_ConvertUTF16toUTF8(logMoveStr);
00511     }
00512     else 
00513     {
00514       nsXPIDLString actionValue;
00515       nsAutoString filterActionID;
00516       filterActionID = NS_LITERAL_STRING("filterAction");
00517       filterActionID.AppendInt(actionType);
00518       rv = bundle->GetStringFromName(filterActionID.get(), getter_Copies(actionValue));
00519       NS_ENSURE_SUCCESS(rv, rv);
00520 
00521       buffer += NS_ConvertUTF16toUTF8(actionValue);
00522     }
00523     buffer += "\n";
00524     
00525     PRUint32 writeCount;
00526 
00527     rv = logStream->Write(LOG_ENTRY_START_TAG, LOG_ENTRY_START_TAG_LEN, &writeCount);
00528     NS_ENSURE_SUCCESS(rv,rv);
00529     NS_ASSERTION(writeCount == LOG_ENTRY_START_TAG_LEN, "failed to write out start log tag");
00530 
00531     // html escape the log for security reasons.
00532     // we don't want some to send us a message with a subject with
00533     // html tags, especially <script>
00534     char *escapedBuffer = nsEscapeHTML(buffer.get());
00535     if (!escapedBuffer)
00536       return NS_ERROR_OUT_OF_MEMORY;
00537 
00538     PRUint32 escapedBufferLen = strlen(escapedBuffer);
00539     rv = logStream->Write(escapedBuffer, escapedBufferLen, &writeCount);
00540     PR_Free(escapedBuffer);
00541     NS_ENSURE_SUCCESS(rv,rv);
00542     NS_ASSERTION(writeCount == escapedBufferLen, "failed to write out log hit");
00543  
00544     rv = logStream->Write(LOG_ENTRY_END_TAG, LOG_ENTRY_END_TAG_LEN, &writeCount);
00545     NS_ENSURE_SUCCESS(rv,rv);
00546     NS_ASSERTION(writeCount == LOG_ENTRY_END_TAG_LEN, "failed to write out end log tag");
00547     return NS_OK;
00548 }
00549 
00550 NS_IMETHODIMP 
00551 nsMsgFilter::MatchHdr(nsIMsgDBHdr *msgHdr, nsIMsgFolder *folder, 
00552                       nsIMsgDatabase *db, const char *headers, 
00553                       PRUint32 headersSize, PRBool *pResult)
00554 {
00555   NS_ENSURE_ARG_POINTER(folder);
00556   // use offlineMail because
00557   nsXPIDLCString folderCharset;
00558   folder->GetCharset(getter_Copies(folderCharset));
00559   nsresult rv = nsMsgSearchOfflineMail::MatchTermsForFilter(msgHdr, m_termList,
00560                   folderCharset.get(),  m_scope,  db,  headers,  headersSize, &m_expressionTree, pResult);
00561   return rv;
00562 }
00563 
00564 void
00565 nsMsgFilter::SetFilterList(nsIMsgFilterList *filterList)
00566 {
00567   // doesn't hold a ref.
00568   m_filterList = filterList;
00569 }
00570 
00571 nsresult
00572 nsMsgFilter::GetFilterList(nsIMsgFilterList **aResult)
00573 {
00574     NS_ENSURE_ARG_POINTER(aResult);
00575     NS_IF_ADDREF(*aResult = m_filterList);
00576     return NS_OK;
00577 }
00578 
00579 void nsMsgFilter::SetFilterScript(nsCString *fileName) 
00580 {
00581   m_scriptFileName = *fileName;
00582 }
00583 
00584 nsresult nsMsgFilter::ConvertMoveOrCopyToFolderValue(nsIMsgRuleAction *filterAction, nsCString &moveValue)
00585 {
00586   NS_ENSURE_ARG_POINTER(filterAction);
00587   PRInt16 filterVersion = kFileVersion;
00588   if (m_filterList)
00589       m_filterList->GetVersion(&filterVersion);
00590   if (filterVersion <= k60Beta1Version)
00591   {
00592     nsCOMPtr <nsIImportService> impSvc = do_GetService(NS_IMPORTSERVICE_CONTRACTID);
00593     NS_ASSERTION(impSvc, "cannot get importService");
00594     nsCOMPtr <nsIMsgFolder> rootFolder;
00595     nsXPIDLCString folderUri;
00596 
00597     m_filterList->GetFolder(getter_AddRefs(rootFolder));
00598 
00599          // if relative path starts with kImap, this is a move to folder on the same server
00600     if (moveValue.Find(kImapPrefix) == 0)
00601     {
00602       PRInt32 prefixLen = PL_strlen(kImapPrefix);
00603       nsCAutoString originalServerPath;
00604       moveValue.Mid(originalServerPath, prefixLen, moveValue.Length() - prefixLen);
00605       if ( filterVersion == k45Version && impSvc)
00606       {
00607         nsAutoString unicodeStr;
00608         impSvc->SystemStringToUnicode(originalServerPath.get(), unicodeStr);
00609         nsresult rv = CopyUTF16toMUTF7(unicodeStr, originalServerPath);
00610         NS_ENSURE_SUCCESS(rv, rv); 
00611       }
00612 
00613       nsCOMPtr <nsIMsgFolder> destIFolder;
00614       if (rootFolder)
00615       {
00616         rootFolder->FindSubFolder(originalServerPath, getter_AddRefs(destIFolder));
00617         if (destIFolder)
00618         {
00619           destIFolder->GetURI(getter_Copies(folderUri));
00620           filterAction->SetTargetFolderUri(folderUri);
00621           moveValue.Assign(folderUri);
00622         }
00623       }
00624     }
00625          else
00626     {
00627       // start off leaving the value the same.
00628       filterAction->SetTargetFolderUri(moveValue.get());
00629       nsresult rv = NS_OK;
00630       nsCOMPtr <nsIMsgFolder> localMailRoot;
00631       rootFolder->GetURI(getter_Copies(folderUri));
00632       // if the root folder is not imap, than the local mail root is the server root.
00633       // otherwise, it's the migrated local folders.
00634       if (nsCRT::strncmp("imap:", folderUri, 5))
00635       {
00636         localMailRoot = rootFolder;
00637       }
00638       else
00639       {
00640         nsCOMPtr<nsIMsgAccountManager> accountManager = 
00641                  do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
00642         if (NS_SUCCEEDED(rv))
00643         {
00644           nsCOMPtr <nsIMsgIncomingServer> server; 
00645           rv = accountManager->GetLocalFoldersServer(getter_AddRefs(server)); 
00646           if (NS_SUCCEEDED(rv) && server)
00647           {
00648             rv = server->GetRootFolder(getter_AddRefs(localMailRoot));
00649           }
00650         }
00651       }
00652       if (NS_SUCCEEDED(rv) && localMailRoot)
00653       {
00654         nsXPIDLCString localRootURI;
00655         nsCOMPtr <nsIMsgFolder> destIMsgFolder;
00656         nsCOMPtr <nsIMsgFolder> localMailRootMsgFolder = do_QueryInterface(localMailRoot);
00657         localMailRoot->GetURI(getter_Copies(localRootURI));
00658         nsCString destFolderUri;
00659         destFolderUri.Assign( localRootURI);
00660         // need to remove ".sbd" from moveValue, and perhaps escape it.
00661         moveValue.ReplaceSubstring(".sbd/", "/");
00662 
00663 #if defined(XP_MAC) || defined(XP_MACOSX)
00664         char *unescapedMoveValue = ToNewCString(moveValue);
00665         nsUnescape(unescapedMoveValue);
00666         moveValue.Assign(unescapedMoveValue);
00667         nsCRT::free(unescapedMoveValue);
00668 #endif
00669         destFolderUri.Append('/');
00670         if ( filterVersion == k45Version && impSvc)
00671         {
00672           nsAutoString unicodeStr;
00673           impSvc->SystemStringToUnicode(moveValue.get(), unicodeStr);
00674           rv = NS_MsgEscapeEncodeURLPath(unicodeStr, moveValue);
00675         }
00676         destFolderUri.Append(moveValue);
00677         localMailRootMsgFolder->GetChildWithURI (destFolderUri.get(), PR_TRUE, PR_FALSE /*caseInsensitive*/, getter_AddRefs(destIMsgFolder));
00678 
00679         if (destIMsgFolder)
00680         {
00681           destIMsgFolder->GetURI(getter_Copies(folderUri));
00682                     filterAction->SetTargetFolderUri(folderUri);
00683           moveValue.Assign(folderUri);
00684         }
00685       }
00686     }
00687   }
00688   else
00689     filterAction->SetTargetFolderUri(moveValue.get());
00690     
00691   return NS_OK;
00692        // set m_action.m_value.m_folderUri
00693 }
00694 
00695 nsresult nsMsgFilter::SaveToTextFile(nsIOFileStream *aStream)
00696 {
00697   NS_ENSURE_ARG_POINTER(aStream);
00698   if (m_unparseable)
00699   {
00700     //we need to trim leading whitespaces before filing out
00701     m_unparsedBuffer.Trim(kWhitespace, PR_TRUE /*leadingCharacters*/, PR_FALSE /*trailingCharacters*/);
00702     *aStream << m_unparsedBuffer.get();
00703     return NS_OK;
00704   }
00705   nsresult err = m_filterList->WriteWstrAttr(nsIMsgFilterList::attribName, m_filterName.get(), aStream);
00706   err = m_filterList->WriteBoolAttr(nsIMsgFilterList::attribEnabled, m_enabled, aStream);
00707   err = m_filterList->WriteStrAttr(nsIMsgFilterList::attribDescription, m_description.get(), aStream);
00708   err = m_filterList->WriteIntAttr(nsIMsgFilterList::attribType, m_type, aStream);
00709   if (IsScript())
00710     err = m_filterList->WriteStrAttr(nsIMsgFilterList::attribScriptFile, m_scriptFileName.get(), aStream);
00711   else
00712     err = SaveRule(aStream);
00713   return err;
00714 }
00715 
00716 nsresult nsMsgFilter::SaveRule(nsIOFileStream *aStream)
00717 {
00718   nsresult err = NS_OK;
00719   nsCOMPtr<nsIMsgFilterList> filterList;
00720   GetFilterList(getter_AddRefs(filterList));
00721   nsCAutoString      actionFilingStr;
00722    
00723   PRUint32 numActions;
00724   err = m_actionList->Count(&numActions);
00725   NS_ENSURE_SUCCESS(err, err);
00726 
00727   for (PRUint32 index =0; index < numActions; index++)
00728   {
00729     nsCOMPtr<nsIMsgRuleAction> action;
00730     err = m_actionList->QueryElementAt(index, NS_GET_IID(nsIMsgRuleAction), (void **)getter_AddRefs(action));
00731     if (!action)
00732       continue;
00733  
00734     nsMsgRuleActionType actionType;
00735     action->GetType(&actionType);
00736     GetActionFilingStr(actionType, actionFilingStr);
00737   
00738     err = filterList->WriteStrAttr(nsIMsgFilterList::attribAction, actionFilingStr.get(), aStream);
00739     NS_ENSURE_SUCCESS(err, err);
00740   
00741     switch(actionType)
00742     {
00743       case nsMsgFilterAction::MoveToFolder:
00744       case nsMsgFilterAction::CopyToFolder:
00745       {
00746         nsXPIDLCString imapTargetString;
00747         action->GetTargetFolderUri(getter_Copies(imapTargetString));
00748         err = filterList->WriteStrAttr(nsIMsgFilterList::attribActionValue, imapTargetString.get(), aStream);
00749       }
00750       break;
00751       case nsMsgFilterAction::ChangePriority:
00752       {
00753         nsMsgPriorityValue priorityValue;
00754         action->GetPriority(&priorityValue);
00755         nsCAutoString priority;
00756         NS_MsgGetUntranslatedPriorityName(priorityValue, priority);
00757         err = filterList->WriteStrAttr(
00758                 nsIMsgFilterList::attribActionValue, priority.get(), aStream);
00759       }
00760       break;
00761       case nsMsgFilterAction::Label:
00762       {
00763         nsMsgLabelValue label;
00764         action->GetLabel(&label);
00765         err = filterList->WriteIntAttr(nsIMsgFilterList::attribActionValue, label, aStream);
00766       }
00767       break;
00768       case nsMsgFilterAction::JunkScore:
00769       {
00770         PRInt32 junkScore;
00771         action->GetJunkScore(&junkScore);
00772         err = filterList->WriteIntAttr(nsIMsgFilterList::attribActionValue, junkScore, aStream);
00773       }
00774       break;
00775       case nsMsgFilterAction::AddTag:
00776       case nsMsgFilterAction::Reply:
00777       case nsMsgFilterAction::Forward:
00778       {
00779         nsXPIDLCString strValue;
00780         action->GetStrValue(getter_Copies(strValue));
00781         // strValue is e-mail address
00782         err = filterList->WriteStrAttr(nsIMsgFilterList::attribActionValue, strValue.get(), aStream);
00783       }
00784       break;
00785       default:
00786         break;
00787     }
00788   }
00789   // and here the fun begins - file out term list...
00790   PRUint32 searchIndex;
00791   nsCAutoString  condition;
00792   PRUint32 count;
00793   m_termList->Count(&count);
00794   for (searchIndex = 0; searchIndex < count && NS_SUCCEEDED(err);
00795         searchIndex++)
00796   {
00797     nsCAutoString    stream;
00798   
00799     nsCOMPtr<nsIMsgSearchTerm> term;
00800     m_termList->QueryElementAt(searchIndex, NS_GET_IID(nsIMsgSearchTerm),
00801       (void **)getter_AddRefs(term));
00802     if (!term)
00803       continue;
00804   
00805     if (condition.Length() > 1)
00806       condition += ' ';
00807   
00808     PRBool booleanAnd;
00809     PRBool matchAll;
00810     term->GetBooleanAnd(&booleanAnd);
00811     term->GetMatchAll(&matchAll);
00812     if (matchAll)
00813     {
00814       condition += "ALL";
00815       break;
00816     }
00817     else if (booleanAnd)
00818       condition += "AND (";
00819     else
00820       condition += "OR (";
00821   
00822     nsresult searchError = term->GetTermAsString(stream);
00823     if (NS_FAILED(searchError))
00824     {
00825       err = searchError;
00826       break;
00827     }
00828   
00829     condition += stream;
00830     condition += ')';
00831   }
00832   if (NS_SUCCEEDED(err))
00833     err = filterList->WriteStrAttr(nsIMsgFilterList::attribCondition, condition.get(), aStream);
00834   return err;
00835 }
00836 
00837 // for each action, this table encodes the filterTypes that support the action.
00838 struct RuleActionsTableEntry
00839 {
00840        nsMsgRuleActionType  action;
00841        nsMsgFilterTypeType         supportedTypes;
00842        PRInt32                            xp_strIndex;
00843        const char                  *actionFilingStr;    /* used for filing out filters, don't translate! */
00844 };
00845 
00846 static struct RuleActionsTableEntry ruleActionsTable[] =
00847 {
00848   { nsMsgFilterAction::MoveToFolder,    nsMsgFilterType::Inbox, 0,  "Move to folder"},
00849   { nsMsgFilterAction::CopyToFolder,    nsMsgFilterType::Inbox, 0,  "Copy to folder"},
00850   { nsMsgFilterAction::ChangePriority,  nsMsgFilterType::Inbox, 0,  "Change priority"},
00851   { nsMsgFilterAction::Delete,          nsMsgFilterType::All,   0,  "Delete"},
00852   { nsMsgFilterAction::MarkRead,        nsMsgFilterType::All,   0,  "Mark read"},
00853   { nsMsgFilterAction::KillThread,      nsMsgFilterType::All,   0,  "Ignore thread"},
00854   { nsMsgFilterAction::WatchThread,     nsMsgFilterType::All,   0,  "Watch thread"},
00855   { nsMsgFilterAction::MarkFlagged,     nsMsgFilterType::All,   0,  "Mark flagged"},
00856   { nsMsgFilterAction::Label,           nsMsgFilterType::All,   0,  "Label"},
00857   { nsMsgFilterAction::Reply,           nsMsgFilterType::All,   0,  "Reply"},
00858   { nsMsgFilterAction::Forward,         nsMsgFilterType::All,   0,  "Forward"},
00859   { nsMsgFilterAction::StopExecution,   nsMsgFilterType::All,   0,  "Stop execution"},
00860   { nsMsgFilterAction::DeleteFromPop3Server, nsMsgFilterType::Inbox,   0, "Delete from Pop3 server"},
00861   { nsMsgFilterAction::LeaveOnPop3Server, nsMsgFilterType::Inbox,   0, "Leave on Pop3 server"},
00862   { nsMsgFilterAction::JunkScore, nsMsgFilterType::All,   0, "JunkScore"},
00863   { nsMsgFilterAction::FetchBodyFromPop3Server, nsMsgFilterType::Inbox,   0, "Fetch body from Pop3Server"},
00864   { nsMsgFilterAction::AddTag,          nsMsgFilterType::All,   0,  "AddTag"},
00865 };
00866 
00867 const char *nsMsgFilter::GetActionStr(nsMsgRuleActionType action)
00868 {
00869   int  numActions = sizeof(ruleActionsTable) / sizeof(ruleActionsTable[0]);
00870   
00871   for (int i = 0; i < numActions; i++)
00872   {
00873     if (action == ruleActionsTable[i].action)
00874       return ruleActionsTable[i].actionFilingStr; 
00875   }
00876   return "";
00877 }
00878 /*static */nsresult nsMsgFilter::GetActionFilingStr(nsMsgRuleActionType action, nsCString &actionStr)
00879 {
00880   int  numActions = sizeof(ruleActionsTable) / sizeof(ruleActionsTable[0]);
00881   
00882   for (int i = 0; i < numActions; i++)
00883   {
00884     if (action == ruleActionsTable[i].action)
00885     {
00886       actionStr = ruleActionsTable[i].actionFilingStr;
00887       return NS_OK;
00888     }
00889   }
00890   return NS_ERROR_INVALID_ARG;
00891 }
00892 
00893 
00894 nsMsgRuleActionType nsMsgFilter::GetActionForFilingStr(nsCString &actionStr)
00895 {
00896   int  numActions = sizeof(ruleActionsTable) / sizeof(ruleActionsTable[0]);
00897   
00898   for (int i = 0; i < numActions; i++)
00899   {
00900     if (actionStr.Equals(ruleActionsTable[i].actionFilingStr))
00901       return ruleActionsTable[i].action;
00902   }
00903   return nsMsgFilterAction::None;
00904 }
00905 
00906 PRInt16
00907 nsMsgFilter::GetVersion()
00908 {
00909     if (!m_filterList) return 0;
00910     PRInt16 version;
00911     m_filterList->GetVersion(&version);
00912     return version;
00913 }
00914 
00915 #ifdef DEBUG
00916 void nsMsgFilter::Dump()
00917 {
00918   nsCAutoString s;
00919   CopyUCS2toASCII(m_filterName, s);
00920   printf("filter %s type = %c desc = %s\n", s.get(), m_type + '0', m_description.get());
00921 }
00922 #endif
00923