简体   繁体   中英

Office365 message encryption strips out email attachments when sending with Python

I have a python3 def that sends an html message via an office 365 account. We have a requirement that the message be sent with Office365 message encryption (OME). This gives the user an html attachment makes them log in via office 365 before viewing the email online. We have enabled OME for all messages sent on the email account that will be sending the message. Here are some observations:

  1. When sending via the python def through the OME-enabled account, there is no attachment when viewing the encrypted message.
  2. When sending via the python def through a normal account, the attachment shows up in the email as expected
  3. When manually sending an email through the OME-enabled account using the office365 website or outlook, the attachment shows up in the email as expected
  4. The email code includes attaching images for the HTML, and that displays fine in both the normal and encrypted messages.

I bet I am missing some random header or something - does anyone know what is going on? Here is the python code:

def sendHTMLEmail(
    emaillist,
    subject,
    body,
    altBody,
    logger,
    mailuser,
    mailpassword,
    fileAttachments=None,
    embeddedImages=None
):  
    """
    Send an html-enabled email to a number of recipients. This method includes the options of embedding images in the email,
    attaching files to the email, and providing alternative plain text in case of an error rendering the HTML. 

    # Arguments
        emaillist (list[string]): List of email addresses to send to
        subject (string): Subject line of the email
        body (string): Body of the email. This can be plain text or HTML, depending on the email. 
        altBody (string): Alternate body text of the email. This will be displayed if the HTML cannot be rendered.
        logger (JvpyLog): optional logger override parameter 
        mailuser (string): SMTP username to send the mail as
        mailpassword (string): login password of the SMTP user
        fileAttachments (list[string]): list of fully qualified filenames to attach to the email
        embeddedImages (list[dict]): list of embeddedImage dicts specifying images to be embedded in the HTML boddy of the email

    # Special Types
    ###### embeddedImages [dict]:
    ```python
    {
        filename (string): fully qualified filename of the image
        imageid (string): unique id for the image. This will be refrernced in the HTML body of the email and repaced by the actual image. 
    }
    ```
    """
    logger.info("Sending email to the following recipients: " + str(emaillist))

    # Define these once; use them twice!
    strFrom = mailuser
    strTo = ", ".join(emaillist)

    # Create the root message and fill in the from, to, and subject headers
    msgRoot = MIMEMultipart('related')
    msgRoot['Subject'] = subject
    msgRoot['From'] = strFrom
    msgRoot['To'] = strTo
    msgRoot.preamble = 'This is a multi-part message in MIME format.'

    # Encapsulate the plain and HTML versions of the message body in an
    # 'alternative' part, so message agents can decide which they want to display.
    msgAlternative = MIMEMultipart('alternative')
    msgRoot.attach(msgAlternative)

    msgText = MIMEText(altBody)
    msgAlternative.attach(msgText)

    # We reference the image in the IMG SRC attribute by the ID we give it below
    msgText = MIMEText(body, 'html')
    msgAlternative.attach(msgText)

    # embed images if populated
    if embeddedImages is not None:
        for image in embeddedImages:
            logger.info("Embedding Image: " + str(image['filename']) + " as content id: " + str(image['imageid']))
            fp = open(image['filename'], 'rb')
            msgImage = MIMEImage(fp.read())
            fp.close()

            # Define the image's ID as referenced above
            msgImage.add_header('Content-ID', image['imageid'])
            msgRoot.attach(msgImage)

    # add attachements if populated
    if fileAttachments is not None:
        for filename in fileAttachments:
            logger.info("Attaching File: " + str(filename))
            part = MIMEApplication(
                open(filename,"rb").read(),
                Name=os.path.basename(filename)
            )
            part['Content-Disposition'] = 'attachment; filename="{0}"'.format(os.path.basename(filename))
            ## add mimetype based on the file extension
            mimetype = mimetypes.types_map["." + os.path.basename(filename).lower().split(".")[1]]
            part['Content-Type'] = mimetype + '; name="{0}"'.format(os.path.basename(filename))
            msgRoot.attach(part)

    # Send the email (this example assumes SMTP authentication is required)
    server = smtplib.SMTP('smtp.office365.com', 587)
    server.ehlo()
    server.starttls()
    server.ehlo()
    server.login(mailuser, mailpassword)
    server.sendmail(strFrom, emaillist, msgRoot.as_string())
    server.quit()

    logger.info("email sent.")

Figured it out: I was comparing the email source files from a manual send and a send with my python code, and I noticed that the Content-Type header was different. The python code used 'multipart/related', while the manual send used 'multipart/mixed'. This ended up being the problem. I changed msgRoot = MIMEMultipart('related') to msgRoot = MIMEMultipart() , and this resolved the issue.

The strange thing is that non-encrypted emails sent by the python code still displayed properly with an attachment - it was only secure emails that didn't play nice with the 'multipart/related' Content type. I built my code on some sample code that used 'multipart/related', so that is why I included it in my original code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM