예제 #1
0
def send_mail(msg):
    global access_key
    global secret_key

    client = SESConnection(
        aws_access_key_id=access_key, aws_secret_access_key=secret_key)
    senders = msg["From"].split(",")
    recipients = msg["To"].split(',')
    for sender in senders:
        client.send_raw_email(msg.as_string(), sender, recipients)
    client.close()
예제 #2
0
class AWSSESSender:
    def __init__(self, aws_access_key, aws_secret_key):
        try:
            self.ses_conn = SESConnection(aws_access_key_id=aws_access_key,
                                          aws_secret_access_key=aws_secret_key)
        except:
            print "SES Connection was failed !!"
            exit()

    def send_email(self, sender, subject, body, recipient_list):
        if self.ses_conn != None:
            self.ses_conn.send_email(sender, subject, body, recipient_list)
        else:
            print "Connection object is null!!"

    def send_raw_email(self, sender, subject, body, recipient_list,
                       attachment_path):
        msg = MIMEMultipart()
        msg['Subject'] = subject
        msg['From'] = sender

        to_list = ""

        for idx, data in enumerate(recipient_list):
            if idx < len(recipient_list) - 1:
                to_list += data + ","
            else:
                to_list += data

        msg['To'] = to_list
        msg.premable = 'Multipart Message.\n'

        # Assembling Mail body
        part = MIMEText(body)
        msg.attach(part)

        # Assembling Attachment
        part = MIMEApplication(open(attachment_path, 'rb').read())
        part.add_header('Content-Disposition',
                        'attachment',
                        filename=os.path.basename(attachment_path))
        msg.attach(part)

        # Send RAW email
        if self.ses_conn != None:
            self.ses_conn.send_raw_email(msg.as_string(),
                                         source=msg['From'],
                                         destinations=recipient_list)
        else:
            print "Connection object is null!!"
예제 #3
0
class AmazonTransport(object):
    __slots__ = ('ephemeral', 'id', 'key', 'host', 'connection')
    
    def __init__(self, config):
        self.id = config.get('id')
        self.key = config.get('key')
        self.host = config.get('host', "email.us-east-1.amazonaws.com")
        self.connection = None
    
    def startup(self):
        self.connection = SESConnection(
                aws_access_key_id = self.id,
                aws_secret_access_key = self.key,
                host = self.host
            )
    
    def deliver(self, message):
        try:
            response = self.connection.send_raw_email(
                    source = message.author.encode(),
                    destinations = message.recipients.encode(),
                    raw_message = str(message)
                )
            
            return (
                    response['SendRawEmailResponse']['SendRawEmailResult']['MessageId'],
                    response['SendRawEmailResponse']['ResponseMetadata']['RequestId']
                )
        
        except SESConnection.ResponseError, err:
            raise # TODO: Raise appropriate internal exception.
예제 #4
0
    def emit(self, record):
        """
        Emit a record.

        Format the record and send it to the specified addressees.
        """
        client =  SESConnection(self.aws_key, self.aws_secret)

        message = MIMEMultipart('alternative')
        message.set_charset('UTF-8')

        message['Subject'] = self._encode_str(self.getSubject(record))
        message['From'] = self._encode_str(self.fromaddr)
        message['To'] = self._convert_to_strings(self.toaddrs)

        from email.utils import formatdate

        body = self.format(record)
        body = "From: %s\r\n" \
               "To: %s\r\n" \
               "Subject: %s\r\n" \
               "Date: %s\r\n\r\n" \
               "%s" % (
               self.fromaddr,
               ",".join(self.toaddrs),
               self.getSubject(record),
               formatdate(),
               body)

        message.attach(MIMEText(self._encode_str(body), 'plain'))

        return client.send_raw_email(message.as_string(), self.fromaddr,
                                     destinations=self.toaddrs)
예제 #5
0
    def emit(self, record):
        """
        Emit a record.

        Format the record and send it to the specified addressees.
        """
        client = SESConnection(self.aws_key, self.aws_secret)

        message = MIMEMultipart('alternative')
        message.set_charset('UTF-8')

        message['Subject'] = self._encode_str(self.getSubject(record))
        message['From'] = self._encode_str(self.fromaddr)
        message['To'] = self._convert_to_strings(self.toaddrs)

        from email.utils import formatdate

        body = self.format(record)
        body = "Date: {0}\r\n\r\n {1}".format(formatdate(), body)

        message.attach(MIMEText(self._encode_str(body), 'plain'))

        return client.send_raw_email(message.as_string(),
                                     self.fromaddr,
                                     destinations=self.toaddrs)
예제 #6
0
class AmazonTransport(object):  # pragma: no cover
    __slots__ = ('ephemeral', 'id', 'key', 'host', 'connection')

    def __init__(self, config):
        self.id = config.get('id')
        self.key = config.get('key')
        self.host = config.get('host', "email.us-east-1.amazonaws.com")
        self.connection = None

    def startup(self):
        self.connection = SESConnection(aws_access_key_id=self.id,
                                        aws_secret_access_key=self.key,
                                        host=self.host)

    def deliver(self, message):
        try:
            response = self.connection.send_raw_email(
                source=message.author.encode(),
                destinations=message.recipients.encode(),
                raw_message=str(message))

            return (response['SendRawEmailResponse']['SendRawEmailResult']
                    ['MessageId'], response['SendRawEmailResponse']
                    ['ResponseMetadata']['RequestId'])

        except SESConnection.ResponseError:
            raise  # TODO: Raise appropriate internal exception.
            # ['status', 'reason', 'body', 'request_id', 'error_code', 'error_message']

    def shutdown(self):
        if self.connection:
            self.connection.close()

        self.connection = None
예제 #7
0
    def send_ses(self, sender_from, recipients, body):
        # SES Connection create
        aws_access_key = EXTERNAL_CONFIG['aws_access_key']
        aws_secret_key = EXTERNAL_CONFIG['aws_secret_key']

        from boto.ses import SESConnection
        ses_conn = SESConnection(aws_access_key, aws_secret_key)
        ret = ses_conn.send_raw_email(sender_from, body, destinations=[])

        current_app.logger.debug('sent mail to %s by SES' % (str(recipients),))
        return ret
예제 #8
0
class SESBackend(BaseEmailBackend):
    """A Django Email backend that uses Amazon's Simple Email Service.
    """
    def __init__(self,
                 fail_silently=False,
                 aws_access_key=None,
                 aws_secret_key=None,
                 aws_region_name=None,
                 aws_region_endpoint=None,
                 aws_auto_throttle=None,
                 dkim_domain=None,
                 dkim_key=None,
                 dkim_selector=None,
                 dkim_headers=None,
                 **kwargs):

        super(SESBackend, self).__init__(fail_silently=fail_silently, **kwargs)
        self._access_key_id = aws_access_key or settings.ACCESS_KEY
        self._access_key = aws_secret_key or settings.SECRET_KEY
        self._region = RegionInfo(name=aws_region_name
                                  or settings.AWS_SES_REGION_NAME,
                                  endpoint=aws_region_endpoint
                                  or settings.AWS_SES_REGION_ENDPOINT)
        self._throttle = aws_auto_throttle or settings.AWS_SES_AUTO_THROTTLE

        self.dkim_domain = dkim_domain or settings.DKIM_DOMAIN
        self.dkim_key = dkim_key or settings.DKIM_PRIVATE_KEY
        self.dkim_selector = dkim_selector or settings.DKIM_SELECTOR
        self.dkim_headers = dkim_headers or settings.DKIM_HEADERS

        self.connection = None

    def open(self):
        """Create a connection to the AWS API server. This can be reused for
        sending multiple emails.
        """
        if self.connection:
            return False

        try:
            self.connection = SESConnection(
                aws_access_key_id=self._access_key_id,
                aws_secret_access_key=self._access_key,
                region=self._region,
            )
        except:
            if not self.fail_silently:
                raise

    def close(self):
        """Close any open HTTP connections to the API server.
        """
        try:
            self.connection.close()
            self.connection = None
        except:
            if not self.fail_silently:
                raise

    def send_messages(self, email_messages):
        """Sends one or more EmailMessage objects and returns the number of
        email messages sent.
        """
        if not email_messages:
            return

        new_conn_created = self.open()
        if not self.connection:
            # Failed silently
            return

        num_sent = 0
        source = settings.AWS_SES_RETURN_PATH
        for message in email_messages:
            # Automatic throttling. Assumes that this is the only SES client
            # currently operating. The AWS_SES_AUTO_THROTTLE setting is a
            # factor to apply to the rate limit, with a default of 0.5 to stay
            # well below the actual SES throttle.
            # Set the setting to 0 or None to disable throttling.
            if self._throttle:
                global recent_send_times

                now = datetime.now()

                # Get and cache the current SES max-per-second rate limit
                # returned by the SES API.
                rate_limit = self.get_rate_limit()

                # Prune from recent_send_times anything more than a few seconds
                # ago. Even though SES reports a maximum per-second, the way
                # they enforce the limit may not be on a one-second window.
                # To be safe, we use a two-second window (but allow 2 times the
                # rate limit) and then also have a default rate limit factor of
                # 0.5 so that we really limit the one-second amount in two
                # seconds.
                window = 2.0  # seconds
                window_start = now - timedelta(seconds=window)
                new_send_times = []
                for time in recent_send_times:
                    if time > window_start:
                        new_send_times.append(time)
                recent_send_times = new_send_times

                # If the number of recent send times in the last 1/_throttle
                # seconds exceeds the rate limit, add a delay.
                # Since I'm not sure how Amazon determines at exactly what
                # point to throttle, better be safe than sorry and let in, say,
                # half of the allowed rate.
                if len(new_send_times) > rate_limit * window * self._throttle:
                    # Sleep the remainder of the window period.
                    delta = now - new_send_times[0]
                    total_seconds = (delta.microseconds +
                                     (delta.seconds + delta.days * 24 * 3600) *
                                     10**6) / 10**6
                    delay = window - total_seconds
                    if delay > 0:
                        sleep(delay)

                recent_send_times.append(now)
                # end of throttling

            try:
                response = self.connection.send_raw_email(
                    source=source or message.from_email,
                    destinations=message.recipients(),
                    raw_message=unicode(
                        dkim_sign(
                            message.message().as_string(),
                            dkim_key=self.dkim_key,
                            dkim_domain=self.dkim_domain,
                            dkim_selector=self.dkim_selector,
                            dkim_headers=self.dkim_headers,
                        ), 'utf-8'))
                message.extra_headers['status'] = 200
                message.extra_headers['message_id'] = response[
                    'SendRawEmailResponse']['SendRawEmailResult']['MessageId']
                message.extra_headers['request_id'] = response[
                    'SendRawEmailResponse']['ResponseMetadata']['RequestId']
                num_sent += 1
            except SESConnection.ResponseError, err:
                # Store failure information so to post process it if required
                error_keys = [
                    'status', 'reason', 'body', 'request_id', 'error_code',
                    'error_message'
                ]
                for key in error_keys:
                    message.extra_headers[key] = getattr(err, key, None)
                if not self.fail_silently:
                    raise

        if new_conn_created:
            self.close()

        return num_sent
예제 #9
0
class SESBackend(BaseEmailBackend):
    """A Django Email backend that uses Amazon's Simple Email Service.
    """

    def __init__(self, fail_silently=False, *args, **kwargs):
        super(SESBackend, self).__init__(fail_silently=fail_silently, *args, **kwargs)

        self._access_key_id = getattr(settings, "AWS_ACCESS_KEY_ID", None)
        self._access_key = getattr(settings, "AWS_SECRET_ACCESS_KEY", None)
        self._api_endpoint = getattr(settings, "AWS_SES_API_HOST", SESConnection.DefaultHost)

        self.connection = None

    def open(self):
        """Create a connection to the AWS API server. This can be reused for
        sending multiple emails.
        """
        if self.connection:
            return False

        try:
            self.connection = SESConnection(
                aws_access_key_id=self._access_key_id, aws_secret_access_key=self._access_key, host=self._api_endpoint
            )
        except:
            if not self.fail_silently:
                raise

    def close(self):
        """Close any open HTTP connections to the API server.
        """
        try:
            self.connection.close()
            self.connection = None
        except:
            if not self.fail_silently:
                raise

    def send_messages(self, email_messages):
        """Sends one or more EmailMessage objects and returns the number of
        email messages sent.
        """
        if not email_messages:
            return

        new_conn_created = self.open()
        if not self.connection:
            # Failed silently
            return

        num_sent = 0
        for message in email_messages:
            try:
                response = self.connection.send_raw_email(
                    source=message.from_email,
                    destinations=message.recipients(),
                    raw_message=message.message().as_string(),
                )
                message.extra_headers["status"] = 200
                message.extra_headers["message_id"] = response["SendRawEmailResponse"]["SendRawEmailResult"][
                    "MessageId"
                ]
                message.extra_headers["request_id"] = response["SendRawEmailResponse"]["ResponseMetadata"]["RequestId"]
                num_sent += 1
            except SESConnection.ResponseError, err:
                # Store failure information so you can post process it if required
                error_keys = ["status", "reason", "body", "request_id", "error_code", "error_message"]
                for key in error_keys:
                    message.extra_headers[key] = getattr(err, key, None)
                if not self.fail_silently:
                    raise

        if new_conn_created:
            self.close()

        return num_sent
예제 #10
0
class SesSender(object):
	logger = None

	def run(self):
		self.init_log()
		try:
			self.log("Args: %r" % argv)
			self.log("Env: %r" % dict(environ))
			self.get_parties()
			self.get_credentials()
			self.make_connection()
			self.process_message()
			self.send_message()
		except Exception:
			self.abort('Unspecified error',1)

	def get_parties(self):
		try:
			self.sender = argv[1]
			self.recipients = argv[2:]
		except Exception:
			self.abort('Missing Sender / Recipients',2)

	def get_credentials(self):
		try:
			self.aws_id = environ['AWS_ACCESS_KEY_ID']
			self.aws_key = environ['AWS_SECRET_KEY']
			assert self.aws_id is not None
			assert self.aws_key is not None
		except Exception:
			self.abort('Missing AWS Credentials',3)

	def make_connection(self):
		try:
			self.conn = SESConnection(self.aws_id,self.aws_key)
		except Exception:
			self.abort('Failed to establish connection',4)

	def process_message(self):
		try:
			msg = stdin.read()
			assert msg[:4] == 'From'
			envelope,msg = msg.split('\n',1)
			self.msg = msg
			self.log('Sender: %r' % self.sender)
			self.log('Recipients: %r' % self.recipients)
			self.log('Message:\n' + msg)
		except Exception:
			self.abort('Failed to process message text',5)

	def send_message(self):
		try:
			self.conn.send_raw_email(
				source=self.sender,
				raw_message=self.msg,
				destinations=self.recipients
			)
		except BotoServerError, bse:
			if 'InvalidTokenId' in bse.body:
				self.abort('Bad AWS Credentials (token)',6)
			if 'SignatureDoesNotMatch' in bse.body:
				self.abort('Bad AWS Credentials (signature)',6)
			self.abort('Failed to actually deliver message',7)
		except Exception:
			self.abort('Failed to actually deliver message',7)
예제 #11
0
class SESBackend(BaseEmailBackend):
    """A Django Email backend that uses Amazon's Simple Email Service.
    """
    def __init__(self, fail_silently=False, *args, **kwargs):
        super(SESBackend, self).__init__(fail_silently=fail_silently,
                                         *args,
                                         **kwargs)

        self._access_key_id = getattr(settings, 'AWS_ACCESS_KEY_ID', None)
        self._access_key = getattr(settings, 'AWS_SECRET_ACCESS_KEY', None)
        self._api_endpoint = getattr(settings, 'AWS_SES_API_HOST',
                                     SESConnection.DefaultHost)

        self.connection = None

    def open(self):
        """Create a connection to the AWS API server. This can be reused for
        sending multiple emails.
        """
        if self.connection:
            return False

        try:
            self.connection = SESConnection(
                aws_access_key_id=self._access_key_id,
                aws_secret_access_key=self._access_key,
                host=self._api_endpoint,
            )
        except:
            if not self.fail_silently:
                raise

    def close(self):
        """Close any open HTTP connections to the API server.
        """
        try:
            self.connection.close()
            self.connection = None
        except:
            if not self.fail_silently:
                raise

    def send_messages(self, email_messages):
        """Sends one or more EmailMessage objects and returns the number of
        email messages sent.
        """
        if not email_messages:
            return

        new_conn_created = self.open()
        if not self.connection:
            # Failed silently
            return

        num_sent = 0
        for message in email_messages:
            try:
                response = self.connection.send_raw_email(
                    source=message.from_email,
                    destinations=message.recipients(),
                    raw_message=message.message().as_string(),
                )
                message.extra_headers['status'] = 200
                message.extra_headers['message_id'] = response[
                    'SendRawEmailResponse']['SendRawEmailResult']['MessageId']
                message.extra_headers['request_id'] = response[
                    'SendRawEmailResponse']['ResponseMetadata']['RequestId']
                num_sent += 1
            except SESConnection.ResponseError, err:
                # Store failure information so you can post process it if required
                error_keys = [
                    'status', 'reason', 'body', 'request_id', 'error_code',
                    'error_message'
                ]
                for key in error_keys:
                    message.extra_headers[key] = getattr(err, key, None)
                if not self.fail_silently:
                    raise

        if new_conn_created:
            self.close()

        return num_sent
예제 #12
0
파일: __init__.py 프로젝트: vonguard/canvas
class SESBackend(BaseEmailBackend):
    """A Django Email backend that uses Amazon's Simple Email Service.
    """
    def __init__(self, fail_silently=False, *args, **kwargs):
        super(SESBackend, self).__init__(fail_silently=fail_silently,
                                         *args,
                                         **kwargs)

        self._access_key_id = getattr(settings, 'AWS_ACCESS_KEY_ID', None)
        self._access_key = getattr(settings, 'AWS_SECRET_ACCESS_KEY', None)
        self._api_endpoint = getattr(settings, 'AWS_SES_API_HOST',
                                     SESConnection.DefaultHost)

        self.connection = None
        self._lock = threading.RLock()

    def open(self):
        """Create a connection to the AWS API server. This can be reused for
        sending multiple emails.
        """
        if self.connection:
            return False

        try:
            self.connection = SESConnection(
                aws_access_key_id=self._access_key_id,
                aws_secret_access_key=self._access_key,
                host=self._api_endpoint,
            )
        except:
            if not self.fail_silently:
                raise

    def close(self):
        """Close any open HTTP connections to the API server.
        """
        try:
            self.connection.close()
            self.connection = None
        except:
            if not self.fail_silently:
                raise

    def dkim_sign(self, message):
        # Courtesy of http://djangosnippets.org/snippets/1995/
        raw_message = message.message().as_string()
        if settings.DKIM_PRIVATE_KEY:
            included_headers = [
                "Content-Type", "MIME-Version", "Content-Transfer-Encoding",
                "Subject", "From", "To"
            ]
            dkim_header = dkim.sign(raw_message,
                                    settings.DKIM_SELECTOR,
                                    settings.DKIM_DOMAIN,
                                    settings.DKIM_PRIVATE_KEY,
                                    include_headers=included_headers)
            raw_message = dkim_header + raw_message
        return raw_message

    def send_messages(self, email_messages):
        """Sends one or more EmailMessage objects and returns the number of
        email messages sent.
        """
        if not email_messages:
            return

        self._lock.acquire()
        try:
            new_conn_created = self.open()
            if not self.connection:
                # Failed silently
                return

            num_sent = 0
            for message in email_messages:
                raw_message = self.dkim_sign(message)
                try:
                    self.connection.send_raw_email(
                        source=message.from_email,
                        destinations=message.recipients(),
                        raw_message=raw_message,
                    )
                    num_sent += 1
                except SESConnection.ResponseError:
                    if not self.fail_silently:
                        raise
                    pass

            if new_conn_created:
                self.close()

        finally:
            self._lock.release()
        return num_sent
예제 #13
0
def mailsender(request):

    mail = request.FILES[u"file"].read()
    to = request.GET["to"]

    # split headers
    i = mail.find("\n\n")
    headers, mail = mail[:i], mail[i:]
    headers = headers.split("\n")
    allowed = {
        "MIME-Version",
        "Message-ID",
        "In-Reply-To",
        "Content-Type",
        "Date",
        "Subject",
        "From",
        "To",
        "Bcc",
        "Cc",
        "References",
    }

    from_ = None

    h = []
    i = 0
    while i < len(headers):
        add = False
        header = headers[i][: headers[i].find(":")]
        if header in allowed:
            add = True
        if header == "From":
            from_ = headers[i][headers[i].find(":") + 1 :].strip()

        while True:
            if add:
                h.append(headers[i])
            i += 1
            if i >= len(headers) or headers[i][0] not in " \t":
                break

    mail = "\n".join(h) + mail

    # find from email
    i = from_.find("<")
    if i != -1:
        from_ = from_[i + 1 :]
        from_ = from_[: from_.find(">")]

    # logger.info("headers: %s", str(headers))
    # logger.info("h: %s", str(h))

    logger.info("Mail from %s to %s recieved", from_, to)
    # Only allow sending from altekamereren domains and registered users.
    if (
        not from_.endswith("@altekamereren.org")
        and not from_.endswith("@altekamereren.com")
        and User.objects.filter(email=from_).exists()
    ):
        logger.info("Sender not accepted.")
        return HttpResponse(status=403)

    to = to.replace(u"flojt", u"flöjt")
    reciever = to.split("@")[0]
    if reciever in ak.sections:
        user_emails = [
            user.email
            for user in User.objects.filter(instrument__in=ak.section_to_short_instruments[reciever], is_active=True)
        ]

        logger.info("Sending to section %s: %s", to, str(user_emails))

    elif reciever == u"infolistan":
        reciever = [user.email for user in User.objects.filter(is_active=True)]
        logger.info("Sending to infolistan: %s", str(reciever))
    else:
        logger.info("List not accepted.")
        return HttpResponse(status=400)

    from django.conf import settings
    from boto.ses import SESConnection
    from boto.exception import BotoServerError

    access_key_id = getattr(settings, "AWS_ACCESS_KEY_ID", None)
    access_key = getattr(settings, "AWS_SECRET_ACCESS_KEY", None)
    api_endpoint = getattr(settings, "AWS_SES_API_HOST", SESConnection.DefaultHost)
    connection = SESConnection(aws_access_key_id=access_key_id, aws_secret_access_key=access_key, host=api_endpoint)

    if not user_emails:
        return HttpResponse(status=400)

    try:
        connection.send_raw_email(mail, settings.ADMINS[0][1], user_emails)
    except BotoServerError as e:
        i = e.body.find("<Message>")
        message = e.body[i + len("<Message>") :]
        message = message[: message.find("</Message>")]

        if message == "Email address is not verified.":
            if MailVerificationSent.objects.filter(
                email=from_, sent__gte=datetime.datetime.now() - datetime.timedelta(days=1)
            ).exists():
                connection.verify_email_address(from_)
                logger.error("Sending verify mail to: %s", from_)
                MailVerificationSent(email=from_).save()
            else:
                logger.error("Verify mail already sent today: %s", from_)
            return HttpResponse(status=444)
        else:
            raise

    return HttpResponse()
예제 #14
0
class SESBackend(BaseEmailBackend):
    """A Django Email backend that uses Amazon's Simple Email Service.
    """

    def __init__(self, fail_silently=False, *args, **kwargs):
        super(SESBackend, self).__init__(fail_silently=fail_silently, *args,
                                         **kwargs)

        self._access_key_id = getattr(settings, 'AWS_ACCESS_KEY_ID', None)
        self._access_key = getattr(settings, 'AWS_SECRET_ACCESS_KEY', None)
        self._api_endpoint = getattr(settings, 'AWS_SES_API_HOST',
                                     SESConnection.DefaultHost)

        self.connection = None
        self._lock = threading.RLock()

    def open(self):
        """Create a connection to the AWS API server. This can be reused for
        sending multiple emails.
        """
        if self.connection:
            return False

        try:
            self.connection = SESConnection(
                aws_access_key_id=self._access_key_id,
                aws_secret_access_key=self._access_key,
                host=self._api_endpoint,
            )
        except:
            if not self.fail_silently:
                raise

    def close(self):
        """Close any open HTTP connections to the API server.
        """
        try:
            self.connection.close()
            self.connection = None
        except:
            if not self.fail_silently:
                raise

    def send_messages(self, email_messages):
        """Sends one or more EmailMessage objects and returns the number of
        email messages sent.
        """
        if not email_messages:
            return

        self._lock.acquire()
        try:
            new_conn_created = self.open()
            if not self.connection:
                # Failed silently
                return

            num_sent = 0
            for message in email_messages:
                try:
                    self.connection.send_raw_email(
                        source=message.from_email,
                        destinations=message.recipients(),
                        raw_message=message.message().as_string(),
                    )
                    num_sent += 1
                except SESConnection.ResponseError:
                    if not self.fail_silently:
                        raise
                    pass

            if new_conn_created:
                self.close()

        finally:
            self._lock.release()
        return num_sent
예제 #15
0
def mailsender(request):
    
    mail = request.FILES[u"file"].read()
    to = request.GET["to"]
    
    #split headers
    i = mail.find("\n\n")
    headers, mail = mail[:i], mail[i:]
    headers = headers.split("\n")
    allowed = {"MIME-Version", "Message-ID", "In-Reply-To", "Content-Type", 
               "Date", "Subject", "From", "To", "Bcc", "Cc", "References"}
    
    from_ = None
    
    h = []
    i=0
    while i<len(headers):
        add = False
        header = headers[i][:headers[i].find(":")]
        if header in allowed:
            add = True
        if header == "From":
            from_ = headers[i][headers[i].find(":")+1:].strip()
            
        while True:
            if add: 
                h.append(headers[i])
            i += 1
            if i>=len(headers) or headers[i][0] not in " \t":
                break
            
    mail = "\n".join(h) + mail
    
    #find from email
    i = from_.find("<")
    if i != -1:
        from_ = from_[i+1:]
        from_ = from_[:from_.find(">")]
    
    #logger.info("headers: %s", str(headers))
    #logger.info("h: %s", str(h))
    
    logger.info("Mail from %s to %s recieved", from_, to)
    # Only allow sending from altekamereren domains and registered users.
    if not from_.endswith("@altekamereren.org") \
            and not from_.endswith("@altekamereren.com") \
            and User.objects.filter(email=from_).exists():
        logger.info("Sender not accepted.")
        return HttpResponse(status=403)
    
    to = to.replace(u"flojt", u"flöjt")
    reciever = to.split("@")[0]
    if reciever in ak.sections:
        user_emails = [user.email for user in User.objects.filter(
            instrument__in=ak.section_to_short_instruments[reciever], 
            is_active=True)]

        

        logger.info("Sending to section %s: %s", to, str(user_emails))

    elif reciever == u"infolistan":
        reciever = [user.email for user in User.objects.filter(is_active=True)]
        logger.info("Sending to infolistan: %s", str(reciever))
    else:
        logger.info("List not accepted.")
        return HttpResponse(status=400)
    
    from django.conf import settings
    from boto.ses import SESConnection
    from boto.exception import BotoServerError
    
    access_key_id = getattr(settings, 'AWS_ACCESS_KEY_ID', None)
    access_key = getattr(settings, 'AWS_SECRET_ACCESS_KEY', None)
    api_endpoint = getattr(settings, 'AWS_SES_API_HOST',
                                     SESConnection.DefaultHost)
    connection = SESConnection(
                aws_access_key_id=access_key_id,
                aws_secret_access_key=access_key,
                host=api_endpoint,
    )
    
    if not user_emails:
        return HttpResponse(status=400)

    try:
        connection.send_raw_email(mail, settings.ADMINS[0][1], user_emails)
    except BotoServerError as e:
        i = e.body.find("<Message>")
        message = e.body[i+len("<Message>"):]
        message = message[:message.find("</Message>")]
        
        if message == "Email address is not verified.":
            if MailVerificationSent.objects.filter(email=from_, 
                    sent__gte=datetime.datetime.now() - datetime.timedelta(days=1)
                        ).exists():
                connection.verify_email_address(from_)
                logger.error("Sending verify mail to: %s", from_)
                MailVerificationSent(email=from_).save()
            else:
                logger.error("Verify mail already sent today: %s", from_)
            return HttpResponse(status=444)
        else:
            raise

    
    return HttpResponse()
예제 #16
0
class SESBackend(BaseEmailBackend):
    """A Django Email backend that uses Amazon's Simple Email Service.
    """
    def __init__(self, fail_silently=False, *args, **kwargs):
        super(SESBackend, self).__init__(fail_silently=fail_silently,
                                         *args,
                                         **kwargs)

        self._access_key_id = getattr(settings, 'AWS_ACCESS_KEY_ID', None)
        self._access_key = getattr(settings, 'AWS_SECRET_ACCESS_KEY', None)
        self._api_endpoint = getattr(settings, 'AWS_SES_API_HOST',
                                     SESConnection.DefaultHost)

        self.connection = None
        self._lock = threading.RLock()

    def open(self):
        """Create a connection to the AWS API server. This can be reused for
        sending multiple emails.
        """
        if self.connection:
            return False

        try:
            self.connection = SESConnection(
                aws_access_key_id=self._access_key_id,
                aws_secret_access_key=self._access_key,
                host=self._api_endpoint,
            )
        except:
            if not self.fail_silently:
                raise

    def close(self):
        """Close any open HTTP connections to the API server.
        """
        try:
            self.connection.close()
            self.connection = None
        except:
            if not self.fail_silently:
                raise

    def send_messages(self, email_messages):
        """Sends one or more EmailMessage objects and returns the number of
        email messages sent.
        """
        if not email_messages:
            return

        self._lock.acquire()
        try:
            new_conn_created = self.open()
            if not self.connection:
                # Failed silently
                return

            num_sent = 0
            for message in email_messages:
                try:
                    self.connection.send_raw_email(
                        source=message.from_email,
                        destinations=message.recipients(),
                        raw_message=message.message().as_string(),
                    )
                    num_sent += 1
                except SESConnection.ResponseError:
                    if not self.fail_silently:
                        raise
                    pass

            if new_conn_created:
                self.close()

        finally:
            self._lock.release()
        return num_sent
예제 #17
0
class SESBackend(BaseEmailBackend):
    """A Django Email backend that uses Amazon's Simple Email Service.
    """

    def __init__(self, fail_silently=False, aws_access_key=None,
                 aws_secret_key=None, aws_region_name=None,
                 aws_region_endpoint=None, aws_auto_throttle=None,
                 dkim_domain=None, dkim_key=None, dkim_selector=None,
                 dkim_headers=None, **kwargs):

        super(SESBackend, self).__init__(fail_silently=fail_silently, **kwargs)
        self._access_key_id = aws_access_key or settings.ACCESS_KEY
        self._access_key = aws_secret_key or settings.SECRET_KEY
        self._region = RegionInfo(
            name=aws_region_name or settings.AWS_SES_REGION_NAME,
            endpoint=aws_region_endpoint or settings.AWS_SES_REGION_ENDPOINT)
        self._throttle = aws_auto_throttle or settings.AWS_SES_AUTO_THROTTLE

        self.dkim_domain = dkim_domain or settings.DKIM_DOMAIN
        self.dkim_key = dkim_key or settings.DKIM_PRIVATE_KEY
        self.dkim_selector = dkim_selector or settings.DKIM_SELECTOR
        self.dkim_headers = dkim_headers or settings.DKIM_HEADERS

        self.connection = None

    def open(self):
        """Create a connection to the AWS API server. This can be reused for
        sending multiple emails.
        """
        if self.connection:
            return False

        try:
            self.connection = SESConnection(
                aws_access_key_id=self._access_key_id,
                aws_secret_access_key=self._access_key,
                region=self._region,
            )
        except:
            if not self.fail_silently:
                raise

    def close(self):
        """Close any open HTTP connections to the API server.
        """
        try:
            self.connection.close()
            self.connection = None
        except:
            if not self.fail_silently:
                raise

    def send_messages(self, email_messages):
        """Sends one or more EmailMessage objects and returns the number of
        email messages sent.
        """
        if not email_messages:
            return

        new_conn_created = self.open()
        if not self.connection:
            # Failed silently
            return

        num_sent = 0
        source = settings.AWS_SES_RETURN_PATH
        for message in email_messages:
            # Automatic throttling. Assumes that this is the only SES client
            # currently operating. The AWS_SES_AUTO_THROTTLE setting is a
            # factor to apply to the rate limit, with a default of 0.5 to stay
            # well below the actual SES throttle.
            # Set the setting to 0 or None to disable throttling.
            if self._throttle:
                global recent_send_times

                now = datetime.now()

                # Get and cache the current SES max-per-second rate limit
                # returned by the SES API.
                rate_limit = self.get_rate_limit()

                # Prune from recent_send_times anything more than a few seconds
                # ago. Even though SES reports a maximum per-second, the way
                # they enforce the limit may not be on a one-second window.
                # To be safe, we use a two-second window (but allow 2 times the
                # rate limit) and then also have a default rate limit factor of
                # 0.5 so that we really limit the one-second amount in two
                # seconds.
                window = 2.0  # seconds
                window_start = now - timedelta(seconds=window)
                new_send_times = []
                for time in recent_send_times:
                    if time > window_start:
                        new_send_times.append(time)
                recent_send_times = new_send_times

                # If the number of recent send times in the last 1/_throttle
                # seconds exceeds the rate limit, add a delay.
                # Since I'm not sure how Amazon determines at exactly what
                # point to throttle, better be safe than sorry and let in, say,
                # half of the allowed rate.
                if len(new_send_times) > rate_limit * window * self._throttle:
                    # Sleep the remainder of the window period.
                    delta = now - new_send_times[0]
                    total_seconds = (delta.microseconds + (delta.seconds +
                            delta.days * 24 * 3600) * 10**6) / 10**6
                    delay = window - total_seconds
                    if delay > 0:
                        sleep(delay)

                recent_send_times.append(now)
                # end of throttling

            try:
                response = self.connection.send_raw_email(
                    source=source or message.from_email,
                    destinations=message.recipients(),
                    raw_message=unicode(dkim_sign(message.message().as_string(),
                                                  dkim_key=self.dkim_key,
                                                  dkim_domain=self.dkim_domain,
                                                  dkim_selector=self.dkim_selector,
                                                  dkim_headers=self.dkim_headers,
                                                  ), 'utf-8')
                )
                message.extra_headers['status'] = 200
                message.extra_headers['message_id'] = response[
                    'SendRawEmailResponse']['SendRawEmailResult']['MessageId']
                message.extra_headers['request_id'] = response[
                    'SendRawEmailResponse']['ResponseMetadata']['RequestId']
                num_sent += 1
            except SESConnection.ResponseError, err:
                # Store failure information so to post process it if required
                error_keys = ['status', 'reason', 'body', 'request_id',
                                'error_code', 'error_message']
                for key in error_keys:
                    message.extra_headers[key] = getattr(err, key, None)
                if not self.fail_silently:
                    raise

        if new_conn_created:
            self.close()

        return num_sent
예제 #18
0
class SESBackend(BaseEmailBackend):
    """A Django Email backend that uses Amazon's Simple Email Service.
    """

    def __init__(self, fail_silently=False, aws_access_key=None,
                 aws_secret_key=None, aws_region_name=None,
                 aws_region_endpoint=None, aws_auto_throttle=None,
                 dkim_domain=None, dkim_key=None, dkim_selector=None,
                 dkim_headers=None, proxy=None, proxy_port=None,
                 proxy_user=None, proxy_pass=None, **kwargs):

        super(SESBackend, self).__init__(fail_silently=fail_silently, **kwargs)
        self._access_key_id = aws_access_key or settings.ACCESS_KEY
        self._access_key = aws_secret_key or settings.SECRET_KEY
        self._region = RegionInfo(
            name=aws_region_name or settings.AWS_SES_REGION_NAME,
            endpoint=aws_region_endpoint or settings.AWS_SES_REGION_ENDPOINT)
        self._throttle = aws_auto_throttle or settings.AWS_SES_AUTO_THROTTLE
        self._proxy = proxy or settings.AWS_SES_PROXY
        self._proxy_port = proxy_port or settings.AWS_SES_PROXY_PORT
        self._proxy_user = proxy_user or settings.AWS_SES_PROXY_USER
        self._proxy_pass = proxy_pass or settings.AWS_SES_PROXY_PASS

        self.dkim_domain = dkim_domain or settings.DKIM_DOMAIN
        self.dkim_key = dkim_key or settings.DKIM_PRIVATE_KEY
        self.dkim_selector = dkim_selector or settings.DKIM_SELECTOR
        self.dkim_headers = dkim_headers or settings.DKIM_HEADERS

        self.connection = None

    def open(self):
        """Create a connection to the AWS API server. This can be reused for
        sending multiple emails.
        """
        if self.connection:
            return False

        try:
            self.connection = SESConnection(
                aws_access_key_id=self._access_key_id,
                aws_secret_access_key=self._access_key,
                region=self._region,
                proxy=self._proxy,
                proxy_port=self._proxy_port,
                proxy_user=self._proxy_user,
                proxy_pass=self._proxy_pass,
            )
        except Exception:
            if not self.fail_silently:
                raise

    def close(self):
        """Close any open HTTP connections to the API server.
        """
        try:
            self.connection.close()
            self.connection = None
        except Exception:
            if not self.fail_silently:
                raise

    def send_messages(self, email_messages):
        """Sends one or more EmailMessage objects and returns the number of
        email messages sent.
        """
        if not email_messages:
            return

        new_conn_created = self.open()
        if not self.connection:
            # Failed silently
            return

        num_sent = 0
        source = settings.AWS_SES_RETURN_PATH
        for message in email_messages:
            # SES Configuration sets. If the AWS_SES_CONFIGURATION_SET setting
            # is not None, append the appropriate header to the message so that
            # SES knows which configuration set it belongs to.
            #
            # If settings.AWS_SES_CONFIGURATION_SET is a callable, pass it the
            # message object and dkim settings and expect it to return a string
            # containing the SES Configuration Set name.
            if (settings.AWS_SES_CONFIGURATION_SET and
                'X-SES-CONFIGURATION-SET' not in message.extra_headers):
                if callable(settings.AWS_SES_CONFIGURATION_SET):
                    message.extra_headers[
                        'X-SES-CONFIGURATION-SET'] = settings.AWS_SES_CONFIGURATION_SET(
                            message,
                            dkim_domain=self.dkim_domain,
                            dkim_key=self.dkim_key,
                            dkim_selector=self.dkim_selector,
                            dkim_headers=self.dkim_headers
                        )
                else:
                    message.extra_headers[
                        'X-SES-CONFIGURATION-SET'] = settings.AWS_SES_CONFIGURATION_SET

            # Automatic throttling. Assumes that this is the only SES client
            # currently operating. The AWS_SES_AUTO_THROTTLE setting is a
            # factor to apply to the rate limit, with a default of 0.5 to stay
            # well below the actual SES throttle.
            # Set the setting to 0 or None to disable throttling.
            if self._throttle:
                global recent_send_times

                now = datetime.now()

                # Get and cache the current SES max-per-second rate limit
                # returned by the SES API.
                rate_limit = self.get_rate_limit()
                logger.debug(u"send_messages.throttle rate_limit='{}'".format(rate_limit))

                # Prune from recent_send_times anything more than a few seconds
                # ago. Even though SES reports a maximum per-second, the way
                # they enforce the limit may not be on a one-second window.
                # To be safe, we use a two-second window (but allow 2 times the
                # rate limit) and then also have a default rate limit factor of
                # 0.5 so that we really limit the one-second amount in two
                # seconds.
                window = 2.0  # seconds
                window_start = now - timedelta(seconds=window)
                new_send_times = []
                for time in recent_send_times:
                    if time > window_start:
                        new_send_times.append(time)
                recent_send_times = new_send_times

                # If the number of recent send times in the last 1/_throttle
                # seconds exceeds the rate limit, add a delay.
                # Since I'm not sure how Amazon determines at exactly what
                # point to throttle, better be safe than sorry and let in, say,
                # half of the allowed rate.
                if len(new_send_times) > rate_limit * window * self._throttle:
                    # Sleep the remainder of the window period.
                    delta = now - new_send_times[0]
                    total_seconds = (delta.microseconds + (delta.seconds +
                                     delta.days * 24 * 3600) * 10**6) / 10**6
                    delay = window - total_seconds
                    if delay > 0:
                        sleep(delay)

                recent_send_times.append(now)
                # end of throttling

            try:
                response = self.connection.send_raw_email(
                    source=source or message.from_email,
                    destinations=message.recipients(),
                    raw_message=dkim_sign(message.message().as_string(),
                                          dkim_key=self.dkim_key,
                                          dkim_domain=self.dkim_domain,
                                          dkim_selector=self.dkim_selector,
                                          dkim_headers=self.dkim_headers)
                )
                message.extra_headers['status'] = 200
                message.extra_headers['message_id'] = response[
                    'SendRawEmailResponse']['SendRawEmailResult']['MessageId']
                message.extra_headers['request_id'] = response[
                    'SendRawEmailResponse']['ResponseMetadata']['RequestId']
                num_sent += 1
                if 'X-SES-CONFIGURATION-SET' in message.extra_headers:
                    logger.debug(u"send_messages.sent from='{}' recipients='{}' message_id='{}' request_id='{}' ses-configuration-set='{}'".format(
                        message.from_email,
                        ", ".join(message.recipients()),
                        message.extra_headers['message_id'],
                        message.extra_headers['request_id'],
                        message.extra_headers['X-SES-CONFIGURATION-SET']
                    ))
                else:
                    logger.debug(u"send_messages.sent from='{}' recipients='{}' message_id='{}' request_id='{}'".format(
                        message.from_email,
                        ", ".join(message.recipients()),
                        message.extra_headers['message_id'],
                        message.extra_headers['request_id']
                    ))

            except SESConnection.ResponseError as err:
                # Store failure information so to post process it if required
                error_keys = ['status', 'reason', 'body', 'request_id',
                              'error_code', 'error_message']
                for key in error_keys:
                    message.extra_headers[key] = getattr(err, key, None)
                if not self.fail_silently:
                    raise

        if new_conn_created:
            self.close()

        return num_sent

    def get_rate_limit(self):
        if self._access_key_id in cached_rate_limits:
            return cached_rate_limits[self._access_key_id]

        new_conn_created = self.open()
        if not self.connection:
            raise Exception(
                "No connection is available to check current SES rate limit.")
        try:
            quota_dict = self.connection.get_send_quota()
            max_per_second = quota_dict['GetSendQuotaResponse'][
                'GetSendQuotaResult']['MaxSendRate']
            ret = float(max_per_second)
            cached_rate_limits[self._access_key_id] = ret
            return ret
        finally:
            if new_conn_created:
                self.close()
예제 #19
0
class SESMessage(object):
    """
    Usage:

    msg = SESMessage('*****@*****.**', '*****@*****.**', 'The subject')
    msg.text = 'Text body'
    msg.html = 'HTML body'
    msg.send()

    """
    def __init__(self, source, to_addresses, subject, **kw):
        self.ses = SESConnection()

        self._source = source
        self._to_addresses = to_addresses
        self._cc_addresses = None
        self._bcc_addresses = None

        self.subject = subject
        self.text = None
        self.html = None
        self.attachments = []

    def send(self):
        if not self.ses:
            raise Exception, 'No connection found'

        if (self.text and not self.html and not self.attachments) or \
           (self.html and not self.text and not self.attachments):
            return self.ses.send_email(self._source,
                                       self.subject,
                                       self.text or self.html,
                                       self._to_addresses,
                                       self._cc_addresses,
                                       self._bcc_addresses,
                                       format='text' if self.text else 'html')
        else:
            if not self.attachments:
                message = MIMEMultipart('alternative')

                message['Subject'] = self.subject
                message['From'] = self._source
                if isinstance(self._to_addresses, (list, tuple)):
                    message['To'] = COMMASPACE.join(self._to_addresses)
                else:
                    message['To'] = self._to_addresses

                message.attach(MIMEText(self.text, 'plain'))
                message.attach(MIMEText(self.html, 'html'))
            else:
                raise NotImplementedError, 'SES does not currently allow ' + \
                                           'messages with attachments.'
#                message = MIMEMultipart()
#
#                message_alt = MIMEMultipart('alternative')
#
#                if self.text:
#                    message_alt.attach(MIMEText(self.text, 'plain'))
#                if self.html:
#                    message_alt.attach(MIMEText(self.html, 'html'))
#
#                message.attach(message_alt)
#
#                message['Subject'] = self.subject
#                message['From'] = self._source
#                if isinstance(self._to_addresses, (list, tuple)):
#                    message['To'] = COMMASPACE.join(self._to_addresses)
#                else:
#                    message['To'] = self._to_addresses
#                message.preamble = 'You will not see this in a MIME-aware mail reader.\n'

#                print 'message: ', message.as_string()

#                for attachment in self.attachments:
#                    # Guess the content type based on the file's extension.  Encoding
#                    # will be ignored, although we should check for simple things like
#                    # gzip'd or compressed files.
#                    ctype, encoding = mimetypes.guess_type(attachment)
#                    if ctype is None or encoding is not None:
#                        # No guess could be made, or the file is encoded (compressed), so
#                        # use a generic bag-of-bits type.
#                        ctype = 'application/octet-stream'
#                    maintype, subtype = ctype.split('/', 1)
#                    if maintype == 'text':
#                        fp = open(attachment)
#                        # Note: we should handle calculating the charset
#                        part = MIMEText(fp.read(), _subtype=subtype)
#                        fp.close()
#                    elif maintype == 'image':
#                        fp = open(attachment, 'rb')
#                        part = MIMEImage(fp.read(), _subtype=subtype)
#                        fp.close()
#                    elif maintype == 'audio':
#                        fp = open(attachment, 'rb')
#                        part = MIMEAudio(fp.read(), _subtype=subtype)
#                        fp.close()
#                    else:
#                        fp = open(attachment, 'rb')
#                        part = MIMEBase(maintype, subtype)
#                        part.set_payload(fp.read())
#                        fp.close()
#                        # Encode the payload using Base64
#                        encoders.encode_base64(part)
#                    # Set the filename parameter
#                    part.add_header('Content-Disposition', 'attachment', filename=attachment)
#                    message.attach(part)

            return self.ses.send_raw_email(self._source,
                                           message.as_string(),
                                           destinations=self._to_addresses)
예제 #20
0
class SesSender(object):
	logger = None

	def run(self):
		self.init_log()
		self.init_signer()
		try:
			self.log("Args: %r" % argv)
			self.log("Env: %r" % dict(environ))
			self.get_parties()
			self.get_credentials()
			self.make_connection()
			self.process_message()
			self.send_message()
		except Exception:
			self.abort('Unspecified error',1)

	def get_parties(self):
		try:
			self.sender = argv[1]
			self.recipients = argv[2:]
		except Exception:
			self.abort('Missing Sender / Recipients',2)

	def get_credentials(self):
		try:
			self.aws_id = environ['AWS_ACCESS_KEY_ID']
			self.aws_key = environ['AWS_SECRET_KEY']
			assert self.aws_id is not None
			assert self.aws_key is not None
		except Exception:
			self.abort('Missing AWS Credentials',3)

	def make_connection(self):
		try:
			self.conn = SESConnection(self.aws_id,self.aws_key)
		except Exception:
			self.abort('Failed to establish connection',4)

	def process_message(self):
		try:
			msg = stdin.read()
			assert msg[:4] == 'From'
			msg = self.sanitize_headers(msg)
			envelope,msg = msg.split('\n',1)
			self.msg = self.sign_message(msg)
			self.log('Sender: %r' % self.sender)
			self.log('Recipients: %r' % self.recipients)
			self.log('Message:\n' + msg)
		except Exception:
			self.abort('Failed to process message text',5)

	def sanitize_headers(self, msg):
		msg_obj = Parser().parsestr(msg)
		for hdr in msg_obj.keys():
			if hdr not in aws_allowed_headers and not hdr.startswith('X-'):
				del msg_obj[hdr]
		return str(msg_obj)

	def sign_message(self, msg):
		if self.dkim:
			return DKIM(msg).sign(self.dkim_selector,
				self.dkim_domain, self.dkim_private_key,
				canonicalize=('relaxed', 'simple'),
				include_headers=dkim_include_headers) + msg
		else:
			return msg

	def send_message(self):
		try:
			self.conn.send_raw_email(
				source=self.sender,
				raw_message=self.msg,
				destinations=self.recipients
			)
		except BotoServerError, bse:
			if 'InvalidTokenId' in bse.body:
				self.abort('Bad AWS Credentials (token)',6)
			if 'SignatureDoesNotMatch' in bse.body:
				self.abort('Bad AWS Credentials (signature)',6)
			if bse.error_code == 'Throttling':
				self.abort('Failed to actually deliver message: quota exceeded',9)
			self.abort('Failed to actually deliver message:',7)
		except Exception:
			self.abort('Failed to actually deliver message:',7)
예제 #21
0
class SESBackend(BaseEmailBackend):
    """A Django Email backend that uses Amazon's Simple Email Service.
    """
    def __init__(self,
                 fail_silently=False,
                 aws_access_key=None,
                 aws_secret_key=None,
                 aws_region_name=None,
                 aws_region_endpoint=None,
                 aws_auto_throttle=None,
                 dkim_domain=None,
                 dkim_key=None,
                 dkim_selector=None,
                 dkim_headers=None,
                 proxy=None,
                 proxy_port=None,
                 proxy_user=None,
                 proxy_pass=None,
                 **kwargs):

        super(SESBackend, self).__init__(fail_silently=fail_silently, **kwargs)
        self._access_key_id = aws_access_key or settings.ACCESS_KEY
        self._access_key = aws_secret_key or settings.SECRET_KEY
        self._region = RegionInfo(name=aws_region_name
                                  or settings.AWS_SES_REGION_NAME,
                                  endpoint=aws_region_endpoint
                                  or settings.AWS_SES_REGION_ENDPOINT)
        self._throttle = aws_auto_throttle or settings.AWS_SES_AUTO_THROTTLE
        self._proxy = proxy or settings.AWS_SES_PROXY
        self._proxy_port = proxy_port or settings.AWS_SES_PROXY_PORT
        self._proxy_user = proxy_user or settings.AWS_SES_PROXY_USER
        self._proxy_pass = proxy_pass or settings.AWS_SES_PROXY_PASS

        self.dkim_domain = dkim_domain or settings.DKIM_DOMAIN
        self.dkim_key = dkim_key or settings.DKIM_PRIVATE_KEY
        self.dkim_selector = dkim_selector or settings.DKIM_SELECTOR
        self.dkim_headers = dkim_headers or settings.DKIM_HEADERS

        self.connection = None

    def open(self):
        """Create a connection to the AWS API server. This can be reused for
        sending multiple emails.
        """
        if self.connection:
            return False

        try:
            self.connection = SESConnection(
                aws_access_key_id=self._access_key_id,
                aws_secret_access_key=self._access_key,
                region=self._region,
                proxy=self._proxy,
                proxy_port=self._proxy_port,
                proxy_user=self._proxy_user,
                proxy_pass=self._proxy_pass,
            )
        except Exception:
            if not self.fail_silently:
                raise

    def close(self):
        """Close any open HTTP connections to the API server.
        """
        try:
            self.connection.close()
            self.connection = None
        except Exception:
            if not self.fail_silently:
                raise

    def send_messages(self, email_messages):
        """Sends one or more EmailMessage objects and returns the number of
        email messages sent.
        """
        if not email_messages:
            return

        new_conn_created = self.open()
        if not self.connection:
            # Failed silently
            return

        num_sent = 0
        source = settings.AWS_SES_RETURN_PATH
        for message in email_messages:
            # SES Configuration sets. If the AWS_SES_CONFIGURATION_SET setting
            # is not None, append the appropriate header to the message so that
            # SES knows which configuration set it belongs to.
            #
            # If settings.AWS_SES_CONFIGURATION_SET is a callable, pass it the
            # message object and dkim settings and expect it to return a string
            # containing the SES Configuration Set name.
            if (settings.AWS_SES_CONFIGURATION_SET and
                    'X-SES-CONFIGURATION-SET' not in message.extra_headers):
                if callable(settings.AWS_SES_CONFIGURATION_SET):
                    message.extra_headers[
                        'X-SES-CONFIGURATION-SET'] = settings.AWS_SES_CONFIGURATION_SET(
                            message,
                            dkim_domain=self.dkim_domain,
                            dkim_key=self.dkim_key,
                            dkim_selector=self.dkim_selector,
                            dkim_headers=self.dkim_headers)
                else:
                    message.extra_headers[
                        'X-SES-CONFIGURATION-SET'] = settings.AWS_SES_CONFIGURATION_SET

            # Automatic throttling. Assumes that this is the only SES client
            # currently operating. The AWS_SES_AUTO_THROTTLE setting is a
            # factor to apply to the rate limit, with a default of 0.5 to stay
            # well below the actual SES throttle.
            # Set the setting to 0 or None to disable throttling.
            if self._throttle:
                global recent_send_times

                now = datetime.now()

                # Get and cache the current SES max-per-second rate limit
                # returned by the SES API.
                rate_limit = self.get_rate_limit()
                logger.debug(u"send_messages.throttle rate_limit='{}'".format(
                    rate_limit))

                # Prune from recent_send_times anything more than a few seconds
                # ago. Even though SES reports a maximum per-second, the way
                # they enforce the limit may not be on a one-second window.
                # To be safe, we use a two-second window (but allow 2 times the
                # rate limit) and then also have a default rate limit factor of
                # 0.5 so that we really limit the one-second amount in two
                # seconds.
                window = 2.0  # seconds
                window_start = now - timedelta(seconds=window)
                new_send_times = []
                for time in recent_send_times:
                    if time > window_start:
                        new_send_times.append(time)
                recent_send_times = new_send_times

                # If the number of recent send times in the last 1/_throttle
                # seconds exceeds the rate limit, add a delay.
                # Since I'm not sure how Amazon determines at exactly what
                # point to throttle, better be safe than sorry and let in, say,
                # half of the allowed rate.
                if len(new_send_times) > rate_limit * window * self._throttle:
                    # Sleep the remainder of the window period.
                    delta = now - new_send_times[0]
                    total_seconds = (delta.microseconds +
                                     (delta.seconds + delta.days * 24 * 3600) *
                                     10**6) / 10**6
                    delay = window - total_seconds
                    if delay > 0:
                        sleep(delay)

                recent_send_times.append(now)
                # end of throttling

            try:
                response = self.connection.send_raw_email(
                    source=source or message.from_email,
                    destinations=message.recipients(),
                    raw_message=dkim_sign(message.message().as_string(),
                                          dkim_key=self.dkim_key,
                                          dkim_domain=self.dkim_domain,
                                          dkim_selector=self.dkim_selector,
                                          dkim_headers=self.dkim_headers))
                message.extra_headers['status'] = 200
                message.extra_headers['message_id'] = response[
                    'SendRawEmailResponse']['SendRawEmailResult']['MessageId']
                message.extra_headers['request_id'] = response[
                    'SendRawEmailResponse']['ResponseMetadata']['RequestId']
                num_sent += 1
                if 'X-SES-CONFIGURATION-SET' in message.extra_headers:
                    logger.debug(
                        u"send_messages.sent from='{}' recipients='{}' message_id='{}' request_id='{}' ses-configuration-set='{}'"
                        .format(
                            message.from_email,
                            ", ".join(message.recipients()),
                            message.extra_headers['message_id'],
                            message.extra_headers['request_id'],
                            message.extra_headers['X-SES-CONFIGURATION-SET']))
                else:
                    logger.debug(
                        u"send_messages.sent from='{}' recipients='{}' message_id='{}' request_id='{}'"
                        .format(message.from_email,
                                ", ".join(message.recipients()),
                                message.extra_headers['message_id'],
                                message.extra_headers['request_id']))

            except SESConnection.ResponseError as err:
                # Store failure information so to post process it if required
                error_keys = [
                    'status', 'reason', 'body', 'request_id', 'error_code',
                    'error_message'
                ]
                for key in error_keys:
                    message.extra_headers[key] = getattr(err, key, None)
                if not self.fail_silently:
                    raise

        if new_conn_created:
            self.close()

        return num_sent

    def get_rate_limit(self):
        if self._access_key_id in cached_rate_limits:
            return cached_rate_limits[self._access_key_id]

        new_conn_created = self.open()
        if not self.connection:
            raise Exception(
                "No connection is available to check current SES rate limit.")
        try:
            quota_dict = self.connection.get_send_quota()
            max_per_second = quota_dict['GetSendQuotaResponse'][
                'GetSendQuotaResult']['MaxSendRate']
            ret = float(max_per_second)
            cached_rate_limits[self._access_key_id] = ret
            return ret
        finally:
            if new_conn_created:
                self.close()
예제 #22
0
class SESBackend(BaseEmailBackend):
    """A Django Email backend that uses Amazon's Simple Email Service.
    """

    def __init__(self, fail_silently=False, *args, **kwargs):
        super(SESBackend, self).__init__(fail_silently=fail_silently, *args,
                                         **kwargs)

        self._access_key_id = getattr(settings, 'AWS_ACCESS_KEY_ID', None)
        self._access_key = getattr(settings, 'AWS_SECRET_ACCESS_KEY', None)
        self._api_endpoint = getattr(settings, 'AWS_SES_API_HOST',
                                     SESConnection.DefaultHost)

        self.connection = None
        self._lock = threading.RLock()

    def open(self):
        """Create a connection to the AWS API server. This can be reused for
        sending multiple emails.
        """
        if self.connection:
            return False

        try:
            self.connection = SESConnection(
                aws_access_key_id=self._access_key_id,
                aws_secret_access_key=self._access_key,
                host=self._api_endpoint,
            )
        except:
            if not self.fail_silently:
                raise

    def close(self):
        """Close any open HTTP connections to the API server.
        """
        try:
            self.connection.close()
            self.connection = None
        except:
            if not self.fail_silently:
                raise

    def dkim_sign(self, message):
        # Courtesy of http://djangosnippets.org/snippets/1995/
        raw_message = message.message().as_string()
        if settings.DKIM_PRIVATE_KEY:
            included_headers = ["Content-Type", "MIME-Version", "Content-Transfer-Encoding", "Subject", "From", "To"]
            dkim_header = dkim.sign(raw_message, settings.DKIM_SELECTOR, settings.DKIM_DOMAIN, settings.DKIM_PRIVATE_KEY, include_headers=included_headers)
            raw_message = dkim_header + raw_message
        return raw_message

    def send_messages(self, email_messages):
        """Sends one or more EmailMessage objects and returns the number of
        email messages sent.
        """
        if not email_messages:
            return

        self._lock.acquire()
        try:
            new_conn_created = self.open()
            if not self.connection:
                # Failed silently
                return

            num_sent = 0
            for message in email_messages:
                raw_message = self.dkim_sign(message)
                try:
                    self.connection.send_raw_email(
                        source=message.from_email,
                        destinations=message.recipients(),
                        raw_message=raw_message,
                    )
                    num_sent += 1
                except SESConnection.ResponseError:
                    if not self.fail_silently:
                        raise
                    pass

            if new_conn_created:
                self.close()

        finally:
            self._lock.release()
        return num_sent