gecko-dev/lib/libmsg/msgmsrch.cpp

1315 lines
45 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
// Implementation of search for POP and IMAP mail folders
#include "msg.h"
#include "pmsgsrch.h"
#include "prsembst.h"
#include "maildb.h"
#include "newsdb.h"
#include "grpinfo.h"
#include "msgfinfo.h"
#include "msgmpane.h"
#include "imap.h"
#include "msgprefs.h"
#include "xpgetstr.h"
#include "mailhdr.h"
#include "libi18n.h"
#include "msgimap.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....
//----------------------------------------------------------------------------
CBoolExpression::CBoolExpression()
{
m_term = NULL;
m_boolOp = MSG_SearchBooleanAND;
m_encodingStr = NULL;
m_evalValue = FALSE;
m_leftChild = NULL;
m_rightChild = NULL;
}
CBoolExpression::CBoolExpression (MSG_SearchTerm * newTerm, XP_Bool 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;
// this expression does not contain sub expressions
m_leftChild = NULL;
m_rightChild = NULL;
}
CBoolExpression::CBoolExpression (CBoolExpression * expr1, CBoolExpression * expr2, MSG_SearchBooleanOp 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 = NULL;
m_evalValue = FALSE;
}
CBoolExpression::~CBoolExpression()
{
// 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;
}
CBoolExpression * CBoolExpression::AddSearchTerm(MSG_SearchTerm * newTerm, char * encodingStr)
// 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 leftToRightAddTerm(newTerm,FALSE,encodingStr);
}
CBoolExpression * CBoolExpression::AddSearchTerm(MSG_SearchTerm * newTerm, XP_Bool evalValue)
// appropriately add the search term to the current expression
// Returns: a pointer to the new expression which includes this new search term
{
return leftToRightAddTerm(newTerm, evalValue,NULL); // currently we build our expressions to
// evaluate left to right.
}
CBoolExpression * CBoolExpression::leftToRightAddTerm(MSG_SearchTerm * newTerm, XP_Bool 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;
}
CBoolExpression * tempExpr = new CBoolExpression (newTerm,evalValue,encodingStr);
if (tempExpr) // make sure creation succeeded
{
CBoolExpression * newExpr = new CBoolExpression (this, tempExpr, newTerm->GetBooleanOp());
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
}
XP_Bool CBoolExpression::OfflineEvaluate()
// returns TRUE or 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 TRUE or 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
XP_Bool result1 = TRUE; // always default to false positives
XP_Bool result2 = TRUE;
if (m_leftChild)
result1 = m_leftChild->OfflineEvaluate();
if (m_rightChild)
result2 = m_rightChild->OfflineEvaluate();
if (m_boolOp == MSG_SearchBooleanOR)
{
if (result1 || result2)
return TRUE;
}
if (m_boolOp == MSG_SearchBooleanAND)
{
if (result1 && result2)
return TRUE;
}
return FALSE;
}
int32 CBoolExpression::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 XP_STRLEN(m_encodingStr);
if (m_boolOp == MSG_SearchBooleanOR)
return sizeOfORTerm + m_leftChild->CalcEncodeStrSize() + m_rightChild->CalcEncodeStrSize();
if (m_boolOp == MSG_SearchBooleanAND)
return sizeOfANDTerm + m_leftChild->CalcEncodeStrSize() + m_rightChild->CalcEncodeStrSize();
return 0;
}
int32 CBoolExpression::GenerateEncodeStr(char * buffer, int32 bufSize)
// recurively combine sub expressions to form a single IMAP/NNTP encoded string
// assumes buffer[0] == '\0'
// RETURNS: number of bytes copied into the buffer
{
if (bufSize < CalcEncodeStrSize() || (!m_term && (!m_leftChild || !m_rightChild))) // is expression empty?
return 0;
if (m_term) // are we a leaf expression?
{
XP_STRCAT(buffer, m_encodingStr);
return XP_STRLEN(m_encodingStr);
}
// add encode strings of each sub expression
int32 numBytesAdded = 0;
if (m_boolOp == MSG_SearchBooleanOR)
{
char * marker = buffer;
XP_STRCAT(buffer, " (OR");
buffer[4] = '\0'; // terminate the string
buffer += 4; // advance buffer
bufSize -= 4; // adjust remaining buffer size
numBytesAdded = m_leftChild->GenerateEncodeStr(buffer, bufSize); // insert left expression into the buffer
buffer += numBytesAdded;
buffer[0] = '\0';
bufSize -= numBytesAdded;
numBytesAdded = m_rightChild->GenerateEncodeStr(buffer, bufSize); // insert right expression into the buffer
// hack. 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.
if (buffer[numBytesAdded-1] == ' ')
numBytesAdded--;
buffer[numBytesAdded++] = ')';
buffer[numBytesAdded] = '\0';
return XP_STRLEN(marker); // return # bytes we have added to the beginning of the buffer
}
if (m_boolOp == MSG_SearchBooleanAND)
{
char * marker = buffer;
buffer[0] = '\0';
numBytesAdded = m_leftChild->GenerateEncodeStr(buffer, bufSize); // insert left expression
buffer += numBytesAdded;
bufSize -= numBytesAdded;
buffer[0] = '\0';
numBytesAdded = m_rightChild->GenerateEncodeStr(buffer, bufSize);
buffer += numBytesAdded;
buffer[numBytesAdded] = '\0';
return XP_STRLEN(marker);
}
return 0;
}
//---------------- Adapter class for searching offline IMAP folders -----------
//-----------------------------------------------------------------------------
msg_SearchIMAPOfflineMail::msg_SearchIMAPOfflineMail (MSG_ScopeTerm *scope, MSG_SearchTermArray &termList) : msg_SearchOfflineMail(scope, termList)
{
}
msg_SearchIMAPOfflineMail::~msg_SearchIMAPOfflineMail()
{
}
MSG_SearchError msg_SearchIMAPOfflineMail::ValidateTerms ()
{
// most of this was copied from MSG_SearchOffline::ValidateTerms()....Difference: When using IMAP offline, we do not
// have a mail folder to validate.
MSG_SearchError err = msg_SearchAdapter::ValidateTerms ();
if (SearchError_Success == err)
{
// Mail folder must exist. Don't worry about the summary file now since we may
// have to regenerate the index later
// XP_StatStruct fileStatus;
// if (!XP_Stat (m_scope->GetMailPath(), &fileStatus, xpMailFolder))
// {
// Make sure the terms themselves are valid
msg_SearchValidityTable *table = NULL;
err = gValidityMgr.GetTable (msg_SearchValidityManager::offlineMail, &table);
if (SearchError_Success == err)
{
XP_ASSERT (table);
err = table->ValidateTerms (m_searchTerms);
}
// }
// else
// XP_ASSERT(0);
}
return err;
}
//-----------------------------------------------------------------------------
//---------------- Adapter class for searching offline folders ----------------
//-----------------------------------------------------------------------------
msg_SearchOfflineMail::msg_SearchOfflineMail (MSG_ScopeTerm *scope, MSG_SearchTermArray &termList) : msg_SearchAdapter (scope, termList)
{
m_db = NULL;
m_listContext = NULL;
m_mailboxParser = NULL;
m_parserState = kOpenFolderState;
}
msg_SearchOfflineMail::~msg_SearchOfflineMail ()
{
// Database should have been closed when the scope term finished.
XP_ASSERT(!m_db);
}
MSG_SearchError msg_SearchOfflineMail::ValidateTerms ()
{
MSG_SearchError err = msg_SearchAdapter::ValidateTerms ();
if (SearchError_Success == err)
{
// Mail folder must exist. Don't worry about the summary file now since we may
// have to regenerate the index later
XP_StatStruct fileStatus;
if (!XP_Stat (m_scope->GetMailPath(), &fileStatus, xpMailFolder))
{
// Make sure the terms themselves are valid
msg_SearchValidityTable *table = NULL;
err = gValidityMgr.GetTable (msg_SearchValidityManager::offlineMail, &table);
if (SearchError_Success == err)
{
XP_ASSERT (table);
err = table->ValidateTerms (m_searchTerms);
}
}
else
XP_ASSERT(0);
}
return err;
}
MSG_SearchError msg_SearchOfflineMail::OpenSummaryFile ()
{
MailDB *mailDb = NULL;
// do password protection of local cache thing.
if (m_scope->m_folder && m_scope->m_folder->UserNeedsToAuthenticateForFolder(FALSE) && m_scope->m_folder->GetMaster()->PromptForHostPassword(m_scope->m_frame->GetContext(), m_scope->m_folder) != 0)
return SearchError_DBOpenFailed;
MsgERR dbErr = MailDB::Open (m_scope->GetMailPath(), FALSE /*create?*/, &mailDb);
MSG_SearchError err = SearchError_Success;
switch (dbErr)
{
case eSUCCESS:
break;
case eDBExistsNot:
case eNoSummaryFile:
case eOldSummaryFile:
m_mailboxParser = new ParseMailboxState (m_scope->GetMailPath());
if (!m_mailboxParser)
err = SearchError_OutOfMemory;
else
{
// Remove the old summary file so maildb::open can create a new one
XP_FileRemove (m_scope->GetMailPath(), xpMailFolderSummary);
dbErr = MailDB::Open (m_scope->GetMailPath(), TRUE /*create?*/, &mailDb, TRUE /*upgrading?*/);
XP_ASSERT(mailDb);
// Initialize the async parser to rebuild the summary file
m_parserState = kOpenFolderState;
m_mailboxParser->SetContext (m_scope->m_frame->GetContext());
m_mailboxParser->SetDB (mailDb);
m_mailboxParser->SetFolder(m_scope->m_folder);
m_mailboxParser->SetIgnoreNonMailFolder(TRUE);
err = SearchError_Success;
}
break;
default:
{
#ifdef _DEBUG
char *buf = PR_smprintf ("Failed to open '%s' with error 0x%08lX", m_scope->m_folder->GetName(), (long) dbErr);
FE_Alert (m_scope->m_frame->GetContext(), buf);
XP_FREE (buf);
#endif
err = SearchError_DBOpenFailed;
}
}
if (mailDb && err == SearchError_Success)
m_db = mailDb;
return err;
}
MSG_SearchError msg_SearchOfflineMail::BuildSummaryFile ()
{
// State machine for rebuilding the summary file asynchronously in the
// middle of the already-asynchronous search.
MSG_SearchError err = SearchError_Success;
int mkErr = 0;
switch (m_parserState)
{
case kOpenFolderState:
mkErr = m_mailboxParser->BeginOpenFolderSock (m_scope->GetMailPath(), NULL, 0, NULL);
if (mkErr == MK_WAITING_FOR_CONNECTION)
m_parserState++;
else
err = SummaryFileError();
break;
case kParseMoreState:
mkErr = m_mailboxParser->ParseMoreFolderSock (m_scope->GetMailPath(), NULL, 0, NULL);
if (mkErr == MK_CONNECTED)
m_parserState++;
else
if (mkErr != MK_WAITING_FOR_CONNECTION)
err = SummaryFileError();
break;
case kCloseFolderState:
m_mailboxParser->CloseFolderSock (NULL, NULL, 0, NULL);
if (!m_mailboxParser->GetIsRealMailFolder())
{
// mailbox parser has already closed the db (right?)
XP_ASSERT(m_mailboxParser->GetDB() == 0);
m_db = NULL;
err = SearchError_ScopeDone;
}
delete m_mailboxParser;
m_mailboxParser = NULL;
// Put our regular "searching Inbox..." status text back up
m_scope->m_frame->UpdateStatusBar(MK_MSG_SEARCH_STATUS);
break;
}
return err;
}
MSG_SearchError msg_SearchOfflineMail::SummaryFileError ()
{
char *errTemplate = XP_GetString(MK_MSG_CANT_SEARCH_IF_NO_SUMMARY);
if (errTemplate)
{
char *prompt = PR_smprintf (errTemplate, m_scope->m_folder->GetName());
if (prompt)
{
FE_Alert (m_scope->m_frame->GetContext(), prompt);
XP_FREE(prompt);
}
}
// If we got a summary file error while parsing, clean up all the parser state
if (m_mailboxParser)
{
m_mailboxParser->CloseFolderSock (NULL, NULL, 0, NULL);
delete m_mailboxParser;
m_mailboxParser = NULL;
m_db = NULL;
}
return SearchError_ScopeDone;
}
MSG_SearchError msg_SearchOfflineMail::MatchTermsForFilter(DBMessageHdr *msgToMatch,
MSG_SearchTermArray & termList,
MSG_ScopeTerm * scope,
MessageDB * db,
char * headers,
uint32 headerSize)
{
return MatchTerms(msgToMatch, termList, scope, db, headers, headerSize, TRUE);
}
// static method which matches a header against a list of search terms.
MSG_SearchError msg_SearchOfflineMail::MatchTermsForSearch(DBMessageHdr *msgToMatch,
MSG_SearchTermArray &termList,
MSG_ScopeTerm *scope,
MessageDB *db)
{
return MatchTerms(msgToMatch, termList, scope, db, NULL, 0, FALSE);
}
MSG_SearchError msg_SearchOfflineMail::MatchTerms(DBMessageHdr *msgToMatch,
MSG_SearchTermArray & termList,
MSG_ScopeTerm * scope,
MessageDB * db,
char * headers,
uint32 headerSize,
XP_Bool Filtering)
{
MSG_SearchError err = SearchError_Success;
XPStringObj recipients;
XPStringObj ccList;
XPStringObj matchString;
MSG_DBHandle dbHandle = (db) ? db->GetDB() : 0;
#ifdef _DEBUG
// Use this to peek at the message since the atom IDs make the header strings opaque
MessageHdrStruct debugHdr;
msgToMatch->CopyToMessageHdr (&debugHdr);
#endif
// Don't even bother to look at expunged messages awaiting compression
if (msgToMatch->GetFlags() & kExpunged)
err = SearchError_NotAMatch;
// Loop over all terms, and match them all to this message.
int16 csid = scope->m_folder->GetFolderCSID() & ~CS_AUTO;
if (CS_DEFAULT == csid)
csid = INTL_DefaultWinCharSetID(0);
CBoolExpression * expression = new CBoolExpression(); // create our expression
if (!expression)
return SearchError_OutOfMemory;
for (int i = 0; i < termList.GetSize(); i++)
{
MSG_SearchTerm *pTerm = termList.GetAt(i);
XP_ASSERT (pTerm->IsValid());
XP_ASSERT (msgToMatch);
switch (pTerm->m_attribute)
{
case attribSender:
msgToMatch->GetAuthor(matchString, dbHandle);
err = pTerm->MatchRfc822String (matchString, csid);
break;
case attribSubject:
msgToMatch->GetSubject(matchString, TRUE, dbHandle);
err = pTerm->MatchString (matchString, csid);
break;
case attribToOrCC:
{
MSG_SearchError errKeepGoing = pTerm->MatchAllBeforeDeciding() ? SearchError_Success : SearchError_NotAMatch;
msgToMatch->GetRecipients(recipients, db->GetDB());
err = pTerm->MatchRfc822String (recipients, csid);
if (errKeepGoing == err)
{
msgToMatch->GetCCList(ccList, db->GetDB());
err = pTerm->MatchRfc822String (ccList, csid);
}
}
break;
case attribBody:
err = pTerm->MatchBody (scope, msgToMatch->GetArticleNum(), msgToMatch->GetLineCount(), csid, msgToMatch, db);
break;
case attribDate:
err = pTerm->MatchDate (msgToMatch->GetDate());
break;
case attribMsgStatus:
err = pTerm->MatchStatus (msgToMatch->GetFlags());
break;
case attribPriority:
err = pTerm->MatchPriority (msgToMatch->GetPriority());
break;
case attribSize:
err = pTerm->MatchSize (msgToMatch->GetByteLength());
break;
case attribTo:
msgToMatch->GetRecipients(recipients, db->GetDB());
err = pTerm->MatchRfc822String(recipients, csid);
break;
case attribCC:
msgToMatch->GetCCList(ccList, db->GetDB());
err = pTerm->MatchRfc822String (ccList, csid);
break;
case attribAgeInDays:
err = pTerm->MatchAge (msgToMatch->GetDate());
break;
case attribOtherHeader:
err = pTerm->MatchArbitraryHeader (scope, msgToMatch->GetArticleNum(), msgToMatch->GetLineCount(),csid,
msgToMatch, db, headers, headerSize, Filtering);
break;
default:
err = SearchError_InvalidAttribute;
}
if (expression && (err == SearchError_Success || err == SearchError_NotAMatch))
expression = expression->AddSearchTerm(pTerm, (err == SearchError_Success)); // added the term and its value to the expression tree
else
return SearchError_OutOfMemory;
}
XP_Bool result = expression->OfflineEvaluate();
delete expression;
return result ? SearchError_Success : SearchError_NotAMatch;
}
MSG_SearchError msg_SearchOfflineMail::Search ()
{
MSG_SearchError err = SearchError_Success;
DBMessageHdr *pHeaders = NULL;
MsgERR dbErr = 0;
// If we need to parse the mailbox before searching it, give another time
// slice to the parser
if (m_mailboxParser)
err = BuildSummaryFile ();
else
// Try to open the DB lazily. This will set up a parser if one is required
if (!m_db)
err = OpenSummaryFile ();
// Reparsing is unnecessary or completed
if (m_mailboxParser == NULL && err == SearchError_Success)
{
XP_ASSERT (m_db);
if (!m_listContext)
dbErr = m_db->ListFirst (&m_listContext, &pHeaders);
else
dbErr = m_db->ListNext (m_listContext, &pHeaders);
if (eSUCCESS != dbErr)
{
// Do clean up for end-of-scope processing
err = SearchError_ScopeDone; //###phil dbErr is dropped on the floor
m_db->ListDone (m_listContext);
// 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();
m_db = NULL;
// If we were searching the body of the message, close the folder
if (m_scope->m_file)
XP_FileClose (m_scope->m_file);
m_scope->m_file = NULL;
return err;
}
else
// Is this message a hit?
err = MatchTermsForSearch (pHeaders, m_searchTerms, m_scope, m_db);
// Add search hits to the results list
if (SearchError_Success == err)
AddResultElement (pHeaders, m_db);
m_scope->m_frame->IncrementOfflineProgress();
delete pHeaders;
}
return err;
}
MSG_SearchError msg_SearchOfflineMail::AddResultElement (DBMessageHdr *pHeaders, MessageDB *db)
{
MSG_SearchError err = SearchError_Success;
MSG_DBHandle dbHandle = (db) ? db->GetDB() : 0;
MSG_ResultElement *newResult = new MSG_ResultElement (this);
if (newResult)
{
XP_ASSERT (newResult);
// This isn't very general. Just add the headers we think we'll be interested in
// to the list of attributes per result element.
MSG_SearchValue *pValue = new MSG_SearchValue;
if (pValue)
{
XPStringObj subject;
pValue->attribute = attribSubject;
char *reString = (pHeaders->GetFlags() & kHasRe) ? "Re: " : "";
pHeaders->GetSubject(subject, FALSE, dbHandle);
pValue->u.string = PR_smprintf ("%s%s", reString, (const char*) subject); // hack. invoke cast operator by force
newResult->AddValue (pValue);
}
pValue = new MSG_SearchValue;
if (pValue)
{
pValue->attribute = attribSender;
pValue->u.string = (char*) XP_ALLOC(64);
if (pValue->u.string)
{
pHeaders->GetAuthor(pValue->u.string, 64, dbHandle);
newResult->AddValue (pValue);
}
else
err = SearchError_OutOfMemory;
}
pValue = new MSG_SearchValue;
if (pValue)
{
pValue->attribute = attribDate;
pValue->u.date = pHeaders->GetDate();
newResult->AddValue (pValue);
}
pValue = new MSG_SearchValue;
if (pValue)
{
pValue->attribute = attribMsgStatus;
pValue->u.msgStatus = pHeaders->GetFlags();
newResult->AddValue (pValue);
}
pValue = new MSG_SearchValue;
if (pValue)
{
pValue->attribute = attribPriority;
pValue->u.priority = pHeaders->GetPriority();
newResult->AddValue (pValue);
}
pValue = new MSG_SearchValue;
if (pValue)
{
pValue->attribute = attribLocation;
pValue->u.string = XP_STRDUP(m_scope->m_folder->GetName());
newResult->AddValue (pValue);
}
pValue = new MSG_SearchValue;
if (pValue)
{
pValue->attribute = attribMessageKey;
pValue->u.key = pHeaders->GetMessageKey();
newResult->AddValue (pValue);
}
pValue = new MSG_SearchValue;
if (pValue)
{
pValue->attribute = attribSize;
pValue->u.size = pHeaders->GetByteLength();
newResult->AddValue (pValue);
}
if (!pValue)
err = SearchError_OutOfMemory;
m_scope->m_frame->AddResultElement (newResult);
}
return err;
}
int
msg_SearchOfflineMail::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();
m_db = NULL;
// If we got aborted in the middle of parsing a mail folder, we should
// free the parser object (esp. so it releases the folderInfo's semaphore)
if (m_mailboxParser)
delete m_mailboxParser;
m_mailboxParser = NULL;
return msg_SearchAdapter::Abort ();
}
//-----------------------------------------------------------------------------
//---------- Adapter class for searching online (IMAP) folders ----------------
//-----------------------------------------------------------------------------
const char *msg_SearchOnlineMail::m_kSearchTemplate = "SEARCH%s%s";
msg_SearchOnlineMail::msg_SearchOnlineMail (MSG_ScopeTerm *scope, MSG_SearchTermArray &termList) : msg_SearchAdapter (scope, termList)
{
m_encoding = NULL;
}
msg_SearchOnlineMail::~msg_SearchOnlineMail ()
{
XP_FREEIF(m_encoding);
}
MSG_SearchError msg_SearchOnlineMail::ValidateTerms ()
{
MSG_SearchError err = msg_SearchAdapter::ValidateTerms ();
if (SearchError_Success == err)
{
// ### mwelch Figure out the charsets to use
// for the search terms and targets.
int16 src_csid, dst_csid;
GetSearchCSIDs(src_csid, dst_csid);
// do IMAP specific validation
char *tmpEncoding = NULL;
err = Encode (&tmpEncoding, m_searchTerms, src_csid, dst_csid);
if (SearchError_Success == err)
{
// we are searching an online folder, right?
XP_ASSERT(m_scope->m_folder->GetType() == FOLDER_IMAPMAIL);
MSG_IMAPFolderInfoMail *imapFolder = (MSG_IMAPFolderInfoMail *) m_scope->m_folder;
m_encoding = CreateImapSearchUrl(imapFolder->GetHostName(),
imapFolder->GetOnlineName(),
imapFolder->GetOnlineHierarchySeparator(),
tmpEncoding,
TRUE); // return UIDs
delete [] tmpEncoding;
}
else
if (err == SearchError_ScopeAgreement)
XP_ASSERT(FALSE);
}
return err;
}
const char *msg_SearchOnlineMail::GetEncoding ()
{
return m_encoding;
}
void msg_SearchOnlineMail::PreExitFunction (URL_Struct * /*url*/, int status, MWContext *context)
{
MSG_SearchFrame *frame = MSG_SearchFrame::FromContext (context);
msg_SearchAdapter *adapter = frame->GetRunningAdapter();
if (status == MK_INTERRUPTED)
{
adapter->Abort();
frame->EndCylonMode();
}
else
{
frame->m_idxRunningScope++;
if (frame->m_idxRunningScope >= frame->m_scopeList.GetSize())
frame->EndCylonMode();
}
}
// taken from offline mail, talk to phil
MSG_SearchError msg_SearchOnlineMail::AddResultElement (DBMessageHdr *pHeaders, MessageDB *db)
{
MSG_SearchError err = SearchError_Success;
MSG_DBHandle dbHandle = (db) ? db->GetDB() : 0;
MSG_ResultElement *newResult = new MSG_ResultElement (this);
if (newResult)
{
XP_ASSERT (newResult);
// This isn't very general. Just add the headers we think we'll be interested in
// to the list of attributes per result element.
MSG_SearchValue *pValue = new MSG_SearchValue;
if (pValue)
{
XPStringObj subject;
pValue->attribute = attribSubject;
char *reString = (pHeaders->GetFlags() & kHasRe) ? "Re:" : "";
pHeaders->GetSubject(subject, FALSE, dbHandle);
pValue->u.string = PR_smprintf ("%s%s", reString, (const char*) subject); // hack. invoke cast operator by force
newResult->AddValue (pValue);
}
pValue = new MSG_SearchValue;
if (pValue)
{
pValue->attribute = attribSender;
pValue->u.string = (char*) XP_ALLOC(64);
if (pValue->u.string)
{
pHeaders->GetAuthor(pValue->u.string, 64, dbHandle);
newResult->AddValue (pValue);
}
else
err = SearchError_OutOfMemory;
}
pValue = new MSG_SearchValue;
if (pValue)
{
pValue->attribute = attribDate;
pValue->u.date = pHeaders->GetDate();
newResult->AddValue (pValue);
}
pValue = new MSG_SearchValue;
if (pValue)
{
pValue->attribute = attribMsgStatus;
pValue->u.msgStatus = pHeaders->GetFlags();
newResult->AddValue (pValue);
}
pValue = new MSG_SearchValue;
if (pValue)
{
pValue->attribute = attribPriority;
pValue->u.priority = pHeaders->GetPriority();
newResult->AddValue (pValue);
}
pValue = new MSG_SearchValue;
if (pValue)
{
pValue->attribute = attribLocation;
pValue->u.string = XP_STRDUP(m_scope->m_folder->GetName());
newResult->AddValue (pValue);
}
pValue = new MSG_SearchValue;
if (pValue)
{
pValue->attribute = attribMessageKey;
pValue->u.key = pHeaders->GetMessageKey();
newResult->AddValue (pValue);
}
pValue = new MSG_SearchValue;
if (pValue)
{
pValue->attribute = attribSize;
pValue->u.size = pHeaders->GetByteLength();
newResult->AddValue (pValue);
}
if (!pValue)
err = SearchError_OutOfMemory;
m_scope->m_frame->AddResultElement (newResult);
}
return err;
}
#define WHITESPACE " \015\012" // token delimiter
SEARCH_API void MSG_AddImapSearchHit (MWContext *context, const char *resultLine)
{
MSG_SearchFrame *frame = MSG_SearchFrame::FromContext (context);
if (frame)
{ // this search happened because of a search dialog
msg_SearchOnlineMail *adapter = (msg_SearchOnlineMail *) frame->GetRunningAdapter();
if (adapter)
{
// open the relevant IMAP db
MailDB *imapDB = NULL;
XP_Bool wasCreated = FALSE;
ImapMailDB::Open(adapter->m_scope->m_folder->GetMailFolderInfo()->GetPathname(),
FALSE, // do not create if not found
&imapDB,
adapter->m_scope->m_folder->GetMaster(),
&wasCreated);
if (imapDB)
{
// expect search results in the form of "* SEARCH <hit> <hit> ..."
char *tokenString = XP_STRDUP(resultLine);
if (tokenString)
{
char *currentPosition = strcasestr(tokenString, "SEARCH");
if (currentPosition)
{
currentPosition += XP_STRLEN("SEARCH");
XP_Bool shownUpdateAlert = FALSE;
char *hitUidToken = XP_STRTOK(currentPosition, WHITESPACE);
while (hitUidToken)
{
long naturalLong; // %l is 64 bits on OSF1
sscanf(hitUidToken, "%ld", &naturalLong);
MessageKey hitUid = (MessageKey) naturalLong;
MailMessageHdr *hitHeader = imapDB->GetMailHdrForKey(hitUid);
if (hitHeader)
adapter->AddResultElement(hitHeader, imapDB);
else if (!shownUpdateAlert)
{
FE_Alert(context, XP_GetString(MK_MSG_SEARCH_HITS_NOT_IN_DB));
shownUpdateAlert = TRUE;
}
hitUidToken = XP_STRTOK(NULL, WHITESPACE);
}
}
XP_FREE(tokenString);
}
imapDB->Close();
}
}
}
else
{
XP_ASSERT(FALSE); // apparently, this was left over from trying to do filtering on the server
}
}
MSG_SearchError msg_SearchOnlineMail::Search ()
{
// we should never end up here for a purely online
// folder. We might for an offline IMAP folder.
MSG_SearchError err = SearchError_NotImplemented;
return err;
}
MSG_SearchError msg_SearchOnlineMail::Encode (char **ppEncoding, MSG_SearchTermArray &searchTerms, int16 src_csid, int16 dest_csid)
{
*ppEncoding = NULL;
char *imapTerms = NULL;
// Get the optional CHARSET parameter, in case we need it.
char *csname = GetImapCharsetParam(dest_csid);
MSG_SearchError err = msg_SearchAdapter::EncodeImap (&imapTerms,searchTerms, src_csid, dest_csid, FALSE);
if (SearchError_Success == err)
{
int len = XP_STRLEN(m_kSearchTemplate) + XP_STRLEN(imapTerms) + (csname ? XP_STRLEN(csname) : 0) + 1;
*ppEncoding = new char [len];
if (*ppEncoding)
{
PR_snprintf (*ppEncoding, len, m_kSearchTemplate,
csname ? csname : "", imapTerms);
}
else
err = SearchError_OutOfMemory;
}
XP_FREEIF(csname);
return err;
}
MSG_SearchError msg_SearchValidityManager::InitOfflineMailTable ()
{
XP_ASSERT (NULL == m_offlineMailTable);
MSG_SearchError err = NewTable (&m_offlineMailTable);
if (SearchError_Success == err)
{
m_offlineMailTable->SetAvailable (attribSender, opContains, 1);
m_offlineMailTable->SetEnabled (attribSender, opContains, 1);
m_offlineMailTable->SetAvailable (attribSender, opDoesntContain, 1);
m_offlineMailTable->SetEnabled (attribSender, opDoesntContain, 1);
m_offlineMailTable->SetAvailable (attribSender, opIs, 1);
m_offlineMailTable->SetEnabled (attribSender, opIs, 1);
m_offlineMailTable->SetAvailable (attribSender, opIsnt, 1);
m_offlineMailTable->SetEnabled (attribSender, opIsnt, 1);
m_offlineMailTable->SetAvailable (attribSender, opBeginsWith, 1);
m_offlineMailTable->SetEnabled (attribSender, opBeginsWith, 1);
m_offlineMailTable->SetAvailable (attribSender, opEndsWith, 1);
m_offlineMailTable->SetEnabled (attribSender, opEndsWith, 1);
m_offlineMailTable->SetAvailable (attribTo, opContains, 1);
m_offlineMailTable->SetEnabled (attribTo, opContains, 1);
m_offlineMailTable->SetAvailable (attribTo, opDoesntContain, 1);
m_offlineMailTable->SetEnabled (attribTo, opDoesntContain, 1);
m_offlineMailTable->SetAvailable (attribTo, opIs, 1);
m_offlineMailTable->SetEnabled (attribTo, opIs, 1);
m_offlineMailTable->SetAvailable (attribTo, opIsnt, 1);
m_offlineMailTable->SetEnabled (attribTo, opIsnt, 1);
m_offlineMailTable->SetAvailable (attribTo, opBeginsWith, 1);
m_offlineMailTable->SetEnabled (attribTo, opBeginsWith, 1);
m_offlineMailTable->SetAvailable (attribTo, opEndsWith, 1);
m_offlineMailTable->SetEnabled (attribTo, opEndsWith, 1);
m_offlineMailTable->SetAvailable (attribCC, opContains, 1);
m_offlineMailTable->SetEnabled (attribCC, opContains, 1);
m_offlineMailTable->SetAvailable (attribCC, opDoesntContain, 1);
m_offlineMailTable->SetEnabled (attribCC, opDoesntContain, 1);
m_offlineMailTable->SetAvailable (attribCC, opIs, 1);
m_offlineMailTable->SetEnabled (attribCC, opIs, 1);
m_offlineMailTable->SetAvailable (attribCC, opIsnt, 1);
m_offlineMailTable->SetEnabled (attribCC, opIsnt, 1);
m_offlineMailTable->SetAvailable (attribCC, opBeginsWith, 1);
m_offlineMailTable->SetEnabled (attribCC, opBeginsWith, 1);
m_offlineMailTable->SetAvailable (attribCC, opEndsWith, 1);
m_offlineMailTable->SetEnabled (attribCC, opEndsWith, 1);
m_offlineMailTable->SetAvailable (attribToOrCC, opContains, 1);
m_offlineMailTable->SetEnabled (attribToOrCC, opContains, 1);
m_offlineMailTable->SetAvailable (attribToOrCC, opDoesntContain, 1);
m_offlineMailTable->SetEnabled (attribToOrCC, opDoesntContain, 1);
m_offlineMailTable->SetAvailable (attribToOrCC, opBeginsWith, 1);
m_offlineMailTable->SetEnabled (attribToOrCC, opBeginsWith, 1);
m_offlineMailTable->SetAvailable (attribToOrCC, opEndsWith, 1);
m_offlineMailTable->SetEnabled (attribToOrCC, opEndsWith, 1);
m_offlineMailTable->SetAvailable (attribSubject, opContains, 1);
m_offlineMailTable->SetEnabled (attribSubject, opContains, 1);
m_offlineMailTable->SetAvailable (attribSubject, opDoesntContain, 1);
m_offlineMailTable->SetEnabled (attribSubject, opDoesntContain, 1);
m_offlineMailTable->SetAvailable (attribSubject, opIs, 1);
m_offlineMailTable->SetEnabled (attribSubject, opIs, 1);
m_offlineMailTable->SetAvailable (attribSubject, opIsnt, 1);
m_offlineMailTable->SetEnabled (attribSubject, opIsnt, 1);
m_offlineMailTable->SetAvailable (attribSubject, opBeginsWith, 1);
m_offlineMailTable->SetEnabled (attribSubject, opBeginsWith, 1);
m_offlineMailTable->SetAvailable (attribSubject, opEndsWith, 1);
m_offlineMailTable->SetEnabled (attribSubject, opEndsWith, 1);
m_offlineMailTable->SetAvailable (attribBody, opContains, 1);
m_offlineMailTable->SetEnabled (attribBody, opContains, 1);
m_offlineMailTable->SetAvailable (attribBody, opDoesntContain, 1);
m_offlineMailTable->SetEnabled (attribBody, opDoesntContain, 1);
m_offlineMailTable->SetAvailable (attribBody, opIs, 1);
m_offlineMailTable->SetEnabled (attribBody, opIs, 1);
m_offlineMailTable->SetAvailable (attribBody, opIsnt, 1);
m_offlineMailTable->SetEnabled (attribBody, opIsnt, 1);
m_offlineMailTable->SetAvailable (attribDate, opIsBefore, 1);
m_offlineMailTable->SetEnabled (attribDate, opIsBefore, 1);
m_offlineMailTable->SetAvailable (attribDate, opIsAfter, 1);
m_offlineMailTable->SetEnabled (attribDate, opIsAfter, 1);
m_offlineMailTable->SetAvailable (attribDate, opIs, 1);
m_offlineMailTable->SetEnabled (attribDate, opIs, 1);
m_offlineMailTable->SetAvailable (attribDate, opIsnt, 1);
m_offlineMailTable->SetEnabled (attribDate, opIsnt, 1);
m_offlineMailTable->SetAvailable (attribPriority, opIsHigherThan, 1);
m_offlineMailTable->SetEnabled (attribPriority, opIsHigherThan, 1);
m_offlineMailTable->SetAvailable (attribPriority, opIsLowerThan, 1);
m_offlineMailTable->SetEnabled (attribPriority, opIsLowerThan, 1);
m_offlineMailTable->SetAvailable (attribPriority, opIs, 1);
m_offlineMailTable->SetEnabled (attribPriority, opIs, 1);
m_offlineMailTable->SetAvailable (attribMsgStatus, opIs, 1);
m_offlineMailTable->SetEnabled (attribMsgStatus, opIs, 1);
m_offlineMailTable->SetAvailable (attribMsgStatus, opIsnt, 1);
m_offlineMailTable->SetEnabled (attribMsgStatus, opIsnt, 1);
// m_offlineMailTable->SetValidButNotShown (attribAgeInDays, opIsHigherThan, 1);
// m_offlineMailTable->SetValidButNotShown (attribAgeInDays, opIsLowerThan, 1);
m_offlineMailTable->SetAvailable (attribAgeInDays, opIsGreaterThan, 1);
m_offlineMailTable->SetEnabled (attribAgeInDays, opIsGreaterThan, 1);
m_offlineMailTable->SetAvailable (attribAgeInDays, opIsLessThan, 1);
m_offlineMailTable->SetEnabled (attribAgeInDays, opIsLessThan, 1);
m_offlineMailTable->SetAvailable (attribAgeInDays, opIs, 1);
m_offlineMailTable->SetEnabled (attribAgeInDays, opIs, 1);
m_offlineMailTable->SetAvailable (attribOtherHeader, opContains, 1); // added for arbitrary headers
m_offlineMailTable->SetEnabled (attribOtherHeader, opContains, 1);
m_offlineMailTable->SetAvailable (attribOtherHeader, opDoesntContain, 1);
m_offlineMailTable->SetEnabled (attribOtherHeader, opDoesntContain, 1);
m_offlineMailTable->SetAvailable (attribOtherHeader, opIs, 1);
m_offlineMailTable->SetEnabled (attribOtherHeader, opIs, 1);
m_offlineMailTable->SetAvailable (attribOtherHeader, opIsnt, 1);
m_offlineMailTable->SetEnabled (attribOtherHeader, opIsnt, 1);
}
return err;
}
MSG_SearchError msg_SearchValidityManager::InitOnlineMailTable ()
{
XP_ASSERT (NULL == m_onlineMailTable);
MSG_SearchError err = NewTable (&m_onlineMailTable);
if (SearchError_Success == err)
{
m_onlineMailTable->SetAvailable (attribSender, opContains, 1);
m_onlineMailTable->SetEnabled (attribSender, opContains, 1);
m_onlineMailTable->SetAvailable (attribSender, opDoesntContain, 1);
m_onlineMailTable->SetEnabled (attribSender, opDoesntContain, 1);
m_onlineMailTable->SetAvailable (attribTo, opContains, 1);
m_onlineMailTable->SetEnabled (attribTo, opContains, 1);
m_onlineMailTable->SetAvailable (attribTo, opDoesntContain, 1);
m_onlineMailTable->SetEnabled (attribTo, opDoesntContain, 1);
m_onlineMailTable->SetAvailable (attribCC, opContains, 1);
m_onlineMailTable->SetEnabled (attribCC, opContains, 1);
m_onlineMailTable->SetAvailable (attribCC, opDoesntContain, 1);
m_onlineMailTable->SetEnabled (attribCC, opDoesntContain, 1);
m_onlineMailTable->SetAvailable (attribToOrCC, opContains, 1);
m_onlineMailTable->SetEnabled (attribToOrCC, opContains, 1);
m_onlineMailTable->SetAvailable (attribToOrCC, opDoesntContain, 1);
m_onlineMailTable->SetEnabled (attribToOrCC, opDoesntContain, 1);
m_onlineMailTable->SetAvailable (attribSubject, opContains, 1);
m_onlineMailTable->SetEnabled (attribSubject, opContains, 1);
m_onlineMailTable->SetAvailable (attribSubject, opDoesntContain, 1);
m_onlineMailTable->SetEnabled (attribSubject, opDoesntContain, 1);
m_onlineMailTable->SetAvailable (attribBody, opContains, 1);
m_onlineMailTable->SetEnabled (attribBody, opContains, 1);
m_onlineMailTable->SetAvailable (attribBody, opDoesntContain, 1);
m_onlineMailTable->SetEnabled (attribBody, opDoesntContain, 1);
m_onlineMailTable->SetAvailable (attribDate, opIsBefore, 1);
m_onlineMailTable->SetEnabled (attribDate, opIsBefore, 1);
m_onlineMailTable->SetAvailable (attribDate, opIsAfter, 1);
m_onlineMailTable->SetEnabled (attribDate, opIsAfter, 1);
m_onlineMailTable->SetAvailable (attribDate, opIs, 1);
m_onlineMailTable->SetEnabled (attribDate, opIs, 1);
m_onlineMailTable->SetAvailable (attribDate, opIsnt, 1);
m_onlineMailTable->SetEnabled (attribDate, opIsnt, 1);
m_onlineMailTable->SetAvailable (attribMsgStatus, opIs, 1);
m_onlineMailTable->SetEnabled (attribMsgStatus, opIs, 1);
m_onlineMailTable->SetAvailable (attribMsgStatus, opIsnt, 1);
m_onlineMailTable->SetEnabled (attribMsgStatus, opIsnt, 1);
m_onlineMailTable->SetAvailable (attribAgeInDays, opIsGreaterThan, 1);
m_onlineMailTable->SetEnabled (attribAgeInDays, opIsGreaterThan, 1);
m_onlineMailTable->SetAvailable (attribAgeInDays, opIsLessThan, 1);
m_onlineMailTable->SetEnabled (attribAgeInDays, opIsLessThan, 1);
m_onlineMailTable->SetEnabled (attribAgeInDays, opIs, 1);
m_onlineMailTable->SetAvailable (attribAgeInDays, opIs, 1);
m_onlineMailTable->SetAvailable (attribOtherHeader, opContains, 1); // added for arbitrary headers
m_onlineMailTable->SetEnabled (attribOtherHeader, opContains, 1);
m_onlineMailTable->SetAvailable (attribOtherHeader, opDoesntContain, 1);
m_onlineMailTable->SetEnabled (attribOtherHeader, opDoesntContain, 1);
}
return err;
}
MSG_SearchError msg_SearchValidityManager::InitOnlineMailFilterTable ()
{
// IMAP filtering happens on the client, fundamentally using the same
// capabilities as POP filtering. However, since we don't yet have the
// IMAP message body, we can't filter on body attributes. So this table
// is supposed to be the same as offline mail, except that the body
// attribute is omitted
XP_ASSERT (NULL == m_onlineMailFilterTable);
MSG_SearchError err = NewTable (&m_onlineMailFilterTable);
if (SearchError_Success == err)
{
m_onlineMailFilterTable->SetAvailable (attribSender, opContains, 1);
m_onlineMailFilterTable->SetEnabled (attribSender, opContains, 1);
m_onlineMailFilterTable->SetAvailable (attribSender, opDoesntContain, 1);
m_onlineMailFilterTable->SetEnabled (attribSender, opDoesntContain, 1);
m_onlineMailFilterTable->SetAvailable (attribSender, opIs, 1);
m_onlineMailFilterTable->SetEnabled (attribSender, opIs, 1);
m_onlineMailFilterTable->SetAvailable (attribSender, opIsnt, 1);
m_onlineMailFilterTable->SetEnabled (attribSender, opIsnt, 1);
m_onlineMailFilterTable->SetAvailable (attribSender, opBeginsWith, 1);
m_onlineMailFilterTable->SetEnabled (attribSender, opBeginsWith, 1);
m_onlineMailFilterTable->SetAvailable (attribSender, opEndsWith, 1);
m_onlineMailFilterTable->SetEnabled (attribSender, opEndsWith, 1);
m_onlineMailFilterTable->SetAvailable (attribTo, opContains, 1);
m_onlineMailFilterTable->SetEnabled (attribTo, opContains, 1);
m_onlineMailFilterTable->SetAvailable (attribTo, opDoesntContain, 1);
m_onlineMailFilterTable->SetEnabled (attribTo, opDoesntContain, 1);
m_onlineMailFilterTable->SetAvailable (attribTo, opIs, 1);
m_onlineMailFilterTable->SetEnabled (attribTo, opIs, 1);
m_onlineMailFilterTable->SetAvailable (attribTo, opIsnt, 1);
m_onlineMailFilterTable->SetEnabled (attribTo, opIsnt, 1);
m_onlineMailFilterTable->SetAvailable (attribTo, opBeginsWith, 1);
m_onlineMailFilterTable->SetEnabled (attribTo, opBeginsWith, 1);
m_onlineMailFilterTable->SetAvailable (attribTo, opEndsWith, 1);
m_onlineMailFilterTable->SetEnabled (attribTo, opEndsWith, 1);
m_onlineMailFilterTable->SetAvailable (attribCC, opContains, 1);
m_onlineMailFilterTable->SetEnabled (attribCC, opContains, 1);
m_onlineMailFilterTable->SetAvailable (attribCC, opDoesntContain, 1);
m_onlineMailFilterTable->SetEnabled (attribCC, opDoesntContain, 1);
m_onlineMailFilterTable->SetAvailable (attribCC, opIs, 1);
m_onlineMailFilterTable->SetEnabled (attribCC, opIs, 1);
m_onlineMailFilterTable->SetAvailable (attribCC, opIsnt, 1);
m_onlineMailFilterTable->SetEnabled (attribCC, opIsnt, 1);
m_onlineMailFilterTable->SetAvailable (attribCC, opBeginsWith, 1);
m_onlineMailFilterTable->SetEnabled (attribCC, opBeginsWith, 1);
m_onlineMailFilterTable->SetAvailable (attribCC, opEndsWith, 1);
m_onlineMailFilterTable->SetEnabled (attribCC, opEndsWith, 1);
m_onlineMailFilterTable->SetAvailable (attribToOrCC, opContains, 1);
m_onlineMailFilterTable->SetEnabled (attribToOrCC, opContains, 1);
m_onlineMailFilterTable->SetAvailable (attribToOrCC, opDoesntContain, 1);
m_onlineMailFilterTable->SetEnabled (attribToOrCC, opDoesntContain, 1);
m_onlineMailFilterTable->SetAvailable (attribToOrCC, opBeginsWith, 1);
m_onlineMailFilterTable->SetEnabled (attribToOrCC, opBeginsWith, 1);
m_onlineMailFilterTable->SetAvailable (attribToOrCC, opEndsWith, 1);
m_onlineMailFilterTable->SetEnabled (attribToOrCC, opEndsWith, 1);
m_onlineMailFilterTable->SetAvailable (attribSubject, opContains, 1);
m_onlineMailFilterTable->SetEnabled (attribSubject, opContains, 1);
m_onlineMailFilterTable->SetAvailable (attribSubject, opDoesntContain, 1);
m_onlineMailFilterTable->SetEnabled (attribSubject, opDoesntContain, 1);
m_onlineMailFilterTable->SetAvailable (attribSubject, opIs, 1);
m_onlineMailFilterTable->SetEnabled (attribSubject, opIs, 1);
m_onlineMailFilterTable->SetAvailable (attribSubject, opIsnt, 1);
m_onlineMailFilterTable->SetEnabled (attribSubject, opIsnt, 1);
m_onlineMailFilterTable->SetAvailable (attribSubject, opBeginsWith, 1);
m_onlineMailFilterTable->SetEnabled (attribSubject, opBeginsWith, 1);
m_onlineMailFilterTable->SetAvailable (attribSubject, opEndsWith, 1);
m_onlineMailFilterTable->SetEnabled (attribSubject, opEndsWith, 1);
m_onlineMailFilterTable->SetAvailable (attribDate, opIsBefore, 1);
m_onlineMailFilterTable->SetEnabled (attribDate, opIsBefore, 1);
m_onlineMailFilterTable->SetAvailable (attribDate, opIsAfter, 1);
m_onlineMailFilterTable->SetEnabled (attribDate, opIsAfter, 1);
m_onlineMailFilterTable->SetAvailable (attribDate, opIs, 1);
m_onlineMailFilterTable->SetEnabled (attribDate, opIs, 1);
m_onlineMailFilterTable->SetAvailable (attribDate, opIsnt, 1);
m_onlineMailFilterTable->SetEnabled (attribDate, opIsnt, 1);
m_onlineMailFilterTable->SetAvailable (attribPriority, opIsHigherThan, 1);
m_onlineMailFilterTable->SetEnabled (attribPriority, opIsHigherThan, 1);
m_onlineMailFilterTable->SetAvailable (attribPriority, opIsLowerThan, 1);
m_onlineMailFilterTable->SetEnabled (attribPriority, opIsLowerThan, 1);
m_onlineMailFilterTable->SetAvailable (attribPriority, opIs, 1);
m_onlineMailFilterTable->SetEnabled (attribPriority, opIs, 1);
m_onlineMailFilterTable->SetAvailable (attribMsgStatus, opIs, 1);
m_onlineMailFilterTable->SetEnabled (attribMsgStatus, opIs, 1);
m_onlineMailFilterTable->SetAvailable (attribMsgStatus, opIsnt, 1);
m_onlineMailFilterTable->SetEnabled (attribMsgStatus, opIsnt, 1);
m_onlineMailFilterTable->SetValidButNotShown (attribAgeInDays, opIsGreaterThan, 1);
m_onlineMailFilterTable->SetValidButNotShown (attribAgeInDays, opIsLessThan, 1);
m_onlineMailFilterTable->SetValidButNotShown (attribAgeInDays, opIs, 1);
m_onlineMailFilterTable->SetAvailable (attribOtherHeader, opContains, 1); // added for arbitrary headers
m_onlineMailFilterTable->SetEnabled (attribOtherHeader, opContains, 1);
m_onlineMailFilterTable->SetAvailable (attribOtherHeader, opDoesntContain, 1);
m_onlineMailFilterTable->SetEnabled (attribOtherHeader, opDoesntContain, 1);
m_onlineMailFilterTable->SetAvailable (attribOtherHeader, opIs, 1);
m_onlineMailFilterTable->SetEnabled (attribOtherHeader, opIs, 1);
m_onlineMailFilterTable->SetAvailable (attribOtherHeader, opIsnt, 1);
m_onlineMailFilterTable->SetEnabled (attribOtherHeader, opIsnt, 1);
}
return err;
}