From 406cce627011e67b8c2b2ea0fcfcf6b3dd403d8c Mon Sep 17 00:00:00 2001 From: Andreas Knuth Date: Sun, 25 Jan 2026 17:23:08 -0600 Subject: [PATCH] _create_forward_message fixed for multipart messages --- .../email_processing/rules_processor.py | 99 ++++++++++--------- 1 file changed, 54 insertions(+), 45 deletions(-) diff --git a/email-worker/email_processing/rules_processor.py b/email-worker/email_processing/rules_processor.py index bb5c728..842eef8 100644 --- a/email-worker/email_processing/rules_processor.py +++ b/email-worker/email_processing/rules_processor.py @@ -231,54 +231,63 @@ class RulesProcessor: msg.attach(body_part) return msg - @staticmethod - def _create_forward_message(original_parsed, recipient: str, forward_to: str, original_from: str): - """Create Forward message as complete MIME message""" - original_subject = original_parsed.get('Subject', '(no subject)') - original_date = original_parsed.get('Date', 'unknown') + @staticmethod + def _create_forward_message(original_parsed, recipient: str, forward_to: str, original_from: str): + """Create Forward message as complete MIME message""" + original_subject = original_parsed.get('Subject', '(no subject)') + original_date = original_parsed.get('Date', 'unknown') - msg = MIMEMultipart('mixed') - msg['From'] = recipient - msg['To'] = forward_to - msg['Subject'] = f"FWD: {original_subject}" - msg['Date'] = formatdate(localtime=True) - msg['Message-ID'] = make_msgid(domain=recipient.split('@')[1]) - msg['Reply-To'] = original_from - msg['X-SES-Worker-Processed'] = 'forwarded' + msg = MIMEMultipart('mixed') + msg['From'] = recipient + msg['To'] = forward_to + msg['Subject'] = f"FWD: {original_subject}" + msg['Date'] = formatdate(localtime=True) + msg['Message-ID'] = make_msgid(domain=recipient.split('@')[1]) + msg['Reply-To'] = original_from + msg['X-SES-Worker-Processed'] = 'forwarded' - text_body, html_body = EmailParser.extract_body_parts(original_parsed) - body_part = MIMEMultipart('alternative') - - # Text version - fwd_text = "---------- Forwarded message ---------\n" - fwd_text += f"From: {original_from}\n" - fwd_text += f"Date: {original_date}\n" - fwd_text += f"Subject: {original_subject}\n" - fwd_text += f"To: {recipient}\n\n" - fwd_text += text_body - body_part.attach(MIMEText(fwd_text, 'plain', 'utf-8')) + text_body, html_body = EmailParser.extract_body_parts(original_parsed) + body_part = MIMEMultipart('alternative') + + # Text version + fwd_text = "---------- Forwarded message ---------\n" + fwd_text += f"From: {original_from}\n" + fwd_text += f"Date: {original_date}\n" + fwd_text += f"Subject: {original_subject}\n" + fwd_text += f"To: {recipient}\n\n" + fwd_text += text_body + body_part.attach(MIMEText(fwd_text, 'plain', 'utf-8')) - # HTML version - if html_body: - fwd_html = "
" - fwd_html += "---------- Forwarded message ---------
" - fwd_html += f"From: {original_from}
" - fwd_html += f"Date: {original_date}
" - fwd_html += f"Subject: {original_subject}
" - fwd_html += f"To: {recipient}

" - fwd_html += html_body - fwd_html += "
" - body_part.attach(MIMEText(fwd_html, 'html', 'utf-8')) + # HTML version + if html_body: + fwd_html = "
" + fwd_html += "---------- Forwarded message ---------
" + fwd_html += f"From: {original_from}
" + fwd_html += f"Date: {original_date}
" + fwd_html += f"Subject: {original_subject}
" + fwd_html += f"To: {recipient}

" + fwd_html += html_body + fwd_html += "
" + body_part.attach(MIMEText(fwd_html, 'html', 'utf-8')) - msg.attach(body_part) + msg.attach(body_part) - # Copy attachments - if original_parsed.is_multipart(): - for part in original_parsed.walk(): - if part.get_content_maintype() == 'multipart': - continue - if part.get_content_type() in ['text/plain', 'text/html']: - continue - msg.attach(part) + # Copy attachments - FIX FILENAMES + if original_parsed.is_multipart(): + for part in original_parsed.walk(): + if part.get_content_maintype() == 'multipart': + continue + if part.get_content_type() in ['text/plain', 'text/html']: + continue + + # Fix malformed filename in Content-Disposition + content_disp = part.get('Content-Disposition', '') + if 'filename=' in content_disp and '"' not in content_disp: + # Add quotes around filename with spaces + import re + fixed_disp = re.sub(r'filename=([^;"\s]+(?:\s+[^;"\s]+)*)', r'filename="\1"', content_disp) + part.replace_header('Content-Disposition', fixed_disp) + + msg.attach(part) - return msg + return msg