/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * * 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 the Initial Developer are Copyright (C) 2000 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the NPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the NPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "msgCore.h" #include "nsMsgSearchAdapter.h" #include "nsXPIDLString.h" #include "nsUnicharUtils.h" #include "nsReadableUtils.h" #include "nsMsgSearchScopeTerm.h" #include "nsMsgResultElement.h" #include "nsMsgSearchTerm.h" #include "nsIMsgHdr.h" #include "nsMsgSearchNews.h" #include "nsIDBFolderInfo.h" #include "prprf.h" // Implementation of search for IMAP mail folders // Implementation of search for newsgroups //----------------------------------------------------------------------------- //----------- Adapter class for searching XPAT-capable news servers ----------- //----------------------------------------------------------------------------- const char *nsMsgSearchNews::m_kNntpFrom = "FROM "; const char *nsMsgSearchNews::m_kNntpSubject = "SUBJECT "; const char *nsMsgSearchNews::m_kTermSeparator = "/"; nsMsgSearchNews::nsMsgSearchNews (nsMsgSearchScopeTerm *scope, nsISupportsArray *termList) : nsMsgSearchAdapter (scope, termList) { } nsMsgSearchNews::~nsMsgSearchNews () { } nsresult nsMsgSearchNews::ValidateTerms () { nsresult err = nsMsgSearchAdapter::ValidateTerms (); if (NS_OK == err) { err = Encode (&m_encoding); } return err; } nsresult nsMsgSearchNews::Search (PRBool *aDone) { // the state machine runs in the news: handler nsresult err = NS_ERROR_NOT_IMPLEMENTED; return err; } PRUnichar *nsMsgSearchNews::EncodeToWildmat (const PRUnichar *value) { // Here we take advantage of XPAT's use of the wildmat format, which allows // a case-insensitive match by specifying each case possibility for each character // So, "FooBar" is encoded as "[Ff][Oo][Bb][Aa][Rr]" PRUnichar *caseInsensitiveValue = (PRUnichar*) nsMemory::Alloc(sizeof(PRUnichar) * ((4 * nsCRT::strlen(value)) + 1)); if (caseInsensitiveValue) { PRUnichar *walkValue = caseInsensitiveValue; while (*value) { if (nsCRT::IsAsciiAlpha(*value)) { *walkValue++ = (PRUnichar)'['; *walkValue++ = ToUpperCase((PRUnichar)*value); *walkValue++ = ToLowerCase((PRUnichar)*value); *walkValue++ = (PRUnichar)']'; } else *walkValue++ = *value; value++; } *walkValue = 0; } return caseInsensitiveValue; } char *nsMsgSearchNews::EncodeTerm (nsIMsgSearchTerm *term) { // Develop an XPAT-style encoding for the search term NS_ASSERTION(term, "null term"); if (!term) return nsnull; // Find a string to represent the attribute const char *attribEncoding = nsnull; nsMsgSearchAttribValue attrib; term->GetAttrib(&attrib); switch (attrib) { case nsMsgSearchAttrib::Sender: attribEncoding = m_kNntpFrom; break; case nsMsgSearchAttrib::Subject: attribEncoding = m_kNntpSubject; break; default: NS_ASSERTION(PR_FALSE,"malformed search"); // malformed search term? return nsnull; } // Build a string to represent the string pattern PRBool leadingStar = PR_FALSE; PRBool trailingStar = PR_FALSE; int overhead = 1; // null terminator nsMsgSearchOpValue op; term->GetOp(&op); switch (op) { case nsMsgSearchOp::Contains: leadingStar = PR_TRUE; trailingStar = PR_TRUE; overhead += 2; break; case nsMsgSearchOp::Is: break; case nsMsgSearchOp::BeginsWith: trailingStar = PR_TRUE; overhead++; break; case nsMsgSearchOp::EndsWith: leadingStar = PR_TRUE; overhead++; break; default: NS_ASSERTION(PR_FALSE,"malformed search"); // malformed search term? return nsnull; } // ### i18N problem Get the csid from FE, which is the correct csid for term // int16 wincsid = INTL_GetCharSetID(INTL_DefaultTextWidgetCsidSel); // Do INTL_FormatNNTPXPATInRFC1522Format trick for non-ASCII string // unsigned char *intlNonRFC1522Value = INTL_FormatNNTPXPATInNonRFC1522Format (wincsid, (unsigned char*)term->m_value.u.string); nsCOMPtr searchValue; nsresult rv = term->GetValue(getter_AddRefs(searchValue)); if (NS_FAILED(rv) || !searchValue) return nsnull; nsXPIDLString intlNonRFC1522Value; rv = searchValue->GetStr(getter_Copies(intlNonRFC1522Value)); if (NS_FAILED(rv) || !intlNonRFC1522Value) return nsnull; PRUnichar *caseInsensitiveValue = EncodeToWildmat (intlNonRFC1522Value); if (!caseInsensitiveValue) return nsnull; // 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 PRUnichar *escapedValue = EscapeSearchUrl (caseInsensitiveValue); nsMemory::Free(caseInsensitiveValue); if (!escapedValue) return nsnull; #if 0 // 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 // PRUnichar * urlEncoded = nsEscape(escapedValue, url_Path); nsCRT::free(escapedValue); if (! urlEncoded) return nsnull; char *pattern = new char [nsCRT::strlen(urlEncoded) + overhead]; if (!pattern) return nsnull; else pattern[0] = '\0'; #else nsCAutoString pattern; #endif if (leadingStar) pattern.Append("*"); pattern.Append(NS_ConvertUCS2toUTF8(escapedValue)); if (trailingStar) pattern.Append("*"); // Combine the XPAT command syntax with the attribute and the pattern to // form the term encoding const char xpatTemplate[] = "XPAT %s 1- %s"; int termLength = (sizeof(xpatTemplate) - 1) + strlen(attribEncoding) + pattern.Length() + 1; char *termEncoding = new char [termLength]; if (termEncoding) PR_snprintf (termEncoding, termLength, xpatTemplate, attribEncoding, pattern.get()); return termEncoding; } nsresult nsMsgSearchNews::GetEncoding(char **result) { NS_ENSURE_ARG(result); *result = ToNewCString(m_encoding); return (*result) ? NS_OK : NS_ERROR_OUT_OF_MEMORY; } nsresult nsMsgSearchNews::Encode (nsCString *outEncoding) { NS_ASSERTION(outEncoding, "no out encoding"); if (!outEncoding) return NS_ERROR_NULL_POINTER; nsresult err = NS_OK; PRUint32 numTerms; m_searchTerms->Count(&numTerms); char **intermediateEncodings = new char * [numTerms]; if (intermediateEncodings) { // Build an XPAT command for each term int encodingLength = 0; PRUint32 i; for (i = 0; i < numTerms; i++) { nsCOMPtr pTerm; m_searchTerms->QueryElementAt(i, NS_GET_IID(nsIMsgSearchTerm), (void **)getter_AddRefs(pTerm)); // set boolean OR term if any of the search terms are an OR...this only works if we are using // homogeneous boolean operators. PRBool isBooleanOpAnd; pTerm->GetBooleanAnd(&isBooleanOpAnd); m_ORSearch = !isBooleanOpAnd; intermediateEncodings[i] = EncodeTerm (pTerm); if (intermediateEncodings[i]) encodingLength += strlen(intermediateEncodings[i]) + strlen(m_kTermSeparator); } encodingLength += strlen("?search"); // Combine all the term encodings into one big encoding char *encoding = new char [encodingLength + 1]; if (encoding) { PL_strcpy (encoding, "?search"); m_searchTerms->Count(&numTerms); for (i = 0; i < numTerms; i++) { if (intermediateEncodings[i]) { PL_strcat (encoding, m_kTermSeparator); PL_strcat (encoding, intermediateEncodings[i]); delete [] intermediateEncodings[i]; } } *outEncoding = encoding; } else err = NS_ERROR_OUT_OF_MEMORY; } else err = NS_ERROR_OUT_OF_MEMORY; delete [] intermediateEncodings; return err; } NS_IMETHODIMP nsMsgSearchNews::AddHit(nsMsgKey key) { m_candidateHits.Add (key); return NS_OK; } /* void CurrentUrlDone (in long exitCode); */ NS_IMETHODIMP nsMsgSearchNews::CurrentUrlDone(PRInt32 exitCode) { CollateHits(); ReportHits(); return NS_OK; } #if 0 // need to switch this to a notify stop loading handler, I think. void nsMsgSearchNews::PreExitFunction (URL_Struct * /*url*/, int status, MWContext *context) { MSG_SearchFrame *frame = MSG_SearchFrame::FromContext (context); nsMsgSearchNews *adapter = (nsMsgSearchNews*) 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.Count()) frame->EndCylonMode(); } } #endif // 0 PRBool nsMsgSearchNews::DuplicateHit(PRUint32 artNum) // ASSUMES m_hits is sorted!! { PRUint32 index; for (index = 0; index < m_hits.GetSize(); index++) if (artNum == m_hits.ElementAt(index)) return PR_TRUE; return PR_FALSE; } void nsMsgSearchNews::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; PRUint32 candidate = m_candidateHits.ElementAt(index); if (m_ORSearch) { for (index = 0; index < size; index++) { candidate = m_candidateHits.ElementAt(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 PRUint32 termCount; m_searchTerms->Count(&termCount); PRUint32 candidateCount = 0; while (index < size) { if (candidate == m_candidateHits.ElementAt(index)) candidateCount++; else candidateCount = 1; if (candidateCount == termCount) m_hits.Add (m_candidateHits.ElementAt(index)); candidate = m_candidateHits.ElementAt(index++); } } void nsMsgSearchNews::ReportHits () { nsCOMPtr db; nsCOMPtr folderInfo; nsCOMPtr scopeFolder; nsresult err = m_scope->GetFolder(getter_AddRefs(scopeFolder)); if (NS_SUCCEEDED(err) && scopeFolder) { err = scopeFolder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db)); } if (db) { for (PRUint32 i = 0; i < m_hits.GetSize(); i++) { nsCOMPtr header; db->GetMsgHdrForKey(m_hits.ElementAt(i), getter_AddRefs(header)); if (header) ReportHit(header, scopeFolder); } } } // ### this should take an nsIMsgFolder instead of a string location. void nsMsgSearchNews::ReportHit (nsIMsgDBHdr *pHeaders, nsIMsgFolder *folder) { // this is totally filched from msg_SearchOfflineMail until I decide whether the // right thing is to get them from the db or from NNTP nsresult err = NS_OK; nsCOMPtr session; nsCOMPtr scopeFolder; err = m_scope->GetFolder(getter_AddRefs(scopeFolder)); m_scope->GetSearchSession(getter_AddRefs(session)); if (session) session->AddSearchHit (pHeaders, scopeFolder); } int PR_CALLBACK nsMsgSearchNews::CompareArticleNumbers (const void *v1, const void *v2, void *data) { // 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 *nsMsgSearchNewsEx::m_kSearchTemplate = "?search/SEARCH HEADER NEWSGROUPS %s %s"; const char *nsMsgSearchNewsEx::m_kProfileTemplate = "%s/dummy?profile/PROFILE NEW %s HEADER NEWSGROUPS %s %s"; nsMsgSearchNewsEx::nsMsgSearchNewsEx (nsMsgSearchScopeTerm *scope, nsISupportsArray *termList) : nsMsgSearchNews (scope, termList) { } nsMsgSearchNewsEx::~nsMsgSearchNewsEx () { } nsresult nsMsgSearchNewsEx::ValidateTerms () { nsresult err = nsMsgSearchAdapter::ValidateTerms (); if (NS_OK == err) err = Encode (&m_encoding); // this used to create a url struct for some reason. return err; } nsresult nsMsgSearchNewsEx::Search (PRBool *aDone) { // State machine runs in mknews.c? return NS_ERROR_NOT_IMPLEMENTED; } nsresult nsMsgSearchNewsEx::Encode (nsCString *ppOutEncoding) { *ppOutEncoding = ""; char *imapTerms = nsnull; // Figure out the charsets to use for the search terms and targets. nsXPIDLString srcCharset, dstCharset; GetSearchCharsets(getter_Copies(srcCharset), getter_Copies(dstCharset)); nsresult err = EncodeImap (&imapTerms, m_searchTerms, srcCharset.get(), dstCharset.get(), PR_TRUE /*reallyDredd*/); #ifdef DOING_DREDD if (NS_OK == err) { char *scopeString = nsnull; err = m_scope->m_frame->EncodeDreddScopes (&scopeString); if (NS_OK == err) { // Wrap the pattern with the RFC-977bis (Dredd) specified SEARCH syntax char *dreddEncoding = PR_smprintf (m_kSearchTemplate, scopeString, imapTerms); if (dreddEncoding) { // Build the encoding part of the URL e.g. search?SEARCH FROM "John Smith" *ppOutEncoding = dreddEncoding; nsCRT::free(dreddEncoding); } else err = NS_ERROR_OUT_OF_MEMORY; nsCRT::free(scopeString); } } #endif return err; } #ifdef DOING_PROFILES nsresult nsMsgSearchNewsEx::SaveProfile (const char *profileName) { nsresult err = NS_OK; 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 = nsnull; MSG_FolderInfoNews *newsFolder = folder->GetNewsFolderInfo(); if (newsFolder) host = newsFolder->GetHost(); else if (FOLDER_CONTAINERONLY == folder->GetType()) host = ((MSG_NewsFolderInfoContainer*) folder)->GetHost(); NS_ABORT_IF_FALSE(nsnull != host && nsnull != profileName, "host and profileName should not be NULL"); if (nsnull != host && nsnull != profileName) { char *scopeString = nsnull; m_scope->m_frame->EncodeDreddScopes (&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 = nsnull; EncodeImap (&termsString, m_searchTerms, src_csid, dst_csid, PR_TRUE /*reallyDredd*/); char *legalProfileName = PL_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 = PR_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, PR_FALSE); if (getUrlErr != 0) err = SearchError_ScopeAgreement; // ### not really. impedance mismatch else m_scope->m_frame->BeginCylonMode(); } else err = NS_ERROR_OUT_OF_MEMORY; nsCRT::free(url); } else err = NS_ERROR_OUT_OF_MEMORY; } CRTFREEIF(scopeString); delete [] termsString; CRTFREEIF(legalProfileName); } return err; } #endif // DOING_PROFILES #if OLDWAY // Callback from libnet SEARCH_API void MSG_AddNewsSearchHit (MWContext *context, const char *resultLine) { MSG_SearchFrame *frame = MSG_SearchFrame::FromContext (context); nsMsgSearchNewsEx *adapter = (nsMsgSearchNewsEx *) 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 NeoMessageHdr to parse it char *xoverCompatLine = XP_STRCHR(resultLine, ':'); if (xoverCompatLine) xoverCompatLine++; else xoverCompatLine = (char*) resultLine; //### casting away const if (NeoMessageHdr::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 nsresult MSG_SaveProfileStatus (MSG_Pane *searchPane, PRBool *cmdEnabled) { nsresult err = NS_OK; NS_ABORT_IF_FALSE(cmdEnabled, "cmdEnabled cannot be NULL"); if (cmdEnabled) { *cmdEnabled = PR_FALSE; MSG_SearchFrame *frame = MSG_SearchFrame::FromPane (searchPane); if (frame) *cmdEnabled = frame->GetSaveProfileStatus(); } else err = NS_ERROR_NULL_POINTER; return err; } SEARCH_API nsresult MSG_SaveProfile (MSG_Pane *searchPane, const char * profileName) { nsresult err = NS_OK; #ifdef _DEBUG PRBool enabled = PR_FALSE; MSG_SaveProfileStatus (searchPane, &enabled); NS_ENSURE_TRUE(enabled, SearchError_ScopeAgreement); #endif if (profileName) { MSG_SearchFrame *frame = MSG_SearchFrame::FromPane (searchPane); NS_ASSERTION(frame, "frame cannot be NULL"); if (frame) { nsMsgSearchNewsEx *adapter = frame->GetProfileAdapter(); NS_ASSERTION(adapter, "adaptor cannot be NULL"); if (adapter) err = adapter->SaveProfile (profileName); } } else err = NS_ERROR_NULL_POINTER; 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; } #endif nsresult nsMsgSearchValidityManager::InitNewsTable() { NS_ASSERTION (nsnull == m_newsTable,"don't call this twice!"); nsresult rv = NewTable (getter_AddRefs(m_newsTable)); if (NS_SUCCEEDED(rv)) { m_newsTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Contains, 1); m_newsTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Contains, 1); m_newsTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Is, 1); m_newsTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Is, 1); m_newsTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::BeginsWith, 1); m_newsTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::BeginsWith, 1); m_newsTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::EndsWith, 1); m_newsTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::EndsWith, 1); m_newsTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Contains, 1); m_newsTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Contains, 1); m_newsTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Is, 1); m_newsTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Is, 1); m_newsTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::BeginsWith, 1); m_newsTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::BeginsWith, 1); m_newsTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::EndsWith, 1); m_newsTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::EndsWith, 1); } return rv; } nsresult nsMsgSearchValidityManager::InitNewsFilterTable() { NS_ASSERTION (nsnull == m_newsFilterTable, "news filter table already initted"); nsresult rv = NewTable (getter_AddRefs(m_newsFilterTable)); if (NS_SUCCEEDED(rv)) { m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Contains, 1); m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Contains, 1); m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::DoesntContain, 1); m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::DoesntContain, 1); m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Is, 1); m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Is, 1); m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Isnt, 1); m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Isnt, 1); m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::BeginsWith, 1); m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::BeginsWith, 1); m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::EndsWith, 1); m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::EndsWith, 1); m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::IsInAB, 1); m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::IsInAB, 1); m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::IsntInAB, 1); m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::IsntInAB, 1); m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Contains, 1); m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Contains, 1); m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::DoesntContain, 1); m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::DoesntContain, 1); m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Is, 1); m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Is, 1); m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Isnt, 1); m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Isnt, 1); m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::BeginsWith, 1); m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::BeginsWith, 1); m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::EndsWith, 1); m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::EndsWith, 1); m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Date, nsMsgSearchOp::IsBefore, 1); m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Date, nsMsgSearchOp::IsBefore, 1); m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Date, nsMsgSearchOp::IsAfter, 1); m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Date, nsMsgSearchOp::IsAfter, 1); m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Date, nsMsgSearchOp::Is, 1); m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Date, nsMsgSearchOp::Is, 1); m_newsFilterTable->SetAvailable (nsMsgSearchAttrib::Date, nsMsgSearchOp::Isnt, 1); m_newsFilterTable->SetEnabled (nsMsgSearchAttrib::Date, nsMsgSearchOp::Isnt, 1); } return rv; } #ifdef DOING_EXNEWSSEARCH nsresult nsMsgSearchValidityManager::InitNewsExTable (nsINntpIncomingServer *newsHost) { nsresult err = NS_OK; if (!m_newsExTable) err = NewTable (getter_AddRefs(m_newsExTable)); if (NS_OK == err) { PRBool hasAttrib = PR_TRUE; if (newsHost) newsHost->QuerySearchableHeader("FROM", &hasAttrib); m_newsExTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Contains, hasAttrib); m_newsExTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Contains, hasAttrib); m_newsExTable->SetAvailable (nsMsgSearchAttrib::Sender, nsMsgSearchOp::DoesntContain, hasAttrib); m_newsExTable->SetEnabled (nsMsgSearchAttrib::Sender, nsMsgSearchOp::DoesntContain, hasAttrib); if (newsHost) newsHost->QuerySearchableHeader("SUBJECT", &hasAttrib); else hasAttrib = PR_TRUE; m_newsExTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Contains, hasAttrib); m_newsExTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Contains, hasAttrib); m_newsExTable->SetAvailable (nsMsgSearchAttrib::Subject, nsMsgSearchOp::DoesntContain, hasAttrib); m_newsExTable->SetEnabled (nsMsgSearchAttrib::Subject, nsMsgSearchOp::DoesntContain, hasAttrib); if (newsHost) newsHost->QuerySearchableHeader("DATE", &hasAttrib); else hasAttrib = PR_TRUE; m_newsExTable->SetAvailable (nsMsgSearchAttrib::Date, nsMsgSearchOp::IsBefore, hasAttrib); m_newsExTable->SetEnabled (nsMsgSearchAttrib::Date, nsMsgSearchOp::IsBefore, hasAttrib); m_newsExTable->SetAvailable (nsMsgSearchAttrib::Date, nsMsgSearchOp::IsAfter, hasAttrib); m_newsExTable->SetEnabled (nsMsgSearchAttrib::Date, nsMsgSearchOp::IsAfter, hasAttrib); if (newsHost) newsHost->QuerySearchableHeader(":TEXT", &hasAttrib); else hasAttrib = PR_TRUE; m_newsExTable->SetAvailable (nsMsgSearchAttrib::AnyText, nsMsgSearchOp::Contains, hasAttrib); m_newsExTable->SetEnabled (nsMsgSearchAttrib::AnyText, nsMsgSearchOp::Contains, hasAttrib); m_newsExTable->SetAvailable (nsMsgSearchAttrib::AnyText, nsMsgSearchOp::DoesntContain, hasAttrib); m_newsExTable->SetEnabled (nsMsgSearchAttrib::AnyText, nsMsgSearchOp::DoesntContain, hasAttrib); if (newsHost) newsHost->QuerySearchableHeader("KEYWORDS", &hasAttrib); else hasAttrib = PR_TRUE; m_newsExTable->SetAvailable (nsMsgSearchAttrib::Keywords, nsMsgSearchOp::Contains, hasAttrib); m_newsExTable->SetEnabled (nsMsgSearchAttrib::Keywords, nsMsgSearchOp::Contains, hasAttrib); m_newsExTable->SetAvailable (nsMsgSearchAttrib::Keywords, nsMsgSearchOp::DoesntContain, hasAttrib); m_newsExTable->SetEnabled (nsMsgSearchAttrib::Keywords, nsMsgSearchOp::DoesntContain, 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") : PR_TRUE; m_newsExTable->SetAvailable (nsMsgSearchAttrib::Newsgroups, nsMsgSearchOp::IsBefore, hasAttrib); m_newsExTable->SetEnabled (nsMsgSearchAttrib::Newsgroups, nsMsgSearchOp::IsBefore, hasAttrib); m_newsExTable->SetAvailable (nsMsgSearchAttrib::Newsgroups, nsMsgSearchOp::IsAfter, hasAttrib); m_newsExTable->SetEnabled (nsMsgSearchAttrib::Newsgroups, nsMsgSearchOp::IsAfter, hasAttrib); #endif if (newsHost) newsHost->QuerySearchableHeader("DATE", &hasAttrib); else hasAttrib = PR_TRUE; m_newsExTable->SetAvailable (nsMsgSearchAttrib::AgeInDays, nsMsgSearchOp::IsGreaterThan, hasAttrib); m_newsExTable->SetEnabled (nsMsgSearchAttrib::AgeInDays, nsMsgSearchOp::IsGreaterThan, hasAttrib); m_newsExTable->SetAvailable (nsMsgSearchAttrib::AgeInDays, nsMsgSearchOp::IsLessThan, hasAttrib); m_newsExTable->SetEnabled (nsMsgSearchAttrib::AgeInDays, nsMsgSearchOp::IsLessThan, hasAttrib); m_newsExTable->SetAvailable (nsMsgSearchAttrib::AgeInDays, nsMsgSearchOp::Is, hasAttrib); m_newsExTable->SetEnabled (nsMsgSearchAttrib::AgeInDays, nsMsgSearchOp::Is, hasAttrib); // it is possible that the user enters an arbitrary header that is not searchable using NNTP search extensions m_newsExTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Contains, 1); // added for arbitrary headers m_newsExTable->SetEnabled (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Contains, 1); m_newsExTable->SetAvailable (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::DoesntContain, 1); m_newsExTable->SetEnabled (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::DoesntContain, 1); } return err; } nsresult nsMsgSearchValidityManager::PostProcessValidityTable (nsINntpIncomingServer *host) { return InitNewsExTable (host); } #endif