/* -*- Mode: C++; tab-width: 4; 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): * * 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 ***** */ // Implementation of db search for POP and offline IMAP mail folders #include "msgCore.h" #include "nsIMsgDatabase.h" #include "nsMsgSearchCore.h" #include "nsMsgLocalSearch.h" #include "nsIStreamListener.h" #include "nsParseMailbox.h" #include "nsMsgSearchBoolExpression.h" #include "nsMsgSearchTerm.h" #include "nsMsgResultElement.h" #include "nsIDBFolderInfo.h" #include "nsMsgBaseCID.h" #include "nsMsgSearchValue.h" #include "nsIMsgLocalMailFolder.h" #include "nsIMsgWindow.h" #include "nsIFileSpec.h" extern "C" { extern int MK_MSG_SEARCH_STATUS; extern int MK_MSG_CANT_SEARCH_IF_NO_SUMMARY; extern int MK_MSG_SEARCH_HITS_NOT_IN_DB; } //---------------------------------------------------------------------------- // Class definitions for the boolean expression structure.... //---------------------------------------------------------------------------- nsMsgSearchBoolExpression * nsMsgSearchBoolExpression::AddSearchTermWithEncoding(nsMsgSearchBoolExpression * aOrigExpr, nsIMsgSearchTerm * aNewTerm, char * aEncodingStr) // appropriately add the search term to the current expression and return a pointer to the // new expression. The encodingStr is the IMAP/NNTP encoding string for newTerm. { return aOrigExpr->leftToRightAddTerm(aNewTerm, PR_FALSE, aEncodingStr); } nsMsgSearchBoolExpression * nsMsgSearchBoolExpression::AddSearchTerm(nsMsgSearchBoolExpression * aOrigExpr, nsIMsgSearchTerm * aNewTerm, PRBool aEvalValue) // appropriately add the search term to the current expression // Returns: a pointer to the new expression which includes this new search term { return aOrigExpr->leftToRightAddTerm(aNewTerm, aEvalValue, nsnull); // currently we build our expressions to // evaluate left to right. } nsMsgSearchBoolExpression * nsMsgSearchBoolExpression::AddExpressionTree(nsMsgSearchBoolExpression * aOrigExpr, nsMsgSearchBoolExpression * aExpression, PRBool aBoolOp) { if (!aOrigExpr->m_term && !aOrigExpr->m_leftChild && !aOrigExpr->m_rightChild) { // just use the original expression tree... // delete the original since we have a new original to use delete aOrigExpr; return aExpression; } nsMsgSearchBoolExpression * newExpr = new nsMsgSearchBoolExpression (aOrigExpr, aExpression, aBoolOp); return (newExpr) ? newExpr : aOrigExpr; } nsMsgSearchBoolExpression::nsMsgSearchBoolExpression() { m_term = nsnull; m_boolOp = nsMsgSearchBooleanOp::BooleanAND; m_evalValue = PR_FALSE; m_leftChild = nsnull; m_rightChild = nsnull; } nsMsgSearchBoolExpression::nsMsgSearchBoolExpression (nsIMsgSearchTerm * newTerm, PRBool evalValue, char * encodingStr) // we are creating an expression which contains a single search term (newTerm) // and the search term's IMAP or NNTP encoding value for online search expressions AND // a boolean evaluation value which is used for offline search expressions. { m_term = newTerm; m_encodingStr = encodingStr; m_evalValue = evalValue; m_boolOp = nsMsgSearchBooleanOp::BooleanAND; // this expression does not contain sub expressions m_leftChild = nsnull; m_rightChild = nsnull; } nsMsgSearchBoolExpression::nsMsgSearchBoolExpression (nsMsgSearchBoolExpression * expr1, nsMsgSearchBoolExpression * expr2, nsMsgSearchBooleanOperator boolOp) // we are creating an expression which contains two sub expressions and a boolean operator used to combine // them. { m_leftChild = expr1; m_rightChild = expr2; m_boolOp = boolOp; m_term = nsnull; m_evalValue = PR_FALSE; } nsMsgSearchBoolExpression::~nsMsgSearchBoolExpression() { // we must recursively destroy all sub expressions before we destroy ourself.....We leave search terms alone! if (m_leftChild) delete m_leftChild; if (m_rightChild) delete m_rightChild; } nsMsgSearchBoolExpression * nsMsgSearchBoolExpression::leftToRightAddTerm(nsIMsgSearchTerm * newTerm, PRBool evalValue, char * encodingStr) { // we have a base case where this is the first term being added to the expression: if (!m_term && !m_leftChild && !m_rightChild) { m_term = newTerm; m_evalValue = evalValue; m_encodingStr = encodingStr; return this; } nsMsgSearchBoolExpression * tempExpr = new nsMsgSearchBoolExpression (newTerm,evalValue,encodingStr); if (tempExpr) // make sure creation succeeded { PRBool booleanAnd; newTerm->GetBooleanAnd(&booleanAnd); nsMsgSearchBoolExpression * newExpr = new nsMsgSearchBoolExpression (this, tempExpr, booleanAnd); if (newExpr) return newExpr; else delete tempExpr; // clean up memory allocation in case of failure } return this; // in case we failed to create a new expression, return self } PRBool nsMsgSearchBoolExpression::OfflineEvaluate() // returns PR_TRUE or PR_FALSE depending on what the current expression evaluates to. Since this is // offline, when we created the expression we stored an evaluation value for each search term in // the expression. These are the values we use to determine if the expression is PR_TRUE or PR_FALSE. { if (m_term) // do we contain just a search term? return m_evalValue; // otherwise we must recursively determine the value of our sub expressions PRBool result1 = PR_TRUE; // always default to false positives PRBool result2 = PR_TRUE; if (m_leftChild) { result1 = m_leftChild->OfflineEvaluate(); if (m_boolOp == nsMsgSearchBooleanOp::BooleanOR && result1 || (m_boolOp == nsMsgSearchBooleanOp::BooleanAND && !result1)) return result1; } if (m_rightChild) result2 = m_rightChild->OfflineEvaluate(); if (m_boolOp == nsMsgSearchBooleanOp::BooleanOR) return (result1 || result2) ? PR_TRUE : PR_FALSE; if (m_boolOp == nsMsgSearchBooleanOp::BooleanAND && result1 && result2) return PR_TRUE; return PR_FALSE; } // ### Maybe we can get rid of these because of our use of nsString??? // constants used for online searching with IMAP/NNTP encoded search terms. // the + 1 is to account for null terminators we add at each stage of assembling the expression... const int sizeOfORTerm = 6+1; // 6 bytes if we are combining two sub expressions with an OR term const int sizeOfANDTerm = 1+1; // 1 byte if we are combining two sub expressions with an AND term PRInt32 nsMsgSearchBoolExpression::CalcEncodeStrSize() // recursively examine each sub expression and calculate a final size for the entire IMAP/NNTP encoding { if (!m_term && (!m_leftChild || !m_rightChild)) // is the expression empty? return 0; if (m_term) // are we a leaf node? return m_encodingStr.Length(); if (m_boolOp == nsMsgSearchBooleanOp::BooleanOR) return sizeOfORTerm + m_leftChild->CalcEncodeStrSize() + m_rightChild->CalcEncodeStrSize(); if (m_boolOp == nsMsgSearchBooleanOp::BooleanAND) return sizeOfANDTerm + m_leftChild->CalcEncodeStrSize() + m_rightChild->CalcEncodeStrSize(); return 0; } void nsMsgSearchBoolExpression::GenerateEncodeStr(nsCString * buffer) // recurively combine sub expressions to form a single IMAP/NNTP encoded string { if ((!m_term && (!m_leftChild || !m_rightChild))) // is expression empty? return; if (m_term) // are we a leaf expression? { *buffer += m_encodingStr; return; } // add encode strings of each sub expression if (m_boolOp == nsMsgSearchBooleanOp::BooleanOR) { *buffer += " (OR"; m_leftChild->GenerateEncodeStr(buffer); // insert left expression into the buffer m_rightChild->GenerateEncodeStr(buffer); // insert right expression into the buffer // HACK ALERT!!! if last returned character in the buffer is now a ' ' then we need to remove it because we don't want // a ' ' to preceded the closing paren in the OR encoding. PRUint32 lastCharPos = buffer->Length() - 1; if (buffer->CharAt(lastCharPos) == ' ') { buffer->Truncate(lastCharPos); } *buffer += ')'; } else if (m_boolOp == nsMsgSearchBooleanOp::BooleanAND) { m_leftChild->GenerateEncodeStr(buffer); // insert left expression m_rightChild->GenerateEncodeStr(buffer); } return; } //----------------------------------------------------------------------------- //---------------- Adapter class for searching offline folders ---------------- //----------------------------------------------------------------------------- NS_IMPL_ISUPPORTS_INHERITED1(nsMsgSearchOfflineMail, nsMsgSearchAdapter, nsIUrlListener) nsMsgSearchOfflineMail::nsMsgSearchOfflineMail (nsIMsgSearchScopeTerm *scope, nsISupportsArray *termList) : nsMsgSearchAdapter (scope, termList) { } nsMsgSearchOfflineMail::~nsMsgSearchOfflineMail () { // Database should have been closed when the scope term finished. CleanUpScope(); NS_ASSERTION(!m_db, "db not closed"); } nsresult nsMsgSearchOfflineMail::ValidateTerms () { return nsMsgSearchAdapter::ValidateTerms (); } nsresult nsMsgSearchOfflineMail::OpenSummaryFile () { nsCOMPtr mailDB ; nsresult err = NS_OK; // do password protection of local cache thing. #ifdef DOING_FOLDER_CACHE_PASSWORDS if (m_scope->m_folder && m_scope->m_folder->UserNeedsToAuthenticateForFolder(PR_FALSE) && m_scope->m_folder->GetMaster()->PromptForHostPassword(m_scope->m_frame->GetContext(), m_scope->m_folder) != 0) { m_scope->m_frame->StopRunning(); return SearchError_ScopeDone; } #endif nsCOMPtr folderInfo; nsCOMPtr scopeFolder; err = m_scope->GetFolder(getter_AddRefs(scopeFolder)); if (NS_SUCCEEDED(err) && scopeFolder) { err = scopeFolder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(m_db)); } else return err; // not sure why m_folder wouldn't be set. switch (err) { case NS_OK: break; case NS_MSG_ERROR_FOLDER_SUMMARY_MISSING: case NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE: { nsCOMPtr localFolder = do_QueryInterface(scopeFolder, &err); if (NS_SUCCEEDED(err) && localFolder) { nsCOMPtr searchSession; m_scope->GetSearchSession(getter_AddRefs(searchSession)); if (searchSession) { nsCOMPtr searchWindow; searchSession->GetWindow(getter_AddRefs(searchWindow)); searchSession->PauseSearch(); localFolder->ParseFolder(searchWindow, this); } } } break; default: { NS_ASSERTION(PR_FALSE, "unexpected error opening db"); } } return err; } nsresult nsMsgSearchOfflineMail::MatchTermsForFilter(nsIMsgDBHdr *msgToMatch, nsISupportsArray *termList, const char *defaultCharset, nsIMsgSearchScopeTerm * scope, nsIMsgDatabase * db, const char * headers, PRUint32 headerSize, PRBool *pResult) { return MatchTerms(msgToMatch, termList, defaultCharset, scope, db, headers, headerSize, PR_TRUE, pResult); } // static method which matches a header against a list of search terms. nsresult nsMsgSearchOfflineMail::MatchTermsForSearch(nsIMsgDBHdr *msgToMatch, nsISupportsArray* termList, const char *defaultCharset, nsIMsgSearchScopeTerm *scope, nsIMsgDatabase *db, PRBool *pResult) { return MatchTerms(msgToMatch, termList, defaultCharset, scope, db, nsnull, 0, PR_FALSE, pResult); } nsresult nsMsgSearchOfflineMail::ConstructExpressionTree(nsIMsgDBHdr *msgToMatch, nsISupportsArray * termList, PRUint32 &aStartPosInList, const char *defaultCharset, nsIMsgSearchScopeTerm * scope, nsIMsgDatabase * db, const char * headers, PRUint32 headerSize, PRBool Filtering, nsMsgSearchBoolExpression ** aExpressionTree, PRBool *pResult) { PRBool result; NS_ENSURE_ARG_POINTER(pResult); *pResult = PR_FALSE; // Don't even bother to look at expunged messages awaiting compression PRUint32 msgFlags; msgToMatch->GetFlags(&msgFlags); if (msgFlags & MSG_FLAG_EXPUNGED) result = PR_FALSE; PRUint32 count; termList->Count(&count); nsMsgSearchBoolExpression * finalExpression = new nsMsgSearchBoolExpression(); while (aStartPosInList < count) { nsCOMPtr pTerm; termList->QueryElementAt(aStartPosInList, NS_GET_IID(nsIMsgSearchTerm), (void **)getter_AddRefs(pTerm)); NS_ASSERTION (msgToMatch, "couldn't get term to match"); PRBool beginsGrouping; PRBool endsGrouping; pTerm->GetBeginsGrouping(&beginsGrouping); pTerm->GetEndsGrouping(&endsGrouping); if (beginsGrouping) { //temporarily turn off the grouping for our recursive call pTerm->SetBeginsGrouping(PR_FALSE); // recursively process this inner expression nsMsgSearchBoolExpression * innerExpression = new nsMsgSearchBoolExpression(); ConstructExpressionTree(msgToMatch, termList, aStartPosInList, defaultCharset, scope, db, headers, headerSize, Filtering, &innerExpression, &result); // the first search term in the grouping is the one that holds the operator for how this search term // should be joined with the expressions to it's left. PRBool booleanAnd; pTerm->GetBooleanAnd(&booleanAnd); // now add this expression tree to our overall expression tree... finalExpression = nsMsgSearchBoolExpression::AddExpressionTree(finalExpression, innerExpression, booleanAnd); // undo our damage pTerm->SetBeginsGrouping(PR_TRUE); } else { ProcessSearchTerm(msgToMatch, pTerm, defaultCharset, scope, db, headers, headerSize, Filtering, &result); finalExpression = nsMsgSearchBoolExpression::AddSearchTerm(finalExpression, pTerm, result); // added the term and its value to the expression tree if (endsGrouping) { // okay, this term marks the end of a grouping...kick out of this function. *pResult = result; *aExpressionTree = finalExpression; return NS_OK; } } aStartPosInList++; } // while we still have terms to process in this group *pResult = PR_TRUE; *aExpressionTree = finalExpression; return NS_OK; } nsresult nsMsgSearchOfflineMail::ProcessSearchTerm(nsIMsgDBHdr *msgToMatch, nsIMsgSearchTerm * aTerm, const char *defaultCharset, nsIMsgSearchScopeTerm * scope, nsIMsgDatabase * db, const char * headers, PRUint32 headerSize, PRBool Filtering, PRBool *pResult) { nsresult err = NS_OK; nsXPIDLCString recipients; nsXPIDLCString ccList; nsXPIDLCString matchString; nsXPIDLCString msgCharset; const char *charset; PRBool charsetOverride = PR_FALSE; /* XXX BUG 68706 */ PRUint32 msgFlags; PRBool result; NS_ENSURE_ARG_POINTER(pResult); *pResult = PR_FALSE; nsMsgSearchAttribValue attrib; aTerm->GetAttrib(&attrib); msgToMatch->GetCharset(getter_Copies(msgCharset)); charset = (const char*)msgCharset; if (!charset || !*charset) charset = (const char*)defaultCharset; msgToMatch->GetFlags(&msgFlags); switch (attrib) { case nsMsgSearchAttrib::Sender: msgToMatch->GetAuthor(getter_Copies(matchString)); err = aTerm->MatchRfc822String (matchString, charset, charsetOverride, &result); break; case nsMsgSearchAttrib::Subject: { msgToMatch->GetSubject(getter_Copies(matchString) /* , PR_TRUE */); if (msgFlags & MSG_FLAG_HAS_RE) { // Make sure we pass along the "Re: " part of the subject if this is a reply. nsXPIDLCString reString; reString.Assign("Re: "); reString.Append(matchString); err = aTerm->MatchRfc2047String(reString, charset, charsetOverride, &result); } else err = aTerm->MatchRfc2047String (matchString, charset, charsetOverride, &result); break; } case nsMsgSearchAttrib::ToOrCC: { PRBool boolKeepGoing; aTerm->GetMatchAllBeforeDeciding(&boolKeepGoing); msgToMatch->GetRecipients(getter_Copies(recipients)); err = aTerm->MatchRfc822String (recipients, charset, charsetOverride, &result); if (boolKeepGoing == result) { msgToMatch->GetCcList(getter_Copies(ccList)); err = aTerm->MatchRfc822String (ccList, charset, charsetOverride, &result); } break; } case nsMsgSearchAttrib::Body: { nsMsgKey messageOffset; PRUint32 lineCount; msgToMatch->GetMessageOffset(&messageOffset); msgToMatch->GetLineCount(&lineCount); err = aTerm->MatchBody (scope, messageOffset, lineCount, charset, msgToMatch, db, &result); break; } case nsMsgSearchAttrib::Date: { PRTime date; msgToMatch->GetDate(&date); err = aTerm->MatchDate (date, &result); break; } case nsMsgSearchAttrib::HasAttachmentStatus: case nsMsgSearchAttrib::MsgStatus: err = aTerm->MatchStatus (msgFlags, &result); break; case nsMsgSearchAttrib::Priority: { nsMsgPriorityValue msgPriority; msgToMatch->GetPriority(&msgPriority); err = aTerm->MatchPriority (msgPriority, &result); break; } case nsMsgSearchAttrib::Size: { PRUint32 messageSize; msgToMatch->GetMessageSize(&messageSize); err = aTerm->MatchSize (messageSize, &result); break; } case nsMsgSearchAttrib::To: msgToMatch->GetRecipients(getter_Copies(recipients)); err = aTerm->MatchRfc822String(recipients, charset, charsetOverride, &result); break; case nsMsgSearchAttrib::CC: msgToMatch->GetCcList(getter_Copies(ccList)); err = aTerm->MatchRfc822String (ccList, charset, charsetOverride, &result); break; case nsMsgSearchAttrib::AgeInDays: { PRTime date; msgToMatch->GetDate(&date); err = aTerm->MatchAge (date, &result); break; } case nsMsgSearchAttrib::Label: { nsMsgLabelValue label; msgToMatch->GetLabel(&label); err = aTerm->MatchLabel(label, &result); break; } case nsMsgSearchAttrib::JunkStatus: { nsXPIDLCString junkScoreStr; msgToMatch->GetStringProperty("junkscore", getter_Copies(junkScoreStr)); err = aTerm->MatchJunkStatus(junkScoreStr, &result); break; } default: // XXX todo // for the temporary return receipts filters, we use a custom header for Content-Type // but unlike the other custom headers, this one doesn't show up in the search / filter // UI. we set the attrib to be nsMsgSearchAttrib::OtherHeader, where as for user // defined custom headers start at nsMsgSearchAttrib::OtherHeader + 1 // Not sure if there is a better way to do this yet. Maybe reserve the last // custom header for ::Content-Type? But if we do, make sure that change // doesn't cause nsMsgFilter::GetTerm() to change, and start making us // ask IMAP servers for the Content-Type header on all messages. if ( attrib >= nsMsgSearchAttrib::OtherHeader && attrib < nsMsgSearchAttrib::kNumMsgSearchAttributes) { PRUint32 lineCount; msgToMatch->GetLineCount(&lineCount); nsMsgKey messageKey; msgToMatch->GetMessageOffset(&messageKey); err = aTerm->MatchArbitraryHeader (scope, messageKey, lineCount,charset, charsetOverride, msgToMatch, db, headers, headerSize, Filtering, &result); } else err = NS_ERROR_INVALID_ARG; // ### was SearchError_InvalidAttribute } *pResult = result; return NS_OK; } nsresult nsMsgSearchOfflineMail::MatchTerms(nsIMsgDBHdr *msgToMatch, nsISupportsArray * termList, const char *defaultCharset, nsIMsgSearchScopeTerm * scope, nsIMsgDatabase * db, const char * headers, PRUint32 headerSize, PRBool Filtering, PRBool *pResult) { nsMsgSearchBoolExpression * expressionTree = nsnull; PRUint32 initialPos = 0; nsresult err = ConstructExpressionTree(msgToMatch, termList, initialPos, defaultCharset, scope, db, headers, headerSize, Filtering, &expressionTree, pResult); // evaluate the expression tree and return the result if (NS_SUCCEEDED(err) && expressionTree) *pResult = expressionTree->OfflineEvaluate(); delete expressionTree; return err; } nsresult nsMsgSearchOfflineMail::Search (PRBool *aDone) { nsresult err = NS_OK; NS_ENSURE_ARG(aDone); nsresult dbErr = NS_OK; nsCOMPtr msgDBHdr; const PRUint32 kTimeSliceInMS = 200; *aDone = PR_FALSE; // Try to open the DB lazily. This will set up a parser if one is required if (!m_db) err = OpenSummaryFile (); if (!m_db) // must be reparsing. return err; // Reparsing is unnecessary or completed if (NS_SUCCEEDED(err)) { if (!m_listContext) dbErr = m_db->EnumerateMessages (getter_AddRefs(m_listContext)); if (NS_SUCCEEDED(dbErr) && m_listContext) { PRIntervalTime startTime = PR_IntervalNow(); while (!*aDone) // we'll break out of the loop after kTimeSliceInMS milliseconds { nsCOMPtr currentItem; dbErr = m_listContext->GetNext(getter_AddRefs(currentItem)); if(NS_SUCCEEDED(dbErr)) { msgDBHdr = do_QueryInterface(currentItem, &dbErr); } if (NS_FAILED(dbErr)) *aDone = PR_TRUE; //###phil dbErr is dropped on the floor. just note that we did have an error so we'll clean up later else { PRBool match = PR_FALSE; nsXPIDLString nullCharset, folderCharset; GetSearchCharsets(getter_Copies(nullCharset), getter_Copies(folderCharset)); NS_ConvertUCS2toUTF8 charset(folderCharset); // Is this message a hit? err = MatchTermsForSearch (msgDBHdr, m_searchTerms, charset.get(), m_scope, m_db, &match); // Add search hits to the results list if (NS_SUCCEEDED(err) && match) { AddResultElement (msgDBHdr); } PRIntervalTime elapsedTime; LL_SUB(elapsedTime, PR_IntervalNow(), startTime); // check if more than kTimeSliceInMS milliseconds have elapsed in this time slice started if (PR_IntervalToMilliseconds(elapsedTime) > kTimeSliceInMS) break; } } } } else *aDone = PR_TRUE; // we couldn't open up the DB. This is an unrecoverable error so mark the scope as done. // in the past an error here would cause an "infinite" search because the url would continue to run... // i.e. if we couldn't open the database, it returns an error code but the caller of this function says, oh, // we did not finish so continue...what we really want is to treat this current scope as done if (*aDone) CleanUpScope(); // Do clean up for end-of-scope processing return err; } void nsMsgSearchOfflineMail::CleanUpScope() { // Let go of the DB when we're done with it so we don't kill the db cache if (m_db) { m_listContext = nsnull; m_db->Close(PR_FALSE); } m_db = nsnull; m_scope->SetInputStream(nsnull); } NS_IMETHODIMP nsMsgSearchOfflineMail::AddResultElement (nsIMsgDBHdr *pHeaders) { nsresult err = NS_OK; nsCOMPtr searchSession; m_scope->GetSearchSession(getter_AddRefs(searchSession)); if (searchSession) { nsCOMPtr scopeFolder; err = m_scope->GetFolder(getter_AddRefs(scopeFolder)); searchSession->AddSearchHit(pHeaders, scopeFolder); } return err; } NS_IMETHODIMP nsMsgSearchOfflineMail::Abort () { // Let go of the DB when we're done with it so we don't kill the db cache if (m_db) m_db->Close(PR_TRUE /* commit in case we downloaded new headers */); m_db = nsnull; return nsMsgSearchAdapter::Abort (); } /* void OnStartRunningUrl (in nsIURI url); */ NS_IMETHODIMP nsMsgSearchOfflineMail::OnStartRunningUrl(nsIURI *url) { return NS_OK; } /* void OnStopRunningUrl (in nsIURI url, in nsresult aExitCode); */ NS_IMETHODIMP nsMsgSearchOfflineMail::OnStopRunningUrl(nsIURI *url, nsresult aExitCode) { nsCOMPtr searchSession; m_scope->GetSearchSession(getter_AddRefs(searchSession)); if (searchSession) searchSession->ResumeSearch(); return NS_OK; } nsMsgSearchOfflineNews::nsMsgSearchOfflineNews (nsIMsgSearchScopeTerm *scope, nsISupportsArray *termList) : nsMsgSearchOfflineMail (scope, termList) { } nsMsgSearchOfflineNews::~nsMsgSearchOfflineNews () { } nsresult nsMsgSearchOfflineNews::OpenSummaryFile () { nsresult err = NS_OK; nsCOMPtr folderInfo; nsCOMPtr scopeFolder; err = m_scope->GetFolder(getter_AddRefs(scopeFolder)); // code here used to check if offline store existed, which breaks offline news. if (NS_SUCCEEDED(err) && scopeFolder) err = scopeFolder->GetMsgDatabase(nsnull, getter_AddRefs(m_db)); return err; } nsresult nsMsgSearchOfflineNews::ValidateTerms () { return nsMsgSearchOfflineMail::ValidateTerms (); } //----------------------------------------------------------------------------- nsresult nsMsgSearchValidityManager::InitLocalNewsTable() { NS_ASSERTION (nsnull == m_localNewsTable, "already have local news validty table"); nsresult err = NewTable (getter_AddRefs(m_localNewsTable)); if (NS_SUCCEEDED(err)) { m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Contains, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Contains, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Is, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Is, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::BeginsWith, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::BeginsWith, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::EndsWith, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::EndsWith, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Contains, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Contains, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Is, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Is, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::BeginsWith, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::BeginsWith, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::EndsWith, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::EndsWith, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Body, nsMsgSearchOp::Contains, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::Body, nsMsgSearchOp::Contains, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Body, nsMsgSearchOp::DoesntContain, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::Body, nsMsgSearchOp::DoesntContain, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Body, nsMsgSearchOp::Is, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::Body, nsMsgSearchOp::Is, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Body, nsMsgSearchOp::Isnt, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::Body, nsMsgSearchOp::Isnt, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::Date, nsMsgSearchOp::IsBefore, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Date, nsMsgSearchOp::IsAfter, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::Date, nsMsgSearchOp::IsAfter, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Date, nsMsgSearchOp::Is, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::Date, nsMsgSearchOp::Is, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Date, nsMsgSearchOp::Isnt, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::Date, nsMsgSearchOp::Isnt, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::AgeInDays, nsMsgSearchOp::IsGreaterThan, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::AgeInDays, nsMsgSearchOp::IsGreaterThan, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::AgeInDays, nsMsgSearchOp::IsLessThan, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::AgeInDays, nsMsgSearchOp::IsLessThan, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::AgeInDays, nsMsgSearchOp::Is, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::AgeInDays, nsMsgSearchOp::Is, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::MsgStatus, nsMsgSearchOp::Is, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::MsgStatus, nsMsgSearchOp::Is, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::MsgStatus, nsMsgSearchOp::Isnt, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::MsgStatus, nsMsgSearchOp::Isnt, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Contains, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Contains, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Is, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Is, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::BeginsWith, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::BeginsWith, 1); m_localNewsTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::EndsWith, 1); m_localNewsTable->SetEnabled (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::EndsWith, 1); } return err; }