For ESPs that have incorporated new owners
in their branding, identify the new owner
in the docs:
- Mailgun -> Sinch Mailgun
- Postmark -> ActiveCampaign Postmark
- SendGrid -> Twilio SendGrid
- SparkPost -> Bird ???
(Bird's rebranding of SparkPost seems to still
be a work in progress at this point. In fact,
Bird's rebranding of itself from MessageBird
seems incomplete.)
There are no current plans to rename ESP
backends, as the new owners seem to be mostly
keeping the original ESP names and domains for
API endpoints, docs, etc.
(Similarly, I'm not updating project keywords.)
Workaround for Django multipart/form-data limitation
where certain attachment filenames cause fields to be dropped
or to end up in request.POST rather than request.FILES.
Handle the MultiValueDictKeyError in inbound webhooks when
this has occurred. Also update docs to recommend avoiding
the problem by using Mailgun and SendGrid's "raw MIME" options.
Also handle reported cases of empty, duplicate keys in Mailgun's
content-id-map.
Fixes#272
Minimum supported versions are now Django 2.0, Python 3.5.
This touches a lot of code, to:
* Remove obsolete portability code and workarounds
(six, backports of email parsers, test utils, etc.)
* Use Python 3 syntax (class defs, raise ... from, etc.)
* Correct inheritance for mixin classes
* Fix outdated docs content and links
* Suppress Python 3 "unclosed SSLSocket" ResourceWarnings
that are beyond our control (in integration tests due to boto3,
python-sparkpost)
Mailgun has two different template mechanisms and two different ways
of providing substitution variables to them. Update Anymail's
normalized merge_data handling to work with either (while preserving
existing batch send and metadata capabilities that also use Mailgun's
custom data and recipient variables parameters).
Completes work started by @anstosa in #156.
Closes#155.
Support merge_metadata in Mailgun, Mailjet, Mandrill, Postmark,
SparkPost, and Test backends. (SendGrid covered in earlier PR.)
Also:
* Add `merge_metadata` to AnymailMessage, AnymailMessageMixin
* Add `is_batch()` logic to BasePayload, for consistent handling
* Docs
Note: Mailjet implementation switches *all* batch sending from their
"Recipients" field to to the "Messages" array bulk sending option.
This allows an independent payload for each batch recipient.
In addition to supporting merge_metadata, this also removes the
prior limitation on mixing Cc/Bcc with merge_data.
Closes#141.
Mailgun's API silently drops attachments without filenames (and inline
attachments without Content-IDs). Raise an AnymailUnsupportedFeature
error on attempts to send these attachments.
Fixes#128
New EmailMessage attribute `envelope_sender` controls ESP's sender,
sending domain, or return path where supported:
* Mailgun: overrides SENDER_DOMAIN on individual message
(domain portion only)
* Mailjet: becomes `Sender` API param
* Mandrill: becomes `return_path_domain` API param
(domain portion only)
* SparkPost: becomes `return_path` API param
* Other ESPs: not believed to be supported
Also support undocumented Django SMTP backend behavior, where envelope
sender is given by `message.from_email` when
`message.extra_headers["From"]` is set. Fixes#91.
This fixes a low severity security issue affecting Anymail v0.2--v1.3.
Django error reporting includes the value of your Anymail
WEBHOOK_AUTHORIZATION setting. In a properly-configured deployment,
this should not be cause for concern. But if you have somehow exposed
your Django error reports (e.g., by mis-deploying with DEBUG=True or by
sending error reports through insecure channels), anyone who gains
access to those reports could discover your webhook shared secret. An
attacker could use this to post fabricated or malicious Anymail
tracking/inbound events to your app, if you are using those Anymail
features.
The fix renames Anymail's webhook shared secret setting so that
Django's error reporting mechanism will [sanitize][0] it.
If you are using Anymail's event tracking and/or inbound webhooks, you
should upgrade to this release and change "WEBHOOK_AUTHORIZATION" to
"WEBHOOK_SECRET" in the ANYMAIL section of your settings.py. You may
also want to [rotate the shared secret][1] value, particularly if you
have ever exposed your Django error reports to untrusted individuals.
If you are only using Anymail's EmailBackends for sending email and
have not set up Anymail's webhooks, this issue does not affect you.
The old WEBHOOK_AUTHORIZATION setting is still allowed in this release,
but will issue a system-check warning when running most Django
management commands. It will be removed completely in a near-future
release, as a breaking change.
Thanks to Charlie DeTar (@yourcelf) for responsibly reporting this
security issue through private channels.
[0]: https://docs.djangoproject.com/en/stable/ref/settings/#debug
[1]: https://anymail.readthedocs.io/en/1.4/tips/securing_webhooks/#use-a-shared-authorization-secret
Mailgun merges user-variables (metadata) into the webhook post data
interspersed with the actual event params. This can lead to ambiguity
interpreting post data.
To extract metadata from an event, Anymail had been attempting to avoid
that ambiguity by instead using X-Mailgun-Variables fields found in the
event's message-headers param. But message-headers isn't included in
some tracking events (opened, clicked, unsubscribed), resulting in
empty metadata for those events. (#76)
Also, conflicting metadata keys could confuse Anymail's Mailgun event
parsing, leading to unexpected values in the normalized event. (#77)
This commit:
* Cleans up Anymail's tracking webhook to be explicit about which
multi-value params it uses, avoiding conflicts with metadata keys.
Fixes#77.
* Extracts metadata from post params for opened, clicked and
unsubscribed events. All unknown event params are assumed to be
metadata. Fixes#76.
* Documents a few metadata key names where it's impossible (or likely
to be unreliable) for Anymail to extract metadata from the post data.
For reference, the order of params in the Mailgun's post data *appears*
to be (from live testing):
* For the timestamp, token and signature params, any user-variable with
the same name appears *before* the corresponding event data.
* For all other params, any user-variable with the same name as a
Mailgun event param appears *after* the Mailgun data.
* **Future breaking change:**
Rename all Anymail backends to just `EmailBackend`,
matching Django's naming convention.
(E.g., switch to "anymail.backends.mailgun.EmailBackend"
rather than "anymail.backends.mailgun.MailgunBackend".)
The old names still work, but will issue a DeprecationWarning
and will be removed in some future release.
(Apologies for this change; the old naming convention was
a holdover from Djrill, and I wanted consistency with
other Django EmailBackends before hitting 1.0.)
Fixes#49.
Allow custom MAILGUN_SENDER_DOMAIN in Anymail
settings. (Replaces need to use global esp_extra.)
Improve docs to cover cases where this is needed.
(esp_extra sender_domain is still supported for
overriding individual messages.)
Fixes#26.
* message.template_id to use ESP stored templates
* message.merge_data and merge_global_data
to supply per-recipient/global merge variables
(with or without an ESP stored template)
* When using per-recipient merge_data, tell ESP to use
batch send: individual message per "to" address.
(Mailgun does this automatically; SendGrid requires
using a different "to" field; Mandrill requires
`preserve_recipients=False`; Postmark doesn't
support *this type* of batch sending with merge data.)
* Allow message.from_email=None (must be set after
init) and message.subject=None to suppress those
fields in API calls (for ESPs that allow "From" and
"Subject" in their template definitions).
Mailgun:
* Emulate merge_global_data by copying to
recipient-variables for each recipient.
SendGrid:
* Add delimiters to merge field names via
esp_extra['merge_field_format'] or
ANYMAIL_SENDGRID_MERGE_FIELD_FORMAT setting.
Mandrill:
* Remove Djrill versions of these features;
update migration notes.
Closes#5.