Back to index

lightning-sunbird  0.9+nobinonly
nsMsgFilterService.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 // this file implements the nsMsgFilterService interface 
00041 
00042 #include "msgCore.h"
00043 #include "nsMsgFilterService.h"
00044 #include "nsFileStream.h"
00045 #include "nsMsgFilterList.h"
00046 #include "nsSpecialSystemDirectory.h"
00047 #include "nsIPrompt.h"
00048 #include "nsIDocShell.h"
00049 #include "nsIMsgWindow.h"
00050 #include "nsIInterfaceRequestor.h"
00051 #include "nsIInterfaceRequestorUtils.h"
00052 #include "nsIStringBundle.h"
00053 #include "nsIMsgSearchNotify.h"
00054 #include "nsIUrlListener.h"
00055 #include "nsIMsgCopyServiceListener.h"
00056 #include "nsIMsgLocalMailFolder.h"
00057 #include "nsIMsgDatabase.h"
00058 #include "nsIMsgHdr.h"
00059 #include "nsIDBFolderInfo.h"
00060 #include "nsIRDFService.h"
00061 #include "nsMsgBaseCID.h"
00062 #include "nsIMsgCopyService.h"
00063 #include "nsIOutputStream.h"
00064 #include "nsIMsgComposeService.h"
00065 #include "nsMsgCompCID.h"
00066 
00067 NS_IMPL_ISUPPORTS1(nsMsgFilterService, nsIMsgFilterService)
00068 
00069 nsMsgFilterService::nsMsgFilterService()
00070 {
00071 }
00072 
00073 nsMsgFilterService::~nsMsgFilterService()
00074 {
00075 }
00076 
00077 NS_IMETHODIMP nsMsgFilterService::OpenFilterList(nsIFileSpec *filterFile, nsIMsgFolder *rootFolder, nsIMsgWindow *aMsgWindow, nsIMsgFilterList **resultFilterList)
00078 {
00079        nsresult ret = NS_OK;
00080 
00081     nsFileSpec filterSpec;
00082     filterFile->GetFileSpec(&filterSpec);
00083        nsIOFileStream *fileStream = new nsIOFileStream(filterSpec);
00084        if (!fileStream)
00085               return NS_ERROR_OUT_OF_MEMORY;
00086 
00087        nsMsgFilterList *filterList = new nsMsgFilterList();
00088        if (!filterList)
00089               return NS_ERROR_OUT_OF_MEMORY;
00090        NS_ADDREF(filterList);
00091     filterList->SetFolder(rootFolder);
00092     
00093     // temporarily tell the filter where it's file path is
00094     filterList->SetDefaultFile(filterFile);
00095     
00096     PRUint32 size;
00097     ret = filterFile->GetFileSize(&size);
00098        if (NS_SUCCEEDED(ret) && size > 0)
00099               ret = filterList->LoadTextFilters(fileStream);
00100   fileStream->close();
00101   delete fileStream;
00102   fileStream =nsnull;
00103        if (NS_SUCCEEDED(ret))
00104   {
00105               *resultFilterList = filterList;
00106         PRInt16 version;
00107         filterList->GetVersion(&version);
00108     if (version != kFileVersion)
00109     {
00110 
00111       SaveFilterList(filterList, filterFile);
00112     }
00113   }
00114        else
00115   {
00116     NS_RELEASE(filterList);
00117     if (ret == NS_MSG_FILTER_PARSE_ERROR && aMsgWindow)
00118     {
00119       ret = BackUpFilterFile(filterFile, aMsgWindow);
00120       NS_ENSURE_SUCCESS(ret, ret);
00121       ret = filterFile->Truncate(0);
00122       NS_ENSURE_SUCCESS(ret, ret);
00123       return OpenFilterList(filterFile, rootFolder, aMsgWindow, resultFilterList);
00124     }
00125     else if(ret == NS_MSG_CUSTOM_HEADERS_OVERFLOW && aMsgWindow)
00126       ThrowAlertMsg("filterCustomHeaderOverflow", aMsgWindow);
00127     else if(ret == NS_MSG_INVALID_CUSTOM_HEADER && aMsgWindow)
00128       ThrowAlertMsg("invalidCustomHeader", aMsgWindow);
00129   }
00130        return ret;
00131 }
00132 
00133 NS_IMETHODIMP nsMsgFilterService::CloseFilterList(nsIMsgFilterList *filterList)
00134 {
00135        //NS_ASSERTION(PR_FALSE,"CloseFilterList doesn't do anything yet");
00136        return NS_OK;
00137 }
00138 
00139 /* save without deleting */
00140 NS_IMETHODIMP nsMsgFilterService::SaveFilterList(nsIMsgFilterList *filterList, nsIFileSpec *filterFile)
00141 {
00142   NS_ENSURE_ARG_POINTER(filterFile);
00143   NS_ENSURE_ARG_POINTER(filterList);
00144 
00145   PRBool tmpExists;
00146   nsresult rv;
00147 
00148   nsresult ret = NS_OK;
00149   nsCOMPtr <nsIFileSpec> tmpFiltersFile;
00150   nsCOMPtr <nsIFileSpec> realFiltersFile;
00151   nsCOMPtr <nsIFileSpec> parentDir;
00152 
00153 
00154   nsSpecialSystemDirectory tmpFile(nsSpecialSystemDirectory::OS_TemporaryDirectory);
00155   tmpFile += "tmprules.dat";
00156 
00157   ret = NS_NewFileSpecWithSpec(tmpFile, getter_AddRefs(tmpFiltersFile));
00158 
00159   NS_ASSERTION(NS_SUCCEEDED(ret),"writing filters file: failed to append filename");
00160   if (NS_FAILED(ret)) 
00161     return ret;
00162 
00163   ret = tmpFiltersFile->MakeUnique();  //need a unique tmp file to prevent dataloss in multiuser environment
00164   NS_ENSURE_SUCCESS(ret, ret);
00165 
00166   nsFileSpec tmpFileSpec;
00167   tmpFiltersFile->GetFileSpec(&tmpFileSpec);
00168 
00169        nsIOFileStream *tmpFileStream = nsnull;
00170   
00171   if (NS_SUCCEEDED(ret))
00172     ret = filterFile->GetParent(getter_AddRefs(parentDir));
00173 
00174   if (NS_SUCCEEDED(ret))
00175     tmpFileStream = new nsIOFileStream(tmpFileSpec);
00176        if (!tmpFileStream)
00177               return NS_ERROR_OUT_OF_MEMORY;
00178   ret = filterList->SaveToFile(tmpFileStream);
00179   tmpFileStream->close();
00180   delete tmpFileStream;
00181   tmpFileStream = nsnull;
00182   
00183   if (NS_SUCCEEDED(ret))
00184   {
00185     // can't move across drives
00186     ret = tmpFiltersFile->CopyToDir(parentDir);
00187     if (NS_SUCCEEDED(ret))
00188     {
00189       filterFile->Delete(PR_FALSE);
00190       nsXPIDLCString tmpFileName;
00191       tmpFiltersFile->GetLeafName(getter_Copies(tmpFileName));
00192       parentDir->AppendRelativeUnixPath(tmpFileName.get());
00193       nsXPIDLCString finalLeafName;
00194       filterFile->GetLeafName(getter_Copies(finalLeafName));
00195       if (!finalLeafName.IsEmpty())
00196         parentDir->Rename(finalLeafName);
00197       else // fall back to msgFilterRules.dat
00198         parentDir->Rename("msgFilterRules.dat");
00199     }
00200 
00201   }
00202   NS_ASSERTION(NS_SUCCEEDED(ret), "error opening/saving filter list");
00203   
00204   rv = tmpFiltersFile->Exists(&tmpExists);
00205   if (NS_SUCCEEDED(rv) && tmpExists)
00206     tmpFiltersFile->Delete(PR_FALSE);
00207 
00208        return ret;
00209 }
00210 
00211 NS_IMETHODIMP nsMsgFilterService::CancelFilterList(nsIMsgFilterList *filterList)
00212 { 
00213        return NS_ERROR_NOT_IMPLEMENTED;
00214 }
00215 
00216 nsresult nsMsgFilterService::BackUpFilterFile(nsIFileSpec *aFilterFile, nsIMsgWindow *aMsgWindow)
00217 {
00218   nsresult rv;
00219   AlertBackingUpFilterFile(aMsgWindow);
00220   aFilterFile->CloseStream();
00221 
00222   nsCOMPtr<nsILocalFile> localFilterFile;
00223   nsFileSpec filterFileSpec;
00224   aFilterFile->GetFileSpec(&filterFileSpec);
00225   rv = NS_FileSpecToIFile(&filterFileSpec, getter_AddRefs(localFilterFile));
00226   NS_ENSURE_SUCCESS(rv,rv);
00227 
00228   nsCOMPtr<nsILocalFile> localParentDir;
00229   nsCOMPtr <nsIFileSpec> parentDir;
00230   rv = aFilterFile->GetParent(getter_AddRefs(parentDir));
00231   NS_ENSURE_SUCCESS(rv,rv);
00232 
00233   nsFileSpec parentDirSpec;
00234   parentDir->GetFileSpec(&parentDirSpec);
00235 
00236   rv = NS_FileSpecToIFile(&parentDirSpec, getter_AddRefs(localParentDir));
00237   NS_ENSURE_SUCCESS(rv,rv);
00238 
00239   //if back-up file exists delete the back up file otherwise copy fails. 
00240   nsCOMPtr <nsILocalFile> backupFile;
00241   rv = NS_FileSpecToIFile(&parentDirSpec, getter_AddRefs(backupFile));
00242   NS_ENSURE_SUCCESS(rv,rv);
00243   backupFile->AppendNative(NS_LITERAL_CSTRING("rulesbackup.dat"));
00244   PRBool exists;
00245   backupFile->Exists(&exists);
00246   if (exists)
00247     backupFile->Remove(PR_FALSE);
00248 
00249   return localFilterFile->CopyToNative(localParentDir, NS_LITERAL_CSTRING("rulesbackup.dat"));
00250 }
00251 
00252 nsresult nsMsgFilterService::AlertBackingUpFilterFile(nsIMsgWindow *aMsgWindow)
00253 {
00254   return ThrowAlertMsg("filterListBackUpMsg", aMsgWindow);
00255 }
00256 
00257 nsresult //Do not use this routine if you have to call it very often because it creates a new bundle each time
00258 nsMsgFilterService::GetStringFromBundle(const char *aMsgName, PRUnichar **aResult)
00259 { 
00260   nsresult rv=NS_OK;
00261   NS_ENSURE_ARG_POINTER(aResult);
00262   nsCOMPtr <nsIStringBundle> bundle;
00263   rv = GetFilterStringBundle(getter_AddRefs(bundle));
00264   if (NS_SUCCEEDED(rv) && bundle)
00265     rv=bundle->GetStringFromName(NS_ConvertASCIItoUCS2(aMsgName).get(), aResult);
00266   return rv;
00267   
00268 }
00269 
00270 nsresult
00271 nsMsgFilterService::GetFilterStringBundle(nsIStringBundle **aBundle)
00272 {
00273   nsresult rv=NS_OK;
00274   NS_ENSURE_ARG_POINTER(aBundle);
00275   nsCOMPtr<nsIStringBundleService> bundleService =
00276          do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
00277   nsCOMPtr<nsIStringBundle> bundle;
00278   if (bundleService && NS_SUCCEEDED(rv))
00279     bundleService->CreateBundle("chrome://messenger/locale/filter.properties",
00280                                  getter_AddRefs(bundle));
00281   NS_IF_ADDREF(*aBundle = bundle);
00282   return rv;
00283 }
00284 
00285 nsresult
00286 nsMsgFilterService::ThrowAlertMsg(const char*aMsgName, nsIMsgWindow *aMsgWindow)
00287 {
00288   nsXPIDLString alertString;
00289   nsresult rv = GetStringFromBundle(aMsgName, getter_Copies(alertString));
00290   if (NS_SUCCEEDED(rv) && alertString && aMsgWindow)
00291   {
00292     nsCOMPtr <nsIDocShell> docShell;
00293     aMsgWindow->GetRootDocShell(getter_AddRefs(docShell));
00294     if (docShell)
00295     {
00296       nsCOMPtr<nsIPrompt> dialog(do_GetInterface(docShell));
00297       if (dialog && alertString)
00298         dialog->Alert(nsnull, alertString);
00299     }
00300   }
00301   return rv;
00302 }
00303 
00304 // this class is used to run filters after the fact, i.e., after new mail has been downloaded from the server.
00305 // It can do the following:
00306 // 1. Apply a single imap or pop3 filter on a single folder.
00307 // 2. Apply multiple filters on a single imap or pop3 folder.
00308 // 3. Apply a single filter on multiple imap or pop3 folders in the same account.
00309 // 4. Apply multiple filters on multiple imap or pop3 folders in the same account.
00310 // This will be called from the front end js code in the case of the apply filters to folder menu code, 
00311 // and from the filter dialog js code with the run filter now command.
00312 
00313 
00314 // this class holds the list of filters and folders, and applies them in turn, first iterating
00315 // over all the filters on one folder, and then advancing to the next folder and repeating. 
00316 // For each filter,we take the filter criteria and create a search term list. Then, we execute the search.
00317 // We are a search listener so that we can build up the list of search hits. 
00318 // Then, when the search is done, we will apply the filter action(s) en-masse, so, for example, if the action is a move, 
00319 // we calls one method to move all the messages to the destination folder. Or, mark all the messages read.
00320 // In the case of imap operations, or imap/local  moves, the action will be asynchronous, so we'll need to be a url listener 
00321 // as well, and kick off the next filter when the action completes.
00322 class nsMsgFilterAfterTheFact : public nsIUrlListener, public nsIMsgSearchNotify, public nsIMsgCopyServiceListener
00323 {
00324 public:
00325   nsMsgFilterAfterTheFact(nsIMsgWindow *aMsgWindow, nsIMsgFilterList *aFilterList, nsISupportsArray *aFolderList);
00326   virtual ~nsMsgFilterAfterTheFact();
00327   NS_DECL_ISUPPORTS
00328   NS_DECL_NSIURLLISTENER
00329   NS_DECL_NSIMSGSEARCHNOTIFY
00330   NS_DECL_NSIMSGCOPYSERVICELISTENER
00331 
00332   nsresult  AdvanceToNextFolder();  // kicks off the process
00333 protected:
00334   nsresult  RunNextFilter();
00335   nsresult  ApplyFilter();
00336   nsresult  OnEndExecution(nsresult executionStatus); // do what we have to do to cleanup.
00337   PRBool    ContinueExecutionPrompt();
00338   nsresult  DisplayConfirmationPrompt(nsIMsgWindow *msgWindow, const PRUnichar *confirmString, PRBool *confirmed);
00339   nsCOMPtr <nsIMsgWindow>     m_msgWindow;
00340   nsCOMPtr <nsIMsgFilterList> m_filters;
00341   nsCOMPtr <nsISupportsArray> m_folders;
00342   nsCOMPtr <nsIMsgFolder>     m_curFolder;
00343   nsCOMPtr <nsIMsgDatabase>   m_curFolderDB;
00344   nsCOMPtr <nsIMsgFilter>     m_curFilter;
00345   PRUint32                    m_curFilterIndex;
00346   PRUint32                    m_curFolderIndex;
00347   PRUint32                    m_numFilters;
00348   PRUint32                    m_numFolders;
00349   nsMsgKeyArray               m_searchHits;
00350   nsCOMPtr <nsISupportsArray> m_searchHitHdrs;
00351   nsCOMPtr <nsIMsgSearchSession> m_searchSession;
00352 };
00353 
00354 NS_IMPL_ISUPPORTS3(nsMsgFilterAfterTheFact, nsIUrlListener, nsIMsgSearchNotify, nsIMsgCopyServiceListener)
00355 
00356 nsMsgFilterAfterTheFact::nsMsgFilterAfterTheFact(nsIMsgWindow *aMsgWindow, nsIMsgFilterList *aFilterList, nsISupportsArray *aFolderList)
00357 {
00358   m_curFilterIndex = m_curFolderIndex = 0;
00359   m_msgWindow = aMsgWindow;
00360   m_filters = aFilterList;
00361   m_folders = aFolderList;
00362   m_filters->GetFilterCount(&m_numFilters);
00363   m_folders->Count(&m_numFolders);
00364 
00365   NS_ADDREF(this); // we own ourselves, and will release ourselves when execution is done.
00366 
00367   NS_NewISupportsArray(getter_AddRefs(m_searchHitHdrs));
00368 }
00369 
00370 nsMsgFilterAfterTheFact::~nsMsgFilterAfterTheFact()
00371 {
00372 }
00373 
00374 // do what we have to do to cleanup.
00375 nsresult nsMsgFilterAfterTheFact::OnEndExecution(nsresult executionStatus)
00376 {
00377   if (m_searchSession)
00378     m_searchSession->UnregisterListener(this);
00379 
00380   if (m_filters)
00381     (void)m_filters->FlushLogIfNecessary();
00382 
00383   Release(); // release ourselves.
00384   return executionStatus;
00385 }
00386 
00387 nsresult nsMsgFilterAfterTheFact::RunNextFilter()
00388 {
00389   nsresult rv;
00390   if (m_curFilterIndex >= m_numFilters)
00391     return AdvanceToNextFolder();
00392 
00393   rv = m_filters->GetFilterAt(m_curFilterIndex++, getter_AddRefs(m_curFilter));
00394   NS_ENSURE_SUCCESS(rv, rv);
00395 
00396   nsCOMPtr <nsISupportsArray> searchTerms;
00397   rv = m_curFilter->GetSearchTerms(getter_AddRefs(searchTerms));
00398   NS_ENSURE_SUCCESS(rv, rv);
00399   if (m_searchSession)
00400     m_searchSession->UnregisterListener(this);
00401   m_searchSession = do_CreateInstance(NS_MSGSEARCHSESSION_CONTRACTID, &rv);
00402   NS_ENSURE_SUCCESS(rv, rv);
00403 
00404   nsMsgSearchScopeValue searchScope = nsMsgSearchScope::offlineMail;
00405   PRUint32 termCount;
00406   searchTerms->Count(&termCount);
00407   for (PRUint32 termIndex = 0; termIndex < termCount; termIndex++)
00408   {
00409     nsCOMPtr <nsIMsgSearchTerm> term;
00410     rv = searchTerms->QueryElementAt(termIndex, NS_GET_IID(nsIMsgSearchTerm), getter_AddRefs(term));
00411     NS_ENSURE_SUCCESS(rv, rv);
00412     rv = m_searchSession->AppendTerm(term);
00413     NS_ENSURE_SUCCESS(rv, rv);
00414   }
00415   m_searchSession->RegisterListener(this);
00416 
00417   rv = m_searchSession->AddScopeTerm(searchScope, m_curFolder);
00418   NS_ENSURE_SUCCESS(rv, rv);
00419   // it's possible that this error handling will need to be rearranged when mscott lands the UI for
00420   // doing filters based on sender in PAB, because we can't do that for IMAP. I believe appending the
00421   // search term will fail, or the Search itself will fail synchronously. In that case, we'll
00422   // have to ignore the filter, I believe. Ultimately, we'd like to re-work the search backend
00423   // so that it can do this.
00424   return m_searchSession->Search(m_msgWindow);
00425 }
00426 
00427 nsresult nsMsgFilterAfterTheFact::AdvanceToNextFolder()
00428 {
00429   if (m_curFolderIndex >= m_numFolders)
00430     return OnEndExecution(NS_OK);
00431 
00432   nsresult rv = m_folders->QueryElementAt(m_curFolderIndex++, NS_GET_IID(nsIMsgFolder), getter_AddRefs(m_curFolder));
00433   NS_ENSURE_SUCCESS(rv, rv);
00434   nsCOMPtr <nsIDBFolderInfo> dbFolderInfo;
00435   rv = m_curFolder->GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo), getter_AddRefs(m_curFolderDB));
00436   if (rv == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING || rv == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE)
00437   {
00438     nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(m_curFolder, &rv);
00439     if (NS_SUCCEEDED(rv) && localFolder)
00440       return localFolder->ParseFolder(m_msgWindow, this);
00441   }
00442   return RunNextFilter();
00443 }
00444 
00445 NS_IMETHODIMP nsMsgFilterAfterTheFact::OnStartRunningUrl(nsIURI *aUrl)
00446 {
00447   return NS_OK;
00448 }
00449 
00450 NS_IMETHODIMP nsMsgFilterAfterTheFact::OnStopRunningUrl(nsIURI *aUrl, nsresult aExitCode)
00451 {
00452   PRBool continueExecution = NS_SUCCEEDED(aExitCode);
00453   if (!continueExecution)
00454     continueExecution = ContinueExecutionPrompt();
00455 
00456   return (continueExecution) ? RunNextFilter() : OnEndExecution(aExitCode);
00457 }
00458 
00459 NS_IMETHODIMP nsMsgFilterAfterTheFact::OnSearchHit(nsIMsgDBHdr *header, nsIMsgFolder *folder)
00460 {
00461   NS_ENSURE_ARG_POINTER(header);
00462   nsMsgKey msgKey;
00463   header->GetMessageKey(&msgKey);
00464   m_searchHits.Add(msgKey);
00465   m_searchHitHdrs->AppendElement(header);
00466   return NS_OK;
00467 }
00468 
00469 NS_IMETHODIMP nsMsgFilterAfterTheFact::OnSearchDone(nsresult status)
00470 {
00471   nsresult rv = status;
00472   PRBool continueExecution = NS_SUCCEEDED(status);
00473   if (!continueExecution)
00474     continueExecution = ContinueExecutionPrompt();
00475 
00476   if (continueExecution)
00477     return (m_searchHits.GetSize() > 0) ? ApplyFilter() : RunNextFilter();
00478   else
00479     return OnEndExecution(rv);
00480 }
00481 
00482 NS_IMETHODIMP nsMsgFilterAfterTheFact::OnNewSearch()
00483 {
00484   m_searchHits.RemoveAll();
00485   m_searchHitHdrs->Clear();
00486   return NS_OK;
00487 }
00488 
00489 nsresult nsMsgFilterAfterTheFact::ApplyFilter()
00490 {
00491   nsresult rv = NS_OK;
00492   if (m_curFilter && m_curFolder)
00493   {
00494     // we're going to log the filter actions before firing them because some actions are async
00495     PRBool loggingEnabled = PR_FALSE;
00496     if (m_filters)
00497       (void)m_filters->GetLoggingEnabled(&loggingEnabled);
00498 
00499     nsCOMPtr<nsISupportsArray> actionList;
00500     rv = NS_NewISupportsArray(getter_AddRefs(actionList));
00501     NS_ENSURE_SUCCESS(rv, rv);
00502     rv = m_curFilter->GetSortedActionList(actionList);
00503     NS_ENSURE_SUCCESS(rv, rv);
00504     PRUint32 numActions;
00505     actionList->Count(&numActions);
00506     PRBool applyMoreActions = PR_TRUE;
00507 
00508     for (PRUint32 actionIndex =0; actionIndex < numActions && applyMoreActions; actionIndex++)
00509     {
00510       nsCOMPtr<nsIMsgRuleAction> filterAction;
00511       actionList->QueryElementAt(actionIndex, NS_GET_IID(nsIMsgRuleAction), (void **)getter_AddRefs(filterAction));
00512       nsMsgRuleActionType actionType;
00513       if (filterAction)
00514         filterAction->GetType(&actionType);
00515       else
00516         continue;
00517       
00518       nsXPIDLCString actionTargetFolderUri;
00519       if (actionType == nsMsgFilterAction::MoveToFolder ||
00520           actionType == nsMsgFilterAction::CopyToFolder)
00521       {
00522         filterAction->GetTargetFolderUri(getter_Copies(actionTargetFolderUri));
00523         if (actionTargetFolderUri.IsEmpty())
00524         {
00525           NS_ASSERTION(PR_FALSE, "actionTargetFolderUri is empty");
00526           continue;
00527         }
00528       }
00529 
00530       if (loggingEnabled) 
00531       {
00532           for (PRUint32 msgIndex = 0; msgIndex < m_searchHits.GetSize(); msgIndex++)
00533           {
00534             nsCOMPtr <nsIMsgDBHdr> msgHdr;
00535             m_searchHitHdrs->QueryElementAt(msgIndex, NS_GET_IID(nsIMsgDBHdr), getter_AddRefs(msgHdr));
00536             if (msgHdr)
00537               (void)m_curFilter->LogRuleHit(filterAction, msgHdr); 
00538           }
00539       }
00540       // all actions that pass "this" as a listener in order to chain filter execution
00541       // when the action is finished need to return before reaching the bottom of this
00542       // routine, because we run the next filter at the end of this routine.
00543       switch (actionType)
00544       {
00545       case nsMsgFilterAction::Delete:
00546         // we can't pass ourselves in as a copy service listener because the copy service
00547         // listener won't get called in several situations (e.g., the delete model is imap delete)
00548         // and we rely on the listener getting called to continue the filter application.
00549         // This means we're going to end up firing off the delete, and then subsequently 
00550         // issuing a search for the next filter, which will block until the delete finishes.
00551         m_curFolder->DeleteMessages(m_searchHitHdrs, m_msgWindow, PR_FALSE, PR_FALSE, nsnull, PR_FALSE /*allow Undo*/ );
00552         //if we are deleting then we couldn't care less about applying remaining filter actions
00553         applyMoreActions = PR_FALSE;
00554         break;
00555       case nsMsgFilterAction::MoveToFolder:
00556       case nsMsgFilterAction::CopyToFolder:
00557       {
00558         // if moving or copying to a different file, do it.
00559         nsXPIDLCString uri;
00560         rv = m_curFolder->GetURI(getter_Copies(uri));
00561 
00562         if ((const char*)actionTargetFolderUri &&
00563             nsCRT::strcmp(uri, actionTargetFolderUri))
00564         {
00565           nsCOMPtr<nsIRDFService> rdf = do_GetService("@mozilla.org/rdf/rdf-service;1",&rv);
00566           nsCOMPtr<nsIRDFResource> res;
00567           rv = rdf->GetResource(actionTargetFolderUri, getter_AddRefs(res));
00568           NS_ENSURE_SUCCESS(rv, rv);
00569 
00570           nsCOMPtr<nsIMsgFolder> destIFolder(do_QueryInterface(res, &rv));
00571           NS_ENSURE_SUCCESS(rv, rv);
00572 
00573           PRBool canFileMessages = PR_TRUE;
00574           nsCOMPtr<nsIMsgFolder> parentFolder;
00575           destIFolder->GetParent(getter_AddRefs(parentFolder));
00576           if (parentFolder)
00577             destIFolder->GetCanFileMessages(&canFileMessages);
00578           if (!parentFolder || !canFileMessages)
00579           {
00580             m_curFilter->SetEnabled(PR_FALSE);
00581             destIFolder->ThrowAlertMsg("filterDisabled",m_msgWindow);
00582             // we need to explicitly save the filter file.
00583             m_filters->SaveToDefaultFile();
00584             // In the case of applying multiple filters
00585             // we might want to remove the filter from the list, but 
00586             // that's a bit evil since we really don't know that we own
00587             // the list. Disabling it doesn't do a lot of good since
00588             // we still apply disabled filters. Currently, we don't
00589             // have any clients that apply filters to multiple folders,
00590             // so this might be the edge case of an edge case.
00591             return RunNextFilter();
00592           }
00593           nsCOMPtr<nsIMsgCopyService> copyService = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
00594           if (copyService)
00595             return copyService->CopyMessages(m_curFolder, m_searchHitHdrs, destIFolder, actionType == nsMsgFilterAction::MoveToFolder, this, m_msgWindow, PR_FALSE);
00596         }
00597         //we have already moved the hdrs so we can't apply more actions
00598         if (actionType == nsMsgFilterAction::MoveToFolder)
00599           applyMoreActions = PR_FALSE;
00600       }
00601         
00602         break;
00603       case nsMsgFilterAction::MarkRead:
00604           // crud, no listener support here - we'll probably just need to go on and apply
00605           // the next filter, and, in the imap case, rely on multiple connection and url
00606           // queueing to stay out of trouble
00607           m_curFolder->MarkMessagesRead(m_searchHitHdrs, PR_TRUE);
00608         break;
00609       case nsMsgFilterAction::MarkFlagged:
00610         m_curFolder->MarkMessagesFlagged(m_searchHitHdrs, PR_TRUE);
00611         break;
00612       case nsMsgFilterAction::KillThread:
00613       case nsMsgFilterAction::WatchThread:
00614         {
00615           for (PRUint32 msgIndex = 0; msgIndex < m_searchHits.GetSize(); msgIndex++)
00616           {
00617             nsCOMPtr <nsIMsgDBHdr> msgHdr;
00618             m_searchHitHdrs->QueryElementAt(msgIndex, NS_GET_IID(nsIMsgDBHdr), getter_AddRefs(msgHdr));
00619             if (msgHdr)
00620             {
00621               nsCOMPtr <nsIMsgThread> msgThread;
00622               nsMsgKey threadKey;
00623               m_curFolderDB->GetThreadContainingMsgHdr(msgHdr, getter_AddRefs(msgThread));
00624               if (msgThread)
00625               {
00626                 msgThread->GetThreadKey(&threadKey);
00627                 if (actionType == nsMsgFilterAction::KillThread)
00628                   m_curFolderDB->MarkThreadIgnored(msgThread, threadKey, PR_TRUE, nsnull);
00629                 else
00630                   m_curFolderDB->MarkThreadWatched(msgThread, threadKey, PR_TRUE, nsnull);
00631               }
00632             }
00633           }
00634         }
00635         break;
00636       case nsMsgFilterAction::ChangePriority:
00637           {
00638               nsMsgPriorityValue filterPriority;
00639               filterAction->GetPriority(&filterPriority);
00640               for (PRUint32 msgIndex = 0; msgIndex < m_searchHits.GetSize(); msgIndex++)
00641               {
00642                 nsCOMPtr <nsIMsgDBHdr> msgHdr;
00643                 m_searchHitHdrs->QueryElementAt(msgIndex, NS_GET_IID(nsIMsgDBHdr), getter_AddRefs(msgHdr));
00644                 if (msgHdr)
00645                   msgHdr->SetPriority(filterPriority);
00646               }
00647           }
00648         break;
00649       case nsMsgFilterAction::Label:
00650         {
00651             nsMsgLabelValue filterLabel;
00652             filterAction->GetLabel(&filterLabel);
00653             m_curFolder->SetLabelForMessages(m_searchHitHdrs, filterLabel);
00654         }
00655         break;
00656       case nsMsgFilterAction::AddTag:
00657         {
00658             nsXPIDLCString keyword;
00659             filterAction->GetStrValue(getter_Copies(keyword));
00660             m_curFolder->AddKeywordsToMessages(m_searchHitHdrs, keyword.get());
00661         }
00662         break;
00663       case nsMsgFilterAction::JunkScore:
00664       {
00665         nsCAutoString junkScoreStr;
00666         PRInt32 junkScore;
00667         filterAction->GetJunkScore(&junkScore);
00668         junkScoreStr.AppendInt(junkScore);
00669         m_curFolder->SetJunkScoreForMessages(m_searchHitHdrs, junkScoreStr.get());
00670         break;
00671       }
00672       case nsMsgFilterAction::Forward:
00673         {
00674           nsXPIDLCString forwardTo;
00675           filterAction->GetStrValue(getter_Copies(forwardTo));
00676           nsCOMPtr <nsIMsgIncomingServer> server;
00677           rv = m_curFolder->GetServer(getter_AddRefs(server));
00678           NS_ENSURE_SUCCESS(rv, rv);
00679           if (!forwardTo.IsEmpty())
00680           {
00681             nsCOMPtr <nsIMsgComposeService> compService = do_GetService (NS_MSGCOMPOSESERVICE_CONTRACTID) ;
00682             if (compService)
00683             {
00684               for (PRUint32 msgIndex = 0; msgIndex < m_searchHits.GetSize(); msgIndex++)
00685               {
00686                 nsCOMPtr <nsIMsgDBHdr> msgHdr;
00687                 m_searchHitHdrs->QueryElementAt(msgIndex, NS_GET_IID(nsIMsgDBHdr), getter_AddRefs(msgHdr));
00688                 if (msgHdr)
00689                 {
00690                   nsAutoString forwardStr;
00691                   forwardStr.AssignWithConversion(forwardTo.get());
00692                   rv = compService->ForwardMessage(forwardStr, msgHdr, m_msgWindow, server);
00693                 }
00694               }
00695             }
00696           }
00697         }
00698         break;
00699       case nsMsgFilterAction::Reply:
00700         {
00701           nsXPIDLCString replyTemplateUri;
00702           filterAction->GetStrValue(getter_Copies(replyTemplateUri));
00703           nsCOMPtr <nsIMsgIncomingServer> server;
00704           rv = m_curFolder->GetServer(getter_AddRefs(server));
00705           NS_ENSURE_SUCCESS(rv, rv);
00706           if (!replyTemplateUri.IsEmpty())
00707           {
00708             nsCOMPtr <nsIMsgComposeService> compService = do_GetService (NS_MSGCOMPOSESERVICE_CONTRACTID) ;
00709             if (compService)
00710             {
00711               for (PRUint32 msgIndex = 0; msgIndex < m_searchHits.GetSize(); msgIndex++)
00712               {
00713                 nsCOMPtr <nsIMsgDBHdr> msgHdr;
00714                 m_searchHitHdrs->QueryElementAt(msgIndex, NS_GET_IID(nsIMsgDBHdr), getter_AddRefs(msgHdr));
00715                 if (msgHdr)
00716                   rv = compService->ReplyWithTemplate(msgHdr, replyTemplateUri, m_msgWindow, server);
00717               }
00718             }
00719           }
00720         }
00721         break;
00722       case nsMsgFilterAction::DeleteFromPop3Server:
00723         {
00724           nsCOMPtr <nsIMsgLocalMailFolder> localFolder = do_QueryInterface(m_curFolder);
00725           if (localFolder)
00726           {
00727             // This action ignores the deleteMailLeftOnServer preference
00728             localFolder->MarkMsgsOnPop3Server(m_searchHitHdrs, POP3_FORCE_DEL);
00729 
00730             nsCOMPtr <nsISupportsArray> partialMsgs;
00731             // Delete the partial headers. They're useless now
00732             // that the server copy is being deleted.
00733             for (PRUint32 msgIndex = 0; msgIndex < m_searchHits.GetSize(); msgIndex++)
00734             {
00735               nsCOMPtr <nsIMsgDBHdr> msgHdr;
00736               m_searchHitHdrs->QueryElementAt(msgIndex, NS_GET_IID(nsIMsgDBHdr), getter_AddRefs(msgHdr));
00737               if (msgHdr)
00738               {
00739                 PRUint32 flags;
00740                 msgHdr->GetFlags(&flags);
00741                 if (flags & MSG_FLAG_PARTIAL)
00742                 {
00743                   if (!partialMsgs)
00744                     partialMsgs = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, &rv);
00745                   NS_ENSURE_SUCCESS(rv, rv);
00746                   partialMsgs->AppendElement(msgHdr);
00747                 }
00748               }
00749             }
00750             if (partialMsgs)
00751               m_curFolder->DeleteMessages(partialMsgs, m_msgWindow, PR_TRUE, PR_FALSE, nsnull, PR_FALSE);
00752           }
00753         }
00754         break;
00755       case nsMsgFilterAction::FetchBodyFromPop3Server:
00756         {
00757           nsCOMPtr <nsIMsgLocalMailFolder> localFolder = do_QueryInterface(m_curFolder);
00758           if (localFolder)
00759           {
00760             nsCOMPtr<nsISupportsArray> messages = do_CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, &rv);
00761             NS_ENSURE_SUCCESS(rv, rv);
00762             for (PRUint32 msgIndex = 0; msgIndex < m_searchHits.GetSize(); msgIndex++)
00763             {
00764               nsCOMPtr <nsIMsgDBHdr> msgHdr;
00765               m_searchHitHdrs->QueryElementAt(msgIndex, NS_GET_IID(nsIMsgDBHdr), getter_AddRefs(msgHdr));
00766               if (msgHdr)
00767               {
00768                 PRUint32 flags = 0;
00769                 msgHdr->GetFlags(&flags);
00770                 if (flags & MSG_FLAG_PARTIAL)
00771                 {
00772                   nsCOMPtr<nsISupports> iSupports = do_QueryInterface(msgHdr);
00773                   messages->AppendElement(iSupports);
00774                 }
00775               }
00776             }
00777             PRUint32 msgsToFetch;
00778             messages->Count(&msgsToFetch);
00779             if (msgsToFetch > 0)
00780               m_curFolder->DownloadMessagesForOffline(messages, m_msgWindow);
00781           }
00782         }
00783         break;
00784       default:
00785         break;
00786       }
00787     }
00788   }
00789   return RunNextFilter();
00790 }
00791 
00792 NS_IMETHODIMP nsMsgFilterService::GetTempFilterList(nsIMsgFolder *aFolder, nsIMsgFilterList **aFilterList)
00793 {
00794   NS_ENSURE_ARG_POINTER(aFilterList);
00795   nsMsgFilterList *filterList = new nsMsgFilterList;
00796   NS_ENSURE_TRUE(filterList, NS_ERROR_OUT_OF_MEMORY);
00797   NS_ADDREF(*aFilterList = filterList);
00798   (*aFilterList)->SetFolder(aFolder);
00799   filterList->m_temporaryList = PR_TRUE;
00800   return NS_OK;
00801 }
00802 
00803 NS_IMETHODIMP nsMsgFilterService::ApplyFiltersToFolders(nsIMsgFilterList *aFilterList, nsISupportsArray *aFolders, nsIMsgWindow *aMsgWindow)
00804 {
00805   nsMsgFilterAfterTheFact *filterExecutor = new nsMsgFilterAfterTheFact(aMsgWindow, aFilterList, aFolders);
00806   if (filterExecutor)
00807     return filterExecutor->AdvanceToNextFolder();
00808   else
00809     return NS_ERROR_OUT_OF_MEMORY;
00810 }
00811 
00812 /* void OnStartCopy (); */
00813 NS_IMETHODIMP nsMsgFilterAfterTheFact::OnStartCopy()
00814 {
00815   return NS_OK;
00816 }
00817 
00818 /* void OnProgress (in PRUint32 aProgress, in PRUint32 aProgressMax); */
00819 NS_IMETHODIMP nsMsgFilterAfterTheFact::OnProgress(PRUint32 aProgress, PRUint32 aProgressMax)
00820 {
00821   return NS_OK;
00822 }
00823 
00824 /* void SetMessageKey (in PRUint32 aKey); */
00825 NS_IMETHODIMP nsMsgFilterAfterTheFact::SetMessageKey(PRUint32 /* aKey */)
00826 {
00827   return NS_OK;
00828 }
00829 
00830 /* [noscript] void GetMessageId (in nsCString aMessageId); */
00831 NS_IMETHODIMP nsMsgFilterAfterTheFact::GetMessageId(nsCString * /* aMessageId */)
00832 {
00833   return NS_OK;
00834 }
00835 
00836 /* void OnStopCopy (in nsresult aStatus); */
00837 NS_IMETHODIMP nsMsgFilterAfterTheFact::OnStopCopy(nsresult aStatus)
00838 {
00839   PRBool continueExecution = NS_SUCCEEDED(aStatus);
00840   if (!continueExecution)
00841     continueExecution = ContinueExecutionPrompt();
00842   return  (continueExecution) ?  RunNextFilter() : OnEndExecution(aStatus);
00843 }
00844 
00845 PRBool nsMsgFilterAfterTheFact::ContinueExecutionPrompt()
00846 {
00847   PRBool returnVal = PR_FALSE;
00848   nsresult rv;
00849   nsCOMPtr <nsIStringBundle> bundle;
00850   nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
00851   if (bundleService && NS_SUCCEEDED(rv))
00852     bundleService->CreateBundle("chrome://messenger/locale/filter.properties",
00853                                  getter_AddRefs(bundle));
00854   if (NS_SUCCEEDED(rv) && bundle)
00855   {
00856     nsXPIDLString filterName;
00857     m_curFilter->GetFilterName(getter_Copies(filterName));
00858     nsXPIDLString formatString;
00859     nsXPIDLString confirmText;
00860     const PRUnichar *formatStrings[] =
00861     {
00862       filterName
00863     };
00864     rv = bundle->FormatStringFromName(NS_LITERAL_STRING("continueFilterExecution").get(),
00865                                       formatStrings, 1, getter_Copies(confirmText));
00866     if (NS_SUCCEEDED(rv))
00867     {
00868       rv = DisplayConfirmationPrompt(m_msgWindow, confirmText, &returnVal);
00869     }
00870   }
00871   return returnVal;
00872 }
00873 nsresult
00874 nsMsgFilterAfterTheFact::DisplayConfirmationPrompt(nsIMsgWindow *msgWindow, const PRUnichar *confirmString, PRBool *confirmed)
00875 {
00876   nsresult rv=NS_OK;
00877   if (msgWindow)
00878   {
00879     nsCOMPtr <nsIDocShell> docShell;
00880     msgWindow->GetRootDocShell(getter_AddRefs(docShell));
00881     if (docShell)
00882     {
00883       nsCOMPtr<nsIPrompt> dialog(do_GetInterface(docShell));
00884       if (dialog && confirmString)
00885         dialog->Confirm(nsnull, confirmString, confirmed);
00886     }
00887   }
00888   return rv;
00889 }