When sending mail, Inbox tries to be a good citizen to the modern world.
This means everything we send is either ASCII or UTF-8.
That means no Latin-1 or ISO-8859-1.
All headers are converted to ASCII and if that doesn't work, UTF-8.
Note that plain text that's UTF-8 will be sent as base64. i.e.:
Content-Type: text/text; charset='utf-8'
Content-Transfer-Encoding: base64
This is because not all servers support 8BIT and so flanker drops to b64.
import uuid
import pkg_resources
from collections import namedtuple
from flanker import mime
from flanker.addresslib import address
from html2text import html2text
VERSION = pkg_resources.get_distribution('inbox').version
SenderInfo = namedtuple('SenderInfo', 'name email')
ReplyToMessage = namedtuple('ReplyToMessage',
                            'thread_id subject message_id references body')
def create_email(sender_info, recipients, subject, html, attachments):
    Creates a MIME email message (both body and sets the needed headers).
    sender_info : SenderInfo(name, email)
    recipients: Recipients(to, cc, bcc) namedtuple
        to, cc, bcc are a lists of utf-8 encoded strings or None.
    subject : string
        a utf-8 encoded string
    html : string
        a utf-8 encoded string
    attachments: list of dicts, optional
        a list of dicts(filename, data, content_type)
    full_name = sender_info.name if sender_info.name else ''
    email_address = sender_info.email
    to = address.parse_list(recipients.to)
    cc = address.parse_list(recipients.cc)
    bcc = address.parse_list(recipients.bcc)
    plaintext = html2text(html)
    # Create a multipart/alternative message
    msg = mime.create.multipart('alternative')
        mime.create.text('text', plaintext),
        mime.create.text('html', html))
    # Create an outer multipart/mixed message
    if attachments:
        text_msg = msg
        msg = mime.create.multipart('mixed')
        # The first part is the multipart/alternative text part
        # The subsequent parts are the attachment parts
        for a in attachments:
            # Disposition should be inline if we add Content-ID
    msg.headers['Subject'] = subject
    # Gmail sets the From: header to the default sending account. We can
    # however set our own custom phrase i.e. the name that appears next to the
    # email address (useful if the user has multiple aliases and wants to
    # specify which to send as), see: http://lee-phillips.org/gmailRewriting/
    # For other providers, we simply use full_name = ''
    from_addr = u'"{0}" <{1}>'.format(full_name, email_address)
    from_addr = address.parse(from_addr)
    msg.headers['From'] = from_addr.full_spec()
    # Need to set these headers so recipients know we sent the email to them:
    # Note also that the To: header has different semantics than the envelope
    # recipient. For example, you can use '"Tony Meyer" <tony.meyer@gmail.com>'
    # as an address in the To: header, but the envelope recipient must be only
    # 'tony.meyer@gmail.com'.
    msg.headers['To'] = u', '.join([addr.full_spec() for addr in to])
    msg.headers['Cc'] = u', '.join([addr.full_spec() for addr in cc])
    msg.headers['Bcc'] = u', '.join([addr.full_spec() for addr in bcc])
    return msg
def add_inbox_headers(msg):
    Set a custom `X-INBOX-ID` header so as to identify messages generated by
    The header is set to a unique id generated randomly per message,
    and is needed for the correct reconciliation of sent messages on
    future syncs.
    We use uuid.uuid4().hex to generate the UUID as a 32-character hexadecimal
    string (random, does not compromise privacy).
    # Set our own custom header for tracking in `Sent Mail` folder
    msg.headers['X-INBOX-ID'] = str(uuid.uuid4().hex)
    # Potentially also use `X-Mailer`
    msg.headers['User-Agent'] = 'Inbox/{0}'.format(VERSION)
def add_reply_headers(replyto, msg):
    """ Add reply specific headers. """
    # Set the In-Reply-To header of the reply:
    msg.headers['In-Reply-To'] = replyto.message_id
    # Set the 'References' header of the reply:
    separator = '\t'
    msg.headers['References'] = replyto.references + separator +\
    return msg
def rfc_transform(msg):
    """ Create an RFC-2821 compliant SMTP message. """
    msgstring = msg.to_string()
    start = msgstring.find('References: ')
    if start == -1:
        return msgstring
    end = msgstring.find('\r\n', start + len('References: '))
    substring = msgstring[start:end]
    separator = '\n\t'
    rfcmsg = msgstring[:start] + substring.replace('\t', separator) +\
    return rfcmsg