Forwarding an email with python smtplib
Asked Answered
A

1

13

I'm trying to put together a script that automatically forwards certain emails that match a specific criteria to another email.

I've got the downloading and parsing of messages using imaplib and email working, but I can't figure out how to forward an entire email to another address. Do I need to build a new message from scratch, or can I somehow modify the old one and re-send it?

Here's what I have so far (client is an imaplib.IMAP4 connection, and id is a message ID):

import smtplib, imaplib

smtp = smtplib.SMTP(host, smtp_port)
smtp.login(user, passw)

client = imaplib.IMAP4(host)
client.login(user, passw)
client.select('INBOX')

status, data = client.fetch(id, '(RFC822)')
email_body = data[0][1]
mail = email.message_from_string(email_body)

# ...Process message...

# This doesn't work
forward = email.message.Message()
forward.set_payload(mail.get_payload())
forward['From'] = '[email protected]'
forward['To'] = '[email protected]'

smtp.sendmail(user, ['[email protected]'], forward.as_string())

I'm sure there's something slightly more complicated I need to be doing with regard to the MIME content of the message. Surely there's some simple way of just forwarding the entire message though?

# This doesn't work either, it just freezes...?
mail['From'] = '[email protected]'
mail['To'] = '[email protected]'
smtp.sendmail(user, ['[email protected]'], mail.as_string())
Abdel answered 26/4, 2010 at 21:54 Comment(4)
There's too much missing context here to make any determination. In particular, are you using the standard smtplib? What version of Python. Where is smtp initialized, is it connect()ed? Have you gotten a proper HELO response?Moonraker
It's the standard smtplib, python2.6.4. The smtplib client is working fine - I can send simple text emails by passing a string as the last argument of smtp. I just want to find a simple way of sending the entire MIME content of a downloaded message to a new address.Abdel
There are three distinct meanings of "forward" in common use. There is "resend the message to a new destination" by embedding it in a new envelope (and, optionally, adding some new headers); this is what the .forward file on traditioal Unix systems does, and apparently what the OP is asking about. Secondly, it can mean attaching the original message with full headers etc to a new message, e.g. as a spam or phishing report, where the recipient can clearly tell exactly what the headers looked like when you received the message. (cont)Kath
(cont) Finally, most uselessly, many ostensibly "modern" email clients have a mechanism for embedding a rendering of another message with incomplete (and in the worst case, translated or otherwise munged) headers and no way to distinguish the original content from the surrounding message or intentional or mistaken editing. Probably never try to emulate or copy that behavior.Kath
D
26

I think the part you had wrong was how to replace the headers in the message, and the fact that you don't need to make a copy of the message, you can just operate directly on it after creating it from the raw data you fetched from the IMAP server.

You did omit some detail so here's my complete solution with all details spelled out. Note that I'm putting the SMTP connection in STARTTLS mode since I need that and note that I've separated the IMAP phase and the SMTP phase from each other. Maybe you thought that altering the message would somehow alter it on the IMAP server? If you did, this should show you clearly that that doesn't happen.

import smtplib, imaplib, email

imap_host = "mail.example.com"
smtp_host = "mail.example.com"
smtp_port = 587
user = "xyz"
passwd = "xyz"
msgid = 7
from_addr = "[email protected]"
to_addr = "[email protected]"

# open IMAP connection and fetch message with id msgid
# store message data in email_data
client = imaplib.IMAP4(imap_host)
client.login(user, passwd)
client.select('INBOX')
status, data = client.fetch(msgid, "(RFC822)")
email_data = data[0][1]
client.close()
client.logout()

# create a Message instance from the email data
message = email.message_from_string(email_data)

# replace headers (could do other processing here)
message.replace_header("From", from_addr)
message.replace_header("To", to_addr)

# open authenticated SMTP connection and send message with
# specified envelope from and to addresses
smtp = smtplib.SMTP(smtp_host, smtp_port)
smtp.starttls()
smtp.login(user, passwd)
smtp.sendmail(from_addr, to_addr, message.as_string())
smtp.quit()

Hope this helps even if this answer comes quite late.

Disbud answered 30/12, 2010 at 21:4 Comment(2)
Small adjustment I needed for Python 3. Binary data from IMAP fetch needs to be decoded, i.e. data[0][1].decode('utf-8').Diametral
@DamianMoore You don't need to decode it in this case. The email module has a function email.message_from_bytes() which is probably safer.Hurdle

© 2022 - 2024 — McMap. All rights reserved.