Commit Graph

21 Commits

Author SHA1 Message Date
medmunds
03dd15d485 Cleanup: add reprs for Attachment, EmailAddress 2020-09-11 14:26:55 -07:00
medmunds
be7f2516b0 Add Attachment.content_type 2020-09-11 11:10:24 -07:00
medmunds
5b04ae30f2 Ensure EmailAddress can't contain newlines 2020-09-11 11:10:24 -07:00
Mike Edmunds
85cec5e9dc Drop Python 2 and Django 1.11 support
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)
2020-08-01 14:53:10 -07:00
medmunds
5dce0895f1 Fix UnicodeEncodeError error while reporting invalid email address.
Fixes #148.
2019-05-19 11:19:21 -07:00
medmunds
ebb6b041e7 Support using AnymailMessage with django-mailer
Make UNSET preserve its identity across pickling. (Also make its repr
mention "unset", to simplify interpretting stack traces.)

Fixes #147.
2019-05-08 13:16:01 -07:00
medmunds
c5c015e9a1 Internal: add CaseInsensitiveCasePreservingDict
Like CaseInsensitiveDict (which we borrow from Requests), but preserves
case of the first key set rather than the last.
2019-02-05 11:01:55 -08:00
Leo Antunes
64f7d31d14 also consider Content-ID when marking attachment as inline (#126)
Handle MIME attachments with Content-ID as inline by default.

Treat MIME attachments that have a *Content-ID* but no explicit *Content-Disposition*
header as inline, matching the behavior of many email clients.
2018-10-11 14:29:00 -07:00
medmunds
dd26fd3108 Don't require boto3 if Amazon SES webhooks aren't actually used
Delay raising AnymailImproperlyInstalled from webhooks.amazon_ses
until an SES webhook view is instantiated. Allows anymail.urls
to import webhooks.amazon_ses without error.

Fixes #103
2018-04-16 15:41:00 -07:00
medmunds
07fbeac6bd Feature: Add envelope_sender
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.
2018-02-26 18:42:19 -08:00
medmunds
bb68f3dd6d Mailgun: fix event/metadata param extraction in tracking webhook
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.
2017-10-27 13:26:37 -07:00
medmunds
3866689084 Utils: convert internal ParsedEmail to documented EmailAddress
Update internal-use ParsedEmail to be more like Python 3.6+
email.headerregistry.Address, and remove "internal use only"
recommendation.

(Prep for exposing inbound email headers in a convenient form.
Old names remain temporarily available for internal use;
should clean up at some point.)
2017-10-25 19:50:31 -07:00
medmunds
fe097ce4b4 Utils: add parse_rfc2822date
(Useful for interpreting date header in inbound messages)
2017-10-25 19:50:31 -07:00
medmunds
68de868ebf Tests: prep for string_concat deprecation in Django 2.1
Test either or both of the lazy string builders `string_concat`
and `format_lazy`, depending on which is/are available in the
Django being tested.
2017-10-11 15:34:11 -07:00
medmunds
6b6793016e Mailgun, SparkPost: support multiple from_email addresses
[RFC-5322 allows](https://tools.ietf.org/html/rfc5322#section-3.6.2)
multiple addresses in the From header.

Django's SMTP backend supports this, as a single comma-separated
string (*not* a list of strings like the recipient params):

    from_email='one@example.com, two@example.com'
    to=['one@example.com', 'two@example.com']

Both Mailgun and SparkPost support multiple From addresses
(and Postmark accepts them, though truncates to the first one
on their end). For compatibility with Django -- and because
Anymail attempts to support all ESP features -- Anymail now
allows multiple From addresses, too, for ESPs that support it.

Note: as a practical matter, deliverability with multiple
From addresses is pretty bad. (Google outright rejects them.)

This change also reworks Anymail's internal ParsedEmail object,
and approach to parsing addresses, for better consistency with
Django's SMTP backend and improved error messaging.

In particular, Django (and now Anymail) allows multiple email
addresses in a single recipient string:

    to=['one@example.com', 'two@example.com, three@example.com']
    len(to) == 2  # but there will be three recipients

Fixes #60
2017-04-19 12:43:33 -07:00
medmunds
8bdc67939a Flake8 clean 2017-04-16 11:43:13 -07:00
medmunds
bff01b440a Fix webhook tests for Django 1.11-alpha
Django 1.11 appears to enforce ALLOWED_HOSTS
(more strictly) during tests.
2017-01-19 19:22:16 -08:00
medmunds
0ba5d1d4ad Mandrill: include auth in webhook signature calc
Mandrill's webhook signature calculation uses the
*exact url* Mandrill is posting to. If HTTP basic
auth is also used, that auth is included in the url.

Anymail was using Django's request.build_absolute_uri,
which doesn't include HTTP basic auth. Anymail now
includes the auth in the calculation, if it was present
in the request.

This should eliminate the need to use the
ANYMAIL_MANDRILL_WEBHOOK_URL override,
if Django's SECURE_PROXY_SSL_HEADER and
USE_X_FORWARDED_HOST (and/or
USE_X_FORWARDED_PROTO) settings are correct
for your server.

(The calculated url is now also included in
the validation failure error message, to aid
debugging.)

Fixes #48
2017-01-19 19:01:36 -08:00
Mike Edmunds
e568e50d0c SendGrid: update to new v3 send API (#50)
SendGrid: update to v3 send API

**SendGrid:** **[possibly-breaking]** Update SendGrid backend to newer Web API v3. This should be a transparent change for most projects. Exceptions: if you use SendGrid username/password auth, esp_extra with "x-smtpapi", or multiple Reply-To addresses, please review the [porting notes](http://anymail.readthedocs.io/en/latest/esps/sendgrid/#sendgrid-v3-upgrade).

Closes #28
2017-01-19 14:29:15 -08:00
Mike Edmunds
edf2a3ddcf Handle Django lazy strings.
In BasePayload, ensure any Django ugettext_lazy
(or similar) are converted to real strings before
handing off to ESP code. This resolves problems where
calling code expects it can use lazy strings "anywhere",
but non-Django code (requests, ESP packages) don't
always handle them correctly.

* Add utils helpers for lazy objects (is_lazy, force_non_lazy*)
* Add lazy object handling to utils.Attachment
* Add lazy object handling converters to BasePayload attr
  processing where appropriate. (This ends up varying by
  the expected attribute type.)

Fixes #34.
2016-12-30 15:48:08 -05:00
medmunds
d0596d100b Raise error for invalidly-formatted email addresses.
A message's `from_email` and each address in its `to`, `cc`, and `bcc` lists must contain exactly one email address. Previous code would silently ignore additional addresses, leading to unusual behavior. Now, raises new `AnymailInvalidAddress` exception.

Example: `from_email='Widgets, Inc. <widgets@example.com>'` is invalid: it needs double-quotes around the "Widgets, Inc." display-name portion. In earlier versions, this probably would have sent the message from something like "From: Widgets <@localhost>". Now, it will raise an exception.

**Potentially-breaking change:** If your code is using an unquoted display-name containing a comma in an email address, it will now raise an error. In earlier versions, this may have appeared to succeed, but was almost certainly not doing what you intended.

Fixes #44.
2016-12-15 13:57:49 -08:00