質問

The Amazon SES documentation is not clear for me. So if you can help me with an example tot send raw text e-mails including a PDF attachment, this will help me a lot. I use Python 2.5 and Google App engine. I have to use "raw", because that is the only SES option if I include attachments.

My problems at the moment :

  • What is the content of the post request.
  • Which message fields should I put in the header.
  • How to handle the "returnPath".
  • How to handle the text body. It should be : Content-Type: text/plain; charset=UTF-8; format=flowed; delsp=yes. Content-Transfer-Encoding: base64
  • How do I construct the HMAC signature for this post. I know how to make the signature, but how does the raw string looks for the signature function.
役に立ちましたか?

解決

Example code here to send SES raw e-mail using a multipart body (plain / html / attachment) :

from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from google.appengine.api import urlfetch
from google.appengine.runtime import DeadlineExceededError
import urllib
import hmac
import base64
import hashlib
from datetime import datetime
import logging


def ses_multi_part(msg_subject, msg_to, msg_body='', msg_cc=None, msg_bcc=None, file_name=None, file_reader=None,
                      msg_type='plain', msg_from='noreply@....', msg_reply_to='contact@....'):
    """ send an html or plain e-mail. Use file_name and file_reader to pass an attachment
        inspiration: https://codeadict.wordpress.com/2010/02/11/send-e-mails-with-attachment-in-python/
    """

    msg = MIMEMultipart()
    msg.set_charset("utf-8")

    msg['Subject'] = msg_subject
    msg['From'] = msg_from
    msg['Reply-to'] = msg_reply_to
    msg['To'] = msg_to
    if msg_cc:
        msg['Cc'] = msg_cc
    if msg_bcc:
        msg['Bcc'] = msg_bcc
    logging.debug(msg)

    msg.preamble = 'Multipart massage.\n'

    part = MIMEText(msg_body, msg_type, "utf-8")
    msg.attach(part)

    if file_name:
        part = MIMEApplication(file_reader.read())
        part.add_header('Content-Disposition', 'attachment', filename=file_name)
        msg.attach(part)

    return msg.as_string()


class SES(object):
    """ SES send RAW e-mail
        inspiration: https://github.com/richieforeman/python-amazon-ses-api/blob/master/amazon_ses.py
    """

    def __init__(self, accessKeyID, secretAccessKey, return_path='contact@....'):

        self._accessKeyID = accessKeyID
        self._secretAccessKey = secretAccessKey
        self.ses_return_path = return_path

    def _getSignature(self, dateValue):

        h = hmac.new(key=self._secretAccessKey, msg=dateValue, digestmod=hashlib.sha256)
        return base64.b64encode(h.digest()).decode()

    def _getHeaders(self):

        headers = {'Content-type': 'application/x-www-form-urlencoded', 'Return-Path': self.ses_return_path}
        d = datetime.utcnow()
        dateValue = d.strftime('%a, %d %b %Y %H:%M:%S GMT')
        headers['Date'] = dateValue
        signature = self._getSignature(dateValue)
        headers['X-Amzn-Authorization'] = 'AWS3-HTTPS AWSAccessKeyId=%s, Algorithm=HMACSHA256, Signature=%s' % (self._accessKeyID, signature)
        return headers

    def _performAction(self, actionName, params=None):

        if not params:
            params = {}
        params['Action'] = actionName

        response = None
        #https://email.us-east-1.amazonaws.com/

        retry = 0  # download error retry
        while retry <= 1:  # dan een eenmalige retry
            try:
                url = 'https://email.us-east-1.amazonaws.com'
                response = urlfetch.fetch(url=url, payload=urllib.urlencode(params), method=urlfetch.POST, headers=self._getHeaders())
                break
            except (urlfetch.DownloadError, DeadlineExceededError), e:
                logging.debug('Amazon SES download or deadline error : %d' % (retry + 1))
                if retry == 0:
                    retry += 1
                    continue  # retry
                else:
                    logging.warning('fetcherror' + str(e))
                    raise  # bij een dubbele fout stoppen

        if response.status_code != 200:
            logging.warning(response.headers)
            logging.warning(response.content)
            raise ValueError('status_code : %s' % (str(response.status_code)))

        logging.debug(response.content)
        return response.content

    def sendRawEmail(self, raw_msg_data):

        return self._performAction("SendRawEmail", params={"RawMessage.Data": base64.b64encode(raw_msg_data)})

Example usage:

ses = SES(settings.AMAZON_ACCESS_KEY_ID, settings.AMAZON_SECRET_ACCESS_KEY, settings.SES_RETURN_PATH[country])
raw_msg_data = ses_multi_part(msg_subject=subject.encode('utf-8'), msg_to=mail_to, msg_body=body_text.encode('utf-8'),
                                 msg_bcc=settings.MAIL_BCC, msg_reply_to=reply_to, msg_from=sender, msg_type=msg_type)
ses.sendRawEmail(raw_msg_data)

他のヒント

The procedure I am using is outlined in this gist. It mostly comes from following the example given here.

Why use raw with their API when you can send attachments and everything via their standard SMTP interface? Besides, last time I checked raw does not support BCC.

SMTP Interface: http://docs.amazonwebservices.com/ses/latest/DeveloperGuide/SMTP.html

List of approved MIME types (otherwise SES gets angry): http://docs.amazonwebservices.com/ses/latest/DeveloperGuide/MIMETypes.html

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top