gecko-dev/lib/libmsg/msgnsrch.cpp

1010 lines
30 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 newsgroups
#include "msg.h"
#include "pmsgsrch.h"
#include "msgfinfo.h"
#include "newsdb.h"
#include "newshdr.h"
#include "newshost.h"
#include "hosttbl.h"
#include "libi18n.h"
//-----------------------------------------------------------------------------
//----------- Adapter class for searching XPAT-capable news servers -----------
//-----------------------------------------------------------------------------
const char *msg_SearchNews::m_kNntpFrom = "FROM ";
const char *msg_SearchNews::m_kNntpSubject = "SUBJECT ";
const char *msg_SearchNews::m_kTermSeparator = "/";
msg_SearchNews::msg_SearchNews (MSG_ScopeTerm *scope, MSG_SearchTermArray &termList) : msg_SearchAdapter (scope, termList)
{
m_encoding = NULL;
}
msg_SearchNews::~msg_SearchNews ()
{
delete [] m_encoding;
}
MSG_SearchError msg_SearchNews::ValidateTerms ()
{
MSG_SearchError err = msg_SearchAdapter::ValidateTerms ();
if (SearchError_Success == err)
{
err = Encode (&m_encoding);
if (SearchError_Success == err)
{
// hack
URL_Struct *url = NET_CreateURLStruct (m_encoding, NET_DONT_RELOAD);
if (url)
{
url->pre_exit_fn = PreExitFunction;
m_scope->m_frame->m_urlStruct = url;
}
else
err = SearchError_OutOfMemory;
}
}
return err;
}
MSG_SearchError msg_SearchNews::Search ()
{
// the state machine runs in the news: handler
MSG_SearchError err = SearchError_NotImplemented;
return err;
}
char *msg_SearchNews::EncodeTerm (MSG_SearchTerm *term)
{
// Develop an XPAT-style encoding for the search term
XP_ASSERT(term);
if (!term)
return NULL;
// Find a string to represent the attribute
const char *attribEncoding = NULL;
switch (term->m_attribute)
{
case attribSender:
attribEncoding = m_kNntpFrom;
break;
case attribSubject:
attribEncoding = m_kNntpSubject;
break;
default:
XP_ASSERT(FALSE); // malformed search term?
return NULL;
}
// Build a string to represent the string pattern
XP_Bool leadingStar = FALSE;
XP_Bool trailingStar = FALSE;
int overhead = 1; // null terminator
switch (term->m_operator)
{
case opContains:
leadingStar = TRUE;
trailingStar = TRUE;
overhead += 2;
break;
case opIs:
break;
case opBeginsWith:
trailingStar = TRUE;
overhead++;
break;
case opEndsWith:
leadingStar = TRUE;
overhead++;
break;
default:
XP_ASSERT(FALSE); // malformed search term?
return NULL;
}
int16 wincsid = INTL_DefaultWinCharSetID(0); // *** FIX ME: Should not get default csid, should get csid from FE or folder
// Do INTL_FormatNNTPXPATInRFC1522Format trick for non-ASCII string
unsigned char *intlNonRFC1522Value =
INTL_FormatNNTPXPATInNonRFC1522Format (wincsid, (unsigned char*)term->m_value.u.string);
if (!intlNonRFC1522Value)
return NULL;
// TO DO: Do INTL_FormatNNTPXPATInRFC1522Format trick for non-ASCII string
// Unfortunately, we currently do not handle xxx or xxx search in XPAT
// Need to add the INTL_FormatNNTPXPATInRFC1522Format call after we can do that
// so we should search a string in either RFC1522 format and non-RFC1522 format
char *escapedValue = MSG_EscapeSearchUrl ((char*)intlNonRFC1522Value);
XP_FREE(intlNonRFC1522Value);
if (!escapedValue)
return NULL;
// We also need to apply NET_Escape to it since we have to pass 8-bits data
// And sometimes % in the 7-bit doulbe byte JIS
//
char * urlEncoded = NET_Escape((char*)escapedValue, URL_PATH);
XP_FREE(escapedValue);
if (! urlEncoded)
return NULL;
char *pattern = pattern = new char [XP_STRLEN(urlEncoded) + overhead];
if (!pattern)
return NULL;
else
pattern[0] = '\0';
if (leadingStar)
XP_STRCAT (pattern, "*");
XP_STRCAT (pattern, urlEncoded);
if (trailingStar)
XP_STRCAT (pattern, "*");
// Combine the XPAT command syntax with the attribute and the pattern to
// form the term encoding
char *xpatTemplate = "XPAT %s 1- %s";
int termLength = XP_STRLEN(xpatTemplate) + XP_STRLEN(attribEncoding) + XP_STRLEN(pattern) + 1;
char *termEncoding = new char [termLength];
if (termEncoding)
PR_snprintf (termEncoding, termLength, xpatTemplate, attribEncoding, pattern);
XP_FREE(urlEncoded);
delete [] pattern;
return termEncoding;
}
char *msg_SearchNews::BuildUrlPrefix ()
{
char *result = NULL;
switch (m_scope->m_folder->GetType())
{
case FOLDER_CONTAINERONLY:
{
// Would be better to do this in the folder info, but we need to get
// back to the NewsHost to find out if it's secure.
msg_HostTable *table = m_scope->m_frame->m_pane->GetMaster()->GetHostTable();
for (int i = 0; i < table->getNumHosts() && !result; i++)
{
MSG_NewsHost *host = table->getHost(i);
if (m_scope->m_folder == host->GetHostInfo())
result = PR_smprintf("%s/unused", host->GetURLBase());
}
break;
}
case FOLDER_NEWSGROUP:
case FOLDER_CATEGORYCONTAINER:
result = m_scope->m_folder->BuildUrl(NULL, MSG_MESSAGEKEYNONE);
break;
default:
XP_ASSERT(FALSE);
}
return result;
}
MSG_SearchError msg_SearchNews::Encode (char **outEncoding)
{
XP_ASSERT(outEncoding);
if (!outEncoding)
return SearchError_NullPointer;
*outEncoding = NULL;
MSG_SearchError err = SearchError_Success;
char **intermediateEncodings = new char * [m_searchTerms.GetSize()];
if (intermediateEncodings)
{
char *urlPrefix = BuildUrlPrefix();
if (urlPrefix)
{
// Build an XPAT command for each term
int encodingLength = XP_STRLEN(urlPrefix);
int i;
for (i = 0; i < m_searchTerms.GetSize(); i++)
{
MSG_SearchTerm * term = m_searchTerms.GetAt(i);
// set boolean OR term if any of the search terms are an OR...this only works if we are using
// homogeneous boolean operators.
m_ORSearch = !(term->IsBooleanOpAND());
intermediateEncodings[i] = EncodeTerm (m_searchTerms.GetAt(i));
if (intermediateEncodings[i])
encodingLength += XP_STRLEN(intermediateEncodings[i]) + XP_STRLEN(m_kTermSeparator);
}
encodingLength += XP_STRLEN("?search");
// Combine all the term encodings into one big encoding
char *encoding = new char [encodingLength + 1];
if (encoding)
{
XP_STRCPY (encoding, urlPrefix);
XP_STRCAT (encoding, "?search");
for (i = 0; i < m_searchTerms.GetSize(); i++)
{
if (intermediateEncodings[i])
{
XP_STRCAT (encoding, m_kTermSeparator);
XP_STRCAT (encoding, intermediateEncodings[i]);
delete [] intermediateEncodings[i];
}
}
*outEncoding = encoding;
}
else
err = SearchError_OutOfMemory;
XP_FREE(urlPrefix);
}
else
err = SearchError_OutOfMemory;
delete [] intermediateEncodings;
}
else
err = SearchError_OutOfMemory;
return err;
}
// Callback from libnet
SEARCH_API void MSG_AddNewsXpatHit (MWContext *context, uint32 artNum)
{
MSG_SearchFrame *frame = MSG_SearchFrame::FromContext(context);
msg_SearchNews *adapter = (msg_SearchNews*) frame->GetRunningAdapter();
adapter->AddHit (artNum);
}
void msg_SearchNews::PreExitFunction (URL_Struct * /*url*/, int status, MWContext *context)
{
MSG_SearchFrame *frame = MSG_SearchFrame::FromContext (context);
msg_SearchNews *adapter = (msg_SearchNews*) frame->GetRunningAdapter();
adapter->CollateHits();
adapter->ReportHits();
if (status == MK_INTERRUPTED)
{
adapter->Abort();
frame->EndCylonMode();
}
else
{
frame->m_idxRunningScope++;
if (frame->m_idxRunningScope >= frame->m_scopeList.GetSize())
frame->EndCylonMode();
}
}
XP_Bool msg_SearchNews::DuplicateHit(uint32 artNum)
// ASSUMES m_hits is sorted!!
{
int index;
for (index = 0; index < m_hits.GetSize(); index++)
if (artNum == m_hits.GetAt(index))
return TRUE;
return FALSE;
}
void msg_SearchNews::CollateHits ()
{
// Since the XPAT commands are processed one at a time, the result set for the
// entire query is the intersection of results for each XPAT command if an AND Search
// otherwise we want the union of all the search hits (minus the duplicates of course)
if (m_candidateHits.GetSize() == 0)
return;
// Sort the article numbers first, so it's easy to tell how many hits
// on a given article we got
m_candidateHits.QuickSort(CompareArticleNumbers);
int size = m_candidateHits.GetSize();
int index = 0;
uint32 candidate = m_candidateHits.GetAt(index);
if (m_ORSearch)
{
for (index = 0; index < size; index++)
{
candidate = m_candidateHits.GetAt(index);
if (!DuplicateHit(candidate)) // if not a dup, add it to the hit list
m_hits.Add (candidate);
}
return;
}
// otherwise we have a traditional and search which must be collated
// In order to get promoted into the hits list, a candidate article number
// must appear in the results of each XPAT command. So if we fire 3 XPAT
// commands (one per search term), the article number must appear 3 times.
// If it appears less than 3 times, it matched some search terms, but not all
int termCount = m_searchTerms.GetSize();
int candidateCount = 0;
while (index < size)
{
if (candidate == m_candidateHits.GetAt(index))
candidateCount++;
else
candidateCount = 1;
if (candidateCount == termCount)
m_hits.Add (m_candidateHits.GetAt(index));
candidate = m_candidateHits.GetAt(index++);
}
}
void msg_SearchNews::ReportHits ()
{
XP_ASSERT (m_scope->m_folder->IsNews());
if (!m_scope->m_folder->IsNews())
return;
MSG_FolderInfoNews *folder = (MSG_FolderInfoNews*) m_scope->m_folder;
// Construct a URL for the newsgroup, since thats what newDB::open wants
char *url = folder->BuildUrl(NULL, MSG_MESSAGEKEYNONE);
if (!url)
return;
NewsGroupDB *newsDB = NULL;
MsgERR status = NewsGroupDB::Open(url, m_scope->m_frame->m_pane->GetMaster(), &newsDB);
if (status == eSUCCESS)
{
XP_ASSERT(newsDB);
if (!newsDB)
return;
XP_FREEIF(url);
for (uint32 i = 0; i < m_hits.GetSize(); i++)
{
NewsMessageHdr *header = newsDB->GetNewsHdrForKey(m_hits.GetAt(i));
if (header)
{
ReportHit(header);
delete header;
}
}
newsDB->Close();
}
}
void msg_SearchNews::ReportHit (MessageHdrStruct *pHeaders, const char *location)
{
// this is taken from msg_SearchOfflineMail until I decide whether the
// right thing is to get them from the db or from NNTP
MSG_SearchError err = SearchError_Success;
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)
{
pValue->attribute = attribSubject;
char *reString = (pHeaders->m_flags & MSG_FLAG_HAS_RE) ? "Re:" : "";
pValue->u.string = PR_smprintf ("%s%s", reString, pHeaders->m_subject);
newResult->AddValue (pValue);
}
pValue = new MSG_SearchValue;
if (pValue)
{
pValue->attribute = attribSender;
pValue->u.string = (char*) XP_ALLOC(64);
if (pValue->u.string)
{
XP_STRNCPY_SAFE(pValue->u.string, pHeaders->m_author, 64);
newResult->AddValue (pValue);
}
else
err = SearchError_OutOfMemory;
}
pValue = new MSG_SearchValue;
if (pValue)
{
pValue->attribute = attribDate;
pValue->u.date = pHeaders->m_date;
newResult->AddValue (pValue);
}
pValue = new MSG_SearchValue;
if (pValue)
{
pValue->attribute = attribMsgStatus;
pValue->u.msgStatus = pHeaders->m_flags;
newResult->AddValue (pValue);
}
pValue = new MSG_SearchValue;
if (pValue)
{
pValue->attribute = attribPriority;
pValue->u.priority = pHeaders->m_priority;
newResult->AddValue (pValue);
}
pValue = new MSG_SearchValue;
if (pValue)
{
pValue->attribute = attribLocation;
pValue->u.string = XP_STRDUP(location);
newResult->AddValue (pValue);
}
pValue = new MSG_SearchValue;
if (pValue)
{
pValue->attribute = attribMessageKey;
pValue->u.key = pHeaders->m_messageKey;
newResult->AddValue (pValue);
}
if (pHeaders->m_messageId)
{
pValue = new MSG_SearchValue;
if (pValue)
{
pValue->attribute = attribMessageId;
pValue->u.string = XP_STRDUP(pHeaders->m_messageId);
newResult->AddValue (pValue);
}
}
if (!pValue)
err = SearchError_OutOfMemory;
m_scope->m_frame->AddResultElement (newResult);
}
}
void msg_SearchNews::ReportHit (DBMessageHdr *pHeaders)
{
MessageHdrStruct hdr;
pHeaders->CopyToMessageHdr(&hdr);
ReportHit (&hdr, m_scope->m_folder->GetName());
}
int msg_SearchNews::CompareArticleNumbers (const void *v1, const void *v2)
{
// QuickSort callback to compare article numbers
uint32 i1 = *(uint32*) v1;
uint32 i2 = *(uint32*) v2;
return i1 - i2;
}
//-----------------------------------------------------------------------------
//-------- Adapter class for searching SEARCH-capable news servers ------------
//-----------------------------------------------------------------------------
const char *msg_SearchNewsEx::m_kSearchTemplate = "?search/SEARCH HEADER NEWSGROUPS %s %s";
const char *msg_SearchNewsEx::m_kProfileTemplate = "%s/dummy?profile/PROFILE NEW %s HEADER NEWSGROUPS %s %s";
msg_SearchNewsEx::msg_SearchNewsEx (MSG_ScopeTerm *scope, MSG_SearchTermArray &termList) : msg_SearchNews (scope, termList)
{
}
msg_SearchNewsEx::~msg_SearchNewsEx ()
{
}
MSG_SearchError msg_SearchNewsEx::ValidateTerms ()
{
MSG_SearchError err = msg_SearchAdapter::ValidateTerms ();
if (SearchError_Success == err)
{
err = Encode (&m_encoding);
if (SearchError_Success == err)
{
// hack.
URL_Struct *url = NET_CreateURLStruct (m_encoding, NET_DONT_RELOAD);
if (url)
{
url->pre_exit_fn = PreExitFunctionEx;
m_scope->m_frame->m_urlStruct = url;
}
else
err = SearchError_OutOfMemory;
}
}
return err;
}
MSG_SearchError msg_SearchNewsEx::Search ()
{
// State machine runs in mknews.c?
return SearchError_NotImplemented;
}
MSG_SearchError msg_SearchNewsEx::Encode (char **ppOutEncoding)
{
*ppOutEncoding = NULL;
char *imapTerms = NULL;
// Figure out the charsets to use for the search terms and targets.
int16 src_csid, dst_csid;
GetSearchCSIDs(src_csid, dst_csid);
MSG_SearchError err = EncodeImap (&imapTerms, m_searchTerms, src_csid, dst_csid, TRUE );
if (SearchError_Success == err)
{
char *scopeString = NULL;
err = m_scope->m_frame->EncodeRFC977bisScopes (&scopeString);
if (SearchError_Success == err)
{
// Wrap the pattern with the RFC-977bis specified SEARCH syntax
char *RFC977bisEncoding = PR_smprintf (m_kSearchTemplate, scopeString, imapTerms);
if (RFC977bisEncoding)
{
// Build the host/group specification
char *urlPrefix = BuildUrlPrefix ();
if (urlPrefix)
{
// Build the whole URL e.g. new://host/local.index/search?SEARCH FROM "John Smith"
*ppOutEncoding = new char [XP_STRLEN(urlPrefix) + XP_STRLEN(RFC977bisEncoding) + 1];
if (*ppOutEncoding)
{
XP_STRCPY (*ppOutEncoding, urlPrefix);
XP_STRCAT (*ppOutEncoding, RFC977bisEncoding);
}
else
err = SearchError_OutOfMemory;
XP_FREE(urlPrefix);
}
else
err = SearchError_OutOfMemory;
XP_FREE(RFC977bisEncoding);
}
else
err = SearchError_OutOfMemory;
XP_FREE(scopeString);
}
}
return err;
}
MSG_SearchError msg_SearchNewsEx::SaveProfile (const char *profileName)
{
MSG_SearchError err = SearchError_Success;
MSG_FolderInfo *folder = m_scope->m_folder;
// Figure out which news host to fire the URL at. Maybe we should have a virtual function in MSG_FolderInfo for this?
MSG_NewsHost *host = NULL;
MSG_FolderInfoNews *newsFolder = folder->GetNewsFolderInfo();
if (newsFolder)
host = newsFolder->GetHost();
else if (FOLDER_CONTAINERONLY == folder->GetType())
host = ((MSG_NewsFolderInfoContainer*) folder)->GetHost();
XP_ASSERT(NULL != host && NULL != profileName);
if (NULL != host && NULL != profileName)
{
char *scopeString = NULL;
m_scope->m_frame->EncodeRFC977bisScopes (&scopeString);
// Figure out the charsets to use for the search terms and targets.
int16 src_csid, dst_csid;
GetSearchCSIDs(src_csid, dst_csid);
char *termsString = NULL;
EncodeImap (&termsString, m_searchTerms,
src_csid, dst_csid,
TRUE );
char *legalProfileName = XP_STRDUP(profileName);
if (termsString && scopeString && legalProfileName)
{
msg_MakeLegalNewsgroupComponent (legalProfileName);
char *url = PR_smprintf (m_kProfileTemplate, host->GetURLBase(),
legalProfileName, scopeString,
termsString);
if (url)
{
URL_Struct *urlStruct = NET_CreateURLStruct (url, NET_DONT_RELOAD);
if (urlStruct)
{
// Set the internal_url flag so just in case someone else happens to have
// a search-libmsg URL, it won't fire my code, and surely crash.
urlStruct->internal_url = TRUE;
// Set the pre_exit_fn to we can turn off cylon mode when we're done
urlStruct->pre_exit_fn = PreExitFunctionEx;
int getUrlErr = m_scope->m_frame->m_pane->GetURL (urlStruct, FALSE);
if (getUrlErr != 0)
err = SearchError_ScopeAgreement; // ### not really. impedance mismatch
else
m_scope->m_frame->BeginCylonMode();
}
else
err = SearchError_OutOfMemory;
XP_FREE(url);
}
else
err = SearchError_OutOfMemory;
}
XP_FREEIF(scopeString);
delete [] termsString;
XP_FREEIF(legalProfileName);
}
return err;
}
// Callback from libnet
SEARCH_API void MSG_AddNewsSearchHit (MWContext *context, const char *resultLine)
{
MSG_SearchFrame *frame = MSG_SearchFrame::FromContext (context);
msg_SearchNewsEx *adapter = (msg_SearchNewsEx *) frame->GetRunningAdapter();
if (adapter)
{
MessageHdrStruct hdr;
XP_BZERO(&hdr, sizeof(hdr));
// Here we make the SEARCH result compatible with xover conventions. In SEARCH, the
// group name and a ':' precede the article number, so try to skip over this stuff
// before asking DBMessageHdr to parse it
char *xoverCompatLine = XP_STRCHR(resultLine, ':');
if (xoverCompatLine)
xoverCompatLine++;
else
xoverCompatLine = (char*) resultLine; //### casting away const
if (DBMessageHdr::ParseLine ((char*) xoverCompatLine, &hdr)) //### casting away const
{
if (hdr.m_flags & kHasRe) // hack around which kind of flag we actually got
{
hdr.m_flags &= !kHasRe;
hdr.m_flags |= MSG_FLAG_HAS_RE;
}
adapter->ReportHit (&hdr, XP_STRTOK((char*) resultLine, ":")); //### casting away const
}
}
}
SEARCH_API MSG_SearchError MSG_SaveProfileStatus (MSG_Pane *searchPane, XP_Bool *cmdEnabled)
{
MSG_SearchError err = SearchError_Success;
XP_ASSERT(cmdEnabled);
if (cmdEnabled)
{
*cmdEnabled = FALSE;
MSG_SearchFrame *frame = MSG_SearchFrame::FromPane (searchPane);
if (frame)
*cmdEnabled = frame->GetSaveProfileStatus();
}
else
err = SearchError_NullPointer;
return err;
}
SEARCH_API MSG_SearchError MSG_SaveProfile (MSG_Pane *searchPane, const char * profileName)
{
MSG_SearchError err = SearchError_Success;
#ifdef _DEBUG
XP_Bool enabled = FALSE;
MSG_SaveProfileStatus (searchPane, &enabled);
XP_ASSERT(enabled);
if (!enabled)
return SearchError_ScopeAgreement;
#endif
if (profileName)
{
MSG_SearchFrame *frame = MSG_SearchFrame::FromPane (searchPane);
XP_ASSERT(frame);
if (frame)
{
msg_SearchNewsEx *adapter = frame->GetProfileAdapter();
XP_ASSERT(adapter);
if (adapter)
err = adapter->SaveProfile (profileName);
}
}
else
err = SearchError_NullPointer;
return err;
}
SEARCH_API int MSG_AddProfileGroup (MSG_Pane *pane, MSG_NewsHost* host,
const char *groupName)
{
MSG_FolderInfoNews *group =
pane->GetMaster()->AddProfileNewsgroup(host, groupName);
return group ? 0 : -1;
}
void msg_SearchNewsEx::PreExitFunctionEx (URL_Struct * /*url*/, int /*status*/, MWContext *context)
{
MSG_SearchFrame *frame = MSG_SearchFrame::FromContext (context);
frame->EndCylonMode();
}
//-----------------------------------------------------------------------------
//------------ Adapter class for searching offline news groups ----------------
//-----------------------------------------------------------------------------
msg_SearchOfflineNews::msg_SearchOfflineNews (MSG_ScopeTerm *scopes, MSG_SearchTermArray &terms) : msg_SearchOfflineMail (scopes, terms)
{
}
msg_SearchOfflineNews::~msg_SearchOfflineNews ()
{
}
MSG_SearchError msg_SearchOfflineNews::OpenSummaryFile ()
{
MSG_SearchError err = SearchError_DBOpenFailed;
if (m_scope->m_folder->IsNews())
{
MSG_FolderInfoNews *newsFolder = (MSG_FolderInfoNews*) m_scope->m_folder;
char *url = newsFolder->BuildUrl(NULL, MSG_MESSAGEKEYNONE);
if (url)
{
NewsGroupDB *newsDb = NULL;
MsgERR msgErr = NewsGroupDB::Open (url, m_scope->m_frame->m_pane->GetMaster(), &newsDb);
if (eSUCCESS == msgErr)
{
m_db = newsDb;
err = SearchError_Success;
}
XP_FREE(url);
}
else
err = SearchError_OutOfMemory;
}
return err;
}
MSG_SearchError msg_SearchOfflineNews::ValidateTerms ()
{
MSG_SearchError err = msg_SearchAdapter::ValidateTerms ();
if (SearchError_Success == err)
{
// Make sure the terms themselves are valid
msg_SearchValidityTable *table = NULL;
err = gValidityMgr.GetTable (msg_SearchValidityManager::localNews, &table);
if (SearchError_Success == err)
{
XP_ASSERT (table);
err = table->ValidateTerms (m_searchTerms);
}
}
return err;
}
//-----------------------------------------------------------------------------
MSG_SearchError msg_SearchValidityManager::InitLocalNewsTable()
{
XP_ASSERT (NULL == m_localNewsTable);
MSG_SearchError err = NewTable (&m_localNewsTable);
if (SearchError_Success == err)
{
m_localNewsTable->SetAvailable (attribSender, opContains, 1);
m_localNewsTable->SetEnabled (attribSender, opContains, 1);
m_localNewsTable->SetAvailable (attribSender, opIs, 1);
m_localNewsTable->SetEnabled (attribSender, opIs, 1);
m_localNewsTable->SetAvailable (attribSender, opBeginsWith, 1);
m_localNewsTable->SetEnabled (attribSender, opBeginsWith, 1);
m_localNewsTable->SetAvailable (attribSender, opEndsWith, 1);
m_localNewsTable->SetEnabled (attribSender, opEndsWith, 1);
m_localNewsTable->SetAvailable (attribSubject, opContains, 1);
m_localNewsTable->SetEnabled (attribSubject, opContains, 1);
m_localNewsTable->SetAvailable (attribSubject, opIs, 1);
m_localNewsTable->SetEnabled (attribSubject, opIs, 1);
m_localNewsTable->SetAvailable (attribSubject, opBeginsWith, 1);
m_localNewsTable->SetEnabled (attribSubject, opBeginsWith, 1);
m_localNewsTable->SetAvailable (attribSubject, opEndsWith, 1);
m_localNewsTable->SetEnabled (attribSubject, opEndsWith, 1);
m_localNewsTable->SetAvailable (attribBody, opContains, 1);
m_localNewsTable->SetEnabled (attribBody, opContains, 1);
m_localNewsTable->SetAvailable (attribBody, opDoesntContain, 1);
m_localNewsTable->SetEnabled (attribBody, opDoesntContain, 1);
m_localNewsTable->SetAvailable (attribBody, opIs, 1);
m_localNewsTable->SetEnabled (attribBody, opIs, 1);
m_localNewsTable->SetAvailable (attribBody, opIsnt, 1);
m_localNewsTable->SetEnabled (attribBody, opIsnt, 1);
m_localNewsTable->SetEnabled (attribDate, opIsBefore, 1);
m_localNewsTable->SetAvailable (attribDate, opIsAfter, 1);
m_localNewsTable->SetEnabled (attribDate, opIsAfter, 1);
m_localNewsTable->SetAvailable (attribDate, opIs, 1);
m_localNewsTable->SetEnabled (attribDate, opIs, 1);
m_localNewsTable->SetAvailable (attribDate, opIsnt, 1);
m_localNewsTable->SetEnabled (attribDate, opIsnt, 1);
m_localNewsTable->SetAvailable (attribOtherHeader, opContains, 1); // added for arbitrary headers
m_localNewsTable->SetEnabled (attribOtherHeader, opContains, 1);
m_localNewsTable->SetAvailable (attribOtherHeader, opDoesntContain, 1);
m_localNewsTable->SetEnabled (attribOtherHeader, opDoesntContain, 1);
m_localNewsTable->SetAvailable (attribOtherHeader, opIs, 1);
m_localNewsTable->SetEnabled (attribOtherHeader, opIs, 1);
m_localNewsTable->SetAvailable (attribOtherHeader, opIsnt, 1);
m_localNewsTable->SetEnabled (attribOtherHeader, opIsnt, 1);
m_localNewsTable->SetAvailable (attribAgeInDays, opIsGreaterThan, 1);
m_localNewsTable->SetEnabled (attribAgeInDays, opIsGreaterThan, 1);
m_localNewsTable->SetAvailable (attribAgeInDays, opIsLessThan, 1);
m_localNewsTable->SetEnabled (attribAgeInDays, opIsLessThan, 1);
m_localNewsTable->SetAvailable (attribAgeInDays, opIs, 1);
m_localNewsTable->SetEnabled (attribAgeInDays, opIs, 1);
m_localNewsTable->SetAvailable (attribMsgStatus, opIs, 1);
m_localNewsTable->SetEnabled (attribMsgStatus, opIs, 1);
m_localNewsTable->SetAvailable (attribMsgStatus, opIsnt, 1);
m_localNewsTable->SetEnabled (attribMsgStatus, opIsnt, 1);
}
return err;
}
MSG_SearchError msg_SearchValidityManager::InitNewsTable ()
{
XP_ASSERT (NULL == m_newsTable);
MSG_SearchError err = NewTable (&m_newsTable);
if (SearchError_Success == err)
{
m_newsTable->SetAvailable (attribSender, opContains, 1);
m_newsTable->SetEnabled (attribSender, opContains, 1);
m_newsTable->SetAvailable (attribSender, opIs, 1);
m_newsTable->SetEnabled (attribSender, opIs, 1);
m_newsTable->SetAvailable (attribSender, opBeginsWith, 1);
m_newsTable->SetEnabled (attribSender, opBeginsWith, 1);
m_newsTable->SetAvailable (attribSender, opEndsWith, 1);
m_newsTable->SetEnabled (attribSender, opEndsWith, 1);
m_newsTable->SetAvailable (attribSubject, opContains, 1);
m_newsTable->SetEnabled (attribSubject, opContains, 1);
m_newsTable->SetAvailable (attribSubject, opIs, 1);
m_newsTable->SetEnabled (attribSubject, opIs, 1);
m_newsTable->SetAvailable (attribSubject, opBeginsWith, 1);
m_newsTable->SetEnabled (attribSubject, opBeginsWith, 1);
m_newsTable->SetAvailable (attribSubject, opEndsWith, 1);
m_newsTable->SetEnabled (attribSubject, opEndsWith, 1);
}
return err;
}
MSG_SearchError msg_SearchValidityManager::InitNewsExTable (MSG_NewsHost *newsHost)
{
MSG_SearchError err = SearchError_Success;
if (!m_newsExTable)
err = NewTable (&m_newsExTable);
if (SearchError_Success == err)
{
XP_Bool hasAttrib = newsHost ? newsHost->QuerySearchableHeader("FROM") : TRUE;
m_newsExTable->SetAvailable (attribSender, opContains, hasAttrib);
m_newsExTable->SetEnabled (attribSender, opContains, hasAttrib);
m_newsExTable->SetAvailable (attribSender, opDoesntContain, hasAttrib);
m_newsExTable->SetEnabled (attribSender, opDoesntContain, hasAttrib);
hasAttrib = newsHost ? newsHost->QuerySearchableHeader ("SUBJECT") : TRUE;
m_newsExTable->SetAvailable (attribSubject, opContains, hasAttrib);
m_newsExTable->SetEnabled (attribSubject, opContains, hasAttrib);
m_newsExTable->SetAvailable (attribSubject, opDoesntContain, hasAttrib);
m_newsExTable->SetEnabled (attribSubject, opDoesntContain, hasAttrib);
hasAttrib = newsHost ? newsHost->QuerySearchableHeader ("DATE") : TRUE;
m_newsExTable->SetAvailable (attribDate, opIsBefore, hasAttrib);
m_newsExTable->SetEnabled (attribDate, opIsBefore, hasAttrib);
m_newsExTable->SetAvailable (attribDate, opIsAfter, hasAttrib);
m_newsExTable->SetEnabled (attribDate, opIsAfter, hasAttrib);
hasAttrib = newsHost ? newsHost->QuerySearchableHeader (":TEXT") : TRUE;
m_newsExTable->SetAvailable (attribAnyText, opContains, hasAttrib);
m_newsExTable->SetEnabled (attribAnyText, opContains, hasAttrib);
m_newsExTable->SetAvailable (attribAnyText, opDoesntContain, hasAttrib);
m_newsExTable->SetEnabled (attribAnyText, opDoesntContain, hasAttrib);
hasAttrib = newsHost ? newsHost->QuerySearchableHeader ("KEYWORDS") : TRUE;
m_newsExTable->SetAvailable (attribKeywords, opContains, hasAttrib);
m_newsExTable->SetEnabled (attribKeywords, opContains, hasAttrib);
m_newsExTable->SetAvailable (attribKeywords, opDoesntContain, hasAttrib);
m_newsExTable->SetEnabled (attribKeywords, opDoesntContain, hasAttrib);
#ifdef LATER
// Not sure whether this would be useful or not. If so, can we specify more
// than one NEWSGROUPS term to the server? If not, it would be tricky to merge
// this with the NEWSGROUPS term we generate for the scope.
hasAttrib = newsHost ? newsHost->QuerySearchableHeader("NEWSGROUPS") : TRUE;
m_newsExTable->SetAvailable (attribNewsgroups, opIsBefore, hasAttrib);
m_newsExTable->SetEnabled (attribNewsgroups, opIsBefore, hasAttrib);
m_newsExTable->SetAvailable (attribNewsgroups, opIsAfter, hasAttrib);
m_newsExTable->SetEnabled (attribNewsgroups, opIsAfter, hasAttrib);
#endif
hasAttrib = newsHost ? newsHost->QuerySearchableHeader("DATE") : TRUE;
m_newsExTable->SetAvailable (attribAgeInDays, opIsGreaterThan, hasAttrib);
m_newsExTable->SetEnabled (attribAgeInDays, opIsGreaterThan, hasAttrib);
m_newsExTable->SetAvailable (attribAgeInDays, opIsLessThan, hasAttrib);
m_newsExTable->SetEnabled (attribAgeInDays, opIsLessThan, hasAttrib);
m_newsExTable->SetAvailable (attribAgeInDays, opIs, hasAttrib);
m_newsExTable->SetEnabled (attribAgeInDays, opIs, hasAttrib);
// it is possible that the user enters an arbitrary header that is not searchable using NNTP search extensions
m_newsExTable->SetAvailable (attribOtherHeader, opContains, 1); // added for arbitrary headers
m_newsExTable->SetEnabled (attribOtherHeader, opContains, 1);
m_newsExTable->SetAvailable (attribOtherHeader, opDoesntContain, 1);
m_newsExTable->SetEnabled (attribOtherHeader, opDoesntContain, 1);
}
return err;
}
MSG_SearchError msg_SearchValidityManager::PostProcessValidityTable (MSG_NewsHost *host)
{
return InitNewsExTable (host);
}