Back to index

lightning-sunbird  0.9+nobinonly
nsMsgLocalSearch.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; 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  *   Howard Chu <hyc@symas.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 // Implementation of db search for POP and offline IMAP mail folders
00040 
00041 #include "msgCore.h"
00042 #include "nsIMsgDatabase.h"
00043 #include "nsMsgSearchCore.h"
00044 #include "nsMsgLocalSearch.h"
00045 #include "nsIStreamListener.h"
00046 #include "nsParseMailbox.h"
00047 #include "nsMsgSearchBoolExpression.h"
00048 #include "nsMsgSearchTerm.h"
00049 #include "nsMsgResultElement.h"
00050 #include "nsIDBFolderInfo.h"
00051 
00052 #include "nsMsgBaseCID.h"
00053 #include "nsMsgSearchValue.h"
00054 #include "nsIMsgLocalMailFolder.h"
00055 #include "nsIMsgWindow.h"
00056 #include "nsIFileSpec.h"
00057 
00058 extern "C"
00059 {
00060     extern int MK_MSG_SEARCH_STATUS;
00061     extern int MK_MSG_CANT_SEARCH_IF_NO_SUMMARY;
00062     extern int MK_MSG_SEARCH_HITS_NOT_IN_DB;
00063 }
00064 
00065 
00066 //----------------------------------------------------------------------------
00067 // Class definitions for the boolean expression structure....
00068 //----------------------------------------------------------------------------
00069 
00070 
00071 nsMsgSearchBoolExpression * nsMsgSearchBoolExpression::AddSearchTerm(nsMsgSearchBoolExpression * aOrigExpr, nsIMsgSearchTerm * aNewTerm, char * aEncodingStr)
00072 // appropriately add the search term to the current expression and return a pointer to the
00073 // new expression. The encodingStr is the IMAP/NNTP encoding string for newTerm.
00074 {
00075     return aOrigExpr->leftToRightAddTerm(aNewTerm, aEncodingStr);
00076 }
00077 
00078 nsMsgSearchBoolExpression * nsMsgSearchBoolExpression::AddExpressionTree(nsMsgSearchBoolExpression * aOrigExpr, nsMsgSearchBoolExpression * aExpression, PRBool aBoolOp)
00079 {
00080   if (!aOrigExpr->m_term && !aOrigExpr->m_leftChild && !aOrigExpr->m_rightChild)
00081   {
00082       // just use the original expression tree...
00083       // delete the original since we have a new original to use
00084       delete aOrigExpr;
00085       return aExpression;
00086   }
00087 
00088   nsMsgSearchBoolExpression * newExpr = new nsMsgSearchBoolExpression (aOrigExpr, aExpression, aBoolOp);  
00089   return (newExpr) ? newExpr : aOrigExpr;
00090 }
00091 
00092 nsMsgSearchBoolExpression::nsMsgSearchBoolExpression() 
00093 {
00094     m_term = nsnull;
00095     m_boolOp = nsMsgSearchBooleanOp::BooleanAND;
00096     m_leftChild = nsnull;
00097     m_rightChild = nsnull;
00098 }
00099 
00100 nsMsgSearchBoolExpression::nsMsgSearchBoolExpression (nsIMsgSearchTerm * newTerm, char * encodingStr) 
00101 // we are creating an expression which contains a single search term (newTerm) 
00102 // and the search term's IMAP or NNTP encoding value for online search expressions AND
00103 // a boolean evaluation value which is used for offline search expressions.
00104 {
00105     m_term = newTerm;
00106     m_encodingStr = encodingStr;
00107     m_boolOp = nsMsgSearchBooleanOp::BooleanAND;
00108 
00109     // this expression does not contain sub expressions
00110     m_leftChild = nsnull;
00111     m_rightChild = nsnull;
00112 }
00113 
00114 
00115 nsMsgSearchBoolExpression::nsMsgSearchBoolExpression (nsMsgSearchBoolExpression * expr1, nsMsgSearchBoolExpression * expr2, nsMsgSearchBooleanOperator boolOp)
00116 // we are creating an expression which contains two sub expressions and a boolean operator used to combine
00117 // them.
00118 {
00119     m_leftChild = expr1;
00120     m_rightChild = expr2;
00121     m_boolOp = boolOp;
00122 
00123     m_term = nsnull;
00124 }
00125 
00126 nsMsgSearchBoolExpression::~nsMsgSearchBoolExpression()
00127 {
00128   // we must recursively destroy all sub expressions before we destroy ourself.....We leave search terms alone!
00129   delete m_leftChild;
00130   delete m_rightChild;
00131 }
00132 
00133 nsMsgSearchBoolExpression *
00134 nsMsgSearchBoolExpression::leftToRightAddTerm(nsIMsgSearchTerm * newTerm, char * encodingStr)
00135 {
00136     // we have a base case where this is the first term being added to the expression:
00137     if (!m_term && !m_leftChild && !m_rightChild)
00138     {
00139         m_term = newTerm;
00140         m_encodingStr = encodingStr;
00141         return this;
00142     }
00143 
00144     nsMsgSearchBoolExpression * tempExpr = new nsMsgSearchBoolExpression (newTerm,encodingStr);
00145     if (tempExpr)  // make sure creation succeeded
00146     {
00147       PRBool booleanAnd;
00148       newTerm->GetBooleanAnd(&booleanAnd);
00149       nsMsgSearchBoolExpression * newExpr = new nsMsgSearchBoolExpression (this, tempExpr, booleanAnd);  
00150       if (newExpr)
00151          return newExpr;
00152       else
00153          delete tempExpr;    // clean up memory allocation in case of failure
00154     }
00155     return this;   // in case we failed to create a new expression, return self
00156 }
00157 
00158 
00159 // returns PR_TRUE or PR_FALSE depending on what the current expression evaluates to. 
00160 PRBool nsMsgSearchBoolExpression::OfflineEvaluate(nsIMsgDBHdr *msgToMatch, const char *defaultCharset,
00161   nsIMsgSearchScopeTerm *scope, nsIMsgDatabase *db, const char *headers,
00162   PRUint32 headerSize, PRBool Filtering)
00163 {
00164     PRBool result = PR_TRUE;    // always default to false positives
00165     PRBool isAnd;
00166 
00167     if (m_term) // do we contain just a search term?
00168     {
00169       nsMsgSearchOfflineMail::ProcessSearchTerm(msgToMatch, m_term,
00170         defaultCharset, scope, db, headers, headerSize, Filtering, &result);
00171       return result;
00172     }
00173     
00174     // otherwise we must recursively determine the value of our sub expressions
00175 
00176     isAnd = (m_boolOp == nsMsgSearchBooleanOp::BooleanAND);
00177 
00178     if (m_leftChild)
00179     {
00180         result = m_leftChild->OfflineEvaluate(msgToMatch, defaultCharset,
00181           scope, db, headers, headerSize, Filtering);
00182         // If (TRUE and OR) or (FALSE and AND) return result
00183         if (result ^ isAnd)
00184           return result;
00185     }
00186 
00187     // If we got this far, either there was no leftChild (which is impossible)
00188     // or we got (FALSE and OR) or (TRUE and AND) from the first result. That
00189     // means the outcome depends entirely on the rightChild.
00190     if (m_rightChild)
00191         result = m_rightChild->OfflineEvaluate(msgToMatch, defaultCharset,
00192           scope, db, headers, headerSize, Filtering);
00193 
00194     return result;
00195 }
00196 
00197 // ### Maybe we can get rid of these because of our use of nsString???
00198 // constants used for online searching with IMAP/NNTP encoded search terms.
00199 // the + 1 is to account for null terminators we add at each stage of assembling the expression...
00200 const int sizeOfORTerm = 6+1;  // 6 bytes if we are combining two sub expressions with an OR term
00201 const int sizeOfANDTerm = 1+1; // 1 byte if we are combining two sub expressions with an AND term
00202 
00203 PRInt32 nsMsgSearchBoolExpression::CalcEncodeStrSize()
00204 // recursively examine each sub expression and calculate a final size for the entire IMAP/NNTP encoding 
00205 {
00206     if (!m_term && (!m_leftChild || !m_rightChild))   // is the expression empty?
00207         return 0;    
00208     if (m_term)  // are we a leaf node?
00209         return m_encodingStr.Length();
00210     if (m_boolOp == nsMsgSearchBooleanOp::BooleanOR)
00211         return sizeOfORTerm + m_leftChild->CalcEncodeStrSize() + m_rightChild->CalcEncodeStrSize();
00212     if (m_boolOp == nsMsgSearchBooleanOp::BooleanAND)
00213         return sizeOfANDTerm + m_leftChild->CalcEncodeStrSize() + m_rightChild->CalcEncodeStrSize();
00214     return 0;
00215 }
00216 
00217 
00218 void nsMsgSearchBoolExpression::GenerateEncodeStr(nsCString * buffer)
00219 // recurively combine sub expressions to form a single IMAP/NNTP encoded string 
00220 {
00221     if ((!m_term && (!m_leftChild || !m_rightChild))) // is expression empty?
00222         return;
00223     
00224     if (m_term) // are we a leaf expression?
00225     {
00226         *buffer += m_encodingStr;
00227         return;
00228     }
00229     
00230     // add encode strings of each sub expression
00231     if (m_boolOp == nsMsgSearchBooleanOp::BooleanOR) 
00232     {
00233         *buffer += " (OR";
00234 
00235         m_leftChild->GenerateEncodeStr(buffer);  // insert left expression into the buffer
00236         m_rightChild->GenerateEncodeStr(buffer);  // insert right expression into the buffer
00237         
00238         // HACK ALERT!!! if last returned character in the buffer is now a ' ' then we need to remove it because we don't want
00239         // a ' ' to preceded the closing paren in the OR encoding.
00240         PRUint32 lastCharPos = buffer->Length() - 1;
00241         if (buffer->CharAt(lastCharPos) == ' ')
00242         {
00243             buffer->Truncate(lastCharPos);
00244         }
00245         
00246         *buffer += ')';
00247     }
00248     else if (m_boolOp == nsMsgSearchBooleanOp::BooleanAND)
00249     {
00250         m_leftChild->GenerateEncodeStr(buffer); // insert left expression
00251         m_rightChild->GenerateEncodeStr(buffer);
00252     }
00253     return;
00254 }
00255 
00256 
00257 //-----------------------------------------------------------------------------
00258 //---------------- Adapter class for searching offline folders ----------------
00259 //-----------------------------------------------------------------------------
00260 
00261 
00262 NS_IMPL_ISUPPORTS_INHERITED1(nsMsgSearchOfflineMail, nsMsgSearchAdapter, nsIUrlListener)
00263 
00264 nsMsgSearchOfflineMail::nsMsgSearchOfflineMail (nsIMsgSearchScopeTerm *scope, nsISupportsArray *termList) : nsMsgSearchAdapter (scope, termList)
00265 {
00266 }
00267 
00268 nsMsgSearchOfflineMail::~nsMsgSearchOfflineMail ()
00269 {
00270   // Database should have been closed when the scope term finished. 
00271   CleanUpScope();
00272   NS_ASSERTION(!m_db, "db not closed");
00273 }
00274 
00275 
00276 nsresult nsMsgSearchOfflineMail::ValidateTerms ()
00277 {
00278   return nsMsgSearchAdapter::ValidateTerms ();
00279 }
00280 
00281 
00282 nsresult nsMsgSearchOfflineMail::OpenSummaryFile ()
00283 {
00284     nsCOMPtr <nsIMsgDatabase> mailDB ;
00285 
00286     nsresult err = NS_OK;
00287     // do password protection of local cache thing.
00288 #ifdef DOING_FOLDER_CACHE_PASSWORDS
00289     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)
00290     {
00291         m_scope->m_frame->StopRunning();
00292         return SearchError_ScopeDone;
00293     }
00294 #endif
00295     nsCOMPtr <nsIDBFolderInfo>  folderInfo;
00296     nsCOMPtr <nsIMsgFolder> scopeFolder;
00297     err = m_scope->GetFolder(getter_AddRefs(scopeFolder));
00298     if (NS_SUCCEEDED(err) && scopeFolder)
00299     {
00300       err = scopeFolder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(m_db));
00301     }
00302     else
00303       return err; // not sure why m_folder wouldn't be set.
00304 
00305     switch (err)
00306     {
00307         case NS_OK:
00308             break;
00309         case NS_MSG_ERROR_FOLDER_SUMMARY_MISSING:
00310         case NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE:
00311           {
00312             nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(scopeFolder, &err);
00313             if (NS_SUCCEEDED(err) && localFolder)
00314             {
00315               nsCOMPtr<nsIMsgSearchSession> searchSession;
00316               m_scope->GetSearchSession(getter_AddRefs(searchSession));
00317               if (searchSession)
00318               {
00319                 nsCOMPtr <nsIMsgWindow> searchWindow;
00320 
00321                 searchSession->GetWindow(getter_AddRefs(searchWindow));
00322                 searchSession->PauseSearch();
00323                 localFolder->ParseFolder(searchWindow, this);
00324               }
00325             }
00326           }
00327             break;
00328         default:
00329         {
00330           NS_ASSERTION(PR_FALSE, "unexpected error opening db");
00331         }
00332     }
00333 
00334     return err;
00335 }
00336 
00337 
00338 nsresult
00339 nsMsgSearchOfflineMail::MatchTermsForFilter(nsIMsgDBHdr *msgToMatch,
00340                                             nsISupportsArray *termList,
00341                                             const char *defaultCharset,
00342                                             nsIMsgSearchScopeTerm * scope,
00343                                             nsIMsgDatabase * db, 
00344                                             const char * headers,
00345                                             PRUint32 headerSize,
00346                                             nsMsgSearchBoolExpression ** aExpressionTree,
00347                                             PRBool *pResult)
00348 {
00349     return MatchTerms(msgToMatch, termList, defaultCharset, scope, db, headers, headerSize, PR_TRUE, aExpressionTree, pResult);
00350 }
00351 
00352 // static method which matches a header against a list of search terms.
00353 nsresult
00354 nsMsgSearchOfflineMail::MatchTermsForSearch(nsIMsgDBHdr *msgToMatch, 
00355                                             nsISupportsArray* termList,
00356                                             const char *defaultCharset,
00357                                             nsIMsgSearchScopeTerm *scope,
00358                                             nsIMsgDatabase *db,
00359                                             nsMsgSearchBoolExpression ** aExpressionTree,
00360                                             PRBool *pResult)
00361 {
00362 
00363     return MatchTerms(msgToMatch, termList, defaultCharset, scope, db, nsnull, 0, PR_FALSE, aExpressionTree, pResult);
00364 }
00365 
00366 nsresult nsMsgSearchOfflineMail::ConstructExpressionTree(nsISupportsArray * termList,
00367                                             PRUint32 termCount,
00368                                             PRUint32 &aStartPosInList,
00369                                             nsMsgSearchBoolExpression ** aExpressionTree)
00370 {
00371   nsMsgSearchBoolExpression * finalExpression = *aExpressionTree;
00372 
00373   if (!finalExpression)
00374     finalExpression = new nsMsgSearchBoolExpression(); 
00375 
00376   while (aStartPosInList < termCount)
00377   {
00378       nsCOMPtr<nsIMsgSearchTerm> pTerm;
00379       termList->QueryElementAt(aStartPosInList, NS_GET_IID(nsIMsgSearchTerm), (void **)getter_AddRefs(pTerm));
00380       NS_ASSERTION (pTerm, "couldn't get term to match");
00381 
00382       PRBool beginsGrouping;
00383       PRBool endsGrouping;
00384       pTerm->GetBeginsGrouping(&beginsGrouping);
00385       pTerm->GetEndsGrouping(&endsGrouping);
00386 
00387       if (beginsGrouping)
00388       {
00389           //temporarily turn off the grouping for our recursive call
00390           pTerm->SetBeginsGrouping(PR_FALSE); 
00391           nsMsgSearchBoolExpression * innerExpression = new nsMsgSearchBoolExpression(); 
00392 
00393           // the first search term in the grouping is the one that holds the operator for how this search term
00394           // should be joined with the expressions to it's left. 
00395           PRBool booleanAnd;
00396           pTerm->GetBooleanAnd(&booleanAnd);
00397 
00398           // now add this expression tree to our overall expression tree...
00399           finalExpression = nsMsgSearchBoolExpression::AddExpressionTree(finalExpression, innerExpression, booleanAnd);
00400 
00401           // recursively process this inner expression
00402           ConstructExpressionTree(termList, termCount, aStartPosInList, 
00403             &finalExpression->m_rightChild);
00404 
00405           // undo our damage
00406           pTerm->SetBeginsGrouping(PR_TRUE); 
00407 
00408       }
00409       else
00410       {
00411         finalExpression = nsMsgSearchBoolExpression::AddSearchTerm(finalExpression, pTerm, nsnull);    // add the term to the expression tree
00412 
00413         if (endsGrouping)
00414           break;
00415       }
00416 
00417       aStartPosInList++;
00418   } // while we still have terms to process in this group
00419 
00420   *aExpressionTree = finalExpression;
00421 
00422   return NS_OK; 
00423 }
00424 
00425 nsresult nsMsgSearchOfflineMail::ProcessSearchTerm(nsIMsgDBHdr *msgToMatch,
00426                                             nsIMsgSearchTerm * aTerm,
00427                                             const char *defaultCharset,
00428                                             nsIMsgSearchScopeTerm * scope,
00429                                             nsIMsgDatabase * db, 
00430                                             const char * headers,
00431                                             PRUint32 headerSize,
00432                                             PRBool Filtering,
00433                                                                              PRBool *pResult) 
00434 {
00435     nsresult err = NS_OK;
00436     nsXPIDLCString  recipients;
00437     nsXPIDLCString  ccList;
00438     nsXPIDLCString  matchString;
00439     nsXPIDLCString  msgCharset;
00440     const char *charset;
00441     PRBool charsetOverride = PR_FALSE; /* XXX BUG 68706 */
00442     PRUint32 msgFlags;
00443     PRBool result;
00444     PRBool matchAll;
00445 
00446     NS_ENSURE_ARG_POINTER(pResult);
00447 
00448     aTerm->GetMatchAll(&matchAll);
00449     if (matchAll)
00450     {
00451       *pResult = PR_TRUE;
00452       return NS_OK;
00453     }
00454     *pResult = PR_FALSE;
00455 
00456     nsMsgSearchAttribValue attrib;
00457     aTerm->GetAttrib(&attrib);
00458     msgToMatch->GetCharset(getter_Copies(msgCharset));
00459     charset = (const char*)msgCharset;
00460     if (!charset || !*charset)
00461       charset = (const char*)defaultCharset;
00462     msgToMatch->GetFlags(&msgFlags);
00463 
00464     switch (attrib)
00465     {
00466       case nsMsgSearchAttrib::Sender:
00467         msgToMatch->GetAuthor(getter_Copies(matchString));
00468         err = aTerm->MatchRfc822String (matchString, charset, charsetOverride, &result);
00469         break;
00470       case nsMsgSearchAttrib::Subject:
00471       {
00472         msgToMatch->GetSubject(getter_Copies(matchString) /* , PR_TRUE */);
00473         if (msgFlags & MSG_FLAG_HAS_RE)
00474         { 
00475           // Make sure we pass along the "Re: " part of the subject if this is a reply.
00476           nsXPIDLCString reString;
00477           reString.Assign("Re: ");
00478           reString.Append(matchString);
00479           err = aTerm->MatchRfc2047String(reString, charset, charsetOverride, &result);
00480         }
00481         else
00482           err = aTerm->MatchRfc2047String (matchString, charset, charsetOverride, &result);
00483         break;
00484       }
00485       case nsMsgSearchAttrib::ToOrCC:
00486       {
00487         PRBool boolKeepGoing;
00488         aTerm->GetMatchAllBeforeDeciding(&boolKeepGoing);
00489         msgToMatch->GetRecipients(getter_Copies(recipients));
00490         err = aTerm->MatchRfc822String (recipients, charset, charsetOverride, &result);
00491         if (boolKeepGoing == result)
00492         {
00493           msgToMatch->GetCcList(getter_Copies(ccList));
00494           err = aTerm->MatchRfc822String (ccList, charset, charsetOverride, &result);
00495         }
00496         break;
00497       }
00498       case nsMsgSearchAttrib::Body:
00499        {
00500          nsMsgKey messageOffset;
00501          PRUint32 lineCount;
00502          msgToMatch->GetMessageOffset(&messageOffset);
00503          msgToMatch->GetLineCount(&lineCount);
00504          err = aTerm->MatchBody (scope, messageOffset, lineCount, charset, msgToMatch, db, &result);
00505          break;
00506        }
00507       case nsMsgSearchAttrib::Date:
00508       {
00509         PRTime date;
00510         msgToMatch->GetDate(&date);
00511         err = aTerm->MatchDate (date, &result);
00512 
00513         break;
00514       }
00515       case nsMsgSearchAttrib::HasAttachmentStatus:
00516       case nsMsgSearchAttrib::MsgStatus:
00517         err = aTerm->MatchStatus (msgFlags, &result);
00518         break;
00519       case nsMsgSearchAttrib::Priority:
00520       {
00521         nsMsgPriorityValue msgPriority;
00522         msgToMatch->GetPriority(&msgPriority);
00523         err = aTerm->MatchPriority (msgPriority, &result);
00524         break;
00525       }      
00526       case nsMsgSearchAttrib::Size:
00527       {
00528         PRUint32 messageSize;
00529         msgToMatch->GetMessageSize(&messageSize);
00530         err = aTerm->MatchSize (messageSize, &result);
00531         break;
00532       }
00533       case nsMsgSearchAttrib::To:
00534         msgToMatch->GetRecipients(getter_Copies(recipients));
00535         err = aTerm->MatchRfc822String(recipients, charset, charsetOverride, &result);
00536         break;
00537       case nsMsgSearchAttrib::CC:
00538         msgToMatch->GetCcList(getter_Copies(ccList));
00539         err = aTerm->MatchRfc822String (ccList, charset, charsetOverride, &result);
00540         break;
00541       case nsMsgSearchAttrib::AgeInDays:
00542       {
00543         PRTime date;
00544         msgToMatch->GetDate(&date);
00545         err = aTerm->MatchAge (date, &result);
00546         break;
00547        }
00548       case nsMsgSearchAttrib::Label:
00549       {
00550          nsMsgLabelValue label;
00551          msgToMatch->GetLabel(&label);
00552          err = aTerm->MatchLabel(label, &result);
00553          break;
00554       }    
00555       case nsMsgSearchAttrib::Keywords:
00556       {
00557           nsXPIDLCString keywords;
00558           nsMsgLabelValue label;
00559           msgToMatch->GetStringProperty("keywords", getter_Copies(keywords));
00560           msgToMatch->GetLabel(&label);
00561           if (label >= 1)
00562           {
00563             if (!keywords.IsEmpty())
00564               keywords.Append(' ');
00565             keywords.Append("$label");
00566             keywords.Append(label + '0');
00567           }
00568           err = aTerm->MatchKeyword(keywords.get(), &result);
00569           break;
00570       }
00571       case nsMsgSearchAttrib::JunkStatus:
00572       {
00573          nsXPIDLCString junkScoreStr;
00574          msgToMatch->GetStringProperty("junkscore", getter_Copies(junkScoreStr));
00575          err = aTerm->MatchJunkStatus(junkScoreStr, &result);
00576          break; 
00577       }
00578       default:
00579           // XXX todo
00580           // for the temporary return receipts filters, we use a custom header for Content-Type
00581           // but unlike the other custom headers, this one doesn't show up in the search / filter
00582           // UI.  we set the attrib to be nsMsgSearchAttrib::OtherHeader, where as for user
00583           // defined custom headers start at nsMsgSearchAttrib::OtherHeader + 1
00584           // Not sure if there is a better way to do this yet.  Maybe reserve the last
00585           // custom header for ::Content-Type?  But if we do, make sure that change
00586           // doesn't cause nsMsgFilter::GetTerm() to change, and start making us
00587           // ask IMAP servers for the Content-Type header on all messages.
00588           if ( attrib >= nsMsgSearchAttrib::OtherHeader && attrib < nsMsgSearchAttrib::kNumMsgSearchAttributes)
00589           {
00590             PRUint32 lineCount;
00591             msgToMatch->GetLineCount(&lineCount);
00592             nsMsgKey messageKey;
00593             msgToMatch->GetMessageOffset(&messageKey);
00594             err = aTerm->MatchArbitraryHeader (scope, messageKey, lineCount,charset, charsetOverride,
00595                                                 msgToMatch, db, headers, headerSize, Filtering, &result);
00596           }
00597           else
00598             err = NS_ERROR_INVALID_ARG; // ### was SearchError_InvalidAttribute
00599     }
00600 
00601     *pResult = result;
00602     return NS_OK;
00603 }
00604 
00605 nsresult nsMsgSearchOfflineMail::MatchTerms(nsIMsgDBHdr *msgToMatch,
00606                                             nsISupportsArray * termList,
00607                                             const char *defaultCharset,
00608                                             nsIMsgSearchScopeTerm * scope,
00609                                             nsIMsgDatabase * db, 
00610                                             const char * headers,
00611                                             PRUint32 headerSize,
00612                                             PRBool Filtering,
00613                                             nsMsgSearchBoolExpression ** aExpressionTree,
00614                                             PRBool *pResult) 
00615 {
00616   NS_ENSURE_ARG(aExpressionTree);
00617   nsresult err;
00618 
00619   if (!*aExpressionTree)
00620   {
00621     PRUint32 initialPos = 0; 
00622     PRUint32 count;
00623     termList->Count(&count);
00624     err = ConstructExpressionTree(termList, count, initialPos, aExpressionTree);
00625     if (NS_FAILED(err))
00626       return err;
00627   }
00628 
00629   // evaluate the expression tree and return the result
00630   *pResult = (*aExpressionTree) 
00631     ?  (*aExpressionTree)->OfflineEvaluate(msgToMatch,
00632                  defaultCharset, scope, db, headers, headerSize, Filtering)
00633     :PR_TRUE; // vacuously true...
00634 
00635   return NS_OK;
00636 }
00637 
00638 
00639 nsresult nsMsgSearchOfflineMail::Search (PRBool *aDone)
00640 {
00641   nsresult err = NS_OK;
00642   
00643   NS_ENSURE_ARG(aDone);
00644   nsresult dbErr = NS_OK;
00645   nsCOMPtr<nsIMsgDBHdr> msgDBHdr;
00646   nsMsgSearchBoolExpression *expressionTree = nsnull;
00647   
00648   const PRUint32 kTimeSliceInMS = 200;
00649 
00650   *aDone = PR_FALSE;
00651   // Try to open the DB lazily. This will set up a parser if one is required
00652   if (!m_db)
00653     err = OpenSummaryFile ();
00654   if (!m_db)  // must be reparsing.
00655     return err;
00656   
00657   // Reparsing is unnecessary or completed
00658   if (NS_SUCCEEDED(err))
00659   {
00660     if (!m_listContext)
00661       dbErr = m_db->EnumerateMessages (getter_AddRefs(m_listContext));
00662     if (NS_SUCCEEDED(dbErr) && m_listContext)
00663     {
00664       PRIntervalTime startTime = PR_IntervalNow();
00665       while (!*aDone)  // we'll break out of the loop after kTimeSliceInMS milliseconds
00666       {
00667         nsCOMPtr<nsISupports> currentItem;
00668       
00669         dbErr = m_listContext->GetNext(getter_AddRefs(currentItem));
00670         if(NS_SUCCEEDED(dbErr))
00671         {
00672           msgDBHdr = do_QueryInterface(currentItem, &dbErr);
00673         }
00674         if (NS_FAILED(dbErr))      
00675           *aDone = PR_TRUE; //###phil dbErr is dropped on the floor. just note that we did have an error so we'll clean up later
00676         else
00677         {
00678           PRBool match = PR_FALSE;
00679           nsXPIDLString nullCharset, folderCharset;
00680           GetSearchCharsets(getter_Copies(nullCharset), getter_Copies(folderCharset));
00681           NS_ConvertUCS2toUTF8 charset(folderCharset);
00682           // Is this message a hit?
00683           err = MatchTermsForSearch (msgDBHdr, m_searchTerms, charset.get(), m_scope, m_db, &expressionTree, &match);
00684           // Add search hits to the results list
00685           if (NS_SUCCEEDED(err) && match)
00686           {
00687             AddResultElement (msgDBHdr);
00688           }
00689           PRIntervalTime elapsedTime;
00690           LL_SUB(elapsedTime, PR_IntervalNow(), startTime);
00691           // check if more than kTimeSliceInMS milliseconds have elapsed in this time slice started
00692           if (PR_IntervalToMilliseconds(elapsedTime) > kTimeSliceInMS)
00693             break;
00694         }
00695       }
00696     }    
00697   }
00698   else
00699     *aDone = PR_TRUE; // we couldn't open up the DB. This is an unrecoverable error so mark the scope as done.
00700   
00701   delete expressionTree;
00702 
00703   // in the past an error here would cause an "infinite" search because the url would continue to run...
00704   // i.e. if we couldn't open the database, it returns an error code but the caller of this function says, oh,
00705   // we did not finish so continue...what we really want is to treat this current scope as done
00706   if (*aDone)
00707     CleanUpScope(); // Do clean up for end-of-scope processing
00708   return err;
00709 }
00710 
00711 void nsMsgSearchOfflineMail::CleanUpScope()
00712 {
00713   // Let go of the DB when we're done with it so we don't kill the db cache
00714   if (m_db)
00715   {
00716     m_listContext = nsnull; 
00717     m_db->Close(PR_FALSE);
00718   }
00719   
00720   m_db = nsnull;
00721   m_scope->SetInputStream(nsnull);
00722 }
00723 
00724 NS_IMETHODIMP nsMsgSearchOfflineMail::AddResultElement (nsIMsgDBHdr *pHeaders)
00725 {
00726     nsresult err = NS_OK;
00727 
00728     nsCOMPtr<nsIMsgSearchSession> searchSession;
00729     m_scope->GetSearchSession(getter_AddRefs(searchSession));
00730     if (searchSession)
00731     {
00732       nsCOMPtr <nsIMsgFolder> scopeFolder;
00733       err = m_scope->GetFolder(getter_AddRefs(scopeFolder));
00734       searchSession->AddSearchHit(pHeaders, scopeFolder);
00735     }
00736     return err;
00737 }
00738 
00739 NS_IMETHODIMP
00740 nsMsgSearchOfflineMail::Abort ()
00741 {
00742     // Let go of the DB when we're done with it so we don't kill the db cache
00743     if (m_db)
00744         m_db->Close(PR_TRUE /* commit in case we downloaded new headers */);
00745     m_db = nsnull;
00746     return nsMsgSearchAdapter::Abort ();
00747 }
00748 
00749 /* void OnStartRunningUrl (in nsIURI url); */
00750 NS_IMETHODIMP nsMsgSearchOfflineMail::OnStartRunningUrl(nsIURI *url)
00751 {
00752     return NS_OK;
00753 }
00754 
00755 /* void OnStopRunningUrl (in nsIURI url, in nsresult aExitCode); */
00756 NS_IMETHODIMP nsMsgSearchOfflineMail::OnStopRunningUrl(nsIURI *url, nsresult aExitCode)
00757 {
00758   nsCOMPtr<nsIMsgSearchSession> searchSession;
00759   if (m_scope)
00760     m_scope->GetSearchSession(getter_AddRefs(searchSession));
00761   if (searchSession)
00762     searchSession->ResumeSearch();
00763 
00764   return NS_OK;
00765 }
00766 
00767 nsMsgSearchOfflineNews::nsMsgSearchOfflineNews (nsIMsgSearchScopeTerm *scope, nsISupportsArray *termList) : nsMsgSearchOfflineMail (scope, termList)
00768 {
00769 }
00770 
00771 
00772 nsMsgSearchOfflineNews::~nsMsgSearchOfflineNews ()
00773 {
00774 }
00775 
00776 
00777 nsresult nsMsgSearchOfflineNews::OpenSummaryFile ()
00778 {
00779   nsresult err = NS_OK;
00780   nsCOMPtr <nsIDBFolderInfo>  folderInfo;
00781   nsCOMPtr <nsIMsgFolder> scopeFolder;
00782   err = m_scope->GetFolder(getter_AddRefs(scopeFolder));
00783   // code here used to check if offline store existed, which breaks offline news.
00784   if (NS_SUCCEEDED(err) && scopeFolder)
00785     err = scopeFolder->GetMsgDatabase(nsnull, getter_AddRefs(m_db));
00786   return err;
00787 }
00788 
00789 nsresult nsMsgSearchOfflineNews::ValidateTerms ()
00790 {
00791   return nsMsgSearchOfflineMail::ValidateTerms ();
00792 }
00793 
00794 
00795 //-----------------------------------------------------------------------------
00796 nsresult nsMsgSearchValidityManager::InitLocalNewsTable()
00797 {
00798   NS_ASSERTION (nsnull == m_localNewsTable, "already have local news validty table");
00799   nsresult err = NewTable (getter_AddRefs(m_localNewsTable));
00800   
00801   if (NS_SUCCEEDED(err))
00802   {
00803     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Contains, 1);
00804     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Contains, 1);
00805     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Is, 1);
00806     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Is, 1);
00807     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::BeginsWith, 1);
00808     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::Sender, nsMsgSearchOp::BeginsWith, 1);
00809     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::EndsWith, 1);
00810     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::Sender, nsMsgSearchOp::EndsWith, 1);
00811     
00812     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Contains, 1);
00813     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Contains, 1);
00814     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Is, 1);
00815     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Is, 1);
00816     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::BeginsWith, 1);
00817     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::Subject, nsMsgSearchOp::BeginsWith, 1);
00818     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::EndsWith, 1);
00819     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::Subject, nsMsgSearchOp::EndsWith, 1);
00820     
00821     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Body, nsMsgSearchOp::Contains, 1);
00822     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::Body, nsMsgSearchOp::Contains, 1);
00823     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Body, nsMsgSearchOp::DoesntContain, 1);
00824     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::Body, nsMsgSearchOp::DoesntContain, 1);
00825     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Body, nsMsgSearchOp::Is, 1);
00826     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::Body, nsMsgSearchOp::Is, 1);
00827     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Body, nsMsgSearchOp::Isnt, 1);
00828     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::Body, nsMsgSearchOp::Isnt, 1);
00829     
00830     
00831     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::Date, nsMsgSearchOp::IsBefore, 1);
00832     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Date, nsMsgSearchOp::IsAfter, 1);
00833     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::Date, nsMsgSearchOp::IsAfter, 1);
00834     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Date, nsMsgSearchOp::Is, 1);
00835     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::Date, nsMsgSearchOp::Is, 1);
00836     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::Date, nsMsgSearchOp::Isnt, 1);
00837     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::Date, nsMsgSearchOp::Isnt, 1);
00838     
00839     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::AgeInDays, nsMsgSearchOp::IsGreaterThan, 1);
00840     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::AgeInDays, nsMsgSearchOp::IsGreaterThan, 1);
00841     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::AgeInDays, nsMsgSearchOp::IsLessThan,  1);
00842     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::AgeInDays, nsMsgSearchOp::IsLessThan, 1);
00843     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::AgeInDays, nsMsgSearchOp::Is,  1);
00844     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::AgeInDays, nsMsgSearchOp::Is, 1);
00845     
00846     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::MsgStatus, nsMsgSearchOp::Is, 1);
00847     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::MsgStatus, nsMsgSearchOp::Is, 1);
00848     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::MsgStatus, nsMsgSearchOp::Isnt, 1);
00849     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::MsgStatus, nsMsgSearchOp::Isnt, 1);
00850     
00851     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Contains, 1);
00852     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Contains, 1);
00853     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Is, 1);
00854     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Is, 1);
00855     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::BeginsWith, 1);
00856     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::BeginsWith, 1);
00857     m_localNewsTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::EndsWith, 1);
00858     m_localNewsTable->SetEnabled   (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::EndsWith, 1);
00859     
00860     
00861   }
00862   
00863   return err;
00864 }
00865 
00866