in mailnews/compose/src/nsMsgCompose.cpp [1496:1932]
nsresult nsMsgCompose::CreateMessage(const nsACString& originalMsgURI,
MSG_ComposeType type,
nsIMsgCompFields* compFields) {
nsresult rv = NS_OK;
mType = type;
mDraftDisposition = nsIMsgFolder::nsMsgDispositionState_None;
mDeleteDraft = (type == nsIMsgCompType::Draft);
nsAutoCString msgUri(originalMsgURI);
bool fileUrl = StringBeginsWith(msgUri, "file:"_ns);
int32_t typeIndex = msgUri.Find("type=application/x-message-display");
if (typeIndex != kNotFound && typeIndex > 0) {
// Strip out type=application/x-message-display because it confuses libmime.
msgUri.Cut(typeIndex, sizeof("type=application/x-message-display"));
if (fileUrl) // we're dealing with an .eml file msg
{
// We have now removed the type from the uri. Make sure we don't have
// an uri with "&&" now. If we do, remove the second '&'.
if (msgUri.CharAt(typeIndex) == '&') msgUri.Cut(typeIndex, 1);
// Remove possible trailing '?'.
if (msgUri.CharAt(msgUri.Length() - 1) == '?')
msgUri.Cut(msgUri.Length() - 1, 1);
} else // we're dealing with a message/rfc822 attachment
{
// nsURLFetcher will check for "realtype=message/rfc822" and will set the
// content type to message/rfc822 in the forwarded message.
msgUri.AppendLiteral("&realtype=message/rfc822");
}
}
if (compFields) {
m_compFields = reinterpret_cast<nsMsgCompFields*>(compFields);
} else {
m_compFields = new nsMsgCompFields();
}
if (m_identity && mType != nsIMsgCompType::Draft) {
// Setup reply-to field.
nsCString replyTo;
m_identity->GetReplyTo(replyTo);
if (!replyTo.IsEmpty()) {
nsCString resultStr;
RemoveDuplicateAddresses(nsDependentCString(m_compFields->GetReplyTo()),
replyTo, resultStr);
if (!resultStr.IsEmpty()) {
replyTo.Append(',');
replyTo.Append(resultStr);
}
m_compFields->SetReplyTo(replyTo.get());
}
// Setup auto-Cc field.
bool doCc;
m_identity->GetDoCc(&doCc);
if (doCc) {
nsCString ccList;
m_identity->GetDoCcList(ccList);
nsCString resultStr;
RemoveDuplicateAddresses(nsDependentCString(m_compFields->GetCc()),
ccList, resultStr);
if (!resultStr.IsEmpty()) {
ccList.Append(',');
ccList.Append(resultStr);
}
m_compFields->SetCc(ccList.get());
}
// Setup auto-Bcc field.
bool doBcc;
m_identity->GetDoBcc(&doBcc);
if (doBcc) {
nsCString bccList;
m_identity->GetDoBccList(bccList);
nsCString resultStr;
RemoveDuplicateAddresses(nsDependentCString(m_compFields->GetBcc()),
bccList, resultStr);
if (!resultStr.IsEmpty()) {
bccList.Append(',');
bccList.Append(resultStr);
}
m_compFields->SetBcc(bccList.get());
}
}
if (mType == nsIMsgCompType::Draft) {
nsCString curDraftIdURL;
rv = m_compFields->GetDraftId(curDraftIdURL);
// Skip if no draft id (probably a new draft msg).
if (NS_SUCCEEDED(rv) && !curDraftIdURL.IsEmpty()) {
nsCOMPtr<nsIMsgDBHdr> msgDBHdr;
rv = GetMsgDBHdrFromURI(curDraftIdURL, getter_AddRefs(msgDBHdr));
NS_ASSERTION(NS_SUCCEEDED(rv),
"CreateMessage can't get msg header DB interface pointer.");
if (msgDBHdr) {
nsCString queuedDisposition;
msgDBHdr->GetStringProperty(QUEUED_DISPOSITION_PROPERTY,
queuedDisposition);
// We need to retrieve the original URI from the database so we can
// set the disposition flags correctly if the draft is a reply or
// forwarded message.
nsCString originalMsgURIfromDB;
msgDBHdr->GetStringProperty(ORIG_URI_PROPERTY, originalMsgURIfromDB);
mOriginalMsgURI = originalMsgURIfromDB;
if (!queuedDisposition.IsEmpty()) {
if (queuedDisposition.EqualsLiteral("replied"))
mDraftDisposition = nsIMsgFolder::nsMsgDispositionState_Replied;
else if (queuedDisposition.EqualsLiteral("forward"))
mDraftDisposition = nsIMsgFolder::nsMsgDispositionState_Forwarded;
else if (queuedDisposition.EqualsLiteral("redirected"))
mDraftDisposition = nsIMsgFolder::nsMsgDispositionState_Redirected;
}
}
} else {
NS_WARNING("CreateMessage can't get draft id");
}
}
// If we don't have an original message URI, nothing else to do...
if (msgUri.IsEmpty()) return NS_OK;
// store the original message URI so we can extract it after we send the
// message to properly mark any disposition flags like replied or forwarded on
// the message.
if (mOriginalMsgURI.IsEmpty()) mOriginalMsgURI = msgUri;
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
// "Forward inline" and "Reply with template" processing.
// Note the early return at the end of the block.
if (type == nsIMsgCompType::ForwardInline ||
type == nsIMsgCompType::ReplyWithTemplate) {
// We want to treat this message as a reference too
nsCOMPtr<nsIMsgDBHdr> msgHdr;
rv = GetMsgDBHdrFromURI(msgUri, getter_AddRefs(msgHdr));
if (NS_SUCCEEDED(rv)) {
nsAutoCString messageId;
msgHdr->GetMessageId(messageId);
nsAutoCString reference;
// When forwarding we only use the original message for "References:" -
// recipients don't have the other messages anyway.
// For reply with template we want to preserve all the references.
if (type == nsIMsgCompType::ReplyWithTemplate) {
uint16_t numReferences = 0;
msgHdr->GetNumReferences(&numReferences);
for (int32_t i = 0; i < numReferences; i++) {
nsAutoCString ref;
msgHdr->GetStringReference(i, ref);
if (!ref.IsEmpty()) {
reference.Append('<');
reference.Append(ref);
reference.AppendLiteral("> ");
}
}
reference.Trim(" ", false, true);
}
msgHdr->GetMessageId(messageId);
reference.Append('<');
reference.Append(messageId);
reference.Append('>');
m_compFields->SetReferences(reference.get());
if (type == nsIMsgCompType::ForwardInline) {
nsString subject;
msgHdr->GetMime2DecodedSubject(subject);
nsCString fwdPrefix;
prefs->GetCharPrefWithDefault("mail.forward_subject_prefix", "Fwd"_ns,
1, fwdPrefix);
nsString unicodeFwdPrefix;
CopyUTF8toUTF16(fwdPrefix, unicodeFwdPrefix);
unicodeFwdPrefix.AppendLiteral(": ");
subject.Insert(unicodeFwdPrefix, 0);
m_compFields->SetSubject(subject);
}
}
// Early return for "ForwardInline" and "ReplyWithTemplate" processing.
return NS_OK;
}
// All other processing.
// Note the following:
// LoadDraftOrTemplate() is run in nsMsgComposeService::OpenComposeWindow()
// for five compose types: ForwardInline, ReplyWithTemplate (both covered
// in the code block above) and Draft, Template and Redirect. For these
// compose types, the charset is already correct (incl. MIME-applied override)
// unless the default charset should be used.
bool isFirstPass = true;
char* uriList = ToNewCString(msgUri);
char* uri = uriList;
char* nextUri;
do {
nextUri = strstr(uri, "://");
if (nextUri) {
// look for next ://, and then back up to previous ','
nextUri = strstr(nextUri + 1, "://");
if (nextUri) {
*nextUri = '\0';
char* saveNextUri = nextUri;
nextUri = strrchr(uri, ',');
if (nextUri) *nextUri = '\0';
*saveNextUri = ':';
}
}
nsCOMPtr<nsIMsgDBHdr> msgHdr;
if (mOrigMsgHdr)
msgHdr = mOrigMsgHdr;
else {
rv = GetMsgDBHdrFromURI(nsDependentCString(uri), getter_AddRefs(msgHdr));
NS_ENSURE_SUCCESS(rv, rv);
}
if (msgHdr) {
nsString subject;
rv = msgHdr->GetMime2DecodedSubject(subject);
if (NS_FAILED(rv)) return rv;
// Check if (was: is present in the subject
int32_t wasOffset = subject.RFind(u" (was:"_ns);
bool strip = true;
if (wasOffset >= 0) {
// Check the number of references, to check if was: should be stripped
// First, assume that it should be stripped; the variable will be set to
// false later if stripping should not happen.
uint16_t numRef;
msgHdr->GetNumReferences(&numRef);
if (numRef) {
// If there are references, look for the first message in the thread
// firstly, get the database via the folder
nsCOMPtr<nsIMsgFolder> folder;
msgHdr->GetFolder(getter_AddRefs(folder));
if (folder) {
nsCOMPtr<nsIMsgDatabase> db;
folder->GetMsgDatabase(getter_AddRefs(db));
if (db) {
nsAutoCString reference;
msgHdr->GetStringReference(0, reference);
nsCOMPtr<nsIMsgDBHdr> refHdr;
db->GetMsgHdrForMessageID(reference.get(),
getter_AddRefs(refHdr));
if (refHdr) {
nsCString refSubject;
rv = refHdr->GetSubject(refSubject);
if (NS_SUCCEEDED(rv)) {
if (refSubject.Find(" (was:") >= 0) strip = false;
}
}
}
}
} else
strip = false;
}
if (strip && wasOffset >= 0) {
// Strip off the "(was: old subject)" part
subject.Assign(Substring(subject, 0, wasOffset));
}
switch (type) {
default:
break;
case nsIMsgCompType::Draft:
case nsIMsgCompType::Template:
case nsIMsgCompType::EditTemplate:
case nsIMsgCompType::EditAsNew: {
// If opening from file, preseve the subject already present, since
// we can't get a subject from db there.
if (mOriginalMsgURI.Find("&realtype=message/rfc822") != -1) {
break;
}
// Otherwise, set up the subject from db, with possible modifications.
uint32_t flags;
msgHdr->GetFlags(&flags);
if (flags & nsMsgMessageFlags::HasRe) {
subject.InsertLiteral(u"Re: ", 0);
}
// Set subject from db, where it's already decrypted. The raw
// header may be encrypted.
m_compFields->SetSubject(subject);
break;
}
case nsIMsgCompType::Reply:
case nsIMsgCompType::ReplyAll:
case nsIMsgCompType::ReplyToList:
case nsIMsgCompType::ReplyToGroup:
case nsIMsgCompType::ReplyToSender:
case nsIMsgCompType::ReplyToSenderAndGroup: {
if (!isFirstPass) // safeguard, just in case...
{
PR_Free(uriList);
return rv;
}
mQuotingToFollow = true;
subject.InsertLiteral(u"Re: ", 0);
m_compFields->SetSubject(subject);
// Setup quoting callbacks for later...
mWhatHolder = 1;
break;
}
case nsIMsgCompType::ForwardAsAttachment: {
// Add the forwarded message in the references, first
nsAutoCString messageId;
msgHdr->GetMessageId(messageId);
if (isFirstPass) {
nsAutoCString reference;
reference.Append('<');
reference.Append(messageId);
reference.Append('>');
m_compFields->SetReferences(reference.get());
} else {
nsAutoCString references;
m_compFields->GetReferences(getter_Copies(references));
references.AppendLiteral(" <");
references.Append(messageId);
references.Append('>');
m_compFields->SetReferences(references.get());
}
uint32_t flags;
msgHdr->GetFlags(&flags);
if (flags & nsMsgMessageFlags::HasRe)
subject.InsertLiteral(u"Re: ", 0);
// Setup quoting callbacks for later...
mQuotingToFollow =
false; // We don't need to quote the original message.
nsCOMPtr<nsIMsgAttachment> attachment = do_CreateInstance(
"@mozilla.org/messengercompose/attachment;1", &rv);
if (NS_SUCCEEDED(rv) && attachment) {
bool addExtension = true;
nsString sanitizedSubj;
prefs->GetBoolPref("mail.forward_add_extension", &addExtension);
// copy subject string to sanitizedSubj, use default if empty
if (subject.IsEmpty()) {
nsresult rv;
nsCOMPtr<nsIStringBundleService> bundleService =
mozilla::components::StringBundle::Service();
NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIStringBundle> composeBundle;
rv = bundleService->CreateBundle(
"chrome://messenger/locale/messengercompose/"
"composeMsgs.properties",
getter_AddRefs(composeBundle));
NS_ENSURE_SUCCESS(rv, rv);
composeBundle->GetStringFromName("messageAttachmentSafeName",
sanitizedSubj);
} else
sanitizedSubj.Assign(subject);
// set the file size
uint32_t messageSize;
msgHdr->GetMessageSize(&messageSize);
attachment->SetSize(messageSize);
// change all '.' to '_' see bug #271211
sanitizedSubj.ReplaceChar(u".", u'_');
if (addExtension) sanitizedSubj.AppendLiteral(".eml");
attachment->SetName(NS_ConvertUTF16toUTF8(sanitizedSubj));
attachment->SetUrl(nsDependentCString(uri));
attachment->SetContentType("message/rfc822"_ns);
m_compFields->AddAttachment(attachment);
}
if (isFirstPass) {
nsCString fwdPrefix;
prefs->GetCharPrefWithDefault("mail.forward_subject_prefix",
"Fwd"_ns, 1, fwdPrefix);
nsString unicodeFwdPrefix;
CopyUTF8toUTF16(fwdPrefix, unicodeFwdPrefix);
unicodeFwdPrefix.AppendLiteral(": ");
subject.Insert(unicodeFwdPrefix, 0);
m_compFields->SetSubject(subject);
}
break;
}
case nsIMsgCompType::Redirect: {
// For a redirect, set the Reply-To: header to what was in the
// original From: header...
nsAutoCString author;
msgHdr->GetAuthor(author);
m_compFields->SetSubject(subject);
m_compFields->SetReplyTo(author.get());
// ... and empty out the various recipient headers
nsAutoString empty;
m_compFields->SetTo(empty);
m_compFields->SetCc(empty);
m_compFields->SetBcc(empty);
m_compFields->SetNewsgroups(empty);
m_compFields->SetFollowupTo(empty);
// Add the redirected message in the references so that threading
// will work when the new recipient eventually replies to the
// original sender.
nsAutoCString messageId;
msgHdr->GetMessageId(messageId);
if (isFirstPass) {
nsAutoCString reference;
reference.Append('<');
reference.Append(messageId);
reference.Append('>');
m_compFields->SetReferences(reference.get());
} else {
nsAutoCString references;
m_compFields->GetReferences(getter_Copies(references));
references.AppendLiteral(" <");
references.Append(messageId);
references.Append('>');
m_compFields->SetReferences(references.get());
}
break;
}
}
}
isFirstPass = false;
if (nextUri) {
// `nextUri` can be a null pointer if `strstr` did not find `://` in the
// URI earlier. Only increment it if that is not the case, to avoid
// undefined behaviors.
uri = nextUri + 1;
}
} while (nextUri);
PR_Free(uriList);
return rv;
}