Back to index

lightning-sunbird  0.9+nobinonly
nsMsgSearchTerm.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Seth Spitzer <sspitzer@netscape.com>
00024  *   Jungshik Shin <jshin@mailaps.org>
00025  *   David Bienvenu <bienvenu@nventure.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "msgCore.h"
00042 #include "nsXPIDLString.h"
00043 #include "nsReadableUtils.h"
00044 
00045 #include "nsMsgSearchCore.h"
00046 #include "nsIMsgSearchSession.h"
00047 #include "nsMsgUtils.h"
00048 #include "nsIMsgDatabase.h"
00049 #include "nsMsgSearchTerm.h"
00050 #include "nsMsgSearchScopeTerm.h"
00051 #include "nsMsgBodyHandler.h"
00052 #include "nsMsgResultElement.h"
00053 #include "nsIMsgImapMailFolder.h"
00054 #include "nsMsgSearchImap.h"
00055 #include "nsMsgLocalSearch.h"
00056 #include "nsMsgSearchNews.h"
00057 #include "nsMsgSearchValue.h"
00058 #include "nsMsgI18N.h"
00059 #include "nsIMimeConverter.h"
00060 #include "nsMsgMimeCID.h"
00061 #include "nsTime.h"
00062 #include "nsIPrefBranch.h"
00063 #include "nsIPrefService.h"
00064 #include "nsIMsgFilterPlugin.h"
00065 #include "nsIFileSpec.h"
00066 #include "nsIRDFService.h"
00067 #include "nsISupportsObsolete.h"
00068 #include "nsNetCID.h"
00069 #include "nsIFileStreams.h"
00070 #include "nsUnicharUtils.h"
00071 //---------------------------------------------------------------------------
00072 // nsMsgSearchTerm specifies one criterion, e.g. name contains phil
00073 //---------------------------------------------------------------------------
00074 
00075 
00076 //-----------------------------------------------------------------------------
00077 //-------------------- Implementation of nsMsgSearchTerm -----------------------
00078 //-----------------------------------------------------------------------------
00079 #define MAILNEWS_CUSTOM_HEADERS "mailnews.customHeaders"
00080 
00081 typedef struct
00082 {
00083   nsMsgSearchAttribValue    attrib;
00084   const char                *attribName;
00085 } nsMsgSearchAttribEntry;
00086 
00087 nsMsgSearchAttribEntry SearchAttribEntryTable[] =
00088 {
00089     {nsMsgSearchAttrib::Subject,    "subject"},
00090     {nsMsgSearchAttrib::Sender,     "from"},
00091     {nsMsgSearchAttrib::Body,       "body"},
00092     {nsMsgSearchAttrib::Date,       "date"},
00093     {nsMsgSearchAttrib::Priority,   "priority"},
00094     {nsMsgSearchAttrib::MsgStatus,  "status"},
00095     {nsMsgSearchAttrib::To,         "to"},
00096     {nsMsgSearchAttrib::CC,         "cc"},
00097     {nsMsgSearchAttrib::ToOrCC,     "to or cc"},
00098     {nsMsgSearchAttrib::AgeInDays,  "age in days"},
00099     {nsMsgSearchAttrib::Label,      "label"},
00100     {nsMsgSearchAttrib::Keywords,   "tag"},
00101     {nsMsgSearchAttrib::Size,       "size"},
00102     // this used to be nsMsgSearchAttrib::SenderInAddressBook
00103     // we used to have two Sender menuitems
00104     // for backward compatability, we can still parse
00105     // the old style.  see bug #179803
00106     {nsMsgSearchAttrib::Sender,     "from in ab"}, 
00107     {nsMsgSearchAttrib::JunkStatus, "junk status"},
00108     {nsMsgSearchAttrib::HasAttachmentStatus, "has attachment status"},
00109 };
00110 
00111 // Take a string which starts off with an attribute
00112 // return the matching attribute. If the string is not in the table, then we can conclude that it is an arbitrary header
00113 nsresult NS_MsgGetAttributeFromString(const char *string, PRInt16 *attrib)
00114 {
00115   NS_ENSURE_ARG_POINTER(string);
00116   NS_ENSURE_ARG_POINTER(attrib);
00117   
00118   PRBool found = PR_FALSE;
00119   for (int idxAttrib = 0; idxAttrib < (int)(sizeof(SearchAttribEntryTable) / sizeof(nsMsgSearchAttribEntry)); idxAttrib++)
00120   {
00121     if (!PL_strcasecmp(string, SearchAttribEntryTable[idxAttrib].attribName))
00122     {
00123       found = PR_TRUE;
00124       *attrib = SearchAttribEntryTable[idxAttrib].attrib;
00125       break;
00126     }
00127   }  
00128   if (!found)
00129   {
00130     nsresult rv;
00131     PRBool goodHdr;
00132     IsRFC822HeaderFieldName(string, &goodHdr);
00133     if (!goodHdr)
00134       return NS_MSG_INVALID_CUSTOM_HEADER;
00135     //49 is for showing customize... in ui, headers start from 50 onwards up until 99.
00136     *attrib = nsMsgSearchAttrib::OtherHeader+1;
00137 
00138     nsCOMPtr<nsIPrefService> prefService = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
00139     NS_ENSURE_SUCCESS(rv, rv);
00140 
00141     nsCOMPtr<nsIPrefBranch> prefBranch;
00142     rv = prefService->GetBranch(nsnull, getter_AddRefs(prefBranch));
00143     NS_ENSURE_SUCCESS(rv, rv);
00144 
00145     nsXPIDLCString headers;
00146     prefBranch->GetCharPref(MAILNEWS_CUSTOM_HEADERS, getter_Copies(headers));
00147 
00148     if (!headers.IsEmpty())
00149     {
00150       char *headersString = ToNewCString(headers);
00151 
00152       nsCAutoString hdrStr;
00153       hdrStr.Adopt(headersString);
00154       hdrStr.StripWhitespace();  //remove whitespace before parsing
00155 
00156       char *newStr=nsnull;
00157       char *token = nsCRT::strtok(headersString,":", &newStr);
00158       PRUint32 i=0;
00159       while (token)
00160       {
00161         if (nsCRT::strcasecmp(token, string) == 0)
00162         {
00163           *attrib += i; //we found custom header in the pref
00164           found = PR_TRUE;
00165           break;
00166         }
00167         token = nsCRT::strtok(newStr,":", &newStr);
00168         i++;
00169       }
00170     }
00171   }
00172   // If we didn't find the header in MAILNEWS_CUSTOM_HEADERS, we're 
00173   // going to return NS_OK and an attrib of nsMsgSearchAttrib::OtherHeader+1.
00174   // in case it's a client side spam filter description filter, 
00175   // which doesn't add its headers to mailnews.customMailHeaders.
00176   // We've already checked that it's a valid header and returned
00177   // an error if so.
00178 
00179   return NS_OK;
00180 }
00181 
00182 nsresult NS_MsgGetStringForAttribute(PRInt16 attrib, const char **string)
00183 {
00184   NS_ENSURE_ARG_POINTER(string);
00185 
00186   PRBool found = PR_FALSE;
00187        for (int idxAttrib = 0; idxAttrib < (int)(sizeof(SearchAttribEntryTable) / sizeof(nsMsgSearchAttribEntry)); idxAttrib++)
00188        {
00189               // I'm using the idx's as aliases into MSG_SearchAttribute and 
00190               // MSG_SearchOperator enums which is legal because of the way the
00191               // enums are defined (starts at 0, numItems at end)
00192               if (attrib == SearchAttribEntryTable[idxAttrib].attrib)
00193               {
00194                      found = PR_TRUE;
00195                      *string = SearchAttribEntryTable[idxAttrib].attribName;
00196                      break;
00197               }
00198        }
00199        // we no longer return invalid attribute. If we cannot find the string in the table, 
00200        // then it is an arbitrary header. Return success regardless if found or not
00201        return NS_OK;
00202 }
00203 
00204 typedef struct
00205 {
00206        nsMsgSearchOpValue  op;
00207        const char                  *opName;
00208 } nsMsgSearchOperatorEntry;
00209 
00210 nsMsgSearchOperatorEntry SearchOperatorEntryTable[] =
00211 {
00212   {nsMsgSearchOp::Contains, "contains"},
00213   {nsMsgSearchOp::DoesntContain,"doesn't contain"},
00214   {nsMsgSearchOp::Is,"is"},
00215   {nsMsgSearchOp::Isnt,     "isn't"},
00216   {nsMsgSearchOp::IsEmpty, "is empty"},
00217   {nsMsgSearchOp::IsBefore, "is before"},
00218   {nsMsgSearchOp::IsAfter, "is after"},
00219   {nsMsgSearchOp::IsHigherThan, "is higher than"},
00220   {nsMsgSearchOp::IsLowerThan, "is lower than"},
00221   {nsMsgSearchOp::BeginsWith, "begins with"},
00222   {nsMsgSearchOp::EndsWith, "ends with"},
00223   {nsMsgSearchOp::IsInAB, "is in ab"},
00224   {nsMsgSearchOp::IsntInAB, "isn't in ab"},
00225   {nsMsgSearchOp::IsGreaterThan, "is greater than"},
00226   {nsMsgSearchOp::IsLessThan, "is less than"}
00227 };
00228 
00229 nsresult NS_MsgGetOperatorFromString(const char *string, PRInt16 *op)
00230 {
00231   NS_ENSURE_ARG_POINTER(string);
00232   NS_ENSURE_ARG_POINTER(op);
00233        
00234        PRBool found = PR_FALSE;
00235        for (unsigned int idxOp = 0; idxOp < sizeof(SearchOperatorEntryTable) / sizeof(nsMsgSearchOperatorEntry); idxOp++)
00236        {
00237               // I'm using the idx's as aliases into MSG_SearchAttribute and 
00238               // MSG_SearchOperator enums which is legal because of the way the
00239               // enums are defined (starts at 0, numItems at end)
00240               if (!PL_strcasecmp(string, SearchOperatorEntryTable[idxOp].opName))
00241               {
00242                      found = PR_TRUE;
00243                      *op = SearchOperatorEntryTable[idxOp].op;
00244                      break;
00245               }
00246        }
00247        return (found) ? NS_OK : NS_ERROR_INVALID_ARG;
00248 }
00249 
00250 nsresult NS_MsgGetStringForOperator(PRInt16 op, const char **string)
00251 {
00252   NS_ENSURE_ARG_POINTER(string);
00253   
00254   PRBool found = PR_FALSE;
00255        for (unsigned int idxOp = 0; idxOp < sizeof(SearchOperatorEntryTable) / sizeof(nsMsgSearchOperatorEntry); idxOp++)
00256        {
00257               // I'm using the idx's as aliases into MSG_SearchAttribute and 
00258               // MSG_SearchOperator enums which is legal because of the way the
00259               // enums are defined (starts at 0, numItems at end)
00260               if (op == SearchOperatorEntryTable[idxOp].op)
00261               {
00262                      found = PR_TRUE;
00263                      *string = SearchOperatorEntryTable[idxOp].opName;
00264                      break;
00265               }
00266        }
00267 
00268        return (found) ? NS_OK : NS_ERROR_INVALID_ARG;
00269 }
00270 
00271 void NS_MsgGetUntranslatedStatusName (uint32 s, nsCString *outName)
00272 {
00273        const char *tmpOutName = NULL;
00274 #define MSG_STATUS_MASK (MSG_FLAG_READ | MSG_FLAG_REPLIED | MSG_FLAG_FORWARDED | MSG_FLAG_NEW | MSG_FLAG_MARKED)
00275        PRUint32 maskOut = (s & MSG_STATUS_MASK);
00276 
00277        // diddle the flags to pay attention to the most important ones first, if multiple
00278        // flags are set. Should remove this code from the winfe.
00279        if (maskOut & MSG_FLAG_NEW) 
00280               maskOut = MSG_FLAG_NEW;
00281        if ( maskOut & MSG_FLAG_REPLIED &&
00282                maskOut & MSG_FLAG_FORWARDED ) 
00283               maskOut = MSG_FLAG_REPLIED|MSG_FLAG_FORWARDED;
00284        else if ( maskOut & MSG_FLAG_FORWARDED )
00285               maskOut = MSG_FLAG_FORWARDED;
00286        else if ( maskOut & MSG_FLAG_REPLIED ) 
00287               maskOut = MSG_FLAG_REPLIED;
00288 
00289        switch (maskOut)
00290        {
00291        case MSG_FLAG_READ:
00292               tmpOutName = "read";
00293               break;
00294        case MSG_FLAG_REPLIED:
00295               tmpOutName = "replied";
00296               break;
00297        case MSG_FLAG_FORWARDED:
00298               tmpOutName = "forwarded";
00299               break;
00300        case MSG_FLAG_FORWARDED|MSG_FLAG_REPLIED:
00301               tmpOutName = "replied and forwarded";
00302               break;
00303        case MSG_FLAG_NEW:
00304               tmpOutName = "new";
00305               break;
00306         case MSG_FLAG_MARKED:
00307                 tmpOutName = "flagged";
00308                 break;
00309        default:
00310               // This is fine, status may be "unread" for example
00311         break;
00312        }
00313 
00314        if (tmpOutName)
00315               *outName = tmpOutName;
00316 }
00317 
00318 
00319 PRInt32 NS_MsgGetStatusValueFromName(char *name)
00320 {
00321   if (!strcmp("read", name))
00322     return MSG_FLAG_READ;
00323   if (!strcmp("replied", name))
00324     return MSG_FLAG_REPLIED;
00325   if (!strcmp("forwarded", name))
00326     return MSG_FLAG_FORWARDED;
00327   if (!strcmp("replied and forwarded", name))
00328     return MSG_FLAG_FORWARDED|MSG_FLAG_REPLIED;
00329   if (!strcmp("new", name))
00330     return MSG_FLAG_NEW;
00331   if (!strcmp("flagged", name))
00332     return MSG_FLAG_MARKED;
00333   return 0;
00334 }
00335 
00336 
00337 // Needed for DeStream method.
00338 nsMsgSearchTerm::nsMsgSearchTerm()
00339 {
00340     // initialize this to zero
00341     m_value.string=nsnull;
00342     m_value.attribute=0;
00343     m_value.u.priority=0;
00344     m_attribute = nsMsgSearchAttrib::Default;
00345     mBeginsGrouping = PR_FALSE;
00346     mEndsGrouping = PR_FALSE;
00347     m_matchAll = PR_FALSE;
00348 }
00349 
00350 nsMsgSearchTerm::nsMsgSearchTerm (
00351                                   nsMsgSearchAttribValue attrib, 
00352                                   nsMsgSearchOpValue op, 
00353                                   nsIMsgSearchValue *val,
00354                                   nsMsgSearchBooleanOperator boolOp,
00355                                   const char * arbitraryHeader) 
00356 {
00357   m_operator = op;
00358   m_attribute = attrib;
00359   m_booleanOp = boolOp;
00360   if (attrib > nsMsgSearchAttrib::OtherHeader  && attrib < nsMsgSearchAttrib::kNumMsgSearchAttributes && arbitraryHeader)
00361     m_arbitraryHeader = arbitraryHeader;
00362   nsMsgResultElement::AssignValues (val, &m_value);
00363   m_matchAll = PR_FALSE;
00364 }
00365 
00366 
00367 
00368 nsMsgSearchTerm::~nsMsgSearchTerm ()
00369 {
00370   if (IS_STRING_ATTRIBUTE (m_attribute) && m_value.string)
00371     Recycle(m_value.string);
00372 }
00373 
00374 NS_IMPL_ISUPPORTS1(nsMsgSearchTerm, nsIMsgSearchTerm)
00375 
00376 
00377 // Perhaps we could find a better place for this?
00378 // Caller needs to free.
00379 /* static */char *nsMsgSearchTerm::EscapeQuotesInStr(const char *str)
00380 {
00381   int  numQuotes = 0;
00382   for (const char *strPtr = str; *strPtr; strPtr++)
00383     if (*strPtr == '"')
00384       numQuotes++;
00385     int escapedStrLen = PL_strlen(str) + numQuotes;
00386     char      *escapedStr = (char *) PR_Malloc(escapedStrLen + 1);
00387     if (escapedStr)
00388     {
00389       char *destPtr;
00390       for (destPtr = escapedStr; *str; str++)
00391       {
00392         if (*str == '"')
00393           *destPtr++ = '\\';
00394         *destPtr++ = *str;
00395       }
00396       *destPtr = '\0';
00397     }
00398     return escapedStr;
00399 }
00400 
00401 
00402 nsresult nsMsgSearchTerm::OutputValue(nsCString &outputStr)
00403 {
00404   if (IS_STRING_ATTRIBUTE(m_attribute) && m_value.string)
00405   {
00406     PRBool    quoteVal = PR_FALSE;
00407     // need to quote strings with ')' and strings starting with '"' or ' '
00408     // filter code will escape quotes
00409     if (PL_strchr(m_value.string, ')') ||
00410       (m_value.string[0] == ' ') ||
00411       (m_value.string[0] == '"'))
00412     {
00413       quoteVal = PR_TRUE;
00414       outputStr += "\"";
00415     }
00416     if (PL_strchr(m_value.string, '"'))
00417     {
00418       char *escapedString = nsMsgSearchTerm::EscapeQuotesInStr(m_value.string);
00419       if (escapedString)
00420       {
00421         outputStr += escapedString;
00422         PR_Free(escapedString);
00423       }
00424       
00425     }
00426     else
00427     {
00428       outputStr += m_value.string;
00429     }
00430     if (quoteVal)
00431       outputStr += "\"";
00432   }
00433   else
00434   {
00435     switch (m_attribute)
00436     {
00437     case nsMsgSearchAttrib::Date:
00438       {
00439         PRExplodedTime exploded;
00440         PR_ExplodeTime(m_value.u.date, PR_LocalTimeParameters, &exploded);
00441         
00442         // wow, so tm_mon is 0 based, tm_mday is 1 based.
00443         char dateBuf[100];
00444         PR_FormatTimeUSEnglish (dateBuf, sizeof(dateBuf), "%d-%b-%Y", &exploded);
00445         outputStr += dateBuf;
00446         break;
00447       }
00448     case nsMsgSearchAttrib::AgeInDays:
00449       {
00450         outputStr.AppendInt(m_value.u.age);
00451         break;
00452       }
00453     case nsMsgSearchAttrib::Label:
00454       {
00455         outputStr.AppendInt(m_value.u.label);
00456         break;
00457       }
00458     case nsMsgSearchAttrib::JunkStatus:
00459       {
00460         outputStr.AppendInt(m_value.u.junkStatus); // only if we write to disk, right?
00461         break;
00462       }
00463     case nsMsgSearchAttrib::MsgStatus:
00464       {
00465         nsCAutoString status;
00466         NS_MsgGetUntranslatedStatusName (m_value.u.msgStatus, &status);
00467         outputStr += status;
00468         break;
00469       }
00470     case nsMsgSearchAttrib::Priority:
00471       {
00472         nsCAutoString priority;
00473         NS_MsgGetUntranslatedPriorityName(m_value.u.priority, priority);
00474         outputStr += priority;
00475         break;
00476       }
00477     case nsMsgSearchAttrib::HasAttachmentStatus:
00478       {
00479         outputStr.Append("true");  // don't need anything here, really
00480         break;
00481       }
00482     case nsMsgSearchAttrib::Size:
00483       {
00484         outputStr.AppendInt(m_value.u.size);
00485         break;
00486       }
00487     default:
00488       NS_ASSERTION(PR_FALSE, "trying to output invalid attribute");
00489       break;
00490     }
00491   }
00492   return NS_OK;
00493 }
00494 
00495 NS_IMETHODIMP nsMsgSearchTerm::GetTermAsString (nsACString &outStream)
00496 {
00497   const char  *attrib, *operatorStr;
00498   nsCAutoString      outputStr;
00499   nsresult    ret;
00500   
00501   if (m_matchAll)
00502   {
00503     outStream = "ALL";
00504     return NS_OK;
00505   }
00506   ret = NS_MsgGetStringForAttribute(m_attribute, &attrib);
00507   if (ret != NS_OK)
00508     return ret;
00509   
00510   if (m_attribute > nsMsgSearchAttrib::OtherHeader && m_attribute < nsMsgSearchAttrib::kNumMsgSearchAttributes)  // if arbitrary header, use it instead!
00511   {
00512     outputStr = "\"";
00513     outputStr += m_arbitraryHeader;
00514     outputStr += "\"";
00515   }
00516   else
00517     outputStr = attrib;
00518   
00519   outputStr += ',';
00520   
00521   ret = NS_MsgGetStringForOperator(m_operator, &operatorStr);
00522   if (ret != NS_OK)
00523     return ret;
00524   
00525   outputStr += operatorStr;
00526   outputStr += ',';
00527   
00528   OutputValue(outputStr);
00529   outStream = outputStr;
00530   return NS_OK;
00531 }
00532 
00533 // fill in m_value from the input stream.
00534 nsresult nsMsgSearchTerm::ParseValue(char *inStream)
00535 {
00536   if (IS_STRING_ATTRIBUTE(m_attribute))
00537   {
00538     PRBool    quoteVal = PR_FALSE;
00539     while (nsCRT::IsAsciiSpace(*inStream))
00540       inStream++;
00541     // need to remove pair of '"', if present
00542     if (*inStream == '"')
00543     {
00544       quoteVal = PR_TRUE;
00545       inStream++;
00546     }
00547     int valueLen = PL_strlen(inStream);
00548     if (quoteVal && inStream[valueLen - 1] == '"')
00549       valueLen--;
00550     
00551     m_value.string = (char *) PR_Malloc(valueLen + 1);
00552     PL_strncpy(m_value.string, inStream, valueLen + 1);
00553     m_value.string[valueLen] = '\0';
00554   }
00555   else
00556   {
00557     switch (m_attribute)
00558     {
00559     case nsMsgSearchAttrib::Date:
00560       PR_ParseTimeString (inStream, PR_FALSE, &m_value.u.date);
00561       break;
00562     case nsMsgSearchAttrib::MsgStatus:
00563       m_value.u.msgStatus = NS_MsgGetStatusValueFromName(inStream);
00564       break;
00565     case nsMsgSearchAttrib::Priority:
00566       NS_MsgGetPriorityFromString(inStream, m_value.u.priority);
00567       break;
00568     case nsMsgSearchAttrib::AgeInDays:
00569       m_value.u.age = atoi(inStream);
00570       break;
00571     case nsMsgSearchAttrib::Label:
00572       m_value.u.label = atoi(inStream);
00573       break;
00574     case nsMsgSearchAttrib::JunkStatus:
00575       m_value.u.junkStatus = atoi(inStream); // only if we read from disk, right?
00576       break;
00577     case nsMsgSearchAttrib::HasAttachmentStatus:
00578       m_value.u.msgStatus = MSG_FLAG_ATTACHMENT;
00579       break; // this should always be true.
00580     case nsMsgSearchAttrib::Size:
00581       m_value.u.size = atoi(inStream);
00582       break;
00583     default:
00584       NS_ASSERTION(PR_FALSE, "invalid attribute parsing search term value");
00585       break;
00586     }
00587   }
00588   m_value.attribute = m_attribute;
00589   return NS_OK;
00590 }
00591 
00592 // find the operator code for this operator string.
00593 nsresult
00594 nsMsgSearchTerm::ParseOperator(char *inStream, nsMsgSearchOpValue *value)
00595 {
00596   NS_ENSURE_ARG_POINTER(value);
00597   PRInt16                          operatorVal;
00598   while (nsCRT::IsAsciiSpace(*inStream))
00599     inStream++;
00600   
00601   char *commaSep = PL_strchr(inStream, ',');
00602   
00603   if (commaSep)
00604     *commaSep = '\0';
00605   
00606   nsresult err = NS_MsgGetOperatorFromString(inStream, &operatorVal);
00607   *value = (nsMsgSearchOpValue) operatorVal;
00608   return err;
00609 }
00610 
00611 // find the attribute code for this comma-delimited attribute. 
00612 nsresult
00613 nsMsgSearchTerm::ParseAttribute(char *inStream, nsMsgSearchAttribValue *attrib)
00614 {   
00615     while (nsCRT::IsAsciiSpace(*inStream))
00616         inStream++;
00617     
00618     // if we are dealing with an arbitrary header, it may be quoted....
00619     PRBool quoteVal = PR_FALSE;
00620     if (*inStream == '"')
00621     {
00622         quoteVal = PR_TRUE;
00623         inStream++;
00624     }
00625     
00626     // arbitrary headers are quoted
00627     char *separator = strchr(inStream, quoteVal ? '"' : ',');
00628 
00629     if (separator)
00630         *separator = '\0';
00631     
00632     PRInt16 attributeVal;
00633     nsresult rv = NS_MsgGetAttributeFromString(inStream, &attributeVal);
00634     NS_ENSURE_SUCCESS(rv, rv);
00635     
00636     *attrib = (nsMsgSearchAttribValue) attributeVal;
00637     
00638     if (*attrib > nsMsgSearchAttrib::OtherHeader && *attrib < nsMsgSearchAttrib::kNumMsgSearchAttributes)  // if we are dealing with an arbitrary header....
00639         m_arbitraryHeader = inStream;
00640     
00641     return rv;
00642 }
00643 
00644 // De stream one search term. If the condition looks like
00645 // condition = "(to or cc, contains, r-thompson) AND (body, doesn't contain, fred)"
00646 // This routine should get called twice, the first time
00647 // with "to or cc, contains, r-thompson", the second time with
00648 // "body, doesn't contain, fred"
00649 
00650 nsresult nsMsgSearchTerm::DeStreamNew (char *inStream, PRInt16 /*length*/)
00651 {
00652   if (!strcmp(inStream, "ALL"))
00653   {
00654     m_matchAll = PR_TRUE;
00655     return NS_OK;
00656   }
00657   char *commaSep = PL_strchr(inStream, ',');
00658   nsresult rv = ParseAttribute(inStream, &m_attribute);  // will allocate space for arbitrary header if necessary
00659   NS_ENSURE_SUCCESS(rv, rv);
00660   if (!commaSep)
00661     return NS_ERROR_INVALID_ARG;
00662   char *secondCommaSep = PL_strchr(commaSep + 1, ',');
00663   if (commaSep)
00664     rv = ParseOperator(commaSep + 1, &m_operator);
00665   NS_ENSURE_SUCCESS(rv, rv);
00666   // convert label filters and saved searches to keyword equivalents
00667   if (secondCommaSep)
00668     ParseValue(secondCommaSep + 1);
00669   if (m_attribute == nsMsgSearchAttrib::Label)
00670   {
00671     nsCAutoString keyword("$label");
00672     m_value.attribute = m_attribute = nsMsgSearchAttrib::Keywords;
00673     keyword.Append('0' + m_value.u.label);
00674     m_value.string = PL_strdup(keyword.get());
00675   }
00676   return NS_OK;
00677 }
00678 
00679 
00680 // Looks in the MessageDB for the user specified arbitrary header, if it finds the header, it then looks for a match against
00681 // the value for the header. 
00682 nsresult nsMsgSearchTerm::MatchArbitraryHeader (nsIMsgSearchScopeTerm *scope,
00683                                                 PRUint32 offset,
00684                                                 PRUint32 length /* in lines*/,
00685                                                 const char *charset,
00686                                                 PRBool charsetOverride,
00687                                                 nsIMsgDBHdr *msg,
00688                                                 nsIMsgDatabase* db,
00689                                                 const char * headers, 
00690                                                 PRUint32 headersSize,
00691                                                 PRBool ForFiltering,
00692                                                 PRBool *pResult)
00693 {
00694   NS_ENSURE_ARG_POINTER(pResult);
00695   *pResult = PR_FALSE;
00696   nsresult err = NS_OK;
00697   PRBool result;
00698   
00699   nsMsgBodyHandler * bodyHandler = new nsMsgBodyHandler (scope, offset,length, msg, db, headers, headersSize, ForFiltering);
00700   if (!bodyHandler)
00701     return NS_ERROR_OUT_OF_MEMORY;
00702   
00703   bodyHandler->SetStripHeaders (PR_FALSE);
00704   
00705   GetMatchAllBeforeDeciding(&result);
00706   
00707   nsCAutoString buf;
00708   nsCAutoString curMsgHeader;
00709   PRBool searchingHeaders = PR_TRUE;
00710   while (searchingHeaders && (bodyHandler->GetNextLine(buf) >=0))
00711   {
00712     char * buf_end = (char *) (buf.get() + buf.Length());
00713     int headerLength = m_arbitraryHeader.Length();
00714     PRBool isContinuationHeader = nsCRT::IsAsciiSpace(buf.CharAt(0));
00715     // this handles wrapped header lines, which start with whitespace. 
00716     // If the line starts with whitespace, then we use the current header.
00717     if (!isContinuationHeader)
00718     {
00719       PRUint32 colonPos = buf.FindChar(':');
00720       buf.Left(curMsgHeader, colonPos);
00721     }
00722 
00723     if (curMsgHeader.Equals(m_arbitraryHeader, nsCaseInsensitiveCStringComparator()))
00724     {
00725       // value occurs after the header name or whitespace continuation char.
00726       const char * headerValue = buf.get() + (isContinuationHeader ? 1 : headerLength); 
00727       if (headerValue < buf_end && headerValue[0] == ':')  // + 1 to account for the colon which is MANDATORY
00728         headerValue++; 
00729       
00730       // strip leading white space
00731       while (headerValue < buf_end && nsCRT::IsAsciiSpace(*headerValue))
00732         headerValue++; // advance to next character
00733       
00734       // strip trailing white space
00735       char * end = buf_end - 1; 
00736       while (end > headerValue && nsCRT::IsAsciiSpace(*end)) // while we haven't gone back past the start and we are white space....
00737       {
00738         *end = '\0'; // eat up the white space
00739         end--;                     // move back and examine the previous character....
00740       }
00741       
00742       if (headerValue < buf_end && *headerValue) // make sure buf has info besides just the header
00743       {
00744         PRBool result2;
00745         err = MatchRfc2047String(headerValue, charset, charsetOverride, &result2);  // match value with the other info...
00746         if (result != result2) // if we found a match
00747         {
00748           searchingHeaders = PR_FALSE;   // then stop examining the headers
00749           result = result2;
00750         }
00751       }
00752       else
00753         NS_ASSERTION(PR_FALSE, "error matching arbitrary headers"); // mscott --> i'd be curious if there is a case where this fails....
00754     }
00755     if (EMPTY_MESSAGE_LINE(buf))
00756       searchingHeaders = PR_FALSE;
00757   }
00758   delete bodyHandler;
00759   *pResult = result;
00760   return err;
00761 }
00762 
00763 nsresult nsMsgSearchTerm::MatchBody (nsIMsgSearchScopeTerm *scope, PRUint32 offset, PRUint32 length /*in lines*/, const char *folderCharset,
00764                                       nsIMsgDBHdr *msg, nsIMsgDatabase* db, PRBool *pResult)
00765 {
00766   NS_ENSURE_ARG_POINTER(pResult);
00767   nsresult err = NS_OK;
00768   
00769   PRBool result = PR_FALSE;
00770   *pResult = PR_FALSE;
00771   
00772   // Small hack so we don't look all through a message when someone has
00773   // specified "BODY IS foo". ### Since length is in lines, this is not quite right.
00774   if ((length > 0) && (m_operator == nsMsgSearchOp::Is || m_operator == nsMsgSearchOp::Isnt))
00775     length = PL_strlen (m_value.string);
00776   
00777   nsMsgBodyHandler * bodyHan  = new nsMsgBodyHandler (scope, offset, length, msg, db);
00778   if (!bodyHan)
00779     return NS_ERROR_OUT_OF_MEMORY;
00780   
00781   nsCAutoString buf;
00782   PRBool endOfFile = PR_FALSE;  // if retValue == 0, we've hit the end of the file
00783   uint32 lines = 0;
00784   
00785   // Change the sense of the loop so we don't bail out prematurely
00786   // on negative terms. i.e. opDoesntContain must look at all lines
00787   PRBool boolContinueLoop;
00788   GetMatchAllBeforeDeciding(&boolContinueLoop);
00789   result = boolContinueLoop;
00790   
00791   // If there's a '=' in the search term, then we're not going to do
00792   // quoted printable decoding. Otherwise we assume everything is
00793   // quoted printable. Obviously everything isn't quoted printable, but
00794   // since we don't have a MIME parser handy, and we want to err on the
00795   // side of too many hits rather than not enough, we'll assume in that
00796   // general direction. Blech. ### FIX ME 
00797   // bug fix #314637: for stateful charsets like ISO-2022-JP, we don't
00798   // want to decode quoted printable since it contains '='.
00799   PRBool isQuotedPrintable = !nsMsgI18Nstateful_charset(folderCharset) &&
00800     (PL_strchr (m_value.string, '=') == nsnull);
00801   
00802   nsCString compare;
00803   while (!endOfFile && result == boolContinueLoop)
00804   {
00805     if (bodyHan->GetNextLine(buf) >= 0)
00806     {
00807       PRBool softLineBreak = PR_FALSE;
00808       // Do in-place decoding of quoted printable
00809       if (isQuotedPrintable)
00810       {
00811         softLineBreak = StringEndsWith(buf, NS_LITERAL_CSTRING("="));
00812         MsgStripQuotedPrintable ((unsigned char*)buf.get());
00813         // in case the string shrunk, reset the length. If soft line break,
00814         // chop off the last char as well.
00815         buf.SetLength(strlen(buf.get()) - (softLineBreak ? 1 : 0));
00816       }
00817       compare.Append(buf);
00818       // If this line ends with a soft line break, loop around
00819       // and get the next line before looking for the search string.
00820       // This assumes the message can't end on a QP soft-line break.
00821       // That seems like a pretty safe assumption.
00822       if (softLineBreak) 
00823         continue;
00824       if (!compare.IsEmpty())
00825       {
00826         char startChar = (char) compare.CharAt(0);
00827         if (startChar != nsCRT::CR && startChar != nsCRT::LF)
00828         {
00829           err = MatchString (compare.get(), folderCharset, &result);
00830           lines++; 
00831         }
00832         compare.Truncate();
00833       }
00834     }
00835     else 
00836       endOfFile = PR_TRUE;
00837   }
00838 #ifdef DO_I18N
00839   if(conv) 
00840     INTL_DestroyCharCodeConverter(conv);
00841 #endif
00842   delete bodyHan;
00843   *pResult = result;
00844   return err;
00845 }
00846 
00847 nsresult nsMsgSearchTerm::InitializeAddressBook()
00848 {
00849   // the search attribute value has the URI for the address book we need to load. 
00850   // we need both the database and the directory.
00851   nsresult rv = NS_OK;
00852 
00853   if (mDirectory)
00854   {
00855     nsXPIDLCString dirURI;
00856     mDirectory->GetDirUri(getter_Copies(dirURI));
00857     if (strcmp(dirURI.get(), m_value.string))
00858       mDirectory = nsnull; // clear out the directory....we are no longer pointing to the right one
00859   }
00860   if (!mDirectory)
00861   {  
00862     nsCOMPtr <nsIRDFService> rdfService = do_GetService("@mozilla.org/rdf/rdf-service;1",&rv);
00863     NS_ENSURE_SUCCESS(rv, rv);
00864 
00865     nsCOMPtr <nsIRDFResource> resource;
00866     rv = rdfService->GetResource(nsDependentCString(m_value.string), getter_AddRefs(resource));
00867     NS_ENSURE_SUCCESS(rv, rv);
00868 
00869     mDirectory = do_QueryInterface(resource, &rv);
00870     NS_ENSURE_SUCCESS(rv, rv);
00871   }
00872 
00873   return NS_OK;
00874 }
00875 
00876 nsresult nsMsgSearchTerm::MatchInAddressBook(const char * aAddress, PRBool *pResult)
00877 {
00878   nsresult rv = InitializeAddressBook(); 
00879   *pResult = PR_FALSE;
00880 
00881   // Some junkmails have empty From: fields.
00882   if (aAddress == NULL || strlen(aAddress) == 0)
00883     return rv;
00884 
00885   if (mDirectory)
00886   {
00887     PRBool cardExists = PR_FALSE;
00888     rv = mDirectory->HasCardForEmailAddress(aAddress, &cardExists);
00889     if ( (m_operator == nsMsgSearchOp::IsInAB && cardExists) || (m_operator == nsMsgSearchOp::IsntInAB && !cardExists))
00890       *pResult = PR_TRUE;
00891   }
00892   
00893   return rv;
00894 }
00895 
00896 // *pResult is PR_FALSE when strings don't match, PR_TRUE if they do.
00897 nsresult nsMsgSearchTerm::MatchRfc2047String (const char *rfc2047string,
00898                                        const char *charset,
00899                                        PRBool charsetOverride,
00900                                        PRBool *pResult)
00901 {
00902   NS_ENSURE_ARG_POINTER(pResult);
00903   NS_ENSURE_ARG_POINTER(rfc2047string);
00904 
00905     nsCOMPtr<nsIMimeConverter> mimeConverter = do_GetService(NS_MIME_CONVERTER_CONTRACTID);
00906        char *stringToMatch = 0;
00907     nsresult res = mimeConverter->DecodeMimeHeader(rfc2047string,
00908                                                    &stringToMatch,
00909                                                    charset, charsetOverride,
00910                                                    PR_FALSE);
00911 
00912     if (m_attribute == nsMsgSearchAttrib::Sender && 
00913         (m_operator == nsMsgSearchOp::IsInAB ||
00914          m_operator == nsMsgSearchOp::IsntInAB))
00915     {
00916       res = MatchInAddressBook(stringToMatch ? stringToMatch : rfc2047string, pResult);
00917     }
00918     else
00919          res = MatchString(stringToMatch ? stringToMatch : rfc2047string,
00920                       nsnull, pResult);
00921 
00922     PR_Free(stringToMatch);
00923 
00924        return res;
00925 }
00926 
00927 // *pResult is PR_FALSE when strings don't match, PR_TRUE if they do.
00928 nsresult nsMsgSearchTerm::MatchString (const char *stringToMatch,
00929                                        const char *charset,
00930                                        PRBool *pResult)
00931 {
00932   NS_ENSURE_ARG_POINTER(pResult);
00933   PRBool result = PR_FALSE;
00934   
00935   nsresult err = NS_OK;
00936   nsAutoString utf16StrToMatch;
00937   nsAutoString needle; 
00938 
00939   // Save some performance for opIsEmpty
00940   if(nsMsgSearchOp::IsEmpty != m_operator)  
00941   {
00942     NS_ASSERTION(IsUTF8(nsDependentCString(m_value.string)),
00943                         "m_value.string is not UTF-8");
00944     CopyUTF8toUTF16(m_value.string, needle);
00945 
00946     if (charset != nsnull)
00947     {
00948       ConvertToUnicode(charset, stringToMatch ? stringToMatch : "",
00949                        utf16StrToMatch);
00950     }
00951     else { 
00952       NS_ASSERTION(IsUTF8(nsDependentCString(stringToMatch)),
00953                    "stringToMatch is not UTF-8");
00954       CopyUTF8toUTF16(stringToMatch, utf16StrToMatch);
00955     }
00956   }
00957   
00958   switch (m_operator)
00959   {
00960   case nsMsgSearchOp::Contains:
00961     if (CaseInsensitiveFindInReadable(needle, utf16StrToMatch))
00962       result = PR_TRUE;
00963     break;
00964   case nsMsgSearchOp::DoesntContain:
00965     if(!CaseInsensitiveFindInReadable(needle, utf16StrToMatch))
00966       result = PR_TRUE;
00967     break;
00968   case nsMsgSearchOp::Is:
00969     if(needle.Equals(utf16StrToMatch, nsCaseInsensitiveStringComparator()))
00970       result = PR_TRUE;
00971     break;
00972   case nsMsgSearchOp::Isnt:
00973     if(!needle.Equals(utf16StrToMatch, nsCaseInsensitiveStringComparator()))
00974       result = PR_TRUE;
00975     break;
00976   case nsMsgSearchOp::IsEmpty:
00977     // For IsEmpty, we didn't copy stringToMatch to utf16StrToMatch.
00978     if (!PL_strlen(stringToMatch))
00979       result = PR_TRUE;
00980     break;
00981   case nsMsgSearchOp::BeginsWith:
00982     if (StringBeginsWith(utf16StrToMatch, needle,
00983                          nsCaseInsensitiveStringComparator()))
00984       result = PR_TRUE;
00985     break;
00986   case nsMsgSearchOp::EndsWith: 
00987     if (StringEndsWith(utf16StrToMatch, needle,
00988                        nsCaseInsensitiveStringComparator()))
00989       result = PR_TRUE;
00990     break;
00991   default:
00992     NS_ASSERTION(PR_FALSE, "invalid operator matching search results");
00993   }
00994   
00995   *pResult = result;
00996   return err;
00997 }
00998 
00999 NS_IMETHODIMP nsMsgSearchTerm::GetMatchAllBeforeDeciding (PRBool *aResult)
01000 {
01001  *aResult = (m_operator == nsMsgSearchOp::DoesntContain || m_operator == nsMsgSearchOp::Isnt);
01002  return NS_OK;
01003 }
01004                                        
01005  nsresult nsMsgSearchTerm::MatchRfc822String (const char *string, const char *charset, PRBool charsetOverride, PRBool *pResult)
01006  {
01007    NS_ENSURE_ARG_POINTER(pResult);
01008    *pResult = PR_FALSE;
01009    PRBool result;
01010    nsresult err = InitHeaderAddressParser();
01011    if (NS_FAILED(err))
01012      return err;
01013    // Isolate the RFC 822 parsing weirdnesses here. MSG_ParseRFC822Addresses
01014    // returns a catenated string of null-terminated strings, which we walk
01015    // across, tring to match the target string to either the name OR the address
01016    
01017    char *names = nsnull, *addresses = nsnull;
01018    
01019    // Change the sense of the loop so we don't bail out prematurely
01020    // on negative terms. i.e. opDoesntContain must look at all recipients
01021    PRBool boolContinueLoop;
01022    GetMatchAllBeforeDeciding(&boolContinueLoop);
01023    result = boolContinueLoop;
01024    
01025    PRUint32 count;
01026    nsresult parseErr = m_headerAddressParser->ParseHeaderAddresses(charset, string, &names, &addresses, &count) ;
01027    
01028    if (NS_SUCCEEDED(parseErr) && count > 0)
01029    {
01030      NS_ASSERTION(names, "couldn't get names");
01031      NS_ASSERTION(addresses, "couldn't get addresses");
01032      if (!names || !addresses)
01033        return err;
01034      
01035      nsCAutoString walkNames;
01036      nsCAutoString walkAddresses;
01037      PRInt32 namePos = 0;
01038      PRInt32 addressPos = 0;
01039      for (PRUint32 i = 0; i < count && result == boolContinueLoop; i++)
01040      {
01041        walkNames = names + namePos;
01042        walkAddresses = addresses + addressPos;
01043        if (m_attribute == nsMsgSearchAttrib::Sender && 
01044          (m_operator == nsMsgSearchOp::IsInAB ||
01045          m_operator == nsMsgSearchOp::IsntInAB))
01046        {
01047          err = MatchRfc2047String (walkAddresses.get(), charset, charsetOverride, &result);
01048        }
01049        else
01050        {
01051          err = MatchRfc2047String (walkNames.get(), charset, charsetOverride, &result);
01052          if (boolContinueLoop == result)
01053            err = MatchRfc2047String (walkAddresses.get(), charset, charsetOverride, &result);
01054        }
01055        
01056        namePos += walkNames.Length() + 1;
01057        addressPos += walkAddresses.Length() + 1;
01058      }
01059      
01060      PR_Free(names);
01061      PR_Free(addresses);
01062    }
01063    *pResult = result;
01064    return err;
01065  }
01066 
01067 
01068 nsresult nsMsgSearchTerm::GetLocalTimes (PRTime a, PRTime b, PRExplodedTime &aExploded, PRExplodedTime &bExploded)
01069 {
01070   PR_ExplodeTime(a, PR_LocalTimeParameters, &aExploded);
01071   PR_ExplodeTime(b, PR_LocalTimeParameters, &bExploded);
01072   return NS_OK;
01073 }
01074 
01075 
01076 nsresult nsMsgSearchTerm::MatchDate (PRTime dateToMatch, PRBool *pResult)
01077 {
01078   NS_ENSURE_ARG_POINTER(pResult);
01079   
01080   nsresult err = NS_OK;
01081   PRBool result = PR_FALSE;
01082   nsTime t_date(dateToMatch);
01083   
01084   switch (m_operator)
01085   {
01086   case nsMsgSearchOp::IsBefore:
01087     if (t_date < nsTime(m_value.u.date))
01088       result = PR_TRUE;
01089     break;
01090   case nsMsgSearchOp::IsAfter:
01091     {
01092       nsTime adjustedDate = nsTime(m_value.u.date);
01093       adjustedDate += 60*60*24; // we want to be greater than the next day....
01094       if (t_date > adjustedDate)
01095         result = PR_TRUE;
01096     }
01097     break;
01098   case nsMsgSearchOp::Is:
01099     {
01100       PRExplodedTime tmToMatch, tmThis;
01101       if (NS_OK == GetLocalTimes (dateToMatch, m_value.u.date, tmToMatch, tmThis))
01102       {
01103         if (tmThis.tm_year == tmToMatch.tm_year &&
01104           tmThis.tm_month == tmToMatch.tm_month &&
01105           tmThis.tm_mday == tmToMatch.tm_mday)
01106           result = PR_TRUE;
01107       }
01108     }
01109     break;
01110   case nsMsgSearchOp::Isnt:
01111     {
01112       PRExplodedTime tmToMatch, tmThis;
01113       if (NS_OK == GetLocalTimes (dateToMatch, m_value.u.date, tmToMatch, tmThis))
01114       {
01115         if (tmThis.tm_year != tmToMatch.tm_year ||
01116           tmThis.tm_month != tmToMatch.tm_month ||
01117           tmThis.tm_mday != tmToMatch.tm_mday)
01118           result = PR_TRUE;
01119       }
01120     }
01121     break;
01122   default:
01123     NS_ASSERTION(PR_FALSE, "invalid compare op for dates");
01124   }
01125   *pResult = result;
01126   return err;
01127 }
01128 
01129 
01130 nsresult nsMsgSearchTerm::MatchAge (PRTime msgDate, PRBool *pResult)
01131 {
01132   NS_ENSURE_ARG_POINTER(pResult);
01133 
01134   PRBool result = PR_FALSE;
01135   nsresult err = NS_OK;
01136 
01137   PRTime now = PR_Now();
01138   PRTime cutOffDay;
01139 
01140   PRInt64 microSecondsPerSecond, secondsInDays, microSecondsInDays;
01141        
01142   LL_I2L(microSecondsPerSecond, PR_USEC_PER_SEC);
01143   LL_UI2L(secondsInDays, 60 * 60 * 24 * m_value.u.age);
01144   LL_MUL(microSecondsInDays, secondsInDays, microSecondsPerSecond);
01145 
01146   LL_SUB(cutOffDay, now, microSecondsInDays); // = now - term->m_value.u.age * 60 * 60 * 24; 
01147   // so now cutOffDay is the PRTime cut-off point. Any msg with a time less than that will be past the age .
01148 
01149   switch (m_operator)
01150   {
01151     case nsMsgSearchOp::IsGreaterThan: // is older than 
01152       if (LL_CMP(msgDate, <, cutOffDay))
01153         result = PR_TRUE;
01154       break;
01155     case nsMsgSearchOp::IsLessThan: // is younger than 
01156       if (LL_CMP(msgDate, >, cutOffDay))
01157         result = PR_TRUE;
01158       break;
01159     case nsMsgSearchOp::Is:
01160       PRExplodedTime msgDateExploded;
01161       PRExplodedTime cutOffDayExploded;
01162       if (NS_SUCCEEDED(GetLocalTimes(msgDate, cutOffDay, msgDateExploded, cutOffDayExploded)))
01163       {
01164         if ((msgDateExploded.tm_mday == cutOffDayExploded.tm_mday) &&
01165             (msgDateExploded.tm_month == cutOffDayExploded.tm_month) &&
01166             (msgDateExploded.tm_year == cutOffDayExploded.tm_year))
01167           result = PR_TRUE;
01168       }
01169       break;
01170     default:
01171       NS_ASSERTION(PR_FALSE, "invalid compare op for msg age");
01172   }
01173   *pResult = result;
01174   return err;
01175 }
01176 
01177 
01178 nsresult nsMsgSearchTerm::MatchSize (PRUint32 sizeToMatch, PRBool *pResult)
01179 {
01180   NS_ENSURE_ARG_POINTER(pResult);
01181 
01182   PRBool result = PR_FALSE;
01183   // We reduce the sizeToMatch rather than supplied size
01184   // as then we can do an exact match on the displayed value
01185   // which will be less confusing to the user.
01186   PRUint32 sizeToMatchKB = sizeToMatch;
01187 
01188   if (sizeToMatchKB < 1024)
01189     sizeToMatchKB = 1024;
01190 
01191   sizeToMatchKB /= 1024;
01192 
01193   switch (m_operator)
01194   {
01195   case nsMsgSearchOp::IsGreaterThan:
01196     if (sizeToMatchKB > m_value.u.size)
01197       result = PR_TRUE;
01198     break;
01199   case nsMsgSearchOp::IsLessThan:
01200     if (sizeToMatchKB < m_value.u.size)
01201       result = PR_TRUE;
01202     break;
01203   case nsMsgSearchOp::Is:
01204     if (sizeToMatchKB == m_value.u.size)
01205       result = PR_TRUE;
01206     break;
01207   default:
01208     break;
01209   }
01210   *pResult = result;
01211   return NS_OK;
01212 }
01213 
01214 nsresult nsMsgSearchTerm::MatchJunkStatus(const char *aJunkScore, PRBool *pResult)
01215 {
01216   NS_ENSURE_ARG_POINTER(pResult);
01217 
01218   nsMsgJunkStatus junkStatus;  
01219   if (aJunkScore && *aJunkScore) {
01220       // cut off set at 50. this may change
01221       // it works for our bayesian plugin, as "0" is good, and "100" is junk
01222       // but it might need tweaking for other plugins
01223       if ( atoi(aJunkScore) > 50 ) {
01224           junkStatus = nsIJunkMailPlugin::JUNK;
01225       } else {
01226           junkStatus = nsIJunkMailPlugin::GOOD;
01227       }
01228   }
01229   else {
01230     // the in UI, we only show "junk" or "not junk"
01231     // unknown, or nsIJunkMailPlugin::UNCLASSIFIED is shown as not junk
01232     // so for the search to work as expected, treat unknown as not junk
01233     junkStatus = nsIJunkMailPlugin::GOOD;
01234   }
01235 
01236   nsresult rv = NS_OK;
01237   PRBool matches = (junkStatus == m_value.u.junkStatus);
01238 
01239   switch (m_operator)
01240   {
01241     case nsMsgSearchOp::Is:
01242       break;
01243     case nsMsgSearchOp::Isnt:
01244       matches = !matches;
01245       break;
01246     default:
01247       rv = NS_ERROR_FAILURE;
01248       NS_ASSERTION(PR_FALSE, "invalid compare op for junk status");
01249   }
01250 
01251   *pResult = matches;
01252   return rv;  
01253 }
01254 
01255 nsresult nsMsgSearchTerm::MatchLabel(nsMsgLabelValue aLabelValue, PRBool *pResult)
01256 {
01257   NS_ENSURE_ARG_POINTER(pResult);
01258   PRBool result = PR_FALSE;
01259   switch (m_operator)
01260   {
01261   case nsMsgSearchOp::Is:
01262     if (m_value.u.label == aLabelValue)
01263       result = PR_TRUE;
01264     break;
01265   default:
01266     if (m_value.u.label != aLabelValue)
01267       result = PR_TRUE;
01268     break;
01269   }
01270   
01271   *pResult = result;
01272   return NS_OK;      
01273 }
01274 
01275 nsresult nsMsgSearchTerm::MatchStatus(PRUint32 statusToMatch, PRBool *pResult)
01276 {
01277   NS_ENSURE_ARG_POINTER(pResult);
01278 
01279   nsresult rv = NS_OK;
01280   PRBool matches = (statusToMatch & m_value.u.msgStatus);
01281 
01282   switch (m_operator)
01283   {
01284   case nsMsgSearchOp::Is:
01285     break;
01286   case nsMsgSearchOp::Isnt:
01287     matches = !matches;
01288     break;
01289   default:
01290     rv = NS_ERROR_FAILURE;
01291     NS_ERROR("invalid compare op for msg status");
01292   }
01293 
01294   *pResult = matches;
01295   return rv;  
01296 }
01297 
01298 nsresult nsMsgSearchTerm::MatchKeyword(const char *keyword, PRBool *pResult)
01299 {
01300   NS_ENSURE_ARG_POINTER(pResult);
01301 
01302   nsresult rv = NS_OK;
01303   nsCAutoString keys;
01304 
01305   PRBool matches = PR_FALSE;
01306 
01307   switch (m_operator)
01308   {
01309   case nsMsgSearchOp::Is:
01310     matches = !strcmp(keyword, m_value.string);
01311     break;
01312   case nsMsgSearchOp::Isnt:
01313     matches = strcmp(keyword, m_value.string);
01314     break;
01315   case nsMsgSearchOp::DoesntContain:
01316   case nsMsgSearchOp::Contains:
01317     {
01318       const char *keywordLoc = PL_strstr(keyword, m_value.string);
01319       const char *startOfKeyword = keyword;
01320       PRUint32 keywordLen = strlen(m_value.string);
01321       while (keywordLoc)
01322       {
01323         // if the keyword is at the beginning of the string, then it's a match if 
01324         // it is either the whole string, or is followed by a space, it's a match.
01325         if (keywordLoc == startOfKeyword || (keywordLoc[-1] == ' '))
01326         {
01327           matches = keywordLen == strlen(keywordLoc) || (keywordLoc[keywordLen] == ' ');
01328           if (matches)
01329             break;
01330         }
01331         startOfKeyword = keywordLoc + keywordLen;
01332         keywordLoc = PL_strstr(keyword, keywordLoc + keywordLen + 1);
01333       }
01334     }
01335     break;
01336   default:
01337     rv = NS_ERROR_FAILURE;
01338     NS_ERROR("invalid compare op for msg status");
01339   }
01340 
01341   *pResult = (m_operator == nsMsgSearchOp::DoesntContain) ? !matches : matches;
01342   return rv;  
01343 }
01344 
01345 nsresult
01346 nsMsgSearchTerm::MatchPriority (nsMsgPriorityValue priorityToMatch,
01347                                 PRBool *pResult)
01348 {
01349   NS_ENSURE_ARG_POINTER(pResult);
01350 
01351   nsresult err = NS_OK;
01352   PRBool result=NS_OK;
01353 
01354   // Use this ugly little hack to get around the fact that enums don't have
01355   // integer compare operators
01356   int p1 = (priorityToMatch == nsMsgPriority::none) ? (int) nsMsgPriority::normal : (int) priorityToMatch;
01357   int p2 = (int) m_value.u.priority;
01358 
01359   switch (m_operator)
01360   {
01361   case nsMsgSearchOp::IsHigherThan:
01362     if (p1 > p2)
01363       result = PR_TRUE;
01364     break;
01365   case nsMsgSearchOp::IsLowerThan:
01366     if (p1 < p2)
01367       result = PR_TRUE;
01368     break;
01369   case nsMsgSearchOp::Is:
01370     if (p1 == p2)
01371       result = PR_TRUE;
01372     break;
01373   default:
01374     result = PR_FALSE;
01375     err = NS_ERROR_FAILURE;
01376     NS_ASSERTION(PR_FALSE, "invalid match operator");
01377   }
01378   *pResult = result;
01379   return err;
01380 }
01381 
01382 // Lazily initialize the rfc822 header parser we're going to use to do
01383 // header matching.
01384 nsresult nsMsgSearchTerm::InitHeaderAddressParser()
01385 {
01386        nsresult res = NS_OK;
01387     
01388        if (!m_headerAddressParser)
01389        {
01390     m_headerAddressParser = do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID, &res);
01391        }
01392        return res;
01393 }
01394 
01395 NS_IMPL_GETSET(nsMsgSearchTerm, Attrib, nsMsgSearchAttribValue, m_attribute)
01396 NS_IMPL_GETSET(nsMsgSearchTerm, Op, nsMsgSearchOpValue, m_operator)
01397 NS_IMPL_GETSET(nsMsgSearchTerm, MatchAll, PRBool, m_matchAll)
01398 
01399 NS_IMETHODIMP
01400 nsMsgSearchTerm::GetValue(nsIMsgSearchValue **aResult)
01401 {
01402     NS_ENSURE_ARG_POINTER(aResult);
01403     *aResult = new nsMsgSearchValueImpl(&m_value);
01404     NS_IF_ADDREF(*aResult);
01405     return NS_OK;
01406 }
01407 
01408 NS_IMETHODIMP
01409 nsMsgSearchTerm::SetValue(nsIMsgSearchValue* aValue)
01410 {
01411   nsMsgResultElement::AssignValues (aValue, &m_value);
01412   return NS_OK;
01413 }
01414 
01415 NS_IMETHODIMP
01416 nsMsgSearchTerm::GetBooleanAnd(PRBool *aResult)
01417 {
01418     NS_ENSURE_ARG_POINTER(aResult);
01419     *aResult = (m_booleanOp == nsMsgSearchBooleanOp::BooleanAND);
01420     return NS_OK;
01421 }
01422 
01423 NS_IMETHODIMP
01424 nsMsgSearchTerm::SetBooleanAnd(PRBool aValue)
01425 {
01426     if (aValue)
01427         m_booleanOp = nsMsgSearchBooleanOperator(nsMsgSearchBooleanOp::BooleanAND);
01428     else
01429         m_booleanOp = nsMsgSearchBooleanOperator(nsMsgSearchBooleanOp::BooleanOR);
01430     return NS_OK;
01431 }
01432 
01433 NS_IMETHODIMP
01434 nsMsgSearchTerm::GetArbitraryHeader(char* *aResult)
01435 {
01436     NS_ENSURE_ARG_POINTER(aResult);
01437     *aResult = ToNewCString(m_arbitraryHeader);
01438     return NS_OK;
01439 }
01440 
01441 NS_IMETHODIMP
01442 nsMsgSearchTerm::SetArbitraryHeader(const char* aValue)
01443 {
01444     m_arbitraryHeader = aValue;
01445     return NS_OK;
01446 }
01447 
01448 NS_IMPL_GETSET(nsMsgSearchTerm, BeginsGrouping, PRBool, mEndsGrouping)
01449 NS_IMPL_GETSET(nsMsgSearchTerm, EndsGrouping, PRBool, mEndsGrouping)
01450 
01451 //-----------------------------------------------------------------------------
01452 // nsMsgSearchScopeTerm implementation
01453 //-----------------------------------------------------------------------------
01454 nsMsgSearchScopeTerm::nsMsgSearchScopeTerm (nsIMsgSearchSession *session,
01455                                             nsMsgSearchScopeValue attribute,
01456                                             nsIMsgFolder *folder)
01457 {
01458   m_attribute = attribute;
01459   m_folder = folder;
01460   m_searchServer = PR_TRUE;
01461   m_searchSession = do_GetWeakReference(session);
01462 }
01463 
01464 nsMsgSearchScopeTerm::nsMsgSearchScopeTerm ()
01465 {
01466   m_searchServer = PR_TRUE;
01467 }
01468 
01469 nsMsgSearchScopeTerm::~nsMsgSearchScopeTerm ()
01470 {  
01471   if (m_inputStream)
01472     m_inputStream->Close();
01473   m_inputStream = nsnull;
01474 }
01475 
01476 NS_IMPL_ISUPPORTS1(nsMsgSearchScopeTerm, nsIMsgSearchScopeTerm)
01477 
01478 NS_IMETHODIMP
01479 nsMsgSearchScopeTerm::GetFolder(nsIMsgFolder **aResult)
01480 {
01481     NS_IF_ADDREF(*aResult = m_folder);
01482     return NS_OK;
01483 }
01484 
01485 NS_IMETHODIMP
01486 nsMsgSearchScopeTerm::GetSearchSession(nsIMsgSearchSession** aResult)
01487 {
01488     NS_ENSURE_ARG_POINTER(aResult);
01489     nsCOMPtr<nsIMsgSearchSession> searchSession = do_QueryReferent (m_searchSession);
01490     NS_IF_ADDREF(*aResult = searchSession);
01491     return NS_OK;
01492 }
01493 
01494 NS_IMETHODIMP nsMsgSearchScopeTerm::GetMailFile(nsILocalFile **aLocalFile)
01495 {
01496   NS_ENSURE_ARG_POINTER(aLocalFile);
01497   if (!m_localFile)
01498   {
01499     if (!m_folder)
01500      return NS_ERROR_NULL_POINTER;
01501 
01502     nsCOMPtr <nsIFileSpec> fileSpec;
01503     m_folder->GetPath(getter_AddRefs(fileSpec));
01504     nsFileSpec realSpec;
01505     fileSpec->GetFileSpec(&realSpec);
01506     NS_FileSpecToIFile(&realSpec, getter_AddRefs(m_localFile));
01507   }
01508   if (m_localFile)
01509   {
01510     NS_ADDREF(*aLocalFile = m_localFile);
01511     return NS_OK;
01512   }
01513   return NS_ERROR_FAILURE;
01514 }
01515 
01516 NS_IMETHODIMP nsMsgSearchScopeTerm::GetInputStream(nsIInputStream **aInputStream)
01517 {
01518   NS_ENSURE_ARG_POINTER(aInputStream);
01519   nsresult rv = NS_OK;
01520   if (!m_inputStream)
01521   {
01522      nsCOMPtr <nsILocalFile> localFile;
01523      rv = GetMailFile(getter_AddRefs(localFile));
01524      NS_ENSURE_SUCCESS(rv, rv);
01525      nsCOMPtr<nsIFileInputStream> fileStream = do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv);
01526      NS_ENSURE_SUCCESS(rv, rv);
01527 
01528      rv = fileStream->Init(localFile,  PR_RDONLY, 0664, PR_FALSE);  //just have to read the messages
01529      m_inputStream = do_QueryInterface(fileStream);
01530    
01531   }
01532   NS_IF_ADDREF(*aInputStream = m_inputStream);
01533   return rv;
01534 }
01535 
01536 NS_IMETHODIMP nsMsgSearchScopeTerm::SetInputStream(nsIInputStream *aInputStream)
01537 {
01538   if (!aInputStream && m_inputStream)
01539     m_inputStream->Close();
01540   m_inputStream = aInputStream;
01541   return NS_OK;
01542 }
01543 
01544 nsresult nsMsgSearchScopeTerm::TimeSlice (PRBool *aDone)
01545 {
01546   return m_adapter->Search(aDone);
01547 }
01548 
01549 nsresult nsMsgSearchScopeTerm::InitializeAdapter (nsISupportsArray *termList)
01550 {
01551   if (m_adapter)
01552     return NS_OK;
01553   
01554   nsresult err = NS_OK;
01555   
01556   switch (m_attribute)
01557   {
01558     case nsMsgSearchScope::onlineMail:    
01559         m_adapter = new nsMsgSearchOnlineMail (this, termList);
01560       break;
01561     case nsMsgSearchScope::offlineMail:
01562         m_adapter = new nsMsgSearchOfflineMail (this, termList);
01563       break;
01564     case nsMsgSearchScope::newsEx:
01565 #ifdef DOING_EXNEWSSEARCH
01566         if (m_folder->KnowsSearchNntpExtension())
01567           m_adapter = new nsMsgSearchNewsEx (this, termList);
01568         else
01569           m_adapter = new nsMsgSearchNews(this, termList);
01570 #endif
01571       NS_ASSERTION(PR_FALSE, "not supporting newsEx yet");
01572       break;
01573     case nsMsgSearchScope::news:
01574           m_adapter = new nsMsgSearchNews (this, termList);
01575         break;
01576     case nsMsgSearchScope::allSearchableGroups:
01577 #ifdef DOING_EXNEWSSEARCH
01578       m_adapter = new msMsgSearchNewsEx (this, termList);
01579 #endif
01580       NS_ASSERTION(PR_FALSE, "not supporting allSearchableGroups yet");
01581       break;
01582     case nsMsgSearchScope::LDAP:
01583       NS_ASSERTION(PR_FALSE, "not supporting LDAP yet");
01584       break;
01585     case nsMsgSearchScope::localNews:
01586       m_adapter = new nsMsgSearchOfflineNews (this, termList);
01587       break;
01588     default:
01589       NS_ASSERTION(PR_FALSE, "invalid scope");
01590       err = NS_ERROR_FAILURE;
01591   }
01592   
01593   if (m_adapter)
01594     err = m_adapter->ValidateTerms ();
01595   
01596   return err;
01597 }
01598 
01599 
01600 char *nsMsgSearchScopeTerm::GetStatusBarName ()
01601 {
01602   return nsnull;
01603 }
01604 
01605 
01606 //-----------------------------------------------------------------------------
01607 // nsMsgResultElement implementation
01608 //-----------------------------------------------------------------------------
01609 
01610 
01611 nsMsgResultElement::nsMsgResultElement(nsIMsgSearchAdapter *adapter)
01612 {
01613   NS_NewISupportsArray(getter_AddRefs(m_valueList));
01614   m_adapter = adapter;
01615 }
01616 
01617 
01618 nsMsgResultElement::~nsMsgResultElement () 
01619 {
01620 }
01621 
01622 
01623 nsresult nsMsgResultElement::AddValue (nsIMsgSearchValue *value)
01624 { 
01625   m_valueList->AppendElement (value); 
01626   return NS_OK;
01627 }
01628 
01629 nsresult nsMsgResultElement::AddValue (nsMsgSearchValue *value)
01630 {
01631   nsMsgSearchValueImpl* valueImpl = new nsMsgSearchValueImpl(value);
01632   delete value;               // we keep the nsIMsgSearchValue, not
01633                               // the nsMsgSearchValue
01634   return AddValue(valueImpl);
01635 }
01636 
01637 
01638 nsresult nsMsgResultElement::AssignValues (nsIMsgSearchValue *src, nsMsgSearchValue *dst)
01639 {
01640   NS_ENSURE_ARG_POINTER(src);
01641   NS_ENSURE_ARG_POINTER(dst);
01642   
01643   // Yes, this could be an operator overload, but nsMsgSearchValue is totally public, so I'd
01644   // have to define a derived class with nothing by operator=, and that seems like a bit much
01645   nsresult err = NS_OK;
01646   src->GetAttrib(&dst->attribute);
01647   switch (dst->attribute)
01648   {
01649   case nsMsgSearchAttrib::Priority:
01650     err = src->GetPriority(&dst->u.priority);
01651     break;
01652   case nsMsgSearchAttrib::Date:
01653     err = src->GetDate(&dst->u.date);
01654     break;
01655   case nsMsgSearchAttrib::HasAttachmentStatus:
01656   case nsMsgSearchAttrib::MsgStatus:
01657     err = src->GetStatus(&dst->u.msgStatus);
01658     break;
01659   case nsMsgSearchAttrib::MessageKey:
01660     err = src->GetMsgKey(&dst->u.key);
01661     break;
01662   case nsMsgSearchAttrib::AgeInDays:
01663     err = src->GetAge(&dst->u.age);
01664     break;
01665   case nsMsgSearchAttrib::Label:
01666     err = src->GetLabel(&dst->u.label);
01667     break;
01668   case nsMsgSearchAttrib::JunkStatus:
01669     err = src->GetJunkStatus(&dst->u.junkStatus);
01670     break;
01671   case nsMsgSearchAttrib::Size:
01672     err = src->GetSize(&dst->u.size);
01673     break;
01674   default:
01675     if (dst->attribute < nsMsgSearchAttrib::kNumMsgSearchAttributes)
01676     {
01677       NS_ASSERTION(IS_STRING_ATTRIBUTE(dst->attribute), "assigning non-string result");
01678       nsXPIDLString unicodeString;
01679       err = src->GetStr(getter_Copies(unicodeString));
01680       dst->string = ToNewUTF8String(unicodeString);
01681     }
01682     else
01683       err = NS_ERROR_INVALID_ARG;
01684   }
01685   return err;
01686 }
01687 
01688 
01689 nsresult nsMsgResultElement::GetValue (nsMsgSearchAttribValue attrib,
01690                                        nsMsgSearchValue **outValue) const
01691 {
01692   nsresult err = NS_OK;
01693   nsCOMPtr<nsIMsgSearchValue> value;
01694   *outValue = NULL;
01695   
01696   PRUint32 count;
01697   m_valueList->Count(&count);
01698   for (PRUint32 i = 0; i < count && err != NS_OK; i++)
01699   {
01700     m_valueList->QueryElementAt(i, NS_GET_IID(nsIMsgSearchValue),
01701       (void **)getter_AddRefs(value));
01702     
01703     nsMsgSearchAttribValue valueAttribute;
01704     value->GetAttrib(&valueAttribute);
01705     if (attrib == valueAttribute)
01706     {
01707       *outValue = new nsMsgSearchValue;
01708       if (*outValue)
01709       {
01710         err = AssignValues (value, *outValue);
01711         err = NS_OK;
01712       }
01713       else
01714         err = NS_ERROR_OUT_OF_MEMORY;
01715     }
01716   }
01717 #ifdef HAVE_SEARCH_PORT
01718   // No need to store the folderInfo separately; we can always get it if/when
01719   // we need it. This code is to support "view thread context" in the search dialog
01720   if (SearchError_ScopeAgreement == err && attrib == nsMsgSearchAttrib::FolderInfo)
01721   {
01722     nsMsgFolderInfo *targetFolder = m_adapter->FindTargetFolder (this);
01723     if (targetFolder)
01724     {
01725       *outValue = new nsMsgSearchValue;
01726       if (*outValue)
01727       {
01728         (*outValue)->u.folder = targetFolder;
01729         (*outValue)->attribute = nsMsgSearchAttrib::FolderInfo;
01730         err = NS_OK;
01731       }
01732     }
01733   }
01734 #endif
01735   return err;
01736 }
01737 
01738 
01739 nsresult
01740 nsMsgResultElement::GetValueRef (nsMsgSearchAttribValue attrib,
01741                                  nsIMsgSearchValue* *aResult) const 
01742 {
01743   nsCOMPtr<nsIMsgSearchValue> value;
01744   PRUint32 count;
01745   m_valueList->Count(&count);
01746   nsresult rv;
01747   for (PRUint32 i = 0; i < count; i++)
01748   {
01749     rv = m_valueList->QueryElementAt(i, NS_GET_IID(nsIMsgSearchValue),
01750       getter_AddRefs(value));
01751     NS_ASSERTION(NS_SUCCEEDED(rv), "bad element of array");
01752     if (NS_FAILED(rv)) continue;
01753     
01754     nsMsgSearchAttribValue valueAttribute;
01755     value->GetAttrib(&valueAttribute);
01756     if (attrib == valueAttribute) {
01757       *aResult = value;
01758       NS_ADDREF(*aResult);
01759     }
01760   }
01761   return NS_ERROR_FAILURE;
01762 }
01763 
01764 
01765 nsresult nsMsgResultElement::GetPrettyName (nsMsgSearchValue **value)
01766 {
01767   nsresult err = GetValue (nsMsgSearchAttrib::Location, value);
01768 #ifdef HAVE_SEARCH_PORT
01769   if (NS_OK == err)
01770   {
01771     nsMsgFolderInfo *folder = m_adapter->m_scope->m_folder;
01772     nsMsgNewsHost *host = NULL;
01773     if (folder)
01774     {
01775       // Find the news host because only the host knows whether pretty
01776       // names are supported. 
01777       if (FOLDER_CONTAINERONLY == folder->GetType())
01778         host = ((nsMsgNewsFolderInfoContainer*) folder)->GetHost();
01779       else if (folder->IsNews())
01780         host = folder->GetNewsFolderInfo()->GetHost();
01781       
01782       // Ask the host whether it knows pretty names. It isn't strictly
01783       // necessary to avoid calling folder->GetPrettiestName() since it
01784       // does the right thing. But we do have to find the folder from the host.
01785       if (host && host->QueryExtension ("LISTPNAMES"))
01786       {
01787         folder = host->FindGroup ((*value)->u.string);
01788         if (folder)
01789         {
01790           char *tmp = nsCRT::strdup (folder->GetPrettiestName());
01791           if (tmp)
01792           {
01793             XP_FREE ((*value)->u.string);
01794             (*value)->u.utf8SstringZ = tmp;
01795           }
01796         }
01797       }
01798     }
01799   }
01800 #endif // HAVE_SEARCH_PORT
01801   return err;
01802 }
01803 
01804 int nsMsgResultElement::CompareByFolderInfoPtrs (const void *e1, const void *e2)
01805 {
01806 #ifdef HAVE_SEARCH_PORT
01807   nsMsgResultElement * re1 = *(nsMsgResultElement **) e1;
01808   nsMsgResultElement * re2 = *(nsMsgResultElement **) e2;
01809   
01810   // get the src folder for each one
01811   
01812   const nsMsgSearchValue * v1 = re1->GetValueRef(attribFolderInfo);
01813   const nsMsgSearchValue * v2 = re2->GetValueRef(attribFolderInfo);
01814   
01815   if (!v1 || !v2)
01816     return 0;
01817   
01818   return (v1->u.folder - v2->u.folder);
01819 #else
01820   return -1;
01821 #endif // HAVE_SEARCH_PORT
01822 }
01823 
01824 
01825 
01826 int nsMsgResultElement::Compare (const void *e1, const void *e2)
01827 {
01828   int ret = 0;
01829     return ret;
01830 }
01831 
01832 #ifdef HAVE_SEARCH_PORT
01833 MWContextType nsMsgResultElement::GetContextType ()
01834 {
01835   MWContextType type=(MWContextType)0;
01836   switch (m_adapter->m_scope->m_attribute)
01837   {
01838   case nsMsgSearchScopeMailFolder:
01839     type = MWContextMailMsg;
01840     break;
01841   case nsMsgSearchScopeOfflineNewsgroup:    // added by mscott could be bug fix...
01842   case nsMsgSearchScopeNewsgroup:
01843   case nsMsgSearchScopeAllSearchableGroups:
01844     type = MWContextNewsMsg;
01845     break;
01846   case nsMsgSearchScopeLdapDirectory:
01847     type = MWContextBrowser;
01848     break;
01849   default:
01850     NS_ASSERTION(PR_FALSE, "invalid scope"); // should never happen
01851   }
01852   return type;
01853 }
01854 
01855 #endif
01856 nsresult nsMsgResultElement::Open (void *window)
01857 {
01858 #ifdef HAVE_SEARCH_PORT
01859   // ###phil this is a little ugly, but I'm not inclined to invest more in it
01860   // until the libnet rework is done and I know what kind of context we'll end up with
01861   
01862   if (window)
01863   {
01864     if (m_adapter->m_scope->m_attribute != nsMsgSearchScopeLdapDirectory)
01865     {
01866       msgPane = (MSG_MessagePane *) window; 
01867       PR_ASSERT (MSG_MESSAGEPANE == msgPane->GetPaneType());
01868       return m_adapter->OpenResultElement (msgPane, this);
01869     }
01870     else
01871     {
01872       context = (MWContext*) window;
01873       PR_ASSERT (MWContextBrowser == context->type);
01874       msg_SearchLdap *thisAdapter = (msg_SearchLdap*) m_adapter;
01875       return thisAdapter->OpenResultElement (context, this);
01876     }
01877   }
01878 #endif
01879   return NS_ERROR_NULL_POINTER;
01880 }
01881 
01882