Example #1
0
def send_mail(name, address, email):
    SERVER = "in-v3.mailjet.com"
    SERVER_PORT = "587"
    SUBJECT = "Purchase Summary"
    FROM = "*****@*****.**"
    PASSWD = "0459523f496bf2875f91890bf63b0e31"
    USER = "******"
    TO = email
    BODY = getBody(name, address)
    msg = MIMEMultipart()
    msg['Subject'] = SUBJECT
    msg['From'] = FROM
    msg['To'] = TO
    fp = open('reports/purchase_report.csv', 'rb')
    adj = MIMEBase('multipart', 'encrypted')
    adj.set_payload(fp.read())
    fp.close()
    encoders.encode_quopri(adj)
    adj.add_header('Content-Disposition',
                   'attachment',
                   filename='purchase_report.csv')
    msg.attach(MIMEText(BODY, 'html'))
    msg.attach(adj)
    TEXT = msg.as_string()
    server = smtplib.SMTP(SERVER, SERVER_PORT)
    server.starttls()
    server.login(USER, PASSWD)
    server.sendmail(FROM, TO, TEXT)
    server.quit()
Example #2
0
    def _multipart(self, depth, atchs, encts):

        base_msg = MIMEMultipart()

        for atch_id, encode_type in zip(atchs, encts):
            ctypes, encoding = mimetypes.guess_type(
                self.cfg[ATCH][atch_id]['file'])
            if not ctypes or encoding:
                ctypes = 'application/octet-stream'
            maintype, subtype = ctypes.split('/', 1)

            attach = MIMEBase(maintype, subtype)
            attach.set_payload(self.atch_files[atch_id])

            if encode_type == 'base64':
                encoders.encode_base64(attach)
            elif encode_type == 'qp':
                encoders.encode_quopri(attach)
            else:
                encoders.encode_7or8bit(attach)
            attach.add_header('Content-Disposition',
                              'attachment',
                              filename=self.cfg[ATCH][atch_id]['file'])
            base_msg.attach(attach)

        outer = []
        outer.append(base_msg)
        for i in range(depth):
            outer.append(MIMEMultipart())
            outer[-1].attach(outer[-2])
        return outer[-1]
Example #3
0
def set_part_content(part, content):
    # Reset old encoding and use quoted-printable (#5414)
    del part['Content-Transfer-Encoding']
    part.set_payload(content)
    encode_quopri(part)

    return True
Example #4
0
def check_html(msg,savname=None):
  "Remove scripts from HTML attachments."
  msgtype = msg.get_content_type().lower()
  # check for more MSIE braindamage
  if msgtype == 'application/octet-stream':
    for (attr,name) in msg.getnames():
      if name and name.lower().endswith(".htm"):
        msgtype = 'text/html'
  if msgtype == 'text/html':
    out = StringIO()
    htmlfilter = HTMLScriptFilter(out)
    try:
      htmlfilter.write(msg.get_payload(decode=True).decode())
      htmlfilter.close()
    #except sgmllib.SGMLParseError:
    except:
      mimetools.copyliteral(msg.get_payload(),open('debug.out','wb'))
      htmlfilter.close()
      hostname = socket.gethostname()
      msg.set_payload(
  "An HTML attachment could not be parsed.  The original is saved as '%s:%s'"
      % (hostname,savname))
      del msg["content-type"]
      del msg["content-disposition"]
      del msg["content-transfer-encoding"]
      name = "WARNING.TXT"
      msg["Content-Type"] = "text/plain; name="+name
      return Milter.CONTINUE
    if htmlfilter.modified:
      msg.set_payload(out)	# remove embedded scripts
      del msg["content-transfer-encoding"]
      encoders.encode_quopri(msg)
  return Milter.CONTINUE
Example #5
0
def to_message(mail):
    """
    Given a MailBase message, this will construct a MIMEPart
    that is canonicalized for use with the Python email API.

    N.B. this changes the original email.message.Message
    """
    ctype, params = mail.content_encoding['Content-Type']
    if not ctype:
        if mail.parts:
            ctype = 'multipart/mixed'
        else:
            ctype = 'text/plain'
    else:
        if mail.parts:
            assert ctype.startswith("multipart") or ctype.startswith(
                "message"
            ), "Content type should be multipart or message, not %r" % ctype

    # adjust the content type according to what it should be now
    mail.content_encoding['Content-Type'] = (ctype, params)

    try:
        out = MIMEPart(ctype, **params)
    except TypeError as exc:
        raise EncodingError(
            "Content-Type malformed, not allowed: %r; %r (Python ERROR: %s" %
            (ctype, params, getattr(exc, "message", "(No error message)")))

    for k in mail.keys():
        if k in ADDRESS_HEADERS_WHITELIST:
            value = header_to_mime_encoding(mail[k])
        else:
            value = header_to_mime_encoding(mail[k], not_email=True)

        if k.lower() in [key.lower() for key in CONTENT_ENCODING_KEYS]:
            del out[k]
            out[k] = value
        else:
            out[k] = value

    out.extract_payload(mail)

    # make sure payload respects cte
    cte, cte_params = mail.content_encoding['Content-Transfer-Encoding']
    if cte == "quoted-printable":
        del out['Content-Transfer-Encoding']
        encoders.encode_quopri(out)
    elif cte == "base64":
        del out['Content-Transfer-Encoding']
        encoders.encode_base64(out)

    # go through the children
    for part in mail.parts:
        out.attach(to_message(part))

    return out
Example #6
0
def to_message(mail):
    """
    Given a MailBase message, this will construct a MIMEPart
    that is canonicalized for use with the Python email API.

    N.B. this changes the original email.message.Message
    """
    ctype, params = mail.content_encoding['Content-Type']
    if not ctype:
        if mail.parts:
            ctype = 'multipart/mixed'
        else:
            ctype = 'text/plain'
    else:
        if mail.parts:
            assert ctype.startswith("multipart") or ctype.startswith("message"), \
                    "Content type should be multipart or message, not %r" % ctype

    # adjust the content type according to what it should be now
    mail.content_encoding['Content-Type'] = (ctype, params)

    try:
        out = MIMEPart(ctype, **params)
    except TypeError as exc:
        raise EncodingError("Content-Type malformed, not allowed: %r; %r (Python ERROR: %s" %
                            (ctype, params, getattr(exc, "message", "(No error message)")))

    for k in mail.keys():
        if k in ADDRESS_HEADERS_WHITELIST:
            value = header_to_mime_encoding(mail[k])
        else:
            value = header_to_mime_encoding(mail[k], not_email=True)

        if k.lower() in [key.lower() for key in CONTENT_ENCODING_KEYS]:
            del out[k]
            out[k] = value
        else:
            out[k] = value

    out.extract_payload(mail)

    # make sure payload respects cte
    cte, cte_params = mail.content_encoding['Content-Transfer-Encoding']
    if cte == "quoted-printable":
        del out['Content-Transfer-Encoding']
        encoders.encode_quopri(out)
    elif cte == "base64":
        del out['Content-Transfer-Encoding']
        encoders.encode_base64(out)

    # go through the children
    for part in mail.parts:
        out.attach(to_message(part))

    return out
Example #7
0
 def fix_encoding(msg):
     cte = 'Content-Transfer-Encoding'
     if msg[cte] == '8bit' and msg.get_content_maintype()=='text' and msg.get_charset() is None:
         charset = msg.get_content_charset()
         if not charset: return #broken
         payload = msg.get_payload(decode=False)
         msg.set_payload(payload)
         msg.set_charset(charset)
     if not sevenbit: return
     try: msg.get_payload().encode('ascii')
     except UnicodeError: encode_quopri(msg)
     else:
         if not cte in msg: msg.add_header(cte,'7bit')
def re_mime_encode(crypto_message):
    '''
        Re-encode message if it was encoded with base64 or quoted printable.

        >>> from goodcrypto.mail.message.crypto_message import CryptoMessage
        >>> from goodcrypto.mail.message.email_message import EmailMessage
        >>> from goodcrypto_tests.mail.message_utils import get_plain_message_name
        >>> with open(get_plain_message_name('basic.txt')) as input_file:
        ...    crypto_message = CryptoMessage(email_message=EmailMessage(input_file))
        ...    re_mime_encode(crypto_message)
        False
    '''
    decoded = re_encoded = False
    message = crypto_message.get_email_message().get_message()
    try:
        encoding = message.__getitem__(
            mime_constants.CONTENT_XFER_ENCODING_KEYWORD)
    except Exception:
        encoding = None

    if encoding is not None:
        encoding = encoding.lower()

        # only use the encoding if it's not a multipart message
        if (encoding == mime_constants.QUOTED_PRINTABLE_ENCODING
                or encoding == mime_constants.BASE64_ENCODING):
            current_content_type = message.get_content_type()
            if (current_content_type is not None
                    and current_content_type.lower().find(
                        mime_constants.MULTIPART_PRIMARY_TYPE) < 0):
                decoded = True
                log_message('payload decoded with {}'.format(encoding))

        if decoded:
            if DEBUGGING:
                log_message('decoded message:\n{}'.format(
                    crypto_message.get_email_message().get_message()))
            if encoding == mime_constants.QUOTED_PRINTABLE_ENCODING:
                encode_quopri(message)
                re_encoded = True
            elif encoding == mime_constants.BASE64_ENCODING:
                encode_base64(message)
                re_encoded = True
            crypto_message.get_email_message().set_message(message)
            log_message('payload re-encoded with {}'.format(encoding))
            if DEBUGGING:
                log_message('encoded message:\n{}'.format(
                    crypto_message.get_email_message().get_message()))

    return re_encoded
Example #9
0
    def _form_email(self, recipient_data):
        """
        Form the html email, including mimetype and headers.
        """

        # instatiate the email object and assign headers
        email_message = MIMEMultipart('related')
        email_message.preamble = 'This is a multi-part message in MIME format.'

        if self.txt_path != "":
            txt = MIMEText(PyMailer._prepare_text(self.txt_path,
                                                  recipient_data),
                           'plain',
                           _charset='utf-8')
            # encoders.encode_quopri(txt)
            email_message.attach(txt)

        if self.html_path != "":
            html = MIMEText(PyMailer._prepare_text(self.html_path,
                                                   recipient_data),
                            'html',
                            _charset='utf-8')
            # encoders.encode_quopri(html)
            email_message.attach(html)

        for image in self.images:
            with open(image, 'rb') as f:
                imageMime = MIMEImage(f.read())
            imageMime.add_header('Content-ID', '<%s>' % image)
            email_message.attach(imageMime)

        for attachment in self.attachments:
            with open(attachment[1], 'rb') as f:
                attachmentMime = MIMENonMultipart(attachment[0].split('/')[0],
                                                  attachment[0].split('/')[1])
                attachmentMime.set_payload(f.read())
                if attachment[0].split('/')[0] == 'text':
                    encoders.encode_quopri(attachmentMime)
                attachmentMime.add_header(
                    'Content-Disposition',
                    'attachment; filename=%s' % attachment[1])
                email_message.attach(attachmentMime)

        email_message['From'] = recipient_data.get('sender')
        email_message['To'] = recipient_data.get('recipient')
        email_message['Subject'] = self.subject

        return email_message.as_string()
def re_mime_encode(crypto_message):
    '''
        Re-encode message if it was encoded with base64 or quoted printable.

        >>> from goodcrypto.mail.message.crypto_message import CryptoMessage
        >>> from goodcrypto.mail.message.email_message import EmailMessage
        >>> from goodcrypto_tests.mail.message_utils import get_plain_message_name
        >>> with open(get_plain_message_name('basic.txt')) as input_file:
        ...    crypto_message = CryptoMessage(email_message=EmailMessage(input_file))
        ...    re_mime_encode(crypto_message)
        False
    '''
    decoded = re_encoded = False
    message = crypto_message.get_email_message().get_message()
    try:
        encoding = message.__getitem__(mime_constants.CONTENT_XFER_ENCODING_KEYWORD)
    except Exception:
        encoding = None

    if encoding is not None:
        encoding = encoding.lower()

        # only use the encoding if it's not a multipart message
        if (encoding == mime_constants.QUOTED_PRINTABLE_ENCODING or
            encoding == mime_constants.BASE64_ENCODING):
            current_content_type = message.get_content_type()
            if (current_content_type is not None and
                current_content_type.lower().find(mime_constants.MULTIPART_PRIMARY_TYPE) < 0):
                decoded = True
                log_message('payload decoded with {}'.format(encoding))

        if decoded:
            if DEBUGGING:
                log_message('decoded message:\n{}'.format(
                    crypto_message.get_email_message().get_message()))
            if encoding == mime_constants.QUOTED_PRINTABLE_ENCODING:
                encode_quopri(message)
                re_encoded = True
            elif encoding == mime_constants.BASE64_ENCODING:
                encode_base64(message)
                re_encoded = True
            crypto_message.get_email_message().set_message(message)
            log_message('payload re-encoded with {}'.format(encoding))
            if DEBUGGING:
                log_message('encoded message:\n{}'.format(
                    crypto_message.get_email_message().get_message()))

    return re_encoded
Example #11
0
def _prepare_message(txt):
    """ Apply the proper encoding to all email fields.
        The body will be Latin-1, the headers will be 'quopri'd when necessary.
    """
    def plain(val):
        """ Return True when val is plain ASCII """
        try:
            val.decode('ascii')
            return True
        except:
            return False

    # Use Latin-1 because not all email clients know UTF-8.
    code = 'ISO-8859-1'

    msg = Message()
    payload = []
    body = False
    header = False
    for line in txt.encode(code, 'replace').split('\n'):
        if header and not line:
            body = True
        if body:
            payload.append(line)
        else:
            m = RE_HEADER.search(line)
            if m:
                header = True
                keyword = m.group(1).strip()
                value = m.group(2).strip()
                if plain(value):
                    # Don't encode if not needed, because some email clients
                    # choke when headers like "date" are encoded.
                    msg.add_header(keyword, value)
                else:
                    header = Header(value, code)
                    msg[keyword] = header

    msg.set_payload('\n'.join(payload), code)

    # Check for proper encoding, else call it explicitly
    if not msg.has_key('Content-Transfer-Encoding'):
        encode_quopri(msg)

    return msg.as_string()
Example #12
0
def _prepare_message(txt):
    """ Apply the proper encoding to all email fields.
        The body will be Latin-1, the headers will be 'quopri'd when necessary.
    """
    def plain(val):
        """ Return True when val is plain ASCII """
        try:
            val.decode('ascii')
            return True
        except:
            return False

    # Use Latin-1 because not all email clients know UTF-8.
    code = 'ISO-8859-1'

    msg = Message()
    payload = []
    body = False
    header = False
    for line in txt.encode(code, 'replace').split('\n'):
        if header and not line:
            body = True
        if body:
            payload.append(line)
        else:
            m = RE_HEADER.search(line)
            if m:
                header = True
                keyword = m.group(1).strip()
                value = m.group(2).strip()
                if plain(value):
                    # Don't encode if not needed, because some email clients
                    # choke when headers like "date" are encoded.
                    msg.add_header(keyword, value)
                else:
                    header = Header(value, code)
                    msg[keyword] = header

    msg.set_payload('\n'.join(payload), code)

    # Check for proper encoding, else call it explicitly
    if not msg.has_key('Content-Transfer-Encoding'):
        encode_quopri(msg)

    return msg.as_string()
Example #13
0
    def send(self, msg_body, mto, mfrom=None, mbcc=None, reply_to=None, subject=None,
             encode=None, immediate=False, charset=None, msg_type=None):
        """Sends a message defined at least by its message body and the
        destination address.
        """
        mhost = self.context.MailHost

        try:
            # Plone 4
            msg = message_from_string(msg_body.encode(charset))
            if encode is None or encode in ["quoted-printable", "qp"]:
                encode_quopri(msg)
            else:
                encode_base64(msg)

            msg['BCC']= Header(mbcc)
            if reply_to:
                msg['Reply-To'] = Header(reply_to)

            mhost.send(msg,
                         mto=mto,
                         mfrom=mfrom,
                         subject=subject,
                         encode=encode,
                         immediate=immediate,
                         msg_type=msg_type,
                         charset=charset)
        except (TypeError, NameError):
            # Plone 3 or earlier
            subtype = msg_type.split('/')[1]

            mhost.secureSend(msg_body,
                             mto=mto,
                             mfrom=mfrom,
                             subject=subject,
                             mbcc=mbcc,
                             subtype=subtype,
                             charset=charset)

        except (MailHostError, socket.error), e:
            logger.error("sending mail with subject %s to %s failed: %s."
                         % (subject, mto, str(e)))
Example #14
0
def _build_mail_to_encrypt(message: str, files: list) -> MIMEMultipart:
    """
    Create the MIMEMultipart mail containing the text message and the potentials attachments.

    :param message: The text message of the encrypted email.
    :param files: The files to attach and encrypt.
    :return: A MIMEMultipart mail object.
    """
    mail_to_encrypt = MIMEMultipart()
    mail_to_encrypt.policy = policy.SMTPUTF8
    if message == '--':
        message = sys.stdin.read()

    message_mail = MIMEBase('text', 'plain', charset='UTF-8')
    message_mail.policy = policy.SMTPUTF8
    message_mail.set_payload(message.encode('UTF-8'))
    encoders.encode_quopri(message_mail)
    mail_to_encrypt.attach(message_mail)

    if files:
        for file in files:
            path = Path(file)

            guessed_type = mimetypes.guess_type(path.absolute().as_uri())[0]

            if not guessed_type:
                print('Could not guess file %s mime-type, using application/octet-stream.' % file, file=sys.stderr)
                guessed_type = 'application/octet-stream'

            mimetype = guessed_type.split('/')

            mail_attachment = MIMEBase(mimetype[0], mimetype[1])
            mail_attachment.policy = policy.SMTPUTF8
            mail_attachment.set_payload(open(str(path.absolute()), 'rb').read())
            encoders.encode_base64(mail_attachment)
            mail_attachment.add_header('Content-Disposition', "attachment", filename=path.name)

            mail_to_encrypt.attach(mail_attachment)

    return mail_to_encrypt
Example #15
0
    def send(self, message, page=None, name=None, email=None, honeypot=None):
        # Detect if a spambot has just filled all form fields.
        if honeypot is not None:
            self.redirect(self.url('feedback-accepted-honeypot'))
            return

        page = page or u""
        name = name or u"UNKNOWN"
        email = email or u"EMPTY-EMAIL"
        message_body = self.mail_template % dict(
            message=message,
            page=page,
            name=name,
            email=email,
            root=self.application_url(),
        )

        message = email_message.Message()
        message.set_payload(message_body.encode('utf-8'))

        subject_header = email_header.Header(
            (u'Assembly Archive feedback about "%s"' % page).encode('utf-8'),
            'utf-8')
        message['Subject'] = subject_header

        message['To'] = self.target_address

        from_header = email_header.Header(
            (u'%s <%s>' % (name, email)).encode('utf-8'), 'utf-8')
        message['From'] = from_header

        email_encoders.encode_quopri(message)
        message.set_charset('utf-8')
        message_str = message.as_string()
        smtp = smtplib.SMTP(self.smtp_host)
        smtp.sendmail(self.target_address, [self.target_address], message_str)
        self.flash(u'Your feedback was accepted.')
        self.redirect(self.url('feedback-accepted'))
Example #16
0
				<li><b>Resolves to:</b> {RESOLV}</li>
				<li><b>Time of occurance:</b> {TIME}</li>
				<li><b>Environment Variable:</b> {ENVIRON}</li>
				<li><b>Parameters:</b> {PARAMS}</li>
				<li><b>Logged On Users:</b> {USERS}</li>
			</ul>
		</div>
	</body>
</html>""".format(IP=IP, RESOLV=RESOLV, TIME=time(), ENVIRON=os.environ, PARAMS=argv, USERS=psutil.users(), SUBJECT="SSH Login: {}@{}".format(USER, DOMAIN))

email_body_text = MIMEText(text, 'plain')

#email_body_html = MIMEText(html, 'html')
email_body_html = MIMEBase('text', 'html')
email_body_html.set_payload(html)
encoders.encode_quopri(email_body_html)
email_body_html.set_charset('UTF-8')

email.attach(email_body_text)
email.attach(email_body_html)

if not PROXY_MAIL:
	for mx_record in dns.resolver.query(TO_DOMAIN, 'MX'):
		mail_server = mx_record.to_text().split()[1][:-1]
		try:
			server = smtplib.SMTP(mail_server)
			server.sendmail(FROM, '{}@{}'.format(TO), email.as_string())
			server.close()
			break
		except Exception as e:
			log.warning("Could not notify our chief of command @ {}!!".format(mail_server))
Example #17
0
            value = header_to_mime_encoding(mail[k])
        else:
            value = header_to_mime_encoding(mail[k], not_email=True)

        if k.lower() in [key.lower() for key in CONTENT_ENCODING_KEYS]:
            del out[k]
            out[k] = value
        else:
            out[k.encode('ascii')] = value

    out.extract_payload(mail)

    # make sure payload respects cte
    cte, cte_params = mail.content_encoding['Content-Transfer-Encoding']
    if cte == "quoted-printable":
        encoders.encode_quopri(out)
    elif cte == "base64":
        encoders.encode_base64(out)

    # go through the children
    for part in mail.parts:
        out.attach(to_message(part))

    return out


def to_string(mail, envelope_header=False):
    """Returns a canonicalized email string you can use to send or store
    somewhere."""
    msg = to_message(mail).as_string(envelope_header)
    assert "From nobody" not in msg
Example #18
0
    def _decrypt_inline_pgp(self, from_user, crypto, passcode):
        '''
            Decrypt an inline PGP message (internal use only).
        '''
        def adjust_attachment_name(part):
            '''Adjust the filename for the attachment.'''

            try:
                filename = part.get_filename()
                if filename and filename.endswith('.pgp'):
                    self.log_message(
                        'original attachment filename: {}'.format(filename))
                    end = len(filename) - len('.pgp')
                    part.replace_header(
                        mime_constants.CONTENT_DISPOSITION_KEYWORD,
                        'attachment; filename="{}"'.format(filename[:end]))
                    filename = part.__getitem__(
                        mime_constants.CONTENT_DISPOSITION_KEYWORD)
                    self.log_message(
                        'new attachment filename: {}'.format(filename))
                else:
                    self.log_message(
                        'attachment filename: {}'.format(filename))
            except:
                record_exception()
                self.log_message(
                    'EXCEPTION - see syr.exception.log for details')

        decrypted = False

        self.log_message("message is inline PGP format")
        message = self.crypto_message.get_email_message().get_message()
        self.log_message("message content type is {}".format(
            message.get_content_type()))

        # remove any clear signed section before decrypting message
        self.crypto_message.get_email_message().remove_pgp_signature_blocks()

        if message.is_multipart():
            for part in message.get_payload():
                content_type = part.get_content_type()
                ciphertext = part.get_payload(decode=True)
                charset, __ = get_charset(part)
                self.log_message(
                    'multipart message char set: {}'.format(charset))
                plaintext = self._decrypt(from_user, ciphertext, charset,
                                          crypto, passcode)
                if plaintext is not None and plaintext != ciphertext:
                    decrypted = True

                    charset, __ = part.get_charset()
                    self.log_message(
                        "message part charset is {}".format(charset))
                    part.set_payload(plaintext, charset=charset)

                    try:
                        content_keyword_in_part = part.__getitem__(
                            mime_constants.CONTENT_DISPOSITION_KEYWORD)
                    except Exception:
                        content_keyword_in_part = None
                    if content_keyword_in_part is not None:
                        adjust_attachment_name(part)

                    try:
                        encoding = part.__getitem__(
                            mime_constants.CONTENT_XFER_ENCODING_KEYWORD)
                    except Exception:
                        encoding = None
                    if encoding == mime_constants.QUOTED_PRINTABLE_ENCODING:
                        encode_quopri(part)
                        self.log_message(
                            "{} encoded message part".format(encoding))
                    elif encoding == mime_constants.BASE64_ENCODING:
                        encode_base64(part)
                        self.log_message(
                            "{} encoded message part".format(encoding))
        else:
            charset, __ = get_charset(self.crypto_message.get_email_message())
            self.log_message('inline pgp char set: {}'.format(charset))
            ciphertext = self.crypto_message.get_email_message().get_content()
            plaintext = self._decrypt(from_user, ciphertext, charset, crypto,
                                      passcode)

            if plaintext is None or ciphertext is None or plaintext == ciphertext:
                decrypted = False
                self.log_message("unable to decrypt {} message".format(
                    message.get_content_type()))
            else:
                # do not specify the codec
                self.crypto_message.get_email_message().get_message(
                ).set_payload(plaintext)
                try:
                    encoding = self.crypto_message.get_email_message(
                    ).get_message().__getitem__(
                        mime_constants.CONTENT_XFER_ENCODING_KEYWORD)
                except Exception:
                    encoding = None
                if encoding == mime_constants.QUOTED_PRINTABLE_ENCODING:
                    encode_quopri(
                        self.crypto_message.get_email_message().get_message())
                elif encoding == mime_constants.BASE64_ENCODING:
                    encode_base64(
                        self.crypto_message.get_email_message().get_message())
                decrypted = True

        return decrypted
Example #19
0
    def construct_and_send_email(self):
        """Do work of constructing & sending reply email. For internal use."""
        # who to respond to?
        to_email = None
        if 'Reply-To' in self.message:
            to_email = self.message['Reply-To']
        elif 'From' in self.message:
            to_email = self.message['From']
        if not to_email:
            log.warn('Cannot decide who to respond to')
            return # XXX throw exception instead

        # what the response subject should be
        if 'Subject' in self.message:
            if self.message['Subject'][:4] != 'Re: ':
                subject = 'Re: '+self.message['Subject']
            else:
                subject = self.message['Subject']
        else:
            subject = 'CryptoBot response'

        from_email = '{0} <{1}>'.format(config.pgp_name, config.pgp_email)

        # start the email
        msg = MIMEMultipart('mixed')
        body = MIMEMultipart('alternative')

        template_vars = {
            'encrypted_right': self.message.encrypted_right,
            'encrypted_wrong': self.message.encrypted_wrong,
            'signed': self.message.signed,
            'pubkey_included': self.message.pubkey_included,
            'pubkey_included_wrong': self.message.pubkey_included_wrong,
            'pubkey_fingerprint': self.message.pubkey_fingerprint
        }

        body_txt = self.txt_template.render(template_vars)
        txt_part = MIMEBase('text', 'plain')
        txt_part.set_payload(body_txt)
        encode_quopri(txt_part)
        body.attach(txt_part)

        body_html = self.html_template.render(template_vars)
        html_part = MIMEBase('text', 'html')
        html_part.set_payload(body_html)
        encode_quopri(html_part)

        body.attach(html_part)
        msg.attach(body)

        # if the message is not encrypted, attach public key (#16)
        if not self.message.encrypted_right:
            pubkey = str(self._gpg.export_keys(self.fp))
            pubkey_filename = '{0} {1} (0x{2}) pub.asc'.format(config.pgp_name, config.pgp_email, str(self.fp)[:-8])

            pubkey_part = MIMEBase('application', 'pgp-keys')
            pubkey_part.add_header('Content-Disposition', 'attachment; filename="%s"' % pubkey_filename)
            pubkey_part.set_payload(pubkey)
            encode_quopri(pubkey_part)
            msg.attach(pubkey_part)

        # sign the message
        msg_string = (self.as_string(msg)+'\n').replace('\n', '\r\n')
        sig = self._gpg.sign(msg_string)

        # make a sig part
        sig_part = MIMEBase('application', 'pgp-signature', name='signature.asc')
        sig_part.add_header('Content-Description', 'OpenPGP digital signature')
        sig_part.add_header('Content-Disposition', 'attachment; filename="signature.asc"')
        sig_part.set_payload(sig)

        # wrap it all up in multipart/signed
        signed = MIMEMultipart(_subtype="signed", micalg="pgp-sha1", protocol="application/pgp-signature")
        signed.attach(msg)
        signed.attach(sig_part)

        # if we're just signing and not encrypting this message, add the headers directly to the signed part
        if not self.message.pubkey_fingerprint:
            signed['Subject'] = subject
            signed['From'] = from_email
            signed['To'] = to_email

        # need to add a '\r\n' right before the sig part (#19)
        # because of this bug http://bugs.python.org/issue14983
        signed_string = self.as_string(signed)
        i = signed_string.rfind('--', 0, signed_string.find('Content-Type: application/pgp-signature; name="signature.asc"'))
        signed_string = signed_string[0:i]+'\r\n'+signed_string[i:]

        # if we have a fingerprint to encrypt to, encrypt it
        if self.message.pubkey_fingerprint:
            # encrypt the message
            ciphertext = self._gpg.encrypt(signed_string, self.message.pubkey_fingerprint)

            # make an application/pgp-encrypted part
            encrypted = MIMEBase("application", "pgp-encrypted")
            encrypted.set_payload("Version: 1\r\n")

            # make application/octet-stream part
            octet_stream = MIMEBase("application", "octet-stream")
            octet_stream.set_payload(ciphertext)

            # make the multipart/encrypted wrapper
            wrapper = MIMEMultipart(_subtype="encrypted", protocol="application/pgp-encrypted")
            wrapper.attach(encrypted)
            wrapper.attach(octet_stream)

            # add headers to the encryption wrapper
            wrapper['Subject'] = subject
            wrapper['From'] = from_email
            wrapper['To'] = to_email

            final_message = self.as_string(wrapper)
        else:
            final_message = signed_string

        self.send_email(final_message, from_email, to_email)
        log.info('Responded to %s: %r', self.message['From'], template_vars)
Example #20
0
            value = header_to_mime_encoding(mail[k])
        else:
            value = header_to_mime_encoding(mail[k], not_email=True)

        if k.lower() in [key.lower() for key in CONTENT_ENCODING_KEYS]:
            del out[k]
            out[k] = value
        else:
            out[k.encode('ascii')] = value

    out.extract_payload(mail)

    # make sure payload respects cte
    cte, cte_params = mail.content_encoding['Content-Transfer-Encoding']
    if cte == "quoted-printable":
        encoders.encode_quopri(out)
    elif cte == "base64":
        encoders.encode_base64(out)

    # go through the children
    for part in mail.parts:
        out.attach(to_message(part))

    return out


def to_string(mail, envelope_header=False):
    """Returns a canonicalized email string you can use to send or store
    somewhere."""
    msg = to_message(mail).as_string(envelope_header)
    assert "From nobody" not in msg
Example #21
0
    def Compile(self):
        """Check message for error and compile it"""
        # self._Message = Message()
        self._Message = MIMEMultipart()
        m = self._Message
        if len(self._Body) == 0:
            raise EmptyBody("Please set Body")
        if len(self._Recipients['To']) + len(self._Recipients['Cc']) + len(self._Recipients['Bcc']) == 0:
            raise EmptyAddr("Recipients address not set")
        # if len(self._AltBody) > 0:
        #     self.ContentType = "multipart/alternative"
        if len(self._MessageDate) != 0:
            m.add_header("Date", self._MessageDate)
        else:
            m.add_header("Date", time.strftime("%a, %d %b %Y %T %z"))
        if len(self._ReturnPath) != 0:
            m.add_header("Return-path", self._ReturnPath)
        elif len(self._Sender) != 0:
            m.add_header("Return-path", self._Sender)
        else:
            m.add_header("Return-path", self._From)
            self._Sender = self._From

        for rcpt in self._Recipients.keys():
            if len(self._Recipients[rcpt]) > 0:
                for each in self._Recipients[rcpt]:
                    if len(each['text']) > 0:
                        m.add_header(rcpt, '"%s" <%s>' % (str(Header(each['text'], 'utf-8')), each['email']))
                    else:
                        m.add_header(rcpt, '<%s>' % (each['email']))
        if m.get("To") in (None, ''):
            m.add_header("To", 'undisclosed-recipients:;')
        m.add_header("From", self._From)

        if self._ReplyTo != '':
            reply_to = self._ReplyTo
            if len(reply_to['text']) > 0:
                m.add_header("Reply-To", '<%s>' (reply_to['email']))
            else:
                m.add_header("Reply-To", '"%s" <%s>' % (str(Header(reply_to['text'], 'utf-8')), reply_to['email']))

        if len(self._MessageID) != 0:
            m.add_header("Message-ID", self._MessageID)
        else:
            m.add_header("Message-ID", '<%s@%s>' % (self.uniqid(), self._Hostname))

        m.add_header('X-Priority', str(self._Priority))
        m.add_header("X-Mailer", self._XMailer)
        m.add_header("Subject", str(Header(self._Subject, 'utf-8')))


        if len(self._AltBody) > 0:
            # alt_body = MIMEText(self._AltBody, )
            # alt_body.set_type(self._AltBodyType)
            alt_body = MIMEBase(self._AltBodyType.split('/')[0], self._AltBodyType.split('/')[1])
            alt_body.set_payload(self._AltBody)
            if self._Encoding == 'base64':
                encoders.encode_base64(alt_body)
            elif self._Encoding == 'quoted':
                encoders.encode_quopri(alt_body)                
            elif self._Encoding in ('8bit', '7bit'):
                encoders.encode_7or8bit(alt_body)
            alt_body.set_charset(self._CharSet)

            # body = MIMEText(self._Body)
            # body.set_type(self._BodyType)
            body = MIMEBase(self._BodyType.split('/')[0], self._BodyType.split('/')[1])
            body.set_payload(self._Body)
            if self._Encoding == 'base64':
                encoders.encode_base64(body)
            elif self._Encoding == 'quoted':
                encoders.encode_quopri(body)
            elif self._Encoding in ('8bit', '7bit'):
                encoders.encode_7or8bit(body)            
            body.set_charset(self._CharSet)

            m.attach(alt_body)
            m.attach(body)
        else:
            # body = MIMEText(self._Body)
            # body.set_type(self._BodyType)
            body = MIMEBase(self._BodyType.split('/')[0], self._BodyType.split('/')[1])
            body.set_payload(self._Body)
            encoders.encode_base64(body)
            body.set_charset(self._CharSet)
            m.attach(body)

        # m.set_charset(self._CharSet)
        m.set_type('multipart/alternative')
        
        self._isCompile = True
Example #22
0
    def send(self, emails):
        if isinstance(emails, Email):
            emails = [emails]
        if len([e for e in emails if e.__class__ != Email]):
            raise TypeError('emails must be Email or list of Email instances')

        smtpclass = SMTP_SSL if self.ssl else SMTP
        if self.server == 'localhost':
            smtp = smtpclass(self.server)
        else:
            smtp = smtpclass(self.server, self.port)
        if self.login and self.password:
            smtp.login(self.login, self.password)
        for email in emails:
            c = Charset(email.charset)
            c.header_encoding = QP
            c.body_encoding = 0
            r = Charset(email.charset)
            r.header_encoding = 0
            r.body_encoding = 0

            mime1, mime2 = email.mimetype.split('/')
            mainpart = MIMEBase(mime1, mime2)
            if not email.force_7bit:
                mainpart.set_param('charset', email.charset)

            if len(email.attachments):
                message = MIMEMultipart('mixed')
                message.attach(mainpart)
                del mainpart['mime-version']
            else:
                message = mainpart

            message['Date'] = datetime.datetime.now().strftime(
                '%a, %d %b %Y %H:%M:%S') + (" +%04d" % (time.timezone / -36, ))

            h = Header()
            fromname = self.fromname.encode(email.charset, 'xmlcharrefreplace')
            h.append(fromname, r if is7bit(fromname) else c)
            h.append('<%s>' % self.email, r)
            message['From'] = h

            message['To'] = email.get_emails_header('rcpt')
            if len(email.cc):
                message['CC'] = email.get_emails_header('cc')
            if len(email.bcc):
                message['BCC'] = email.get_emails_header('bcc')

            subject = email.subject.encode(email.charset, 'xmlcharrefreplace')
            message['Subject'] = Header(subject, r if is7bit(subject) else c)

            if email.force_7bit:
                body = email.body.encode('ascii', 'xmlcharrefreplace')
            else:
                body = email.body.encode(email.charset, 'xmlcharrefreplace')
            mainpart.set_payload(body)

            for hn, hv in email.headers.items():
                if hn == 'X-Mailgun-Campaign-Tag':
                    hn = 'X-Mailgun-Tag'
                message[hn] = hv

            if is7bit(body):
                mainpart['Content-Transfer-Encoding'] = '7bit'
            else:
                encode_quopri(mainpart)

            for attachment in email.attachments:
                if attachment.__class__ != Attachment:
                    raise TypeError("invalid attachment")

                mimetype = attachment.mimetype
                if not mimetype:
                    mimetype, encoding = guess_type(attachment.filename)
                    if not mimetype:
                        mimetype = 'application/octet-stream'
                mime1, mime2 = mimetype.split('/')
                part = MIMEBase(mime1, mime2)

                # using newer rfc2231 (not supported by Outlook):
                # part.set_param('name', attachment.filename.encode('utf-8'), charset = 'utf-8')

                # hack: using deprecated rfc2047 - supported by Outlook:
                part.set_param('name', str(Header(attachment.filename)))
                del part['mime-version']

                if attachment.id:
                    part['Content-Disposition'] = 'inline'
                else:
                    part['Content-Disposition'] = 'attachment'

                # using newer rfc2231 (not supported by Outlook):
                # part.set_param('filename',
                #                attachment.filename.encode('utf-8'),
                #                'Content-Disposition',
                #                charset = 'utf-8')

                # hack: using deprecated rfc2047 - supported by Outlook:
                part.set_param('filename', str(Header(attachment.filename)),
                               'Content-Disposition')

                if attachment.id:
                    part['Content-ID'] = '<%s>' % attachment.id

                part.set_payload(attachment.content)
                encode_base64(part)

                message.attach(part)

            emails = email.rcpt + email.cc + email.bcc
            smtp.sendmail(self.email, [email for _, email in emails],
                          message.as_string())

        smtp.quit()
Example #23
0
def email(configuration):
    if not 'TEXT_TEMPLATE' in configuration or configuration[
            'TEXT_TEMPLATE'] is None:
        return None
    if not 'HTML_TEMPLATE' in configuration or configuration[
            'HTML_TEMPLATE'] is None:
        return None
    log('Sending e-mail from <{SSH_MAIL_USER_FROM}@{SSH_MAIL_USER_FROMDOMAIN}> to <{SSH_MAIL_USER_TO}@{SSH_MAIL_USER_TODOMAIN}>'
        .format(**configuration),
        level=LOG_LEVELS.DEBUG)

    configuration['HASH'] = md5(
        bytes(
            '{SSH_MAIL_USER_FROM}@{SSH_MAIL_USER_FROMDOMAIN}+{SSH_MAIL_USER_TO}@{SSH_MAIL_USER_TODOMAIN}'
            .format(**configuration), 'UTF-8')).hexdigest()
    configuration[
        'Message-ID'] = '<{RAW_TIME}.{HASH}@{SSH_MAIL_USER_FROMDOMAIN}>'.format(
            **configuration)

    ## TODO: https://support.google.com/mail/answer/81126
    ## TODO:(DKIM) https://russell.ballestrini.net/quickstart-to-dkim-sign-email-with-python/
    ## TODO:(S/MIME) https://tools.ietf.org/doc/python-m2crypto/howto.smime.html
    ## TODO: https://support.rackspace.com/how-to/create-an-spf-txt-record/
    ##
    ## https://toolbox.googleapps.com/apps/checkmx/check?domain={DOMAIN}&dkim_selector=
    ## https://github.com/PowerDNS/pdns/issues/2881

    email = MIMEMultipart('alternative')
    email['Subject'] = configuration['SUBJECT']
    email[
        'From'] = "{SSH_MAIL_USER_FROM} <{SSH_MAIL_USER_FROM}@{SSH_MAIL_USER_FROMDOMAIN}>".format(
            **configuration)
    email['To'] = "<{SSH_MAIL_USER_TO}@{SSH_MAIL_USER_TODOMAIN}>".format(
        **configuration)
    email['Message-ID'] = configuration['Message-ID']
    email.preamble = configuration['SUBJECT']

    text = configuration['TEXT_TEMPLATE'].format(**configuration)
    html = configuration['HTML_TEMPLATE'].format(**configuration)

    email_body_text = MIMEText(text, 'plain')
    email_body_html = MIMEBase('text', 'html')
    email_body_html.set_payload(html)
    encoders.encode_quopri(email_body_html)
    email_body_html.set_charset('UTF-8')

    email.attach(email_body_text)
    email.attach(email_body_html)

    email["DKIM-Signature"] = sign_email(email, configuration)

    context = ssl.create_default_context()
    for mx_record in dns.resolver.query(
            configuration['SSH_MAIL_USER_TODOMAIN'], 'MX'):
        mail_server = mx_record.to_text().split()[1][:-1]
        try:
            server = smtplib.SMTP(mail_server,
                                  local_hostname='hvornum.se',
                                  port=25,
                                  timeout=10)  # 587 = TLS, 465 = SSL

            if server.starttls(context=context)[0] != 220:
                log('Could not start TLS.', level=3, origin='mailer')

            configuration['mail_server'] = mail_server
            log('Mail via {mail_server} | from {SSH_MAIL_USER_FROM}@{SSH_MAIL_USER_FROMDOMAIN} -> {SSH_MAIL_USER_TO}@{SSH_MAIL_USER_TODOMAIN}'
                .format(**configuration),
                origin='mailer',
                level=LOG_LEVELS.DEBUG)

            server.sendmail(
                '{SSH_MAIL_USER_FROM}@{SSH_MAIL_USER_FROMDOMAIN}'.format(
                    **configuration),
                '{SSH_MAIL_USER_TO}@{SSH_MAIL_USER_TODOMAIN}'.format(
                    **configuration), email.as_string())
            server.quit()
            #		server.close()

            configuration['mail_server'] = mail_server
            log("Sent email from {SSH_MAIL_USER_FROM}@{SSH_MAIL_USER_FROMDOMAIN} to {SSH_MAIL_USER_TO}@{SSH_MAIL_USER_TODOMAIN} about via {mail_server}."
                .format(**configuration),
                level=LOG_LEVELS.INFO,
                origin='mailer')

            return True
        except Exception as e:
            log("Could not send email via: {}!!".format(mail_server),
                level=3,
                origin='mailer')
            log("{}".format(e), level=3, origin='mailer')

            if configuration['TRY_ONE_MAILSERVER']:
                break

    return False
Example #24
0
email = MIMEMultipart('alternative')
email['Subject'] = configuration['SUBJECT']
email['From'] = "SSH Guard <{SSH_MAIL_USER_FROM}@{DOMAIN}>".format(**configuration)
email['To'] = "{SSH_MAIL_TO_REALNAME} <{SSH_MAIL_USER_TO}@{SSH_MAIL_USER_TODOMAIN}>".format(**configuration)
email['Message-ID'] = configuration['Message-ID']
email.preamble = configuration['SUBJECT']

text = load_text_template()
html = load_html_template()


email_body_text = MIMEText(text, 'plain')
email_body_html = MIMEBase('text', 'html')
email_body_html.set_payload(html)
encoders.encode_quopri(email_body_html)
email_body_html.set_charset('UTF-8')

email.attach(email_body_text)
email.attach(email_body_html)

email["DKIM-Signature"] = sign_email(email)

context = ssl.create_default_context()
for mx_record in dns.resolver.query('gmail.com', 'MX'):
	mail_server = mx_record.to_text().split()[1][:-1]
	try:
		server = smtplib.SMTP(mail_server, port=25, timeout=10) # 587 = TLS, 465 = SSL
		if server.starttls(context=context)[0] != 220:
			log.warning('Could not start TLS.')
		
Example #25
0
    def _decrypt_inline_pgp(self, from_user, crypto, passcode):
        '''
            Decrypt an inline PGP message (internal use only).
        '''

        def adjust_attachment_name(part):
            '''Adjust the filename for the attachment.'''

            try:
                filename = part.get_filename()
                if filename and filename.endswith('.pgp'):
                    self.log_message('original attachment filename: {}'.format(filename))
                    end = len(filename) - len('.pgp')
                    part.replace_header(
                      mime_constants.CONTENT_DISPOSITION_KEYWORD,
                      'attachment; filename="{}"'.format(filename[:end]))
                    filename = part.__getitem__(mime_constants.CONTENT_DISPOSITION_KEYWORD)
                    self.log_message('new attachment filename: {}'.format(filename))
                else:
                    self.log_message('attachment filename: {}'.format(filename))
            except:
                record_exception()
                self.log_message('EXCEPTION - see syr.exception.log for details')


        decrypted = False

        self.log_message("message is inline PGP format")
        message = self.crypto_message.get_email_message().get_message()
        self.log_message("message content type is {}".format(message.get_content_type()))

        # remove any clear signed section before decrypting message
        self.crypto_message.get_email_message().remove_pgp_signature_blocks()

        if message.is_multipart():
            for part in message.get_payload():
                content_type = part.get_content_type()
                ciphertext = part.get_payload(decode=True)
                charset, __ = get_charset(part)
                self.log_message('multipart message char set: {}'.format(charset))
                plaintext = self._decrypt(from_user, ciphertext, charset, crypto, passcode)
                if plaintext is not None and plaintext != ciphertext:
                    decrypted = True

                    charset, __ = part.get_charset()
                    self.log_message("message part charset is {}".format(charset))
                    part.set_payload(plaintext, charset=charset)

                    try:
                        content_keyword_in_part = part.__getitem__(mime_constants.CONTENT_DISPOSITION_KEYWORD)
                    except Exception:
                        content_keyword_in_part = None
                    if content_keyword_in_part is not None:
                        adjust_attachment_name(part)

                    try:
                        encoding = part.__getitem__(mime_constants.CONTENT_XFER_ENCODING_KEYWORD)
                    except Exception:
                        encoding = None
                    if encoding == mime_constants.QUOTED_PRINTABLE_ENCODING:
                        encode_quopri(part)
                        self.log_message("{} encoded message part".format(encoding))
                    elif encoding == mime_constants.BASE64_ENCODING:
                        encode_base64(part)
                        self.log_message("{} encoded message part".format(encoding))
        else:
            charset, __ = get_charset(self.crypto_message.get_email_message())
            self.log_message('inline pgp char set: {}'.format(charset))
            ciphertext = self.crypto_message.get_email_message().get_content()
            plaintext = self._decrypt(from_user, ciphertext, charset, crypto, passcode)

            if plaintext is None or ciphertext is None or plaintext == ciphertext:
                decrypted = False
                self.log_message("unable to decrypt {} message".format(message.get_content_type()))
            else:
                # do not specify the codec
                self.crypto_message.get_email_message().get_message().set_payload(plaintext)
                try:
                    encoding = self.crypto_message.get_email_message().get_message().__getitem__(
                        mime_constants.CONTENT_XFER_ENCODING_KEYWORD)
                except Exception:
                    encoding = None
                if encoding == mime_constants.QUOTED_PRINTABLE_ENCODING:
                    encode_quopri(self.crypto_message.get_email_message().get_message())
                elif encoding == mime_constants.BASE64_ENCODING:
                    encode_base64(self.crypto_message.get_email_message().get_message())
                decrypted = True

        return decrypted
Example #26
0
    def send(self, emails):
        if isinstance(emails, Email):
            emails = [emails]
        if len([e for e in emails if e.__class__ != Email]):
            raise TypeError('emails must be Email or list of Email instances')

        smtpclass = SMTP_SSL if self.ssl else SMTP
        if self.server == 'localhost':
            smtp = smtpclass(self.server)
        else:
            smtp = smtpclass(self.server, self.port)
        if self.tls:
            smtp.starttls()
        if self.login and self.password:
            smtp.login(self.login, self.password)
        for email in emails:
            c = Charset(email.charset)
            c.header_encoding = QP
            c.body_encoding = 0
            r = Charset(email.charset)
            r.header_encoding = 0
            r.body_encoding = 0

            email.normalize_email_list('rcpt')
            email.normalize_email_list('cc')
            email.normalize_email_list('bcc')
            mime1, mime2 = email.mimetype.split('/')
            mainpart = MIMEBase(mime1, mime2)
            if not email.force_7bit:
                mainpart.set_param('charset', email.charset)

            if len(email.attachments):
                message = MIMEMultipart('mixed')
                message.attach(mainpart)
                del mainpart['mime-version']
            else:
                message = mainpart

            message['Date'] = datetime.datetime.now().strftime(
                '%a, %d %b %Y %H:%M:%S') + (" +%04d" % (time.timezone/-36,))

            h = Header(maxlinelen=1000) # FIXME: what is correct max length?
            fromname = self.fromname.encode(email.charset, 'xmlcharrefreplace')
            h.append(fromname, r if is7bit(fromname) else c)
            h.append('<%s>' % self.email, r)
            message['From'] = h

            message['To'] = email.get_emails_header('rcpt')
            if len(email.cc):
                message['CC'] = email.get_emails_header('cc')
            if len(email.bcc):
                message['BCC'] = email.get_emails_header('bcc')

            subject = email.subject.encode(email.charset, 'xmlcharrefreplace')
            message['Subject'] = Header(subject, r if is7bit(subject) else c)

            if email.reply_to:
                message['Reply-To'] = email.get_emails_header('reply_to')

            if email.force_7bit:
                body = email.body.encode('ascii', 'xmlcharrefreplace')
            else:
                body = email.body.encode(email.charset, 'xmlcharrefreplace')
            mainpart.set_payload(body)

            if is7bit(body):
                mainpart['Content-Transfer-Encoding'] = '7bit'
            else:
                encode_quopri(mainpart)

            for attachment in email.attachments:
                if attachment.__class__ != Attachment:
                    raise TypeError("invalid attachment")

                mimetype = attachment.mimetype
                if not mimetype:
                    mimetype, encoding = guess_type(attachment.filename)
                    if not mimetype:
                        mimetype = 'application/octet-stream'
                mime1, mime2 = mimetype.split('/')
                part = MIMEBase(mime1, mime2)

                # using newer rfc2231 (not supported by Outlook):
                # part.set_param('name', attachment.filename.encode('utf-8'), charset = 'utf-8')

                # hack: using deprecated rfc2047 - supported by Outlook:
                part.set_param('name', str(Header(attachment.filename)))
                del part['mime-version']

                if attachment.id:
                    part['Content-Disposition'] = 'inline'
                else:
                    part['Content-Disposition'] = 'attachment'

                # using newer rfc2231 (not supported by Outlook):
                # part.set_param('filename',
                #                attachment.filename.encode('utf-8'),
                #                'Content-Disposition',
                #                charset = 'utf-8')

                # hack: using deprecated rfc2047 - supported by Outlook:
                part.set_param('filename',
                               str(Header(attachment.filename)),
                               'Content-Disposition')
                if attachment.id:
                    part['Content-ID'] = '<%s>' % attachment.id

                part.set_payload(attachment.content)
                encode_base64(part)

                # Do this AFTER encode_base64(part), or Content-Transfer-Encoding header will duplicate,
                # or even happen 2 times with different values.
                if attachment.charset:
                    part.set_charset(attachment.charset)

                message.attach(part)

            smtp.sendmail(self.email, [rcpt[1] for rcpt in email.rcpt] +
                          [cc[1] for cc in email.cc] +
                          [bcc[1] for bcc in email.bcc], message.as_string())

        smtp.quit()
Example #27
0
    def construct_and_send_email(self):
        """Do work of constructing & sending reply email. For internal use."""
        # who to respond to?
        to_email = self.message.sender_address
        if not to_email:
            logging.error('Cannot decide who to respond to in %s' %
                          (self.message_id()))
            return  # XXX throw exception instead

        # what the response subject should be
        if 'Subject' in self.message:
            if self.message['Subject'][:4] != 'Re: ':
                subject = 'Re: ' + self.message['Subject']
            else:
                subject = self.message['Subject']
        else:
            subject = 'CryptoBot response'

        from_email = '{0} <{1}>'.format(config.PGP_NAME, config.PGP_EMAIL)

        # start the email
        msg = MIMEMultipart('mixed')
        body = MIMEMultipart('alternative')

        template_vars = {
            'encrypted_right': self.message.encrypted_right,
            'encrypted_wrong': self.message.encrypted_wrong,
            'signed': self.message.signed,
            'pubkey_included': self.message.pubkey_included,
            'pubkey_included_wrong': self.message.pubkey_included_wrong,
            'pubkey_fingerprint': self.message.pubkey_fingerprint
        }

        body_txt = self.txt_template.render(template_vars)
        txt_part = MIMEBase('text', 'plain')
        txt_part.set_payload(body_txt)
        encode_quopri(txt_part)
        body.attach(txt_part)

        body_html = self.html_template.render(template_vars)
        html_part = MIMEBase('text', 'html')
        html_part.set_payload(body_html)
        encode_quopri(html_part)

        body.attach(html_part)
        msg.attach(body)

        # if the message is not encrypted, attach public key (#16)
        if not self.message.encrypted_right:
            pubkey = str(self._gpg.export_keys(self.fingerprint))
            pubkey_filename = '{0} {1} (0x{2}) pub.asc'.format(
                config.PGP_NAME, config.PGP_EMAIL,
                str(self.fingerprint)[:-8])

            pubkey_part = MIMEBase('application', 'pgp-keys')
            pubkey_part.add_header(
                'Content-Disposition',
                'attachment; filename="%s"' % pubkey_filename)
            pubkey_part.set_payload(pubkey)
            encode_quopri(pubkey_part)
            msg.attach(pubkey_part)

        # sign the message
        msg_string = (self.as_string(msg) + '\n').replace('\n', '\r\n')
        sig = self._gpg.sign(msg_string)

        # make a sig part
        sig_part = MIMEBase('application',
                            'pgp-signature',
                            name='signature.asc')
        sig_part.add_header('Content-Description', 'OpenPGP digital signature')
        sig_part.add_header('Content-Disposition',
                            'attachment; filename="signature.asc"')
        sig_part.set_payload(sig)

        # wrap it all up in multipart/signed
        signed = MIMEMultipart(_subtype="signed",
                               micalg="pgp-sha1",
                               protocol="application/pgp-signature")
        signed.attach(msg)
        signed.attach(sig_part)

        # if we're just signing and not encrypting this message, add the headers directly to the signed part
        if not self.message.pubkey_fingerprint:
            signed['Subject'] = subject
            signed['From'] = from_email
            signed['To'] = to_email

        # need to add a '\r\n' right before the sig part (#19)
        # because of this bug http://bugs.python.org/issue14983
        signed_string = self.as_string(signed)
        i = signed_string.rfind(
            '--', 0,
            signed_string.find(
                'Content-Type: application/pgp-signature; name="signature.asc"'
            ))
        signed_string = signed_string[0:i] + '\r\n' + signed_string[i:]

        # if we have a fingerprint to encrypt to, encrypt it
        if self.message.pubkey_fingerprint:
            # encrypt the message
            ciphertext = self._gpg.encrypt(signed_string,
                                           self.message.pubkey_fingerprint)

            # make an application/pgp-encrypted part
            encrypted = MIMEBase("application", "pgp-encrypted")
            encrypted.set_payload("Version: 1\r\n")

            # make application/octet-stream part
            octet_stream = MIMEBase("application", "octet-stream")
            octet_stream.set_payload(ciphertext)

            # make the multipart/encrypted wrapper
            wrapper = MIMEMultipart(_subtype="encrypted",
                                    protocol="application/pgp-encrypted")
            wrapper.attach(encrypted)
            wrapper.attach(octet_stream)

            # add headers to the encryption wrapper
            wrapper['Subject'] = subject
            wrapper['From'] = from_email
            wrapper['To'] = to_email

            final_message = self.as_string(wrapper)
        else:
            final_message = signed_string

        self.sender(final_message, from_email, to_email)
        logging.info('Responded to {0} {1}'.format(self.message['From'],
                                                   str(template_vars)))