Bug 842632, part 1: Distinguish between building address strings for display or MIME, r=IanN, sr=Neil.
Even though seamonkey is a CLOSED TREE. --HG-- extra : amend_source : af960b16b0cb922faf08059afa172af938ccc28f
This commit is contained in:
Родитель
a8e0bc982f
Коммит
ae33eff54f
|
@ -549,7 +549,7 @@ function GenerateAddressFromCard(card)
|
|||
else
|
||||
email = card.primaryEmail;
|
||||
|
||||
return MailServices.headerParser.makeFullAddress(card.displayName, email);
|
||||
return MailServices.headerParser.makeMimeAddress(card.displayName, email);
|
||||
}
|
||||
|
||||
function GetDirectoryFromURI(uri)
|
||||
|
|
|
@ -144,7 +144,12 @@ function Recipients2CompFields(msgCompFields)
|
|||
case "addr_bcc" :
|
||||
case "addr_reply" :
|
||||
try {
|
||||
recipient = MailServices.headerParser.reformatUnquotedAddresses(fieldValue);
|
||||
let headerParser = MailServices.headerParser;
|
||||
recipient = [headerParser.makeMimeAddress(fullValue.name,
|
||||
fullValue.email) for
|
||||
(fullValue of
|
||||
headerParser.makeFromDisplayAddress(fieldValue, {}))]
|
||||
.join(", ");
|
||||
} catch (ex) {recipient = fieldValue;}
|
||||
break;
|
||||
}
|
||||
|
@ -289,18 +294,8 @@ function awSetInputAndPopupFromArray(inputArray, popupValue, parentNode, templat
|
|||
{
|
||||
if (popupValue)
|
||||
{
|
||||
var recipient;
|
||||
for (var index = 0; index < inputArray.length; index++)
|
||||
{
|
||||
recipient = null;
|
||||
try {
|
||||
recipient =
|
||||
MailServices.headerParser.unquotePhraseOrAddrWString(inputArray[index], true);
|
||||
} catch (ex) {};
|
||||
if (!recipient)
|
||||
recipient = inputArray[index];
|
||||
for (let recipient of inputArray)
|
||||
_awSetInputAndPopup(recipient, popupValue, parentNode, templateNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -112,19 +112,17 @@ function GetListValue(mailList, doAdd)
|
|||
cardproperty = cardproperty.QueryInterface(Components.interfaces.nsIAbCard);
|
||||
if (cardproperty)
|
||||
{
|
||||
var addresses = {};
|
||||
var names = {};
|
||||
var fullNames = {};
|
||||
var numAddresses = MailServices.headerParser.parseHeadersWithArray(fieldValue, addresses, names, fullNames);
|
||||
for (var j = 0; j < numAddresses; j++)
|
||||
let addrObjects = MailServices.headerParser
|
||||
.makeFromDisplayAddress(fieldValue, {});
|
||||
for (let j = 0; j < addrObjects.length; j++)
|
||||
{
|
||||
if (j > 0)
|
||||
{
|
||||
cardproperty = Components.classes["@mozilla.org/addressbook/cardproperty;1"].createInstance();
|
||||
cardproperty = cardproperty.QueryInterface(Components.interfaces.nsIAbCard);
|
||||
}
|
||||
cardproperty.primaryEmail = addresses.value[j];
|
||||
cardproperty.displayName = names.value[j];
|
||||
cardproperty.primaryEmail = addrObjects[j].email;
|
||||
cardproperty.displayName = addrObjects[j].name || addrObjects[j].email;
|
||||
|
||||
if (doAdd || (doAdd == false && pos >= oldTotal))
|
||||
mailList.addressLists.appendElement(cardproperty, false);
|
||||
|
@ -270,8 +268,8 @@ function OnLoadEditList()
|
|||
for (let i = 0; i < total; i++)
|
||||
{
|
||||
let card = gEditList.addressLists.queryElementAt(i, Components.interfaces.nsIAbCard);
|
||||
let address = MailServices.headerParser.makeFullAddress(card.displayName,
|
||||
card.primaryEmail);
|
||||
let address = MailServices.headerParser.makeMailboxObject(
|
||||
card.displayName, card.primaryEmail).toString();
|
||||
SetInputValue(address, newListBoxNode, templateNode);
|
||||
}
|
||||
listbox.parentNode.replaceChild(newListBoxNode, listbox);
|
||||
|
@ -535,12 +533,22 @@ function DropOnAddressListTree(event)
|
|||
|
||||
function DropListAddress(target, address)
|
||||
{
|
||||
awClickEmptySpace(target, true); //that will automatically set the focus on a new available row, and make sure is visible
|
||||
if (top.MAX_RECIPIENTS == 0)
|
||||
// Set focus on a new available, visible row.
|
||||
awClickEmptySpace(target, true);
|
||||
if (top.MAX_RECIPIENTS == 0)
|
||||
top.MAX_RECIPIENTS = 1;
|
||||
var lastInput = awGetInputElement(top.MAX_RECIPIENTS);
|
||||
lastInput.value = address;
|
||||
|
||||
// Break apart the MIME-ready header address into individual addressees to
|
||||
// add to the dialog.
|
||||
let addresses = {}, names = {}, fullNames = {};
|
||||
MailServices.headerParser.parseHeadersWithArray(address, addresses, names,
|
||||
fullNames);
|
||||
for (let full of fullNames.value)
|
||||
{
|
||||
let lastInput = awGetInputElement(top.MAX_RECIPIENTS);
|
||||
lastInput.value = full;
|
||||
awAppendNewRow(true);
|
||||
}
|
||||
}
|
||||
|
||||
/* Allows extensions to register a listener function for
|
||||
|
|
|
@ -291,16 +291,10 @@ nsAbAutoCompleteSearch.prototype = {
|
|||
_addToResult: function _addToResult(commentColumn, directory, card,
|
||||
emailToUse, isPrimaryEmail, result) {
|
||||
var emailAddress =
|
||||
this._parser.makeFullAddress(card.displayName,
|
||||
card.isMailList ?
|
||||
card.getProperty("Notes", "") || card.displayName :
|
||||
emailToUse);
|
||||
|
||||
// The old code used to try it manually. I think if the parser can't work
|
||||
// out the address from what we've supplied, then its busted and we're not
|
||||
// going to do any better doing it manually.
|
||||
if (!emailAddress)
|
||||
return;
|
||||
this._parser.makeMailboxObject(card.displayName,
|
||||
card.isMailList ?
|
||||
card.getProperty("Notes", "") || card.displayName :
|
||||
emailToUse).toString();
|
||||
|
||||
// If it is a duplicate, then just return and don't add it. The
|
||||
// _checkDuplicate function deals with it all for us.
|
||||
|
|
|
@ -112,15 +112,11 @@ nsAbLDAPAutoCompleteSearch.prototype = {
|
|||
},
|
||||
|
||||
_addToResult: function _addToResult(card) {
|
||||
var emailAddress =
|
||||
this._parser.makeFullAddress(card.displayName, card.isMailList ?
|
||||
card.getProperty("Notes", "") || card.displayName : card.primaryEmail);
|
||||
|
||||
// The old code used to try it manually. I think if the parser can't work
|
||||
// out the address from what we've supplied, then its busted and we're not
|
||||
// going to do any better doing it manually.
|
||||
if (!emailAddress)
|
||||
return;
|
||||
let emailAddress =
|
||||
this._parser.makeMailboxObject(card.displayName,
|
||||
card.isMailList ?
|
||||
card.getProperty("Notes", "") || card.displayName :
|
||||
card.primaryEmail).toString();
|
||||
|
||||
// If it is a duplicate, then just return and don't add it. The
|
||||
// _checkDuplicate function deals with it all for us.
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
#include "nsIMimeConverter.h"
|
||||
#include "nsArrayEnumerator.h"
|
||||
#include "nsMemory.h"
|
||||
#include "mozilla/mailnews/MimeHeaderParser.h"
|
||||
|
||||
using namespace mozilla::mailnews;
|
||||
|
||||
/* the following macro actually implement addref, release and query interface for our component. */
|
||||
NS_IMPL_ISUPPORTS1(nsMsgCompFields, nsIMsgCompFields)
|
||||
|
@ -540,25 +543,19 @@ nsMsgCompFields::SplitRecipients(const nsAString &aRecipients,
|
|||
|
||||
for (i = 0; i < numAddresses; ++i)
|
||||
{
|
||||
nsCString fullAddress;
|
||||
nsAutoString recipient;
|
||||
if (!aEmailAddressOnly)
|
||||
{
|
||||
nsCString decodedName;
|
||||
converter->DecodeMimeHeaderToUTF8(nsDependentCString(pNames),
|
||||
GetCharacterSet(), false, true,
|
||||
decodedName);
|
||||
rv = parser->MakeFullAddressString((!decodedName.IsEmpty() ?
|
||||
decodedName.get() : pNames),
|
||||
pAddresses,
|
||||
getter_Copies(fullAddress));
|
||||
nsString decodedName;
|
||||
converter->DecodeMimeHeader(pNames, GetCharacterSet(), false, true,
|
||||
decodedName);
|
||||
if (decodedName.IsEmpty())
|
||||
CopyUTF8toUTF16(pNames, decodedName);
|
||||
MakeDisplayAddress(decodedName, NS_ConvertUTF8toUTF16(pAddresses),
|
||||
recipient);
|
||||
}
|
||||
if (NS_SUCCEEDED(rv) && !aEmailAddressOnly)
|
||||
rv = ConvertToUnicode("UTF-8", fullAddress, recipient);
|
||||
else
|
||||
rv = ConvertToUnicode("UTF-8", nsDependentCString(pAddresses), recipient);
|
||||
if (NS_FAILED(rv))
|
||||
break;
|
||||
CopyUTF8toUTF16(pAddresses, recipient);
|
||||
|
||||
result[i] = ToNewUnicode(recipient);
|
||||
if (!result[i])
|
||||
|
@ -621,18 +618,13 @@ nsresult nsMsgCompFields::SplitRecipientsEx(const nsAString &recipients,
|
|||
converter->DecodeMimeHeaderToUTF8(nsDependentCString(pNames),
|
||||
GetCharacterSet(), false, true,
|
||||
decodedName);
|
||||
rv = parser->MakeFullAddressString((!decodedName.IsEmpty() ?
|
||||
decodedName.get() : pNames),
|
||||
pAddresses,
|
||||
getter_Copies(fullAddress));
|
||||
if (decodedName.IsEmpty())
|
||||
decodedName.Assign(pNames);
|
||||
MakeMimeAddress(decodedName, nsDependentCString(pAddresses), fullAddress);
|
||||
|
||||
nsMsgRecipient msgRecipient;
|
||||
|
||||
rv = ConvertToUnicode("UTF-8",
|
||||
NS_SUCCEEDED(rv) ? fullAddress.get() : pAddresses,
|
||||
msgRecipient.mAddress);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
CopyUTF8toUTF16(fullAddress, msgRecipient.mAddress);
|
||||
|
||||
rv = ConvertToUnicode("UTF-8", pAddresses, msgRecipient.mEmail);
|
||||
if (NS_FAILED(rv))
|
||||
|
|
|
@ -78,8 +78,11 @@
|
|||
#include "nsIAbManager.h"
|
||||
#include "nsCRT.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/mailnews/MimeHeaderParser.h"
|
||||
#include "nsISelection.h"
|
||||
|
||||
using namespace mozilla::mailnews;
|
||||
|
||||
static void GetReplyHeaderInfo(int32_t* reply_header_type,
|
||||
nsString& reply_header_locale,
|
||||
nsString& reply_header_authorwrote,
|
||||
|
@ -1048,12 +1051,7 @@ nsresult nsMsgCompose::_SendMsg(MSG_DeliverMode deliverMode, nsIMsgIdentity *ide
|
|||
identity->GetOrganization(organization);
|
||||
|
||||
nsCString sender;
|
||||
nsCOMPtr<nsIMsgHeaderParser> parser (do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID));
|
||||
if (parser) {
|
||||
// convert to UTF8 before passing to MakeFullAddressString
|
||||
parser->MakeFullAddressString(NS_ConvertUTF16toUTF8(fullName).get(),
|
||||
email.get(), getter_Copies(sender));
|
||||
}
|
||||
MakeMimeAddress(NS_ConvertUTF16toUTF8(fullName), email, sender);
|
||||
|
||||
m_compFields->SetFrom(sender.IsEmpty() ? email.get() : sender.get());
|
||||
m_compFields->SetOrganization(organization);
|
||||
|
@ -4757,9 +4755,8 @@ nsMsgCompose::CheckAndPopulateRecipients(bool aPopulateMailList,
|
|||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (parser)
|
||||
parser->MakeFullAddress(pDisplayName, newRecipient.mEmail,
|
||||
newRecipient.mAddress);
|
||||
MakeMimeAddress(pDisplayName, newRecipient.mEmail,
|
||||
newRecipient.mAddress);
|
||||
|
||||
if (newRecipient.mAddress.IsEmpty())
|
||||
{
|
||||
|
@ -5412,16 +5409,13 @@ NS_IMETHODIMP nsMsgCompose::CheckCharsetConversion(nsIMsgIdentity *identity, cha
|
|||
nsMsgMailList::nsMsgMailList(nsIAbDirectory* directory) :
|
||||
mDirectory(directory)
|
||||
{
|
||||
nsCOMPtr<nsIMsgHeaderParser> parser (do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID));
|
||||
|
||||
nsString listName, listDescription;
|
||||
mDirectory->GetDirName(listName);
|
||||
mDirectory->GetDescription(listDescription);
|
||||
|
||||
if (parser)
|
||||
parser->MakeFullAddress(listName,
|
||||
listDescription.IsEmpty() ? listName : listDescription,
|
||||
mFullName);
|
||||
MakeDisplayAddress(listName,
|
||||
listDescription.IsEmpty() ? listName : listDescription,
|
||||
mFullName);
|
||||
|
||||
if (mFullName.IsEmpty())
|
||||
{
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include "nsIMsgWindow.h"
|
||||
#include "MailNewsTypes2.h" // for nsMsgSocketType and nsMsgAuthMethod
|
||||
#include "nsIIDNService.h"
|
||||
#include "mozilla/mailnews/MimeHeaderParser.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsINetAddr.h"
|
||||
|
||||
|
@ -55,6 +56,8 @@
|
|||
|
||||
static PRLogModuleInfo *SMTPLogModule = nullptr;
|
||||
|
||||
using namespace mozilla::mailnews;
|
||||
|
||||
/* the output_buffer_size must be larger than the largest possible line
|
||||
* 2000 seems good for news
|
||||
*
|
||||
|
@ -604,17 +607,9 @@ nsresult nsSmtpProtocol::SendHeloResponse(nsIInputStream * inputStream, uint32_t
|
|||
return(NS_ERROR_COULD_NOT_GET_USERS_MAIL_ADDRESS);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIMsgHeaderParser> parser = do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID);
|
||||
nsCString fullAddress;
|
||||
if (parser)
|
||||
{
|
||||
// pass nullptr for the name, since we just want the email.
|
||||
//
|
||||
// seems a little weird that we are passing in the emailAddress
|
||||
// when that's the out parameter
|
||||
parser->MakeFullAddressString(nullptr, emailAddress.get(),
|
||||
getter_Copies(fullAddress));
|
||||
}
|
||||
// Quote the email address before passing it to the SMTP server.
|
||||
MakeMimeAddress(EmptyCString(), emailAddress, fullAddress);
|
||||
|
||||
buffer = "MAIL FROM:<";
|
||||
buffer += fullAddress;
|
||||
|
|
|
@ -18,7 +18,7 @@ const splitRecipientsTests =
|
|||
{ recipients: '"foo bar" <me@foo.invalid>',
|
||||
emailAddressOnly: false,
|
||||
count: 1,
|
||||
result: [ '"foo bar" <me@foo.invalid>' ]
|
||||
result: [ 'foo bar <me@foo.invalid>' ]
|
||||
},
|
||||
{ recipients: '"foo bar" <me@foo.invalid>',
|
||||
emailAddressOnly: true,
|
||||
|
@ -28,7 +28,7 @@ const splitRecipientsTests =
|
|||
{ recipients: '"foo bar" <me@foo.invalid>, "bar foo" <me2@foo.invalid>',
|
||||
emailAddressOnly: false,
|
||||
count: 2,
|
||||
result: [ '"foo bar" <me@foo.invalid>', '"bar foo" <me2@foo.invalid>' ]
|
||||
result: [ 'foo bar <me@foo.invalid>', 'bar foo <me2@foo.invalid>' ]
|
||||
},
|
||||
{ recipients: '"foo bar" <me@foo.invalid>, "bar foo" <me2@foo.invalid>',
|
||||
emailAddressOnly: true,
|
||||
|
@ -63,7 +63,7 @@ const splitRecipientsTests =
|
|||
{ recipients: '"A " <a@xxx.invalid>; b@xxx.invalid',
|
||||
emailAddressOnly: false,
|
||||
count: 2,
|
||||
result: [ '"A " <a@xxx.invalid>', "b@xxx.invalid" ]
|
||||
result: [ 'A <a@xxx.invalid>', "b@xxx.invalid" ]
|
||||
},
|
||||
{ recipients: 'A <a@xxx.invalid>; B <b@xxx.invalid>',
|
||||
emailAddressOnly: false,
|
||||
|
@ -73,12 +73,12 @@ const splitRecipientsTests =
|
|||
{ recipients: "A (this: is, a comment;) <a.invalid>; g: (this: is, <a> comment;) C <c.invalid>, d.invalid;",
|
||||
emailAddressOnly: false,
|
||||
count: 3,
|
||||
result: [ '"A (this: is, a comment;)" <a.invalid>', '"g: (this: is, <a> comment;) C" <c.invalid>', "d.invalid" ]
|
||||
result: [ 'A (this: is, a comment;) <a.invalid>', 'g: (this: is, <a> comment;) C <c.invalid>', "d.invalid" ]
|
||||
},
|
||||
{ recipients: 'Mary Smith <mary@x.invalid>, extra:;, group:jdoe@example.invalid; Who? <one@y.invalid>; <boss@nil.invalid>, "Giant; \"Big\" Box" <sysservices@example.invalid>, ',
|
||||
{ recipients: 'Mary Smith <mary@x.invalid>, extra:;, group:jdoe@example.invalid; Who? <one@y.invalid>; <boss@nil.invalid>, "Giant; \\"Big\\" Box" <sysservices@example.invalid>, ',
|
||||
emailAddressOnly: false,
|
||||
count: 6,
|
||||
result: [ "Mary Smith <mary@x.invalid>", "extra:;", "group:jdoe@example.invalid;", "Who? <one@y.invalid>", "boss@nil.invalid", '"Giant; \"Big\" Box" <sysservices@example.invalid>' ]
|
||||
result: [ "Mary Smith <mary@x.invalid>", "extra:;", "group:jdoe@example.invalid;", "Who? <one@y.invalid>", "boss@nil.invalid", 'Giant; \"Big\" Box <sysservices@example.invalid>' ]
|
||||
},
|
||||
{ recipients: 'Undisclosed recipients: a@foo.invalid ;;extra:;',
|
||||
emailAddressOnly: true,
|
||||
|
|
|
@ -4,14 +4,16 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "msgCore.h"
|
||||
#include "mozilla/mailnews/MimeHeaderParser.h"
|
||||
#include "nsMsgHdr.h"
|
||||
#include "nsMsgDatabase.h"
|
||||
#include "nsMsgUtils.h"
|
||||
#include "nsIMsgHeaderParser.h"
|
||||
#include "nsIMsgThread.h"
|
||||
#include "nsMsgMimeCID.h"
|
||||
#include "nsIMimeConverter.h"
|
||||
|
||||
using namespace mozilla::mailnews;
|
||||
|
||||
NS_IMPL_ISUPPORTS1(nsMsgHdr, nsIMsgDBHdr)
|
||||
|
||||
#define FLAGS_INITED 0x1
|
||||
|
@ -404,38 +406,16 @@ nsresult nsMsgHdr::BuildRecipientsFromArray(const char *names, const char *addre
|
|||
nsresult ret = NS_OK;
|
||||
const char *curName = names;
|
||||
const char *curAddress = addresses;
|
||||
nsIMsgHeaderParser *headerParser = m_mdb->GetHeaderParser();
|
||||
|
||||
for (uint32_t i = 0; i < numAddresses; i++, curName += strlen(curName) + 1, curAddress += strlen(curAddress) + 1)
|
||||
{
|
||||
if (i > 0)
|
||||
allRecipients += ", ";
|
||||
|
||||
if (headerParser)
|
||||
{
|
||||
nsCString fullAddress;
|
||||
ret = headerParser->MakeFullAddressString(curName, curAddress,
|
||||
getter_Copies(fullAddress));
|
||||
if (NS_SUCCEEDED(ret) && !fullAddress.IsEmpty())
|
||||
{
|
||||
allRecipients += fullAddress;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Just in case the parser failed...
|
||||
if (strlen(curName))
|
||||
{
|
||||
allRecipients += curName;
|
||||
allRecipients += ' ';
|
||||
}
|
||||
|
||||
if (strlen(curAddress))
|
||||
{
|
||||
allRecipients += '<';
|
||||
allRecipients += curAddress;
|
||||
allRecipients += '>';
|
||||
}
|
||||
nsCString fullAddress;
|
||||
MakeMimeAddress(nsDependentCString(curName),
|
||||
nsDependentCString(curAddress), fullAddress);
|
||||
allRecipients += fullAddress;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include "nsComposeStrings.h"
|
||||
#include "nsISmtpServer.h"
|
||||
#include "nsIPrompt.h"
|
||||
#include "nsIMsgHeaderParser.h"
|
||||
#include "nsIMsgCompUtils.h"
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsIPrefBranch.h"
|
||||
|
@ -33,6 +32,9 @@
|
|||
#include "mozilla/Services.h"
|
||||
#include "nsIArray.h"
|
||||
#include "nsArrayUtils.h"
|
||||
#include "mozilla/mailnews/MimeHeaderParser.h"
|
||||
|
||||
using namespace mozilla::mailnews;
|
||||
|
||||
#define MDN_NOT_IN_TO_CC ((int) 0x0001)
|
||||
#define MDN_OUTSIDE_DOMAIN ((int) 0x0002)
|
||||
|
@ -461,16 +463,10 @@ nsresult nsMsgMdnGenerator::CreateFirstPart()
|
|||
m_identity->GetFullName(fullName);
|
||||
|
||||
nsCString fullAddress;
|
||||
nsCOMPtr<nsIMsgHeaderParser> parser (do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID));
|
||||
if (parser)
|
||||
{
|
||||
// convert fullName to UTF8 before passing it to MakeFullAddressString
|
||||
parser->MakeFullAddressString(NS_ConvertUTF16toUTF8(fullName).get(),
|
||||
m_email.get(), getter_Copies(fullAddress));
|
||||
}
|
||||
// convert fullName to UTF8 before passing it to MakeMimeAddress
|
||||
MakeMimeAddress(NS_ConvertUTF16toUTF8(fullName), m_email, fullAddress);
|
||||
|
||||
convbuf = nsMsgI18NEncodeMimePartIIStr(
|
||||
(!fullAddress.IsEmpty()) ? fullAddress.get(): m_email.get(),
|
||||
convbuf = nsMsgI18NEncodeMimePartIIStr(fullAddress.get(),
|
||||
true, m_charset.get(), 0, conformToStandard);
|
||||
|
||||
parm = PR_smprintf("From: %s" CRLF, convbuf ? convbuf : m_email.get());
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef MimeHeaderParser_h__
|
||||
#define MimeHeaderParser_h__
|
||||
|
||||
#include "nsStringGlue.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace mailnews {
|
||||
|
||||
/**
|
||||
* Given a name and an email, both encoded in UTF-8, produce a string suitable
|
||||
* for writing in an email header by quoting where necessary.
|
||||
*
|
||||
* If name is not empty, the output string will be name <email>. If it is empty,
|
||||
* the output string is just the email. Note that this DOES NOT do any RFC 2047
|
||||
* encoding.
|
||||
*/
|
||||
void MakeMimeAddress(const nsACString &aName, const nsACString &aEmail,
|
||||
nsACString &full);
|
||||
|
||||
/**
|
||||
* Given a name and an email, produce a string suitable for writing in an email
|
||||
* header by quoting where necessary.
|
||||
*
|
||||
* If name is not empty, the output string will be name <email>. If it is empty,
|
||||
* the output string is just the email. Note that this DOES NOT do any RFC 2047
|
||||
* encoding.
|
||||
*/
|
||||
void MakeMimeAddress(const nsAString &aName, const nsAString &aEmail,
|
||||
nsAString &full);
|
||||
|
||||
/**
|
||||
* Given a name and an email, both encoded in UTF-8, produce a string suitable
|
||||
* for displaying in UI.
|
||||
*
|
||||
* If name is not empty, the output string will be name <email>. If it is empty,
|
||||
* the output string is just the email.
|
||||
*/
|
||||
void MakeDisplayAddress(const nsAString &aName, const nsAString &aEmail,
|
||||
nsAString &full);
|
||||
|
||||
} // namespace mailnews
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -25,4 +25,5 @@ EXPORTS += [
|
|||
|
||||
EXPORTS.mozilla.mailnews += [
|
||||
'MimeEncoder.h',
|
||||
'MimeHeaderParser.h',
|
||||
]
|
||||
|
|
|
@ -14,11 +14,43 @@ interface nsISimpleEnumerator;
|
|||
"@mozilla.org/messenger/headerparser;1"
|
||||
%}
|
||||
|
||||
/**
|
||||
* A structured representation of a single address.
|
||||
*
|
||||
* This is meant to be correspond to the address production from RFC 5322.
|
||||
*/
|
||||
[scriptable, uuid(15f9cc44-afdf-48c8-9de7-3ca0f18fc3b6)]
|
||||
interface msgIAddressObject : nsISupports {
|
||||
/// The name of the mailbox.
|
||||
readonly attribute AString name;
|
||||
|
||||
/// The email of the mailbox.
|
||||
readonly attribute AString email;
|
||||
|
||||
/// Return a string form of this object that is suitable for display.
|
||||
AString toString();
|
||||
};
|
||||
|
||||
/*
|
||||
* nsIMsgRFCParser Interface declaration
|
||||
*/
|
||||
[scriptable, uuid(a67d4d96-e5cb-458d-9311-794d5d1a9832)]
|
||||
[scriptable, uuid(4da84fa8-bd45-45e7-9c98-3ba2be88343f)]
|
||||
interface nsIMsgHeaderParser : nsISupports {
|
||||
/// Return a structured mailbox object having the given name and email.
|
||||
msgIAddressObject makeMailboxObject(in AString aName, in AString aEmail);
|
||||
|
||||
/**
|
||||
* Return an array of structured mailbox for the given display name string.
|
||||
*
|
||||
* The string is expected to be a comma-separated sequence of strings that
|
||||
* would be produced by msgIAddressObject::toString(). For example, the string
|
||||
* "Bond, James <agent007@mi5.invalid>" would produce one address object,
|
||||
* while the string "webmaster@nowhere.invalid, child@nowhere.invalid" would
|
||||
* produce two address objects.
|
||||
*/
|
||||
void makeFromDisplayAddress(in AString aDisplayAddresses,
|
||||
out unsigned long count,
|
||||
[retval, array, size_is(count)] out msgIAddressObject addresses);
|
||||
|
||||
void parseHeadersWithArray(in wstring aLine,
|
||||
[array, size_is(count)] out wstring aEmailAddresses,
|
||||
|
@ -108,26 +140,6 @@ interface nsIMsgHeaderParser : nsISupports {
|
|||
AUTF8String removeDuplicateAddresses(in AUTF8String aAddrs,
|
||||
in AUTF8String aOtherAddrs);
|
||||
|
||||
/**
|
||||
* Given an e-mail address and a person's name, cons them together into a
|
||||
* single string, doing all the necessary quoting.
|
||||
*
|
||||
* @param aName The name of the sender.
|
||||
* @param aAddress The address of the sender.
|
||||
* @return A string of the form name <address>.
|
||||
*/
|
||||
AString makeFullAddress(in AString aName, in AString aAddress);
|
||||
|
||||
/**
|
||||
* Given an e-mail address and a person's name, cons them together into a
|
||||
* single string, doing all the necessary quoting (const char* version).
|
||||
*
|
||||
* @param aName The name of the sender. This should be in UTF-8.
|
||||
* @param aAddress The address of the sender. This should be in UTF-8.
|
||||
* @return A string of the form name <address>.
|
||||
*/
|
||||
[noscript] string makeFullAddressString(in string aName, in string aAddress);
|
||||
|
||||
/* This function removes the quoting if you want to show the
|
||||
names to users. e.g. summary file, address book. If preserveIntegrity is set to true,
|
||||
quote will not be removed in case the name part of the email contains a comma.
|
||||
|
@ -137,5 +149,13 @@ interface nsIMsgHeaderParser : nsISupports {
|
|||
|
||||
/* Given a string, will make it safe to use by adding missing quote and escaping needed quote */
|
||||
wstring reformatUnquotedAddresses(in wstring line);
|
||||
|
||||
/**
|
||||
* Given a name and email address, produce a string that is suitable for
|
||||
* emitting in a MIME header (after applying RFC 2047 encoding).
|
||||
*
|
||||
* @note This is a temporary method.
|
||||
*/
|
||||
AString makeMimeAddress(in AString aName, in AString aEmail);
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 "mozilla/mailnews/MimeHeaderParser.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIMsgHeaderParser.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace mailnews {
|
||||
|
||||
void MakeMimeAddress(const nsACString &aName, const nsACString &aEmail,
|
||||
nsACString &full)
|
||||
{
|
||||
nsAutoString utf16Address;
|
||||
MakeMimeAddress(NS_ConvertUTF8toUTF16(aName), NS_ConvertUTF8toUTF16(aEmail),
|
||||
utf16Address);
|
||||
|
||||
CopyUTF16toUTF8(utf16Address, full);
|
||||
}
|
||||
|
||||
void MakeMimeAddress(const nsAString &aName, const nsAString &aEmail,
|
||||
nsAString &full)
|
||||
{
|
||||
nsCOMPtr<nsIMsgHeaderParser> headerParser =
|
||||
do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID);
|
||||
|
||||
headerParser->MakeMimeAddress(aName, aEmail, full);
|
||||
}
|
||||
|
||||
void MakeDisplayAddress(const nsAString &aName, const nsAString &aEmail,
|
||||
nsAString &full)
|
||||
{
|
||||
nsCOMPtr<nsIMsgHeaderParser> headerParser =
|
||||
do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID);
|
||||
|
||||
nsCOMPtr<msgIAddressObject> object;
|
||||
headerParser->MakeMailboxObject(aName, aEmail, getter_AddRefs(object));
|
||||
object->ToString(full);
|
||||
}
|
||||
|
||||
} // namespace mailnews
|
||||
} // namespace mozilla
|
|
@ -36,6 +36,7 @@ SOURCES += [
|
|||
'mimeenc.cpp',
|
||||
'mimeeobj.cpp',
|
||||
'mimehdrs.cpp',
|
||||
'MimeHeaderParser.cpp',
|
||||
'mimei.cpp',
|
||||
'mimeiimg.cpp',
|
||||
'mimeleaf.cpp',
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "prmem.h"
|
||||
#include <ctype.h>
|
||||
#include "nsAlgorithm.h"
|
||||
#include "nsMsgUtils.h"
|
||||
#include "nsStringGlue.h"
|
||||
#include <algorithm>
|
||||
|
||||
|
@ -69,7 +70,6 @@ nsresult FillResultsArray(const char * aName, const char *aAddress, PRUnichar **
|
|||
PRUnichar ** aOutgoingFullName, nsIMsgHeaderParser *aParser)
|
||||
{
|
||||
NS_ENSURE_ARG(aParser);
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
*aOutgoingFullName = nullptr;
|
||||
*aOutgoingEmailAddress = nullptr;
|
||||
|
@ -90,9 +90,8 @@ nsresult FillResultsArray(const char * aName, const char *aAddress, PRUnichar **
|
|||
|
||||
nsCString fullAddress;
|
||||
nsCString unquotedAddress;
|
||||
rv = aParser->MakeFullAddressString(aName, aAddress,
|
||||
getter_Copies(fullAddress));
|
||||
if (NS_SUCCEEDED(rv) && !fullAddress.IsEmpty())
|
||||
fullAddress.Adopt(msg_make_full_address(aName, aAddress));
|
||||
if (!fullAddress.IsEmpty())
|
||||
{
|
||||
MIME_DecodeMimeHeader(fullAddress.get(), nullptr, false, true, result);
|
||||
if (!result.IsEmpty())
|
||||
|
@ -105,7 +104,7 @@ nsresult FillResultsArray(const char * aName, const char *aAddress, PRUnichar **
|
|||
else
|
||||
*aOutgoingFullName = nullptr;
|
||||
|
||||
return rv;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgHeaderParser::ParseHeadersWithArray(const PRUnichar * aLine, PRUnichar *** aEmailAddresses,
|
||||
|
@ -195,16 +194,7 @@ nsMsgHeaderParser::RemoveDuplicateAddresses(const nsACString &aAddrs,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMsgHeaderParser::MakeFullAddressString(const char *aName,
|
||||
const char *aAddress, char **aResult)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aResult);
|
||||
*aResult = msg_make_full_address(aName, aAddress);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMsgHeaderParser::MakeFullAddress(const nsAString &aName,
|
||||
nsMsgHeaderParser::MakeMimeAddress(const nsAString &aName,
|
||||
const nsAString &aAddress, nsAString &aResult)
|
||||
{
|
||||
nsCString utf8Str;
|
||||
|
@ -1563,3 +1553,117 @@ msg_make_full_address(const char* name, const char* addr)
|
|||
buf = (char *)PR_Realloc (buf, L);
|
||||
return buf;
|
||||
}
|
||||
|
||||
class MsgAddressObject MOZ_FINAL : public msgIAddressObject
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_MSGIADDRESSOBJECT
|
||||
|
||||
MsgAddressObject(const nsAString &aName, const nsAString &aEmail);
|
||||
|
||||
private:
|
||||
nsString mName;
|
||||
nsString mEmail;
|
||||
};
|
||||
|
||||
MsgAddressObject::MsgAddressObject(const nsAString &aName,
|
||||
const nsAString &aEmail)
|
||||
: mName(aName),
|
||||
mEmail(aEmail)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS1(MsgAddressObject, msgIAddressObject)
|
||||
|
||||
NS_IMETHODIMP MsgAddressObject::GetName(nsAString &name)
|
||||
{
|
||||
name = mName;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP MsgAddressObject::GetEmail(nsAString &email)
|
||||
{
|
||||
email = mEmail;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP MsgAddressObject::ToString(nsAString &display)
|
||||
{
|
||||
nsMsgHeaderParser headerParser;
|
||||
nsAutoString quotedString;
|
||||
headerParser.MakeMimeAddress(mName, mEmail, quotedString);
|
||||
headerParser.UnquotePhraseOrAddrWString(quotedString.get(), false,
|
||||
getter_Copies(display));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgHeaderParser::MakeMailboxObject(const nsAString &aName,
|
||||
const nsAString &aEmail, msgIAddressObject **retval)
|
||||
{
|
||||
nsCOMPtr<msgIAddressObject> object = new MsgAddressObject(aName, aEmail);
|
||||
object.forget(retval);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static MsgAddressObject *MakeSingleAddress(
|
||||
const nsAString &aDisplay)
|
||||
{
|
||||
// This is a wasteful copy, but the internal API does not have RFindChar on
|
||||
// nsAString, only nsString.
|
||||
nsString display(aDisplay);
|
||||
// Strip leading/trailing whitespace
|
||||
MsgCompressWhitespace(display);
|
||||
nsCOMPtr<msgIAddressObject> object;
|
||||
int32_t addrstart = display.RFindChar('<');
|
||||
if (addrstart != -1)
|
||||
{
|
||||
// Adjust is used to strip off exactly one space char if it's present.
|
||||
int32_t adjust = addrstart == 0 ? 0 : 1;
|
||||
int32_t addrend = display.RFindChar('>');
|
||||
return new MsgAddressObject(
|
||||
StringHead(display, addrstart - adjust),
|
||||
Substring(display, addrstart + 1, addrend - addrstart - 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
return new MsgAddressObject(EmptyString(), display);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsMsgHeaderParser::MakeFromDisplayAddress(
|
||||
const nsAString &aDisplay, uint32_t *count, msgIAddressObject ***retval)
|
||||
{
|
||||
// We split on every comma, so long as a @ exists before that comma.
|
||||
nsCOMArray<msgIAddressObject> addresses;
|
||||
int32_t lastComma = -1;
|
||||
while (!aDisplay.IsEmpty() && lastComma < (int32_t)aDisplay.Length())
|
||||
{
|
||||
// Find the next , that follows an email address (which must have an @).
|
||||
int32_t atSign = aDisplay.FindChar('@', lastComma + 1);
|
||||
// If there is no @, just consume the rest of the string as the "address"
|
||||
if (atSign == -1)
|
||||
atSign = aDisplay.Length() - 1;
|
||||
int32_t nextComma = aDisplay.FindChar(',', atSign + 1);
|
||||
if (nextComma == -1)
|
||||
nextComma = aDisplay.Length();
|
||||
|
||||
// The substring from [lastComma + 1, nextComma) is an email address.
|
||||
addresses.AppendElement(MakeSingleAddress(
|
||||
Substring(aDisplay, lastComma + 1, nextComma - (lastComma + 1))));
|
||||
|
||||
// Move lastComma along
|
||||
lastComma = nextComma;
|
||||
}
|
||||
|
||||
// Add all the elements to the output
|
||||
msgIAddressObject **out = (msgIAddressObject **)NS_Alloc(
|
||||
sizeof(msgIAddressObject*) * addresses.Length());
|
||||
for (uint32_t i = 0; i < addresses.Length(); i++)
|
||||
NS_IF_ADDREF(out[i] = addresses[i]);
|
||||
|
||||
*count = addresses.Length();
|
||||
*retval = out;
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "nsIMsgHeaderParser.h" /* include the interface we are going to support */
|
||||
#include "nsIMimeConverter.h"
|
||||
#include "comi18n.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
/*
|
||||
|
|
|
@ -24,11 +24,11 @@ function run_test() {
|
|||
|
||||
// Test - empty strings
|
||||
|
||||
do_check_eq(MailServices.headerParser.makeFullAddress("", ""), "");
|
||||
do_check_eq(MailServices.headerParser.makeMimeAddress("", ""), "");
|
||||
|
||||
// Test - makeFullAddressWString
|
||||
// Test - makeMimeAddress
|
||||
|
||||
for (let i = 0; i < checks.length; ++i)
|
||||
do_check_eq(MailServices.headerParser.makeFullAddress(checks[i][0], checks[i][1]),
|
||||
do_check_eq(MailServices.headerParser.makeMimeAddress(checks[i][0], checks[i][1]),
|
||||
checks[i][2]);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/*
|
||||
* Test suite for nsIMsgHeaderParser::makeFromDisplayAddress
|
||||
*/
|
||||
|
||||
Components.utils.import("resource:///modules/mailServices.js");
|
||||
|
||||
function run_test() {
|
||||
const checks =
|
||||
[
|
||||
{ displayString: "",
|
||||
addresses: [] },
|
||||
{ displayString: "test@foo.invalid",
|
||||
addresses: [["", "test@foo.invalid"]] },
|
||||
{ displayString: "test@foo.invalid, test2@foo.invalid",
|
||||
addresses: [["", "test@foo.invalid"],
|
||||
["", "test2@foo.invalid"]] },
|
||||
{ displayString: "John Doe <test@foo.invalid>",
|
||||
addresses: [["John Doe", "test@foo.invalid"]] },
|
||||
{ displayString: "Doe, John <test@foo.invalid>",
|
||||
addresses: [["Doe, John", "test@foo.invalid"]] },
|
||||
{ displayString: "Doe, John <test@foo.invalid>, Bond, James <test2@foo.invalid>",
|
||||
addresses: [["Doe, John", "test@foo.invalid"],
|
||||
["Bond, James", "test2@foo.invalid"]] },
|
||||
];
|
||||
|
||||
// Test - strings
|
||||
|
||||
for (let i = 0; i < checks.length; ++i) {
|
||||
dump("Test " + i + "\n");
|
||||
let addrs = MailServices.headerParser.makeFromDisplayAddress(checks[i].displayString, {});
|
||||
let checkaddrs = checks[i].addresses;
|
||||
do_check_eq(addrs.length, checkaddrs.length);
|
||||
for (let j = 0; j < addrs.length; j++) {
|
||||
do_check_eq(addrs[j].name, checkaddrs[j][0]);
|
||||
do_check_eq(addrs[j].email, checkaddrs[j][1]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,18 +3,19 @@ head = head_mime.js
|
|||
tail = tail_mime.js
|
||||
|
||||
[test_EncodeMimePartIIStr_UTF8.js]
|
||||
[test_hidden_attachments.js]
|
||||
[test_alternate_p7m_handling.js]
|
||||
[test_attachment_size.js]
|
||||
[test_badContentType.js]
|
||||
[test_bug493544.js]
|
||||
[test_hidden_attachments.js]
|
||||
[test_message_attachment.js]
|
||||
[test_mimeContentType.js]
|
||||
[test_mimeStreaming.js]
|
||||
[test_nsIMsgHeaderParser1.js]
|
||||
[test_nsIMsgHeaderParser2.js]
|
||||
[test_nsIMsgHeaderParser3.js]
|
||||
[test_parser.js]
|
||||
[test_text_attachment.js]
|
||||
[test_message_attachment.js]
|
||||
[test_nsIMsgHeaderParser4.js]
|
||||
[test_parseHeadersWithArray.js]
|
||||
[test_parser.js]
|
||||
[test_rfc822_body.js]
|
||||
[test_bug493544.js]
|
||||
[test_alternate_p7m_handling.js]
|
||||
[test_text_attachment.js]
|
||||
|
|
|
@ -570,7 +570,7 @@ function GenerateAddressFromCard(card)
|
|||
}
|
||||
else
|
||||
email = card.primaryEmail;
|
||||
return MailServices.headerParser.makeFullAddress(card.displayName, email);
|
||||
return MailServices.headerParser.makeMimeAddress(card.displayName, email);
|
||||
}
|
||||
|
||||
function GetDirectoryFromURI(uri)
|
||||
|
|
|
@ -114,7 +114,8 @@ function Recipients2CompFields(msgCompFields)
|
|||
case "addr_bcc" :
|
||||
case "addr_reply" :
|
||||
try {
|
||||
recipient = gMimeHeaderParser.reformatUnquotedAddresses(fieldValue);
|
||||
let headerParser = MailServices.headerParser;
|
||||
recipient = [headerParser.makeMimeAddress(fullValue.name, fullValue.email) for (fullValue of headerParser.makeFromDisplayAddress(fieldValue, {}))].join(", ");
|
||||
} catch (ex) {recipient = fieldValue;}
|
||||
break;
|
||||
}
|
||||
|
@ -152,8 +153,6 @@ function Recipients2CompFields(msgCompFields)
|
|||
function CompFields2Recipients(msgCompFields)
|
||||
{
|
||||
if (msgCompFields) {
|
||||
gMimeHeaderParser = Components.classes["@mozilla.org/messenger/headerparser;1"].getService(Components.interfaces.nsIMsgHeaderParser);
|
||||
|
||||
var listbox = document.getElementById('addressingWidget');
|
||||
var newListBoxNode = listbox.cloneNode(false);
|
||||
var listBoxColsClone = listbox.firstChild.cloneNode(true);
|
||||
|
@ -207,8 +206,6 @@ function CompFields2Recipients(msgCompFields)
|
|||
// CompFields2Recipients is called whenever a user replies or edits an existing message.
|
||||
// We want to add all of the recipients for this message to the ignore list for spell check
|
||||
addRecipientsToIgnoreList((gCurrentIdentity ? gCurrentIdentity.identityName + ', ' : '') + msgTo + ', ' + msgCC + ', ' + msgBCC);
|
||||
|
||||
gMimeHeaderParser = null; //Release the mime parser
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -265,19 +262,8 @@ function awSetInputAndPopupFromArray(inputArray, popupValue, parentNode, templat
|
|||
{
|
||||
if (popupValue)
|
||||
{
|
||||
var recipient;
|
||||
for (var index = 0; index < inputArray.length; index++)
|
||||
{
|
||||
recipient = null;
|
||||
if (gMimeHeaderParser)
|
||||
try {
|
||||
recipient =
|
||||
gMimeHeaderParser.unquotePhraseOrAddrWString(inputArray[index], true);
|
||||
} catch (ex) {};
|
||||
if (!recipient)
|
||||
recipient = inputArray[index];
|
||||
for (let recipient of inputArray)
|
||||
_awSetInputAndPopup(recipient, popupValue, parentNode, templateNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче