From 7735dc7cfdb82455807d0dd2dde01ca87fa5811f Mon Sep 17 00:00:00 2001 From: "bienvenu%netscape.com" Date: Thu, 13 May 1999 02:10:05 +0000 Subject: [PATCH] search and filter checkpoint git-svn-id: svn://10.0.0.236/trunk@31369 18797224-902f-48f8-a5cc-f745e15eee43 --- .../base/search/public/nsMsgSearchCore.h | 76 +++- mozilla/mailnews/base/search/src/makefile.win | 2 + .../base/search/src/nsMsgBodyHandler.cpp | 334 ++++++++++++++++++ .../mailnews/base/search/src/nsMsgFilter.cpp | 2 - .../base/search/src/nsMsgSearchTerm.cpp | 324 +++++++++++++---- 5 files changed, 671 insertions(+), 67 deletions(-) create mode 100644 mozilla/mailnews/base/search/src/nsMsgBodyHandler.cpp diff --git a/mozilla/mailnews/base/search/public/nsMsgSearchCore.h b/mozilla/mailnews/base/search/public/nsMsgSearchCore.h index 6773e9bbc31..6c2c2dcadee 100644 --- a/mozilla/mailnews/base/search/public/nsMsgSearchCore.h +++ b/mozilla/mailnews/base/search/public/nsMsgSearchCore.h @@ -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); +}; + + diff --git a/mozilla/mailnews/base/search/src/makefile.win b/mozilla/mailnews/base/search/src/makefile.win index cc442e5e8e2..b84908c0159 100644 --- a/mozilla/mailnews/base/search/src/makefile.win +++ b/mozilla/mailnews/base/search/src/makefile.win @@ -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) diff --git a/mozilla/mailnews/base/search/src/nsMsgBodyHandler.cpp b/mozilla/mailnews/base/search/src/nsMsgBodyHandler.cpp new file mode 100644 index 00000000000..cdc1a9bcc84 --- /dev/null +++ b/mozilla/mailnews/base/search/src/nsMsgBodyHandler.cpp @@ -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 + diff --git a/mozilla/mailnews/base/search/src/nsMsgFilter.cpp b/mozilla/mailnews/base/search/src/nsMsgFilter.cpp index d8b81cef7e7..d582fce2026 100644 --- a/mozilla/mailnews/base/search/src/nsMsgFilter.cpp +++ b/mozilla/mailnews/base/search/src/nsMsgFilter.cpp @@ -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) diff --git a/mozilla/mailnews/base/search/src/nsMsgSearchTerm.cpp b/mozilla/mailnews/base/search/src/nsMsgSearchTerm.cpp index 4211b616482..85efa48927f 100644 --- a/mozilla/mailnews/base/search/src/nsMsgSearchTerm.cpp +++ b/mozilla/mailnews/base/search/src/nsMsgSearchTerm.cpp @@ -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 " @@ -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 " @@ -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; }