in mail/base/content/mailCommands.js [64:455]
async function ComposeMessage(
type,
format,
folder,
messageArray,
selection = null,
autodetectCharset = false
) {
const aboutMessage =
document.getElementById("tabmail")?.currentAboutMessage ||
document.getElementById("messageBrowser")?.contentWindow;
const currentHeaderData = aboutMessage?.currentHeaderData;
function isCurrentlyDisplayed(hdr) {
return (
currentHeaderData && // ignoring enclosing brackets:
currentHeaderData["message-id"]?.headerValue.includes(hdr.messageId)
);
}
function findDeliveredToIdentityEmail(hdr) {
// This function reads from currentHeaderData, which is only useful if we're
// looking at the currently-displayed message. Otherwise, just return
// immediately so we don't waste time.
if (!isCurrentlyDisplayed(hdr)) {
return "";
}
// Get the delivered-to headers.
let key = "delivered-to";
const deliveredTos = [];
let index = 0;
let header = "";
while ((header = currentHeaderData[key])) {
deliveredTos.push(header.headerValue.toLowerCase().trim());
key = "delivered-to" + index++;
}
// Reverse the array so that the last delivered-to header will show at front.
deliveredTos.reverse();
for (let i = 0; i < deliveredTos.length; i++) {
for (const identity of MailServices.accounts.allIdentities) {
if (!identity.email) {
continue;
}
// If the deliver-to header contains the defined identity, that's it.
if (
deliveredTos[i] == identity.email.toLowerCase() ||
deliveredTos[i].includes("<" + identity.email.toLowerCase() + ">")
) {
return identity.email;
}
}
}
return "";
}
let msgKey;
if (messageArray && messageArray.length == 1) {
msgKey = GetMsgKeyFromURI(messageArray[0]);
}
// Check if the draft is already open in another window. If it is, just focus the window.
if (type == Ci.nsIMsgCompType.Draft && messageArray.length == 1) {
// We'll search this uri in the opened windows.
for (const win of Services.wm.getEnumerator("")) {
// Check if it is a compose window.
if (
win.document.defaultView.gMsgCompose &&
win.document.defaultView.gMsgCompose.compFields.draftId
) {
const wKey = GetMsgKeyFromURI(
win.document.defaultView.gMsgCompose.compFields.draftId
);
if (wKey == msgKey) {
// Found ! just focus it...
win.focus();
// ...and nothing to do anymore.
return;
}
}
}
}
let identity = null;
let newsgroup = null;
let hdr;
try {
if (folder) {
// Get the incoming server associated with this uri.
const server = folder.server;
// If they hit new or reply and they are reading a newsgroup,
// turn this into a new post or a reply to group.
if (
!folder.isServer &&
server.type == "nntp" &&
type == Ci.nsIMsgCompType.New
) {
type = Ci.nsIMsgCompType.NewsPost;
newsgroup = folder.URI;
}
identity = folder.customIdentity;
if (!identity) {
[identity] = MailUtils.getIdentityForServer(server);
}
}
} catch (ex) {
dump("failed to get an identity to pre-select: " + ex + "\n");
}
// dump("\nComposeMessage from XUL: " + identity + "\n");
switch (type) {
case Ci.nsIMsgCompType.New: // new message
// dump("OpenComposeWindow with " + identity + "\n");
MailServices.compose.OpenComposeWindow(
null,
null,
null,
type,
format,
identity,
null,
msgWindow
);
return;
case Ci.nsIMsgCompType.NewsPost:
// dump("OpenComposeWindow with " + identity + " and " + newsgroup + "\n");
MailServices.compose.OpenComposeWindow(
null,
null,
newsgroup,
type,
format,
identity,
null,
msgWindow
);
return;
case Ci.nsIMsgCompType.ForwardAsAttachment:
if (messageArray && messageArray.length) {
// If we have more than one ForwardAsAttachment then pass null instead
// of the header to tell the compose service to work out the attachment
// subjects from the URIs.
hdr =
messageArray.length > 1
? null
: messenger.msgHdrFromURI(messageArray[0]);
MailServices.compose.OpenComposeWindow(
null,
hdr,
messageArray.join(","),
type,
format,
identity,
null,
msgWindow
);
}
return;
default:
if (!messageArray) {
return;
}
// Limit the number of new compose windows to 8. Why 8 ?
// I like that number :-)
if (messageArray.length > 8) {
messageArray.length = 8;
}
for (const messageUri of messageArray) {
hdr = messenger.msgHdrFromURI(messageUri);
if (
[
Ci.nsIMsgCompType.Reply,
Ci.nsIMsgCompType.ReplyAll,
Ci.nsIMsgCompType.ReplyToSender,
// Author's address doesn't matter for followup to a newsgroup.
// Ci.nsIMsgCompType.ReplyToGroup,
Ci.nsIMsgCompType.ReplyToSenderAndGroup,
Ci.nsIMsgCompType.ReplyWithTemplate,
Ci.nsIMsgCompType.ReplyToList,
].includes(type)
) {
const replyTo = hdr.getStringProperty("replyTo");
const from = replyTo || hdr.author;
const fromAddrs = MailServices.headerParser.parseEncodedHeader(
from,
null
);
let email = fromAddrs[0]?.email;
if (
type == Ci.nsIMsgCompType.ReplyToList &&
isCurrentlyDisplayed(hdr)
) {
// ReplyToList is only enabled for current message (if at all), so
// using currentHeaderData is ok.
// List-Post value is of the format <mailto:list@example.com>
const listPost = currentHeaderData["list-post"]?.headerValue;
if (listPost) {
email = listPost.replace(/.*<mailto:(.+)>.*/, "$1");
}
}
if (
/^(.*[._-])?(do[._-]?not|no)[._-]?reply([._+-].*)?@/i.test(email)
) {
const [title, message, replyAnywayButton] =
await document.l10n.formatValues([
{ id: "no-reply-title" },
{ id: "no-reply-message", args: { email } },
{ id: "no-reply-reply-anyway-button" },
]);
const buttonFlags =
Ci.nsIPrompt.BUTTON_TITLE_IS_STRING * Ci.nsIPrompt.BUTTON_POS_0 +
Ci.nsIPrompt.BUTTON_TITLE_CANCEL * Ci.nsIPrompt.BUTTON_POS_1 +
Ci.nsIPrompt.BUTTON_POS_1_DEFAULT;
if (
Services.prompt.confirmEx(
window,
title,
message,
buttonFlags,
replyAnywayButton,
null, // cancel
null,
null,
{}
)
) {
continue;
}
}
}
if (FeedUtils.isFeedMessage(hdr)) {
// Do not use the header derived identity for feeds, pass on only a
// possible server identity from above.
openComposeWindowForRSSArticle(
null,
hdr,
messageUri,
type,
format,
identity,
msgWindow
);
} else {
// Replies come here.
let useCatchAll = false;
// Check if we are using catchAll on any identity. If current
// folder has some customIdentity set, ignore catchAll settings.
// CatchAll is not applicable to news (and doesn't work, bug 545365).
if (
hdr.folder &&
hdr.folder.server.type != "nntp" &&
!hdr.folder.customIdentity
) {
useCatchAll = MailServices.accounts.allIdentities.some(
id => id.catchAll
);
}
if (useCatchAll) {
// If we use catchAll, we need to get all headers.
// MsgHdr retrieval is asynchronous, do everything in the callback.
MsgHdrToMimeMessage(
hdr,
null,
function (msgHdr, mimeMsg) {
const catchAllHeaders = Services.prefs
.getStringPref("mail.compose.catchAllHeaders")
.split(",")
.map(header => header.toLowerCase().trim());
// Collect catchAll hints from given headers.
let collectedHeaderAddresses = "";
for (const header of catchAllHeaders) {
if (mimeMsg.has(header)) {
for (const mimeMsgHeader of mimeMsg.headers[header]) {
collectedHeaderAddresses +=
MailServices.headerParser
.parseEncodedHeaderW(mimeMsgHeader)
.toString() + ",";
}
}
}
let [hdrIdentity, matchingHint] =
MailUtils.getIdentityForHeader(
msgHdr,
type,
collectedHeaderAddresses
);
// The found identity might have no catchAll enabled.
if (hdrIdentity.catchAll && matchingHint) {
// If name is not set in matchingHint, search trough other hints.
if (matchingHint.email && !matchingHint.name) {
const hints =
MailServices.headerParser.makeFromDisplayAddress(
msgHdr.recipients +
"," +
msgHdr.ccList +
"," +
collectedHeaderAddresses
);
for (const hint of hints) {
if (
hint.name &&
hint.email.toLowerCase() ==
matchingHint.email.toLowerCase()
) {
matchingHint =
MailServices.headerParser.makeMailboxObject(
hint.name,
matchingHint.email
);
break;
}
}
}
} else {
matchingHint = MailServices.headerParser.makeMailboxObject(
"",
""
);
}
// Now open compose window and use matching hint as reply sender.
MailServices.compose.OpenComposeWindow(
null,
msgHdr,
messageUri,
type,
format,
hdrIdentity,
matchingHint.toString(),
msgWindow,
selection,
autodetectCharset
);
},
true,
{ saneBodySize: true }
);
} else {
let bestIdentity = null;
if (!identity && currentHeaderData?.newsgroups) {
// This appears to be a standalone newsgroup message opened from
// a file or 'news:' URI. Try to get the identity of the first
// NNTP account.
const server = MailServices.accounts.accounts.find(
account => account.incomingServer.type == "nntp"
)?.incomingServer;
if (server) {
[bestIdentity] = MailUtils.getIdentityForServer(server);
}
}
if (!bestIdentity) {
// Fall back to traditional behavior.
[bestIdentity] = MailUtils.getIdentityForHeader(
hdr,
type,
findDeliveredToIdentityEmail(hdr)
);
}
MailServices.compose.OpenComposeWindow(
null,
hdr,
messageUri,
type,
format,
bestIdentity,
null,
msgWindow,
selection,
autodetectCharset
);
}
}
}
}
}