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 #include "msgCore.h"
6 #include "nsMsgSearchAdapter.h"
7 #include "nsUnicharUtils.h"
8 #include "nsMsgSearchScopeTerm.h"
9 #include "nsMsgResultElement.h"
10 #include "nsMsgSearchTerm.h"
11 #include "nsIMsgHdr.h"
12 #include "nsMsgSearchNews.h"
13 #include "nsIDBFolderInfo.h"
14 #include "prprf.h"
15 #include "nsIMsgDatabase.h"
16 #include "nsMemory.h"
17 #include <ctype.h>
18 
19 // Implementation of search for IMAP mail folders
20 
21 // Implementation of search for newsgroups
22 
23 //-----------------------------------------------------------------------------
24 //----------- Adapter class for searching XPAT-capable news servers -----------
25 //-----------------------------------------------------------------------------
26 
27 const char* nsMsgSearchNews::m_kNntpFrom = "FROM ";
28 const char* nsMsgSearchNews::m_kNntpSubject = "SUBJECT ";
29 const char* nsMsgSearchNews::m_kTermSeparator = "/";
30 
nsMsgSearchNews(nsMsgSearchScopeTerm * scope,nsTArray<RefPtr<nsIMsgSearchTerm>> const & termList)31 nsMsgSearchNews::nsMsgSearchNews(
32     nsMsgSearchScopeTerm* scope,
33     nsTArray<RefPtr<nsIMsgSearchTerm>> const& termList)
34     : nsMsgSearchAdapter(scope, termList) {
35   m_searchType = ST_UNINITIALIZED;
36 }
37 
~nsMsgSearchNews()38 nsMsgSearchNews::~nsMsgSearchNews() {}
39 
ValidateTerms()40 nsresult nsMsgSearchNews::ValidateTerms() {
41   nsresult err = nsMsgSearchAdapter::ValidateTerms();
42   if (NS_OK == err) {
43     err = Encode(&m_encoding);
44   }
45 
46   return err;
47 }
48 
Search(bool * aDone)49 nsresult nsMsgSearchNews::Search(bool* aDone) {
50   // the state machine runs in the news: handler
51   nsresult err = NS_ERROR_NOT_IMPLEMENTED;
52   return err;
53 }
54 
EncodeToWildmat(const char16_t * value)55 char16_t* nsMsgSearchNews::EncodeToWildmat(const char16_t* value) {
56   // Here we take advantage of XPAT's use of the wildmat format, which allows
57   // a case-insensitive match by specifying each case possibility for each
58   // character So, "FooBar" is encoded as "[Ff][Oo][Bb][Aa][Rr]"
59 
60   char16_t* caseInsensitiveValue =
61       (char16_t*)moz_xmalloc(sizeof(char16_t) * ((4 * NS_strlen(value)) + 1));
62   if (caseInsensitiveValue) {
63     char16_t* walkValue = caseInsensitiveValue;
64     while (*value) {
65       if (isalpha(*value)) {
66         *walkValue++ = (char16_t)'[';
67         *walkValue++ = ToUpperCase((char16_t)*value);
68         *walkValue++ = ToLowerCase((char16_t)*value);
69         *walkValue++ = (char16_t)']';
70       } else
71         *walkValue++ = *value;
72       value++;
73     }
74     *walkValue = 0;
75   }
76   return caseInsensitiveValue;
77 }
78 
EncodeTerm(nsIMsgSearchTerm * term)79 char* nsMsgSearchNews::EncodeTerm(nsIMsgSearchTerm* term) {
80   // Develop an XPAT-style encoding for the search term
81 
82   NS_ASSERTION(term, "null term");
83   if (!term) return nullptr;
84 
85   // Find a string to represent the attribute
86   const char* attribEncoding = nullptr;
87   nsMsgSearchAttribValue attrib;
88 
89   term->GetAttrib(&attrib);
90 
91   switch (attrib) {
92     case nsMsgSearchAttrib::Sender:
93       attribEncoding = m_kNntpFrom;
94       break;
95     case nsMsgSearchAttrib::Subject:
96       attribEncoding = m_kNntpSubject;
97       break;
98     default:
99       nsCString header;
100       term->GetArbitraryHeader(header);
101       if (header.IsEmpty()) {
102         NS_ASSERTION(false, "malformed search");  // malformed search term?
103         return nullptr;
104       }
105       attribEncoding = header.get();
106   }
107 
108   // Build a string to represent the string pattern
109   bool leadingStar = false;
110   bool trailingStar = false;
111   nsMsgSearchOpValue op;
112   term->GetOp(&op);
113 
114   switch (op) {
115     case nsMsgSearchOp::Contains:
116       leadingStar = true;
117       trailingStar = true;
118       break;
119     case nsMsgSearchOp::Is:
120       break;
121     case nsMsgSearchOp::BeginsWith:
122       trailingStar = true;
123       break;
124     case nsMsgSearchOp::EndsWith:
125       leadingStar = true;
126       break;
127     default:
128       NS_ASSERTION(false, "malformed search");  // malformed search term?
129       return nullptr;
130   }
131 
132   // ### i18N problem Get the csid from FE, which is the correct csid for term
133   //  int16 wincsid = INTL_GetCharSetID(INTL_DefaultTextWidgetCsidSel);
134 
135   // Do INTL_FormatNNTPXPATInRFC1522Format trick for non-ASCII string
136   //  unsigned char *intlNonRFC1522Value = INTL_FormatNNTPXPATInNonRFC1522Format
137   //  (wincsid, (unsigned char*)term->m_value.u.string);
138   nsCOMPtr<nsIMsgSearchValue> searchValue;
139 
140   nsresult rv = term->GetValue(getter_AddRefs(searchValue));
141   if (NS_FAILED(rv) || !searchValue) return nullptr;
142 
143   nsString intlNonRFC1522Value;
144   rv = searchValue->GetStr(intlNonRFC1522Value);
145   if (NS_FAILED(rv) || intlNonRFC1522Value.IsEmpty()) return nullptr;
146 
147   char16_t* caseInsensitiveValue = EncodeToWildmat(intlNonRFC1522Value.get());
148   if (!caseInsensitiveValue) return nullptr;
149 
150   // TO DO: Do INTL_FormatNNTPXPATInRFC1522Format trick for non-ASCII string
151   // Unfortunately, we currently do not handle xxx or xxx search in XPAT
152   // Need to add the INTL_FormatNNTPXPATInRFC1522Format call after we can do
153   // that so we should search a string in either RFC1522 format and non-RFC1522
154   // format
155 
156   char16_t* escapedValue = EscapeSearchUrl(caseInsensitiveValue);
157   free(caseInsensitiveValue);
158   if (!escapedValue) return nullptr;
159 
160   nsAutoCString pattern;
161 
162   if (leadingStar) pattern.Append('*');
163   pattern.Append(NS_ConvertUTF16toUTF8(escapedValue));
164   if (trailingStar) pattern.Append('*');
165 
166   // Combine the XPAT command syntax with the attribute and the pattern to
167   // form the term encoding
168   const char xpatTemplate[] = "XPAT %s 1- %s";
169   int termLength = (sizeof(xpatTemplate) - 1) + strlen(attribEncoding) +
170                    pattern.Length() + 1;
171   char* termEncoding = new char[termLength];
172   if (termEncoding)
173     PR_snprintf(termEncoding, termLength, xpatTemplate, attribEncoding,
174                 pattern.get());
175 
176   return termEncoding;
177 }
178 
GetEncoding(char ** result)179 nsresult nsMsgSearchNews::GetEncoding(char** result) {
180   NS_ENSURE_ARG(result);
181   *result = ToNewCString(m_encoding);
182   return (*result) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
183 }
184 
Encode(nsCString * outEncoding)185 nsresult nsMsgSearchNews::Encode(nsCString* outEncoding) {
186   NS_ASSERTION(outEncoding, "no out encoding");
187   if (!outEncoding) return NS_ERROR_NULL_POINTER;
188 
189   nsresult err = NS_OK;
190 
191   uint32_t numTerms = m_searchTerms.Length();
192 
193   char** intermediateEncodings = new char*[numTerms];
194   if (intermediateEncodings) {
195     // Build an XPAT command for each term
196     int encodingLength = 0;
197     for (uint32_t i = 0; i < numTerms; i++) {
198       nsIMsgSearchTerm* pTerm = m_searchTerms[i];
199       // set boolean OR term if any of the search terms are an OR...this only
200       // works if we are using homogeneous boolean operators.
201       bool isBooleanOpAnd;
202       pTerm->GetBooleanAnd(&isBooleanOpAnd);
203       m_searchType = isBooleanOpAnd ? ST_AND_SEARCH : ST_OR_SEARCH;
204 
205       intermediateEncodings[i] = EncodeTerm(pTerm);
206       if (intermediateEncodings[i])
207         encodingLength +=
208             strlen(intermediateEncodings[i]) + strlen(m_kTermSeparator);
209     }
210     encodingLength += strlen("?search");
211     // Combine all the term encodings into one big encoding
212     char* encoding = new char[encodingLength + 1];
213     if (encoding) {
214       PL_strcpy(encoding, "?search");
215 
216       for (uint32_t i = 0; i < numTerms; i++) {
217         if (intermediateEncodings[i]) {
218           PL_strcat(encoding, m_kTermSeparator);
219           PL_strcat(encoding, intermediateEncodings[i]);
220           delete[] intermediateEncodings[i];
221         }
222       }
223       *outEncoding = encoding;
224     } else
225       err = NS_ERROR_OUT_OF_MEMORY;
226   } else
227     err = NS_ERROR_OUT_OF_MEMORY;
228   delete[] intermediateEncodings;
229 
230   return err;
231 }
232 
AddHit(nsMsgKey key)233 NS_IMETHODIMP nsMsgSearchNews::AddHit(nsMsgKey key) {
234   m_candidateHits.AppendElement(key);
235   return NS_OK;
236 }
237 
238 /* void CurrentUrlDone (in nsresult exitCode); */
CurrentUrlDone(nsresult exitCode)239 NS_IMETHODIMP nsMsgSearchNews::CurrentUrlDone(nsresult exitCode) {
240   CollateHits();
241   ReportHits();
242   return NS_OK;
243 }
244 
245 #if 0   // need to switch this to a notify stop loading handler, I think.
246 void nsMsgSearchNews::PreExitFunction (URL_Struct * /*url*/, int status, MWContext *context)
247 {
248   MSG_SearchFrame *frame = MSG_SearchFrame::FromContext (context);
249   nsMsgSearchNews *adapter = (nsMsgSearchNews*) frame->GetRunningAdapter();
250   adapter->CollateHits();
251   adapter->ReportHits();
252 
253   if (status == MK_INTERRUPTED)
254   {
255     adapter->Abort();
256     frame->EndCylonMode();
257   }
258   else
259   {
260     frame->m_idxRunningScope++;
261     if (frame->m_idxRunningScope >= frame->m_scopeList.Count())
262       frame->EndCylonMode();
263   }
264 }
265 #endif  // 0
266 
CollateHits()267 void nsMsgSearchNews::CollateHits() {
268   // Since the XPAT commands are processed one at a time, the result set for the
269   // entire query is the intersection of results for each XPAT command if an AND
270   // search, otherwise we want the union of all the search hits (minus the
271   // duplicates of course).
272 
273   uint32_t size = m_candidateHits.Length();
274   if (!size) return;
275 
276   // Sort the article numbers first, so it's easy to tell how many hits
277   // on a given article we got
278   m_candidateHits.Sort();
279 
280   // For an OR search we only need to count the first occurrence of a candidate.
281   uint32_t termCount = 1;
282   MOZ_ASSERT(m_searchType != ST_UNINITIALIZED,
283              "m_searchType accessed without being set");
284   if (m_searchType == ST_AND_SEARCH) {
285     // We have a traditional AND search which must be collated. In order to
286     // get promoted into the hits list, a candidate article number must appear
287     // in the results of each XPAT command. So if we fire 3 XPAT commands (one
288     // per search term), the article number must appear 3 times. If it appears
289     // fewer than 3 times, it matched some search terms, but not all.
290     termCount = m_searchTerms.Length();
291   }
292   uint32_t candidateCount = 0;
293   uint32_t candidate = m_candidateHits[0];
294   for (uint32_t index = 0; index < size; ++index) {
295     uint32_t possibleCandidate = m_candidateHits[index];
296     if (candidate == possibleCandidate) {
297       ++candidateCount;
298     } else {
299       candidateCount = 1;
300       candidate = possibleCandidate;
301     }
302     if (candidateCount == termCount) m_hits.AppendElement(candidate);
303   }
304 }
305 
ReportHits()306 void nsMsgSearchNews::ReportHits() {
307   nsCOMPtr<nsIMsgDatabase> db;
308   nsCOMPtr<nsIDBFolderInfo> folderInfo;
309   nsCOMPtr<nsIMsgFolder> scopeFolder;
310 
311   nsresult err = m_scope->GetFolder(getter_AddRefs(scopeFolder));
312   if (NS_SUCCEEDED(err) && scopeFolder) {
313     err = scopeFolder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo),
314                                             getter_AddRefs(db));
315   }
316 
317   if (db) {
318     uint32_t size = m_hits.Length();
319     for (uint32_t i = 0; i < size; ++i) {
320       nsCOMPtr<nsIMsgDBHdr> header;
321 
322       db->GetMsgHdrForKey(m_hits.ElementAt(i), getter_AddRefs(header));
323       if (header) ReportHit(header, scopeFolder);
324     }
325   }
326 }
327 
328 // ### this should take an nsIMsgFolder instead of a string location.
ReportHit(nsIMsgDBHdr * pHeaders,nsIMsgFolder * folder)329 void nsMsgSearchNews::ReportHit(nsIMsgDBHdr* pHeaders, nsIMsgFolder* folder) {
330   // this is totally filched from msg_SearchOfflineMail until I decide whether
331   // the right thing is to get them from the db or from NNTP
332   nsCOMPtr<nsIMsgSearchSession> session;
333   nsCOMPtr<nsIMsgFolder> scopeFolder;
334   m_scope->GetFolder(getter_AddRefs(scopeFolder));
335   m_scope->GetSearchSession(getter_AddRefs(session));
336   if (session) session->AddSearchHit(pHeaders, scopeFolder);
337 }
338 
InitNewsTable()339 nsresult nsMsgSearchValidityManager::InitNewsTable() {
340   NS_ASSERTION(nullptr == m_newsTable, "don't call this twice!");
341   nsresult rv = NewTable(getter_AddRefs(m_newsTable));
342 
343   if (NS_SUCCEEDED(rv)) {
344     // clang-format off
345     m_newsTable->SetAvailable(nsMsgSearchAttrib::Sender, nsMsgSearchOp::Contains, 1);
346     m_newsTable->SetEnabled  (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Contains, 1);
347     m_newsTable->SetAvailable(nsMsgSearchAttrib::Sender, nsMsgSearchOp::Is, 1);
348     m_newsTable->SetEnabled  (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Is, 1);
349     m_newsTable->SetAvailable(nsMsgSearchAttrib::Sender, nsMsgSearchOp::BeginsWith, 1);
350     m_newsTable->SetEnabled  (nsMsgSearchAttrib::Sender, nsMsgSearchOp::BeginsWith, 1);
351     m_newsTable->SetAvailable(nsMsgSearchAttrib::Sender, nsMsgSearchOp::EndsWith, 1);
352     m_newsTable->SetEnabled  (nsMsgSearchAttrib::Sender, nsMsgSearchOp::EndsWith, 1);
353 
354     m_newsTable->SetAvailable(nsMsgSearchAttrib::Subject, nsMsgSearchOp::Contains, 1);
355     m_newsTable->SetEnabled  (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Contains, 1);
356     m_newsTable->SetAvailable(nsMsgSearchAttrib::Subject, nsMsgSearchOp::Is, 1);
357     m_newsTable->SetEnabled  (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Is, 1);
358     m_newsTable->SetAvailable(nsMsgSearchAttrib::Subject, nsMsgSearchOp::BeginsWith, 1);
359     m_newsTable->SetEnabled  (nsMsgSearchAttrib::Subject, nsMsgSearchOp::BeginsWith, 1);
360     m_newsTable->SetAvailable(nsMsgSearchAttrib::Subject, nsMsgSearchOp::EndsWith, 1);
361     m_newsTable->SetEnabled  (nsMsgSearchAttrib::Subject, nsMsgSearchOp::EndsWith, 1);
362 
363 #if 0
364     // Size should be handled after the fact...
365     m_newsTable->SetAvailable(nsMsgSearchAttrib::Size, nsMsgSearchOp::IsGreaterThan, 1);
366     m_newsTable->SetEnabled  (nsMsgSearchAttrib::Size, nsMsgSearchOp::IsGreaterThan, 1);
367     m_newsTable->SetAvailable(nsMsgSearchAttrib::Size, nsMsgSearchOp::IsLessThan, 1);
368     m_newsTable->SetEnabled  (nsMsgSearchAttrib::Size, nsMsgSearchOp::IsLessThan, 1);
369 #endif
370     m_newsTable->SetAvailable(nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Contains, 1);
371     m_newsTable->SetEnabled  (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Contains, 1);
372     m_newsTable->SetAvailable(nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Is, 1);
373     m_newsTable->SetEnabled  (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Is, 1);
374     m_newsTable->SetAvailable(nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::BeginsWith, 1);
375     m_newsTable->SetEnabled  (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::BeginsWith, 1);
376     m_newsTable->SetAvailable(nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::EndsWith, 1);
377     m_newsTable->SetEnabled  (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::EndsWith, 1);
378     // clang-format on
379   }
380 
381   return rv;
382 }
383 
InitNewsFilterTable()384 nsresult nsMsgSearchValidityManager::InitNewsFilterTable() {
385   NS_ASSERTION(nullptr == m_newsFilterTable,
386                "news filter table already initted");
387   nsresult rv = NewTable(getter_AddRefs(m_newsFilterTable));
388 
389   if (NS_SUCCEEDED(rv)) {
390     // clang-format off
391     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::Sender, nsMsgSearchOp::Contains, 1);
392     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Contains, 1);
393     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::Sender, nsMsgSearchOp::DoesntContain, 1);
394     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::Sender, nsMsgSearchOp::DoesntContain, 1);
395     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::Sender, nsMsgSearchOp::Is, 1);
396     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Is, 1);
397     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::Sender, nsMsgSearchOp::Isnt, 1);
398     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::Sender, nsMsgSearchOp::Isnt, 1);
399     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::Sender, nsMsgSearchOp::BeginsWith, 1);
400     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::Sender, nsMsgSearchOp::BeginsWith, 1);
401     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::Sender, nsMsgSearchOp::EndsWith, 1);
402     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::Sender, nsMsgSearchOp::EndsWith, 1);
403 
404     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::Sender, nsMsgSearchOp::IsInAB, 1);
405     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::Sender, nsMsgSearchOp::IsInAB, 1);
406     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::Sender, nsMsgSearchOp::IsntInAB, 1);
407     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::Sender, nsMsgSearchOp::IsntInAB, 1);
408 
409     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::Subject, nsMsgSearchOp::Contains, 1);
410     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Contains, 1);
411     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::Subject, nsMsgSearchOp::DoesntContain, 1);
412     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::Subject, nsMsgSearchOp::DoesntContain, 1);
413     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::Subject, nsMsgSearchOp::Is, 1);
414     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Is, 1);
415     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::Subject, nsMsgSearchOp::Isnt, 1);
416     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::Subject, nsMsgSearchOp::Isnt, 1);
417     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::Subject, nsMsgSearchOp::BeginsWith, 1);
418     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::Subject, nsMsgSearchOp::BeginsWith, 1);
419     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::Subject, nsMsgSearchOp::EndsWith, 1);
420     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::Subject, nsMsgSearchOp::EndsWith, 1);
421 
422     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::Date, nsMsgSearchOp::IsBefore, 1);
423     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::Date, nsMsgSearchOp::IsBefore, 1);
424     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::Date, nsMsgSearchOp::IsAfter, 1);
425     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::Date, nsMsgSearchOp::IsAfter, 1);
426     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::Date, nsMsgSearchOp::Is, 1);
427     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::Date, nsMsgSearchOp::Is, 1);
428     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::Date, nsMsgSearchOp::Isnt, 1);
429     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::Date, nsMsgSearchOp::Isnt, 1);
430 
431     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::Size, nsMsgSearchOp::IsGreaterThan, 1);
432     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::Size, nsMsgSearchOp::IsGreaterThan, 1);
433     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::Size, nsMsgSearchOp::IsLessThan, 1);
434     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::Size, nsMsgSearchOp::IsLessThan, 1);
435 
436     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Contains, 1);
437     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Contains, 1);
438     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::DoesntContain, 1);
439     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::DoesntContain, 1);
440     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Is, 1);
441     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Is, 1);
442     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Isnt, 1);
443     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::Isnt, 1);
444     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::BeginsWith, 1);
445     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::BeginsWith, 1);
446     m_newsFilterTable->SetAvailable(nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::EndsWith, 1);
447     m_newsFilterTable->SetEnabled  (nsMsgSearchAttrib::OtherHeader, nsMsgSearchOp::EndsWith, 1);
448     // clang-format on
449   }
450 
451   return rv;
452 }
453