mirror of
https://github.com/pacnpal/django-anymail.git
synced 2025-12-20 11:51:05 -05:00
Properly encode path components used to construct API URLs
Resolves #144 and similar potential issues
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from email.utils import encode_rfc2231
|
from email.utils import encode_rfc2231
|
||||||
from six.moves.urllib.parse import quote_plus
|
from six.moves.urllib.parse import quote
|
||||||
|
|
||||||
from requests import Request
|
from requests import Request
|
||||||
|
|
||||||
@@ -82,12 +82,12 @@ class MailgunPayload(RequestsPayload):
|
|||||||
"Either provide valid `from_email`, "
|
"Either provide valid `from_email`, "
|
||||||
"or set `message.esp_extra={'sender_domain': 'example.com'}`",
|
"or set `message.esp_extra={'sender_domain': 'example.com'}`",
|
||||||
backend=self.backend, email_message=self.message, payload=self)
|
backend=self.backend, email_message=self.message, payload=self)
|
||||||
if '/' in self.sender_domain or '%' in self.sender_domain:
|
if '/' in self.sender_domain or '%2f' in self.sender_domain.lower():
|
||||||
# Mailgun returns a cryptic 200-OK "Mailgun Magnificent API" response
|
# Mailgun returns a cryptic 200-OK "Mailgun Magnificent API" response
|
||||||
# if '/' (or even %-encoded '/') confuses it about the API endpoint.
|
# if '/' (or even %-encoded '/') confuses it about the API endpoint.
|
||||||
raise AnymailError("Invalid sender domain '%s'" % self.sender_domain,
|
raise AnymailError("Invalid '/' in sender domain '%s'" % self.sender_domain,
|
||||||
backend=self.backend, email_message=self.message, payload=self)
|
backend=self.backend, email_message=self.message, payload=self)
|
||||||
return "%s/messages" % quote_plus(self.sender_domain)
|
return "%s/messages" % quote(self.sender_domain, safe='')
|
||||||
|
|
||||||
def get_request_params(self, api_url):
|
def get_request_params(self, api_url):
|
||||||
params = super(MailgunPayload, self).get_request_params(api_url)
|
params = super(MailgunPayload, self).get_request_params(api_url)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
from six.moves.urllib.parse import quote
|
||||||
|
|
||||||
from ..exceptions import AnymailRequestsAPIError
|
from ..exceptions import AnymailRequestsAPIError
|
||||||
from ..message import AnymailRecipientStatus, ANYMAIL_STATUSES
|
from ..message import AnymailRecipientStatus, ANYMAIL_STATUSES
|
||||||
from ..utils import get_anymail_setting, EmailAddress, parse_address_list
|
from ..utils import get_anymail_setting, EmailAddress, parse_address_list
|
||||||
@@ -131,7 +133,7 @@ class MailjetPayload(RequestsPayload):
|
|||||||
template_id = self.data.get("Mj-TemplateID")
|
template_id = self.data.get("Mj-TemplateID")
|
||||||
if template_id and not self.data.get("FromEmail"):
|
if template_id and not self.data.get("FromEmail"):
|
||||||
response = self.backend.session.get(
|
response = self.backend.session.get(
|
||||||
"%sREST/template/%s/detailcontent" % (self.backend.api_url, template_id),
|
"%sREST/template/%s/detailcontent" % (self.backend.api_url, quote(str(template_id), safe='')),
|
||||||
auth=self.auth, timeout=self.backend.timeout
|
auth=self.auth, timeout=self.backend.timeout
|
||||||
)
|
)
|
||||||
self.backend.raise_for_status(response, None, self.message)
|
self.backend.raise_for_status(response, None, self.message)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from requests.structures import CaseInsensitiveDict
|
from requests.structures import CaseInsensitiveDict
|
||||||
|
from six.moves.urllib.parse import quote
|
||||||
|
|
||||||
from .base_requests import AnymailRequestsBackend, RequestsPayload
|
from .base_requests import AnymailRequestsBackend, RequestsPayload
|
||||||
from ..exceptions import AnymailRequestsAPIError
|
from ..exceptions import AnymailRequestsAPIError
|
||||||
@@ -76,7 +77,7 @@ class SendinBluePayload(RequestsPayload):
|
|||||||
|
|
||||||
def get_api_endpoint(self):
|
def get_api_endpoint(self):
|
||||||
if self.template_id:
|
if self.template_id:
|
||||||
return "smtp/templates/%s/send" % self.template_id
|
return "smtp/templates/%s/send" % quote(str(self.template_id), safe='')
|
||||||
else:
|
else:
|
||||||
return "smtp/email"
|
return "smtp/email"
|
||||||
|
|
||||||
|
|||||||
@@ -565,7 +565,7 @@ class MailgunBackendAnymailFeatureTests(MailgunBackendMockAPITestCase):
|
|||||||
# (which returns a cryptic 200-OK "Mailgun Magnificent API" response).
|
# (which returns a cryptic 200-OK "Mailgun Magnificent API" response).
|
||||||
self.message.from_email = "<from@example.com/invalid>"
|
self.message.from_email = "<from@example.com/invalid>"
|
||||||
with self.assertRaisesMessage(AnymailError,
|
with self.assertRaisesMessage(AnymailError,
|
||||||
"Invalid sender domain 'example.com/invalid'"):
|
"Invalid '/' in sender domain 'example.com/invalid'"):
|
||||||
self.message.send()
|
self.message.send()
|
||||||
|
|
||||||
@override_settings(ANYMAIL_MAILGUN_SENDER_DOMAIN='example.com%2Finvalid')
|
@override_settings(ANYMAIL_MAILGUN_SENDER_DOMAIN='example.com%2Finvalid')
|
||||||
@@ -573,9 +573,16 @@ class MailgunBackendAnymailFeatureTests(MailgunBackendMockAPITestCase):
|
|||||||
# See previous test. Also, note that Mailgun unquotes % encoding *before*
|
# See previous test. Also, note that Mailgun unquotes % encoding *before*
|
||||||
# extracting the sender domain (so %2f is just as bad as '/')
|
# extracting the sender domain (so %2f is just as bad as '/')
|
||||||
with self.assertRaisesMessage(AnymailError,
|
with self.assertRaisesMessage(AnymailError,
|
||||||
"Invalid sender domain 'example.com%2Finvalid'"):
|
"Invalid '/' in sender domain 'example.com%2Finvalid'"):
|
||||||
self.message.send()
|
self.message.send()
|
||||||
|
|
||||||
|
@override_settings(ANYMAIL_MAILGUN_SENDER_DOMAIN='example.com # oops')
|
||||||
|
def test_encode_sender_domain(self):
|
||||||
|
# See previous tests. For anything other than slashes, we let Mailgun detect
|
||||||
|
# the problem (but must properly encode the domain in the API URL)
|
||||||
|
self.message.send()
|
||||||
|
self.assert_esp_called('/example.com%20%23%20oops/messages')
|
||||||
|
|
||||||
def test_default_omits_options(self):
|
def test_default_omits_options(self):
|
||||||
"""Make sure by default we don't send any ESP-specific options.
|
"""Make sure by default we don't send any ESP-specific options.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user