[REF] mail: remove deprecated email formataddr
[PEP 594] is going to depreciate the legacy `email.message.Message` API and its related modules. Among them the `email.utils.formataddr` function, this function takes a `(name, email)` pair and returns a string value suitable for From, To and Cc headers. The stdlib function is capable of handling several character encoding and two binary-to-ascii encoding. Odoo only uses `utf-8` which is compatible with the base64 b2a encoding. The re-implementation of that function has been simplified to only support base64-ed utf-8 and ascii. [PEP 594]: https://www.python.org/dev/peps/pep-0594/\#email-lagacy-api
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import _, api, fields, models, tools
|
||||
from email.utils import formataddr
|
||||
|
||||
|
||||
class MailCCMixin(models.AbstractModel):
|
||||
@@ -16,7 +15,7 @@ class MailCCMixin(models.AbstractModel):
|
||||
'''return a dict of sanitize_email:raw_email from a string of cc'''
|
||||
if not cc_string:
|
||||
return {}
|
||||
return {tools.email_normalize(email): formataddr((name, tools.email_normalize(email)))
|
||||
return {tools.email_normalize(email): tools.formataddr((name, tools.email_normalize(email)))
|
||||
for (name, email) in tools.email_split_tuples(cc_string)}
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -5,13 +5,12 @@ import base64
|
||||
import logging
|
||||
import re
|
||||
|
||||
from email.utils import formataddr
|
||||
from uuid import uuid4
|
||||
|
||||
from odoo import _, api, fields, models, modules, tools
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.osv import expression
|
||||
from odoo.tools import ormcache
|
||||
from odoo.tools import ormcache, formataddr
|
||||
|
||||
MODERATION_FIELDS = ['moderation', 'moderator_ids', 'moderation_ids', 'moderation_notify', 'moderation_notify_msg', 'moderation_guidelines', 'moderation_guidelines_msg']
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -10,7 +10,6 @@ import threading
|
||||
import re
|
||||
|
||||
from collections import defaultdict
|
||||
from email.utils import formataddr
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo import tools
|
||||
@@ -195,7 +194,7 @@ class MailMail(models.Model):
|
||||
body = self._send_prepare_body()
|
||||
body_alternative = tools.html2plaintext(body)
|
||||
if partner:
|
||||
email_to = [formataddr((partner.name or 'False', partner.email or 'False'))]
|
||||
email_to = [tools.formataddr((partner.name or 'False', partner.email or 'False'))]
|
||||
else:
|
||||
email_to = tools.email_split_and_format(self.email_to)
|
||||
res = {
|
||||
|
||||
@@ -7,7 +7,6 @@ import re
|
||||
from binascii import Error as binascii_error
|
||||
from collections import defaultdict
|
||||
from operator import itemgetter
|
||||
from email.utils import formataddr
|
||||
from openerp.http import request
|
||||
|
||||
from odoo import _, api, fields, models, modules, tools
|
||||
@@ -32,7 +31,7 @@ class Message(models.Model):
|
||||
@api.model
|
||||
def _get_default_from(self):
|
||||
if self.env.user.email:
|
||||
return formataddr((self.env.user.name, self.env.user.email))
|
||||
return tools.formataddr((self.env.user.name, self.env.user.email))
|
||||
raise UserError(_("Unable to post message, please configure the sender's email address."))
|
||||
|
||||
@api.model
|
||||
@@ -1179,7 +1178,7 @@ class Message(models.Model):
|
||||
if not msg.email_from:
|
||||
continue
|
||||
if self.env.user.partner_id.email:
|
||||
email_from = formataddr((self.env.user.partner_id.name, self.env.user.partner_id.email))
|
||||
email_from = tools.formataddr((self.env.user.partner_id.name, self.env.user.partner_id.email))
|
||||
else:
|
||||
email_from = self.env.company.catchall
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ except ImportError:
|
||||
|
||||
from collections import namedtuple
|
||||
from email.message import EmailMessage
|
||||
from email.utils import formataddr
|
||||
from lxml import etree
|
||||
from werkzeug import url_encode
|
||||
from werkzeug import urls
|
||||
@@ -2633,7 +2632,7 @@ class MailThread(models.AbstractModel):
|
||||
company_name = company.name if company else self.env.company.name
|
||||
for res_id in result_email.keys():
|
||||
name = '%s%s%s' % (company_name, ' ' if doc_names.get(res_id) else '', doc_names.get(res_id, ''))
|
||||
result[res_id] = formataddr((name, result_email[res_id]))
|
||||
result[res_id] = tools.formataddr((name, result_email[res_id]))
|
||||
|
||||
left_ids = set(_res_ids) - set(result_email)
|
||||
if left_ids:
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from email.utils import formataddr
|
||||
|
||||
import base64
|
||||
import re
|
||||
|
||||
@@ -68,7 +66,7 @@ class MailComposer(models.TransientModel):
|
||||
elif 'email_from' not in result:
|
||||
author = self.env['res.partner'].browse(result['author_id'])
|
||||
if author.email:
|
||||
result['email_from'] = formataddr((author.name, author.email))
|
||||
result['email_from'] = tools.formataddr((author.name, author.email))
|
||||
|
||||
# v6.1 compatibility mode
|
||||
result['composition_mode'] = result.get('composition_mode', self._context.get('mail.compose.message.mode', 'comment'))
|
||||
|
||||
@@ -143,7 +143,7 @@ class TestSurveyInvite(common.SurveyCase):
|
||||
self.assertEqual(len(answers), 3)
|
||||
self.assertEqual(
|
||||
set(answers.mapped('email')),
|
||||
set(['test1@example.com', 'Raoulette Vignolette <test2@example.com>', self.customer.email]))
|
||||
set(['test1@example.com', '"Raoulette Vignolette" <test2@example.com>', self.customer.email]))
|
||||
self.assertEqual(answers.mapped('partner_id'), self.customer)
|
||||
|
||||
@users('survey_manager')
|
||||
@@ -164,7 +164,7 @@ class TestSurveyInvite(common.SurveyCase):
|
||||
self.assertEqual(len(answers), 3)
|
||||
self.assertEqual(
|
||||
set(answers.mapped('email')),
|
||||
set(['test1@example.com', 'Raoulette Vignolette <test2@example.com>', self.customer.email]))
|
||||
set(['test1@example.com', '"Raoulette Vignolette" <test2@example.com>', self.customer.email]))
|
||||
self.assertEqual(answers.mapped('partner_id'), self.customer)
|
||||
|
||||
@users('survey_manager')
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
import logging
|
||||
import re
|
||||
|
||||
from email.utils import formataddr
|
||||
|
||||
from odoo import api, fields, models, tools, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
@@ -21,7 +19,7 @@ class SurveyInvite(models.TransientModel):
|
||||
@api.model
|
||||
def _get_default_from(self):
|
||||
if self.env.user.email:
|
||||
return formataddr((self.env.user.name, self.env.user.email))
|
||||
return tools.formataddr((self.env.user.name, self.env.user.email))
|
||||
raise UserError(_("Unable to post message, please configure the sender's email address."))
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -4,12 +4,12 @@ import json
|
||||
import time
|
||||
|
||||
from contextlib import contextmanager
|
||||
from email.utils import formataddr
|
||||
from functools import partial
|
||||
|
||||
from odoo import api
|
||||
from odoo.addons.bus.models.bus import json_dump
|
||||
from odoo.tests import common, tagged, new_test_user
|
||||
from odoo.tools import formataddr
|
||||
|
||||
mail_new_test_user = partial(new_test_user, context={'mail_create_nolog': True, 'mail_create_nosubscribe': True, 'mail_notrack': True, 'no_reset_password': True})
|
||||
|
||||
@@ -327,7 +327,7 @@ class MockEmails(common.SingleTransactionCase):
|
||||
self._mails[:] = []
|
||||
|
||||
def format(self, template, to='groups@example.com, other@gmail.com', subject='Frogs',
|
||||
extra='', email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>',
|
||||
extra='', email_from='"Sylvie Lelitre" <test.sylvie.lelitre@agrolait.com>',
|
||||
cc='', msg_id='<1198923581.41972151344608186760.JavaMail@agrolait.com>'):
|
||||
return template.format(to=to, subject=subject, cc=cc, extra=extra, email_from=email_from, msg_id=msg_id)
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from email.utils import formataddr
|
||||
|
||||
from odoo.tests import tagged
|
||||
from odoo.addons.test_mail.tests import common
|
||||
from odoo.addons.test_mail.tests.common import mail_new_test_user
|
||||
from odoo.exceptions import AccessError, except_orm, ValidationError, UserError
|
||||
from odoo.tools import mute_logger
|
||||
from odoo.tools import mute_logger, formataddr
|
||||
|
||||
|
||||
class TestChannelAccessRights(common.BaseFunctionalTest, common.MockEmails):
|
||||
|
||||
@@ -5,7 +5,6 @@ import email
|
||||
import email.policy
|
||||
import socket
|
||||
|
||||
from email.utils import formataddr
|
||||
from unittest.mock import DEFAULT
|
||||
from unittest.mock import patch
|
||||
|
||||
@@ -16,7 +15,11 @@ from odoo.addons.test_mail.models.test_mail_models import MailTestGateway
|
||||
from odoo.addons.test_mail.tests.common import BaseFunctionalTest, MockEmails
|
||||
from odoo.addons.test_mail.tests.common import mail_new_test_user
|
||||
from odoo.tests import tagged
|
||||
<<<<<<< HEAD
|
||||
from odoo.tools import email_split_and_format, mute_logger, pycompat
|
||||
=======
|
||||
from odoo.tools import mute_logger, pycompat, formataddr
|
||||
>>>>>>> [REF] mail: remove deprecated email formataddr
|
||||
|
||||
def from_string(text):
|
||||
return email.message_from_string(pycompat.to_text(text), policy=email.policy.SMTP)
|
||||
@@ -27,12 +30,12 @@ class TestEmailParsing(BaseFunctionalTest, MockEmails):
|
||||
|
||||
def test_message_parse_body(self):
|
||||
# test pure plaintext
|
||||
plaintext = self.format(test_mail_data.MAIL_TEMPLATE_PLAINTEXT, email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>')
|
||||
plaintext = self.format(test_mail_data.MAIL_TEMPLATE_PLAINTEXT, email_from='"Sylvie Lelitre" <test.sylvie.lelitre@agrolait.com>')
|
||||
res = self.env['mail.thread'].message_parse(from_string(plaintext))
|
||||
self.assertIn('Please call me as soon as possible this afternoon!', res['body'])
|
||||
|
||||
# test multipart / text and html -> html has priority
|
||||
multipart = self.format(MAIL_TEMPLATE, email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>')
|
||||
multipart = self.format(MAIL_TEMPLATE, email_from='"Sylvie Lelitre" <test.sylvie.lelitre@agrolait.com>')
|
||||
res = self.env['mail.thread'].message_parse(from_string(multipart))
|
||||
self.assertIn('<p>Please call me as soon as possible this afternoon!</p>', res['body'])
|
||||
|
||||
@@ -59,12 +62,12 @@ class TestEmailParsing(BaseFunctionalTest, MockEmails):
|
||||
|
||||
def test_message_parse_eml(self):
|
||||
# Test that the parsing of mail with embedded emails as eml(msg) which generates empty attachments, can be processed.
|
||||
mail = self.format(test_mail_data.MAIL_EML_ATTACHMENT, email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>', to='generic@test.com')
|
||||
mail = self.format(test_mail_data.MAIL_EML_ATTACHMENT, email_from='"Sylvie Lelitre" <test.sylvie.lelitre@agrolait.com>', to='generic@test.com')
|
||||
self.env['mail.thread'].message_parse(from_string(mail))
|
||||
|
||||
def test_message_parse_plaintext(self):
|
||||
""" Incoming email in plaintext should be stored as html """
|
||||
mail = self.format(test_mail_data.MAIL_TEMPLATE_PLAINTEXT, email_from='Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>', to='generic@test.com')
|
||||
mail = self.format(test_mail_data.MAIL_TEMPLATE_PLAINTEXT, email_from='"Sylvie Lelitre" <test.sylvie.lelitre@agrolait.com>', to='generic@test.com')
|
||||
res = self.env['mail.thread'].message_parse(from_string(mail))
|
||||
self.assertIn('<pre>\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n</pre>', res['body'])
|
||||
|
||||
@@ -88,7 +91,7 @@ class TestMailgateway(BaseFunctionalTest, MockEmails):
|
||||
def setUpClass(cls):
|
||||
super(TestMailgateway, cls).setUpClass()
|
||||
cls.test_model = cls.env['ir.model']._get('mail.test.gateway')
|
||||
cls.email_from = 'Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>'
|
||||
cls.email_from = '"Sylvie Lelitre" <test.sylvie.lelitre@agrolait.com>'
|
||||
|
||||
cls.test_record = cls.env['mail.test.gateway'].with_context(cls._test_context).create({
|
||||
'name': 'Test',
|
||||
@@ -190,7 +193,7 @@ class TestMailgateway(BaseFunctionalTest, MockEmails):
|
||||
self.assertEqual(len(self._mails), 0, 'No notification / bounce should be sent')
|
||||
|
||||
# Email recognized if partner has a formatted email
|
||||
self.partner_1.write({'email': 'Valid Lelitre <%s>' % self.partner_1.email})
|
||||
self.partner_1.write({'email': '"Valid Lelitre" <%s>' % self.partner_1.email})
|
||||
record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email, 'groups@test.com', subject='Test2')
|
||||
|
||||
self.assertEqual(record.message_ids[0].author_id, self.partner_1,
|
||||
@@ -269,7 +272,7 @@ class TestMailgateway(BaseFunctionalTest, MockEmails):
|
||||
# Test bounce email
|
||||
self.assertEqual(self._mails[0].get('subject'), 'Re: Should Bounce')
|
||||
self.assertEqual(self._mails[0].get('email_to')[0], 'whatever-2a840@postmaster.twitter.com')
|
||||
self.assertEqual(self._mails[0].get('email_from'), formataddr(('MAILER-DAEMON', 'bounce.test@test.com')))
|
||||
self.assertEqual(self._mails[0].get('email_from'), 'MAILER-DAEMON <bounce.test@test.com>')
|
||||
|
||||
@mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_message_process_alias_followers_bounce(self):
|
||||
@@ -287,7 +290,7 @@ class TestMailgateway(BaseFunctionalTest, MockEmails):
|
||||
'message_process: incoming email on Followers alias should send a bounce email')
|
||||
self.assertEqual(self._mails[0].get('subject'), 'Re: Should Bounce')
|
||||
self.assertEqual(self._mails[0].get('email_to')[0], 'whatever-2a840@postmaster.twitter.com')
|
||||
self.assertEqual(self._mails[0].get('email_from'), formataddr(('MAILER-DAEMON', 'bounce.test@test.com')))
|
||||
self.assertEqual(self._mails[0].get('email_from'), 'MAILER-DAEMON <bounce.test@test.com>')
|
||||
|
||||
# Test: partner on followers alias -> bounce
|
||||
self._init_mock_build_email()
|
||||
@@ -297,7 +300,7 @@ class TestMailgateway(BaseFunctionalTest, MockEmails):
|
||||
'message_process: incoming email on Followers alias should send a bounce email')
|
||||
self.assertEqual(self._mails[0].get('subject'), 'Re: Should Bounce')
|
||||
self.assertEqual(self._mails[0].get('email_to')[0], 'whatever-2a840@postmaster.twitter.com')
|
||||
self.assertEqual(self._mails[0].get('email_from'), formataddr(('MAILER-DAEMON', 'bounce.test@test.com')))
|
||||
self.assertEqual(self._mails[0].get('email_from'), 'MAILER-DAEMON <bounce.test@test.com>')
|
||||
|
||||
@mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models')
|
||||
def test_message_process_alias_partner(self):
|
||||
@@ -421,7 +424,7 @@ class TestMailgateway(BaseFunctionalTest, MockEmails):
|
||||
# Test bounce email
|
||||
self.assertEqual(self._mails[0].get('subject'), 'Re: Should Bounce')
|
||||
self.assertEqual(self._mails[0].get('email_to')[0], 'whatever-2a840@postmaster.twitter.com')
|
||||
self.assertEqual(self._mails[0].get('email_from'), formataddr(('MAILER-DAEMON', 'bounce.test@test.com')))
|
||||
self.assertEqual(self._mails[0].get('email_from'), 'MAILER-DAEMON <bounce.test@test.com>')
|
||||
|
||||
@mute_logger('odoo.addons.mail.models.mail_thread')
|
||||
def test_message_process_bounce_alias(self):
|
||||
@@ -782,7 +785,7 @@ class TestMailThreadCC(BaseFunctionalTest, MockEmails):
|
||||
self.format_and_process(MAIL_TEMPLATE, self.email_from, 'cc_record@test.com',
|
||||
cc='cc2 <cc2@example.com>, cc3@example.com', target_model='mail.test.cc')
|
||||
cc = email_split_and_format(record.email_cc)
|
||||
self.assertEqual(sorted(cc), ['cc1 <cc1@example.com>', 'cc2@example.com', 'cc3@example.com'], 'new cc should have been added on record (unique)')
|
||||
self.assertEqual(sorted(cc), ['"cc1" <cc1@example.com>', 'cc2@example.com', 'cc3@example.com'], 'new cc should have been added on record (unique)')
|
||||
|
||||
@mute_logger('odoo.addons.mail.models.mail_thread')
|
||||
def test_message_cc_update_no_old(self):
|
||||
@@ -792,4 +795,4 @@ class TestMailThreadCC(BaseFunctionalTest, MockEmails):
|
||||
self.format_and_process(MAIL_TEMPLATE, self.email_from, 'cc_record@test.com',
|
||||
cc='cc2 <cc2@example.com>, cc3@example.com', target_model='mail.test.cc')
|
||||
cc = email_split_and_format(record.email_cc)
|
||||
self.assertEqual(sorted(cc), ['cc2 <cc2@example.com>', 'cc3@example.com'], 'new cc should have been added on record (unique)')
|
||||
self.assertEqual(sorted(cc), ['"cc2" <cc2@example.com>', 'cc3@example.com'], 'new cc should have been added on record (unique)')
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import base64
|
||||
from email.utils import formataddr
|
||||
|
||||
from odoo.addons.test_mail.tests import common
|
||||
from odoo.addons.test_mail.tests.common import mail_new_test_user
|
||||
from odoo.exceptions import AccessError, except_orm
|
||||
from odoo.tools import mute_logger
|
||||
from odoo.tools import mute_logger, formataddr
|
||||
from odoo.tests import tagged
|
||||
|
||||
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
|
||||
import base64
|
||||
|
||||
from email.utils import formataddr
|
||||
from unittest.mock import patch
|
||||
|
||||
from odoo.addons.test_mail.tests.common import BaseFunctionalTest, MockEmails, TestRecipients
|
||||
from odoo.addons.test_mail.data.test_mail_data import MAIL_TEMPLATE_PLAINTEXT
|
||||
from odoo.addons.test_mail.models.test_mail_models import MailTestSimple
|
||||
from odoo.exceptions import AccessError
|
||||
from odoo.tools import mute_logger
|
||||
from odoo.tools import mute_logger, formataddr
|
||||
|
||||
|
||||
class TestMessagePost(BaseFunctionalTest, TestRecipients, MockEmails):
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from email.utils import formataddr
|
||||
from unittest.mock import patch
|
||||
|
||||
from odoo.addons.test_mail.tests import common
|
||||
from odoo.tools import formataddr
|
||||
|
||||
|
||||
class TestTracking(common.BaseFunctionalTest, common.MockEmails):
|
||||
|
||||
@@ -2,11 +2,9 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import base64
|
||||
|
||||
from email.utils import formataddr
|
||||
|
||||
from odoo.tests.common import TransactionCase, users, warmup
|
||||
from odoo.tests import tagged
|
||||
from odoo.tools import mute_logger
|
||||
from odoo.tools import mute_logger, formataddr
|
||||
|
||||
|
||||
@tagged('mail_performance')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from email.utils import formataddr
|
||||
from odoo.addons.test_mail.tests import common
|
||||
from odoo.tools import formataddr
|
||||
|
||||
|
||||
class TestChannelPartnersNotification(common.MockEmails):
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
import logging
|
||||
import re
|
||||
|
||||
from email.utils import formataddr
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import formataddr
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -9,8 +9,6 @@ import pytz
|
||||
import threading
|
||||
import re
|
||||
|
||||
from email.utils import formataddr
|
||||
|
||||
import requests
|
||||
from lxml import etree
|
||||
from werkzeug import urls
|
||||
@@ -355,7 +353,7 @@ class Partner(models.Model):
|
||||
def _compute_email_formatted(self):
|
||||
for partner in self:
|
||||
if partner.email:
|
||||
partner.email_formatted = formataddr((partner.name or u"False", partner.email or u"False"))
|
||||
partner.email_formatted = tools.formataddr((partner.name or u"False", partner.email or u"False"))
|
||||
else:
|
||||
partner.email_formatted = ''
|
||||
|
||||
|
||||
+39
-1
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import base64
|
||||
import collections
|
||||
import logging
|
||||
from lxml.html import clean
|
||||
@@ -11,7 +12,7 @@ import threading
|
||||
import time
|
||||
|
||||
from email.header import decode_header, Header
|
||||
from email.utils import getaddresses, formataddr
|
||||
from email.utils import getaddresses
|
||||
from lxml import etree
|
||||
|
||||
import odoo
|
||||
@@ -438,6 +439,8 @@ single_email_re = re.compile(r"""^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6
|
||||
|
||||
mail_header_msgid_re = re.compile('<[^<>]+>')
|
||||
|
||||
email_addr_escapes_re = re.compile(r'[\\"]')
|
||||
|
||||
|
||||
def generate_tracking_message_id(res_id):
|
||||
"""Returns a string that can be used in the Message-ID RFC822 header field
|
||||
@@ -561,3 +564,38 @@ def decode_smtp_header(smtp_header):
|
||||
# was mail_thread.decode_header()
|
||||
def decode_message_header(message, header, separator=' '):
|
||||
return separator.join(decode_smtp_header(h) for h in message.get_all(header, []) if h)
|
||||
|
||||
def formataddr(pair):
|
||||
"""Takes a 2-tuple of the form (realname, email_address) and returns
|
||||
the string value suitable for an RFC 2822 From, To or Cc header.
|
||||
|
||||
The email address is considered valid and is left unmodified.
|
||||
|
||||
If the first element of pair is falsy then only the email address
|
||||
is returned.
|
||||
|
||||
>>> formataddr(('John Doe', 'johndoe@example.com'))
|
||||
'"John Doe" <johndoe@example.com>'
|
||||
|
||||
>>> formataddr(('', 'johndoe@example.com'))
|
||||
'johndoe@example.com'
|
||||
"""
|
||||
name, address = pair
|
||||
address.encode('ascii')
|
||||
if name:
|
||||
try:
|
||||
name.encode('ascii')
|
||||
except UnicodeEncodeError:
|
||||
# non-ascii name, transcode the name in a safe format
|
||||
# rfc2047 - MIME Message Header Extensions for Non-ASCII Text
|
||||
return "=?utf-8?b?{name}?= {addr}".format(
|
||||
name=base64.b64encode(name.encode('utf-8')).decode('ascii'),
|
||||
addr=address)
|
||||
else:
|
||||
# ascii name, escape it if needed
|
||||
# rfc2822 - Internet Message Format
|
||||
# #section-3.4 - Address Specification
|
||||
return '"{name}" <{addr}>'.format(
|
||||
name=email_addr_escapes_re.sub(r'\\\g<0>', name),
|
||||
addr=address)
|
||||
return address
|
||||
|
||||
Reference in New Issue
Block a user