summaryrefslogtreecommitdiffstats
path: root/mailnews/base/util/nsMsgUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/base/util/nsMsgUtils.cpp')
-rw-r--r--mailnews/base/util/nsMsgUtils.cpp2520
1 files changed, 2520 insertions, 0 deletions
diff --git a/mailnews/base/util/nsMsgUtils.cpp b/mailnews/base/util/nsMsgUtils.cpp
new file mode 100644
index 000000000..0ed04cd47
--- /dev/null
+++ b/mailnews/base/util/nsMsgUtils.cpp
@@ -0,0 +1,2520 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "msgCore.h"
+#include "nsIMsgHdr.h"
+#include "nsMsgUtils.h"
+#include "nsMsgFolderFlags.h"
+#include "nsMsgMessageFlags.h"
+#include "nsStringGlue.h"
+#include "nsIServiceManager.h"
+#include "nsCOMPtr.h"
+#include "nsIFolderLookupService.h"
+#include "nsIImapUrl.h"
+#include "nsIMailboxUrl.h"
+#include "nsINntpUrl.h"
+#include "nsMsgNewsCID.h"
+#include "nsMsgLocalCID.h"
+#include "nsMsgBaseCID.h"
+#include "nsMsgImapCID.h"
+#include "nsMsgI18N.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsCharTraits.h"
+#include "prprf.h"
+#include "prmem.h"
+#include "nsNetCID.h"
+#include "nsIIOService.h"
+#include "nsIRDFService.h"
+#include "nsIMimeConverter.h"
+#include "nsMsgMimeCID.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIPrefLocalizedString.h"
+#include "nsIRelativeFilePref.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsISpamSettings.h"
+#include "nsICryptoHash.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIRssIncomingServer.h"
+#include "nsIMsgFolder.h"
+#include "nsIMsgProtocolInfo.h"
+#include "nsIMsgMessageService.h"
+#include "nsIMsgAccountManager.h"
+#include "nsIOutputStream.h"
+#include "nsMsgFileStream.h"
+#include "nsIFileURL.h"
+#include "nsNetUtil.h"
+#include "nsProtocolProxyService.h"
+#include "nsIMsgDatabase.h"
+#include "nsIMutableArray.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsArrayUtils.h"
+#include "nsIStringBundle.h"
+#include "nsIMsgWindow.h"
+#include "nsIWindowWatcher.h"
+#include "nsIPrompt.h"
+// Disable deprecation warnings generated by nsISupportsArray and associated
+// classes.
+#if defined(__GNUC__)
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#elif defined(_MSC_VER)
+#pragma warning (disable : 4996)
+#endif
+#include "nsISupportsArray.h"
+#include "nsIMsgSearchTerm.h"
+#include "nsTextFormatter.h"
+#include "nsIAtomService.h"
+#include "nsIStreamListener.h"
+#include "nsReadLine.h"
+#include "nsICharsetDetectionObserver.h"
+#include "nsICharsetDetector.h"
+#include "nsILineInputStream.h"
+#include "nsIPlatformCharset.h"
+#include "nsIParserUtils.h"
+#include "nsICharsetConverterManager.h"
+#include "nsIDocumentEncoder.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Services.h"
+#include "locale.h"
+#include "nsStringStream.h"
+#include "nsIInputStreamPump.h"
+#include "nsIChannel.h"
+
+/* for logging to Error Console */
+#include "nsIScriptError.h"
+#include "nsIConsoleService.h"
+
+// Log an error string to the error console
+// (adapted from nsContentUtils::LogSimpleConsoleError).
+// Flag can indicate error, warning or info.
+NS_MSG_BASE void MsgLogToConsole4(const nsAString &aErrorText,
+ const nsAString &aFilename,
+ uint32_t aLinenumber,
+ uint32_t aFlag)
+{
+ nsCOMPtr<nsIScriptError> scriptError =
+ do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
+ if (NS_WARN_IF(!scriptError))
+ return;
+ nsCOMPtr<nsIConsoleService> console =
+ do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+ if (NS_WARN_IF(!console))
+ return;
+ if (NS_FAILED(scriptError->Init(aErrorText,
+ aFilename,
+ EmptyString(),
+ aLinenumber,
+ 0,
+ aFlag,
+ "mailnews")))
+ return;
+ console->LogMessage(scriptError);
+ return;
+}
+
+using namespace mozilla;
+using namespace mozilla::net;
+
+static NS_DEFINE_CID(kImapUrlCID, NS_IMAPURL_CID);
+static NS_DEFINE_CID(kCMailboxUrl, NS_MAILBOXURL_CID);
+static NS_DEFINE_CID(kCNntpUrlCID, NS_NNTPURL_CID);
+
+#define ILLEGAL_FOLDER_CHARS ";#"
+#define ILLEGAL_FOLDER_CHARS_AS_FIRST_LETTER "."
+#define ILLEGAL_FOLDER_CHARS_AS_LAST_LETTER ".~ "
+
+nsresult GetMessageServiceContractIDForURI(const char *uri, nsCString &contractID)
+{
+ nsresult rv = NS_OK;
+ //Find protocol
+ nsAutoCString uriStr(uri);
+ int32_t pos = uriStr.FindChar(':');
+ if (pos == -1)
+ return NS_ERROR_FAILURE;
+
+ nsAutoCString protocol(StringHead(uriStr, pos));
+
+ if (protocol.Equals("file") && uriStr.Find("application/x-message-display") != -1)
+ protocol.Assign("mailbox");
+ //Build message service contractid
+ contractID = "@mozilla.org/messenger/messageservice;1?type=";
+ contractID += protocol.get();
+
+ return rv;
+}
+
+nsresult GetMessageServiceFromURI(const nsACString& uri, nsIMsgMessageService **aMessageService)
+{
+ nsresult rv;
+
+ nsAutoCString contractID;
+ rv = GetMessageServiceContractIDForURI(PromiseFlatCString(uri).get(), contractID);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr <nsIMsgMessageService> msgService = do_GetService(contractID.get(), &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ NS_IF_ADDREF(*aMessageService = msgService);
+ return rv;
+}
+
+nsresult GetMsgDBHdrFromURI(const char *uri, nsIMsgDBHdr **msgHdr)
+{
+ nsCOMPtr <nsIMsgMessageService> msgMessageService;
+ nsresult rv = GetMessageServiceFromURI(nsDependentCString(uri), getter_AddRefs(msgMessageService));
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (!msgMessageService) return NS_ERROR_FAILURE;
+
+ return msgMessageService->MessageURIToMsgHdr(uri, msgHdr);
+}
+
+nsresult CreateStartupUrl(const char *uri, nsIURI** aUrl)
+{
+ nsresult rv = NS_ERROR_NULL_POINTER;
+ if (!uri || !*uri || !aUrl) return rv;
+ *aUrl = nullptr;
+
+ // XXX fix this, so that base doesn't depend on imap, local or news.
+ // we can't do NS_NewURI(uri, aUrl), because these are imap-message://, mailbox-message://, news-message:// uris.
+ // I think we should do something like GetMessageServiceFromURI() to get the service, and then have the service create the
+ // appropriate nsI*Url, and then QI to nsIURI, and return it.
+ // see bug #110689
+ if (PL_strncasecmp(uri, "imap", 4) == 0)
+ {
+ nsCOMPtr<nsIImapUrl> imapUrl = do_CreateInstance(kImapUrlCID, &rv);
+
+ if (NS_SUCCEEDED(rv) && imapUrl)
+ rv = imapUrl->QueryInterface(NS_GET_IID(nsIURI),
+ (void**) aUrl);
+ }
+ else if (PL_strncasecmp(uri, "mailbox", 7) == 0)
+ {
+ nsCOMPtr<nsIMailboxUrl> mailboxUrl = do_CreateInstance(kCMailboxUrl, &rv);
+ if (NS_SUCCEEDED(rv) && mailboxUrl)
+ rv = mailboxUrl->QueryInterface(NS_GET_IID(nsIURI),
+ (void**) aUrl);
+ }
+ else if (PL_strncasecmp(uri, "news", 4) == 0)
+ {
+ nsCOMPtr<nsINntpUrl> nntpUrl = do_CreateInstance(kCNntpUrlCID, &rv);
+ if (NS_SUCCEEDED(rv) && nntpUrl)
+ rv = nntpUrl->QueryInterface(NS_GET_IID(nsIURI),
+ (void**) aUrl);
+ }
+ if (*aUrl) // SetSpec can fail, for mailbox urls, but we still have a url.
+ (void) (*aUrl)->SetSpec(nsDependentCString(uri));
+ return rv;
+}
+
+
+// Where should this live? It's a utility used to convert a string priority,
+// e.g., "High, Low, Normal" to an enum.
+// Perhaps we should have an interface that groups together all these
+// utilities...
+nsresult NS_MsgGetPriorityFromString(
+ const char * const priority,
+ nsMsgPriorityValue & outPriority)
+{
+ if (!priority)
+ return NS_ERROR_NULL_POINTER;
+
+ // Note: Checking the values separately and _before_ the names,
+ // hoping for a much faster match;
+ // Only _drawback_, as "priority" handling is not truly specified:
+ // some softwares may have the number meanings reversed (1=Lowest) !?
+ if (PL_strchr(priority, '1'))
+ outPriority = nsMsgPriority::highest;
+ else if (PL_strchr(priority, '2'))
+ outPriority = nsMsgPriority::high;
+ else if (PL_strchr(priority, '3'))
+ outPriority = nsMsgPriority::normal;
+ else if (PL_strchr(priority, '4'))
+ outPriority = nsMsgPriority::low;
+ else if (PL_strchr(priority, '5'))
+ outPriority = nsMsgPriority::lowest;
+ else if (PL_strcasestr(priority, "Highest"))
+ outPriority = nsMsgPriority::highest;
+ // Important: "High" must be tested after "Highest" !
+ else if (PL_strcasestr(priority, "High") ||
+ PL_strcasestr(priority, "Urgent"))
+ outPriority = nsMsgPriority::high;
+ else if (PL_strcasestr(priority, "Normal"))
+ outPriority = nsMsgPriority::normal;
+ else if (PL_strcasestr(priority, "Lowest"))
+ outPriority = nsMsgPriority::lowest;
+ // Important: "Low" must be tested after "Lowest" !
+ else if (PL_strcasestr(priority, "Low") ||
+ PL_strcasestr(priority, "Non-urgent"))
+ outPriority = nsMsgPriority::low;
+ else
+ // "Default" case gets default value.
+ outPriority = nsMsgPriority::Default;
+
+ return NS_OK;
+}
+
+nsresult NS_MsgGetPriorityValueString(
+ const nsMsgPriorityValue p,
+ nsACString & outValueString)
+{
+ switch (p)
+ {
+ case nsMsgPriority::highest:
+ outValueString.AssignLiteral("1");
+ break;
+ case nsMsgPriority::high:
+ outValueString.AssignLiteral("2");
+ break;
+ case nsMsgPriority::normal:
+ outValueString.AssignLiteral("3");
+ break;
+ case nsMsgPriority::low:
+ outValueString.AssignLiteral("4");
+ break;
+ case nsMsgPriority::lowest:
+ outValueString.AssignLiteral("5");
+ break;
+ case nsMsgPriority::none:
+ case nsMsgPriority::notSet:
+ // Note: '0' is a "fake" value; we expect to never be in this case.
+ outValueString.AssignLiteral("0");
+ break;
+ default:
+ NS_ASSERTION(false, "invalid priority value");
+ }
+
+ return NS_OK;
+}
+
+nsresult NS_MsgGetUntranslatedPriorityName(
+ const nsMsgPriorityValue p,
+ nsACString & outName)
+{
+ switch (p)
+ {
+ case nsMsgPriority::highest:
+ outName.AssignLiteral("Highest");
+ break;
+ case nsMsgPriority::high:
+ outName.AssignLiteral("High");
+ break;
+ case nsMsgPriority::normal:
+ outName.AssignLiteral("Normal");
+ break;
+ case nsMsgPriority::low:
+ outName.AssignLiteral("Low");
+ break;
+ case nsMsgPriority::lowest:
+ outName.AssignLiteral("Lowest");
+ break;
+ case nsMsgPriority::none:
+ case nsMsgPriority::notSet:
+ // Note: 'None' is a "fake" value; we expect to never be in this case.
+ outName.AssignLiteral("None");
+ break;
+ default:
+ NS_ASSERTION(false, "invalid priority value");
+ }
+
+ return NS_OK;
+}
+
+
+/* this used to be XP_StringHash2 from xp_hash.c */
+/* phong's linear congruential hash */
+static uint32_t StringHash(const char *ubuf, int32_t len = -1)
+{
+ unsigned char * buf = (unsigned char*) ubuf;
+ uint32_t h=1;
+ unsigned char *end = buf + (len == -1 ? strlen(ubuf) : len);
+ while(buf < end) {
+ h = 0x63c63cd9*h + 0x9c39c33d + (int32_t)*buf;
+ buf++;
+ }
+ return h;
+}
+
+inline uint32_t StringHash(const nsAutoString& str)
+{
+ const char16_t *strbuf = str.get();
+ return StringHash(reinterpret_cast<const char*>(strbuf),
+ str.Length() * 2);
+}
+
+#ifndef MOZILLA_INTERNAL_API
+static int GetFindInSetFilter(const char* aChars)
+{
+ uint8_t filter = 0;
+ while (*aChars)
+ filter |= *aChars++;
+ return ~filter;
+}
+#endif
+
+/* Utility functions used in a few places in mailnews */
+int32_t
+MsgFindCharInSet(const nsCString &aString,
+ const char* aChars, uint32_t aOffset)
+{
+#ifdef MOZILLA_INTERNAL_API
+ return aString.FindCharInSet(aChars, aOffset);
+#else
+ const char *str;
+ uint32_t len = aString.BeginReading(&str);
+ int filter = GetFindInSetFilter(aChars);
+ for (uint32_t index = aOffset; index < len; index++) {
+ if (!(str[index] & filter) && strchr(aChars, str[index]))
+ return index;
+ }
+ return -1;
+#endif
+}
+
+int32_t
+MsgFindCharInSet(const nsString &aString,
+ const char* aChars, uint32_t aOffset)
+{
+#ifdef MOZILLA_INTERNAL_API
+ return aString.FindCharInSet(aChars, aOffset);
+#else
+ const char16_t *str;
+ uint32_t len = aString.BeginReading(&str);
+ int filter = GetFindInSetFilter(aChars);
+ for (uint32_t index = aOffset; index < len; index++) {
+ if (!(str[index] & filter) && strchr(aChars, str[index]))
+ return index;
+ }
+ return -1;
+#endif
+}
+
+static bool ConvertibleToNative(const nsAutoString& str)
+{
+ nsAutoCString native;
+ nsAutoString roundTripped;
+#ifdef MOZILLA_INTERNAL_API
+ NS_CopyUnicodeToNative(str, native);
+ NS_CopyNativeToUnicode(native, roundTripped);
+#else
+ nsMsgI18NConvertFromUnicode(nsMsgI18NFileSystemCharset(), str, native);
+ nsMsgI18NConvertToUnicode(nsMsgI18NFileSystemCharset(), native, roundTripped);
+#endif
+ return str.Equals(roundTripped);
+}
+
+#if defined(XP_UNIX)
+ const static uint32_t MAX_LEN = 55;
+#elif defined(XP_WIN32)
+ const static uint32_t MAX_LEN = 55;
+#else
+ #error need_to_define_your_max_filename_length
+#endif
+
+nsresult NS_MsgHashIfNecessary(nsAutoCString &name)
+{
+ if (name.IsEmpty())
+ return NS_OK; // Nothing to do.
+ nsAutoCString str(name);
+
+ // Given a filename, make it safe for filesystem
+ // certain filenames require hashing because they
+ // are too long or contain illegal characters
+ int32_t illegalCharacterIndex = MsgFindCharInSet(str,
+ FILE_PATH_SEPARATOR
+ FILE_ILLEGAL_CHARACTERS
+ ILLEGAL_FOLDER_CHARS, 0);
+
+ // Need to check the first ('.') and last ('.', '~' and ' ') char
+ if (illegalCharacterIndex == -1)
+ {
+ int32_t lastIndex = str.Length() - 1;
+ if (NS_LITERAL_CSTRING(ILLEGAL_FOLDER_CHARS_AS_FIRST_LETTER).FindChar(str[0]) != -1)
+ illegalCharacterIndex = 0;
+ else if (NS_LITERAL_CSTRING(ILLEGAL_FOLDER_CHARS_AS_LAST_LETTER).FindChar(str[lastIndex]) != -1)
+ illegalCharacterIndex = lastIndex;
+ else
+ illegalCharacterIndex = -1;
+ }
+
+ char hashedname[MAX_LEN + 1];
+ if (illegalCharacterIndex == -1)
+ {
+ // no illegal chars, it's just too long
+ // keep the initial part of the string, but hash to make it fit
+ if (str.Length() > MAX_LEN)
+ {
+ PL_strncpy(hashedname, str.get(), MAX_LEN + 1);
+ PR_snprintf(hashedname + MAX_LEN - 8, 9, "%08lx",
+ (unsigned long) StringHash(str.get()));
+ name = hashedname;
+ }
+ }
+ else
+ {
+ // found illegal chars, hash the whole thing
+ // if we do substitution, then hash, two strings
+ // could hash to the same value.
+ // for example, on mac: "foo__bar", "foo:_bar", "foo::bar"
+ // would map to "foo_bar". this way, all three will map to
+ // different values
+ PR_snprintf(hashedname, 9, "%08lx",
+ (unsigned long) StringHash(str.get()));
+ name = hashedname;
+ }
+
+ return NS_OK;
+}
+
+// XXX : The number of UTF-16 2byte code units are half the number of
+// bytes in legacy encodings for CJK strings and non-Latin1 in UTF-8.
+// The ratio can be 1/3 for CJK strings in UTF-8. However, we can
+// get away with using the same MAX_LEN for nsCString and nsString
+// because MAX_LEN is defined rather conservatively in the first place.
+nsresult NS_MsgHashIfNecessary(nsAutoString &name)
+{
+ if (name.IsEmpty())
+ return NS_OK; // Nothing to do.
+ int32_t illegalCharacterIndex = MsgFindCharInSet(name,
+ FILE_PATH_SEPARATOR
+ FILE_ILLEGAL_CHARACTERS
+ ILLEGAL_FOLDER_CHARS, 0);
+
+ // Need to check the first ('.') and last ('.', '~' and ' ') char
+ if (illegalCharacterIndex == -1)
+ {
+ int32_t lastIndex = name.Length() - 1;
+ if (NS_LITERAL_STRING(ILLEGAL_FOLDER_CHARS_AS_FIRST_LETTER).FindChar(name[0]) != -1)
+ illegalCharacterIndex = 0;
+ else if (NS_LITERAL_STRING(ILLEGAL_FOLDER_CHARS_AS_LAST_LETTER).FindChar(name[lastIndex]) != -1)
+ illegalCharacterIndex = lastIndex;
+ else
+ illegalCharacterIndex = -1;
+ }
+
+ char hashedname[9];
+ int32_t keptLength = -1;
+ if (illegalCharacterIndex != -1)
+ keptLength = illegalCharacterIndex;
+ else if (!ConvertibleToNative(name))
+ keptLength = 0;
+ else if (name.Length() > MAX_LEN) {
+ keptLength = MAX_LEN-8;
+ // To avoid keeping only the high surrogate of a surrogate pair
+ if (NS_IS_HIGH_SURROGATE(name.CharAt(keptLength-1)))
+ --keptLength;
+ }
+
+ if (keptLength >= 0) {
+ PR_snprintf(hashedname, 9, "%08lx", (unsigned long) StringHash(name));
+ name.SetLength(keptLength);
+ name.Append(NS_ConvertASCIItoUTF16(hashedname));
+ }
+
+ return NS_OK;
+}
+
+nsresult FormatFileSize(int64_t size, bool useKB, nsAString &formattedSize)
+{
+ NS_NAMED_LITERAL_STRING(byteAbbr, "byteAbbreviation2");
+ NS_NAMED_LITERAL_STRING(kbAbbr, "kiloByteAbbreviation2");
+ NS_NAMED_LITERAL_STRING(mbAbbr, "megaByteAbbreviation2");
+ NS_NAMED_LITERAL_STRING(gbAbbr, "gigaByteAbbreviation2");
+
+ const char16_t *sizeAbbrNames[] = {
+ byteAbbr.get(), kbAbbr.get(), mbAbbr.get(), gbAbbr.get()
+ };
+
+ nsresult rv;
+
+ nsCOMPtr<nsIStringBundleService> bundleSvc =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleSvc, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleSvc->CreateBundle("chrome://messenger/locale/messenger.properties",
+ getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ double unitSize = size < 0 ? 0.0 : size;
+ uint32_t unitIndex = 0;
+
+ if (useKB) {
+ // Start by formatting in kilobytes
+ unitSize /= 1024;
+ if (unitSize < 0.1 && unitSize != 0)
+ unitSize = 0.1;
+ unitIndex++;
+ }
+
+ // Convert to next unit if it needs 4 digits (after rounding), but only if
+ // we know the name of the next unit
+ while ((unitSize >= 999.5) && (unitIndex < ArrayLength(sizeAbbrNames) - 1))
+ {
+ unitSize /= 1024;
+ unitIndex++;
+ }
+
+ // Grab the string for the appropriate unit
+ nsString sizeAbbr;
+ rv = bundle->GetStringFromName(sizeAbbrNames[unitIndex],
+ getter_Copies(sizeAbbr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get rid of insignificant bits by truncating to 1 or 0 decimal points
+ // 0.1 -> 0.1; 1.2 -> 1.2; 12.3 -> 12.3; 123.4 -> 123; 234.5 -> 235
+ nsTextFormatter::ssprintf(
+ formattedSize, sizeAbbr.get(),
+ (unitIndex != 0) && (unitSize < 99.95 && unitSize != 0) ? 1 : 0, unitSize);
+
+ int32_t separatorPos = formattedSize.FindChar('.');
+ if (separatorPos != kNotFound) {
+ // The ssprintf returned a decimal number using a dot (.) as the decimal
+ // separator. Now we try to localize the separator.
+ // Try to get the decimal separator from the system's locale.
+ char *decimalPoint;
+#ifdef HAVE_LOCALECONV
+ struct lconv *locale = localeconv();
+ decimalPoint = locale->decimal_point;
+#else
+ decimalPoint = getenv("LOCALE_DECIMAL_POINT");
+#endif
+ NS_ConvertUTF8toUTF16 decimalSeparator(decimalPoint);
+ if (decimalSeparator.IsEmpty())
+ decimalSeparator.AssignLiteral(".");
+
+ formattedSize.Replace(separatorPos, 1, decimalSeparator);
+ }
+
+ return NS_OK;
+}
+
+nsresult NS_MsgCreatePathStringFromFolderURI(const char *aFolderURI,
+ nsCString& aPathCString,
+ const nsCString &aScheme,
+ bool aIsNewsFolder)
+{
+ // A file name has to be in native charset. Here we convert
+ // to UTF-16 and check for 'unsafe' characters before converting
+ // to native charset.
+ NS_ENSURE_TRUE(MsgIsUTF8(nsDependentCString(aFolderURI)), NS_ERROR_UNEXPECTED);
+ NS_ConvertUTF8toUTF16 oldPath(aFolderURI);
+
+ nsAutoString pathPiece, path;
+
+ int32_t startSlashPos = oldPath.FindChar('/');
+ int32_t endSlashPos = (startSlashPos >= 0)
+ ? oldPath.FindChar('/', startSlashPos + 1) - 1 : oldPath.Length() - 1;
+ if (endSlashPos < 0)
+ endSlashPos = oldPath.Length();
+#if defined(XP_UNIX) || defined(XP_MACOSX)
+ bool isLocalUri = aScheme.EqualsLiteral("none") ||
+ aScheme.EqualsLiteral("pop3") ||
+ aScheme.EqualsLiteral("rss");
+#endif
+ // trick to make sure we only add the path to the first n-1 folders
+ bool haveFirst=false;
+ while (startSlashPos != -1) {
+ pathPiece.Assign(Substring(oldPath, startSlashPos + 1, endSlashPos - startSlashPos));
+ // skip leading '/' (and other // style things)
+ if (!pathPiece.IsEmpty())
+ {
+
+ // add .sbd onto the previous path
+ if (haveFirst)
+ {
+ path.AppendLiteral(".sbd/");
+ }
+
+ if (aIsNewsFolder)
+ {
+ nsAutoCString tmp;
+ CopyUTF16toMUTF7(pathPiece, tmp);
+ CopyASCIItoUTF16(tmp, pathPiece);
+ }
+#if defined(XP_UNIX) || defined(XP_MACOSX)
+ // Don't hash path pieces because local mail folder uri's have already
+ // been hashed. We're only doing this on the mac to limit potential
+ // regressions.
+ if (!isLocalUri)
+#endif
+ NS_MsgHashIfNecessary(pathPiece);
+ path += pathPiece;
+ haveFirst=true;
+ }
+ // look for the next slash
+ startSlashPos = endSlashPos + 1;
+
+ endSlashPos = (startSlashPos >= 0)
+ ? oldPath.FindChar('/', startSlashPos + 1) - 1: oldPath.Length() - 1;
+ if (endSlashPos < 0)
+ endSlashPos = oldPath.Length();
+
+ if (startSlashPos >= endSlashPos)
+ break;
+ }
+#ifdef MOZILLA_INTERNAL_API
+ return NS_CopyUnicodeToNative(path, aPathCString);
+#else
+ return nsMsgI18NConvertFromUnicode(nsMsgI18NFileSystemCharset(), path, aPathCString);
+#endif
+}
+
+bool NS_MsgStripRE(const nsCString& subject, nsCString& modifiedSubject)
+{
+ bool result = false;
+
+ // Get localizedRe pref.
+ nsresult rv;
+ nsString utf16LocalizedRe;
+ NS_GetLocalizedUnicharPreferenceWithDefault(nullptr,
+ "mailnews.localizedRe",
+ EmptyString(),
+ utf16LocalizedRe);
+ NS_ConvertUTF16toUTF8 localizedRe(utf16LocalizedRe);
+
+ // Hardcoded "Re" so that no one can configure Mozilla standards incompatible.
+ nsAutoCString checkString("Re,RE,re,rE");
+ if (!localizedRe.IsEmpty()) {
+ checkString.Append(',');
+ checkString.Append(localizedRe);
+ }
+
+ // Decode the string.
+ nsCString decodedString;
+ nsCOMPtr<nsIMimeConverter> mimeConverter;
+ // We cannot strip "Re:" for RFC2047-encoded subject without modifying the original.
+ if (subject.Find("=?") != kNotFound)
+ {
+ mimeConverter = do_GetService(NS_MIME_CONVERTER_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ rv = mimeConverter->DecodeMimeHeaderToUTF8(subject,
+ nullptr, false, true, decodedString);
+ }
+
+ const char *s, *s_end;
+ if (decodedString.IsEmpty()) {
+ s = subject.BeginReading();
+ s_end = s + subject.Length();
+ } else {
+ s = decodedString.BeginReading();
+ s_end = s + decodedString.Length();
+ }
+
+AGAIN:
+ while (s < s_end && IS_SPACE(*s))
+ s++;
+
+ const char *tokPtr = checkString.get();
+ while (*tokPtr)
+ {
+ // Tokenize the comma separated list.
+ size_t tokenLength = 0;
+ while (*tokPtr && *tokPtr != ',') {
+ tokenLength++;
+ tokPtr++;
+ }
+ // Check if the beginning of s is the actual token.
+ if (tokenLength && !strncmp(s, tokPtr - tokenLength, tokenLength))
+ {
+ if (s[tokenLength] == ':')
+ {
+ s = s + tokenLength + 1; /* Skip over "Re:" */
+ result = true; /* Yes, we stripped it. */
+ goto AGAIN; /* Skip whitespace and try again. */
+ }
+ else if (s[tokenLength] == '[' || s[tokenLength] == '(')
+ {
+ const char *s2 = s + tokenLength + 1; /* Skip over "Re[" */
+
+ // Skip forward over digits after the "[".
+ while (s2 < (s_end - 2) && isdigit((unsigned char)*s2))
+ s2++;
+
+ // Now ensure that the following thing is "]:".
+ // Only if it is do we alter `s`.
+ if ((s2[0] == ']' || s2[0] == ')') && s2[1] == ':')
+ {
+ s = s2 + 2; /* Skip over "]:" */
+ result = true; /* Yes, we stripped it. */
+ goto AGAIN; /* Skip whitespace and try again. */
+ }
+ }
+ }
+ if (*tokPtr)
+ tokPtr++;
+ }
+
+ // If we didn't strip anything, we can return here.
+ if (!result)
+ return false;
+
+ if (decodedString.IsEmpty()) {
+ // We didn't decode anything, so just return a new string.
+ modifiedSubject.Assign(s);
+ return true;
+ }
+
+ // We decoded the string, so we need to encode it again. We always encode in UTF-8.
+ mimeConverter->EncodeMimePartIIStr_UTF8(nsDependentCString(s),
+ false, "UTF-8", sizeof("Subject:"),
+ nsIMimeConverter::MIME_ENCODED_WORD_SIZE, modifiedSubject);
+ return true;
+}
+
+/* Very similar to strdup except it free's too
+ */
+char * NS_MsgSACopy (char **destination, const char *source)
+{
+ if(*destination)
+ {
+ PR_Free(*destination);
+ *destination = 0;
+ }
+ if (! source)
+ *destination = nullptr;
+ else
+ {
+ *destination = (char *) PR_Malloc (PL_strlen(source) + 1);
+ if (*destination == nullptr)
+ return(nullptr);
+
+ PL_strcpy (*destination, source);
+ }
+ return *destination;
+}
+
+/* Again like strdup but it concatenates and free's and uses Realloc.
+*/
+char * NS_MsgSACat (char **destination, const char *source)
+{
+ if (source && *source)
+ {
+ int destLength = *destination ? PL_strlen(*destination) : 0;
+ char* newDestination = (char*) PR_Realloc(*destination, destLength + PL_strlen(source) + 1);
+ if (newDestination == nullptr)
+ return nullptr;
+
+ *destination = newDestination;
+ PL_strcpy(*destination + destLength, source);
+ }
+ return *destination;
+}
+
+nsresult NS_MsgEscapeEncodeURLPath(const nsAString& aStr, nsCString& aResult)
+{
+ return MsgEscapeString(NS_ConvertUTF16toUTF8(aStr), nsINetUtil::ESCAPE_URL_PATH, aResult);
+}
+
+nsresult NS_MsgDecodeUnescapeURLPath(const nsACString& aPath,
+ nsAString& aResult)
+{
+ nsAutoCString unescapedName;
+ MsgUnescapeString(aPath, nsINetUtil::ESCAPE_URL_FILE_BASENAME |
+ nsINetUtil::ESCAPE_URL_FORCED, unescapedName);
+ CopyUTF8toUTF16(unescapedName, aResult);
+ return NS_OK;
+}
+
+bool WeAreOffline()
+{
+ bool offline = false;
+
+ nsCOMPtr <nsIIOService> ioService =
+ mozilla::services::GetIOService();
+ if (ioService)
+ ioService->GetOffline(&offline);
+
+ return offline;
+}
+
+nsresult GetExistingFolder(const nsCString& aFolderURI, nsIMsgFolder **aFolder)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+
+ *aFolder = nullptr;
+
+ nsresult rv;
+ nsCOMPtr<nsIFolderLookupService> fls(do_GetService(NSIFLS_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = fls->GetFolderForURL(aFolderURI, aFolder);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return *aFolder ? NS_OK : NS_ERROR_FAILURE;
+}
+
+bool IsAFromSpaceLine(char *start, const char *end)
+{
+ bool rv = false;
+ while ((start < end) && (*start == '>'))
+ start++;
+ // If the leading '>'s are followed by an 'F' then we have a possible case here.
+ if ( (*start == 'F') && (end-start > 4) && !strncmp(start, "From ", 5) )
+ rv = true;
+ return rv;
+}
+
+//
+// This function finds all lines starting with "From " or "From " preceeding
+// with one or more '>' (ie, ">From", ">>From", etc) in the input buffer
+// (between 'start' and 'end') and prefix them with a ">" .
+//
+nsresult EscapeFromSpaceLine(nsIOutputStream *outputStream, char *start, const char *end)
+{
+ nsresult rv;
+ char *pChar;
+ uint32_t written;
+
+ pChar = start;
+ while (start < end)
+ {
+ while ((pChar < end) && (*pChar != '\r') && ((pChar + 1) < end) &&
+ (*(pChar + 1) != '\n'))
+ pChar++;
+ if ((pChar + 1) == end)
+ pChar++;
+
+ if (pChar < end)
+ {
+ // Found a line so check if it's a qualified "From " line.
+ if (IsAFromSpaceLine(start, pChar))
+ rv = outputStream->Write(">", 1, &written);
+ int32_t lineTerminatorCount = (*(pChar + 1) == '\n') ? 2 : 1;
+ rv = outputStream->Write(start, pChar - start + lineTerminatorCount, &written);
+ NS_ENSURE_SUCCESS(rv,rv);
+ pChar += lineTerminatorCount;
+ start = pChar;
+ }
+ else if (start < end)
+ {
+ // Check and flush out the remaining data and we're done.
+ if (IsAFromSpaceLine(start, end))
+ rv = outputStream->Write(">", 1, &written);
+ rv = outputStream->Write(start, end-start, &written);
+ NS_ENSURE_SUCCESS(rv,rv);
+ break;
+ }
+ }
+ return NS_OK;
+}
+
+nsresult IsRFC822HeaderFieldName(const char *aHdr, bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aHdr);
+ NS_ENSURE_ARG_POINTER(aResult);
+ uint32_t length = strlen(aHdr);
+ for(uint32_t i=0; i<length; i++)
+ {
+ char c = aHdr[i];
+ if ( c < '!' || c == ':' || c > '~')
+ {
+ *aResult = false;
+ return NS_OK;
+ }
+ }
+ *aResult = true;
+ return NS_OK;
+}
+
+// Warning, currently this routine only works for the Junk Folder
+nsresult
+GetOrCreateFolder(const nsACString &aURI, nsIUrlListener *aListener)
+{
+ nsresult rv;
+ nsCOMPtr <nsIRDFService> rdf = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // get the corresponding RDF resource
+ // RDF will create the folder resource if it doesn't already exist
+ nsCOMPtr<nsIRDFResource> resource;
+ rv = rdf->GetResource(aURI, getter_AddRefs(resource));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr <nsIMsgFolder> folderResource;
+ folderResource = do_QueryInterface(resource, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // don't check validity of folder - caller will handle creating it
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ // make sure that folder hierarchy is built so that legitimate parent-child relationship is established
+ rv = folderResource->GetServer(getter_AddRefs(server));
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!server)
+ return NS_ERROR_UNEXPECTED;
+
+ nsCOMPtr <nsIMsgFolder> msgFolder;
+ rv = server->GetMsgFolderFromURI(folderResource, aURI, getter_AddRefs(msgFolder));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr <nsIMsgFolder> parent;
+ rv = msgFolder->GetParent(getter_AddRefs(parent));
+ if (NS_FAILED(rv) || !parent)
+ {
+ nsCOMPtr <nsIFile> folderPath;
+ // for local folders, path is to the berkeley mailbox.
+ // for imap folders, path needs to have .msf appended to the name
+ msgFolder->GetFilePath(getter_AddRefs(folderPath));
+
+ nsCOMPtr<nsIMsgProtocolInfo> protocolInfo;
+ rv = server->GetProtocolInfo(getter_AddRefs(protocolInfo));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool isAsyncFolder;
+ rv = protocolInfo->GetFoldersCreatedAsync(&isAsyncFolder);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // if we can't get the path from the folder, then try to create the storage.
+ // for imap, it doesn't matter if the .msf file exists - it still might not
+ // exist on the server, so we should try to create it
+ bool exists = false;
+ if (!isAsyncFolder && folderPath)
+ folderPath->Exists(&exists);
+ if (!exists)
+ {
+ // Hack to work around a localization bug with the Junk Folder.
+ // Please see Bug #270261 for more information...
+ nsString localizedJunkName;
+ msgFolder->GetName(localizedJunkName);
+
+ // force the junk folder name to be Junk so it gets created on disk correctly...
+ msgFolder->SetName(NS_LITERAL_STRING("Junk"));
+ msgFolder->SetFlag(nsMsgFolderFlags::Junk);
+ rv = msgFolder->CreateStorageIfMissing(aListener);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // now restore the localized folder name...
+ msgFolder->SetName(localizedJunkName);
+
+ // XXX TODO
+ // JUNK MAIL RELATED
+ // ugh, I hate this hack
+ // we have to do this (for now)
+ // because imap and local are different (one creates folder asynch, the other synch)
+ // one will notify the listener, one will not.
+ // I blame nsMsgCopy.
+ // we should look into making it so no matter what the folder type
+ // we always call the listener
+ // this code should move into local folder's version of CreateStorageIfMissing()
+ if (!isAsyncFolder && aListener) {
+ rv = aListener->OnStartRunningUrl(nullptr);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = aListener->OnStopRunningUrl(nullptr, NS_OK);
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ }
+ }
+ else {
+ // if the folder exists, we should set the junk flag on it
+ // which is what the listener will do
+ if (aListener) {
+ rv = aListener->OnStartRunningUrl(nullptr);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = aListener->OnStopRunningUrl(nullptr, NS_OK);
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult IsRSSArticle(nsIURI * aMsgURI, bool *aIsRSSArticle)
+{
+ nsresult rv;
+ *aIsRSSArticle = false;
+
+ nsCOMPtr<nsIMsgMessageUrl> msgUrl = do_QueryInterface(aMsgURI, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ nsCString resourceURI;
+ msgUrl->GetUri(getter_Copies(resourceURI));
+
+ // get the msg service for this URI
+ nsCOMPtr<nsIMsgMessageService> msgService;
+ rv = GetMessageServiceFromURI(resourceURI, getter_AddRefs(msgService));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Check if the message is a feed message, regardless of folder.
+ uint32_t flags;
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ rv = msgService->MessageURIToMsgHdr(resourceURI.get(), getter_AddRefs(msgHdr));
+ NS_ENSURE_SUCCESS(rv, rv);
+ msgHdr->GetFlags(&flags);
+ if (flags & nsMsgMessageFlags::FeedMsg)
+ {
+ *aIsRSSArticle = true;
+ return rv;
+ }
+
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(aMsgURI, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // get the folder and the server from the msghdr
+ nsCOMPtr<nsIRssIncomingServer> rssServer;
+ nsCOMPtr<nsIMsgFolder> folder;
+ rv = msgHdr->GetFolder(getter_AddRefs(folder));
+ if (NS_SUCCEEDED(rv) && folder)
+ {
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ folder->GetServer(getter_AddRefs(server));
+ rssServer = do_QueryInterface(server);
+
+ if (rssServer)
+ *aIsRSSArticle = true;
+ }
+
+ return rv;
+}
+
+
+// digest needs to be a pointer to a DIGEST_LENGTH (16) byte buffer
+nsresult MSGCramMD5(const char *text, int32_t text_len, const char *key, int32_t key_len, unsigned char *digest)
+{
+ nsresult rv;
+
+ nsAutoCString hash;
+ nsCOMPtr<nsICryptoHash> hasher = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+
+ // this code adapted from http://www.cis.ohio-state.edu/cgi-bin/rfc/rfc2104.html
+
+ char innerPad[65]; /* inner padding - key XORd with innerPad */
+ char outerPad[65]; /* outer padding - key XORd with outerPad */
+ int i;
+ /* if key is longer than 64 bytes reset it to key=MD5(key) */
+ if (key_len > 64)
+ {
+
+ rv = hasher->Init(nsICryptoHash::MD5);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = hasher->Update((const uint8_t*) key, key_len);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = hasher->Finish(false, hash);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ key = hash.get();
+ key_len = DIGEST_LENGTH;
+ }
+
+ /*
+ * the HMAC_MD5 transform looks like:
+ *
+ * MD5(K XOR outerPad, MD5(K XOR innerPad, text))
+ *
+ * where K is an n byte key
+ * innerPad is the byte 0x36 repeated 64 times
+ * outerPad is the byte 0x5c repeated 64 times
+ * and text is the data being protected
+ */
+
+ /* start out by storing key in pads */
+ memset(innerPad, 0, sizeof innerPad);
+ memset(outerPad, 0, sizeof outerPad);
+ memcpy(innerPad, key, key_len);
+ memcpy(outerPad, key, key_len);
+
+ /* XOR key with innerPad and outerPad values */
+ for (i=0; i<64; i++)
+ {
+ innerPad[i] ^= 0x36;
+ outerPad[i] ^= 0x5c;
+ }
+ /*
+ * perform inner MD5
+ */
+ nsAutoCString result;
+ rv = hasher->Init(nsICryptoHash::MD5); /* init context for 1st pass */
+ rv = hasher->Update((const uint8_t*)innerPad, 64); /* start with inner pad */
+ rv = hasher->Update((const uint8_t*)text, text_len); /* then text of datagram */
+ rv = hasher->Finish(false, result); /* finish up 1st pass */
+
+ /*
+ * perform outer MD5
+ */
+ hasher->Init(nsICryptoHash::MD5); /* init context for 2nd pass */
+ rv = hasher->Update((const uint8_t*)outerPad, 64); /* start with outer pad */
+ rv = hasher->Update((const uint8_t*)result.get(), 16);/* then results of 1st hash */
+ rv = hasher->Finish(false, result); /* finish up 2nd pass */
+
+ if (result.Length() != DIGEST_LENGTH)
+ return NS_ERROR_UNEXPECTED;
+
+ memcpy(digest, result.get(), DIGEST_LENGTH);
+
+ return rv;
+
+}
+
+
+// digest needs to be a pointer to a DIGEST_LENGTH (16) byte buffer
+nsresult MSGApopMD5(const char *text, int32_t text_len, const char *password, int32_t password_len, unsigned char *digest)
+{
+ nsresult rv;
+ nsAutoCString result;
+
+ nsCOMPtr<nsICryptoHash> hasher = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = hasher->Init(nsICryptoHash::MD5);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = hasher->Update((const uint8_t*) text, text_len);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = hasher->Update((const uint8_t*) password, password_len);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = hasher->Finish(false, result);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (result.Length() != DIGEST_LENGTH)
+ return NS_ERROR_UNEXPECTED;
+
+ memcpy(digest, result.get(), DIGEST_LENGTH);
+ return rv;
+}
+
+NS_MSG_BASE nsresult NS_GetPersistentFile(const char *relPrefName,
+ const char *absPrefName,
+ const char *dirServiceProp,
+ bool& gotRelPref,
+ nsIFile **aFile,
+ nsIPrefBranch *prefBranch)
+{
+ NS_ENSURE_ARG_POINTER(aFile);
+ *aFile = nullptr;
+ NS_ENSURE_ARG(relPrefName);
+ NS_ENSURE_ARG(absPrefName);
+ gotRelPref = false;
+
+ nsCOMPtr<nsIPrefBranch> mainBranch;
+ if (!prefBranch) {
+ nsCOMPtr<nsIPrefService> prefService(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ if (!prefService) return NS_ERROR_FAILURE;
+ prefService->GetBranch(nullptr, getter_AddRefs(mainBranch));
+ if (!mainBranch) return NS_ERROR_FAILURE;
+ prefBranch = mainBranch;
+ }
+
+ nsCOMPtr<nsIFile> localFile;
+
+ // Get the relative first
+ nsCOMPtr<nsIRelativeFilePref> relFilePref;
+ prefBranch->GetComplexValue(relPrefName,
+ NS_GET_IID(nsIRelativeFilePref), getter_AddRefs(relFilePref));
+ if (relFilePref) {
+ relFilePref->GetFile(getter_AddRefs(localFile));
+ NS_ASSERTION(localFile, "An nsIRelativeFilePref has no file.");
+ if (localFile)
+ gotRelPref = true;
+ }
+
+ // If not, get the old absolute
+ if (!localFile) {
+ prefBranch->GetComplexValue(absPrefName,
+ NS_GET_IID(nsIFile), getter_AddRefs(localFile));
+
+ // If not, and given a dirServiceProp, use directory service.
+ if (!localFile && dirServiceProp) {
+ nsCOMPtr<nsIProperties> dirService(do_GetService("@mozilla.org/file/directory_service;1"));
+ if (!dirService) return NS_ERROR_FAILURE;
+ dirService->Get(dirServiceProp, NS_GET_IID(nsIFile), getter_AddRefs(localFile));
+ if (!localFile) return NS_ERROR_FAILURE;
+ }
+ }
+
+ if (localFile) {
+ localFile->Normalize();
+ *aFile = localFile;
+ NS_ADDREF(*aFile);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_MSG_BASE nsresult NS_SetPersistentFile(const char *relPrefName,
+ const char *absPrefName,
+ nsIFile *aFile,
+ nsIPrefBranch *prefBranch)
+{
+ NS_ENSURE_ARG(relPrefName);
+ NS_ENSURE_ARG(absPrefName);
+ NS_ENSURE_ARG(aFile);
+
+ nsCOMPtr<nsIPrefBranch> mainBranch;
+ if (!prefBranch) {
+ nsCOMPtr<nsIPrefService> prefService(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ if (!prefService) return NS_ERROR_FAILURE;
+ prefService->GetBranch(nullptr, getter_AddRefs(mainBranch));
+ if (!mainBranch) return NS_ERROR_FAILURE;
+ prefBranch = mainBranch;
+ }
+
+ // Write the absolute for backwards compatibilty's sake.
+ // Or, if aPath is on a different drive than the profile dir.
+ nsresult rv = prefBranch->SetComplexValue(absPrefName, NS_GET_IID(nsIFile), aFile);
+
+ // Write the relative path.
+ nsCOMPtr<nsIRelativeFilePref> relFilePref;
+ NS_NewRelativeFilePref(aFile, nsDependentCString(NS_APP_USER_PROFILE_50_DIR), getter_AddRefs(relFilePref));
+ if (relFilePref) {
+ nsresult rv2 = prefBranch->SetComplexValue(relPrefName, NS_GET_IID(nsIRelativeFilePref), relFilePref);
+ if (NS_FAILED(rv2) && NS_SUCCEEDED(rv))
+ prefBranch->ClearUserPref(relPrefName);
+ }
+
+ return rv;
+}
+
+NS_MSG_BASE nsresult NS_GetUnicharPreferenceWithDefault(nsIPrefBranch *prefBranch, //can be null, if so uses the root branch
+ const char *prefName,
+ const nsAString& defValue,
+ nsAString& prefValue)
+{
+ NS_ENSURE_ARG(prefName);
+
+ nsCOMPtr<nsIPrefBranch> pbr;
+ if(!prefBranch) {
+ pbr = do_GetService(NS_PREFSERVICE_CONTRACTID);
+ prefBranch = pbr;
+ }
+
+ nsCOMPtr<nsISupportsString> str;
+ nsresult rv = prefBranch->GetComplexValue(prefName, NS_GET_IID(nsISupportsString), getter_AddRefs(str));
+ if (NS_SUCCEEDED(rv))
+ str->GetData(prefValue);
+ else
+ prefValue = defValue;
+ return NS_OK;
+}
+
+NS_MSG_BASE nsresult NS_GetLocalizedUnicharPreferenceWithDefault(nsIPrefBranch *prefBranch, //can be null, if so uses the root branch
+ const char *prefName,
+ const nsAString& defValue,
+ nsAString& prefValue)
+{
+ NS_ENSURE_ARG(prefName);
+
+ nsCOMPtr<nsIPrefBranch> pbr;
+ if(!prefBranch) {
+ pbr = do_GetService(NS_PREFSERVICE_CONTRACTID);
+ prefBranch = pbr;
+ }
+
+ nsCOMPtr<nsIPrefLocalizedString> str;
+ nsresult rv = prefBranch->GetComplexValue(prefName, NS_GET_IID(nsIPrefLocalizedString), getter_AddRefs(str));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsString tmpValue;
+ str->ToString(getter_Copies(tmpValue));
+ prefValue.Assign(tmpValue);
+ }
+ else
+ prefValue = defValue;
+ return NS_OK;
+}
+
+NS_MSG_BASE nsresult NS_GetLocalizedUnicharPreference(nsIPrefBranch *prefBranch, //can be null, if so uses the root branch
+ const char *prefName,
+ nsAString& prefValue)
+{
+ NS_ENSURE_ARG_POINTER(prefName);
+
+ nsCOMPtr<nsIPrefBranch> pbr;
+ if (!prefBranch) {
+ pbr = do_GetService(NS_PREFSERVICE_CONTRACTID);
+ prefBranch = pbr;
+ }
+
+ nsCOMPtr<nsIPrefLocalizedString> str;
+ nsresult rv = prefBranch->GetComplexValue(prefName, NS_GET_IID(nsIPrefLocalizedString), getter_AddRefs(str));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsString tmpValue;
+ str->ToString(getter_Copies(tmpValue));
+ prefValue.Assign(tmpValue);
+ return NS_OK;
+}
+
+void PRTime2Seconds(PRTime prTime, uint32_t *seconds)
+{
+ *seconds = (uint32_t)(prTime / PR_USEC_PER_SEC);
+}
+
+void PRTime2Seconds(PRTime prTime, int32_t *seconds)
+{
+ *seconds = (int32_t)(prTime / PR_USEC_PER_SEC);
+}
+
+void Seconds2PRTime(uint32_t seconds, PRTime *prTime)
+{
+ *prTime = (PRTime)seconds * PR_USEC_PER_SEC;
+}
+
+nsresult GetSummaryFileLocation(nsIFile* fileLocation, nsIFile** summaryLocation)
+{
+ nsresult rv;
+ nsCOMPtr <nsIFile> newSummaryLocation = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ newSummaryLocation->InitWithFile(fileLocation);
+ nsString fileName;
+
+ rv = newSummaryLocation->GetLeafName(fileName);
+ if (NS_FAILED(rv))
+ return rv;
+
+ fileName.Append(NS_LITERAL_STRING(SUMMARY_SUFFIX));
+ rv = newSummaryLocation->SetLeafName(fileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_IF_ADDREF(*summaryLocation = newSummaryLocation);
+ return NS_OK;
+}
+
+void MsgGenerateNowStr(nsACString &nowStr)
+{
+ char dateBuf[100];
+ dateBuf[0] = '\0';
+ PRExplodedTime exploded;
+ PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &exploded);
+ PR_FormatTimeUSEnglish(dateBuf, sizeof(dateBuf), "%a %b %d %H:%M:%S %Y", &exploded);
+ nowStr.Assign(dateBuf);
+}
+
+
+// Gets a special directory and appends the supplied file name onto it.
+nsresult GetSpecialDirectoryWithFileName(const char* specialDirName,
+ const char* fileName,
+ nsIFile** result)
+{
+ nsresult rv = NS_GetSpecialDirectory(specialDirName, result);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return (*result)->AppendNative(nsDependentCString(fileName));
+}
+
+// Cleans up temp files with matching names
+nsresult MsgCleanupTempFiles(const char *fileName, const char *extension)
+{
+ nsCOMPtr<nsIFile> tmpFile;
+ nsCString rootName(fileName);
+ rootName.Append(".");
+ rootName.Append(extension);
+ nsresult rv = GetSpecialDirectoryWithFileName(NS_OS_TEMP_DIR,
+ rootName.get(),
+ getter_AddRefs(tmpFile));
+
+ NS_ENSURE_SUCCESS(rv, rv);
+ int index = 1;
+ bool exists;
+ do
+ {
+ tmpFile->Exists(&exists);
+ if (exists)
+ {
+ tmpFile->Remove(false);
+ nsCString leafName(fileName);
+ leafName.Append("-");
+ leafName.AppendInt(index);
+ leafName.Append(".");
+ leafName.Append(extension);
+ // start with "Picture-1.jpg" after "Picture.jpg" exists
+ tmpFile->SetNativeLeafName(leafName);
+ }
+ }
+ while (exists && ++index < 10000);
+ return NS_OK;
+}
+
+nsresult MsgGetFileStream(nsIFile *file, nsIOutputStream **fileStream)
+{
+ nsMsgFileStream *newFileStream = new nsMsgFileStream;
+ NS_ENSURE_TRUE(newFileStream, NS_ERROR_OUT_OF_MEMORY);
+ nsresult rv = newFileStream->InitWithFile(file);
+ if (NS_SUCCEEDED(rv))
+ rv = newFileStream->QueryInterface(NS_GET_IID(nsIOutputStream), (void **) fileStream);
+ return rv;
+}
+
+nsresult MsgReopenFileStream(nsIFile *file, nsIInputStream *fileStream)
+{
+ nsMsgFileStream *msgFileStream = static_cast<nsMsgFileStream *>(fileStream);
+ if (msgFileStream)
+ return msgFileStream->InitWithFile(file);
+ else
+ return NS_ERROR_FAILURE;
+}
+
+nsresult MsgNewBufferedFileOutputStream(nsIOutputStream **aResult,
+ nsIFile* aFile,
+ int32_t aIOFlags,
+ int32_t aPerm)
+{
+ nsCOMPtr<nsIOutputStream> stream;
+ nsresult rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), aFile, aIOFlags, aPerm);
+ if (NS_SUCCEEDED(rv))
+ rv = NS_NewBufferedOutputStream(aResult, stream, FILE_IO_BUFFER_SIZE);
+ return rv;
+}
+
+nsresult MsgNewSafeBufferedFileOutputStream(nsIOutputStream **aResult,
+ nsIFile* aFile,
+ int32_t aIOFlags,
+ int32_t aPerm)
+{
+ nsCOMPtr<nsIOutputStream> stream;
+ nsresult rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(stream), aFile, aIOFlags, aPerm);
+ if (NS_SUCCEEDED(rv))
+ rv = NS_NewBufferedOutputStream(aResult, stream, FILE_IO_BUFFER_SIZE);
+ return rv;
+}
+
+bool MsgFindKeyword(const nsCString &keyword, nsCString &keywords, int32_t *aStartOfKeyword, int32_t *aLength)
+{
+#ifdef MOZILLA_INTERNAL_API
+// nsTString_CharT::Find(const nsCString& aString,
+// bool aIgnoreCase=false,
+// int32_t aOffset=0,
+// int32_t aCount=-1 ) const;
+#define FIND_KEYWORD(keywords,keyword,offset) ((keywords).Find((keyword), false, (offset)))
+#else
+// nsAString::Find(const self_type& aStr,
+// uint32_t aOffset,
+// ComparatorFunc c = DefaultComparator) const;
+#define FIND_KEYWORD(keywords,keyword,offset) ((keywords).Find((keyword), static_cast<uint32_t>(offset)))
+#endif
+ // 'keyword' is the single keyword we're looking for
+ // 'keywords' is a space delimited list of keywords to be searched,
+ // which may be just a single keyword or even be empty
+ const int32_t kKeywordLen = keyword.Length();
+ const char* start = keywords.BeginReading();
+ const char* end = keywords.EndReading();
+ *aStartOfKeyword = FIND_KEYWORD(keywords, keyword, 0);
+ while (*aStartOfKeyword >= 0)
+ {
+ const char* matchStart = start + *aStartOfKeyword;
+ const char* matchEnd = matchStart + kKeywordLen;
+ // For a real match, matchStart must be the start of keywords or preceded
+ // by a space and matchEnd must be the end of keywords or point to a space.
+ if ((matchStart == start || *(matchStart - 1) == ' ') &&
+ (matchEnd == end || *matchEnd == ' '))
+ {
+ *aLength = kKeywordLen;
+ return true;
+ }
+ *aStartOfKeyword = FIND_KEYWORD(keywords, keyword, *aStartOfKeyword + kKeywordLen);
+ }
+
+ *aLength = 0;
+ return false;
+#undef FIND_KEYWORD
+}
+
+bool MsgHostDomainIsTrusted(nsCString &host, nsCString &trustedMailDomains)
+{
+ const char *end;
+ uint32_t hostLen, domainLen;
+ bool domainIsTrusted = false;
+
+ const char *domain = trustedMailDomains.BeginReading();
+ const char *domainEnd = trustedMailDomains.EndReading();
+ const char *hostStart = host.BeginReading();
+ hostLen = host.Length();
+
+ do {
+ // skip any whitespace
+ while (*domain == ' ' || *domain == '\t')
+ ++domain;
+
+ // find end of this domain in the string
+ end = strchr(domain, ',');
+ if (!end)
+ end = domainEnd;
+
+ // to see if the hostname is in the domain, check if the domain
+ // matches the end of the hostname.
+ domainLen = end - domain;
+ if (domainLen && hostLen >= domainLen) {
+ const char *hostTail = hostStart + hostLen - domainLen;
+ if (PL_strncasecmp(domain, hostTail, domainLen) == 0)
+ {
+ // now, make sure either that the hostname is a direct match or
+ // that the hostname begins with a dot.
+ if (hostLen == domainLen || *hostTail == '.' || *(hostTail - 1) == '.')
+ {
+ domainIsTrusted = true;
+ break;
+ }
+ }
+ }
+
+ domain = end + 1;
+ } while (*end);
+ return domainIsTrusted;
+}
+
+nsresult MsgGetLocalFileFromURI(const nsACString &aUTF8Path, nsIFile **aFile)
+{
+ nsresult rv;
+ nsCOMPtr<nsIURI> argURI;
+ rv = NS_NewURI(getter_AddRefs(argURI), aUTF8Path);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIFileURL> argFileURL(do_QueryInterface(argURI, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> argFile;
+ rv = argFileURL->GetFile(getter_AddRefs(argFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ argFile.forget(aFile);
+ return NS_OK;
+}
+
+#ifndef MOZILLA_INTERNAL_API
+/*
+ * Function copied from nsReadableUtils.
+ * Migrating to frozen linkage is the only change done
+ */
+NS_MSG_BASE bool MsgIsUTF8(const nsACString& aString)
+{
+ const char *done_reading = aString.EndReading();
+
+ int32_t state = 0;
+ bool overlong = false;
+ bool surrogate = false;
+ bool nonchar = false;
+ uint16_t olupper = 0; // overlong byte upper bound.
+ uint16_t slower = 0; // surrogate byte lower bound.
+
+ const char *ptr = aString.BeginReading();
+
+ while (ptr < done_reading) {
+ uint8_t c;
+
+ if (0 == state) {
+
+ c = *ptr++;
+
+ if ((c & 0x80) == 0x00)
+ continue;
+
+ if ( c <= 0xC1 ) // [80-BF] where not expected, [C0-C1] for overlong.
+ return false;
+ else if ((c & 0xE0) == 0xC0)
+ state = 1;
+ else if ((c & 0xF0) == 0xE0) {
+ state = 2;
+ if ( c == 0xE0 ) { // to exclude E0[80-9F][80-BF]
+ overlong = true;
+ olupper = 0x9F;
+ } else if ( c == 0xED ) { // ED[A0-BF][80-BF] : surrogate codepoint
+ surrogate = true;
+ slower = 0xA0;
+ } else if ( c == 0xEF ) // EF BF [BE-BF] : non-character
+ nonchar = true;
+ } else if ( c <= 0xF4 ) { // XXX replace /w UTF8traits::is4byte when it's updated to exclude [F5-F7].(bug 199090)
+ state = 3;
+ nonchar = true;
+ if ( c == 0xF0 ) { // to exclude F0[80-8F][80-BF]{2}
+ overlong = true;
+ olupper = 0x8F;
+ }
+ else if ( c == 0xF4 ) { // to exclude F4[90-BF][80-BF]
+ // actually not surrogates but codepoints beyond 0x10FFFF
+ surrogate = true;
+ slower = 0x90;
+ }
+ } else
+ return false; // Not UTF-8 string
+ }
+
+ while (ptr < done_reading && state) {
+ c = *ptr++;
+ --state;
+
+ // non-character : EF BF [BE-BF] or F[0-7] [89AB]F BF [BE-BF]
+ if ( nonchar && ( !state && c < 0xBE ||
+ state == 1 && c != 0xBF ||
+ state == 2 && 0x0F != (0x0F & c) ))
+ nonchar = false;
+
+ if ((c & 0xC0) != 0x80 || overlong && c <= olupper ||
+ surrogate && slower <= c || nonchar && !state )
+ return false; // Not UTF-8 string
+ overlong = surrogate = false;
+ }
+ }
+ return !state; // state != 0 at the end indicates an invalid UTF-8 seq.
+}
+
+#endif
+
+NS_MSG_BASE void MsgStripQuotedPrintable (unsigned char *src)
+{
+ // decode quoted printable text in place
+
+ if (!*src)
+ return;
+ unsigned char *dest = src;
+ int srcIdx = 0, destIdx = 0;
+
+ while (src[srcIdx] != 0)
+ {
+ // Decode sequence of '=XY' into a character with code XY.
+ if (src[srcIdx] == '=')
+ {
+ if (MsgIsHex((const char*)src + srcIdx + 1, 2)) {
+ // If we got here, we successfully decoded a quoted printable sequence,
+ // so bump each pointer past it and move on to the next char.
+ dest[destIdx++] = MsgUnhex((const char*)src + srcIdx + 1, 2);
+ srcIdx += 3;
+ }
+ else
+ {
+ // If first char after '=' isn't hex check if it's a normal char
+ // or a soft line break. If it's a soft line break, eat the
+ // CR/LF/CRLF.
+ if (src[srcIdx + 1] == '\r' || src[srcIdx + 1] == '\n')
+ {
+ srcIdx++; // soft line break, ignore the '=';
+ if (src[srcIdx] == '\r' || src[srcIdx] == '\n')
+ {
+ srcIdx++;
+ if (src[srcIdx] == '\n')
+ srcIdx++;
+ }
+ }
+ else // The first or second char after '=' isn't hex, just copy the '='.
+ {
+ dest[destIdx++] = src[srcIdx++];
+ }
+ continue;
+ }
+ }
+ else
+ dest[destIdx++] = src[srcIdx++];
+ }
+
+ dest[destIdx] = src[srcIdx]; // null terminate
+}
+
+NS_MSG_BASE nsresult MsgEscapeString(const nsACString &aStr,
+ uint32_t aType, nsACString &aResult)
+{
+ nsresult rv;
+ nsCOMPtr<nsINetUtil> nu = do_GetService(NS_NETUTIL_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return nu->EscapeString(aStr, aType, aResult);
+}
+
+NS_MSG_BASE nsresult MsgUnescapeString(const nsACString &aStr, uint32_t aFlags,
+ nsACString &aResult)
+{
+ nsresult rv;
+ nsCOMPtr<nsINetUtil> nu = do_GetService(NS_NETUTIL_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return nu->UnescapeString(aStr, aFlags, aResult);
+}
+
+NS_MSG_BASE nsresult MsgEscapeURL(const nsACString &aStr, uint32_t aFlags,
+ nsACString &aResult)
+{
+ nsresult rv;
+ nsCOMPtr<nsINetUtil> nu = do_GetService(NS_NETUTIL_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return nu->EscapeURL(aStr, aFlags, aResult);
+}
+
+#ifndef MOZILLA_INTERNAL_API
+
+NS_MSG_BASE char *MsgEscapeHTML(const char *string)
+{
+ char *rv = nullptr;
+ /* XXX Hardcoded max entity len. The +1 is for the trailing null. */
+ uint32_t len = PL_strlen(string);
+ if (len >= (PR_UINT32_MAX / 6))
+ return nullptr;
+
+ rv = (char *)NS_Alloc( (6 * len) + 1 );
+ char *ptr = rv;
+
+ if (rv)
+ {
+ for(; *string != '\0'; string++)
+ {
+ if (*string == '<')
+ {
+ *ptr++ = '&';
+ *ptr++ = 'l';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ }
+ else if (*string == '>')
+ {
+ *ptr++ = '&';
+ *ptr++ = 'g';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ }
+ else if (*string == '&')
+ {
+ *ptr++ = '&';
+ *ptr++ = 'a';
+ *ptr++ = 'm';
+ *ptr++ = 'p';
+ *ptr++ = ';';
+ }
+ else if (*string == '"')
+ {
+ *ptr++ = '&';
+ *ptr++ = 'q';
+ *ptr++ = 'u';
+ *ptr++ = 'o';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ }
+ else if (*string == '\'')
+ {
+ *ptr++ = '&';
+ *ptr++ = '#';
+ *ptr++ = '3';
+ *ptr++ = '9';
+ *ptr++ = ';';
+ }
+ else
+ {
+ *ptr++ = *string;
+ }
+ }
+ *ptr = '\0';
+ }
+ return(rv);
+}
+
+NS_MSG_BASE char16_t *MsgEscapeHTML2(const char16_t *aSourceBuffer,
+ int32_t aSourceBufferLen)
+{
+ // if the caller didn't calculate the length
+ if (aSourceBufferLen == -1) {
+ aSourceBufferLen = NS_strlen(aSourceBuffer); // ...then I will
+ }
+
+ /* XXX Hardcoded max entity len. */
+ if (aSourceBufferLen >=
+ ((PR_UINT32_MAX - sizeof(char16_t)) / (6 * sizeof(char16_t))) )
+ return nullptr;
+
+ char16_t *resultBuffer = (char16_t *)moz_xmalloc(aSourceBufferLen *
+ 6 * sizeof(char16_t) + sizeof(char16_t('\0')));
+
+ char16_t *ptr = resultBuffer;
+
+ if (resultBuffer) {
+ int32_t i;
+
+ for(i = 0; i < aSourceBufferLen; i++) {
+ if(aSourceBuffer[i] == '<') {
+ *ptr++ = '&';
+ *ptr++ = 'l';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ } else if(aSourceBuffer[i] == '>') {
+ *ptr++ = '&';
+ *ptr++ = 'g';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ } else if(aSourceBuffer[i] == '&') {
+ *ptr++ = '&';
+ *ptr++ = 'a';
+ *ptr++ = 'm';
+ *ptr++ = 'p';
+ *ptr++ = ';';
+ } else if (aSourceBuffer[i] == '"') {
+ *ptr++ = '&';
+ *ptr++ = 'q';
+ *ptr++ = 'u';
+ *ptr++ = 'o';
+ *ptr++ = 't';
+ *ptr++ = ';';
+ } else if (aSourceBuffer[i] == '\'') {
+ *ptr++ = '&';
+ *ptr++ = '#';
+ *ptr++ = '3';
+ *ptr++ = '9';
+ *ptr++ = ';';
+ } else {
+ *ptr++ = aSourceBuffer[i];
+ }
+ }
+ *ptr = 0;
+ }
+
+ return resultBuffer;
+}
+
+NS_MSG_BASE void MsgCompressWhitespace(nsCString& aString)
+{
+ // This code is frozen linkage specific
+ aString.Trim(" \f\n\r\t\v");
+
+ char *start, *end;
+ aString.BeginWriting(&start, &end);
+
+ for (char *cur = start; cur < end; ++cur) {
+ if (!IS_SPACE(*cur))
+ continue;
+
+ *cur = ' ';
+
+ if (!IS_SPACE(*(cur + 1)))
+ continue;
+
+ // Loop through the white space
+ char *wend = cur + 2;
+ while (IS_SPACE(*wend))
+ ++wend;
+
+ uint32_t wlen = wend - cur - 1;
+
+ // fix "end"
+ end -= wlen;
+
+ // move everything forwards a bit
+ for (char *m = cur + 1; m < end; ++m) {
+ *m = *(m + wlen);
+ }
+ }
+
+ // Set the new length.
+ aString.SetLength(end - start);
+}
+
+
+NS_MSG_BASE void MsgReplaceChar(nsString& str, const char *set, const char16_t replacement)
+{
+ char16_t *c_str = str.BeginWriting();
+ while (*set) {
+ int32_t pos = 0;
+ while ((pos = str.FindChar(*set, pos)) != -1) {
+ c_str[pos++] = replacement;
+ }
+ set++;
+ }
+}
+
+NS_MSG_BASE void MsgReplaceChar(nsCString& str, const char needle, const char replacement)
+{
+ char *c_str = str.BeginWriting();
+ while ((c_str = strchr(c_str, needle))) {
+ *c_str = replacement;
+ c_str++;
+ }
+}
+
+NS_MSG_BASE already_AddRefed<nsIAtom> MsgNewAtom(const char* aString)
+{
+ nsCOMPtr<nsIAtomService> atomService(do_GetService("@mozilla.org/atom-service;1"));
+ nsCOMPtr<nsIAtom> atom;
+
+ if (atomService)
+ atomService->GetAtomUTF8(aString, getter_AddRefs(atom));
+ return atom.forget();
+}
+
+NS_MSG_BASE void MsgReplaceSubstring(nsAString &str, const nsAString &what, const nsAString &replacement)
+{
+ const char16_t* replacement_str;
+ uint32_t replacementLength = replacement.BeginReading(&replacement_str);
+ uint32_t whatLength = what.Length();
+ int32_t i = 0;
+
+ while ((i = str.Find(what, i)) != kNotFound)
+ {
+ str.Replace(i, whatLength, replacement_str, replacementLength);
+ i += replacementLength;
+ }
+}
+
+NS_MSG_BASE void MsgReplaceSubstring(nsACString &str, const char *what, const char *replacement)
+{
+ uint32_t replacementLength = strlen(replacement);
+ uint32_t whatLength = strlen(what);
+ int32_t i = 0;
+
+ /* We have to create nsDependentCString from 'what' because there's no
+ * str.Find(char *what, int offset) but there is only
+ * str.Find(char *what, int length) */
+ nsDependentCString what_dependent(what);
+ while ((i = str.Find(what_dependent, i)) != kNotFound)
+ {
+ str.Replace(i, whatLength, replacement, replacementLength);
+ i += replacementLength;
+ }
+}
+
+/* This class is based on nsInterfaceRequestorAgg from nsInterfaceRequestorAgg.h */
+class MsgInterfaceRequestorAgg : public nsIInterfaceRequestor
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIINTERFACEREQUESTOR
+
+ MsgInterfaceRequestorAgg(nsIInterfaceRequestor *aFirst,
+ nsIInterfaceRequestor *aSecond)
+ : mFirst(aFirst)
+ , mSecond(aSecond) {}
+
+ nsCOMPtr<nsIInterfaceRequestor> mFirst, mSecond;
+};
+
+// XXX This needs to support threadsafe refcounting until we fix bug 243591.
+NS_IMPL_ISUPPORTS(MsgInterfaceRequestorAgg, nsIInterfaceRequestor)
+
+NS_IMETHODIMP
+MsgInterfaceRequestorAgg::GetInterface(const nsIID &aIID, void **aResult)
+{
+ nsresult rv = NS_ERROR_NO_INTERFACE;
+ if (mFirst)
+ rv = mFirst->GetInterface(aIID, aResult);
+ if (mSecond && NS_FAILED(rv))
+ rv = mSecond->GetInterface(aIID, aResult);
+ return rv;
+}
+
+/* This function is based on NS_NewInterfaceRequestorAggregation from
+ * nsInterfaceRequestorAgg.h */
+NS_MSG_BASE nsresult
+MsgNewInterfaceRequestorAggregation(nsIInterfaceRequestor *aFirst,
+ nsIInterfaceRequestor *aSecond,
+ nsIInterfaceRequestor **aResult)
+{
+ *aResult = new MsgInterfaceRequestorAgg(aFirst, aSecond);
+ if (!*aResult)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+nsresult NS_FASTCALL MsgQueryElementAt::operator()( const nsIID& aIID, void** aResult ) const
+ {
+ nsresult status = mArray
+ ? mArray->QueryElementAt(mIndex, aIID, aResult)
+ : NS_ERROR_NULL_POINTER;
+
+ if ( mErrorPtr )
+ *mErrorPtr = status;
+
+ return status;
+ }
+
+#endif
+
+NS_MSG_BASE nsresult MsgGetHeadersFromKeys(nsIMsgDatabase *aDB, const nsTArray<nsMsgKey> &aMsgKeys,
+ nsIMutableArray *aHeaders)
+{
+ NS_ENSURE_ARG_POINTER(aDB);
+
+ uint32_t count = aMsgKeys.Length();
+ nsresult rv = NS_OK;
+
+ for (uint32_t kindex = 0; kindex < count; kindex++)
+ {
+ nsMsgKey key = aMsgKeys.ElementAt(kindex);
+
+ bool hasKey;
+ rv = aDB->ContainsKey(key, &hasKey);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // This function silently skips when the key is not found. This is an expected case.
+ if (hasKey)
+ {
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ rv = aDB->GetMsgHdrForKey(key, getter_AddRefs(msgHdr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aHeaders->AppendElement(msgHdr, false);
+ }
+ }
+
+ return rv;
+}
+
+NS_MSG_BASE nsresult MsgGetHdrsFromKeys(nsIMsgDatabase *aDB, nsMsgKey *aMsgKeys,
+ uint32_t aNumKeys, nsIMutableArray **aHeaders)
+{
+ NS_ENSURE_ARG_POINTER(aDB);
+ NS_ENSURE_ARG_POINTER(aMsgKeys);
+ NS_ENSURE_ARG_POINTER(aHeaders);
+
+ nsresult rv;
+ nsCOMPtr<nsIMutableArray> messages(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (uint32_t kindex = 0; kindex < aNumKeys; kindex++) {
+ nsMsgKey key = aMsgKeys[kindex];
+ bool hasKey;
+ rv = aDB->ContainsKey(key, &hasKey);
+ // This function silently skips when the key is not found. This is an expected case.
+ if (NS_SUCCEEDED(rv) && hasKey)
+ {
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ rv = aDB->GetMsgHdrForKey(key, getter_AddRefs(msgHdr));
+ if (NS_SUCCEEDED(rv))
+ messages->AppendElement(msgHdr, false);
+ }
+ }
+
+ messages.forget(aHeaders);
+ return NS_OK;
+}
+
+bool MsgAdvanceToNextLine(const char *buffer, uint32_t &bufferOffset, uint32_t maxBufferOffset)
+{
+ bool result = false;
+ for (; bufferOffset < maxBufferOffset; bufferOffset++)
+ {
+ if (buffer[bufferOffset] == '\r' || buffer[bufferOffset] == '\n')
+ {
+ bufferOffset++;
+ if (buffer[bufferOffset- 1] == '\r' && buffer[bufferOffset] == '\n')
+ bufferOffset++;
+ result = true;
+ break;
+ }
+ }
+ return result;
+}
+
+NS_MSG_BASE nsresult
+MsgExamineForProxy(nsIChannel *channel, nsIProxyInfo **proxyInfo)
+{
+ nsresult rv;
+
+#ifdef DEBUG
+ nsCOMPtr<nsIURI> uri;
+ rv = channel->GetURI(getter_AddRefs(uri));
+ NS_ASSERTION(NS_SUCCEEDED(rv) && uri,
+ "The URI needs to be set before calling the proxy service");
+#endif
+
+ nsCOMPtr<nsIProtocolProxyService> proxyService =
+ do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // XXX: This "interface" ID is exposed, but it's not hooked up to the QI.
+ // Until it is, use a static_cast for now.
+#if 0
+ RefPtr<nsProtocolProxyService> rawProxyService = do_QueryObject(proxyService, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+#else
+ nsProtocolProxyService *rawProxyService = static_cast<nsProtocolProxyService*>(proxyService.get());
+#endif
+
+ return rawProxyService->DeprecatedBlockingResolve(channel, 0, proxyInfo);
+}
+
+NS_MSG_BASE nsresult MsgPromptLoginFailed(nsIMsgWindow *aMsgWindow,
+ const nsCString &aHostname,
+ int32_t *aResult)
+{
+
+ nsCOMPtr<nsIPrompt> dialog;
+ if (aMsgWindow)
+ aMsgWindow->GetPromptDialog(getter_AddRefs(dialog));
+
+ nsresult rv;
+
+ // If we haven't got one, use a default dialog.
+ if (!dialog)
+ {
+ nsCOMPtr<nsIWindowWatcher> wwatch =
+ do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = wwatch->GetNewPrompter(0, getter_AddRefs(dialog));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsCOMPtr<nsIStringBundleService> bundleSvc =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleSvc, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleSvc->CreateBundle("chrome://messenger/locale/messenger.properties",
+ getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsString message;
+ NS_ConvertUTF8toUTF16 hostNameUTF16(aHostname);
+ const char16_t *formatStrings[] = { hostNameUTF16.get() };
+
+ rv = bundle->FormatStringFromName(u"mailServerLoginFailed",
+ formatStrings, 1,
+ getter_Copies(message));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsString title;
+ rv = bundle->GetStringFromName(
+ u"mailServerLoginFailedTitle", getter_Copies(title));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsString button0;
+ rv = bundle->GetStringFromName(
+ u"mailServerLoginFailedRetryButton",
+ getter_Copies(button0));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsString button2;
+ rv = bundle->GetStringFromName(
+ u"mailServerLoginFailedEnterNewPasswordButton",
+ getter_Copies(button2));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool dummyValue = false;
+ return dialog->ConfirmEx(
+ title.get(), message.get(),
+ (nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_0) +
+ (nsIPrompt::BUTTON_TITLE_CANCEL * nsIPrompt::BUTTON_POS_1) +
+ (nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2),
+ button0.get(), nullptr, button2.get(), nullptr, &dummyValue, aResult);
+}
+
+NS_MSG_BASE PRTime MsgConvertAgeInDaysToCutoffDate(int32_t ageInDays)
+{
+ PRTime now = PR_Now();
+
+ return now - PR_USEC_PER_DAY * ageInDays;
+}
+
+NS_MSG_BASE nsresult MsgTermListToString(nsISupportsArray *aTermList, nsCString &aOutString)
+{
+ uint32_t count;
+ aTermList->Count(&count);
+ nsresult rv = NS_OK;
+
+ for (uint32_t searchIndex = 0; searchIndex < count;
+ searchIndex++)
+ {
+ nsAutoCString stream;
+
+ nsCOMPtr<nsIMsgSearchTerm> term;
+ aTermList->QueryElementAt(searchIndex, NS_GET_IID(nsIMsgSearchTerm),
+ (void **)getter_AddRefs(term));
+ if (!term)
+ continue;
+
+ if (aOutString.Length() > 1)
+ aOutString += ' ';
+
+ bool booleanAnd;
+ bool matchAll;
+ term->GetBooleanAnd(&booleanAnd);
+ term->GetMatchAll(&matchAll);
+ if (matchAll)
+ {
+ aOutString += "ALL";
+ continue;
+ }
+ else if (booleanAnd)
+ aOutString += "AND (";
+ else
+ aOutString += "OR (";
+
+ rv = term->GetTermAsString(stream);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aOutString += stream;
+ aOutString += ')';
+ }
+ return rv;
+}
+
+NS_MSG_BASE uint64_t ParseUint64Str(const char *str)
+{
+#ifdef XP_WIN
+ {
+ char *endPtr;
+ return _strtoui64(str, &endPtr, 10);
+ }
+#else
+ return strtoull(str, nullptr, 10);
+#endif
+}
+
+NS_MSG_BASE uint64_t MsgUnhex(const char *aHexString, size_t aNumChars)
+{
+ // Large numbers will not fit into uint64_t.
+ NS_ASSERTION(aNumChars <= 16, "Hex literal too long to convert!");
+
+ uint64_t result = 0;
+ for (size_t i = 0; i < aNumChars; i++)
+ {
+ unsigned char c = aHexString[i];
+ uint8_t digit;
+ if ((c >= '0') && (c <= '9'))
+ digit = (c - '0');
+ else if ((c >= 'a') && (c <= 'f'))
+ digit = ((c - 'a') + 10);
+ else if ((c >= 'A') && (c <= 'F'))
+ digit = ((c - 'A') + 10);
+ else
+ break;
+
+ result = (result << 4) | digit;
+ }
+
+ return result;
+}
+
+NS_MSG_BASE bool MsgIsHex(const char *aHexString, size_t aNumChars)
+{
+ for (size_t i = 0; i < aNumChars; i++)
+ {
+ if (!isxdigit(aHexString[i]))
+ return false;
+ }
+ return true;
+}
+
+
+NS_MSG_BASE nsresult
+MsgStreamMsgHeaders(nsIInputStream *aInputStream, nsIStreamListener *aConsumer)
+{
+ nsAutoPtr<nsLineBuffer<char> > lineBuffer(new nsLineBuffer<char>);
+ NS_ENSURE_TRUE(lineBuffer, NS_ERROR_OUT_OF_MEMORY);
+
+ nsresult rv;
+
+ nsAutoCString msgHeaders;
+ nsAutoCString curLine;
+
+ bool more = true;
+
+ // We want to NS_ReadLine until we get to a blank line (the end of the headers)
+ while (more)
+ {
+ rv = NS_ReadLine(aInputStream, lineBuffer.get(), curLine, &more);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (curLine.IsEmpty())
+ break;
+ msgHeaders.Append(curLine);
+ msgHeaders.Append(NS_LITERAL_CSTRING("\r\n"));
+ }
+ lineBuffer = nullptr;
+ nsCOMPtr<nsIStringInputStream> hdrsStream =
+ do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ hdrsStream->SetData(msgHeaders.get(), msgHeaders.Length());
+ nsCOMPtr<nsIInputStreamPump> pump;
+ rv = NS_NewInputStreamPump(getter_AddRefs(pump), hdrsStream);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return pump->AsyncRead(aConsumer, nullptr);
+}
+
+class CharsetDetectionObserver : public nsICharsetDetectionObserver
+{
+public:
+ NS_DECL_ISUPPORTS
+ CharsetDetectionObserver() {};
+ NS_IMETHOD Notify(const char* aCharset, nsDetectionConfident aConf) override
+ {
+ mCharset = aCharset;
+ return NS_OK;
+ };
+ const char *GetDetectedCharset() { return mCharset.get(); }
+
+private:
+ virtual ~CharsetDetectionObserver() {}
+ nsCString mCharset;
+};
+
+NS_IMPL_ISUPPORTS(CharsetDetectionObserver, nsICharsetDetectionObserver)
+
+NS_MSG_BASE nsresult
+MsgDetectCharsetFromFile(nsIFile *aFile, nsACString &aCharset)
+{
+ // First try the universal charset detector
+ nsCOMPtr<nsICharsetDetector> detector
+ = do_CreateInstance(NS_CHARSET_DETECTOR_CONTRACTID_BASE
+ "universal_charset_detector");
+ if (!detector) {
+ // No universal charset detector, try the default charset detector
+ nsString detectorName;
+ NS_GetLocalizedUnicharPreferenceWithDefault(nullptr, "intl.charset.detector",
+ EmptyString(), detectorName);
+ if (!detectorName.IsEmpty()) {
+ nsAutoCString detectorContractID;
+ detectorContractID.AssignLiteral(NS_CHARSET_DETECTOR_CONTRACTID_BASE);
+ AppendUTF16toUTF8(detectorName, detectorContractID);
+ detector = do_CreateInstance(detectorContractID.get());
+ }
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIInputStream> inputStream;
+ rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), aFile);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (detector) {
+ nsAutoCString buffer;
+
+ RefPtr<CharsetDetectionObserver> observer = new CharsetDetectionObserver();
+
+ rv = detector->Init(observer);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsILineInputStream> lineInputStream;
+ lineInputStream = do_QueryInterface(inputStream, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool isMore = true;
+ bool dontFeed = false;
+ while (isMore &&
+ NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore)) &&
+ buffer.Length() > 0) {
+ detector->DoIt(buffer.get(), buffer.Length(), &dontFeed);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (dontFeed)
+ break;
+ }
+ rv = detector->Done();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aCharset = observer->GetDetectedCharset();
+ } else {
+ // no charset detector available, check the BOM
+ char sniffBuf[3];
+ uint32_t numRead;
+ rv = inputStream->Read(sniffBuf, sizeof(sniffBuf), &numRead);
+
+ if (numRead >= 2 &&
+ sniffBuf[0] == (char)0xfe &&
+ sniffBuf[1] == (char)0xff) {
+ aCharset = "UTF-16BE";
+ } else if (numRead >= 2 &&
+ sniffBuf[0] == (char)0xff &&
+ sniffBuf[1] == (char)0xfe) {
+ aCharset = "UTF-16LE";
+ } else if (numRead >= 3 &&
+ sniffBuf[0] == (char)0xef &&
+ sniffBuf[1] == (char)0xbb &&
+ sniffBuf[2] == (char)0xbf) {
+ aCharset = "UTF-8";
+ }
+ }
+
+ if (aCharset.IsEmpty()) { // No sniffed or charset.
+ nsAutoCString buffer;
+ nsCOMPtr<nsILineInputStream> lineInputStream =
+ do_QueryInterface(inputStream, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool isMore = true;
+ bool isUTF8Compat = true;
+ while (isMore && isUTF8Compat &&
+ NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) {
+ isUTF8Compat = MsgIsUTF8(buffer);
+ }
+
+ // If the file content is UTF-8 compatible, use that. Otherwise let's not
+ // make a bad guess.
+ if (isUTF8Compat)
+ aCharset.AssignLiteral("UTF-8");
+ }
+
+ if (aCharset.IsEmpty()) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+/*
+ * Converts a buffer to plain text. Some conversions may
+ * or may not work with certain end charsets which is why we
+ * need that as an argument to the function. If charset is
+ * unknown or deemed of no importance NULL could be passed.
+ */
+NS_MSG_BASE nsresult
+ConvertBufToPlainText(nsString &aConBuf, bool formatFlowed, bool delsp,
+ bool formatOutput, bool disallowBreaks)
+{
+ if (aConBuf.IsEmpty())
+ return NS_OK;
+
+ int32_t wrapWidth = 72;
+ nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
+
+ if (pPrefBranch)
+ {
+ pPrefBranch->GetIntPref("mailnews.wraplength", &wrapWidth);
+ // Let sanity reign!
+ if (wrapWidth == 0 || wrapWidth > 990)
+ wrapWidth = 990;
+ else if (wrapWidth < 10)
+ wrapWidth = 10;
+ }
+
+ uint32_t converterFlags = nsIDocumentEncoder::OutputPersistNBSP;
+ if (formatFlowed)
+ converterFlags |= nsIDocumentEncoder::OutputFormatFlowed;
+ if (delsp)
+ converterFlags |= nsIDocumentEncoder::OutputFormatDelSp;
+ if (formatOutput)
+ converterFlags |= nsIDocumentEncoder::OutputFormatted;
+ if (disallowBreaks)
+ converterFlags |= nsIDocumentEncoder::OutputDisallowLineBreaking;
+
+ nsCOMPtr<nsIParserUtils> utils =
+ do_GetService(NS_PARSERUTILS_CONTRACTID);
+ return utils->ConvertToPlainText(aConBuf,
+ converterFlags,
+ wrapWidth,
+ aConBuf);
+}
+
+NS_MSG_BASE nsMsgKey msgKeyFromInt(uint32_t aValue)
+{
+ return aValue;
+}
+
+NS_MSG_BASE nsMsgKey msgKeyFromInt(uint64_t aValue)
+{
+ NS_ASSERTION(aValue <= PR_UINT32_MAX, "Msg key value too big!");
+ return aValue;
+}
+
+// Helper function to extract a query qualifier.
+nsAutoCString MsgExtractQueryPart(nsAutoCString spec, const char* queryToExtract)
+{
+ nsAutoCString queryPart;
+ int32_t queryIndex = spec.Find(queryToExtract);
+ if (queryIndex == kNotFound)
+ return queryPart;
+
+ int32_t queryEnd = Substring(spec, queryIndex + 1).FindChar('&');
+ if (queryEnd == kNotFound)
+ queryEnd = Substring(spec, queryIndex + 1).FindChar('?');
+ if (queryEnd == kNotFound) {
+ // Nothing follows, so return from where the query qualifier started.
+ queryPart.Assign(Substring(spec, queryIndex));
+ } else {
+ // Return the substring that represents the query qualifier.
+ queryPart.Assign(Substring(spec, queryIndex, queryEnd + 1));
+ }
+ return queryPart;
+}