1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "msgCore.h"
7 #include "nsTextFormatter.h"
8 #include "nsMsgSearchCore.h"
9 #include "nsMsgSearchAdapter.h"
10 #include "nsMsgSearchScopeTerm.h"
11 #include "nsMsgI18N.h"
12 #include "nsIPrefService.h"
13 #include "nsIPrefBranch.h"
14 #include "nsIPrefLocalizedString.h"
15 #include "nsMsgSearchTerm.h"
16 #include "nsMsgSearchBoolExpression.h"
17 #include "nsIIOService.h"
18 #include "nsNetCID.h"
19 #include "prprf.h"
20 #include "mozilla/UniquePtr.h"
21 #include "prmem.h"
22 #include "MailNewsTypes.h"
23 #include "nsComponentManagerUtils.h"
24 #include "nsServiceManagerUtils.h"
25 #include "nsMemory.h"
26 #include "nsMsgMessageFlags.h"
27 #include "mozilla/Attributes.h"
28 #include "nsIMsgNewsFolder.h"
29 
30 // This stuff lives in the base class because the IMAP search syntax
31 // is used by the Dredd SEARCH command as well as IMAP itself
32 
33 // km - the NOT and HEADER strings are not encoded with a trailing
34 //      <space> because they always precede a mnemonic that has a
35 //      preceding <space> and double <space> characters cause some
36 //    imap servers to return an error.
37 const char* nsMsgSearchAdapter::m_kImapBefore = " SENTBEFORE ";
38 const char* nsMsgSearchAdapter::m_kImapBody = " BODY ";
39 const char* nsMsgSearchAdapter::m_kImapCC = " CC ";
40 const char* nsMsgSearchAdapter::m_kImapFrom = " FROM ";
41 const char* nsMsgSearchAdapter::m_kImapNot = " NOT";
42 const char* nsMsgSearchAdapter::m_kImapUnDeleted = " UNDELETED";
43 const char* nsMsgSearchAdapter::m_kImapOr = " OR";
44 const char* nsMsgSearchAdapter::m_kImapSince = " SENTSINCE ";
45 const char* nsMsgSearchAdapter::m_kImapSubject = " SUBJECT ";
46 const char* nsMsgSearchAdapter::m_kImapTo = " TO ";
47 const char* nsMsgSearchAdapter::m_kImapHeader = " HEADER";
48 const char* nsMsgSearchAdapter::m_kImapAnyText = " TEXT ";
49 const char* nsMsgSearchAdapter::m_kImapKeyword = " KEYWORD ";
50 const char* nsMsgSearchAdapter::m_kNntpKeywords = " KEYWORDS ";  // ggrrrr...
51 const char* nsMsgSearchAdapter::m_kImapSentOn = " SENTON ";
52 const char* nsMsgSearchAdapter::m_kImapSeen = " SEEN ";
53 const char* nsMsgSearchAdapter::m_kImapAnswered = " ANSWERED ";
54 const char* nsMsgSearchAdapter::m_kImapNotSeen = " UNSEEN ";
55 const char* nsMsgSearchAdapter::m_kImapNotAnswered = " UNANSWERED ";
56 const char* nsMsgSearchAdapter::m_kImapCharset = " CHARSET ";
57 const char* nsMsgSearchAdapter::m_kImapSizeSmaller = " SMALLER ";
58 const char* nsMsgSearchAdapter::m_kImapSizeLarger = " LARGER ";
59 const char* nsMsgSearchAdapter::m_kImapNew = " NEW ";
60 const char* nsMsgSearchAdapter::m_kImapNotNew = " OLD SEEN ";
61 const char* nsMsgSearchAdapter::m_kImapFlagged = " FLAGGED ";
62 const char* nsMsgSearchAdapter::m_kImapNotFlagged = " UNFLAGGED ";
63 
64 #define PREF_CUSTOM_HEADERS "mailnews.customHeaders"
65 
FindTargetFolder(const nsMsgResultElement *,nsIMsgFolder **)66 NS_IMETHODIMP nsMsgSearchAdapter::FindTargetFolder(const nsMsgResultElement*,
67                                                    nsIMsgFolder**) {
68   return NS_ERROR_NOT_IMPLEMENTED;
69 }
70 
ModifyResultElement(nsMsgResultElement *,nsMsgSearchValue *)71 NS_IMETHODIMP nsMsgSearchAdapter::ModifyResultElement(nsMsgResultElement*,
72                                                       nsMsgSearchValue*) {
73   return NS_ERROR_NOT_IMPLEMENTED;
74 }
75 
OpenResultElement(nsMsgResultElement *)76 NS_IMETHODIMP nsMsgSearchAdapter::OpenResultElement(nsMsgResultElement*) {
77   return NS_ERROR_NOT_IMPLEMENTED;
78 }
79 
NS_IMPL_ISUPPORTS(nsMsgSearchAdapter,nsIMsgSearchAdapter)80 NS_IMPL_ISUPPORTS(nsMsgSearchAdapter, nsIMsgSearchAdapter)
81 
82 nsMsgSearchAdapter::nsMsgSearchAdapter(
83     nsIMsgSearchScopeTerm* scope,
84     nsTArray<RefPtr<nsIMsgSearchTerm>> const& searchTerms)
85     : m_scope(scope), m_searchTerms(searchTerms.Clone()) {}
86 
~nsMsgSearchAdapter()87 nsMsgSearchAdapter::~nsMsgSearchAdapter() {}
88 
ClearScope()89 NS_IMETHODIMP nsMsgSearchAdapter::ClearScope() {
90   if (m_scope) {
91     m_scope->CloseInputStream();
92     m_scope = nullptr;
93   }
94   return NS_OK;
95 }
96 
ValidateTerms()97 NS_IMETHODIMP nsMsgSearchAdapter::ValidateTerms() {
98   // all this used to do is check if the object had been deleted - we can skip
99   // that.
100   return NS_OK;
101 }
102 
Abort()103 NS_IMETHODIMP nsMsgSearchAdapter::Abort() { return NS_ERROR_NOT_IMPLEMENTED; }
Search(bool * aDone)104 NS_IMETHODIMP nsMsgSearchAdapter::Search(bool* aDone) { return NS_OK; }
105 
SendUrl()106 NS_IMETHODIMP nsMsgSearchAdapter::SendUrl() { return NS_OK; }
107 
108 /* void CurrentUrlDone (in nsresult exitCode); */
CurrentUrlDone(nsresult exitCode)109 NS_IMETHODIMP nsMsgSearchAdapter::CurrentUrlDone(nsresult exitCode) {
110   // base implementation doesn't need to do anything.
111   return NS_OK;
112 }
113 
GetEncoding(char ** encoding)114 NS_IMETHODIMP nsMsgSearchAdapter::GetEncoding(char** encoding) { return NS_OK; }
115 
AddResultElement(nsIMsgDBHdr * pHeaders)116 NS_IMETHODIMP nsMsgSearchAdapter::AddResultElement(nsIMsgDBHdr* pHeaders) {
117   NS_ASSERTION(false, "shouldn't call this base class impl");
118   return NS_ERROR_FAILURE;
119 }
120 
AddHit(nsMsgKey key)121 NS_IMETHODIMP nsMsgSearchAdapter::AddHit(nsMsgKey key) {
122   NS_ASSERTION(false, "shouldn't call this base class impl");
123   return NS_ERROR_FAILURE;
124 }
125 
GetImapCharsetParam(const char16_t * destCharset)126 char* nsMsgSearchAdapter::GetImapCharsetParam(const char16_t* destCharset) {
127   char* result = nullptr;
128 
129   // Specify a character set unless we happen to be US-ASCII.
130   if (NS_strcmp(destCharset, u"us-ascii"))
131     result = PR_smprintf("%s%s", nsMsgSearchAdapter::m_kImapCharset,
132                          NS_ConvertUTF16toUTF8(destCharset).get());
133 
134   return result;
135 }
136 
137 /*
138    09/21/2000 - taka@netscape.com
139    This method is bogus. Escape must be done against char * not char16_t *
140    should be rewritten later.
141    for now, just duplicate the string.
142 */
EscapeSearchUrl(const char16_t * nntpCommand)143 char16_t* nsMsgSearchAdapter::EscapeSearchUrl(const char16_t* nntpCommand) {
144   return nntpCommand ? NS_xstrdup(nntpCommand) : nullptr;
145 }
146 
147 /*
148    09/21/2000 - taka@netscape.com
149    This method is bogus. Escape must be done against char * not char16_t *
150    should be rewritten later.
151    for now, just duplicate the string.
152 */
EscapeImapSearchProtocol(const char16_t * imapCommand)153 char16_t* nsMsgSearchAdapter::EscapeImapSearchProtocol(
154     const char16_t* imapCommand) {
155   return imapCommand ? NS_xstrdup(imapCommand) : nullptr;
156 }
157 
158 /*
159    09/21/2000 - taka@netscape.com
160    This method is bogus. Escape must be done against char * not char16_t *
161    should be rewritten later.
162    for now, just duplicate the string.
163 */
EscapeQuoteImapSearchProtocol(const char16_t * imapCommand)164 char16_t* nsMsgSearchAdapter::EscapeQuoteImapSearchProtocol(
165     const char16_t* imapCommand) {
166   return imapCommand ? NS_xstrdup(imapCommand) : nullptr;
167 }
168 
UnEscapeSearchUrl(const char * commandSpecificData)169 char* nsMsgSearchAdapter::UnEscapeSearchUrl(const char* commandSpecificData) {
170   char* result = (char*)PR_Malloc(strlen(commandSpecificData) + 1);
171   if (result) {
172     char* resultPtr = result;
173     while (1) {
174       char ch = *commandSpecificData++;
175       if (!ch) break;
176       if (ch == '\\') {
177         char scratchBuf[3];
178         scratchBuf[0] = (char)*commandSpecificData++;
179         scratchBuf[1] = (char)*commandSpecificData++;
180         scratchBuf[2] = '\0';
181         unsigned int accum = 0;
182         sscanf(scratchBuf, "%X", &accum);
183         *resultPtr++ = (char)accum;
184       } else
185         *resultPtr++ = ch;
186     }
187     *resultPtr = '\0';
188   }
189   return result;
190 }
191 
GetSearchCharsets(nsAString & srcCharset,nsAString & dstCharset)192 nsresult nsMsgSearchAdapter::GetSearchCharsets(nsAString& srcCharset,
193                                                nsAString& dstCharset) {
194   nsresult rv;
195   bool forceAsciiSearch = false;
196 
197   nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
198   if (NS_SUCCEEDED(rv)) {
199     prefs->GetBoolPref("mailnews.force_ascii_search", &forceAsciiSearch);
200   }
201 
202   srcCharset = m_defaultCharset;
203   dstCharset.Assign(srcCharset);
204 
205   if (m_scope) {
206     nsCOMPtr<nsIMsgFolder> folder;
207     rv = m_scope->GetFolder(getter_AddRefs(folder));
208     if (NS_SUCCEEDED(rv) && folder) {
209       nsCOMPtr<nsIMsgNewsFolder> newsfolder(do_QueryInterface(folder));
210       if (newsfolder) {
211         nsCString folderCharset;
212         rv = newsfolder->GetCharset(folderCharset);
213         if (NS_SUCCEEDED(rv))
214           dstCharset.Assign(NS_ConvertASCIItoUTF16(folderCharset));
215       }
216     }
217   }
218 
219   if (forceAsciiSearch) {
220     // Special cases to use in order to force US-ASCII searching with Latin1
221     // or MacRoman text. Eurgh. This only has to happen because IMAP
222     // and Dredd servers currently (4/23/97) only support US-ASCII.
223     //
224     // If the dest csid is ISO Latin 1 or MacRoman, attempt to convert the
225     // source text to US-ASCII. (Not for now.)
226     // if ((dst_csid == CS_LATIN1) || (dst_csid == CS_MAC_ROMAN))
227     dstCharset.AssignLiteral("us-ascii");
228   }
229 
230   return NS_OK;
231 }
232 
EncodeImapTerm(nsIMsgSearchTerm * term,bool reallyDredd,const char16_t * srcCharset,const char16_t * destCharset,char ** ppOutTerm)233 nsresult nsMsgSearchAdapter::EncodeImapTerm(nsIMsgSearchTerm* term,
234                                             bool reallyDredd,
235                                             const char16_t* srcCharset,
236                                             const char16_t* destCharset,
237                                             char** ppOutTerm) {
238   NS_ENSURE_ARG_POINTER(term);
239   NS_ENSURE_ARG_POINTER(ppOutTerm);
240 
241   nsresult err = NS_OK;
242   bool useNot = false;
243   bool useQuotes = false;
244   bool ignoreValue = false;
245   nsAutoCString arbitraryHeader;
246   const char* whichMnemonic = nullptr;
247   const char* orHeaderMnemonic = nullptr;
248 
249   *ppOutTerm = nullptr;
250 
251   nsCOMPtr<nsIMsgSearchValue> searchValue;
252   nsresult rv = term->GetValue(getter_AddRefs(searchValue));
253 
254   NS_ENSURE_SUCCESS(rv, rv);
255 
256   nsMsgSearchOpValue op;
257   term->GetOp(&op);
258 
259   if (op == nsMsgSearchOp::DoesntContain || op == nsMsgSearchOp::Isnt)
260     useNot = true;
261 
262   nsMsgSearchAttribValue attrib;
263   term->GetAttrib(&attrib);
264 
265   switch (attrib) {
266     case nsMsgSearchAttrib::ToOrCC:
267       orHeaderMnemonic = m_kImapCC;
268       // fall through to case nsMsgSearchAttrib::To:
269       [[fallthrough]];
270     case nsMsgSearchAttrib::To:
271       whichMnemonic = m_kImapTo;
272       break;
273     case nsMsgSearchAttrib::CC:
274       whichMnemonic = m_kImapCC;
275       break;
276     case nsMsgSearchAttrib::Sender:
277       whichMnemonic = m_kImapFrom;
278       break;
279     case nsMsgSearchAttrib::Subject:
280       whichMnemonic = m_kImapSubject;
281       break;
282     case nsMsgSearchAttrib::Body:
283       whichMnemonic = m_kImapBody;
284       break;
285     case nsMsgSearchAttrib::AgeInDays:  // added for searching online for age in
286                                         // days...
287       // for AgeInDays, we are actually going to perform a search by date, so
288       // convert the operations for age to the IMAP mnemonics that we would use
289       // for date!
290       {
291         // If we have a future date, the > and < are reversed.
292         // e.g. ageInDays > 2 means more than 2 days old ("date before X")
293         // whereas
294         //      ageInDays > -2 should be more than 2 days in the future ("date
295         //      after X")
296         int32_t ageInDays;
297         searchValue->GetAge(&ageInDays);
298         bool dateInFuture = (ageInDays < 0);
299         switch (op) {
300           case nsMsgSearchOp::IsGreaterThan:
301             whichMnemonic = (!dateInFuture) ? m_kImapBefore : m_kImapSince;
302             break;
303           case nsMsgSearchOp::IsLessThan:
304             whichMnemonic = (!dateInFuture) ? m_kImapSince : m_kImapBefore;
305             break;
306           case nsMsgSearchOp::Is:
307             whichMnemonic = m_kImapSentOn;
308             break;
309           default:
310             NS_ASSERTION(false, "invalid search operator");
311             return NS_ERROR_INVALID_ARG;
312         }
313       }
314       break;
315     case nsMsgSearchAttrib::Size:
316       switch (op) {
317         case nsMsgSearchOp::IsGreaterThan:
318           whichMnemonic = m_kImapSizeLarger;
319           break;
320         case nsMsgSearchOp::IsLessThan:
321           whichMnemonic = m_kImapSizeSmaller;
322           break;
323         default:
324           NS_ASSERTION(false, "invalid search operator");
325           return NS_ERROR_INVALID_ARG;
326       }
327       break;
328     case nsMsgSearchAttrib::Date:
329       switch (op) {
330         case nsMsgSearchOp::IsBefore:
331           whichMnemonic = m_kImapBefore;
332           break;
333         case nsMsgSearchOp::IsAfter:
334           whichMnemonic = m_kImapSince;
335           break;
336         case nsMsgSearchOp::Isnt: /* we've already added the "Not" so just
337                                      process it like it was a date is search */
338         case nsMsgSearchOp::Is:
339           whichMnemonic = m_kImapSentOn;
340           break;
341         default:
342           NS_ASSERTION(false, "invalid search operator");
343           return NS_ERROR_INVALID_ARG;
344       }
345       break;
346     case nsMsgSearchAttrib::AnyText:
347       whichMnemonic = m_kImapAnyText;
348       break;
349     case nsMsgSearchAttrib::Keywords:
350       whichMnemonic = m_kImapKeyword;
351       break;
352     case nsMsgSearchAttrib::MsgStatus:
353       useNot = false;      // bizarrely, NOT SEEN is wrong, but UNSEEN is right.
354       ignoreValue = true;  // the mnemonic is all we need
355       uint32_t status;
356       searchValue->GetStatus(&status);
357 
358       switch (status) {
359         case nsMsgMessageFlags::Read:
360           whichMnemonic =
361               op == nsMsgSearchOp::Is ? m_kImapSeen : m_kImapNotSeen;
362           break;
363         case nsMsgMessageFlags::Replied:
364           whichMnemonic =
365               op == nsMsgSearchOp::Is ? m_kImapAnswered : m_kImapNotAnswered;
366           break;
367         case nsMsgMessageFlags::New:
368           whichMnemonic = op == nsMsgSearchOp::Is ? m_kImapNew : m_kImapNotNew;
369           break;
370         case nsMsgMessageFlags::Marked:
371           whichMnemonic =
372               op == nsMsgSearchOp::Is ? m_kImapFlagged : m_kImapNotFlagged;
373           break;
374         default:
375           NS_ASSERTION(false, "invalid search operator");
376           return NS_ERROR_INVALID_ARG;
377       }
378       break;
379     default:
380       if (attrib > nsMsgSearchAttrib::OtherHeader &&
381           attrib < nsMsgSearchAttrib::kNumMsgSearchAttributes) {
382         nsCString arbitraryHeaderTerm;
383         term->GetArbitraryHeader(arbitraryHeaderTerm);
384         if (!arbitraryHeaderTerm.IsEmpty()) {
385           arbitraryHeader.AssignLiteral(" \"");
386           arbitraryHeader.Append(arbitraryHeaderTerm);
387           arbitraryHeader.AppendLiteral("\" ");
388           whichMnemonic = arbitraryHeader.get();
389         } else
390           return NS_ERROR_FAILURE;
391       } else {
392         NS_ASSERTION(false, "invalid search operator");
393         return NS_ERROR_INVALID_ARG;
394       }
395   }
396 
397   char* value = nullptr;
398   char dateBuf[100];
399   dateBuf[0] = '\0';
400 
401   bool valueWasAllocated = false;
402   if (attrib == nsMsgSearchAttrib::Date) {
403     // note that there used to be code here that encoded an RFC822 date for imap
404     // searches. The IMAP RFC 2060 is misleading to the point that it looks like
405     // it requires an RFC822 date but really it expects dd-mmm-yyyy, like dredd,
406     // and refers to the RFC822 date only in that the dd-mmm-yyyy date will
407     // match the RFC822 date within the message.
408 
409     PRTime adjustedDate;
410     searchValue->GetDate(&adjustedDate);
411     if (whichMnemonic == m_kImapSince) {
412       // it looks like the IMAP server searches on Since includes the date in
413       // question... our UI presents Is, IsGreater and IsLessThan. For the
414       // IsGreater case (m_kImapSince) we need to adjust the date so we get
415       // greater than and not greater than or equal to which is what the IMAP
416       // server wants to search on won't work on Mac.
417       adjustedDate += PR_USEC_PER_DAY;
418     }
419 
420     PRExplodedTime exploded;
421     PR_ExplodeTime(adjustedDate, PR_LocalTimeParameters, &exploded);
422     PR_FormatTimeUSEnglish(dateBuf, sizeof(dateBuf), "%d-%b-%Y", &exploded);
423     //    strftime (dateBuf, sizeof(dateBuf), "%d-%b-%Y", localtime (/*
424     //    &term->m_value.u.date */ &adjustedDate));
425     value = dateBuf;
426   } else {
427     if (attrib == nsMsgSearchAttrib::AgeInDays) {
428       // okay, take the current date, subtract off the age in days, then do an
429       // appropriate Date search on the resulting day.
430       int32_t ageInDays;
431 
432       searchValue->GetAge(&ageInDays);
433 
434       PRTime now = PR_Now();
435       PRTime matchDay = now - ageInDays * PR_USEC_PER_DAY;
436 
437       PRExplodedTime exploded;
438       PR_ExplodeTime(matchDay, PR_LocalTimeParameters, &exploded);
439       PR_FormatTimeUSEnglish(dateBuf, sizeof(dateBuf), "%d-%b-%Y", &exploded);
440       //      strftime (dateBuf, sizeof(dateBuf), "%d-%b-%Y", localtime
441       //      (&matchDay));
442       value = dateBuf;
443     } else if (attrib == nsMsgSearchAttrib::Size) {
444       uint32_t sizeValue;
445       nsAutoCString searchTermValue;
446       searchValue->GetSize(&sizeValue);
447 
448       // Multiply by 1024 to get into kb resolution
449       sizeValue *= 1024;
450 
451       // Ensure that greater than is really greater than
452       // in kb resolution.
453       if (op == nsMsgSearchOp::IsGreaterThan) sizeValue += 1024;
454 
455       searchTermValue.AppendInt(sizeValue);
456 
457       value = ToNewCString(searchTermValue);
458       valueWasAllocated = true;
459     } else
460 
461         if (IS_STRING_ATTRIBUTE(attrib)) {
462       char16_t*
463           convertedValue;  // = reallyDredd ? MSG_EscapeSearchUrl
464                            // (term->m_value.u.string) :
465                            // msg_EscapeImapSearchProtocol(term->m_value.u.string);
466       nsString searchTermValue;
467       searchValue->GetStr(searchTermValue);
468       // Ugly switch for Korean mail/news charsets.
469       // We want to do this here because here is where
470       // we know what charset we want to use.
471 #ifdef DOING_CHARSET
472       if (reallyDredd)
473         dest_csid = INTL_DefaultNewsCharSetID(dest_csid);
474       else
475         dest_csid = INTL_DefaultMailCharSetID(dest_csid);
476 #endif
477 
478       // do all sorts of crazy escaping
479       convertedValue = reallyDredd
480                            ? EscapeSearchUrl(searchTermValue.get())
481                            : EscapeImapSearchProtocol(searchTermValue.get());
482       useQuotes =
483           ((!reallyDredd ||
484             (nsDependentString(convertedValue).FindChar(char16_t(' ')) !=
485              -1)) &&
486            (attrib != nsMsgSearchAttrib::Keywords));
487       // now convert to char* and escape quoted_specials
488       nsAutoCString valueStr;
489       nsresult rv = nsMsgI18NConvertFromUnicode(
490           NS_LossyConvertUTF16toASCII(destCharset),
491           nsDependentString(convertedValue), valueStr);
492       if (NS_SUCCEEDED(rv)) {
493         const char* vptr = valueStr.get();
494         // max escaped length is one extra character for every character in the
495         // cmd.
496         mozilla::UniquePtr<char[]> newValue =
497             mozilla::MakeUnique<char[]>(2 * strlen(vptr) + 1);
498         if (newValue) {
499           char* p = newValue.get();
500           while (1) {
501             char ch = *vptr++;
502             if (!ch) break;
503             if ((useQuotes ? ch == '"' : 0) || ch == '\\') *p++ = '\\';
504             *p++ = ch;
505           }
506           *p = '\0';
507           value = strdup(newValue.get());  // realloc down to smaller size
508         }
509       } else
510         value = strdup("");
511       free(convertedValue);
512       valueWasAllocated = true;
513     }
514   }
515 
516   // this should be rewritten to use nsCString
517   int subLen = (value ? strlen(value) : 0) + (useNot ? strlen(m_kImapNot) : 0) +
518                strlen(m_kImapHeader);
519   int len =
520       strlen(whichMnemonic) + subLen + (useQuotes ? 2 : 0) +
521       (orHeaderMnemonic
522            ? (subLen + strlen(m_kImapOr) + strlen(orHeaderMnemonic) + 2 /*""*/)
523            : 0) +
524       10;  // add slough for imap string literals
525   char* encoding = new char[len];
526   if (encoding) {
527     encoding[0] = '\0';
528     // Remember: if ToOrCC and useNot then the expression becomes NOT To AND Not
529     // CC as opposed to (NOT TO) || (NOT CC)
530     if (orHeaderMnemonic && !useNot) PL_strcat(encoding, m_kImapOr);
531     if (useNot) PL_strcat(encoding, m_kImapNot);
532     if (!arbitraryHeader.IsEmpty()) PL_strcat(encoding, m_kImapHeader);
533     PL_strcat(encoding, whichMnemonic);
534     if (!ignoreValue)
535       err = EncodeImapValue(encoding, value, useQuotes, reallyDredd);
536 
537     if (orHeaderMnemonic) {
538       if (useNot) PL_strcat(encoding, m_kImapNot);
539 
540       PL_strcat(encoding, m_kImapHeader);
541 
542       PL_strcat(encoding, orHeaderMnemonic);
543       if (!ignoreValue)
544         err = EncodeImapValue(encoding, value, useQuotes, reallyDredd);
545     }
546 
547     // kmcentee, don't let the encoding end with whitespace,
548     // this throws off later url STRCMP
549     if (*encoding && *(encoding + strlen(encoding) - 1) == ' ')
550       *(encoding + strlen(encoding) - 1) = '\0';
551   }
552 
553   if (value && valueWasAllocated) free(value);
554 
555   *ppOutTerm = encoding;
556 
557   return err;
558 }
559 
EncodeImapValue(char * encoding,const char * value,bool useQuotes,bool reallyDredd)560 nsresult nsMsgSearchAdapter::EncodeImapValue(char* encoding, const char* value,
561                                              bool useQuotes, bool reallyDredd) {
562   // By NNTP RFC, SEARCH HEADER SUBJECT "" is legal and means 'find messages
563   // without a subject header'
564   if (!reallyDredd) {
565     // By IMAP RFC, SEARCH HEADER SUBJECT "" is illegal and will generate an
566     // error from the server
567     if (!value || !value[0]) return NS_ERROR_NULL_POINTER;
568   }
569 
570   if (!NS_IsAscii(value)) {
571     nsAutoCString lengthStr;
572     PL_strcat(encoding, "{");
573     lengthStr.AppendInt((int32_t)strlen(value));
574     PL_strcat(encoding, lengthStr.get());
575     PL_strcat(encoding, "}" CRLF);
576     PL_strcat(encoding, value);
577     return NS_OK;
578   }
579   if (useQuotes) PL_strcat(encoding, "\"");
580   PL_strcat(encoding, value);
581   if (useQuotes) PL_strcat(encoding, "\"");
582 
583   return NS_OK;
584 }
585 
EncodeImap(char ** ppOutEncoding,nsTArray<RefPtr<nsIMsgSearchTerm>> const & searchTerms,const char16_t * srcCharset,const char16_t * destCharset,bool reallyDredd)586 nsresult nsMsgSearchAdapter::EncodeImap(
587     char** ppOutEncoding, nsTArray<RefPtr<nsIMsgSearchTerm>> const& searchTerms,
588     const char16_t* srcCharset, const char16_t* destCharset, bool reallyDredd) {
589   // i've left the old code (before using CBoolExpression for debugging purposes
590   // to make sure that the new code generates the same encoding string as the
591   // old code.....
592 
593   nsresult err = NS_OK;
594   *ppOutEncoding = nullptr;
595 
596   // create our expression
597   nsMsgSearchBoolExpression* expression = new nsMsgSearchBoolExpression();
598   if (!expression) return NS_ERROR_OUT_OF_MEMORY;
599 
600   for (nsIMsgSearchTerm* pTerm : searchTerms) {
601     bool matchAll;
602     pTerm->GetMatchAll(&matchAll);
603     if (matchAll) continue;
604     char* termEncoding;
605     err = EncodeImapTerm(pTerm, reallyDredd, srcCharset, destCharset,
606                          &termEncoding);
607     if (NS_SUCCEEDED(err) && nullptr != termEncoding) {
608       expression = nsMsgSearchBoolExpression::AddSearchTerm(expression, pTerm,
609                                                             termEncoding);
610       delete[] termEncoding;
611     } else {
612       break;
613     }
614   }
615 
616   if (NS_SUCCEEDED(err)) {
617     // Catenate the intermediate encodings together into a big string
618     nsAutoCString encodingBuff;
619 
620     if (!reallyDredd) encodingBuff.Append(m_kImapUnDeleted);
621 
622     expression->GenerateEncodeStr(&encodingBuff);
623     *ppOutEncoding = ToNewCString(encodingBuff);
624   }
625 
626   delete expression;
627 
628   return err;
629 }
630 
TransformSpacesToStars(const char * spaceString,msg_TransformType transformType)631 char* nsMsgSearchAdapter::TransformSpacesToStars(
632     const char* spaceString, msg_TransformType transformType) {
633   char* starString;
634 
635   if (transformType == kOverwrite) {
636     if ((starString = strdup(spaceString)) != nullptr) {
637       char* star = starString;
638       while ((star = PL_strchr(star, ' ')) != nullptr) *star = '*';
639     }
640   } else {
641     int i, count;
642 
643     for (i = 0, count = 0; spaceString[i];) {
644       if (spaceString[i++] == ' ') {
645         count++;
646         while (spaceString[i] && spaceString[i] == ' ') i++;
647       }
648     }
649 
650     if (transformType == kSurround) count *= 2;
651 
652     if (count > 0) {
653       if ((starString = (char*)PR_Malloc(i + count + 1)) != nullptr) {
654         int j;
655 
656         for (i = 0, j = 0; spaceString[i];) {
657           if (spaceString[i] == ' ') {
658             starString[j++] = '*';
659             starString[j++] = ' ';
660             if (transformType == kSurround) starString[j++] = '*';
661 
662             i++;
663             while (spaceString[i] && spaceString[i] == ' ') i++;
664           } else
665             starString[j++] = spaceString[i++];
666         }
667         starString[j] = 0;
668       }
669     } else
670       starString = strdup(spaceString);
671   }
672 
673   return starString;
674 }
675 
676 //-----------------------------------------------------------------------------
677 //------------------- Validity checking for menu items etc. -------------------
678 //-----------------------------------------------------------------------------
679 
nsMsgSearchValidityTable()680 nsMsgSearchValidityTable::nsMsgSearchValidityTable() {
681   // Set everything to be unavailable and disabled
682   for (int i = 0; i < nsMsgSearchAttrib::kNumMsgSearchAttributes; i++)
683     for (int j = 0; j < nsMsgSearchOp::kNumMsgSearchOperators; j++) {
684       SetAvailable(i, j, false);
685       SetEnabled(i, j, false);
686       SetValidButNotShown(i, j, false);
687     }
688   m_numAvailAttribs =
689       0;  // # of attributes marked with at least one available operator
690   // assume default is Subject, which it is for mail and news search
691   // it's not for LDAP, so we'll call SetDefaultAttrib()
692   m_defaultAttrib = nsMsgSearchAttrib::Subject;
693 }
694 
NS_IMPL_ISUPPORTS(nsMsgSearchValidityTable,nsIMsgSearchValidityTable)695 NS_IMPL_ISUPPORTS(nsMsgSearchValidityTable, nsIMsgSearchValidityTable)
696 
697 nsresult nsMsgSearchValidityTable::GetNumAvailAttribs(int32_t* aResult) {
698   m_numAvailAttribs = 0;
699   for (int i = 0; i < nsMsgSearchAttrib::kNumMsgSearchAttributes; i++)
700     for (int j = 0; j < nsMsgSearchOp::kNumMsgSearchOperators; j++) {
701       bool available;
702       GetAvailable(i, j, &available);
703       if (available) {
704         m_numAvailAttribs++;
705         break;
706       }
707     }
708   *aResult = m_numAvailAttribs;
709   return NS_OK;
710 }
711 
GetAvailableAttributes(nsTArray<nsMsgSearchAttribValue> & aResult)712 nsresult nsMsgSearchValidityTable::GetAvailableAttributes(
713     nsTArray<nsMsgSearchAttribValue>& aResult) {
714   aResult.Clear();
715   int32_t i, j;
716   for (i = 0; i < nsMsgSearchAttrib::kNumMsgSearchAttributes; i++) {
717     for (j = 0; j < nsMsgSearchOp::kNumMsgSearchOperators; j++) {
718       if (m_table[i][j].bitAvailable) {
719         aResult.AppendElement(static_cast<nsMsgSearchAttribValue>(i));
720         break;
721       }
722     }
723   }
724   return NS_OK;
725 }
726 
GetAvailableOperators(nsMsgSearchAttribValue aAttribute,nsTArray<nsMsgSearchOpValue> & aResult)727 nsresult nsMsgSearchValidityTable::GetAvailableOperators(
728     nsMsgSearchAttribValue aAttribute, nsTArray<nsMsgSearchOpValue>& aResult) {
729   aResult.Clear();
730 
731   nsMsgSearchAttribValue attr;
732   if (aAttribute == nsMsgSearchAttrib::Default)
733     attr = m_defaultAttrib;
734   else
735     attr = std::min(aAttribute,
736                     (nsMsgSearchAttribValue)nsMsgSearchAttrib::OtherHeader);
737 
738   int32_t i;
739   for (i = 0; i < nsMsgSearchOp::kNumMsgSearchOperators; i++) {
740     if (m_table[attr][i].bitAvailable) {
741       aResult.AppendElement(static_cast<nsMsgSearchOpValue>(i));
742     }
743   }
744   return NS_OK;
745 }
746 
747 NS_IMETHODIMP
SetDefaultAttrib(nsMsgSearchAttribValue aAttribute)748 nsMsgSearchValidityTable::SetDefaultAttrib(nsMsgSearchAttribValue aAttribute) {
749   m_defaultAttrib = aAttribute;
750   return NS_OK;
751 }
752 
nsMsgSearchValidityManager()753 nsMsgSearchValidityManager::nsMsgSearchValidityManager() {}
754 
~nsMsgSearchValidityManager()755 nsMsgSearchValidityManager::~nsMsgSearchValidityManager() {
756   // tables released by nsCOMPtr
757 }
758 
NS_IMPL_ISUPPORTS(nsMsgSearchValidityManager,nsIMsgSearchValidityManager)759 NS_IMPL_ISUPPORTS(nsMsgSearchValidityManager, nsIMsgSearchValidityManager)
760 
761 //-----------------------------------------------------------------------------
762 // Bottleneck accesses to the objects so we can allocate and initialize them
763 // lazily. This way, there's no heap overhead for the validity tables until the
764 // user actually searches that scope.
765 //-----------------------------------------------------------------------------
766 
767 NS_IMETHODIMP nsMsgSearchValidityManager::GetTable(
768     int whichTable, nsIMsgSearchValidityTable** ppOutTable) {
769   NS_ENSURE_ARG_POINTER(ppOutTable);
770 
771   nsresult rv;
772   *ppOutTable = nullptr;
773 
774   nsCOMPtr<nsIPrefBranch> pref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
775   nsCString customHeaders;
776   if (NS_SUCCEEDED(rv)) pref->GetCharPref(PREF_CUSTOM_HEADERS, customHeaders);
777 
778   switch (whichTable) {
779     case nsMsgSearchScope::offlineMail:
780       if (!m_offlineMailTable) rv = InitOfflineMailTable();
781       if (m_offlineMailTable)
782         rv = SetOtherHeadersInTable(m_offlineMailTable, customHeaders.get());
783       *ppOutTable = m_offlineMailTable;
784       break;
785     case nsMsgSearchScope::offlineMailFilter:
786       if (!m_offlineMailFilterTable) rv = InitOfflineMailFilterTable();
787       if (m_offlineMailFilterTable)
788         rv = SetOtherHeadersInTable(m_offlineMailFilterTable,
789                                     customHeaders.get());
790       *ppOutTable = m_offlineMailFilterTable;
791       break;
792     case nsMsgSearchScope::onlineMail:
793       if (!m_onlineMailTable) rv = InitOnlineMailTable();
794       if (m_onlineMailTable)
795         rv = SetOtherHeadersInTable(m_onlineMailTable, customHeaders.get());
796       *ppOutTable = m_onlineMailTable;
797       break;
798     case nsMsgSearchScope::onlineMailFilter:
799       if (!m_onlineMailFilterTable) rv = InitOnlineMailFilterTable();
800       if (m_onlineMailFilterTable)
801         rv = SetOtherHeadersInTable(m_onlineMailFilterTable,
802                                     customHeaders.get());
803       *ppOutTable = m_onlineMailFilterTable;
804       break;
805     case nsMsgSearchScope::news:
806       if (!m_newsTable) rv = InitNewsTable();
807       if (m_newsTable)
808         rv = SetOtherHeadersInTable(m_newsTable, customHeaders.get());
809       *ppOutTable = m_newsTable;
810       break;
811     case nsMsgSearchScope::newsFilter:
812       if (!m_newsFilterTable) rv = InitNewsFilterTable();
813       if (m_newsFilterTable)
814         rv = SetOtherHeadersInTable(m_newsFilterTable, customHeaders.get());
815       *ppOutTable = m_newsFilterTable;
816       break;
817     case nsMsgSearchScope::localNews:
818       if (!m_localNewsTable) rv = InitLocalNewsTable();
819       if (m_localNewsTable)
820         rv = SetOtherHeadersInTable(m_localNewsTable, customHeaders.get());
821       *ppOutTable = m_localNewsTable;
822       break;
823     case nsMsgSearchScope::localNewsJunk:
824       if (!m_localNewsJunkTable) rv = InitLocalNewsJunkTable();
825       if (m_localNewsJunkTable)
826         rv = SetOtherHeadersInTable(m_localNewsJunkTable, customHeaders.get());
827       *ppOutTable = m_localNewsJunkTable;
828       break;
829     case nsMsgSearchScope::localNewsBody:
830       if (!m_localNewsBodyTable) rv = InitLocalNewsBodyTable();
831       if (m_localNewsBodyTable)
832         rv = SetOtherHeadersInTable(m_localNewsBodyTable, customHeaders.get());
833       *ppOutTable = m_localNewsBodyTable;
834       break;
835     case nsMsgSearchScope::localNewsJunkBody:
836       if (!m_localNewsJunkBodyTable) rv = InitLocalNewsJunkBodyTable();
837       if (m_localNewsJunkBodyTable)
838         rv = SetOtherHeadersInTable(m_localNewsJunkBodyTable,
839                                     customHeaders.get());
840       *ppOutTable = m_localNewsJunkBodyTable;
841       break;
842 
843     case nsMsgSearchScope::onlineManual:
844       if (!m_onlineManualFilterTable) rv = InitOnlineManualFilterTable();
845       if (m_onlineManualFilterTable)
846         rv = SetOtherHeadersInTable(m_onlineManualFilterTable,
847                                     customHeaders.get());
848       *ppOutTable = m_onlineManualFilterTable;
849       break;
850     case nsMsgSearchScope::LDAP:
851       if (!m_ldapTable) rv = InitLdapTable();
852       *ppOutTable = m_ldapTable;
853       break;
854     case nsMsgSearchScope::LDAPAnd:
855       if (!m_ldapAndTable) rv = InitLdapAndTable();
856       *ppOutTable = m_ldapAndTable;
857       break;
858     case nsMsgSearchScope::LocalAB:
859       if (!m_localABTable) rv = InitLocalABTable();
860       *ppOutTable = m_localABTable;
861       break;
862     case nsMsgSearchScope::LocalABAnd:
863       if (!m_localABAndTable) rv = InitLocalABAndTable();
864       *ppOutTable = m_localABAndTable;
865       break;
866     default:
867       NS_ASSERTION(false, "invalid table type");
868       rv = NS_MSG_ERROR_INVALID_SEARCH_TERM;
869   }
870 
871   NS_IF_ADDREF(*ppOutTable);  // Was populated from member variable.
872   return rv;
873 }
874 
875 // mapping between ordered attribute values, and property strings
876 // see search-attributes.properties
877 static struct {
878   nsMsgSearchAttribValue id;
879   const char* property;
880 } nsMsgSearchAttribMap[] = {
881     {nsMsgSearchAttrib::Subject, "Subject"},
882     {nsMsgSearchAttrib::Sender, "From"},
883     {nsMsgSearchAttrib::Body, "Body"},
884     {nsMsgSearchAttrib::Date, "Date"},
885     {nsMsgSearchAttrib::Priority, "Priority"},
886     {nsMsgSearchAttrib::MsgStatus, "Status"},
887     {nsMsgSearchAttrib::To, "To"},
888     {nsMsgSearchAttrib::CC, "Cc"},
889     {nsMsgSearchAttrib::ToOrCC, "ToOrCc"},
890     {nsMsgSearchAttrib::AgeInDays, "AgeInDays"},
891     {nsMsgSearchAttrib::Size, "SizeKB"},
892     {nsMsgSearchAttrib::Keywords, "Tags"},
893     {nsMsgSearchAttrib::Name, "AnyName"},
894     {nsMsgSearchAttrib::DisplayName, "DisplayName"},
895     {nsMsgSearchAttrib::Nickname, "Nickname"},
896     {nsMsgSearchAttrib::ScreenName, "ScreenName"},
897     {nsMsgSearchAttrib::Email, "Email"},
898     {nsMsgSearchAttrib::AdditionalEmail, "AdditionalEmail"},
899     {nsMsgSearchAttrib::PhoneNumber, "AnyNumber"},
900     {nsMsgSearchAttrib::WorkPhone, "WorkPhone"},
901     {nsMsgSearchAttrib::HomePhone, "HomePhone"},
902     {nsMsgSearchAttrib::Fax, "Fax"},
903     {nsMsgSearchAttrib::Pager, "Pager"},
904     {nsMsgSearchAttrib::Mobile, "Mobile"},
905     {nsMsgSearchAttrib::City, "City"},
906     {nsMsgSearchAttrib::Street, "Street"},
907     {nsMsgSearchAttrib::Title, "Title"},
908     {nsMsgSearchAttrib::Organization, "Organization"},
909     {nsMsgSearchAttrib::Department, "Department"},
910     {nsMsgSearchAttrib::AllAddresses, "FromToCcOrBcc"},
911     {nsMsgSearchAttrib::JunkScoreOrigin, "JunkScoreOrigin"},
912     {nsMsgSearchAttrib::JunkPercent, "JunkPercent"},
913     {nsMsgSearchAttrib::HasAttachmentStatus, "AttachmentStatus"},
914     {nsMsgSearchAttrib::JunkStatus, "JunkStatus"},
915     {nsMsgSearchAttrib::Label, "Label"},
916     {nsMsgSearchAttrib::OtherHeader, "Customize"},
917     // the last id is -1 to denote end of table
918     {-1, ""}};
919 
920 NS_IMETHODIMP
GetAttributeProperty(nsMsgSearchAttribValue aSearchAttribute,nsAString & aProperty)921 nsMsgSearchValidityManager::GetAttributeProperty(
922     nsMsgSearchAttribValue aSearchAttribute, nsAString& aProperty) {
923   for (int32_t i = 0; nsMsgSearchAttribMap[i].id >= 0; ++i) {
924     if (nsMsgSearchAttribMap[i].id == aSearchAttribute) {
925       aProperty.Assign(NS_ConvertUTF8toUTF16(nsMsgSearchAttribMap[i].property));
926       return NS_OK;
927     }
928   }
929   return NS_ERROR_FAILURE;
930 }
931 
NewTable(nsIMsgSearchValidityTable ** aTable)932 nsresult nsMsgSearchValidityManager::NewTable(
933     nsIMsgSearchValidityTable** aTable) {
934   NS_ENSURE_ARG_POINTER(aTable);
935   NS_ADDREF(*aTable = new nsMsgSearchValidityTable);
936   return NS_OK;
937 }
938 
SetOtherHeadersInTable(nsIMsgSearchValidityTable * aTable,const char * customHeaders)939 nsresult nsMsgSearchValidityManager::SetOtherHeadersInTable(
940     nsIMsgSearchValidityTable* aTable, const char* customHeaders) {
941   uint32_t customHeadersLength = strlen(customHeaders);
942   uint32_t numHeaders = 0;
943   if (customHeadersLength) {
944     nsAutoCString hdrStr(customHeaders);
945     hdrStr.StripWhitespace();  // remove whitespace before parsing
946     char* newStr = hdrStr.BeginWriting();
947     char* token = NS_strtok(":", &newStr);
948     while (token) {
949       numHeaders++;
950       token = NS_strtok(":", &newStr);
951     }
952   }
953 
954   NS_ASSERTION(nsMsgSearchAttrib::OtherHeader + numHeaders <
955                    nsMsgSearchAttrib::kNumMsgSearchAttributes,
956                "more headers than the table can hold");
957 
958   uint32_t maxHdrs =
959       std::min(nsMsgSearchAttrib::OtherHeader + numHeaders + 1,
960                (uint32_t)nsMsgSearchAttrib::kNumMsgSearchAttributes);
961   for (uint32_t i = nsMsgSearchAttrib::OtherHeader + 1; i < maxHdrs; i++) {
962     // clang-format off
963     aTable->SetAvailable(i, nsMsgSearchOp::Contains, 1);  // added for arbitrary headers
964     aTable->SetEnabled  (i, nsMsgSearchOp::Contains, 1);
965     aTable->SetAvailable(i, nsMsgSearchOp::DoesntContain, 1);
966     aTable->SetEnabled  (i, nsMsgSearchOp::DoesntContain, 1);
967     aTable->SetAvailable(i, nsMsgSearchOp::Is, 1);
968     aTable->SetEnabled  (i, nsMsgSearchOp::Is, 1);
969     aTable->SetAvailable(i, nsMsgSearchOp::Isnt, 1);
970     aTable->SetEnabled  (i, nsMsgSearchOp::Isnt, 1);
971     // clang-format on
972   }
973   // because custom headers can change; so reset the table for those which are
974   // no longer used.
975   for (uint32_t j = maxHdrs; j < nsMsgSearchAttrib::kNumMsgSearchAttributes;
976        j++) {
977     for (uint32_t k = 0; k < nsMsgSearchOp::kNumMsgSearchOperators; k++) {
978       aTable->SetAvailable(j, k, 0);
979       aTable->SetEnabled(j, k, 0);
980     }
981   }
982   return NS_OK;
983 }
984 
EnableDirectoryAttribute(nsIMsgSearchValidityTable * table,nsMsgSearchAttribValue aSearchAttrib)985 nsresult nsMsgSearchValidityManager::EnableDirectoryAttribute(
986     nsIMsgSearchValidityTable* table, nsMsgSearchAttribValue aSearchAttrib) {
987   // clang-format off
988   table->SetAvailable(aSearchAttrib, nsMsgSearchOp::Contains, 1);
989   table->SetEnabled  (aSearchAttrib, nsMsgSearchOp::Contains, 1);
990   table->SetAvailable(aSearchAttrib, nsMsgSearchOp::DoesntContain, 1);
991   table->SetEnabled  (aSearchAttrib, nsMsgSearchOp::DoesntContain, 1);
992   table->SetAvailable(aSearchAttrib, nsMsgSearchOp::Is, 1);
993   table->SetEnabled  (aSearchAttrib, nsMsgSearchOp::Is, 1);
994   table->SetAvailable(aSearchAttrib, nsMsgSearchOp::Isnt, 1);
995   table->SetEnabled  (aSearchAttrib, nsMsgSearchOp::Isnt, 1);
996   table->SetAvailable(aSearchAttrib, nsMsgSearchOp::BeginsWith, 1);
997   table->SetEnabled  (aSearchAttrib, nsMsgSearchOp::BeginsWith, 1);
998   table->SetAvailable(aSearchAttrib, nsMsgSearchOp::EndsWith, 1);
999   table->SetEnabled  (aSearchAttrib, nsMsgSearchOp::EndsWith, 1);
1000   table->SetAvailable(aSearchAttrib, nsMsgSearchOp::SoundsLike, 1);
1001   table->SetEnabled  (aSearchAttrib, nsMsgSearchOp::SoundsLike, 1);
1002   // clang-format on
1003   return NS_OK;
1004 }
1005 
InitLdapTable()1006 nsresult nsMsgSearchValidityManager::InitLdapTable() {
1007   NS_ASSERTION(!m_ldapTable, "don't call this twice!");
1008 
1009   nsresult rv = NewTable(getter_AddRefs(m_ldapTable));
1010   NS_ENSURE_SUCCESS(rv, rv);
1011 
1012   rv = SetUpABTable(m_ldapTable, true);
1013   NS_ENSURE_SUCCESS(rv, rv);
1014   return rv;
1015 }
1016 
InitLdapAndTable()1017 nsresult nsMsgSearchValidityManager::InitLdapAndTable() {
1018   NS_ASSERTION(!m_ldapAndTable, "don't call this twice!");
1019 
1020   nsresult rv = NewTable(getter_AddRefs(m_ldapAndTable));
1021   NS_ENSURE_SUCCESS(rv, rv);
1022 
1023   rv = SetUpABTable(m_ldapAndTable, false);
1024   NS_ENSURE_SUCCESS(rv, rv);
1025   return rv;
1026 }
1027 
InitLocalABTable()1028 nsresult nsMsgSearchValidityManager::InitLocalABTable() {
1029   NS_ASSERTION(!m_localABTable, "don't call this twice!");
1030 
1031   nsresult rv = NewTable(getter_AddRefs(m_localABTable));
1032   NS_ENSURE_SUCCESS(rv, rv);
1033 
1034   rv = SetUpABTable(m_localABTable, true);
1035   NS_ENSURE_SUCCESS(rv, rv);
1036   return rv;
1037 }
1038 
InitLocalABAndTable()1039 nsresult nsMsgSearchValidityManager::InitLocalABAndTable() {
1040   NS_ASSERTION(!m_localABAndTable, "don't call this twice!");
1041 
1042   nsresult rv = NewTable(getter_AddRefs(m_localABAndTable));
1043   NS_ENSURE_SUCCESS(rv, rv);
1044 
1045   rv = SetUpABTable(m_localABAndTable, false);
1046   NS_ENSURE_SUCCESS(rv, rv);
1047   return rv;
1048 }
1049 
SetUpABTable(nsIMsgSearchValidityTable * aTable,bool isOrTable)1050 nsresult nsMsgSearchValidityManager::SetUpABTable(
1051     nsIMsgSearchValidityTable* aTable, bool isOrTable) {
1052   nsresult rv = aTable->SetDefaultAttrib(
1053       isOrTable ? nsMsgSearchAttrib::Name : nsMsgSearchAttrib::DisplayName);
1054   NS_ENSURE_SUCCESS(rv, rv);
1055 
1056   if (isOrTable) {
1057     rv = EnableDirectoryAttribute(aTable, nsMsgSearchAttrib::Name);
1058     NS_ENSURE_SUCCESS(rv, rv);
1059 
1060     rv = EnableDirectoryAttribute(aTable, nsMsgSearchAttrib::PhoneNumber);
1061     NS_ENSURE_SUCCESS(rv, rv);
1062   }
1063 
1064   rv = EnableDirectoryAttribute(aTable, nsMsgSearchAttrib::DisplayName);
1065   NS_ENSURE_SUCCESS(rv, rv);
1066 
1067   rv = EnableDirectoryAttribute(aTable, nsMsgSearchAttrib::Email);
1068   NS_ENSURE_SUCCESS(rv, rv);
1069 
1070   rv = EnableDirectoryAttribute(aTable, nsMsgSearchAttrib::AdditionalEmail);
1071   NS_ENSURE_SUCCESS(rv, rv);
1072 
1073   rv = EnableDirectoryAttribute(aTable, nsMsgSearchAttrib::ScreenName);
1074   NS_ENSURE_SUCCESS(rv, rv);
1075 
1076   rv = EnableDirectoryAttribute(aTable, nsMsgSearchAttrib::Street);
1077   NS_ENSURE_SUCCESS(rv, rv);
1078 
1079   rv = EnableDirectoryAttribute(aTable, nsMsgSearchAttrib::City);
1080   NS_ENSURE_SUCCESS(rv, rv);
1081 
1082   rv = EnableDirectoryAttribute(aTable, nsMsgSearchAttrib::Title);
1083   NS_ENSURE_SUCCESS(rv, rv);
1084 
1085   rv = EnableDirectoryAttribute(aTable, nsMsgSearchAttrib::Organization);
1086   NS_ENSURE_SUCCESS(rv, rv);
1087 
1088   rv = EnableDirectoryAttribute(aTable, nsMsgSearchAttrib::Department);
1089   NS_ENSURE_SUCCESS(rv, rv);
1090 
1091   rv = EnableDirectoryAttribute(aTable, nsMsgSearchAttrib::Nickname);
1092   NS_ENSURE_SUCCESS(rv, rv);
1093 
1094   rv = EnableDirectoryAttribute(aTable, nsMsgSearchAttrib::WorkPhone);
1095   NS_ENSURE_SUCCESS(rv, rv);
1096 
1097   rv = EnableDirectoryAttribute(aTable, nsMsgSearchAttrib::HomePhone);
1098   NS_ENSURE_SUCCESS(rv, rv);
1099 
1100   rv = EnableDirectoryAttribute(aTable, nsMsgSearchAttrib::Fax);
1101   NS_ENSURE_SUCCESS(rv, rv);
1102 
1103   rv = EnableDirectoryAttribute(aTable, nsMsgSearchAttrib::Pager);
1104   NS_ENSURE_SUCCESS(rv, rv);
1105 
1106   rv = EnableDirectoryAttribute(aTable, nsMsgSearchAttrib::Mobile);
1107   NS_ENSURE_SUCCESS(rv, rv);
1108 
1109   return rv;
1110 }
1111