search and filter checkpoint

This commit is contained in:
bienvenu%netscape.com 1999-05-13 02:10:05 +00:00
parent e62a1788b1
commit 23302098d4
5 changed files with 671 additions and 67 deletions

View File

@ -22,7 +22,7 @@
#include "MailNewsTypes.h"
#include "nsString2.h"
class nsMsgDatabase;
class nsIMsgDatabase;
class nsIMsgFolder;
class nsMsgSearchAdapter;
@ -255,15 +255,15 @@ public:
virtual ~nsMsgSearchTerm ();
void StripQuotedPrintable (unsigned char*);
int32 GetNextIMAPOfflineMsgLine (char * buf, int bufferSize, int msgOffset, nsIMessage * msg, nsMsgDatabase * db);
PRInt32 GetNextIMAPOfflineMsgLine (char * buf, int bufferSize, int msgOffset, nsIMessage * msg, nsIMsgDatabase * db);
nsresult MatchBody (nsMsgScopeTerm*, PRUint32 offset, PRUint32 length, PRInt16 csid, nsIMsgFolder * msg, nsMsgDatabase * db);
nsresult MatchArbitraryHeader (nsMsgScopeTerm *,PRUint32 offset, PRUint32 length, PRInt16 csid, nsIMsgFolder * msg, nsMsgDatabase *db,
nsresult MatchBody (nsMsgScopeTerm*, PRUint32 offset, PRUint32 length, PRInt16 csid, nsIMessage * msg, nsIMsgDatabase * db);
nsresult MatchArbitraryHeader (nsMsgScopeTerm *,PRUint32 offset, PRUint32 length, PRInt16 csid, nsIMessage * msg, nsIMsgDatabase *db,
char * headers, /* NULL terminated header list for msgs being filtered. Ignored unless ForFilters */
PRUint32 headersSize, /* size of the NULL terminated list of headers */
PRBool ForFilters /* true if we are filtering */);
nsresult MatchString (const char *, PRInt16 csid, PRBool body = FALSE);
nsresult MatchString (nsString2 *, PRInt16 csid, PRBool body = FALSE);
nsresult MatchDate (time_t);
nsresult MatchStatus (PRUint32);
nsresult MatchPriority (nsMsgPriority);
@ -308,6 +308,72 @@ typedef struct nsMsgSearchMenuItem
} nsMsgSearchMenuItem;
//---------------------------------------------------------------------------
// MSG_BodyHandler: used to retrive lines from POP and IMAP offline messages.
// This is a helper class used by MSG_SearchTerm::MatchBody
//---------------------------------------------------------------------------
class nsMsgBodyHandler
{
public:
nsMsgBodyHandler (nsMsgScopeTerm *, PRUint32 offset, PRUint32 length, nsIMessage * msg, nsIMsgDatabase * db);
// we can also create a body handler when doing arbitrary header filtering...we need the list of headers and the header size as well
// if we are doing filtering...if ForFilters is false, headers and headersSize is ignored!!!
nsMsgBodyHandler (nsMsgScopeTerm *, PRUint32 offset, PRUint32 length, nsIMessage * msg, nsIMsgDatabase * db,
char * headers /* NULL terminated list of headers */, PRUint32 headersSize, PRBool ForFilters);
virtual ~nsMsgBodyHandler();
// Returns nextline
PRInt32 GetNextLine(char * buf, int bufSize); // returns next message line in buf, up to bufSize bytes.
// Transformations
void SetStripHtml (PRBool strip) { m_stripHtml = strip; }
void SetStripHeaders (PRBool strip) { m_stripHeaders = strip; }
protected:
void Initialize(); // common initialization code
// filter related methods. For filtering we always use the headers list instead of the database...
PRBool m_Filtering;
PRInt32 GetNextFilterLine(char * buf, int bufSize);
char * m_headers; // pointer into the headers list in the original message hdr db...
PRUint32 m_headersSize;
PRUint32 m_headerBytesRead;
// local / POP related methods
void OpenLocalFolder();
PRInt32 GetNextLocalLine(char * buf, int bufSize); // goes through the mail folder
nsMsgScopeTerm *m_scope;
// local file state
// XP_File *m_localFile;
// need a file stream here, I bet.
PRInt32 m_localFileOffset; // current offset into the mail folder file
PRUint32 m_numLocalLines;
// Offline IMAP related methods & state
PRInt32 GetNextIMAPLine(char * buf, int bufSize); // goes through the MessageDB
nsIMessage * m_msgHdr;
nsIMsgDatabase * m_db;
PRInt32 m_IMAPMessageOffset;
PRBool m_OfflineIMAP; // TRUE if we are in Offline IMAP mode, FALSE otherwise
// News related methods & state
PRInt32 m_NewsArticleOffset;
PRInt32 GetNextNewsLine (nsIMsgDatabase * newsDB, char * buf, int bufSize); // goes through the NewsDB
// Transformations
PRBool m_stripHeaders; // TRUE if we're supposed to strip of message headers
PRBool m_stripHtml; // TRUE if we're supposed to strip off HTML tags
PRBool m_passedHeaders; // TRUE if we've already skipped over the headers
PRBool m_messageIsHtml; // TRUE if the Content-type header claims text/html
PRInt32 ApplyTransformations (char *buf, PRInt32 length, PRBool &returnThisLine);
void StripHtml (char *buf);
};

View File

@ -30,12 +30,14 @@ CPPSRCS= nsMsgFilterService.cpp\
nsMsgFilterList.cpp\
nsMsgFilter.cpp\
nsMsgSearchTerm.cpp\
nsMsgBodyHandler.cpp\
$(NULL)
CPP_OBJS= .\$(OBJDIR)\nsMsgFilterService.obj \
.\$(OBJDIR)\nsMsgFilterList.obj\
.\$(OBJDIR)\nsMsgFilter.obj\
.\$(OBJDIR)\nsMsgSearchTerm.obj\
.\$(OBJDIR)\nsMsgBodyHandler.obj\
$(NULL)

View File

@ -0,0 +1,334 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1999 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "msgCore.h"
#include "nsMsgSearchCore.h"
#include "nsMsgUtils.h"
nsMsgBodyHandler::nsMsgBodyHandler (nsMsgScopeTerm * scope, PRUint32 offset, PRUint32 numLines, nsIMessage* msg, nsIMsgDatabase * db)
{
m_scope = scope;
m_localFileOffset = offset;
m_numLocalLines = numLines;
m_msgHdr = msg;
m_db = db;
// the following are variables used when the body handler is handling stuff from filters....through this constructor, that is not the
// case so we set them to NULL.
m_headers = NULL;
m_headersSize = 0;
m_Filtering = FALSE; // make sure we set this before we call initialize...
#ifdef DO_BODY
Initialize(); // common initialization stuff
if (m_scope->IsOfflineIMAPMail() || m_scope->m_folder->GetType() == FOLDER_IMAPMAIL) // if we are here with an IMAP folder, assume offline!
m_OfflineIMAP = TRUE;
else
OpenLocalFolder(); // POP so open the mail folder file
#endif
}
nsMsgBodyHandler::nsMsgBodyHandler(nsMsgScopeTerm * scope, PRUint32 offset, PRUint32 numLines, nsIMessage* msg, nsIMsgDatabase* db,
char * headers, PRUint32 headersSize, PRBool Filtering)
{
m_scope = scope;
m_localFileOffset = offset;
m_numLocalLines = numLines;
m_msgHdr = msg;
m_db = db;
m_headersSize = headersSize;
m_Filtering = Filtering;
#ifdef DO_BODY
Initialize();
if (m_Filtering)
m_headers = headers;
else
if (m_scope->IsOfflineIMAPMail() || m_scope->m_folder->GetType() == FOLDER_IMAPMAIL)
m_OfflineIMAP = TRUE;
else
OpenLocalFolder(); // if nothing else applies, then we must be a POP folder file
#endif
}
void nsMsgBodyHandler::Initialize()
// common initialization code regardless of what body type we are handling...
{
// Default transformations for local message search and MAPI access
m_stripHeaders = TRUE;
m_stripHtml = TRUE;
m_messageIsHtml = FALSE;
m_passedHeaders = FALSE;
// set our offsets to 0 since we haven't handled any bytes yet...
m_IMAPMessageOffset = 0;
m_NewsArticleOffset = 0;
m_headerBytesRead = 0;
m_OfflineIMAP = FALSE;
}
nsMsgBodyHandler::~nsMsgBodyHandler()
{
#ifdef DO_BODY
if (m_scope->m_file)
{
XP_FileClose (m_scope->m_file);
m_scope->m_file = NULL;
}
#endif
}
PRInt32 nsMsgBodyHandler::GetNextLine (char * buf, int bufSize)
{
PRInt32 length = 0;
#ifdef DO_BODY
PRBool eatThisLine = FALSE;
do {
// first, handle the filtering case...this is easy....
if (m_Filtering)
length = GetNextFilterLine(buf, bufSize);
else
{
// 3 cases: Offline IMAP, POP, or we are dealing with a news message....
if (m_db)
{
MailDB * mailDB = m_db->GetMailDB();
if (mailDB) // a mail data base?
{
if (m_OfflineIMAP)
length = GetNextIMAPLine (buf, bufSize); // (1) IMAP Offline
else
length = GetNextLocalLine (buf, bufSize); // (2) POP
}
else
{
NewsGroupDB * newsDB = m_db->GetNewsDB();
if (newsDB)
length = GetNextNewsLine (newsDB, buf, bufSize); // (3) News
}
}
}
if (length > 0)
length = ApplyTransformations (buf, length, eatThisLine);
} while (eatThisLine && length); // if we hit eof, make sure we break out of this loop. Bug #:
#endif // DO_BODY
return length;
}
#ifdef DO_BODY
void nsMsgBodyHandler::OpenLocalFolder()
{
if (!m_scope->m_file)
{
const char *path = m_scope->GetMailPath();
if (path)
m_scope->m_file = XP_FileOpen (path, xpMailFolder, XP_FILE_READ_BIN); // open the folder
}
if (m_scope->m_file)
XP_FileSeek (m_scope->m_file, m_localFileOffset, SEEK_SET);
}
PRInt32 nsMsgBodyHandler::GetNextFilterLine(char * buf, int bufSize)
{
// m_nextHdr always points to the next header in the list....the list is NULL terminated...
int numBytesCopied = 0;
if (m_headersSize > 0)
{
// #mscott. Ugly hack! filter headers list have CRs & LFs inside the NULL delimited list of header
// strings. It is possible to have: To NULL CR LF From. We want to skip over these CR/LFs if they start
// at the beginning of what we think is another header.
while ((m_headers[0] == CR || m_headers[0] == LF || m_headers[0] == ' ' || m_headers[0] == '\0') && m_headersSize > 0)
{
m_headers++; // skip over these chars...
m_headersSize--;
}
if (m_headersSize > 0)
{
numBytesCopied = XP_STRLEN(m_headers)+1 /* + 1 to include NULL */ < bufSize ? XP_STRLEN(m_headers)+1 : bufSize;
XP_MEMCPY(buf, m_headers, numBytesCopied);
m_headers += numBytesCopied;
// be careful...m_headersSize is unsigned. Don't let it go negative or we overflow to 2^32....*yikes*
if (m_headersSize < numBytesCopied)
m_headersSize = 0;
else
m_headersSize -= numBytesCopied; // update # bytes we have read from the headers list
return numBytesCopied;
}
}
return 0;
}
PRInt32 nsMsgBodyHandler::GetNextNewsLine (NewsGroupDB * /* newsDB */, char * buf, int bufSize)
{
// we know we have a safe downcasting on m_msgHdr to a NewsMessageHdr because we checked that
// m_db is a news data base b4 calling this routine
PRInt32 msgLength = ((NewsMessageHdr *)m_msgHdr)->GetOfflineMessageLength (m_db->GetDB()) - m_NewsArticleOffset;
if (buf && msgLength != 0) // make sure the news article exists....
{
PRInt32 bytesToCopy = (msgLength < bufSize-2) ? msgLength : bufSize - 2; // this -2 is a small hack
PRInt32 bytesCopied = ((NewsMessageHdr *)m_msgHdr)->ReadFromArticle (buf, bytesToCopy, m_NewsArticleOffset, m_db->GetDB());
if (bytesCopied == 0) // reached end of message?
return bytesCopied;
// now determine the location of the nearest CR/LF pairing...
char * tmp = buf;
while (tmp < buf + bytesCopied && *tmp != 0 && *tmp != CR && *tmp != LF)
tmp++;
if (tmp && (*tmp == CR || *tmp == LF) )
{
// a line is contained within the buffer. Null terminate 2 positions past the CR/LF pair, update new offset value
// we know we have at least 2 bytes leftover in the buffer
if (*tmp == CR && *(tmp+1) == LF) // if it is a CR LF pair then null terminate after the pair
*(tmp+2) = '\0';
else // otherwise, null terminate string after the CR or LF
*(tmp+1) = '\0';
}
else
buf[bytesCopied] = '\0';
m_NewsArticleOffset += XP_STRLEN (buf);
return XP_STRLEN (buf); // return num bytes stored in the buf
}
return 0;
}
PRInt32 nsMsgBodyHandler::GetNextLocalLine(char * buf, int bufSize)
// returns number of bytes copied
{
char * line = NULL;
if (m_numLocalLines)
{
if (m_passedHeaders)
m_numLocalLines--; // the line count is only for body lines
line = XP_FileReadLine (buf, bufSize, m_scope->m_file);
if (line)
return XP_STRLEN(line);
}
return 0;
}
PRInt32 nsMsgBodyHandler::GetNextIMAPLine(char * buf, int bufSize)
{
// we know we have safe downcasting on m_msgHdr because we checked that m_db is a mail data base b4 calling
// this routine.
PRInt32 msgLength = ((MailMessageHdr *) m_msgHdr)->GetOfflineMessageLength (m_db->GetDB()) - m_IMAPMessageOffset;
if (buf && msgLength != 0) // make sure message exists
{
PRInt32 bytesToCopy = (msgLength < bufSize-2) ? msgLength : bufSize-2; // the -2 is a small hack
PRInt32 bytesCopied = ((MailMessageHdr *) m_msgHdr)->ReadFromOfflineMessage (buf, bytesToCopy, m_IMAPMessageOffset, m_db->GetDB());
if (bytesCopied == 0) // we reached end of message
return bytesCopied;
// now determine the location of the nearest CR/LF pairing...
char * tmp = buf;
while (tmp < buf + bytesCopied && *tmp != 0 && *tmp != CR && *tmp != LF)
tmp++;
if (tmp && (*tmp == CR || *tmp == LF) )
{
// a line is contained within the buffer. Null terminate 2 positions past the CR/LF pair, update new offset value
// we know we have at least 2 bytes leftover in the buffer so it is safe to check tmp and tmp + 1...
if (*tmp == CR && *(tmp+1) == LF) // if it is a CR LF pair then null terminate after the pair
*(tmp+2) = '\0';
else // otherwise, null terminate string after the CR or LF
*(tmp+1) = '\0';
}
else
buf[bytesCopied] = '\0';
m_IMAPMessageOffset += XP_STRLEN (buf);
return XP_STRLEN (buf); // return num bytes stored in the buf
}
return 0;
}
PRInt32 nsMsgBodyHandler::ApplyTransformations (char *buf, PRInt32 length, PRBool &eatThisLine)
{
PRInt32 newLength = length;
eatThisLine = FALSE;
if (!m_passedHeaders) // buf is a line from the message headers
{
if (m_stripHeaders)
eatThisLine = TRUE;
if (!XP_STRNCASECMP(buf, "Content-Type:", 13) && strcasestr (buf, "text/html"))
m_messageIsHtml = TRUE;
m_passedHeaders = EMPTY_MESSAGE_LINE(buf);
}
else // buf is a line from the message body
{
if (m_stripHtml && m_messageIsHtml)
{
StripHtml (buf);
newLength = XP_STRLEN (buf);
}
}
return newLength;
}
void nsMsgBodyHandler::StripHtml (char *pBufInOut)
{
char *pBuf = (char*) XP_ALLOC (XP_STRLEN(pBufInOut) + 1);
if (pBuf)
{
char *pWalk = pBuf;
char *pWalkInOut = pBufInOut;
PRBool inTag = FALSE;
while (*pWalkInOut) // throw away everything inside < >
{
if (!inTag)
if (*pWalkInOut == '<')
inTag = TRUE;
else
*pWalk++ = *pWalkInOut;
else
if (*pWalkInOut == '>')
inTag = FALSE;
pWalkInOut++;
}
*pWalk = 0; // null terminator
// copy the temp buffer back to the real one
pWalk = pBuf;
pWalkInOut = pBufInOut;
while (*pWalk)
*pWalkInOut++ = *pWalk++;
*pWalkInOut = *pWalk; // null terminator
XP_FREE (pBuf);
}
}
#endif // DO_BODY

View File

@ -209,7 +209,6 @@ nsresult nsMsgFilter::SaveRule()
{
nsresult err = NS_OK;
char *relativePath = nsnull;
const char *folderDirectory;
nsMsgFilterList *filterList = GetFilterList();
nsString2 actionFilingStr(eOneByte);
@ -244,7 +243,6 @@ nsresult nsMsgFilter::SaveRule()
searchIndex++)
{
nsString2 stream(eOneByte);
int16 length;
nsMsgSearchTerm * term = (nsMsgSearchTerm *) m_termList.ElementAt(searchIndex);
if (term == NULL)

View File

@ -18,6 +18,8 @@
#include "msgCore.h"
#include "nsMsgSearchCore.h"
#include "nsMsgUtils.h"
#include "nsIMsgDatabase.h"
//---------------------------------------------------------------------------
// nsMsgSearchTerm specifies one criterion, e.g. name contains phil
@ -28,6 +30,194 @@
//-------------------- Implementation of nsMsgSearchTerm -----------------------
//-----------------------------------------------------------------------------
typedef struct
{
nsMsgSearchAttribute attrib;
const char *attribName;
} nsMsgSearchAttribEntry;
nsMsgSearchAttribEntry SearchAttribEntryTable[] =
{
{nsMsgSearchAttribSubject, "subject"},
{nsMsgSearchAttribSender, "from"},
{nsMsgSearchAttribBody, "body"},
{nsMsgSearchAttribDate, "date"},
{nsMsgSearchAttribPriority, "priority"},
{nsMsgSearchAttribMsgStatus, "status"},
{nsMsgSearchAttribTo, "to"},
{nsMsgSearchAttribCC, "CC"},
{nsMsgSearchAttribToOrCC, "to or CC"}
};
// Take a string which starts off with an attribute
// return the matching attribute. If the string is not in the table, then we can conclude that it is an arbitrary header
nsresult NS_MsgGetAttributeFromString(const char *string, int16 *attrib)
{
if (NULL == string || NULL == attrib)
return NS_ERROR_NULL_POINTER;
PRBool found = FALSE;
for (int idxAttrib = 0; idxAttrib < sizeof(SearchAttribEntryTable) / sizeof(nsMsgSearchAttribEntry); idxAttrib++)
{
if (!PL_strcasecmp(string, SearchAttribEntryTable[idxAttrib].attribName))
{
found = TRUE;
*attrib = SearchAttribEntryTable[idxAttrib].attrib;
break;
}
}
if (!found)
*attrib = nsMsgSearchAttribOtherHeader; // assume arbitrary header if we could not find the header in the table
return NS_OK; // we always succeed now
}
nsresult NS_MsgGetStringForAttribute(int16 attrib, const char **string)
{
if (NULL == string)
return NS_ERROR_NULL_POINTER;
PRBool found = FALSE;
for (int idxAttrib = 0; idxAttrib < sizeof(SearchAttribEntryTable) / sizeof(nsMsgSearchAttribEntry); idxAttrib++)
{
// I'm using the idx's as aliases into MSG_SearchAttribute and
// MSG_SearchOperator enums which is legal because of the way the
// enums are defined (starts at 0, numItems at end)
if (attrib == SearchAttribEntryTable[idxAttrib].attrib)
{
found = PR_TRUE;
*string = SearchAttribEntryTable[idxAttrib].attribName;
break;
}
}
// we no longer return invalid attribute. If we cannot find the string in the table,
// then it is an arbitrary header. Return success regardless if found or not
// return (found) ? SearchError_Success : SearchError_InvalidAttribute;
return NS_OK;
}
typedef struct
{
nsMsgSearchOperator op;
const char *opName;
} nsMsgSearchOperatorEntry;
nsMsgSearchOperatorEntry SearchOperatorEntryTable[] =
{
{nsMsgSearchOpContains, "contains"},
{nsMsgSearchOpDoesntContain,"doesn't contain"},
{nsMsgSearchOpIs, "is"},
{nsMsgSearchOpIsnt, "isn't"},
{nsMsgSearchOpIsEmpty, "is empty"},
{nsMsgSearchOpIsBefore, "is before"},
{nsMsgSearchOpIsAfter, "is after"},
{nsMsgSearchOpIsHigherThan, "is higher than"},
{nsMsgSearchOpIsLowerThan, "is lower than"},
{nsMsgSearchOpBeginsWith, "begins with"},
{nsMsgSearchOpEndsWith, "ends with"}
};
nsresult NS_MsgGetOperatorFromString(const char *string, int16 *op)
{
if (NULL == string || NULL == op)
return NS_ERROR_NULL_POINTER;
XP_Bool found = FALSE;
for (int idxOp = 0; idxOp < sizeof(SearchOperatorEntryTable) / sizeof(nsMsgSearchOperatorEntry); idxOp++)
{
// I'm using the idx's as aliases into MSG_SearchAttribute and
// MSG_SearchOperator enums which is legal because of the way the
// enums are defined (starts at 0, numItems at end)
if (!PL_strcasecmp(string, SearchOperatorEntryTable[idxOp].opName))
{
found = TRUE;
*op = SearchOperatorEntryTable[idxOp].op;
break;
}
}
return (found) ? NS_OK : NS_ERROR_INVALID_ARG;
}
nsresult NS_MsgGetStringForOperator(int16 op, const char **string)
{
if (NULL == string)
return NS_ERROR_NULL_POINTER;
PRBool found = FALSE;
for (int idxOp = 0; idxOp < sizeof(SearchOperatorEntryTable) / sizeof(nsMsgSearchOperatorEntry); idxOp++)
{
// I'm using the idx's as aliases into MSG_SearchAttribute and
// MSG_SearchOperator enums which is legal because of the way the
// enums are defined (starts at 0, numItems at end)
if (op == SearchOperatorEntryTable[idxOp].op)
{
found = TRUE;
*string = SearchOperatorEntryTable[idxOp].opName;
break;
}
}
return (found) ? NS_OK : NS_ERROR_INVALID_ARG;
}
void NS_MsgGetUntranslatedStatusName (uint32 s, nsString2 *outName)
{
char *tmpOutName = NULL;
#define MSG_STATUS_MASK (MSG_FLAG_READ | MSG_FLAG_REPLIED | MSG_FLAG_FORWARDED | MSG_FLAG_NEW)
PRUint32 maskOut = (s & MSG_STATUS_MASK);
// diddle the flags to pay attention to the most important ones first, if multiple
// flags are set. Should remove this code from the winfe.
if (maskOut & MSG_FLAG_NEW)
maskOut = MSG_FLAG_NEW;
if ( maskOut & MSG_FLAG_REPLIED &&
maskOut & MSG_FLAG_FORWARDED )
maskOut = MSG_FLAG_REPLIED|MSG_FLAG_FORWARDED;
else if ( maskOut & MSG_FLAG_FORWARDED )
maskOut = MSG_FLAG_FORWARDED;
else if ( maskOut & MSG_FLAG_REPLIED )
maskOut = MSG_FLAG_REPLIED;
switch (maskOut)
{
case MSG_FLAG_READ:
tmpOutName = "read";
break;
case MSG_FLAG_REPLIED:
tmpOutName = "replied";
break;
case MSG_FLAG_FORWARDED:
tmpOutName = "forwarded";
break;
case MSG_FLAG_FORWARDED|MSG_FLAG_REPLIED:
tmpOutName = "replied and forwarded";
break;
case MSG_FLAG_NEW:
tmpOutName = "new";
break;
default:
// This is fine, status may be "unread" for example
break;
}
if (tmpOutName)
*outName = tmpOutName;
}
PRInt32 NS_MsgGetStatusValueFromName(char *name)
{
if (PL_strcmp("read", name))
return MSG_FLAG_READ;
if (PL_strcmp("replied", name))
return MSG_FLAG_REPLIED;
if (PL_strcmp("forwarded", name))
return MSG_FLAG_FORWARDED;
if (PL_strcmp("replied and forwarded", name))
return MSG_FLAG_FORWARDED|MSG_FLAG_REPLIED;
if (PL_strcmp("new", name))
return MSG_FLAG_NEW;
return 0;
}
// Needed for DeStream method.
nsMsgSearchTerm::nsMsgSearchTerm()
{
@ -146,17 +336,16 @@ nsresult nsMsgSearchTerm::OutputValue(nsString2 &outputStr)
}
case nsMsgSearchAttribMsgStatus:
{
char status[40];
MSG_GetStatusName (m_value.u.msgStatus, status, sizeof(status));
nsString2 status(eOneByte);
NS_MsgGetUntranslatedStatusName (m_value.u.msgStatus, &status);
outputStr += status;
break;
}
case nsMsgSearchAttribPriority:
{
char priority[40];
MSG_GetUntranslatedPriorityName( m_value.u.priority,
priority,
sizeof(priority));
nsString2 priority(eOneByte);
NS_MsgGetUntranslatedPriorityName( m_value.u.priority,
&priority);
outputStr += priority;
break;
}
@ -174,15 +363,14 @@ nsresult nsMsgSearchTerm::EnStreamNew (nsString2 &outStream)
nsString2 outputStr(eOneByte);
nsresult ret;
ret = MSG_GetStringForAttribute(m_attribute, &attrib);
ret = NS_MsgGetStringForAttribute(m_attribute, &attrib);
if (ret != NS_OK)
return ret;
if (m_attribute == nsMsgSearchAttribOtherHeader) // if arbitrary header, use it instead!
{
outputStr = "\"";
if (m_arbitraryHeader)
outputStr += m_arbitraryHeader;
outputStr += m_arbitraryHeader;
outputStr += "\"";
}
else
@ -190,7 +378,7 @@ nsresult nsMsgSearchTerm::EnStreamNew (nsString2 &outStream)
outputStr += ',';
ret = MSG_GetStringForOperator(m_operator, &operatorStr);
ret = NS_MsgGetStringForOperator(m_operator, &operatorStr);
if (ret != NS_OK)
return ret;
@ -214,7 +402,7 @@ nsresult nsMsgSearchTerm::ParseValue(char *inStream)
// need to remove pair of '"', if present
if (*inStream == '"')
{
quoteVal = TRUE;
quoteVal = PR_TRUE;
inStream++;
}
int valueLen = PL_strlen(inStream);
@ -222,20 +410,22 @@ nsresult nsMsgSearchTerm::ParseValue(char *inStream)
valueLen--;
m_value.u.string = (char *) PR_Malloc(valueLen + 1);
XP_STRNCPY_SAFE(m_value.u.string, inStream, valueLen + 1);
PL_strncpy(m_value.u.string, inStream, valueLen + 1);
}
else
{
switch (m_attribute)
{
case nsMsgSearchAttribDate:
#ifdef DO_DATE_YET
m_value.u.date = XP_ParseTimeString (inStream, PR_FALSE);
#endif
break;
case nsMsgSearchAttribMsgStatus:
m_value.u.msgStatus = MSG_GetStatusValueFromName(inStream);
m_value.u.msgStatus = NS_MsgGetStatusValueFromName(inStream);
break;
case nsMsgSearchAttribPriority:
m_value.u.priority = MSG_GetPriorityFromString(inStream);
NS_MsgGetPriorityFromString(inStream, &m_value.u.priority);
break;
default:
NS_ASSERTION(PR_FALSE, "invalid attribute parsing search term value");
@ -260,7 +450,7 @@ nsMsgSearchOperator nsMsgSearchTerm::ParseOperator(char *inStream)
if (commaSep)
*commaSep = '\0';
err = MSG_GetOperatorFromString(inStream, &operatorVal);
err = NS_MsgGetOperatorFromString(inStream, &operatorVal);
return (nsMsgSearchOperator) operatorVal;
}
@ -291,7 +481,7 @@ nsMsgSearchAttribute nsMsgSearchTerm::ParseAttribute(char *inStream)
if (separator)
*separator = '\0';
err = MSG_GetAttributeFromString(inStream, &attributeVal);
err = NS_MsgGetAttributeFromString(inStream, &attributeVal);
nsMsgSearchAttribute attrib = (nsMsgSearchAttribute) attributeVal;
if (attrib == nsMsgSearchAttribOtherHeader) // if we are dealing with an arbitrary header....
@ -311,7 +501,7 @@ nsresult nsMsgSearchTerm::DeStreamNew (char *inStream, int16 /*length*/)
char *commaSep = PL_strchr(inStream, ',');
m_attribute = ParseAttribute(inStream); // will allocate space for arbitrary header if necessary
if (!commaSep)
return SearchError_InvalidSearchTerm;
return NS_ERROR_INVALID_ARG;
char *secondCommaSep = PL_strchr(commaSep + 1, ',');
if (commaSep)
m_operator = ParseOperator(commaSep + 1);
@ -377,15 +567,14 @@ void nsMsgSearchTerm::StripQuotedPrintable (unsigned char *src)
dest[destIdx] = src[srcIdx]; // null terminate
}
#define EMPTY_MESSAGE_LINE(buf) (buf[0] == CR || buf[0] == LF || buf[0] == '\0')
// Looks in the MessageDB for the user specified arbitrary header, if it finds the header, it then looks for a match against
// the value for the header.
nsresult nsMsgSearchTerm::MatchArbitraryHeader (nsMsgScopeTerm *scope, PRUint32 offset, PRUint32 length /* in lines*/, int16 foldcsid,
nsMsgHdr *msg, nsIMsgDatabase* db, char * headers, PRUint32 headersSize, PRBool ForFiltering)
nsIMessage *msg, nsIMsgDatabase* db, char * headers, PRUint32 headersSize, PRBool ForFiltering)
{
if (!m_arbitraryHeader)
return SearchError_NotAMatch;
nsresult err = SearchError_NotAMatch;
nsresult err = NS_COMFALSE;
nsMsgBodyHandler * bodyHan = new nsMsgBodyHandler (scope, offset,length, msg, db, headers, headersSize, ForFiltering);
if (!bodyHan)
return NS_ERROR_OUT_OF_MEMORY;
@ -395,7 +584,7 @@ nsresult nsMsgSearchTerm::MatchArbitraryHeader (nsMsgScopeTerm *scope, PRUint32
if (MatchAllBeforeDeciding())
err = NS_OK;
else
err = SearchError_NotAMatch;
err = NS_COMFALSE;
const int kBufSize = 512; // max size of a line??
char * buf = (char *) PR_Malloc(kBufSize);
@ -405,8 +594,8 @@ nsresult nsMsgSearchTerm::MatchArbitraryHeader (nsMsgScopeTerm *scope, PRUint32
while (searchingHeaders && bodyHan->GetNextLine(buf, kBufSize))
{
char * buf_end = buf + PL_strlen(buf);
int headerLength = PL_strlen(m_arbitraryHeader);
if (!XP_STRNCASECMP(buf,m_arbitraryHeader,headerLength))
int headerLength = m_arbitraryHeader.Length();
if (m_arbitraryHeader.Equals(buf))
{
char * headerValue = buf + headerLength; // value occurs after the header name...
if (headerValue < buf_end && headerValue[0] == ':') // + 1 to account for the colon which is MANDATORY
@ -426,7 +615,8 @@ nsresult nsMsgSearchTerm::MatchArbitraryHeader (nsMsgScopeTerm *scope, PRUint32
if (headerValue < buf_end && *headerValue) // make sure buf has info besides just the header
{
nsresult err2 = MatchString(headerValue, foldcsid); // match value with the other info...
nsString2 headerStr = headerValue;
nsresult err2 = MatchString(&headerStr, foldcsid); // match value with the other info...
if (err != err2) // if we found a match
{
searchingHeaders = PR_FALSE; // then stop examining the headers
@ -451,9 +641,9 @@ nsresult nsMsgSearchTerm::MatchArbitraryHeader (nsMsgScopeTerm *scope, PRUint32
}
nsresult nsMsgSearchTerm::MatchBody (nsMsgScopeTerm *scope, PRUint32 offset, PRUint32 length /*in lines*/, int16 foldcsid,
nsMsgHdr *msg, nsMsgDatabase* db)
nsIMessage *msg, nsIMsgDatabase* db)
{
nsresult err = SearchError_NotAMatch;
nsresult err = NS_COMFALSE;
// Small hack so we don't look all through a message when someone has
// specified "BODY IS foo"
@ -466,6 +656,7 @@ nsresult nsMsgSearchTerm::MatchBody (nsMsgScopeTerm *scope, PRUint32 offset, PRU
const int kBufSize = 512; // max size of a line???
char *buf = (char*) PR_Malloc(kBufSize);
#ifdef HAVE_I18N
if (buf)
{
PRBool endOfFile = PR_FALSE; // if retValue == 0, we've hit the end of the file
@ -484,7 +675,7 @@ nsresult nsMsgSearchTerm::MatchBody (nsMsgScopeTerm *scope, PRUint32 offset, PRU
if (MatchAllBeforeDeciding())
err = errContinueLoop = NS_OK;
else
err = errContinueLoop = SearchError_NotAMatch;
err = errContinueLoop = NS_COMFALSE;
// If there's a '=' in the search term, then we're not going to do
// quoted printable decoding. Otherwise we assume everything is
@ -537,17 +728,20 @@ nsresult nsMsgSearchTerm::MatchBody (nsMsgScopeTerm *scope, PRUint32 offset, PRU
}
else
err = NS_ERROR_OUT_OF_MEMORY;
#endif // HAVE_I18N
return err;
}
nsresult nsMsgSearchTerm::MatchString (const char *stringToMatch, int16 csid, PRBool body)
// returns NS_COMFALSE when strings don't match, NS_OK if they do.
nsresult nsMsgSearchTerm::MatchString (nsString2 *stringToMatch, int16 csid, PRBool body)
{
nsresult err = SearchError_NotAMatch;
unsigned char* n_str = nsnull;
nsresult err = NS_COMFALSE;
nsString2 n_str(eOneByte);
unsigned char* n_header = nsnull;
if(nsMsgSearchOpIsEmpty != m_operator) // Save some performance for opIsEmpty
{
#ifdef DO_I18N
n_str = INTL_GetNormalizeStr(csid , (unsigned char*)m_value.u.string); // Always new buffer unless not enough memory
if (!body)
n_header = INTL_GetNormalizeStrFromRFC1522(csid , (unsigned char*)stringToMatch); // Always new buffer unless not enough memory
@ -556,23 +750,28 @@ nsresult nsMsgSearchTerm::MatchString (const char *stringToMatch, int16 csid, PR
NS_ASSERTION(n_str, "failed get normalized string");
NS_ASSERTION(n_header, "failed get normalized header");
#else
n_str = m_value.u.string;
#endif // DO_I18N
}
switch (m_operator)
{
case nsMsgSearchOpContains:
if((nsnull != n_str) && (nsnull != n_header) && (n_str[0]) && INTL_StrContains(csid, n_header, n_str))
if ((nsnull != n_header) && (n_str[0]) && /* INTL_StrContains(csid, n_header, n_str) */
n_str.Find(*stringToMatch, PR_TRUE) != -1)
err = NS_OK;
break;
case nsMsgSearchOpDoesntContain:
if((nsnull != n_str) && (nsnull != n_header) && (n_str[0]) && (! INTL_StrContains(csid, n_header, n_str)))
if ((nsnull != n_header) && (n_str[0]) && ( /* !INTL_StrContains(csid, n_header, n_str) */
n_str.Find(*stringToMatch, PR_TRUE) == -1))
err = NS_OK;
break;
case nsMsgSearchOpIs:
if(n_str && n_header)
if(n_header)
{
if (n_str[0])
{
if (INTL_StrIs(csid, n_header, n_str))
if (n_str.Equals(*stringToMatch, PR_TRUE) != -1/* INTL_StrIs(csid, n_header, n_str)*/ )
err = NS_OK;
}
else if (n_header[0] == '\0') // Special case for "is <the empty string>"
@ -580,11 +779,11 @@ nsresult nsMsgSearchTerm::MatchString (const char *stringToMatch, int16 csid, PR
}
break;
case nsMsgSearchOpIsnt:
if(n_str && n_header)
if(n_header)
{
if (n_str[0])
{
if (! INTL_StrIs(csid, n_header, n_str))
if (!n_str.Equals(*stringToMatch, PR_TRUE)/* INTL_StrIs(csid, n_header, n_str)*/ )
err = NS_OK;
}
else if (n_header[0] != '\0') // Special case for "isn't <the empty string>"
@ -592,28 +791,31 @@ nsresult nsMsgSearchTerm::MatchString (const char *stringToMatch, int16 csid, PR
}
break;
case nsMsgSearchOpIsEmpty:
if (!stringToMatch || stringToMatch[0] == '\0')
if (stringToMatch->Length() == 0)
err = NS_OK;
break;
case nsMsgSearchOpBeginsWith:
#ifdef DO_I18N_YET
if((nsnull != n_str) && (nsnull != n_header) && INTL_StrBeginWith(csid, n_header, n_str))
err = NS_OK;
#else
NS_ASSERTION(PR_FALSE, "not implemented yet");
#endif
break;
case nsMsgSearchOpEndsWith:
#ifdef DO_I18N_YET
{
if((nsnull != n_str) && (nsnull != n_header) && INTL_StrEndWith(csid, n_header, n_str))
err = NS_OK;
}
#else
NS_ASSERTION(PR_FALSE, "not implemented yet");
#endif
break;
default:
NS_ASSERTION(PR_FALSE, "invalid operator matching search results");
}
if(n_str) // Need to free the normalized string
PR_Free(n_str);
if(n_header) // Need to free the normalized string
PR_Free(n_header);
return err;
}
@ -628,6 +830,8 @@ PRBool nsMsgSearchTerm::MatchAllBeforeDeciding ()
nsresult nsMsgSearchTerm::MatchRfc822String (const char *string, int16 csid)
{
nsresult err;
#ifdef DO_RFC822
// Isolate the RFC 822 parsing weirdnesses here. MSG_ParseRFC822Addresses
// returns a catenated string of null-terminated strings, which we walk
// across, tring to match the target string to either the name OR the address
@ -636,12 +840,11 @@ nsresult nsMsgSearchTerm::MatchRfc822String (const char *string, int16 csid)
// Change the sense of the loop so we don't bail out prematurely
// on negative terms. i.e. opDoesntContain must look at all recipients
nsresult err;
nsresult errContinueLoop;
if (MatchAllBeforeDeciding())
err = errContinueLoop = NS_OK;
else
err = errContinueLoop = SearchError_NotAMatch;
err = errContinueLoop = NS_COMFALSE;
int count = MSG_ParseRFC822Addresses (string, &names, &addresses);
if (count > 0)
@ -651,14 +854,14 @@ nsresult nsMsgSearchTerm::MatchRfc822String (const char *string, int16 csid)
if (!names || !addresses)
return err;
char *walkNames = names;
char *walkAddresses = addresses;
nsString2 walkNames = names;
nsString2 walkAddresses = addresses;
for (int i = 0; i < count && err == errContinueLoop; i++)
{
err = MatchString (walkNames, csid);
err = MatchString (&walkNames, csid);
if (errContinueLoop == err)
err = MatchString (walkAddresses, csid);
err = MatchString (&walkAddresses, csid);
walkNames += PL_strlen(walkNames) + 1;
walkAddresses += PL_strlen(walkAddresses) + 1;
@ -667,7 +870,7 @@ nsresult nsMsgSearchTerm::MatchRfc822String (const char *string, int16 csid)
PR_FREEIF(names);
PR_FREEIF(addresses);
}
#endif // DO_RFC822
return err;
}
@ -689,13 +892,13 @@ nsresult nsMsgSearchTerm::GetLocalTimes (time_t a, time_t b, struct tm &aTm, str
return NS_OK;
}
}
return SearchError_InvalidAttribute;
return NS_ERROR_INVALID_ARG;
}
nsresult nsMsgSearchTerm::MatchDate (time_t dateToMatch)
{
nsresult err = SearchError_NotAMatch;
nsresult err = NS_COMFALSE;
switch (m_operator)
{
case nsMsgSearchOpIsBefore:
@ -742,7 +945,8 @@ nsresult nsMsgSearchTerm::MatchDate (time_t dateToMatch)
nsresult nsMsgSearchTerm::MatchAge (time_t msgDate)
{
nsresult err = SearchError_NotAMatch;
nsresult err = NS_COMFALSE;
#ifdef DO_AGE_YET
time_t now = XP_TIME();
time_t matchDay = now - (m_value.u.age * 60 * 60 * 24);
struct tm * matchTime = localtime(&matchDay);
@ -774,14 +978,14 @@ nsresult nsMsgSearchTerm::MatchAge (time_t msgDate)
default:
NS_ASSERTION(PR_FALSE, "invalid compare op comparing msg age");
}
#endif // DO_AGE_YET
return err;
}
nsresult nsMsgSearchTerm::MatchSize (PRUint32 sizeToMatch)
{
nsresult err = SearchError_NotAMatch;
nsresult err = NS_COMFALSE;
switch (m_operator)
{
case nsMsgSearchOpIsHigherThan:
@ -801,7 +1005,7 @@ nsresult nsMsgSearchTerm::MatchSize (PRUint32 sizeToMatch)
nsresult nsMsgSearchTerm::MatchStatus (PRUint32 statusToMatch)
{
nsresult err = SearchError_NotAMatch;
nsresult err = NS_COMFALSE;
PRBool matches = PR_FALSE;
if (statusToMatch & m_value.u.msgStatus)
@ -827,7 +1031,7 @@ nsresult nsMsgSearchTerm::MatchStatus (PRUint32 statusToMatch)
nsresult nsMsgSearchTerm::MatchPriority (nsMsgPriority priorityToMatch)
{
nsresult err = SearchError_NotAMatch;
nsresult err = NS_COMFALSE;
// Use this ugly little hack to get around the fact that enums don't have
// integer compare operators
@ -886,7 +1090,7 @@ nsMsgResultElement::~nsMsgResultElement ()
nsresult nsMsgResultElement::AddValue (nsMsgSearchValue *value)
{
m_valueList.AddElement (value);
m_valueList.AppendElement (value);
return NS_OK;
}
@ -940,7 +1144,7 @@ nsresult nsMsgResultElement::AssignValues (nsMsgSearchValue *src, nsMsgSearchVal
err = NS_ERROR_OUT_OF_MEMORY;
}
else
err = SearchError_InvalidAttribute;
err = NS_ERROR_INVALID_ARG;
}
return err;
}