mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-08 16:27:31 +09:00
When cross-compiling for Windows on Linux, fix mismatched types in nsMsgImapSearch.cpp. nsString::get() returns a char16ptr_t (with MOZ_USE_CHAR16_WRAPPER) and that causes a mismatch so we cast it to a const char16_t*. usAsciiCharSet is declared as a nsAutoString so we instead declare it as a char16_t* to avoid subsequent type mismatches. Tag #1273
1803 lines
55 KiB
C++
1803 lines
55 KiB
C++
/* -*- 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 "nsCOMPtr.h"
|
|
#include "nsMsgCompUtils.h"
|
|
#include "nsIPrefService.h"
|
|
#include "nsIPrefBranch.h"
|
|
#include "prmem.h"
|
|
#include "nsMsgSend.h"
|
|
#include "nsIIOService.h"
|
|
#include "nsIHttpProtocolHandler.h"
|
|
#include "nsMailHeaders.h"
|
|
#include "nsMsgI18N.h"
|
|
#include "nsINntpService.h"
|
|
#include "nsMimeTypes.h"
|
|
#include "nsDirectoryServiceDefs.h"
|
|
#include "nsIURI.h"
|
|
#include "nsNetCID.h"
|
|
#include "nsMsgPrompts.h"
|
|
#include "nsMsgUtils.h"
|
|
#include "nsCExternalHandlerService.h"
|
|
#include "nsIMIMEService.h"
|
|
#include "nsComposeStrings.h"
|
|
#include "nsIMsgCompUtils.h"
|
|
#include "nsIMsgMdnGenerator.h"
|
|
#include "nsServiceManagerUtils.h"
|
|
#include "nsComponentManagerUtils.h"
|
|
#include "nsMemory.h"
|
|
#include "nsCRTGlue.h"
|
|
#include <ctype.h>
|
|
#include "mozilla/mailnews/Services.h"
|
|
#include "mozilla/Services.h"
|
|
#include "nsIMIMEInfo.h"
|
|
#include "nsIMsgHeaderParser.h"
|
|
#include "nsIRandomGenerator.h"
|
|
#include "nsID.h"
|
|
|
|
NS_IMPL_ISUPPORTS(nsMsgCompUtils, nsIMsgCompUtils)
|
|
|
|
nsMsgCompUtils::nsMsgCompUtils()
|
|
{
|
|
}
|
|
|
|
nsMsgCompUtils::~nsMsgCompUtils()
|
|
{
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgCompUtils::MimeMakeSeparator(const char *prefix,
|
|
char **_retval)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(prefix);
|
|
NS_ENSURE_ARG_POINTER(_retval);
|
|
*_retval = mime_make_separator(prefix);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgCompUtils::MsgGenerateMessageId(nsIMsgIdentity *identity,
|
|
char **_retval)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(identity);
|
|
NS_ENSURE_ARG_POINTER(_retval);
|
|
*_retval = msg_generate_message_id(identity);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgCompUtils::GetMsgMimeConformToStandard(bool *_retval)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(_retval);
|
|
*_retval = nsMsgMIMEGetConformToStandard();
|
|
return NS_OK;
|
|
}
|
|
|
|
//
|
|
// Create a file for the a unique temp file
|
|
// on the local machine. Caller must free memory
|
|
//
|
|
nsresult
|
|
nsMsgCreateTempFile(const char *tFileName, nsIFile **tFile)
|
|
{
|
|
if ((!tFileName) || (!*tFileName))
|
|
tFileName = "nsmail.tmp";
|
|
|
|
nsresult rv = GetSpecialDirectoryWithFileName(NS_OS_TEMP_DIR,
|
|
tFileName,
|
|
tFile);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
rv = (*tFile)->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600);
|
|
if (NS_FAILED(rv))
|
|
NS_RELEASE(*tFile);
|
|
|
|
return rv;
|
|
}
|
|
|
|
//
|
|
// Create a file spec for the a unique temp file
|
|
// on the local machine. Caller must free memory
|
|
// returned
|
|
//
|
|
char *
|
|
nsMsgCreateTempFileName(const char *tFileName)
|
|
{
|
|
if ((!tFileName) || (!*tFileName))
|
|
tFileName = "nsmail.tmp";
|
|
|
|
nsCOMPtr<nsIFile> tmpFile;
|
|
|
|
nsresult rv = GetSpecialDirectoryWithFileName(NS_OS_TEMP_DIR,
|
|
tFileName,
|
|
getter_AddRefs(tmpFile));
|
|
if (NS_FAILED(rv))
|
|
return nullptr;
|
|
|
|
rv = tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600);
|
|
if (NS_FAILED(rv))
|
|
return nullptr;
|
|
|
|
nsCString tempString;
|
|
rv = tmpFile->GetNativePath(tempString);
|
|
if (NS_FAILED(rv))
|
|
return nullptr;
|
|
|
|
char *tString = ToNewCString(tempString);
|
|
if (!tString)
|
|
return PL_strdup("mozmail.tmp"); // No need to I18N
|
|
|
|
return tString;
|
|
}
|
|
|
|
// This is the value a caller will Get if they don't Set first (like MDN
|
|
// sending a return receipt), so init to the default value of the
|
|
// mail.strictly_mime_headers preference.
|
|
static bool mime_headers_use_quoted_printable_p = true;
|
|
|
|
bool
|
|
nsMsgMIMEGetConformToStandard (void)
|
|
{
|
|
return mime_headers_use_quoted_printable_p;
|
|
}
|
|
|
|
void
|
|
nsMsgMIMESetConformToStandard (bool conform_p)
|
|
{
|
|
/*
|
|
* If we are conforming to mime standard no matter what we set
|
|
* for the headers preference when generating mime headers we should
|
|
* also conform to the standard. Otherwise, depends the preference
|
|
* we set. For now, the headers preference is not accessible from UI.
|
|
*/
|
|
if (conform_p)
|
|
mime_headers_use_quoted_printable_p = true;
|
|
else {
|
|
nsresult rv;
|
|
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
|
|
if (NS_SUCCEEDED(rv)) {
|
|
prefs->GetBoolPref("mail.strictly_mime_headers", &mime_headers_use_quoted_printable_p);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if the recipient fields have sane values for message send.
|
|
*/
|
|
nsresult mime_sanity_check_fields_recipients (
|
|
const char *to,
|
|
const char *cc,
|
|
const char *bcc,
|
|
const char *newsgroups)
|
|
{
|
|
if (to)
|
|
while (IS_SPACE(*to))
|
|
to++;
|
|
if (cc)
|
|
while (IS_SPACE(*cc))
|
|
cc++;
|
|
if (bcc)
|
|
while (IS_SPACE(*bcc))
|
|
bcc++;
|
|
if (newsgroups)
|
|
while (IS_SPACE(*newsgroups))
|
|
newsgroups++;
|
|
|
|
if ((!to || !*to) && (!cc || !*cc) &&
|
|
(!bcc || !*bcc) && (!newsgroups || !*newsgroups))
|
|
return NS_MSG_NO_RECIPIENTS;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
/**
|
|
* Checks if the compose fields have sane values for message send.
|
|
*/
|
|
nsresult mime_sanity_check_fields (
|
|
const char *from,
|
|
const char *reply_to,
|
|
const char *to,
|
|
const char *cc,
|
|
const char *bcc,
|
|
const char *fcc,
|
|
const char *newsgroups,
|
|
const char *followup_to,
|
|
const char * /*subject*/,
|
|
const char * /*references*/,
|
|
const char * /*organization*/,
|
|
const char * /*other_random_headers*/)
|
|
{
|
|
if (from)
|
|
while (IS_SPACE(*from))
|
|
from++;
|
|
if (reply_to)
|
|
while (IS_SPACE(*reply_to))
|
|
reply_to++;
|
|
if (fcc)
|
|
while (IS_SPACE(*fcc))
|
|
fcc++;
|
|
if (followup_to)
|
|
while (IS_SPACE(*followup_to))
|
|
followup_to++;
|
|
|
|
// TODO: sanity check other_random_headers for newline conventions
|
|
if (!from || !*from)
|
|
return NS_MSG_NO_SENDER;
|
|
|
|
return mime_sanity_check_fields_recipients(to, cc, bcc, newsgroups);
|
|
}
|
|
|
|
//
|
|
// Generate the message headers for the new RFC822 message
|
|
//
|
|
#define UA_PREF_PREFIX "general.useragent."
|
|
|
|
// Helper macro for generating the X-Mozilla-Draft-Info header.
|
|
#define APPEND_BOOL(method, param) \
|
|
do { \
|
|
bool val = false; \
|
|
fields->Get##method(&val); \
|
|
if (val) \
|
|
draftInfo.AppendLiteral(param "=1"); \
|
|
else \
|
|
draftInfo.AppendLiteral(param "=0"); \
|
|
} while (false)
|
|
|
|
nsresult mime_generate_headers(nsIMsgCompFields *fields,
|
|
nsMsgDeliverMode deliver_mode,
|
|
msgIWritableStructuredHeaders *finalHeaders)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
bool isDraft =
|
|
deliver_mode == nsIMsgSend::nsMsgSaveAsDraft ||
|
|
deliver_mode == nsIMsgSend::nsMsgSaveAsTemplate ||
|
|
deliver_mode == nsIMsgSend::nsMsgQueueForLater ||
|
|
deliver_mode == nsIMsgSend::nsMsgDeliverBackground;
|
|
|
|
bool hasDisclosedRecipient = false;
|
|
|
|
MOZ_ASSERT(fields, "null fields");
|
|
NS_ENSURE_ARG_POINTER(fields);
|
|
|
|
nsCOMArray<msgIAddressObject> from;
|
|
fields->GetAddressingHeader("From", from, true);
|
|
|
|
// Copy all headers from the original compose field.
|
|
rv = finalHeaders->AddAllHeaders(fields);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
bool hasMessageId = false;
|
|
if (NS_SUCCEEDED(fields->HasHeader("Message-ID", &hasMessageId)) &&
|
|
hasMessageId)
|
|
{
|
|
/* MDN request header requires to have MessageID header presented
|
|
* in the message in order to
|
|
* coorelate the MDN reports to the original message. Here will be
|
|
* the right place
|
|
*/
|
|
|
|
bool returnReceipt = false;
|
|
fields->GetReturnReceipt(&returnReceipt);
|
|
if (returnReceipt &&
|
|
(deliver_mode != nsIMsgSend::nsMsgSaveAsDraft &&
|
|
deliver_mode != nsIMsgSend::nsMsgSaveAsTemplate))
|
|
{
|
|
int32_t receipt_header_type = nsIMsgMdnGenerator::eDntType;
|
|
fields->GetReceiptHeaderType(&receipt_header_type);
|
|
|
|
// nsIMsgMdnGenerator::eDntType = MDN Disposition-Notification-To: ;
|
|
// nsIMsgMdnGenerator::eRrtType = Return-Receipt-To: ;
|
|
// nsIMsgMdnGenerator::eDntRrtType = both MDN DNT and RRT headers .
|
|
if (receipt_header_type != nsIMsgMdnGenerator::eRrtType)
|
|
finalHeaders->SetAddressingHeader("Disposition-Notification-To", from);
|
|
if (receipt_header_type != nsIMsgMdnGenerator::eDntType)
|
|
finalHeaders->SetAddressingHeader("Return-Receipt-To", from);
|
|
}
|
|
}
|
|
|
|
PRExplodedTime now;
|
|
PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &now);
|
|
int gmtoffset = (now.tm_params.tp_gmt_offset + now.tm_params.tp_dst_offset) / 60;
|
|
|
|
/* Use PR_FormatTimeUSEnglish() to format the date in US English format,
|
|
then figure out what our local GMT offset is, and append it (since
|
|
PR_FormatTimeUSEnglish() can't do that.) Generate four digit years as
|
|
per RFC 1123 (superceding RFC 822.)
|
|
*/
|
|
char dateString[130];
|
|
PR_FormatTimeUSEnglish(dateString, sizeof(dateString),
|
|
"%a, %d %b %Y %H:%M:%S ",
|
|
&now);
|
|
|
|
char *entryPoint = dateString + strlen(dateString);
|
|
PR_snprintf(entryPoint, sizeof(dateString) - (entryPoint - dateString),
|
|
"%c%02d%02d" CRLF,
|
|
(gmtoffset >= 0 ? '+' : '-'),
|
|
((gmtoffset >= 0 ? gmtoffset : -gmtoffset) / 60),
|
|
((gmtoffset >= 0 ? gmtoffset : -gmtoffset) % 60));
|
|
finalHeaders->SetRawHeader("Date", nsDependentCString(dateString), nullptr);
|
|
|
|
// X-Mozilla-Draft-Info
|
|
if (isDraft)
|
|
{
|
|
nsAutoCString draftInfo;
|
|
draftInfo.AppendLiteral("internal/draft; ");
|
|
APPEND_BOOL(AttachVCard, "vcard");
|
|
draftInfo.AppendLiteral("; ");
|
|
bool hasReturnReceipt = false;
|
|
fields->GetReturnReceipt(&hasReturnReceipt);
|
|
if (hasReturnReceipt)
|
|
{
|
|
// slight change compared to 4.x; we used to use receipt= to tell
|
|
// whether the draft/template has request for either MDN or DNS or both
|
|
// return receipt; since the DNS is out of the picture we now use the
|
|
// header type + 1 to tell whether user has requested the return receipt
|
|
int32_t headerType = 0;
|
|
fields->GetReceiptHeaderType(&headerType);
|
|
draftInfo.AppendLiteral("receipt=");
|
|
draftInfo.AppendInt(headerType + 1);
|
|
}
|
|
else
|
|
draftInfo.AppendLiteral("receipt=0");
|
|
draftInfo.AppendLiteral("; ");
|
|
APPEND_BOOL(DSN, "DSN");
|
|
draftInfo.AppendLiteral("; ");
|
|
draftInfo.AppendLiteral("uuencode=0");
|
|
draftInfo.AppendLiteral("; ");
|
|
APPEND_BOOL(AttachmentReminder, "attachmentreminder");
|
|
draftInfo.AppendLiteral("; ");
|
|
int32_t deliveryFormat;
|
|
fields->GetDeliveryFormat(&deliveryFormat);
|
|
draftInfo.AppendLiteral("deliveryformat=");
|
|
draftInfo.AppendInt(deliveryFormat);
|
|
|
|
finalHeaders->SetRawHeader(HEADER_X_MOZILLA_DRAFT_INFO, draftInfo, nullptr);
|
|
}
|
|
|
|
nsCOMPtr<nsIHttpProtocolHandler> pHTTPHandler = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv);
|
|
if (NS_SUCCEEDED(rv) && pHTTPHandler)
|
|
{
|
|
nsAutoCString userAgentString;
|
|
pHTTPHandler->GetUserAgent(userAgentString);
|
|
|
|
if (!userAgentString.IsEmpty())
|
|
finalHeaders->SetUnstructuredHeader("User-Agent",
|
|
NS_ConvertUTF8toUTF16(userAgentString));
|
|
}
|
|
|
|
finalHeaders->SetUnstructuredHeader("MIME-Version", NS_LITERAL_STRING("1.0"));
|
|
|
|
nsAutoCString newsgroups;
|
|
finalHeaders->GetRawHeader("Newsgroups", newsgroups);
|
|
if (!newsgroups.IsEmpty())
|
|
{
|
|
// Since the newsgroup header can contain data in the form of:
|
|
// "news://news.mozilla.org/netscape.test,news://news.mozilla.org/netscape.junk"
|
|
// we need to turn that into: "netscape.test,netscape.junk"
|
|
// (XXX: can it really?)
|
|
nsCOMPtr<nsINntpService> nntpService =
|
|
do_GetService("@mozilla.org/messenger/nntpservice;1", &rv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsCString newsgroupsHeaderVal;
|
|
nsCString newshostHeaderVal;
|
|
rv = nntpService->GenerateNewsHeaderValsForPosting(newsgroups,
|
|
getter_Copies(newsgroupsHeaderVal), getter_Copies(newshostHeaderVal));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
finalHeaders->SetRawHeader("Newsgroups", newsgroupsHeaderVal, nullptr);
|
|
|
|
// If we are here, we are NOT going to send this now. (i.e. it is a Draft,
|
|
// Send Later file, etc...). Because of that, we need to store what the user
|
|
// typed in on the original composition window for use later when rebuilding
|
|
// the headers
|
|
if (deliver_mode != nsIMsgSend::nsMsgDeliverNow &&
|
|
deliver_mode != nsIMsgSend::nsMsgSendUnsent)
|
|
{
|
|
// This is going to be saved for later, that means we should just store
|
|
// what the user typed into the "Newsgroup" line in the HEADER_X_MOZILLA_NEWSHOST
|
|
// header for later use by "Send Unsent Messages", "Drafts" or "Templates"
|
|
finalHeaders->SetRawHeader(HEADER_X_MOZILLA_NEWSHOST, newshostHeaderVal,
|
|
nullptr);
|
|
}
|
|
|
|
// Newsgroups are a recipient...
|
|
hasDisclosedRecipient = true;
|
|
}
|
|
|
|
nsCOMArray<msgIAddressObject> recipients;
|
|
finalHeaders->GetAddressingHeader("To", recipients);
|
|
hasDisclosedRecipient |= !recipients.IsEmpty();
|
|
finalHeaders->GetAddressingHeader("Cc", recipients);
|
|
hasDisclosedRecipient |= !recipients.IsEmpty();
|
|
|
|
// If we don't have disclosed recipient (only Bcc), address the message to
|
|
// undisclosed-recipients to prevent problem with some servers
|
|
|
|
// If we are saving the message as a draft, don't bother inserting the undisclosed recipients field. We'll take care of that when we
|
|
// really send the message.
|
|
if (!hasDisclosedRecipient && !isDraft)
|
|
{
|
|
bool bAddUndisclosedRecipients = true;
|
|
prefs->GetBoolPref("mail.compose.add_undisclosed_recipients", &bAddUndisclosedRecipients);
|
|
if (bAddUndisclosedRecipients)
|
|
{
|
|
bool hasBcc = false;
|
|
fields->HasHeader("Bcc", &hasBcc);
|
|
if (hasBcc)
|
|
{
|
|
nsCOMPtr<nsIStringBundleService> stringService =
|
|
mozilla::services::GetStringBundleService();
|
|
if (stringService)
|
|
{
|
|
nsCOMPtr<nsIStringBundle> composeStringBundle;
|
|
rv = stringService->CreateBundle("chrome://messenger/locale/messengercompose/composeMsgs.properties", getter_AddRefs(composeStringBundle));
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
nsString undisclosedRecipients;
|
|
rv = composeStringBundle->GetStringFromName(u"undisclosedRecipients",
|
|
getter_Copies(undisclosedRecipients));
|
|
if (NS_SUCCEEDED(rv) && !undisclosedRecipients.IsEmpty())
|
|
{
|
|
nsCOMPtr<nsIMsgHeaderParser> headerParser(
|
|
mozilla::services::GetHeaderParser());
|
|
nsCOMPtr<msgIAddressObject> group;
|
|
headerParser->MakeGroupObject(undisclosedRecipients,
|
|
nullptr, 0, getter_AddRefs(group));
|
|
recipients.AppendElement(group);
|
|
finalHeaders->SetAddressingHeader("To", recipients);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// We don't want to emit a Bcc header to the output. If we are saving this to
|
|
// Drafts/Sent, this is readded later in nsMsgSend.cpp.
|
|
finalHeaders->DeleteHeader("bcc");
|
|
|
|
// Skip no or empty priority.
|
|
nsAutoCString priority;
|
|
rv = fields->GetRawHeader("X-Priority", priority);
|
|
if (NS_SUCCEEDED(rv) && !priority.IsEmpty())
|
|
{
|
|
nsMsgPriorityValue priorityValue;
|
|
|
|
NS_MsgGetPriorityFromString(priority.get(), priorityValue);
|
|
|
|
// Skip default priority.
|
|
if (priorityValue != nsMsgPriority::Default) {
|
|
nsAutoCString priorityName;
|
|
nsAutoCString priorityValueString;
|
|
|
|
NS_MsgGetPriorityValueString(priorityValue, priorityValueString);
|
|
NS_MsgGetUntranslatedPriorityName(priorityValue, priorityName);
|
|
|
|
// Output format: [X-Priority: <pValue> (<pName>)]
|
|
priorityValueString.AppendLiteral(" (");
|
|
priorityValueString += priorityName;
|
|
priorityValueString.AppendLiteral(")");
|
|
finalHeaders->SetRawHeader("X-Priority", priorityValueString, nullptr);
|
|
}
|
|
}
|
|
|
|
nsAutoCString references;
|
|
finalHeaders->GetRawHeader("References", references);
|
|
if (!references.IsEmpty())
|
|
{
|
|
// The References header should be kept under 998 characters: if it's too
|
|
// long, trim out the earliest references to make it smaller.
|
|
if (references.Length() > 986)
|
|
{
|
|
int32_t firstRef = references.FindChar('<');
|
|
int32_t secondRef = references.FindChar('<', firstRef + 1);
|
|
if (secondRef > 0)
|
|
{
|
|
nsAutoCString newReferences(StringHead(references, secondRef));
|
|
int32_t bracket = references.FindChar('<',
|
|
references.Length() + newReferences.Length() - 986);
|
|
if (bracket > 0)
|
|
{
|
|
newReferences.Append(Substring(references, bracket));
|
|
finalHeaders->SetRawHeader("References", newReferences, nullptr);
|
|
}
|
|
}
|
|
}
|
|
// The In-Reply-To header is the last entry in the references header...
|
|
int32_t bracket = references.RFind("<");
|
|
if (bracket >= 0)
|
|
finalHeaders->SetRawHeader("In-Reply-To", Substring(references, bracket),
|
|
nullptr);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
#undef APPEND_BOOL // X-Mozilla-Draft-Info helper macro
|
|
|
|
static void
|
|
GenerateGlobalRandomBytes(unsigned char *buf, int32_t len)
|
|
{
|
|
// Attempt to generate bytes from system entropy-based RNG.
|
|
nsCOMPtr<nsIRandomGenerator> randomGenerator(do_GetService("@mozilla.org/security/random-generator;1"));
|
|
MOZ_ASSERT(randomGenerator, "nsIRandomGenerator service not retrievable");
|
|
uint8_t *tempBuffer;
|
|
nsresult rv = randomGenerator->GenerateRandomBytes(len, &tempBuffer);
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
memcpy(buf, tempBuffer, len);
|
|
free(tempBuffer);
|
|
return;
|
|
}
|
|
// nsIRandomGenerator failed -- fall back to low entropy PRNG.
|
|
static bool firstTime = true;
|
|
if (firstTime)
|
|
{
|
|
// Seed the random-number generator with current time so that
|
|
// the numbers will be different every time we run.
|
|
srand( (unsigned)PR_Now() );
|
|
firstTime = false;
|
|
}
|
|
|
|
for( int32_t i = 0; i < len; i++ )
|
|
buf[i] = rand() % 256;
|
|
}
|
|
|
|
char
|
|
*mime_make_separator(const char *prefix)
|
|
{
|
|
unsigned char rand_buf[13];
|
|
GenerateGlobalRandomBytes(rand_buf, 12);
|
|
|
|
return PR_smprintf("------------%s"
|
|
"%02X%02X%02X%02X"
|
|
"%02X%02X%02X%02X"
|
|
"%02X%02X%02X%02X",
|
|
prefix,
|
|
rand_buf[0], rand_buf[1], rand_buf[2], rand_buf[3],
|
|
rand_buf[4], rand_buf[5], rand_buf[6], rand_buf[7],
|
|
rand_buf[8], rand_buf[9], rand_buf[10], rand_buf[11]);
|
|
}
|
|
|
|
static char *
|
|
RFC2231ParmFolding(const char *parmName, const nsCString& charset,
|
|
const char *language, const nsString& parmValue);
|
|
|
|
static char *
|
|
LegacyParmFolding(const nsCString& aCharset,
|
|
const nsCString& aFileName, int32_t aParmFolding);
|
|
|
|
char *
|
|
mime_generate_attachment_headers (const char *type,
|
|
const char *type_param,
|
|
const char *encoding,
|
|
const char *description,
|
|
const char *x_mac_type,
|
|
const char *x_mac_creator,
|
|
const char *real_name,
|
|
const char *base_url,
|
|
bool /*digest_p*/,
|
|
nsMsgAttachmentHandler * /*ma*/,
|
|
const char *attachmentCharset,
|
|
const char *bodyCharset,
|
|
bool bodyIsAsciiOnly,
|
|
const char *content_id,
|
|
bool aBodyDocument)
|
|
{
|
|
NS_ASSERTION (encoding, "null encoding");
|
|
|
|
int32_t parmFolding = 2; // RFC 2231-compliant
|
|
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
|
|
if (prefs)
|
|
prefs->GetIntPref("mail.strictly_mime.parm_folding", &parmFolding);
|
|
|
|
/* Let's encode the real name */
|
|
char *encodedRealName = nullptr;
|
|
nsCString charset; // actual charset used for MIME encode
|
|
nsAutoString realName;
|
|
if (real_name)
|
|
{
|
|
// first try main body's charset to encode the file name,
|
|
// then try local file system charset if fails
|
|
CopyUTF8toUTF16(nsDependentCString(real_name), realName);
|
|
if (bodyCharset && *bodyCharset &&
|
|
nsMsgI18Ncheck_data_in_charset_range(bodyCharset, realName.get()))
|
|
charset.Assign(bodyCharset);
|
|
else
|
|
{
|
|
charset.Assign(nsMsgI18NFileSystemCharset());
|
|
if (!nsMsgI18Ncheck_data_in_charset_range(charset.get(), realName.get()))
|
|
charset.Assign("UTF-8"); // set to UTF-8 if fails again
|
|
}
|
|
|
|
encodedRealName = RFC2231ParmFolding("filename", charset, nullptr,
|
|
realName);
|
|
// somehow RFC2231ParamFolding failed. fall back to legacy method
|
|
if (!encodedRealName || !*encodedRealName) {
|
|
PR_FREEIF(encodedRealName);
|
|
parmFolding = 0;
|
|
// Not RFC 2231 style encoding (it's not standard-compliant)
|
|
encodedRealName =
|
|
LegacyParmFolding(charset, nsDependentCString(real_name), parmFolding);
|
|
}
|
|
}
|
|
|
|
nsCString buf; // very likely to be longer than 64 characters
|
|
buf.Append("Content-Type: ");
|
|
buf.Append(type);
|
|
if (type_param && *type_param)
|
|
{
|
|
if (*type_param != ';')
|
|
buf.Append("; ");
|
|
buf.Append(type_param);
|
|
}
|
|
|
|
if (mime_type_needs_charset (type))
|
|
{
|
|
|
|
char charset_label[65] = ""; // Content-Type: charset
|
|
if (attachmentCharset)
|
|
{
|
|
PL_strncpy(charset_label, attachmentCharset, sizeof(charset_label)-1);
|
|
charset_label[sizeof(charset_label)-1] = '\0';
|
|
}
|
|
|
|
/* If the characters are all 7bit, arguably it's better to
|
|
claim the charset to be US-ASCII. However, it causes
|
|
a major 'interoperability problem' with MS OE, which makes it hard
|
|
to sell Mozilla/TB to people most of whose correspondents use
|
|
MS OE. MS OE turns all non-ASCII characters to question marks
|
|
in replies to messages labeled as US-ASCII if users select 'send as is'
|
|
with MIME turned on. (with MIME turned off, this happens without
|
|
any warning.) To avoid this, we use the label 'US-ASCII' only when
|
|
it's explicitly requested by setting the hidden pref.
|
|
'mail.label_ascii_only_mail_as_us_ascii'. (bug 247958) */
|
|
bool labelAsciiAsAscii = false;
|
|
if (prefs)
|
|
prefs->GetBoolPref("mail.label_ascii_only_mail_as_us_ascii",
|
|
&labelAsciiAsAscii);
|
|
if (labelAsciiAsAscii && encoding &&
|
|
!PL_strcasecmp (encoding, "7bit") && bodyIsAsciiOnly)
|
|
PL_strcpy (charset_label, "us-ascii");
|
|
|
|
// If charset is multibyte then no charset to be specified (apply base64 instead).
|
|
// The list of file types match with PickEncoding() where we put base64 label.
|
|
if ( ((attachmentCharset && !nsMsgI18Nmultibyte_charset(attachmentCharset)) ||
|
|
((PL_strcasecmp(type, TEXT_HTML) == 0) ||
|
|
(PL_strcasecmp(type, TEXT_MDL) == 0) ||
|
|
(PL_strcasecmp(type, TEXT_PLAIN) == 0) ||
|
|
(PL_strcasecmp(type, TEXT_RICHTEXT) == 0) ||
|
|
(PL_strcasecmp(type, TEXT_ENRICHED) == 0) ||
|
|
(PL_strcasecmp(type, TEXT_VCARD) == 0) ||
|
|
(PL_strcasecmp(type, APPLICATION_DIRECTORY) == 0) || /* text/x-vcard synonym */
|
|
(PL_strcasecmp(type, TEXT_CSS) == 0) ||
|
|
(PL_strcasecmp(type, TEXT_JSSS) == 0)) ||
|
|
(PL_strcasecmp(encoding, ENCODING_BASE64) != 0)) &&
|
|
(*charset_label))
|
|
{
|
|
buf.Append("; charset=");
|
|
buf.Append(charset_label);
|
|
}
|
|
}
|
|
|
|
// Only do this if we are in the body of a message
|
|
if (aBodyDocument)
|
|
{
|
|
// Add format=flowed as in RFC 2646 if we are using that
|
|
if(type && !PL_strcasecmp(type, "text/plain"))
|
|
{
|
|
bool flowed, delsp, formatted, disallowBreaks;
|
|
GetSerialiserFlags(bodyCharset, &flowed, &delsp, &formatted, &disallowBreaks);
|
|
if(flowed)
|
|
buf.Append("; format=flowed");
|
|
if (delsp)
|
|
buf.Append("; delsp=yes");
|
|
// else
|
|
// {
|
|
// Don't add a markup. Could use
|
|
// PUSH_STRING ("; format=fixed");
|
|
// but it is equivalent to nothing at all and we do want
|
|
// to save bandwidth. Don't we?
|
|
// }
|
|
}
|
|
}
|
|
|
|
if (x_mac_type && *x_mac_type) {
|
|
buf.Append("; x-mac-type=\"");
|
|
buf.Append(x_mac_type);
|
|
buf.Append("\"");
|
|
}
|
|
|
|
if (x_mac_creator && *x_mac_creator) {
|
|
buf.Append("; x-mac-creator=\"");
|
|
buf.Append(x_mac_creator);
|
|
buf.Append("\"");
|
|
}
|
|
|
|
#ifdef EMIT_NAME_IN_CONTENT_TYPE
|
|
if (encodedRealName && *encodedRealName) {
|
|
// Note that we don't need to output the name field if the name encoding is
|
|
// RFC 2231. If the MUA knows the RFC 2231, it should know the RFC 2183 too.
|
|
if (parmFolding != 2) {
|
|
// The underlying JS MIME code will only handle UTF-8 here.
|
|
char *nameValue = LegacyParmFolding(NS_LITERAL_CSTRING("UTF-8"),
|
|
nsDependentCString(real_name),
|
|
parmFolding);
|
|
if (!nameValue || !*nameValue) {
|
|
PR_FREEIF(nameValue);
|
|
nameValue = encodedRealName;
|
|
}
|
|
buf.Append(";\r\n name=\"");
|
|
buf.Append(nameValue);
|
|
buf.Append("\"");
|
|
if (nameValue != encodedRealName)
|
|
PR_FREEIF(nameValue);
|
|
}
|
|
}
|
|
#endif /* EMIT_NAME_IN_CONTENT_TYPE */
|
|
buf.Append(CRLF);
|
|
|
|
buf.Append("Content-Transfer-Encoding: ");
|
|
buf.Append(encoding);
|
|
buf.Append(CRLF);
|
|
|
|
if (description && *description) {
|
|
char *s = mime_fix_header (description);
|
|
if (s) {
|
|
buf.Append("Content-Description: ");
|
|
buf.Append(s);
|
|
buf.Append(CRLF);
|
|
PR_Free(s);
|
|
}
|
|
}
|
|
|
|
if ( (content_id) && (*content_id) )
|
|
{
|
|
buf.Append("Content-ID: <");
|
|
buf.Append(content_id);
|
|
buf.Append(">");
|
|
buf.Append(CRLF);
|
|
}
|
|
|
|
if (encodedRealName && *encodedRealName) {
|
|
char *period = PL_strrchr(encodedRealName, '.');
|
|
int32_t pref_content_disposition = 0;
|
|
|
|
if (prefs) {
|
|
mozilla::DebugOnly<nsresult> rv = prefs->GetIntPref("mail.content_disposition_type",
|
|
&pref_content_disposition);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get mail.content_disposition_type");
|
|
}
|
|
|
|
buf.Append("Content-Disposition: ");
|
|
|
|
// If this is an attachment which is part of the message body and therefore has a
|
|
// Content-ID (e.g, image in HTML msg), then Content-Disposition has to be inline
|
|
if (content_id && *content_id)
|
|
buf.Append("inline");
|
|
else if (pref_content_disposition == 1)
|
|
buf.Append("attachment");
|
|
else
|
|
if (pref_content_disposition == 2 &&
|
|
(!PL_strcasecmp(type, TEXT_PLAIN) ||
|
|
(period && !PL_strcasecmp(period, ".txt"))))
|
|
buf.Append("attachment");
|
|
|
|
/* If this document is an anonymous binary file or a vcard,
|
|
then always show it as an attachment, never inline. */
|
|
else
|
|
if (!PL_strcasecmp(type, APPLICATION_OCTET_STREAM) ||
|
|
!PL_strcasecmp(type, TEXT_VCARD) ||
|
|
!PL_strcasecmp(type, APPLICATION_DIRECTORY)) /* text/x-vcard synonym */
|
|
buf.Append("attachment");
|
|
else
|
|
buf.Append("inline");
|
|
|
|
buf.Append(";\r\n ");
|
|
buf.Append(encodedRealName);
|
|
buf.Append(CRLF);
|
|
}
|
|
else
|
|
if (type &&
|
|
(!PL_strcasecmp (type, MESSAGE_RFC822) ||
|
|
!PL_strcasecmp (type, MESSAGE_NEWS)))
|
|
buf.Append("Content-Disposition: inline" CRLF);
|
|
|
|
#ifdef GENERATE_CONTENT_BASE
|
|
/* If this is an HTML document, and we know the URL it originally
|
|
came from, write out a Content-Base header. */
|
|
if (type &&
|
|
(!PL_strcasecmp (type, TEXT_HTML) ||
|
|
!PL_strcasecmp (type, TEXT_MDL)) &&
|
|
base_url && *base_url)
|
|
{
|
|
int32_t col = 0;
|
|
const char *s = base_url;
|
|
const char *colon = PL_strchr (s, ':');
|
|
bool useContentLocation = false; /* rhp - add this */
|
|
|
|
if (!colon)
|
|
goto GIVE_UP_ON_CONTENT_BASE; /* malformed URL? */
|
|
|
|
/* Don't emit a content-base that points to (or into) a news or
|
|
mail message. */
|
|
if (!PL_strncasecmp (s, "news:", 5) ||
|
|
!PL_strncasecmp (s, "snews:", 6) ||
|
|
!PL_strncasecmp (s, "IMAP:", 5) ||
|
|
!PL_strncasecmp (s, "file:", 5) || /* rhp: fix targets from included HTML files */
|
|
!PL_strncasecmp (s, "mailbox:", 8))
|
|
goto GIVE_UP_ON_CONTENT_BASE;
|
|
|
|
/* rhp - Put in a pref for using Content-Location instead of Content-Base.
|
|
This will get tweaked to default to true in 5.0
|
|
*/
|
|
if (prefs)
|
|
prefs->GetBoolPref("mail.use_content_location_on_send", &useContentLocation);
|
|
|
|
if (useContentLocation)
|
|
buf.Append("Content-Location: \"");
|
|
else
|
|
buf.Append("Content-Base: \"");
|
|
/* rhp - Pref for Content-Location usage */
|
|
|
|
/* rhp: this is to work with the Content-Location stuff */
|
|
CONTENT_LOC_HACK:
|
|
|
|
while (*s != 0 && *s != '#')
|
|
{
|
|
uint32_t ot=buf.Length();
|
|
char tmp[]="\x00\x00";
|
|
/* URLs must be wrapped at 40 characters or less. */
|
|
if (col >= 38) {
|
|
buf.Append(CRLF "\t");
|
|
col = 0;
|
|
}
|
|
|
|
if (*s == ' ')
|
|
buf.Append("%20");
|
|
else if (*s == '\t')
|
|
buf.Append("%09");
|
|
else if (*s == '\n')
|
|
buf.Append("%0A");
|
|
else if (*s == '\r')
|
|
buf.Append("%0D");
|
|
else {
|
|
tmp[0]=*s;
|
|
buf.Append(tmp);
|
|
}
|
|
s++;
|
|
col += (buf.Length() - ot);
|
|
}
|
|
buf.Append("\"" CRLF);
|
|
|
|
/* rhp: this is to try to get around this fun problem with Content-Location */
|
|
if (!useContentLocation) {
|
|
buf.Append("Content-Location: \"");
|
|
s = base_url;
|
|
col = 0;
|
|
useContentLocation = true;
|
|
goto CONTENT_LOC_HACK;
|
|
}
|
|
/* rhp: this is to try to get around this fun problem with Content-Location */
|
|
|
|
GIVE_UP_ON_CONTENT_BASE:
|
|
;
|
|
}
|
|
#endif /* GENERATE_CONTENT_BASE */
|
|
|
|
/* realloc it smaller... */
|
|
|
|
#ifdef DEBUG_jungshik
|
|
printf ("header=%s\n", buf.get());
|
|
#endif
|
|
PR_Free(encodedRealName);
|
|
return PL_strdup(buf.get());
|
|
}
|
|
|
|
static bool isValidHost( const char* host )
|
|
{
|
|
if ( host )
|
|
for (const char *s = host; *s; ++s)
|
|
if ( !isalpha(*s)
|
|
&& !isdigit(*s)
|
|
&& *s != '-'
|
|
&& *s != '_'
|
|
&& *s != '.'
|
|
)
|
|
{
|
|
host = nullptr;
|
|
break;
|
|
}
|
|
|
|
return nullptr != host;
|
|
}
|
|
|
|
char *
|
|
msg_generate_message_id (nsIMsgIdentity *identity)
|
|
{
|
|
const char *host = 0;
|
|
|
|
nsCString forcedFQDN;
|
|
nsCString from;
|
|
nsresult rv = NS_OK;
|
|
|
|
rv = identity->GetCharAttribute("FQDN", forcedFQDN);
|
|
|
|
if (NS_SUCCEEDED(rv) && !forcedFQDN.IsEmpty())
|
|
host = forcedFQDN.get();
|
|
|
|
if (!isValidHost(host))
|
|
{
|
|
nsresult rv = identity->GetEmail(from);
|
|
if (NS_SUCCEEDED(rv) && !from.IsEmpty())
|
|
host = strchr(from.get(),'@');
|
|
|
|
// No '@'? Munged address, anti-spam?
|
|
// see bug #197203
|
|
if (host)
|
|
++host;
|
|
}
|
|
|
|
if (!isValidHost(host))
|
|
/* If we couldn't find a valid host name to use, we can't generate a
|
|
valid message ID, so bail, and let NNTP and SMTP generate them. */
|
|
return 0;
|
|
|
|
// Generate 128-bit UUID for the local part. We use the high-entropy
|
|
// GenerateGlobalRandomBytes to make tracking more difficult.
|
|
nsID uuid;
|
|
GenerateGlobalRandomBytes((unsigned char*) &uuid, sizeof(nsID));
|
|
char uuidString[NSID_LENGTH];
|
|
uuid.ToProvidedString(uuidString);
|
|
// Drop first and last characters (curly braces).
|
|
uuidString[NSID_LENGTH - 2] = 0;
|
|
return PR_smprintf("<%s@%s>", uuidString + 1, host);
|
|
}
|
|
|
|
|
|
inline static bool is7bitCharset(const nsCString& charset)
|
|
{
|
|
// charset name is canonical (no worry about case-sensitivity)
|
|
return Substring(charset, 0, 8).EqualsLiteral("ISO-2022-");
|
|
}
|
|
|
|
#define PR_MAX_FOLDING_LEN 75 // this is to gurantee the folded line will
|
|
// never be greater than 78 = 75 + CRLFLWSP
|
|
/*static */ char *
|
|
RFC2231ParmFolding(const char *parmName, const nsCString& charset,
|
|
const char *language, const nsString& parmValue)
|
|
{
|
|
NS_ENSURE_TRUE(parmName && *parmName && !parmValue.IsEmpty(), nullptr);
|
|
|
|
bool needEscape;
|
|
nsCString dupParm;
|
|
|
|
if (!NS_IsAscii(static_cast<const char16_t*>(parmValue.get())) || is7bitCharset(charset)) {
|
|
needEscape = true;
|
|
nsAutoCString nativeParmValue;
|
|
ConvertFromUnicode(charset.get(), parmValue, nativeParmValue);
|
|
MsgEscapeString(nativeParmValue, nsINetUtil::ESCAPE_ALL, dupParm);
|
|
}
|
|
else {
|
|
needEscape = false;
|
|
dupParm.Adopt(
|
|
msg_make_filename_qtext(NS_LossyConvertUTF16toASCII(parmValue).get(),
|
|
true));
|
|
}
|
|
|
|
if (dupParm.IsEmpty())
|
|
return nullptr;
|
|
|
|
int32_t parmNameLen = PL_strlen(parmName);
|
|
int32_t parmValueLen = dupParm.Length();
|
|
|
|
parmNameLen += 5; // *=__'__'___ or *[0]*=__'__'__ or *[1]*=___ or *[0]="___"
|
|
|
|
int32_t languageLen = language ? PL_strlen(language) : 0;
|
|
int32_t charsetLen = charset.Length();
|
|
char *foldedParm = nullptr;
|
|
|
|
if ((parmValueLen + parmNameLen + charsetLen + languageLen) <
|
|
PR_MAX_FOLDING_LEN)
|
|
{
|
|
foldedParm = PL_strdup(parmName);
|
|
if (needEscape)
|
|
{
|
|
NS_MsgSACat(&foldedParm, "*=");
|
|
if (charsetLen)
|
|
NS_MsgSACat(&foldedParm, charset.get());
|
|
NS_MsgSACat(&foldedParm, "'");
|
|
if (languageLen)
|
|
NS_MsgSACat(&foldedParm, language);
|
|
NS_MsgSACat(&foldedParm, "'");
|
|
}
|
|
else
|
|
NS_MsgSACat(&foldedParm, "=\"");
|
|
NS_MsgSACat(&foldedParm, dupParm.get());
|
|
if (!needEscape)
|
|
NS_MsgSACat(&foldedParm, "\"");
|
|
}
|
|
else
|
|
{
|
|
int curLineLen = 0;
|
|
int counter = 0;
|
|
char digits[32];
|
|
char *start = dupParm.BeginWriting();
|
|
char *end = NULL;
|
|
char tmp = 0;
|
|
|
|
while (parmValueLen > 0)
|
|
{
|
|
curLineLen = 0;
|
|
if (counter == 0) {
|
|
PR_FREEIF(foldedParm)
|
|
foldedParm = PL_strdup(parmName);
|
|
}
|
|
else {
|
|
NS_MsgSACat(&foldedParm, ";\r\n ");
|
|
NS_MsgSACat(&foldedParm, parmName);
|
|
}
|
|
PR_snprintf(digits, sizeof(digits), "*%d", counter);
|
|
NS_MsgSACat(&foldedParm, digits);
|
|
curLineLen += PL_strlen(digits);
|
|
if (needEscape)
|
|
{
|
|
NS_MsgSACat(&foldedParm, "*=");
|
|
if (counter == 0)
|
|
{
|
|
if (charsetLen)
|
|
NS_MsgSACat(&foldedParm, charset.get());
|
|
NS_MsgSACat(&foldedParm, "'");
|
|
if (languageLen)
|
|
NS_MsgSACat(&foldedParm, language);
|
|
NS_MsgSACat(&foldedParm, "'");
|
|
curLineLen += charsetLen;
|
|
curLineLen += languageLen;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NS_MsgSACat(&foldedParm, "=\"");
|
|
}
|
|
counter++;
|
|
curLineLen += parmNameLen;
|
|
if (parmValueLen <= PR_MAX_FOLDING_LEN - curLineLen)
|
|
end = start + parmValueLen;
|
|
else
|
|
end = start + (PR_MAX_FOLDING_LEN - curLineLen);
|
|
|
|
tmp = 0;
|
|
if (*end && needEscape)
|
|
{
|
|
// check to see if we are in the middle of escaped char
|
|
if (*end == '%')
|
|
{
|
|
tmp = '%'; *end = 0;
|
|
}
|
|
else if (end-1 > start && *(end-1) == '%')
|
|
{
|
|
end -= 1; tmp = '%'; *end = 0;
|
|
}
|
|
else if (end-2 > start && *(end-2) == '%')
|
|
{
|
|
end -= 2; tmp = '%'; *end = 0;
|
|
}
|
|
else
|
|
{
|
|
tmp = *end; *end = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// XXX should check if we are in the middle of escaped char (RFC 822)
|
|
tmp = *end; *end = 0;
|
|
}
|
|
NS_MsgSACat(&foldedParm, start);
|
|
if (!needEscape)
|
|
NS_MsgSACat(&foldedParm, "\"");
|
|
|
|
parmValueLen -= (end-start);
|
|
if (tmp)
|
|
*end = tmp;
|
|
start = end;
|
|
}
|
|
}
|
|
|
|
return foldedParm;
|
|
}
|
|
|
|
/*static */ char *
|
|
LegacyParmFolding(const nsCString& aCharset,
|
|
const nsCString& aFileName, int32_t aParmFolding)
|
|
{
|
|
bool usemime = nsMsgMIMEGetConformToStandard();
|
|
char *encodedRealName =
|
|
nsMsgI18NEncodeMimePartIIStr(aFileName.get(), false, aCharset.get(),
|
|
0, usemime);
|
|
|
|
if (!encodedRealName || !*encodedRealName) {
|
|
PR_FREEIF(encodedRealName);
|
|
encodedRealName = (char *) PR_Malloc(aFileName.Length() + 1);
|
|
if (encodedRealName)
|
|
PL_strcpy(encodedRealName, aFileName.get());
|
|
}
|
|
|
|
// Now put backslashes before special characters per RFC 822
|
|
char *qtextName =
|
|
msg_make_filename_qtext(encodedRealName, aParmFolding == 0);
|
|
if (qtextName) {
|
|
PR_FREEIF(encodedRealName);
|
|
encodedRealName = qtextName;
|
|
}
|
|
return encodedRealName;
|
|
}
|
|
|
|
bool
|
|
mime_7bit_data_p (const char *string, uint32_t size)
|
|
{
|
|
if ((!string) || (!*string))
|
|
return true;
|
|
|
|
char *ptr = (char *)string;
|
|
for (uint32_t i=0; i<size; i++)
|
|
{
|
|
if ((unsigned char) ptr[i] > 0x7F)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* Strips whitespace, and expands newlines into newline-tab for use in
|
|
mail headers. Returns a new string or 0 (if it would have been empty.)
|
|
If addr_p is true, the addresses will be parsed and reemitted as
|
|
rfc822 mailboxes.
|
|
*/
|
|
char *
|
|
mime_fix_header_1 (const char *string, bool addr_p, bool news_p)
|
|
{
|
|
char *new_string;
|
|
const char *in;
|
|
char *out;
|
|
int32_t i, old_size, new_size;
|
|
|
|
if (!string || !*string)
|
|
return 0;
|
|
|
|
if (addr_p) {
|
|
return strdup(string);
|
|
}
|
|
|
|
old_size = PL_strlen (string);
|
|
new_size = old_size;
|
|
for (i = 0; i < old_size; i++)
|
|
if (string[i] == '\r' || string[i] == '\n')
|
|
new_size += 2;
|
|
|
|
new_string = (char *) PR_Malloc (new_size + 1);
|
|
if (! new_string)
|
|
return 0;
|
|
|
|
in = string;
|
|
out = new_string;
|
|
|
|
/* strip leading whitespace. */
|
|
while (IS_SPACE (*in))
|
|
in++;
|
|
|
|
/* replace CR, LF, or CRLF with CRLF-TAB. */
|
|
while (*in) {
|
|
if (*in == '\r' || *in == '\n') {
|
|
if (*in == '\r' && in[1] == '\n')
|
|
in++;
|
|
in++;
|
|
*out++ = '\r';
|
|
*out++ = '\n';
|
|
*out++ = '\t';
|
|
}
|
|
else
|
|
if (news_p && *in == ',') {
|
|
*out++ = *in++;
|
|
/* skip over all whitespace after a comma. */
|
|
while (IS_SPACE (*in))
|
|
in++;
|
|
}
|
|
else
|
|
*out++ = *in++;
|
|
}
|
|
*out = 0;
|
|
|
|
/* strip trailing whitespace. */
|
|
while (out > in && IS_SPACE (out[-1]))
|
|
*out-- = 0;
|
|
|
|
/* If we ended up throwing it all away, use 0 instead of "". */
|
|
if (!*new_string) {
|
|
PR_Free (new_string);
|
|
new_string = 0;
|
|
}
|
|
|
|
return new_string;
|
|
}
|
|
|
|
char *
|
|
mime_fix_header (const char *string)
|
|
{
|
|
return mime_fix_header_1 (string, false, false);
|
|
}
|
|
|
|
char *
|
|
mime_fix_addr_header (const char *string)
|
|
{
|
|
return mime_fix_header_1 (string, true, false);
|
|
}
|
|
|
|
char *
|
|
mime_fix_news_header (const char *string)
|
|
{
|
|
return mime_fix_header_1 (string, false, true);
|
|
}
|
|
|
|
bool
|
|
mime_type_requires_b64_p (const char *type)
|
|
{
|
|
if (!type || !PL_strcasecmp (type, UNKNOWN_CONTENT_TYPE))
|
|
/* Unknown types don't necessarily require encoding. (Note that
|
|
"unknown" and "application/octet-stream" aren't the same.) */
|
|
return false;
|
|
|
|
else if (!PL_strncasecmp (type, "image/", 6) ||
|
|
!PL_strncasecmp (type, "audio/", 6) ||
|
|
!PL_strncasecmp (type, "video/", 6) ||
|
|
!PL_strncasecmp (type, "application/", 12))
|
|
{
|
|
/* The following types are application/ or image/ types that are actually
|
|
known to contain textual data (meaning line-based, not binary, where
|
|
CRLF conversion is desired rather than disasterous.) So, if the type
|
|
is any of these, it does not *require* base64, and if we do need to
|
|
encode it for other reasons, we'll probably use quoted-printable.
|
|
But, if it's not one of these types, then we assume that any subtypes
|
|
of the non-"text/" types are binary data, where CRLF conversion would
|
|
corrupt it, so we use base64 right off the bat.
|
|
|
|
The reason it's desirable to ship these as text instead of just using
|
|
base64 all the time is mainly to preserve the readability of them for
|
|
non-MIME users: if I mail a /bin/sh script to someone, it might not
|
|
need to be encoded at all, so we should leave it readable if we can.
|
|
|
|
This list of types was derived from the comp.mail.mime FAQ, section
|
|
10.2.2, "List of known unregistered MIME types" on 2-Feb-96.
|
|
*/
|
|
static const char *app_and_image_types_which_are_really_text[] = {
|
|
"application/mac-binhex40", /* APPLICATION_BINHEX */
|
|
"application/pgp", /* APPLICATION_PGP */
|
|
"application/pgp-keys",
|
|
"application/x-pgp-message", /* APPLICATION_PGP2 */
|
|
"application/postscript", /* APPLICATION_POSTSCRIPT */
|
|
"application/x-uuencode", /* APPLICATION_UUENCODE */
|
|
"application/x-uue", /* APPLICATION_UUENCODE2 */
|
|
"application/uue", /* APPLICATION_UUENCODE4 */
|
|
"application/uuencode", /* APPLICATION_UUENCODE3 */
|
|
"application/sgml",
|
|
"application/x-csh",
|
|
"application/javascript",
|
|
"application/ecmascript",
|
|
"application/x-javascript",
|
|
"application/x-latex",
|
|
"application/x-macbinhex40",
|
|
"application/x-ns-proxy-autoconfig",
|
|
"application/x-www-form-urlencoded",
|
|
"application/x-perl",
|
|
"application/x-sh",
|
|
"application/x-shar",
|
|
"application/x-tcl",
|
|
"application/x-tex",
|
|
"application/x-texinfo",
|
|
"application/x-troff",
|
|
"application/x-troff-man",
|
|
"application/x-troff-me",
|
|
"application/x-troff-ms",
|
|
"application/x-troff-ms",
|
|
"application/x-wais-source",
|
|
"image/x-bitmap",
|
|
"image/x-pbm",
|
|
"image/x-pgm",
|
|
"image/x-portable-anymap",
|
|
"image/x-portable-bitmap",
|
|
"image/x-portable-graymap",
|
|
"image/x-portable-pixmap", /* IMAGE_PPM */
|
|
"image/x-ppm",
|
|
"image/x-xbitmap", /* IMAGE_XBM */
|
|
"image/x-xbm", /* IMAGE_XBM2 */
|
|
"image/xbm", /* IMAGE_XBM3 */
|
|
"image/x-xpixmap",
|
|
"image/x-xpm",
|
|
0 };
|
|
const char **s;
|
|
for (s = app_and_image_types_which_are_really_text; *s; s++)
|
|
if (!PL_strcasecmp (type, *s))
|
|
return false;
|
|
|
|
/* All others must be assumed to be binary formats, and need Base64. */
|
|
return true;
|
|
}
|
|
|
|
else
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Some types should have a "charset=" parameter, and some shouldn't.
|
|
// This is what decides.
|
|
//
|
|
bool
|
|
mime_type_needs_charset (const char *type)
|
|
{
|
|
/* Only text types should have charset. */
|
|
if (!type || !*type)
|
|
return false;
|
|
else
|
|
if (!PL_strncasecmp (type, "text", 4))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/* Given a string, convert it to 'qtext' (quoted text) for RFC822 header purposes. */
|
|
char *
|
|
msg_make_filename_qtext(const char *srcText, bool stripCRLFs)
|
|
{
|
|
/* newString can be at most twice the original string (every char quoted). */
|
|
char *newString = (char *) PR_Malloc(PL_strlen(srcText)*2 + 1);
|
|
if (!newString) return NULL;
|
|
|
|
const char *s = srcText;
|
|
const char *end = srcText + PL_strlen(srcText);
|
|
char *d = newString;
|
|
|
|
while(*s)
|
|
{
|
|
/* Put backslashes in front of existing backslashes, or double quote
|
|
characters.
|
|
If stripCRLFs is true, don't write out CRs or LFs. Otherwise,
|
|
write out a backslash followed by the CR but not
|
|
linear-white-space.
|
|
We might already have quoted pair of "\ " or "\\t" skip it.
|
|
*/
|
|
if (*s == '\\' || *s == '"' ||
|
|
(!stripCRLFs &&
|
|
(*s == '\r' && (s[1] != '\n' ||
|
|
(s[1] == '\n' && (s+2) < end && !IS_SPACE(s[2]))))))
|
|
*d++ = '\\';
|
|
|
|
if (stripCRLFs && *s == '\r' && s[1] == '\n' && (s+2) < end && IS_SPACE(s[2]))
|
|
{
|
|
s += 3; // skip CRLFLWSP
|
|
}
|
|
else
|
|
{
|
|
*d++ = *s++;
|
|
}
|
|
}
|
|
*d = 0;
|
|
|
|
return newString;
|
|
}
|
|
|
|
/* Rip apart the URL and extract a reasonable value for the `real_name' slot.
|
|
*/
|
|
void
|
|
msg_pick_real_name (nsMsgAttachmentHandler *attachment, const char16_t *proposedName, const char *charset)
|
|
{
|
|
const char *s, *s2;
|
|
|
|
if (!attachment->m_realName.IsEmpty())
|
|
return;
|
|
|
|
if (proposedName && *proposedName)
|
|
{
|
|
attachment->m_realName.Adopt(ToNewUTF8String(nsAutoString(proposedName)));
|
|
}
|
|
else //Let's extract the name from the URL
|
|
{
|
|
nsCString url;
|
|
nsresult rv = attachment->mURL->GetSpec(url);
|
|
if (NS_FAILED(rv))
|
|
return;
|
|
|
|
s = url.get();
|
|
s2 = PL_strchr (s, ':');
|
|
if (s2)
|
|
s = s2 + 1;
|
|
/* If we know the URL doesn't have a sensible file name in it,
|
|
don't bother emitting a content-disposition. */
|
|
if (StringBeginsWith (url, NS_LITERAL_CSTRING("news:"), nsCaseInsensitiveCStringComparator()) ||
|
|
StringBeginsWith (url, NS_LITERAL_CSTRING("snews:"), nsCaseInsensitiveCStringComparator()) ||
|
|
StringBeginsWith (url, NS_LITERAL_CSTRING("IMAP:"), nsCaseInsensitiveCStringComparator()) ||
|
|
StringBeginsWith (url, NS_LITERAL_CSTRING("mailbox:"), nsCaseInsensitiveCStringComparator()))
|
|
return;
|
|
|
|
if (StringBeginsWith(url, NS_LITERAL_CSTRING("data:"),
|
|
nsCaseInsensitiveCStringComparator()))
|
|
{
|
|
int32_t endNonData = url.FindChar(',');
|
|
if (endNonData == -1)
|
|
return;
|
|
nsCString nonDataPart(Substring(url, 5, endNonData - 5));
|
|
int32_t filenamePos = nonDataPart.Find("filename=");
|
|
if (filenamePos != -1)
|
|
{
|
|
filenamePos += 9;
|
|
int32_t endFilename = nonDataPart.FindChar(';', filenamePos);
|
|
if (endFilename == -1)
|
|
endFilename = endNonData;
|
|
attachment->m_realName = Substring(nonDataPart, filenamePos,
|
|
endFilename - filenamePos);
|
|
}
|
|
else
|
|
{
|
|
// no filename; need to construct one based on the content type.
|
|
nsCOMPtr<nsIMIMEService> mimeService(do_GetService(NS_MIMESERVICE_CONTRACTID));
|
|
if (!mimeService)
|
|
return;
|
|
nsCOMPtr<nsIMIMEInfo> mimeInfo;
|
|
nsCString mediaType(Substring(nonDataPart, 0, nonDataPart.FindChar(';')));
|
|
mimeService->GetFromTypeAndExtension(mediaType, EmptyCString(), getter_AddRefs(mimeInfo));
|
|
if (!mimeInfo)
|
|
return;
|
|
nsCString filename;
|
|
nsCString extension;
|
|
mimeInfo->GetPrimaryExtension(extension);
|
|
unsigned char filePrefixBytes[8];
|
|
GenerateGlobalRandomBytes(filePrefixBytes, 8);
|
|
// Create a filename prefix with 16 lowercase letters,
|
|
// representing 8 bytes.
|
|
for (int32_t i = 0; i < 8; i++)
|
|
{
|
|
// A pair of letters, each any of (a-p).
|
|
filename.Append((filePrefixBytes[i] & 0xF) + 'a');
|
|
filename.Append((filePrefixBytes[i] >> 4) + 'a');
|
|
}
|
|
filename.Append('.');
|
|
filename.Append(extension);
|
|
attachment->m_realName = filename;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Take the part of the file name after the last / or \ */
|
|
s2 = PL_strrchr (s, '/');
|
|
if (s2) s = s2+1;
|
|
s2 = PL_strrchr (s, '\\');
|
|
|
|
if (s2) s = s2+1;
|
|
/* Copy it into the attachment struct. */
|
|
attachment->m_realName = s;
|
|
int32_t charPos = attachment->m_realName.FindChar('?');
|
|
if (charPos != -1)
|
|
attachment->m_realName.SetLength(charPos);
|
|
/* Now trim off any named anchors or search data. */
|
|
charPos = attachment->m_realName.FindChar('#');
|
|
if (charPos != -1)
|
|
attachment->m_realName.SetLength(charPos);
|
|
}
|
|
/* Now lose the %XX crap. */
|
|
nsCString unescaped_real_name;
|
|
MsgUnescapeString(attachment->m_realName, 0, unescaped_real_name);
|
|
attachment->m_realName = unescaped_real_name;
|
|
}
|
|
|
|
/* Now a special case for attaching uuencoded files...
|
|
|
|
If we attach a file "foo.txt.uu", we will send it out with
|
|
Content-Type: text/plain; Content-Transfer-Encoding: x-uuencode.
|
|
When saving such a file, a mail reader will generally decode it first
|
|
(thus removing the uuencoding.) So, let's make life a little easier by
|
|
removing the indication of uuencoding from the file name itself. (This
|
|
will presumably make the file name in the Content-Disposition header be
|
|
the same as the file name in the "begin" line of the uuencoded data.)
|
|
|
|
However, since there are mailers out there (including earlier versions of
|
|
Mozilla) that will use "foo.txt.uu" as the file name, we still need to
|
|
cope with that; the code which copes with that is in the MIME parser, in
|
|
libmime/mimei.c.
|
|
*/
|
|
if (attachment->m_already_encoded_p && !attachment->m_encoding.IsEmpty())
|
|
{
|
|
/* #### TOTAL KLUDGE.
|
|
I'd like to ask the mime.types file, "what extensions correspond
|
|
to obj->encoding (which happens to be "x-uuencode") but doing that
|
|
in a non-sphagetti way would require brain surgery. So, since
|
|
currently uuencode is the only content-transfer-encoding which we
|
|
understand which traditionally has an extension, we just special-
|
|
case it here!
|
|
|
|
Note that it's special-cased in a similar way in libmime/mimei.c.
|
|
*/
|
|
if (attachment->m_encoding.LowerCaseEqualsLiteral(ENCODING_UUENCODE) ||
|
|
attachment->m_encoding.LowerCaseEqualsLiteral(ENCODING_UUENCODE2) ||
|
|
attachment->m_encoding.LowerCaseEqualsLiteral(ENCODING_UUENCODE3) ||
|
|
attachment->m_encoding.LowerCaseEqualsLiteral(ENCODING_UUENCODE4))
|
|
{
|
|
if (StringEndsWith(attachment->m_realName, NS_LITERAL_CSTRING(".uu")))
|
|
attachment->m_realName.Cut(attachment->m_realName.Length() - 3, 3);
|
|
else if (StringEndsWith(attachment->m_realName, NS_LITERAL_CSTRING(".uue")))
|
|
attachment->m_realName.Cut(attachment->m_realName.Length() - 4, 4);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Utility to create a nsIURI object...
|
|
nsresult
|
|
nsMsgNewURL(nsIURI** aInstancePtrResult, const char * aSpec)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
if (nullptr == aInstancePtrResult)
|
|
return NS_ERROR_NULL_POINTER;
|
|
nsCOMPtr<nsIIOService> pNetService =
|
|
mozilla::services::GetIOService();
|
|
NS_ENSURE_TRUE(pNetService, NS_ERROR_UNEXPECTED);
|
|
if (PL_strstr(aSpec, "://") == nullptr && strncmp(aSpec, "data:", 5))
|
|
{
|
|
//XXXjag Temporary fix for bug 139362 until the real problem(bug 70083) get fixed
|
|
nsAutoCString uri(NS_LITERAL_CSTRING("http://"));
|
|
uri.Append(aSpec);
|
|
rv = pNetService->NewURI(uri, nullptr, nullptr, aInstancePtrResult);
|
|
}
|
|
else
|
|
rv = pNetService->NewURI(nsDependentCString(aSpec), nullptr, nullptr, aInstancePtrResult);
|
|
return rv;
|
|
}
|
|
|
|
bool
|
|
nsMsgIsLocalFile(const char *url)
|
|
{
|
|
/*
|
|
A url is considered as a local file if it's start with file://
|
|
But on Window, we need to filter UNC file url because there
|
|
are not really local file. Those start with file:////
|
|
*/
|
|
if (PL_strncasecmp(url, "file://", 7) == 0)
|
|
{
|
|
#ifdef XP_WIN
|
|
if (PL_strncasecmp(url, "file:////", 9) == 0)
|
|
return false;
|
|
#endif
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
char
|
|
*nsMsgGetLocalFileFromURL(const char *url)
|
|
{
|
|
char * finalPath;
|
|
NS_ASSERTION(PL_strncasecmp(url, "file://", 7) == 0, "invalid url");
|
|
finalPath = (char*)PR_Malloc(strlen(url));
|
|
if (finalPath == NULL)
|
|
return NULL;
|
|
strcpy(finalPath, url+6+1);
|
|
return finalPath;
|
|
}
|
|
|
|
char *
|
|
nsMsgParseURLHost(const char *url)
|
|
{
|
|
nsIURI *workURI = nullptr;
|
|
nsresult rv;
|
|
|
|
rv = nsMsgNewURL(&workURI, url);
|
|
if (NS_FAILED(rv) || !workURI)
|
|
return nullptr;
|
|
|
|
nsAutoCString host;
|
|
rv = workURI->GetHost(host);
|
|
NS_IF_RELEASE(workURI);
|
|
if (NS_FAILED(rv))
|
|
return nullptr;
|
|
|
|
return ToNewCString(host);
|
|
}
|
|
|
|
char *
|
|
GenerateFileNameFromURI(nsIURI *aURL)
|
|
{
|
|
nsresult rv;
|
|
nsCString file;
|
|
nsCString spec;
|
|
char *returnString;
|
|
char *cp = nullptr;
|
|
char *cp1 = nullptr;
|
|
|
|
rv = aURL->GetPath(file);
|
|
if ( NS_SUCCEEDED(rv) && !file.IsEmpty())
|
|
{
|
|
char *newFile = ToNewCString(file);
|
|
if (!newFile)
|
|
return nullptr;
|
|
|
|
// strip '/'
|
|
cp = PL_strrchr(newFile, '/');
|
|
if (cp)
|
|
++cp;
|
|
else
|
|
cp = newFile;
|
|
|
|
if (*cp)
|
|
{
|
|
if ((cp1 = PL_strchr(cp, '/'))) *cp1 = 0;
|
|
if ((cp1 = PL_strchr(cp, '?'))) *cp1 = 0;
|
|
if ((cp1 = PL_strchr(cp, '>'))) *cp1 = 0;
|
|
if (*cp != '\0')
|
|
{
|
|
returnString = PL_strdup(cp);
|
|
PR_FREEIF(newFile);
|
|
return returnString;
|
|
}
|
|
}
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
cp = nullptr;
|
|
cp1 = nullptr;
|
|
|
|
|
|
rv = aURL->GetSpec(spec);
|
|
if ( NS_SUCCEEDED(rv) && !spec.IsEmpty())
|
|
{
|
|
char *newSpec = ToNewCString(spec);
|
|
if (!newSpec)
|
|
return nullptr;
|
|
|
|
char *cp2 = NULL, *cp3=NULL ;
|
|
|
|
// strip '"'
|
|
cp2 = newSpec;
|
|
while (*cp2 == '"')
|
|
cp2++;
|
|
if ((cp3 = PL_strchr(cp2, '"')))
|
|
*cp3 = 0;
|
|
|
|
char *hostStr = nsMsgParseURLHost(cp2);
|
|
if (!hostStr)
|
|
hostStr = PL_strdup(cp2);
|
|
|
|
bool isHTTP = false;
|
|
if (NS_SUCCEEDED(aURL->SchemeIs("http", &isHTTP)) && isHTTP)
|
|
{
|
|
returnString = PR_smprintf("%s.html", hostStr);
|
|
PR_FREEIF(hostStr);
|
|
}
|
|
else
|
|
returnString = hostStr;
|
|
|
|
PR_FREEIF(newSpec);
|
|
return returnString;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
//
|
|
// This routine will generate a content id for use in a mail part.
|
|
// It will take the part number passed in as well as the email
|
|
// address. If the email address is null or invalid, we will simply
|
|
// use netscape.com for the interesting part. The content ID's will
|
|
// look like the following:
|
|
//
|
|
// Content-ID: <part1.36DF1DCE.73B5A330@netscape.com>
|
|
//
|
|
char *
|
|
mime_gen_content_id(uint32_t aPartNum, const char *aEmailAddress)
|
|
{
|
|
int32_t randLen = 5;
|
|
unsigned char rand_buf1[5];
|
|
unsigned char rand_buf2[5];
|
|
const char *domain = nullptr;
|
|
const char *defaultDomain = "@netscape.com";
|
|
|
|
memset(rand_buf1, 0, randLen-1);
|
|
memset(rand_buf2, 0, randLen-1);
|
|
|
|
GenerateGlobalRandomBytes(rand_buf1, randLen);
|
|
GenerateGlobalRandomBytes(rand_buf2, randLen);
|
|
|
|
// Find the @domain.com string...
|
|
if (aEmailAddress && *aEmailAddress)
|
|
domain = const_cast<const char*>(PL_strchr(aEmailAddress, '@'));
|
|
|
|
if (!domain)
|
|
domain = defaultDomain;
|
|
|
|
char *retVal = PR_smprintf("part%d."
|
|
"%02X%02X%02X%02X"
|
|
"."
|
|
"%02X%02X%02X%02X"
|
|
"%s",
|
|
aPartNum,
|
|
rand_buf1[0], rand_buf1[1], rand_buf1[2], rand_buf1[3],
|
|
rand_buf2[0], rand_buf2[1], rand_buf2[2], rand_buf2[3],
|
|
domain);
|
|
|
|
return retVal;
|
|
}
|
|
|
|
void
|
|
GetFolderURIFromUserPrefs(nsMsgDeliverMode aMode, nsIMsgIdentity* identity, nsCString& uri)
|
|
{
|
|
nsresult rv;
|
|
uri.Truncate();
|
|
|
|
// QueueForLater (Outbox)
|
|
if (aMode == nsIMsgSend::nsMsgQueueForLater ||
|
|
aMode == nsIMsgSend::nsMsgDeliverBackground)
|
|
{
|
|
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
|
|
if (NS_FAILED(rv))
|
|
return;
|
|
rv = prefs->GetCharPref("mail.default_sendlater_uri", getter_Copies(uri));
|
|
if (NS_FAILED(rv) || uri.IsEmpty())
|
|
uri.AssignLiteral(ANY_SERVER);
|
|
else
|
|
{
|
|
// check if uri is unescaped, and if so, escape it and reset the pef.
|
|
if (uri.FindChar(' ') != kNotFound)
|
|
{
|
|
MsgReplaceSubstring(uri, " ", "%20");
|
|
prefs->SetCharPref("mail.default_sendlater_uri", uri.get());
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!identity)
|
|
return;
|
|
|
|
if (aMode == nsIMsgSend::nsMsgSaveAsDraft) // SaveAsDraft (Drafts)
|
|
rv = identity->GetDraftFolder(uri);
|
|
else if (aMode == nsIMsgSend::nsMsgSaveAsTemplate) // SaveAsTemplate (Templates)
|
|
rv = identity->GetStationeryFolder(uri);
|
|
else
|
|
{
|
|
bool doFcc = false;
|
|
rv = identity->GetDoFcc(&doFcc);
|
|
if (doFcc)
|
|
rv = identity->GetFccFolder(uri);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Check if we should use format=flowed (RFC 2646) for a mail.
|
|
* We will use format=flowed unless the preference tells us not to do so.
|
|
* In this function we set all the serialiser flags.
|
|
* 'formatted' is always 'true'.
|
|
*/
|
|
void GetSerialiserFlags(const char* charset, bool* flowed, bool* delsp, bool* formatted, bool* disallowBreaks)
|
|
{
|
|
*flowed = false;
|
|
*delsp = false;
|
|
*formatted = true;
|
|
*disallowBreaks = true;
|
|
|
|
// Set format=flowed as in RFC 2646 according to the preference.
|
|
nsresult rv;
|
|
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
|
|
if (NS_SUCCEEDED(rv)) {
|
|
prefs->GetBoolPref("mailnews.send_plaintext_flowed", flowed);
|
|
}
|
|
|
|
// We could test statefulCharset(charset) here, but since ISO-2022-JP is the
|
|
// only one left we support, we might as well check for it directly.
|
|
if (PL_strcasecmp(charset, "ISO-2022-JP") == 0) {
|
|
// Make sure we honour RFC 1468. For encoding in ISO-2022-JP we need to
|
|
// send short lines to allow 7bit transfer encoding.
|
|
*disallowBreaks = false;
|
|
if (*flowed)
|
|
*delsp = true;
|
|
}
|
|
}
|