doesn't load the next unread message. thanks to ssu for the initial patch, and neil for the suggestions. r/sr=bienvenu fix for #49305. in "view all headers" mode, we only see the last header for headers where multiple headers are legal. thanks to richard.li@sun.com for the initial patch. r/sr=bienvenu fix for #179803. when we added mail views, we added "Sender is[n't] in addressbook" support to filter / search / views, but it left us with two "Sender" menuitems. this code unifies them into one. on disk (for filters and views) both the old way ("from in ab") and the new way ("from") work, so there isn't a migration issue. r/sr=bienvenu git-svn-id: svn://10.0.0.236/trunk@135107 18797224-902f-48f8-a5cc-f745e15eee43
865 lines
29 KiB
C++
865 lines
29 KiB
C++
/* -*- 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 <nsIMsgSearchValue> 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<nsIMsgSearchTerm> 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 <nsIMsgDatabase> db;
|
|
nsCOMPtr <nsIDBFolderInfo> folderInfo;
|
|
nsCOMPtr <nsIMsgFolder> 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 <nsIMsgDBHdr> 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<nsIMsgSearchSession> session;
|
|
nsCOMPtr <nsIMsgFolder> 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
|