diff options
Diffstat (limited to 'mailnews/base/util/nsMsgUtils.cpp')
-rw-r--r-- | mailnews/base/util/nsMsgUtils.cpp | 2520 |
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; +} |