Skip to content

Commit

Permalink
split code into functions, add documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
smcmurtry committed Sep 26, 2023
1 parent 309be02 commit a1be9a9
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 24 deletions.
8 changes: 1 addition & 7 deletions app/clients/email/aws_ses.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from time import monotonic
import base64

import boto3
import botocore
from flask import current_app
from notifications_utils.recipients import InvalidEmailError
from notifications_utils.statsd_decorators import statsd
# from unidecode import unidecode

from app.clients.email import EmailClient, EmailClientException

Expand Down Expand Up @@ -64,8 +62,6 @@ def attach_html(m, content):
attachments = attachments or []
if isinstance(to_addresses, str):
to_addresses = [to_addresses]
# TODO: fix this to allow accents
# source = base64.b64encode(bytes(source, "utf-8"))

reply_to_addresses = [reply_to_address] if reply_to_address else []

Expand Down Expand Up @@ -104,10 +100,8 @@ def attach_html(m, content):
msg.attach(attachment_part)

start_time = monotonic()
response = self._client.send_raw_email(RawMessage={"Data": msg.as_string()})
response = self._client.send_raw_email(Source=source, RawMessage={"Data": msg.as_string()})
except botocore.exceptions.ClientError as e:
# got an error here using a service with accents in the name

self.statsd_client.incr("clients.ses.error")

# http://docs.aws.amazon.com/ses/latest/DeveloperGuide/api-error-codes.html
Expand Down
44 changes: 27 additions & 17 deletions app/delivery/send_to_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from datetime import datetime
from typing import Dict
from uuid import UUID
from unidecode import unidecode

from flask import current_app
from notifications_utils.recipients import (
Expand All @@ -17,6 +16,7 @@
PlainTextEmailTemplate,
SMSMessageTemplate,
)
from unidecode import unidecode

from app import bounce_rate_client, clients, document_download_client, statsd_client
from app.celery.research_mode_tasks import send_email_response, send_sms_response
Expand Down Expand Up @@ -187,6 +187,28 @@ def check_service_over_bounce_rate(service_id: str):
)


def mime_encoded_word_syntax(charset="utf-8", encoding="B", encoded_text="") -> str:
"""MIME encoded-word syntax is a way to encode non-ASCII characters in email headers.
It is described here:
https://docs.aws.amazon.com/ses/latest/dg/send-email-raw.html#send-email-mime-encoding-headers
"""
return f"=?{charset}?{encoding}?{encoded_text}?="


def get_from_address(friendly_from: str, email_from: str, sending_domain: str) -> str:
"""
This function returns the from_address or source in MIME encoded-word syntax
friendly_from is the sender's display name and may contain accents so we need to encode it to base64
email_from and sending_domain should be ASCII only
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ses/client/send_raw_email.html
"If you want to use Unicode characters in the “friendly from” name, you must encode the “friendly from”
name using MIME encoded-word syntax, as described in Sending raw email using the Amazon SES API."
"""
friendly_from_b64 = base64.b64encode(friendly_from.encode()).decode("utf-8")
friendly_from_mime = mime_encoded_word_syntax(charset="utf-8", encoding="B", encoded_text=friendly_from_b64)
return f'"{friendly_from_mime}" <{unidecode(email_from)}@{unidecode(sending_domain)}>'


def send_email_to_provider(notification: Notification):
current_app.logger.info(f"Sending email to provider for notification id {notification.id}")
service = notification.service
Expand Down Expand Up @@ -268,22 +290,10 @@ def send_email_to_provider(notification: Notification):
sending_domain = current_app.config["NOTIFY_EMAIL_DOMAIN"]
else:
sending_domain = service.sending_domain

# do not use unidecode for the service name as it may contain accents
# https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ses/client/send_raw_email.html
# Amazon SES does not support the SMTPUTF8 extension, as described in RFC6531. For this reason,
# the email address string must be 7-bit ASCII. If you want to send to or from email addresses that contain
# Unicode characters in the domain part of an address, you must encode the domain using Punycode.
# Punycode is not permitted in the local part of the email address (the part before the @ sign) nor in the “friendly from” name.
# If you want to use Unicode characters in the “friendly from” name, you must encode the “friendly from” name using
# MIME encoded-word syntax, as described in Sending raw email using the Amazon SES API. For more information about Punycode,
# see RFC 3492.

service_name = service.name
x = base64.b64encode(service_name.encode()).decode("utf-8")
friendly_from = f"=?utf-8?B?{x}?="
from_address = f'"{friendly_from}" <{unidecode(service.email_from)}@{unidecode(sending_domain)}>'


from_address = get_from_address(
friendly_from=service.name, email_from=service.email_from, sending_domain=sending_domain
)
email_reply_to = notification.reply_to_text

reference = provider.send_email(
Expand Down

0 comments on commit a1be9a9

Please sign in to comment.