The objective is to forward a message in Python the way recognized by email clients like Evolution, MacOS X Mail, https://mail.yahoo.com. Also I'd like to attach some text where I could explain why I did the forward and have the option to access the original message when received the forwarded message. The approach proposed in the thread Forwarding an email with python smtplib replacing FROM, TO headers doesn't allow to do so. This is the function:
def make_fwd_msg(orig, from_, to_, orig_text=None, references=True):
"""Return the message [container] with the description of the
forward and the ORIG message"""
msg = MIMEMultipart('mixed')
msg['SUBJECT'] = 'Fwd: {}'.format(orig['SUBJECT'])
if references:
msg['REFERENCES'] = orig['MESSAGE-ID']
msg['FROM'] = from_
msg['TO'] = to_
if orig_text:
msg.attach(MIMEText(orig_text, 'plain'))
alt = MIMEMultipart('alternative')
alt.attach(orig)
msg.attach(alt)
return msg
The full sample is in the end. I did some testing in the aforementioned email clients. They handle forwarding differently. Therefore it is hard for me know how far I have gone from the "right" forward. Please help!
Using Debian GNU/Linux 11 (bullseye), Python 3.9.2
The full sample
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import email
import smtplib
import argparse
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
class SMTPYahoo(smtplib.SMTP_SSL):
PORT = 465
HOST = 'smtp.mail.yahoo.com'
def __init__(self, *args, **kwds):
super().__init__(*args, host=self.HOST, port=self.PORT,
**kwds)
self.user = os.getenv('USER_NAME_EMAIL')
self.login(self.user + '@yahoo.com', os.getenv('YAHOO_APP_PWD'))
class SMTPGmail(smtplib.SMTP_SSL):
PORT = 465
HOST = 'smtp.gmail.com'
def __init__(self, *args, **kwds):
super().__init__(*args, host=self.HOST, port=self.PORT,
**kwds)
self.user = os.getenv('USER_NAME_EMAIL')
self.login(self.user + '@gmail.com', os.getenv('GMAIL_APP_PWD'))
parser = argparse.ArgumentParser(description="""\
Forward message using Yahoo or Gmail mail server""")
parser.add_argument('--smtp-server', choices=('yahoo', 'gmail'), type=str,
default='yahoo', help="""
Choose smtp server""")
parser.add_argument('message_file', type=str, help="""\
A file with the message to forward""")
parser.add_argument('--verbose', '-v', action='count', default=0)
def make_fwd_msg(orig, from_, to_, orig_text=None, references=True):
"""Return the message [container] with the description of the
forward and the ORIG message"""
msg = MIMEMultipart('mixed')
msg['SUBJECT'] = 'Fwd: {}'.format(orig['SUBJECT'])
if references:
msg['REFERENCES'] = orig['MESSAGE-ID']
msg['FROM'] = from_
msg['TO'] = to_
if orig_text:
msg.attach(MIMEText(orig_text, 'plain'))
alt = MIMEMultipart('alternative')
alt.attach(orig)
msg.attach(alt)
return msg
if __name__ == '__main__':
args = parser.parse_args()
if 0 < args.verbose:
print(args)
smtp_server = (SMTPYahoo if args.smtp_server == 'yahoo' else SMTPGmail)()
from_ = smtp_server.user
to_ = from_.split('@')[0] + ('@gmail.com' if args.smtp_server == 'yahoo'
else '@yahoo.com')
with open(args.message_file, 'rb') as fp:
orig = email.message_from_bytes(fp.read())
msg = make_fwd_msg(orig, from_, to_, orig_text='<Original text goes here>')
try:
with smtp_server as s:
if 2 < args.verbose:
s.set_debuglevel(1)
answer = input(f'from_={from_}, to_={to_} send [Y/n]? ')
if answer in ('', 'y', 'Y'):
s.send_message(msg)
except (Exception, smtplib.SMTPDataError) as e:
print(e)
UPDATE Still have no clear picture how the forward should look like. The following version of the make_fwd_msg() seems more concise and logical (credit to @tripleee)
def make_fwd_msg(orig, from_, to_, orig_text=None, references=True):
"""Return the message [container] with the description of the
forward and the ORIG message"""
msg = EmailMessage()
# msg = MIMEMultipart('mixed')
msg['SUBJECT'] = 'Fwd: {}'.format(orig['SUBJECT'])
if references:
msg['REFERENCES'] = orig['MESSAGE-ID']
msg['FROM'] = from_
msg['TO'] = to_
if orig_text:
msg.set_content(orig_text)
msg.add_attachment(orig.as_bytes(),
maintype='message', subtype='rfc822')
return msg