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 "nsIMsgHdr.h"
8 #include "nsMsgUtils.h"
9 #include "nsMsgFolderFlags.h"
10 #include "nsMsgMessageFlags.h"
11 #include "nsString.h"
12 #include "nsIServiceManager.h"
13 #include "nsCOMPtr.h"
14 #include "nsIFolderLookupService.h"
15 #include "nsIImapUrl.h"
16 #include "nsIMailboxUrl.h"
17 #include "nsINntpUrl.h"
18 #include "nsMsgNewsCID.h"
19 #include "nsMsgLocalCID.h"
20 #include "nsMsgBaseCID.h"
21 #include "nsMsgImapCID.h"
22 #include "nsMsgI18N.h"
23 #include "nsNativeCharsetUtils.h"
24 #include "nsCharTraits.h"
25 #include "prprf.h"
26 #include "prmem.h"
27 #include "nsNetCID.h"
28 #include "nsIIOService.h"
29 #include "nsIMimeConverter.h"
30 #include "nsMsgMimeCID.h"
31 #include "nsIPrefService.h"
32 #include "nsIPrefBranch.h"
33 #include "nsISupportsPrimitives.h"
34 #include "nsIPrefLocalizedString.h"
35 #include "nsIRelativeFilePref.h"
36 #include "mozilla/nsRelativeFilePref.h"
37 #include "nsAppDirectoryServiceDefs.h"
38 #include "nsISpamSettings.h"
39 #include "nsICryptoHash.h"
40 #include "nsNativeCharsetUtils.h"
41 #include "nsDirectoryServiceUtils.h"
42 #include "nsDirectoryServiceDefs.h"
43 #include "nsIRssIncomingServer.h"
44 #include "nsIMsgFolder.h"
45 #include "nsIMsgProtocolInfo.h"
46 #include "nsIMsgMessageService.h"
47 #include "nsIMsgAccountManager.h"
48 #include "nsIOutputStream.h"
49 #include "nsMsgFileStream.h"
50 #include "nsIFileURL.h"
51 #include "nsNetUtil.h"
52 #include "nsProtocolProxyService.h"
53 #include "nsIProtocolProxyCallback.h"
54 #include "nsICancelable.h"
55 #include "nsIMsgDatabase.h"
56 #include "nsIMsgMailNewsUrl.h"
57 #include "nsIStringBundle.h"
58 #include "nsIMsgWindow.h"
59 #include "nsIWindowWatcher.h"
60 #include "nsIPrompt.h"
61 #include "nsIMsgSearchTerm.h"
62 #include "nsTextFormatter.h"
63 #include "nsIStreamListener.h"
64 #include "nsReadLine.h"
65 #include "nsILineInputStream.h"
66 #include "nsIParserUtils.h"
67 #include "nsICharsetConverterManager.h"
68 #include "nsIDocumentEncoder.h"
69 #include "mozilla/Services.h"
70 #include "locale.h"
71 #include "nsStringStream.h"
72 #include "nsIInputStreamPump.h"
73 #include "nsIInputStream.h"
74 #include "nsIChannel.h"
75 #include "nsIURIMutator.h"
76 #include "mozilla/Unused.h"
77 #include "mozilla/Preferences.h"
78 #include "mozilla/Encoding.h"
79 #include "mozilla/EncodingDetector.h"
80 #include "mozilla/UniquePtr.h"
81 #include "mozilla/Utf8.h"
82 
83 /* for logging to Error Console */
84 #include "nsIScriptError.h"
85 #include "nsIConsoleService.h"
86 
87 // Log an error string to the error console
88 // (adapted from nsContentUtils::LogSimpleConsoleError).
89 // Flag can indicate error, warning or info.
MsgLogToConsole4(const nsAString & aErrorText,const nsAString & aFilename,uint32_t aLinenumber,uint32_t aFlag)90 NS_MSG_BASE void MsgLogToConsole4(const nsAString& aErrorText,
91                                   const nsAString& aFilename,
92                                   uint32_t aLinenumber, uint32_t aFlag) {
93   nsCOMPtr<nsIScriptError> scriptError =
94       do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
95   if (NS_WARN_IF(!scriptError)) return;
96   nsCOMPtr<nsIConsoleService> console =
97       do_GetService(NS_CONSOLESERVICE_CONTRACTID);
98   if (NS_WARN_IF(!console)) return;
99   if (NS_FAILED(scriptError->Init(aErrorText, aFilename, EmptyString(),
100                                   aLinenumber, 0, aFlag, "mailnews", false,
101                                   false)))
102     return;
103   console->LogMessage(scriptError);
104   return;
105 }
106 
107 using namespace mozilla;
108 using namespace mozilla::net;
109 
110 #define ILLEGAL_FOLDER_CHARS ";#"
111 #define ILLEGAL_FOLDER_CHARS_AS_FIRST_LETTER "."
112 #define ILLEGAL_FOLDER_CHARS_AS_LAST_LETTER ".~ "
113 
GetMessageServiceContractIDForURI(const char * uri,nsCString & contractID)114 nsresult GetMessageServiceContractIDForURI(const char* uri,
115                                            nsCString& contractID) {
116   nsresult rv = NS_OK;
117   // Find protocol
118   nsAutoCString uriStr(uri);
119   int32_t pos = uriStr.FindChar(':');
120   if (pos == -1) return NS_ERROR_FAILURE;
121 
122   nsAutoCString protocol(StringHead(uriStr, pos));
123 
124   if (protocol.EqualsLiteral("file") &&
125       uriStr.Find("application/x-message-display") != -1)
126     protocol.AssignLiteral("mailbox");
127   // Build message service contractid
128   contractID = "@mozilla.org/messenger/messageservice;1?type=";
129   contractID += protocol.get();
130 
131   return rv;
132 }
133 
GetMessageServiceFromURI(const nsACString & uri,nsIMsgMessageService ** aMessageService)134 nsresult GetMessageServiceFromURI(const nsACString& uri,
135                                   nsIMsgMessageService** aMessageService) {
136   nsresult rv;
137 
138   nsAutoCString contractID;
139   rv = GetMessageServiceContractIDForURI(PromiseFlatCString(uri).get(),
140                                          contractID);
141   NS_ENSURE_SUCCESS(rv, rv);
142 
143   nsCOMPtr<nsIMsgMessageService> msgService =
144       do_GetService(contractID.get(), &rv);
145   NS_ENSURE_SUCCESS(rv, rv);
146 
147   msgService.forget(aMessageService);
148   return rv;
149 }
150 
GetMsgDBHdrFromURI(const nsACString & uri,nsIMsgDBHdr ** msgHdr)151 nsresult GetMsgDBHdrFromURI(const nsACString& uri, nsIMsgDBHdr** msgHdr) {
152   nsCOMPtr<nsIMsgMessageService> msgMessageService;
153   nsresult rv =
154       GetMessageServiceFromURI(uri, getter_AddRefs(msgMessageService));
155   NS_ENSURE_SUCCESS(rv, rv);
156   if (!msgMessageService) return NS_ERROR_FAILURE;
157 
158   return msgMessageService->MessageURIToMsgHdr(uri, msgHdr);
159 }
160 
161 // Where should this live? It's a utility used to convert a string priority,
162 //  e.g., "High, Low, Normal" to an enum.
163 // Perhaps we should have an interface that groups together all these
164 //  utilities...
NS_MsgGetPriorityFromString(const char * const priority,nsMsgPriorityValue & outPriority)165 nsresult NS_MsgGetPriorityFromString(const char* const priority,
166                                      nsMsgPriorityValue& outPriority) {
167   if (!priority) return NS_ERROR_NULL_POINTER;
168 
169   // Note: Checking the values separately and _before_ the names,
170   //        hoping for a much faster match;
171   //       Only _drawback_, as "priority" handling is not truly specified:
172   //        some software may have the number meanings reversed (1=Lowest) !?
173   if (PL_strchr(priority, '1'))
174     outPriority = nsMsgPriority::highest;
175   else if (PL_strchr(priority, '2'))
176     outPriority = nsMsgPriority::high;
177   else if (PL_strchr(priority, '3'))
178     outPriority = nsMsgPriority::normal;
179   else if (PL_strchr(priority, '4'))
180     outPriority = nsMsgPriority::low;
181   else if (PL_strchr(priority, '5'))
182     outPriority = nsMsgPriority::lowest;
183   else if (PL_strcasestr(priority, "Highest"))
184     outPriority = nsMsgPriority::highest;
185   // Important: "High" must be tested after "Highest" !
186   else if (PL_strcasestr(priority, "High") || PL_strcasestr(priority, "Urgent"))
187     outPriority = nsMsgPriority::high;
188   else if (PL_strcasestr(priority, "Normal"))
189     outPriority = nsMsgPriority::normal;
190   else if (PL_strcasestr(priority, "Lowest"))
191     outPriority = nsMsgPriority::lowest;
192   // Important: "Low" must be tested after "Lowest" !
193   else if (PL_strcasestr(priority, "Low") ||
194            PL_strcasestr(priority, "Non-urgent"))
195     outPriority = nsMsgPriority::low;
196   else
197     // "Default" case gets default value.
198     outPriority = nsMsgPriority::Default;
199 
200   return NS_OK;
201 }
202 
NS_MsgGetPriorityValueString(const nsMsgPriorityValue p,nsACString & outValueString)203 nsresult NS_MsgGetPriorityValueString(const nsMsgPriorityValue p,
204                                       nsACString& outValueString) {
205   switch (p) {
206     case nsMsgPriority::highest:
207       outValueString.Assign('1');
208       break;
209     case nsMsgPriority::high:
210       outValueString.Assign('2');
211       break;
212     case nsMsgPriority::normal:
213       outValueString.Assign('3');
214       break;
215     case nsMsgPriority::low:
216       outValueString.Assign('4');
217       break;
218     case nsMsgPriority::lowest:
219       outValueString.Assign('5');
220       break;
221     case nsMsgPriority::none:
222     case nsMsgPriority::notSet:
223       // Note: '0' is a "fake" value; we expect to never be in this case.
224       outValueString.Assign('0');
225       break;
226     default:
227       NS_ASSERTION(false, "invalid priority value");
228   }
229 
230   return NS_OK;
231 }
232 
NS_MsgGetUntranslatedPriorityName(const nsMsgPriorityValue p,nsACString & outName)233 nsresult NS_MsgGetUntranslatedPriorityName(const nsMsgPriorityValue p,
234                                            nsACString& outName) {
235   switch (p) {
236     case nsMsgPriority::highest:
237       outName.AssignLiteral("Highest");
238       break;
239     case nsMsgPriority::high:
240       outName.AssignLiteral("High");
241       break;
242     case nsMsgPriority::normal:
243       outName.AssignLiteral("Normal");
244       break;
245     case nsMsgPriority::low:
246       outName.AssignLiteral("Low");
247       break;
248     case nsMsgPriority::lowest:
249       outName.AssignLiteral("Lowest");
250       break;
251     case nsMsgPriority::none:
252     case nsMsgPriority::notSet:
253       // Note: 'None' is a "fake" value; we expect to never be in this case.
254       outName.AssignLiteral("None");
255       break;
256     default:
257       NS_ASSERTION(false, "invalid priority value");
258   }
259 
260   return NS_OK;
261 }
262 
263 /* this used to be XP_StringHash2 from xp_hash.c */
264 /* phong's linear congruential hash  */
StringHash(const char * ubuf,int32_t len=-1)265 static uint32_t StringHash(const char* ubuf, int32_t len = -1) {
266   unsigned char* buf = (unsigned char*)ubuf;
267   uint32_t h = 1;
268   unsigned char* end = buf + (len == -1 ? strlen(ubuf) : len);
269   while (buf < end) {
270     h = 0x63c63cd9 * h + 0x9c39c33d + (int32_t)*buf;
271     buf++;
272   }
273   return h;
274 }
275 
StringHash(const nsAutoString & str)276 inline uint32_t StringHash(const nsAutoString& str) {
277   const char16_t* strbuf = str.get();
278   return StringHash(reinterpret_cast<const char*>(strbuf), str.Length() * 2);
279 }
280 
281 /* Utility functions used in a few places in mailnews */
MsgFindCharInSet(const nsCString & aString,const char * aChars,uint32_t aOffset)282 int32_t MsgFindCharInSet(const nsCString& aString, const char* aChars,
283                          uint32_t aOffset) {
284   return aString.FindCharInSet(aChars, aOffset);
285 }
286 
MsgFindCharInSet(const nsString & aString,const char * aChars,uint32_t aOffset)287 int32_t MsgFindCharInSet(const nsString& aString, const char* aChars,
288                          uint32_t aOffset) {
289   return aString.FindCharInSet(aChars, aOffset);
290 }
291 
ConvertibleToNative(const nsAutoString & str)292 static bool ConvertibleToNative(const nsAutoString& str) {
293   nsAutoCString native;
294   nsAutoString roundTripped;
295   NS_CopyUnicodeToNative(str, native);
296   NS_CopyNativeToUnicode(native, roundTripped);
297   return str.Equals(roundTripped);
298 }
299 
300 #if defined(XP_UNIX)
301 const static uint32_t MAX_LEN = 55;
302 #elif defined(XP_WIN)
303 const static uint32_t MAX_LEN = 55;
304 #else
305 #  error need_to_define_your_max_filename_length
306 #endif
307 
NS_MsgHashIfNecessary(nsAutoCString & name)308 nsresult NS_MsgHashIfNecessary(nsAutoCString& name) {
309   if (name.IsEmpty()) return NS_OK;  // Nothing to do.
310   nsAutoCString str(name);
311 
312   // Given a filename, make it safe for filesystem
313   // certain filenames require hashing because they
314   // are too long or contain illegal characters
315   int32_t illegalCharacterIndex = MsgFindCharInSet(
316       str, FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS ILLEGAL_FOLDER_CHARS, 0);
317 
318   // Need to check the first ('.') and last ('.', '~' and ' ') char
319   if (illegalCharacterIndex == -1) {
320     int32_t lastIndex = str.Length() - 1;
321     if (nsLiteralCString(ILLEGAL_FOLDER_CHARS_AS_FIRST_LETTER)
322             .FindChar(str[0]) != -1)
323       illegalCharacterIndex = 0;
324     else if (nsLiteralCString(ILLEGAL_FOLDER_CHARS_AS_LAST_LETTER)
325                  .FindChar(str[lastIndex]) != -1)
326       illegalCharacterIndex = lastIndex;
327     else
328       illegalCharacterIndex = -1;
329   }
330 
331   char hashedname[MAX_LEN + 1];
332   if (illegalCharacterIndex == -1) {
333     // no illegal chars, it's just too long
334     // keep the initial part of the string, but hash to make it fit
335     if (str.Length() > MAX_LEN) {
336       PL_strncpy(hashedname, str.get(), MAX_LEN + 1);
337       PR_snprintf(hashedname + MAX_LEN - 8, 9, "%08lx",
338                   (unsigned long)StringHash(str.get()));
339       name = hashedname;
340     }
341   } else {
342     // found illegal chars, hash the whole thing
343     // if we do substitution, then hash, two strings
344     // could hash to the same value.
345     // for example, on mac:  "foo__bar", "foo:_bar", "foo::bar"
346     // would map to "foo_bar".  this way, all three will map to
347     // different values
348     PR_snprintf(hashedname, 9, "%08lx", (unsigned long)StringHash(str.get()));
349     name = hashedname;
350   }
351 
352   return NS_OK;
353 }
354 
355 // XXX : The number of UTF-16 2byte code units are half the number of
356 // bytes in legacy encodings for CJK strings and non-Latin1 in UTF-8.
357 // The ratio can be 1/3 for CJK strings in UTF-8. However, we can
358 // get away with using the same MAX_LEN for nsCString and nsString
359 // because MAX_LEN is defined rather conservatively in the first place.
NS_MsgHashIfNecessary(nsAutoString & name)360 nsresult NS_MsgHashIfNecessary(nsAutoString& name) {
361   if (name.IsEmpty()) return NS_OK;  // Nothing to do.
362   int32_t illegalCharacterIndex = MsgFindCharInSet(
363       name, FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS ILLEGAL_FOLDER_CHARS,
364       0);
365 
366   // Need to check the first ('.') and last ('.', '~' and ' ') char
367   if (illegalCharacterIndex == -1) {
368     int32_t lastIndex = name.Length() - 1;
369     if (NS_LITERAL_STRING_FROM_CSTRING(ILLEGAL_FOLDER_CHARS_AS_FIRST_LETTER)
370             .FindChar(name[0]) != -1)
371       illegalCharacterIndex = 0;
372     else if (NS_LITERAL_STRING_FROM_CSTRING(ILLEGAL_FOLDER_CHARS_AS_LAST_LETTER)
373                  .FindChar(name[lastIndex]) != -1)
374       illegalCharacterIndex = lastIndex;
375     else
376       illegalCharacterIndex = -1;
377   }
378 
379   char hashedname[9];
380   int32_t keptLength = -1;
381   if (illegalCharacterIndex != -1)
382     keptLength = illegalCharacterIndex;
383   else if (!ConvertibleToNative(name))
384     keptLength = 0;
385   else if (name.Length() > MAX_LEN) {
386     keptLength = MAX_LEN - 8;
387     // To avoid keeping only the high surrogate of a surrogate pair
388     if (NS_IS_HIGH_SURROGATE(name.CharAt(keptLength - 1))) --keptLength;
389   }
390 
391   if (keptLength >= 0) {
392     PR_snprintf(hashedname, 9, "%08lx", (unsigned long)StringHash(name));
393     name.SetLength(keptLength);
394     name.Append(NS_ConvertASCIItoUTF16(hashedname));
395   }
396 
397   return NS_OK;
398 }
399 
FormatFileSize(int64_t size,bool useKB,nsAString & formattedSize)400 nsresult FormatFileSize(int64_t size, bool useKB, nsAString& formattedSize) {
401   const char* sizeAbbrNames[] = {
402       "byteAbbreviation2",     "kiloByteAbbreviation2", "megaByteAbbreviation2",
403       "gigaByteAbbreviation2", "teraByteAbbreviation2", "petaByteAbbreviation2",
404   };
405 
406   nsresult rv;
407 
408   nsCOMPtr<nsIStringBundleService> bundleSvc =
409       mozilla::services::GetStringBundleService();
410   NS_ENSURE_TRUE(bundleSvc, NS_ERROR_UNEXPECTED);
411 
412   nsCOMPtr<nsIStringBundle> bundle;
413   rv = bundleSvc->CreateBundle("chrome://messenger/locale/messenger.properties",
414                                getter_AddRefs(bundle));
415   NS_ENSURE_SUCCESS(rv, rv);
416 
417   double unitSize = size < 0 ? 0.0 : size;
418   uint32_t unitIndex = 0;
419 
420   if (useKB) {
421     // Start by formatting in kilobytes
422     unitSize /= 1024;
423     if (unitSize < 0.1 && unitSize != 0) unitSize = 0.1;
424     unitIndex++;
425   }
426 
427   // Convert to next unit if it needs 4 digits (after rounding), but only if
428   // we know the name of the next unit
429   while ((unitSize >= 999.5) && (unitIndex < ArrayLength(sizeAbbrNames) - 1)) {
430     unitSize /= 1024;
431     unitIndex++;
432   }
433 
434   // Grab the string for the appropriate unit
435   nsString sizeAbbr;
436   rv = bundle->GetStringFromName(sizeAbbrNames[unitIndex], sizeAbbr);
437   NS_ENSURE_SUCCESS(rv, rv);
438 
439   // Get rid of insignificant bits by truncating to 1 or 0 decimal points
440   // 0.1 -> 0.1; 1.2 -> 1.2; 12.3 -> 12.3; 123.4 -> 123; 234.5 -> 235
441   nsTextFormatter::ssprintf(
442       formattedSize, sizeAbbr.get(),
443       (unitIndex != 0) && (unitSize < 99.95 && unitSize != 0) ? 1 : 0,
444       unitSize);
445 
446   int32_t separatorPos = formattedSize.FindChar('.');
447   if (separatorPos != kNotFound) {
448     // The ssprintf returned a decimal number using a dot (.) as the decimal
449     // separator. Now we try to localize the separator.
450     // Try to get the decimal separator from the system's locale.
451     char* decimalPoint;
452 #ifdef HAVE_LOCALECONV
453     struct lconv* locale = localeconv();
454     decimalPoint = locale->decimal_point;
455 #else
456     decimalPoint = getenv("LOCALE_DECIMAL_POINT");
457 #endif
458     NS_ConvertUTF8toUTF16 decimalSeparator(decimalPoint);
459     if (decimalSeparator.IsEmpty()) decimalSeparator.Assign('.');
460 
461     formattedSize.Replace(separatorPos, 1, decimalSeparator);
462   }
463 
464   return NS_OK;
465 }
466 
NS_MsgCreatePathStringFromFolderURI(const char * aFolderURI,nsCString & aPathCString,const nsCString & aScheme,bool aIsNewsFolder)467 nsresult NS_MsgCreatePathStringFromFolderURI(const char* aFolderURI,
468                                              nsCString& aPathCString,
469                                              const nsCString& aScheme,
470                                              bool aIsNewsFolder) {
471   // A file name has to be in native charset. Here we convert
472   // to UTF-16 and check for 'unsafe' characters before converting
473   // to native charset.
474   NS_ENSURE_TRUE(mozilla::IsUtf8(nsDependentCString(aFolderURI)),
475                  NS_ERROR_UNEXPECTED);
476   NS_ConvertUTF8toUTF16 oldPath(aFolderURI);
477 
478   nsAutoString pathPiece, path;
479 
480   int32_t startSlashPos = oldPath.FindChar('/');
481   int32_t endSlashPos = (startSlashPos >= 0)
482                             ? oldPath.FindChar('/', startSlashPos + 1) - 1
483                             : oldPath.Length() - 1;
484   if (endSlashPos < 0) endSlashPos = oldPath.Length();
485 #if defined(XP_UNIX) || defined(XP_MACOSX)
486   bool isLocalUri = aScheme.EqualsLiteral("none") ||
487                     aScheme.EqualsLiteral("pop3") ||
488                     aScheme.EqualsLiteral("rss");
489 #endif
490   // trick to make sure we only add the path to the first n-1 folders
491   bool haveFirst = false;
492   while (startSlashPos != -1) {
493     pathPiece.Assign(
494         Substring(oldPath, startSlashPos + 1, endSlashPos - startSlashPos));
495     // skip leading '/' (and other // style things)
496     if (!pathPiece.IsEmpty()) {
497       // add .sbd onto the previous path
498       if (haveFirst) {
499         path.AppendLiteral(FOLDER_SUFFIX "/");
500       }
501 
502       if (aIsNewsFolder) {
503         nsAutoCString tmp;
504         CopyUTF16toMUTF7(pathPiece, tmp);
505         CopyASCIItoUTF16(tmp, pathPiece);
506       }
507 #if defined(XP_UNIX) || defined(XP_MACOSX)
508       // Don't hash path pieces because local mail folder uri's have already
509       // been hashed. We're only doing this on the mac to limit potential
510       // regressions.
511       if (!isLocalUri)
512 #endif
513         NS_MsgHashIfNecessary(pathPiece);
514       path += pathPiece;
515       haveFirst = true;
516     }
517     // look for the next slash
518     startSlashPos = endSlashPos + 1;
519 
520     endSlashPos = (startSlashPos >= 0)
521                       ? oldPath.FindChar('/', startSlashPos + 1) - 1
522                       : oldPath.Length() - 1;
523     if (endSlashPos < 0) endSlashPos = oldPath.Length();
524 
525     if (startSlashPos >= endSlashPos) break;
526   }
527   return NS_CopyUnicodeToNative(path, aPathCString);
528 }
529 
NS_MsgStripRE(const nsCString & subject,nsCString & modifiedSubject)530 bool NS_MsgStripRE(const nsCString& subject, nsCString& modifiedSubject) {
531   bool result = false;
532 
533   // Get localizedRe pref.
534   nsresult rv;
535   nsString utf16LocalizedRe;
536   NS_GetLocalizedUnicharPreferenceWithDefault(nullptr, "mailnews.localizedRe",
537                                               EmptyString(), utf16LocalizedRe);
538   NS_ConvertUTF16toUTF8 localizedRe(utf16LocalizedRe);
539 
540   // Hardcoded "Re" so that no one can configure Mozilla standards incompatible.
541   nsAutoCString checkString("Re,RE,re,rE");
542   if (!localizedRe.IsEmpty()) {
543     checkString.Append(',');
544     checkString.Append(localizedRe);
545   }
546 
547   // Decode the string.
548   nsCString decodedString;
549   nsCOMPtr<nsIMimeConverter> mimeConverter;
550   // We cannot strip "Re:" for RFC2047-encoded subject without modifying the
551   // original.
552   if (subject.Find("=?") != kNotFound) {
553     mimeConverter = do_GetService(NS_MIME_CONVERTER_CONTRACTID, &rv);
554     if (NS_SUCCEEDED(rv))
555       rv = mimeConverter->DecodeMimeHeaderToUTF8(subject, nullptr, false, true,
556                                                  decodedString);
557   }
558 
559   const char *s, *s_end;
560   if (decodedString.IsEmpty()) {
561     s = subject.BeginReading();
562     s_end = s + subject.Length();
563   } else {
564     s = decodedString.BeginReading();
565     s_end = s + decodedString.Length();
566   }
567 
568 AGAIN:
569   while (s < s_end && IS_SPACE(*s)) s++;
570 
571   const char* tokPtr = checkString.get();
572   while (*tokPtr) {
573     // Tokenize the comma separated list.
574     size_t tokenLength = 0;
575     while (*tokPtr && *tokPtr != ',') {
576       tokenLength++;
577       tokPtr++;
578     }
579     // Check if the beginning of s is the actual token.
580     if (tokenLength && !strncmp(s, tokPtr - tokenLength, tokenLength)) {
581       if (s[tokenLength] == ':') {
582         s = s + tokenLength + 1; /* Skip over "Re:" */
583         result = true;           /* Yes, we stripped it. */
584         goto AGAIN;              /* Skip whitespace and try again. */
585       } else if (s[tokenLength] == '[' || s[tokenLength] == '(') {
586         const char* s2 = s + tokenLength + 1; /* Skip over "Re[" */
587 
588         // Skip forward over digits after the "[".
589         while (s2 < (s_end - 2) && isdigit((unsigned char)*s2)) s2++;
590 
591         // Now ensure that the following thing is "]:".
592         // Only if it is do we alter `s`.
593         if ((s2[0] == ']' || s2[0] == ')') && s2[1] == ':') {
594           s = s2 + 2;    /* Skip over "]:" */
595           result = true; /* Yes, we stripped it. */
596           goto AGAIN;    /* Skip whitespace and try again. */
597         }
598       }
599     }
600     if (*tokPtr) tokPtr++;
601   }
602 
603   // If we didn't strip anything, we can return here.
604   if (!result) return false;
605 
606   if (decodedString.IsEmpty()) {
607     // We didn't decode anything, so just return a new string.
608     modifiedSubject.Assign(s);
609     return true;
610   }
611 
612   // We decoded the string, so we need to encode it again. We always encode in
613   // UTF-8.
614   mimeConverter->EncodeMimePartIIStr_UTF8(
615       nsDependentCString(s), false, sizeof("Subject:"),
616       nsIMimeConverter::MIME_ENCODED_WORD_SIZE, modifiedSubject);
617   return true;
618 }
619 
620 /*  Very similar to strdup except it free's too
621  */
NS_MsgSACopy(char ** destination,const char * source)622 char* NS_MsgSACopy(char** destination, const char* source) {
623   if (*destination) {
624     PR_Free(*destination);
625     *destination = 0;
626   }
627   if (!source)
628     *destination = nullptr;
629   else {
630     *destination = (char*)PR_Malloc(PL_strlen(source) + 1);
631     if (*destination == nullptr) return (nullptr);
632 
633     PL_strcpy(*destination, source);
634   }
635   return *destination;
636 }
637 
638 /*  Again like strdup but it concatenates and free's and uses Realloc.
639  */
NS_MsgSACat(char ** destination,const char * source)640 char* NS_MsgSACat(char** destination, const char* source) {
641   if (source && *source) {
642     int destLength = *destination ? PL_strlen(*destination) : 0;
643     char* newDestination =
644         (char*)PR_Realloc(*destination, destLength + PL_strlen(source) + 1);
645     if (newDestination == nullptr) return nullptr;
646 
647     *destination = newDestination;
648     PL_strcpy(*destination + destLength, source);
649   }
650   return *destination;
651 }
652 
NS_MsgEscapeEncodeURLPath(const nsAString & aStr,nsCString & aResult)653 nsresult NS_MsgEscapeEncodeURLPath(const nsAString& aStr, nsCString& aResult) {
654   return MsgEscapeString(NS_ConvertUTF16toUTF8(aStr),
655                          nsINetUtil::ESCAPE_URL_PATH, aResult);
656 }
657 
NS_MsgDecodeUnescapeURLPath(const nsACString & aPath,nsAString & aResult)658 nsresult NS_MsgDecodeUnescapeURLPath(const nsACString& aPath,
659                                      nsAString& aResult) {
660   nsAutoCString unescapedName;
661   MsgUnescapeString(
662       aPath,
663       nsINetUtil::ESCAPE_URL_FILE_BASENAME | nsINetUtil::ESCAPE_URL_FORCED,
664       unescapedName);
665   CopyUTF8toUTF16(unescapedName, aResult);
666   return NS_OK;
667 }
668 
WeAreOffline()669 bool WeAreOffline() {
670   bool offline = false;
671 
672   nsCOMPtr<nsIIOService> ioService = mozilla::services::GetIOService();
673   if (ioService) ioService->GetOffline(&offline);
674 
675   return offline;
676 }
677 
678 // Find a folder by URL. If it doesn't exist, null will be returned
679 // via aFolder.
FindFolder(const nsACString & aFolderURI,nsIMsgFolder ** aFolder)680 nsresult FindFolder(const nsACString& aFolderURI, nsIMsgFolder** aFolder) {
681   NS_ENSURE_ARG_POINTER(aFolder);
682 
683   *aFolder = nullptr;
684 
685   nsresult rv;
686   nsCOMPtr<nsIFolderLookupService> fls(do_GetService(NSIFLS_CONTRACTID, &rv));
687   NS_ENSURE_SUCCESS(rv, rv);
688 
689   // GetFolderForURL returns NS_OK and null for non-existent folders
690   rv = fls->GetFolderForURL(aFolderURI, aFolder);
691   NS_ENSURE_SUCCESS(rv, rv);
692 
693   return NS_OK;
694 }
695 
696 // Fetch an existing folder by URL
697 // The returned aFolder will be non-null if and only if result is NS_OK.
698 // NS_OK - folder was found
699 // NS_MSG_FOLDER_MISSING - if aFolderURI not found
GetExistingFolder(const nsACString & aFolderURI,nsIMsgFolder ** aFolder)700 nsresult GetExistingFolder(const nsACString& aFolderURI,
701                            nsIMsgFolder** aFolder) {
702   nsresult rv = FindFolder(aFolderURI, aFolder);
703   NS_ENSURE_SUCCESS(rv, rv);
704   return *aFolder ? NS_OK : NS_MSG_ERROR_FOLDER_MISSING;
705 }
706 
GetOrCreateFolder(const nsACString & aFolderURI,nsIMsgFolder ** aFolder)707 nsresult GetOrCreateFolder(const nsACString& aFolderURI,
708                            nsIMsgFolder** aFolder) {
709   NS_ENSURE_ARG_POINTER(aFolder);
710 
711   *aFolder = nullptr;
712 
713   nsresult rv;
714   nsCOMPtr<nsIFolderLookupService> fls(do_GetService(NSIFLS_CONTRACTID, &rv));
715   NS_ENSURE_SUCCESS(rv, rv);
716 
717   rv = fls->GetOrCreateFolderForURL(aFolderURI, aFolder);
718   NS_ENSURE_SUCCESS(rv, rv);
719 
720   return *aFolder ? NS_OK : NS_ERROR_FAILURE;
721 }
722 
IsAFromSpaceLine(char * start,const char * end)723 bool IsAFromSpaceLine(char* start, const char* end) {
724   bool rv = false;
725   while ((start < end) && (*start == '>')) start++;
726   // If the leading '>'s are followed by an 'F' then we have a possible case
727   // here.
728   if ((*start == 'F') && (end - start > 4) && !strncmp(start, "From ", 5))
729     rv = true;
730   return rv;
731 }
732 
733 //
734 // This function finds all lines starting with "From " or "From " preceding
735 // with one or more '>' (ie, ">From", ">>From", etc) in the input buffer
736 // (between 'start' and 'end') and prefix them with a ">" .
737 //
EscapeFromSpaceLine(nsIOutputStream * outputStream,char * start,const char * end)738 nsresult EscapeFromSpaceLine(nsIOutputStream* outputStream, char* start,
739                              const char* end) {
740   nsresult rv;
741   char* pChar;
742   uint32_t written;
743 
744   pChar = start;
745   while (start < end) {
746     while ((pChar < end) && (*pChar != '\r') && ((pChar + 1) < end) &&
747            (*(pChar + 1) != '\n'))
748       pChar++;
749     if ((pChar + 1) == end) pChar++;
750 
751     if (pChar < end) {
752       // Found a line so check if it's a qualified "From " line.
753       if (IsAFromSpaceLine(start, pChar))
754         rv = outputStream->Write(">", 1, &written);
755       int32_t lineTerminatorCount = (*(pChar + 1) == '\n') ? 2 : 1;
756       rv = outputStream->Write(start, pChar - start + lineTerminatorCount,
757                                &written);
758       NS_ENSURE_SUCCESS(rv, rv);
759       pChar += lineTerminatorCount;
760       start = pChar;
761     } else if (start < end) {
762       // Check and flush out the remaining data and we're done.
763       if (IsAFromSpaceLine(start, end))
764         rv = outputStream->Write(">", 1, &written);
765       rv = outputStream->Write(start, end - start, &written);
766       NS_ENSURE_SUCCESS(rv, rv);
767       break;
768     }
769   }
770   return NS_OK;
771 }
772 
IsRFC822HeaderFieldName(const char * aHdr,bool * aResult)773 nsresult IsRFC822HeaderFieldName(const char* aHdr, bool* aResult) {
774   NS_ENSURE_ARG_POINTER(aHdr);
775   NS_ENSURE_ARG_POINTER(aResult);
776   uint32_t length = strlen(aHdr);
777   for (uint32_t i = 0; i < length; i++) {
778     char c = aHdr[i];
779     if (c < '!' || c == ':' || c > '~') {
780       *aResult = false;
781       return NS_OK;
782     }
783   }
784   *aResult = true;
785   return NS_OK;
786 }
787 
788 // Warning, currently this routine only works for the Junk Folder
GetOrCreateJunkFolder(const nsACString & aURI,nsIUrlListener * aListener)789 nsresult GetOrCreateJunkFolder(const nsACString& aURI,
790                                nsIUrlListener* aListener) {
791   nsresult rv;
792 
793   nsCOMPtr<nsIMsgFolder> folder;
794   rv = GetOrCreateFolder(aURI, getter_AddRefs(folder));
795   NS_ENSURE_SUCCESS(rv, rv);
796 
797   // don't check validity of folder - caller will handle creating it
798   nsCOMPtr<nsIMsgIncomingServer> server;
799   // make sure that folder hierarchy is built so that legitimate parent-child
800   // relationship is established
801   rv = folder->GetServer(getter_AddRefs(server));
802   NS_ENSURE_SUCCESS(rv, rv);
803   if (!server) return NS_ERROR_UNEXPECTED;
804 
805   nsCOMPtr<nsIMsgFolder> msgFolder;
806   rv = server->GetMsgFolderFromURI(folder, aURI, getter_AddRefs(msgFolder));
807   NS_ENSURE_SUCCESS(rv, rv);
808 
809   nsCOMPtr<nsIMsgFolder> parent;
810   rv = msgFolder->GetParent(getter_AddRefs(parent));
811   if (NS_FAILED(rv) || !parent) {
812     nsCOMPtr<nsIFile> folderPath;
813     // for local folders, path is to the berkeley mailbox.
814     // for imap folders, path needs to have .msf appended to the name
815     msgFolder->GetFilePath(getter_AddRefs(folderPath));
816 
817     nsCOMPtr<nsIMsgProtocolInfo> protocolInfo;
818     rv = server->GetProtocolInfo(getter_AddRefs(protocolInfo));
819     NS_ENSURE_SUCCESS(rv, rv);
820 
821     bool isAsyncFolder;
822     rv = protocolInfo->GetFoldersCreatedAsync(&isAsyncFolder);
823     NS_ENSURE_SUCCESS(rv, rv);
824 
825     // if we can't get the path from the folder, then try to create the storage.
826     // for imap, it doesn't matter if the .msf file exists - it still might not
827     // exist on the server, so we should try to create it
828     bool exists = false;
829     if (!isAsyncFolder && folderPath) folderPath->Exists(&exists);
830     if (!exists) {
831       // Hack to work around a localization bug with the Junk Folder.
832       // Please see Bug #270261 for more information...
833       nsString localizedJunkName;
834       msgFolder->GetName(localizedJunkName);
835 
836       // force the junk folder name to be Junk so it gets created on disk
837       // correctly...
838       msgFolder->SetName(u"Junk"_ns);
839       msgFolder->SetFlag(nsMsgFolderFlags::Junk);
840       rv = msgFolder->CreateStorageIfMissing(aListener);
841       NS_ENSURE_SUCCESS(rv, rv);
842 
843       // now restore the localized folder name...
844       msgFolder->SetName(localizedJunkName);
845 
846       // XXX TODO
847       // JUNK MAIL RELATED
848       // ugh, I hate this hack
849       // we have to do this (for now)
850       // because imap and local are different (one creates folder asynch, the
851       // other synch) one will notify the listener, one will not. I blame
852       // nsMsgCopy. we should look into making it so no matter what the folder
853       // type we always call the listener this code should move into local
854       // folder's version of CreateStorageIfMissing()
855       if (!isAsyncFolder && aListener) {
856         rv = aListener->OnStartRunningUrl(nullptr);
857         NS_ENSURE_SUCCESS(rv, rv);
858 
859         rv = aListener->OnStopRunningUrl(nullptr, NS_OK);
860         NS_ENSURE_SUCCESS(rv, rv);
861       }
862     }
863   } else {
864     // if the folder exists, we should set the junk flag on it
865     // which is what the listener will do
866     if (aListener) {
867       rv = aListener->OnStartRunningUrl(nullptr);
868       NS_ENSURE_SUCCESS(rv, rv);
869 
870       rv = aListener->OnStopRunningUrl(nullptr, NS_OK);
871       NS_ENSURE_SUCCESS(rv, rv);
872     }
873   }
874 
875   return NS_OK;
876 }
877 
IsRSSArticle(nsIURI * aMsgURI,bool * aIsRSSArticle)878 nsresult IsRSSArticle(nsIURI* aMsgURI, bool* aIsRSSArticle) {
879   nsresult rv;
880   *aIsRSSArticle = false;
881 
882   nsCOMPtr<nsIMsgMessageUrl> msgUrl = do_QueryInterface(aMsgURI, &rv);
883   if (NS_FAILED(rv)) return rv;
884 
885   nsCString resourceURI;
886   msgUrl->GetUri(resourceURI);
887 
888   // get the msg service for this URI
889   nsCOMPtr<nsIMsgMessageService> msgService;
890   rv = GetMessageServiceFromURI(resourceURI, getter_AddRefs(msgService));
891   NS_ENSURE_SUCCESS(rv, rv);
892 
893   // Check if the message is a feed message, regardless of folder.
894   uint32_t flags;
895   nsCOMPtr<nsIMsgDBHdr> msgHdr;
896   rv = msgService->MessageURIToMsgHdr(resourceURI, getter_AddRefs(msgHdr));
897   NS_ENSURE_SUCCESS(rv, rv);
898   msgHdr->GetFlags(&flags);
899   if (flags & nsMsgMessageFlags::FeedMsg) {
900     *aIsRSSArticle = true;
901     return rv;
902   }
903 
904   nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(aMsgURI, &rv);
905   mozilla::Unused << mailnewsUrl;
906   NS_ENSURE_SUCCESS(rv, rv);
907 
908   // get the folder and the server from the msghdr
909   nsCOMPtr<nsIMsgFolder> folder;
910   rv = msgHdr->GetFolder(getter_AddRefs(folder));
911   if (NS_SUCCEEDED(rv) && folder) {
912     nsCOMPtr<nsIMsgIncomingServer> server;
913     folder->GetServer(getter_AddRefs(server));
914     nsCOMPtr<nsIRssIncomingServer> rssServer = do_QueryInterface(server);
915 
916     if (rssServer) *aIsRSSArticle = true;
917   }
918 
919   return rv;
920 }
921 
922 // digest needs to be a pointer to a DIGEST_LENGTH (16) byte buffer
MSGCramMD5(const char * text,int32_t text_len,const char * key,int32_t key_len,unsigned char * digest)923 nsresult MSGCramMD5(const char* text, int32_t text_len, const char* key,
924                     int32_t key_len, unsigned char* digest) {
925   nsresult rv;
926 
927   nsAutoCString hash;
928   nsCOMPtr<nsICryptoHash> hasher =
929       do_CreateInstance("@mozilla.org/security/hash;1", &rv);
930   NS_ENSURE_SUCCESS(rv, rv);
931 
932   // this code adapted from
933   // http://www.cis.ohio-state.edu/cgi-bin/rfc/rfc2104.html
934 
935   char innerPad[65]; /* inner padding - key XORd with innerPad */
936   char outerPad[65]; /* outer padding - key XORd with outerPad */
937   int i;
938   /* if key is longer than 64 bytes reset it to key=MD5(key) */
939   if (key_len > 64) {
940     rv = hasher->Init(nsICryptoHash::MD5);
941     NS_ENSURE_SUCCESS(rv, rv);
942 
943     rv = hasher->Update((const uint8_t*)key, key_len);
944     NS_ENSURE_SUCCESS(rv, rv);
945 
946     rv = hasher->Finish(false, hash);
947     NS_ENSURE_SUCCESS(rv, rv);
948 
949     key = hash.get();
950     key_len = DIGEST_LENGTH;
951   }
952 
953   /*
954    * the HMAC_MD5 transform looks like:
955    *
956    * MD5(K XOR outerPad, MD5(K XOR innerPad, text))
957    *
958    * where K is an n byte key
959    * innerPad is the byte 0x36 repeated 64 times
960    * outerPad is the byte 0x5c repeated 64 times
961    * and text is the data being protected
962    */
963 
964   /* start out by storing key in pads */
965   memset(innerPad, 0, sizeof innerPad);
966   memset(outerPad, 0, sizeof outerPad);
967   memcpy(innerPad, key, key_len);
968   memcpy(outerPad, key, key_len);
969 
970   /* XOR key with innerPad and outerPad values */
971   for (i = 0; i < 64; i++) {
972     innerPad[i] ^= 0x36;
973     outerPad[i] ^= 0x5c;
974   }
975   /*
976    * perform inner MD5
977    */
978   nsAutoCString result;
979   rv = hasher->Init(nsICryptoHash::MD5); /* init context for 1st pass */
980   rv = hasher->Update((const uint8_t*)innerPad, 64); /* start with inner pad */
981   rv = hasher->Update((const uint8_t*)text,
982                       text_len);      /* then text of datagram */
983   rv = hasher->Finish(false, result); /* finish up 1st pass */
984 
985   /*
986    * perform outer MD5
987    */
988   hasher->Init(nsICryptoHash::MD5); /* init context for 2nd pass */
989   rv = hasher->Update((const uint8_t*)outerPad, 64); /* start with outer pad */
990   rv = hasher->Update((const uint8_t*)result.get(),
991                       16);            /* then results of 1st hash */
992   rv = hasher->Finish(false, result); /* finish up 2nd pass */
993 
994   if (result.Length() != DIGEST_LENGTH) return NS_ERROR_UNEXPECTED;
995 
996   memcpy(digest, result.get(), DIGEST_LENGTH);
997 
998   return rv;
999 }
1000 
1001 // digest needs to be a pointer to a DIGEST_LENGTH (16) byte buffer
MSGApopMD5(const char * text,int32_t text_len,const char * password,int32_t password_len,unsigned char * digest)1002 nsresult MSGApopMD5(const char* text, int32_t text_len, const char* password,
1003                     int32_t password_len, unsigned char* digest) {
1004   nsresult rv;
1005   nsAutoCString result;
1006 
1007   nsCOMPtr<nsICryptoHash> hasher =
1008       do_CreateInstance("@mozilla.org/security/hash;1", &rv);
1009   NS_ENSURE_SUCCESS(rv, rv);
1010 
1011   rv = hasher->Init(nsICryptoHash::MD5);
1012   NS_ENSURE_SUCCESS(rv, rv);
1013 
1014   rv = hasher->Update((const uint8_t*)text, text_len);
1015   NS_ENSURE_SUCCESS(rv, rv);
1016 
1017   rv = hasher->Update((const uint8_t*)password, password_len);
1018   NS_ENSURE_SUCCESS(rv, rv);
1019 
1020   rv = hasher->Finish(false, result);
1021   NS_ENSURE_SUCCESS(rv, rv);
1022 
1023   if (result.Length() != DIGEST_LENGTH) return NS_ERROR_UNEXPECTED;
1024 
1025   memcpy(digest, result.get(), DIGEST_LENGTH);
1026   return rv;
1027 }
1028 
NS_GetPersistentFile(const char * relPrefName,const char * absPrefName,const char * dirServiceProp,bool & gotRelPref,nsIFile ** aFile,nsIPrefBranch * prefBranch)1029 NS_MSG_BASE nsresult NS_GetPersistentFile(const char* relPrefName,
1030                                           const char* absPrefName,
1031                                           const char* dirServiceProp,
1032                                           bool& gotRelPref, nsIFile** aFile,
1033                                           nsIPrefBranch* prefBranch) {
1034   NS_ENSURE_ARG_POINTER(aFile);
1035   *aFile = nullptr;
1036   NS_ENSURE_ARG(relPrefName);
1037   NS_ENSURE_ARG(absPrefName);
1038   gotRelPref = false;
1039 
1040   nsCOMPtr<nsIPrefBranch> mainBranch;
1041   if (!prefBranch) {
1042     nsCOMPtr<nsIPrefService> prefService(
1043         do_GetService(NS_PREFSERVICE_CONTRACTID));
1044     if (!prefService) return NS_ERROR_FAILURE;
1045     prefService->GetBranch(nullptr, getter_AddRefs(mainBranch));
1046     if (!mainBranch) return NS_ERROR_FAILURE;
1047     prefBranch = mainBranch;
1048   }
1049 
1050   nsCOMPtr<nsIFile> localFile;
1051 
1052   // Get the relative first
1053   nsCOMPtr<nsIRelativeFilePref> relFilePref;
1054   prefBranch->GetComplexValue(relPrefName, NS_GET_IID(nsIRelativeFilePref),
1055                               getter_AddRefs(relFilePref));
1056   if (relFilePref) {
1057     relFilePref->GetFile(getter_AddRefs(localFile));
1058     NS_ASSERTION(localFile, "An nsIRelativeFilePref has no file.");
1059     if (localFile) gotRelPref = true;
1060   }
1061 
1062   // If not, get the old absolute
1063   if (!localFile) {
1064     prefBranch->GetComplexValue(absPrefName, NS_GET_IID(nsIFile),
1065                                 getter_AddRefs(localFile));
1066 
1067     // If not, and given a dirServiceProp, use directory service.
1068     if (!localFile && dirServiceProp) {
1069       nsCOMPtr<nsIProperties> dirService(
1070           do_GetService("@mozilla.org/file/directory_service;1"));
1071       if (!dirService) return NS_ERROR_FAILURE;
1072       dirService->Get(dirServiceProp, NS_GET_IID(nsIFile),
1073                       getter_AddRefs(localFile));
1074       if (!localFile) return NS_ERROR_FAILURE;
1075     }
1076   }
1077 
1078   if (localFile) {
1079     localFile->Normalize();
1080     localFile.forget(aFile);
1081     return NS_OK;
1082   }
1083 
1084   return NS_ERROR_FAILURE;
1085 }
1086 
NS_SetPersistentFile(const char * relPrefName,const char * absPrefName,nsIFile * aFile,nsIPrefBranch * prefBranch)1087 NS_MSG_BASE nsresult NS_SetPersistentFile(const char* relPrefName,
1088                                           const char* absPrefName,
1089                                           nsIFile* aFile,
1090                                           nsIPrefBranch* prefBranch) {
1091   NS_ENSURE_ARG(relPrefName);
1092   NS_ENSURE_ARG(absPrefName);
1093   NS_ENSURE_ARG(aFile);
1094 
1095   nsCOMPtr<nsIPrefBranch> mainBranch;
1096   if (!prefBranch) {
1097     nsCOMPtr<nsIPrefService> prefService(
1098         do_GetService(NS_PREFSERVICE_CONTRACTID));
1099     if (!prefService) return NS_ERROR_FAILURE;
1100     prefService->GetBranch(nullptr, getter_AddRefs(mainBranch));
1101     if (!mainBranch) return NS_ERROR_FAILURE;
1102     prefBranch = mainBranch;
1103   }
1104 
1105   // Write the absolute for backwards compatibilty's sake.
1106   // Or, if aPath is on a different drive than the profile dir.
1107   nsresult rv =
1108       prefBranch->SetComplexValue(absPrefName, NS_GET_IID(nsIFile), aFile);
1109 
1110   // Write the relative path.
1111   nsCOMPtr<nsIRelativeFilePref> relFilePref = new nsRelativeFilePref();
1112   mozilla::Unused << relFilePref->SetFile(aFile);
1113   mozilla::Unused << relFilePref->SetRelativeToKey(
1114       nsLiteralCString(NS_APP_USER_PROFILE_50_DIR));
1115 
1116   nsresult rv2 = prefBranch->SetComplexValue(
1117       relPrefName, NS_GET_IID(nsIRelativeFilePref), relFilePref);
1118   if (NS_FAILED(rv2) && NS_SUCCEEDED(rv))
1119     prefBranch->ClearUserPref(relPrefName);
1120 
1121   return rv;
1122 }
1123 
NS_GetUnicharPreferenceWithDefault(nsIPrefBranch * prefBranch,const char * prefName,const nsAString & defValue,nsAString & prefValue)1124 NS_MSG_BASE nsresult NS_GetUnicharPreferenceWithDefault(
1125     nsIPrefBranch* prefBranch,  // can be null, if so uses the root branch
1126     const char* prefName, const nsAString& defValue, nsAString& prefValue) {
1127   NS_ENSURE_ARG(prefName);
1128 
1129   nsCOMPtr<nsIPrefBranch> pbr;
1130   if (!prefBranch) {
1131     pbr = do_GetService(NS_PREFSERVICE_CONTRACTID);
1132     prefBranch = pbr;
1133   }
1134 
1135   nsCString valueUtf8;
1136   nsresult rv =
1137       prefBranch->GetStringPref(prefName, EmptyCString(), 0, valueUtf8);
1138   if (NS_SUCCEEDED(rv))
1139     CopyUTF8toUTF16(valueUtf8, prefValue);
1140   else
1141     prefValue = defValue;
1142   return NS_OK;
1143 }
1144 
NS_GetLocalizedUnicharPreferenceWithDefault(nsIPrefBranch * prefBranch,const char * prefName,const nsAString & defValue,nsAString & prefValue)1145 NS_MSG_BASE nsresult NS_GetLocalizedUnicharPreferenceWithDefault(
1146     nsIPrefBranch* prefBranch,  // can be null, if so uses the root branch
1147     const char* prefName, const nsAString& defValue, nsAString& prefValue) {
1148   NS_ENSURE_ARG(prefName);
1149 
1150   nsCOMPtr<nsIPrefBranch> pbr;
1151   if (!prefBranch) {
1152     pbr = do_GetService(NS_PREFSERVICE_CONTRACTID);
1153     prefBranch = pbr;
1154   }
1155 
1156   nsCOMPtr<nsIPrefLocalizedString> str;
1157   nsresult rv = prefBranch->GetComplexValue(
1158       prefName, NS_GET_IID(nsIPrefLocalizedString), getter_AddRefs(str));
1159   if (NS_SUCCEEDED(rv)) {
1160     nsString tmpValue;
1161     str->ToString(getter_Copies(tmpValue));
1162     prefValue.Assign(tmpValue);
1163   } else
1164     prefValue = defValue;
1165   return NS_OK;
1166 }
1167 
NS_GetLocalizedUnicharPreference(nsIPrefBranch * prefBranch,const char * prefName,nsAString & prefValue)1168 NS_MSG_BASE nsresult NS_GetLocalizedUnicharPreference(
1169     nsIPrefBranch* prefBranch,  // can be null, if so uses the root branch
1170     const char* prefName, nsAString& prefValue) {
1171   NS_ENSURE_ARG_POINTER(prefName);
1172 
1173   nsCOMPtr<nsIPrefBranch> pbr;
1174   if (!prefBranch) {
1175     pbr = do_GetService(NS_PREFSERVICE_CONTRACTID);
1176     prefBranch = pbr;
1177   }
1178 
1179   nsCOMPtr<nsIPrefLocalizedString> str;
1180   nsresult rv = prefBranch->GetComplexValue(
1181       prefName, NS_GET_IID(nsIPrefLocalizedString), getter_AddRefs(str));
1182   NS_ENSURE_SUCCESS(rv, rv);
1183 
1184   nsString tmpValue;
1185   str->ToString(getter_Copies(tmpValue));
1186   prefValue.Assign(tmpValue);
1187   return NS_OK;
1188 }
1189 
PRTime2Seconds(PRTime prTime,uint32_t * seconds)1190 void PRTime2Seconds(PRTime prTime, uint32_t* seconds) {
1191   *seconds = (uint32_t)(prTime / PR_USEC_PER_SEC);
1192 }
1193 
PRTime2Seconds(PRTime prTime,int32_t * seconds)1194 void PRTime2Seconds(PRTime prTime, int32_t* seconds) {
1195   *seconds = (int32_t)(prTime / PR_USEC_PER_SEC);
1196 }
1197 
Seconds2PRTime(uint32_t seconds,PRTime * prTime)1198 void Seconds2PRTime(uint32_t seconds, PRTime* prTime) {
1199   *prTime = (PRTime)seconds * PR_USEC_PER_SEC;
1200 }
1201 
GetSummaryFileLocation(nsIFile * fileLocation,nsIFile ** summaryLocation)1202 nsresult GetSummaryFileLocation(nsIFile* fileLocation,
1203                                 nsIFile** summaryLocation) {
1204   nsresult rv;
1205   nsCOMPtr<nsIFile> newSummaryLocation =
1206       do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
1207   NS_ENSURE_SUCCESS(rv, rv);
1208 
1209   newSummaryLocation->InitWithFile(fileLocation);
1210   nsString fileName;
1211 
1212   rv = newSummaryLocation->GetLeafName(fileName);
1213   if (NS_FAILED(rv)) return rv;
1214 
1215   fileName.AppendLiteral(SUMMARY_SUFFIX);
1216   rv = newSummaryLocation->SetLeafName(fileName);
1217   NS_ENSURE_SUCCESS(rv, rv);
1218 
1219   newSummaryLocation.forget(summaryLocation);
1220   return NS_OK;
1221 }
1222 
MsgGenerateNowStr(nsACString & nowStr)1223 void MsgGenerateNowStr(nsACString& nowStr) {
1224   char dateBuf[100];
1225   dateBuf[0] = '\0';
1226   PRExplodedTime exploded;
1227   PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &exploded);
1228   PR_FormatTimeUSEnglish(dateBuf, sizeof(dateBuf), "%a %b %d %H:%M:%S %Y",
1229                          &exploded);
1230   nowStr.Assign(dateBuf);
1231 }
1232 
1233 // Gets a special directory and appends the supplied file name onto it.
GetSpecialDirectoryWithFileName(const char * specialDirName,const char * fileName,nsIFile ** result)1234 nsresult GetSpecialDirectoryWithFileName(const char* specialDirName,
1235                                          const char* fileName,
1236                                          nsIFile** result) {
1237   nsresult rv = NS_GetSpecialDirectory(specialDirName, result);
1238   NS_ENSURE_SUCCESS(rv, rv);
1239 
1240   return (*result)->AppendNative(nsDependentCString(fileName));
1241 }
1242 
1243 // Cleans up temp files with matching names
MsgCleanupTempFiles(const char * fileName,const char * extension)1244 nsresult MsgCleanupTempFiles(const char* fileName, const char* extension) {
1245   nsCOMPtr<nsIFile> tmpFile;
1246   nsCString rootName(fileName);
1247   rootName.Append('.');
1248   rootName.Append(extension);
1249   nsresult rv = GetSpecialDirectoryWithFileName(NS_OS_TEMP_DIR, rootName.get(),
1250                                                 getter_AddRefs(tmpFile));
1251 
1252   NS_ENSURE_SUCCESS(rv, rv);
1253   int index = 1;
1254   bool exists;
1255   do {
1256     tmpFile->Exists(&exists);
1257     if (exists) {
1258       tmpFile->Remove(false);
1259       nsCString leafName(fileName);
1260       leafName.Append('-');
1261       leafName.AppendInt(index);
1262       leafName.Append('.');
1263       leafName.Append(extension);
1264       // start with "Picture-1.jpg" after "Picture.jpg" exists
1265       tmpFile->SetNativeLeafName(leafName);
1266     }
1267   } while (exists && ++index < 10000);
1268   return NS_OK;
1269 }
1270 
MsgGetFileStream(nsIFile * file,nsIOutputStream ** fileStream)1271 nsresult MsgGetFileStream(nsIFile* file, nsIOutputStream** fileStream) {
1272   RefPtr<nsMsgFileStream> newFileStream = new nsMsgFileStream;
1273   nsresult rv = newFileStream->InitWithFile(file);
1274   if (NS_SUCCEEDED(rv))
1275     rv = newFileStream->QueryInterface(NS_GET_IID(nsIOutputStream),
1276                                        (void**)fileStream);
1277   return rv;
1278 }
1279 
MsgReopenFileStream(nsIFile * file,nsIInputStream * fileStream)1280 nsresult MsgReopenFileStream(nsIFile* file, nsIInputStream* fileStream) {
1281   nsMsgFileStream* msgFileStream = static_cast<nsMsgFileStream*>(fileStream);
1282   if (msgFileStream) {
1283     return msgFileStream->InitWithFile(file);
1284   }
1285   return NS_ERROR_FAILURE;
1286 }
1287 
MsgNewBufferedFileOutputStream(nsIOutputStream ** aResult,nsIFile * aFile,int32_t aIOFlags,int32_t aPerm)1288 nsresult MsgNewBufferedFileOutputStream(nsIOutputStream** aResult,
1289                                         nsIFile* aFile, int32_t aIOFlags,
1290                                         int32_t aPerm) {
1291   nsCOMPtr<nsIOutputStream> stream;
1292   nsresult rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), aFile,
1293                                             aIOFlags, aPerm);
1294   if (NS_SUCCEEDED(rv))
1295     rv = NS_NewBufferedOutputStream(aResult, stream.forget(),
1296                                     FILE_IO_BUFFER_SIZE);
1297   return rv;
1298 }
1299 
MsgNewSafeBufferedFileOutputStream(nsIOutputStream ** aResult,nsIFile * aFile,int32_t aIOFlags,int32_t aPerm)1300 nsresult MsgNewSafeBufferedFileOutputStream(nsIOutputStream** aResult,
1301                                             nsIFile* aFile, int32_t aIOFlags,
1302                                             int32_t aPerm) {
1303   nsCOMPtr<nsIOutputStream> stream;
1304   nsresult rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(stream), aFile,
1305                                                 aIOFlags, aPerm);
1306   if (NS_SUCCEEDED(rv))
1307     rv = NS_NewBufferedOutputStream(aResult, stream.forget(),
1308                                     FILE_IO_BUFFER_SIZE);
1309   return rv;
1310 }
1311 
MsgFindKeyword(const nsCString & keyword,nsCString & keywords,int32_t * aStartOfKeyword,int32_t * aLength)1312 bool MsgFindKeyword(const nsCString& keyword, nsCString& keywords,
1313                     int32_t* aStartOfKeyword, int32_t* aLength) {
1314 // nsTString_CharT::Find(const nsCString& aString,
1315 //                       bool aIgnoreCase=false,
1316 //                       int32_t aOffset=0,
1317 //                       int32_t aCount=-1 ) const;
1318 #define FIND_KEYWORD(keywords, keyword, offset) \
1319   ((keywords).Find((keyword), false, (offset)))
1320   // 'keyword' is the single keyword we're looking for
1321   // 'keywords' is a space delimited list of keywords to be searched,
1322   // which may be just a single keyword or even be empty
1323   const int32_t kKeywordLen = keyword.Length();
1324   const char* start = keywords.BeginReading();
1325   const char* end = keywords.EndReading();
1326   *aStartOfKeyword = FIND_KEYWORD(keywords, keyword, 0);
1327   while (*aStartOfKeyword >= 0) {
1328     const char* matchStart = start + *aStartOfKeyword;
1329     const char* matchEnd = matchStart + kKeywordLen;
1330     // For a real match, matchStart must be the start of keywords or preceded
1331     // by a space and matchEnd must be the end of keywords or point to a space.
1332     if ((matchStart == start || *(matchStart - 1) == ' ') &&
1333         (matchEnd == end || *matchEnd == ' ')) {
1334       *aLength = kKeywordLen;
1335       return true;
1336     }
1337     *aStartOfKeyword =
1338         FIND_KEYWORD(keywords, keyword, *aStartOfKeyword + kKeywordLen);
1339   }
1340 
1341   *aLength = 0;
1342   return false;
1343 #undef FIND_KEYWORD
1344 }
1345 
MsgHostDomainIsTrusted(nsCString & host,nsCString & trustedMailDomains)1346 bool MsgHostDomainIsTrusted(nsCString& host, nsCString& trustedMailDomains) {
1347   const char* end;
1348   uint32_t hostLen, domainLen;
1349   bool domainIsTrusted = false;
1350 
1351   const char* domain = trustedMailDomains.BeginReading();
1352   const char* domainEnd = trustedMailDomains.EndReading();
1353   const char* hostStart = host.BeginReading();
1354   hostLen = host.Length();
1355 
1356   do {
1357     // skip any whitespace
1358     while (*domain == ' ' || *domain == '\t') ++domain;
1359 
1360     // find end of this domain in the string
1361     end = strchr(domain, ',');
1362     if (!end) end = domainEnd;
1363 
1364     // to see if the hostname is in the domain, check if the domain
1365     // matches the end of the hostname.
1366     domainLen = end - domain;
1367     if (domainLen && hostLen >= domainLen) {
1368       const char* hostTail = hostStart + hostLen - domainLen;
1369       if (PL_strncasecmp(domain, hostTail, domainLen) == 0) {
1370         // now, make sure either that the hostname is a direct match or
1371         // that the hostname begins with a dot.
1372         if (hostLen == domainLen || *hostTail == '.' ||
1373             *(hostTail - 1) == '.') {
1374           domainIsTrusted = true;
1375           break;
1376         }
1377       }
1378     }
1379 
1380     domain = end + 1;
1381   } while (*end);
1382   return domainIsTrusted;
1383 }
1384 
MsgGetLocalFileFromURI(const nsACString & aUTF8Path,nsIFile ** aFile)1385 nsresult MsgGetLocalFileFromURI(const nsACString& aUTF8Path, nsIFile** aFile) {
1386   nsresult rv;
1387   nsCOMPtr<nsIURI> argURI;
1388   rv = NS_NewURI(getter_AddRefs(argURI), aUTF8Path);
1389   NS_ENSURE_SUCCESS(rv, rv);
1390   nsCOMPtr<nsIFileURL> argFileURL(do_QueryInterface(argURI, &rv));
1391   NS_ENSURE_SUCCESS(rv, rv);
1392 
1393   nsCOMPtr<nsIFile> argFile;
1394   rv = argFileURL->GetFile(getter_AddRefs(argFile));
1395   NS_ENSURE_SUCCESS(rv, rv);
1396 
1397   argFile.forget(aFile);
1398   return NS_OK;
1399 }
1400 
MsgStripQuotedPrintable(nsCString & aSrc)1401 NS_MSG_BASE void MsgStripQuotedPrintable(nsCString& aSrc) {
1402   // decode quoted printable text in place
1403 
1404   if (aSrc.IsEmpty()) return;
1405 
1406   char* src = aSrc.BeginWriting();
1407   char* dest = src;
1408   int srcIdx = 0, destIdx = 0;
1409 
1410   while (src[srcIdx] != 0) {
1411     // Decode sequence of '=XY' into a character with code XY.
1412     if (src[srcIdx] == '=') {
1413       if (MsgIsHex((const char*)src + srcIdx + 1, 2)) {
1414         // If we got here, we successfully decoded a quoted printable sequence,
1415         // so bump each pointer past it and move on to the next char.
1416         dest[destIdx++] = MsgUnhex((const char*)src + srcIdx + 1, 2);
1417         srcIdx += 3;
1418       } else {
1419         // If first char after '=' isn't hex check if it's a normal char
1420         // or a soft line break. If it's a soft line break, eat the
1421         // CR/LF/CRLF.
1422         if (src[srcIdx + 1] == '\r' || src[srcIdx + 1] == '\n') {
1423           srcIdx++;  // soft line break, ignore the '=';
1424           if (src[srcIdx] == '\r' || src[srcIdx] == '\n') {
1425             srcIdx++;
1426             if (src[srcIdx] == '\n') srcIdx++;
1427           }
1428         } else  // The first or second char after '=' isn't hex, just copy the
1429                 // '='.
1430         {
1431           dest[destIdx++] = src[srcIdx++];
1432         }
1433         continue;
1434       }
1435     } else
1436       dest[destIdx++] = src[srcIdx++];
1437   }
1438 
1439   dest[destIdx] = src[srcIdx];  // null terminate
1440   aSrc.SetLength(destIdx);
1441 }
1442 
MsgEscapeString(const nsACString & aStr,uint32_t aType,nsACString & aResult)1443 NS_MSG_BASE nsresult MsgEscapeString(const nsACString& aStr, uint32_t aType,
1444                                      nsACString& aResult) {
1445   nsresult rv;
1446   nsCOMPtr<nsINetUtil> nu = do_GetService(NS_NETUTIL_CONTRACTID, &rv);
1447   NS_ENSURE_SUCCESS(rv, rv);
1448 
1449   return nu->EscapeString(aStr, aType, aResult);
1450 }
1451 
MsgUnescapeString(const nsACString & aStr,uint32_t aFlags,nsACString & aResult)1452 NS_MSG_BASE nsresult MsgUnescapeString(const nsACString& aStr, uint32_t aFlags,
1453                                        nsACString& aResult) {
1454   nsresult rv;
1455   nsCOMPtr<nsINetUtil> nu = do_GetService(NS_NETUTIL_CONTRACTID, &rv);
1456   NS_ENSURE_SUCCESS(rv, rv);
1457 
1458   return nu->UnescapeString(aStr, aFlags, aResult);
1459 }
1460 
MsgEscapeURL(const nsACString & aStr,uint32_t aFlags,nsACString & aResult)1461 NS_MSG_BASE nsresult MsgEscapeURL(const nsACString& aStr, uint32_t aFlags,
1462                                   nsACString& aResult) {
1463   nsresult rv;
1464   nsCOMPtr<nsINetUtil> nu = do_GetService(NS_NETUTIL_CONTRACTID, &rv);
1465   NS_ENSURE_SUCCESS(rv, rv);
1466 
1467   return nu->EscapeURL(aStr, aFlags, aResult);
1468 }
1469 
1470 NS_MSG_BASE nsresult
MsgGetHeadersFromKeys(nsIMsgDatabase * aDB,const nsTArray<nsMsgKey> & aMsgKeys,nsTArray<RefPtr<nsIMsgDBHdr>> & aHeaders)1471 MsgGetHeadersFromKeys(nsIMsgDatabase* aDB, const nsTArray<nsMsgKey>& aMsgKeys,
1472                       nsTArray<RefPtr<nsIMsgDBHdr>>& aHeaders) {
1473   NS_ENSURE_ARG_POINTER(aDB);
1474   aHeaders.Clear();
1475   aHeaders.SetCapacity(aMsgKeys.Length());
1476 
1477   for (auto key : aMsgKeys) {
1478     // This function silently skips when the key is not found. This is an
1479     // expected case.
1480     bool hasKey;
1481     nsresult rv = aDB->ContainsKey(key, &hasKey);
1482     NS_ENSURE_SUCCESS(rv, rv);
1483     if (hasKey) {
1484       nsCOMPtr<nsIMsgDBHdr> msgHdr;
1485       rv = aDB->GetMsgHdrForKey(key, getter_AddRefs(msgHdr));
1486       NS_ENSURE_SUCCESS(rv, rv);
1487       aHeaders.AppendElement(msgHdr);
1488     }
1489   }
1490   return NS_OK;
1491 }
1492 
MsgAdvanceToNextLine(const char * buffer,uint32_t & bufferOffset,uint32_t maxBufferOffset)1493 bool MsgAdvanceToNextLine(const char* buffer, uint32_t& bufferOffset,
1494                           uint32_t maxBufferOffset) {
1495   bool result = false;
1496   for (; bufferOffset < maxBufferOffset; bufferOffset++) {
1497     if (buffer[bufferOffset] == '\r' || buffer[bufferOffset] == '\n') {
1498       bufferOffset++;
1499       if (buffer[bufferOffset - 1] == '\r' && buffer[bufferOffset] == '\n')
1500         bufferOffset++;
1501       result = true;
1502       break;
1503     }
1504   }
1505   return result;
1506 }
1507 
MsgExamineForProxyAsync(nsIChannel * channel,nsIProtocolProxyCallback * listener,nsICancelable ** result)1508 NS_MSG_BASE nsresult MsgExamineForProxyAsync(nsIChannel* channel,
1509                                              nsIProtocolProxyCallback* listener,
1510                                              nsICancelable** result) {
1511   nsresult rv;
1512 
1513 #ifdef DEBUG
1514   nsCOMPtr<nsIURI> uri;
1515   rv = channel->GetURI(getter_AddRefs(uri));
1516   NS_ASSERTION(NS_SUCCEEDED(rv) && uri,
1517                "The URI needs to be set before calling the proxy service");
1518 #endif
1519 
1520   nsCOMPtr<nsIProtocolProxyService> pps =
1521       do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
1522   NS_ENSURE_SUCCESS(rv, rv);
1523 
1524   return pps->AsyncResolve(channel, 0, listener, nullptr, result);
1525 }
1526 
MsgPromptLoginFailed(nsIMsgWindow * aMsgWindow,const nsACString & aHostname,const nsACString & aUsername,const nsAString & aAccountname,int32_t * aResult)1527 NS_MSG_BASE nsresult MsgPromptLoginFailed(nsIMsgWindow* aMsgWindow,
1528                                           const nsACString& aHostname,
1529                                           const nsACString& aUsername,
1530                                           const nsAString& aAccountname,
1531                                           int32_t* aResult) {
1532   nsCOMPtr<nsIPrompt> dialog;
1533   if (aMsgWindow) aMsgWindow->GetPromptDialog(getter_AddRefs(dialog));
1534 
1535   nsresult rv;
1536 
1537   // If we haven't got one, use a default dialog.
1538   if (!dialog) {
1539     nsCOMPtr<nsIWindowWatcher> wwatch =
1540         do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
1541     NS_ENSURE_SUCCESS(rv, rv);
1542 
1543     rv = wwatch->GetNewPrompter(0, getter_AddRefs(dialog));
1544     NS_ENSURE_SUCCESS(rv, rv);
1545   }
1546 
1547   nsCOMPtr<nsIStringBundleService> bundleSvc =
1548       mozilla::services::GetStringBundleService();
1549   NS_ENSURE_TRUE(bundleSvc, NS_ERROR_UNEXPECTED);
1550 
1551   nsCOMPtr<nsIStringBundle> bundle;
1552   rv = bundleSvc->CreateBundle("chrome://messenger/locale/messenger.properties",
1553                                getter_AddRefs(bundle));
1554   NS_ENSURE_SUCCESS(rv, rv);
1555 
1556   nsString message;
1557   AutoTArray<nsString, 2> formatStrings;
1558   CopyUTF8toUTF16(aHostname, *formatStrings.AppendElement());
1559   CopyUTF8toUTF16(aUsername, *formatStrings.AppendElement());
1560 
1561   rv = bundle->FormatStringFromName("mailServerLoginFailed2", formatStrings,
1562                                     message);
1563   NS_ENSURE_SUCCESS(rv, rv);
1564 
1565   nsString title;
1566   if (aAccountname.IsEmpty()) {
1567     // Account name may be empty e.g. on a SMTP server.
1568     rv = bundle->GetStringFromName("mailServerLoginFailedTitle", title);
1569   } else {
1570     AutoTArray<nsString, 1> formatStrings = {nsString(aAccountname)};
1571     rv = bundle->FormatStringFromName("mailServerLoginFailedTitleWithAccount",
1572                                       formatStrings, title);
1573   }
1574   NS_ENSURE_SUCCESS(rv, rv);
1575 
1576   nsString button0;
1577   rv = bundle->GetStringFromName("mailServerLoginFailedRetryButton", button0);
1578   NS_ENSURE_SUCCESS(rv, rv);
1579 
1580   nsString button2;
1581   rv = bundle->GetStringFromName("mailServerLoginFailedEnterNewPasswordButton",
1582                                  button2);
1583   NS_ENSURE_SUCCESS(rv, rv);
1584 
1585   bool dummyValue = false;
1586   return dialog->ConfirmEx(
1587       title.get(), message.get(),
1588       (nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_0) +
1589           (nsIPrompt::BUTTON_TITLE_CANCEL * nsIPrompt::BUTTON_POS_1) +
1590           (nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2),
1591       button0.get(), nullptr, button2.get(), nullptr, &dummyValue, aResult);
1592 }
1593 
MsgConvertAgeInDaysToCutoffDate(int32_t ageInDays)1594 NS_MSG_BASE PRTime MsgConvertAgeInDaysToCutoffDate(int32_t ageInDays) {
1595   PRTime now = PR_Now();
1596 
1597   return now - PR_USEC_PER_DAY * ageInDays;
1598 }
1599 
1600 NS_MSG_BASE nsresult
MsgTermListToString(nsTArray<RefPtr<nsIMsgSearchTerm>> const & aTermList,nsCString & aOutString)1601 MsgTermListToString(nsTArray<RefPtr<nsIMsgSearchTerm>> const& aTermList,
1602                     nsCString& aOutString) {
1603   nsresult rv = NS_OK;
1604   for (nsIMsgSearchTerm* term : aTermList) {
1605     nsAutoCString stream;
1606 
1607     if (aOutString.Length() > 1) aOutString += ' ';
1608 
1609     bool booleanAnd;
1610     bool matchAll;
1611     term->GetBooleanAnd(&booleanAnd);
1612     term->GetMatchAll(&matchAll);
1613     if (matchAll) {
1614       aOutString += "ALL";
1615       continue;
1616     } else if (booleanAnd)
1617       aOutString += "AND (";
1618     else
1619       aOutString += "OR (";
1620 
1621     rv = term->GetTermAsString(stream);
1622     NS_ENSURE_SUCCESS(rv, rv);
1623 
1624     aOutString += stream;
1625     aOutString += ')';
1626   }
1627   return rv;
1628 }
1629 
ParseUint64Str(const char * str)1630 NS_MSG_BASE uint64_t ParseUint64Str(const char* str) {
1631 #ifdef XP_WIN
1632   {
1633     char* endPtr;
1634     return _strtoui64(str, &endPtr, 10);
1635   }
1636 #else
1637   return strtoull(str, nullptr, 10);
1638 #endif
1639 }
1640 
MsgUnhex(const char * aHexString,size_t aNumChars)1641 NS_MSG_BASE uint64_t MsgUnhex(const char* aHexString, size_t aNumChars) {
1642   // Large numbers will not fit into uint64_t.
1643   NS_ASSERTION(aNumChars <= 16, "Hex literal too long to convert!");
1644 
1645   uint64_t result = 0;
1646   for (size_t i = 0; i < aNumChars; i++) {
1647     unsigned char c = aHexString[i];
1648     uint8_t digit;
1649     if ((c >= '0') && (c <= '9'))
1650       digit = (c - '0');
1651     else if ((c >= 'a') && (c <= 'f'))
1652       digit = ((c - 'a') + 10);
1653     else if ((c >= 'A') && (c <= 'F'))
1654       digit = ((c - 'A') + 10);
1655     else
1656       break;
1657 
1658     result = (result << 4) | digit;
1659   }
1660 
1661   return result;
1662 }
1663 
MsgIsHex(const char * aHexString,size_t aNumChars)1664 NS_MSG_BASE bool MsgIsHex(const char* aHexString, size_t aNumChars) {
1665   for (size_t i = 0; i < aNumChars; i++) {
1666     if (!isxdigit(aHexString[i])) return false;
1667   }
1668   return true;
1669 }
1670 
MsgStreamMsgHeaders(nsIInputStream * aInputStream,nsIStreamListener * aConsumer)1671 NS_MSG_BASE nsresult MsgStreamMsgHeaders(nsIInputStream* aInputStream,
1672                                          nsIStreamListener* aConsumer) {
1673   mozilla::UniquePtr<nsLineBuffer<char>> lineBuffer(new nsLineBuffer<char>);
1674 
1675   nsresult rv;
1676 
1677   nsAutoCString msgHeaders;
1678   nsAutoCString curLine;
1679 
1680   bool more = true;
1681 
1682   // We want to NS_ReadLine until we get to a blank line (the end of the
1683   // headers)
1684   while (more) {
1685     rv = NS_ReadLine(aInputStream, lineBuffer.get(), curLine, &more);
1686     NS_ENSURE_SUCCESS(rv, rv);
1687     if (curLine.IsEmpty()) break;
1688     msgHeaders.Append(curLine);
1689     msgHeaders.AppendLiteral("\r\n");
1690   }
1691   lineBuffer.reset();
1692   nsCOMPtr<nsIStringInputStream> hdrsStream =
1693       do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
1694   NS_ENSURE_SUCCESS(rv, rv);
1695   hdrsStream->SetData(msgHeaders.get(), msgHeaders.Length());
1696 
1697   nsCOMPtr<nsIInputStreamPump> pump;
1698   rv = NS_NewInputStreamPump(getter_AddRefs(pump), hdrsStream.forget());
1699   NS_ENSURE_SUCCESS(rv, rv);
1700 
1701   return pump->AsyncRead(aConsumer);
1702 }
1703 
MsgDetectCharsetFromFile(nsIFile * aFile,nsACString & aCharset)1704 NS_MSG_BASE nsresult MsgDetectCharsetFromFile(nsIFile* aFile,
1705                                               nsACString& aCharset) {
1706   // We do the detection in this order:
1707   // Check BOM.
1708   // If no BOM, run localized detection (Russian, Ukrainian or Japanese).
1709   // We need to run this first, since ISO-2022-JP is 7bit ASCII and would be
1710   // detected as UTF-8. If ISO-2022-JP not detected, check for UTF-8. If no
1711   // UTF-8, but detector detected something, use that, otherwise return an
1712   // error.
1713   aCharset.Truncate();
1714 
1715   nsresult rv;
1716   nsCOMPtr<nsIInputStream> inputStream;
1717   rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), aFile);
1718   NS_ENSURE_SUCCESS(rv, rv);
1719 
1720   // Check the BOM.
1721   char sniffBuf[3];
1722   uint32_t numRead;
1723   rv = inputStream->Read(sniffBuf, sizeof(sniffBuf), &numRead);
1724 
1725   if (numRead >= 2 && sniffBuf[0] == (char)0xfe && sniffBuf[1] == (char)0xff) {
1726     aCharset = "UTF-16BE";
1727   } else if (numRead >= 2 && sniffBuf[0] == (char)0xff &&
1728              sniffBuf[1] == (char)0xfe) {
1729     aCharset = "UTF-16LE";
1730   } else if (numRead >= 3 && sniffBuf[0] == (char)0xef &&
1731              sniffBuf[1] == (char)0xbb && sniffBuf[2] == (char)0xbf) {
1732     aCharset = "UTF-8";
1733   }
1734   if (!aCharset.IsEmpty()) return NS_OK;
1735 
1736   // Position back to the beginning.
1737   nsCOMPtr<nsISeekableStream> seekStream = do_QueryInterface(inputStream);
1738   if (seekStream) seekStream->Seek(nsISeekableStream::NS_SEEK_SET, 0);
1739 
1740   // Use detector.
1741   mozilla::UniquePtr<mozilla::EncodingDetector> detector =
1742       mozilla::EncodingDetector::Create();
1743   char buffer[1024];
1744   numRead = 0;
1745   while (NS_SUCCEEDED(inputStream->Read(buffer, sizeof(buffer), &numRead))) {
1746     mozilla::Span<const uint8_t> src =
1747         mozilla::AsBytes(mozilla::Span(buffer, numRead));
1748     Unused << detector->Feed(src, false);
1749     if (numRead == 0) {
1750       break;
1751     }
1752   }
1753   Unused << detector->Feed(nullptr, true);
1754   auto encoding = detector->Guess(nullptr, true);
1755   encoding->Name(aCharset);
1756   return NS_OK;
1757 }
1758 
1759 /*
1760  * Converts a buffer to plain text. Some conversions may
1761  * or may not work with certain end charsets which is why we
1762  * need that as an argument to the function. If charset is
1763  * unknown or deemed of no importance NULL could be passed.
1764  */
ConvertBufToPlainText(nsString & aConBuf,bool formatFlowed,bool formatOutput,bool disallowBreaks)1765 NS_MSG_BASE nsresult ConvertBufToPlainText(nsString& aConBuf, bool formatFlowed,
1766                                            bool formatOutput,
1767                                            bool disallowBreaks) {
1768   if (aConBuf.IsEmpty()) return NS_OK;
1769 
1770   int32_t wrapWidth = 72;
1771   nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
1772 
1773   if (pPrefBranch) {
1774     pPrefBranch->GetIntPref("mailnews.wraplength", &wrapWidth);
1775     // Let sanity reign!
1776     if (wrapWidth == 0 || wrapWidth > 990)
1777       wrapWidth = 990;
1778     else if (wrapWidth < 10)
1779       wrapWidth = 10;
1780   }
1781 
1782   uint32_t converterFlags = nsIDocumentEncoder::OutputPersistNBSP;
1783   if (formatFlowed) converterFlags |= nsIDocumentEncoder::OutputFormatFlowed;
1784   if (formatOutput) converterFlags |= nsIDocumentEncoder::OutputFormatted;
1785   if (disallowBreaks)
1786     converterFlags |= nsIDocumentEncoder::OutputDisallowLineBreaking;
1787 
1788   nsCOMPtr<nsIParserUtils> utils = do_GetService(NS_PARSERUTILS_CONTRACTID);
1789   return utils->ConvertToPlainText(aConBuf, converterFlags, wrapWidth, aConBuf);
1790 }
1791 
msgKeyFromInt(uint32_t aValue)1792 NS_MSG_BASE nsMsgKey msgKeyFromInt(uint32_t aValue) { return aValue; }
1793 
msgKeyFromInt(uint64_t aValue)1794 NS_MSG_BASE nsMsgKey msgKeyFromInt(uint64_t aValue) {
1795   NS_ASSERTION(aValue <= PR_UINT32_MAX, "Msg key value too big!");
1796   return aValue;
1797 }
1798 
msgKeyToInt(nsMsgKey aMsgKey)1799 NS_MSG_BASE uint32_t msgKeyToInt(nsMsgKey aMsgKey) { return (uint32_t)aMsgKey; }
1800 
1801 // Helper function to extract a query qualifier.
MsgExtractQueryPart(const nsACString & spec,const char * queryToExtract)1802 nsCString MsgExtractQueryPart(const nsACString& spec,
1803                               const char* queryToExtract) {
1804   nsCString queryPart;
1805   int32_t queryIndex = PromiseFlatCString(spec).Find(queryToExtract);
1806   if (queryIndex == kNotFound) return queryPart;
1807 
1808   int32_t queryEnd = spec.FindChar('&', queryIndex + 1);
1809   if (queryEnd == kNotFound) queryEnd = spec.FindChar('?', queryIndex + 1);
1810   if (queryEnd == kNotFound) {
1811     // Nothing follows, so return from where the query qualifier started.
1812     queryPart.Assign(Substring(spec, queryIndex));
1813   } else {
1814     // Return the substring that represents the query qualifier.
1815     queryPart.Assign(Substring(spec, queryIndex, queryEnd - queryIndex));
1816   }
1817   return queryPart;
1818 }
1819 
1820 // Helper function to remove query part from URL spec or path.
MsgRemoveQueryPart(nsCString & aSpec)1821 void MsgRemoveQueryPart(nsCString& aSpec) {
1822   // Sadly the query part can have different forms, these were seen
1823   // "in the wild", even with two ?:
1824   // /;section=2?part=1.2&filename=A01.JPG
1825   // ?section=2?part=1.2&filename=A01.JPG&type=image/jpeg&filename=A01.JPG
1826   // ?header=quotebody/;section=2.2?part=1.2.2&filename=lijbmghmkilicioj.png
1827   // ?part=1.2&type=image/jpeg&filename=IMG_C0030.jpg
1828   // ?header=quotebody&part=1.2&filename=lijbmghmkilicioj.png
1829 
1830   // Truncate path at the first of /; or ?
1831   int32_t ind = aSpec.FindChar('?');
1832   if (ind != kNotFound) aSpec.SetLength(ind);
1833   ind = aSpec.Find("/;");
1834   if (ind != kNotFound) aSpec.SetLength(ind);
1835 }
1836