Skip to content

fix(imap): fix mojibake in nested emails, empty headers, and date handling#909

Merged
dreamhunter2333 merged 3 commits intomainfrom
fix/imap-mojibake-nested
Mar 22, 2026
Merged

fix(imap): fix mojibake in nested emails, empty headers, and date handling#909
dreamhunter2333 merged 3 commits intomainfrom
fix/imap-mojibake-nested

Conversation

@dreamhunter2333
Copy link
Owner

@dreamhunter2333 dreamhunter2333 commented Mar 22, 2026

User description

Summary

  • 修复嵌套回复邮件(message/rfc822 附件)UTF-8 乱码:新增逐行 mojibake 修复兜底,处理含混合内容(base64 图片等)的复杂邮件
  • 修复 Gmail 邮件空 Content-Type 头导致 multipart 解析失败:全局清理空 header 行,覆盖嵌套 MIME 部分
  • 修复缺失 Date 头的邮件显示错误日期:使用 created_at 自动填充,locale 无关的英文日期格式
  • 其他改进:修复 non-multipart getSubPart、接受客户端 CREATE 请求、密码 strip、发件箱改用 MIMEText、保留原始 CTE 编码

Test plan

  • 已部署到生产环境容器并验证
  • 测试嵌套回复邮件(含 message/rfc822 附件)乱码是否修复
  • 测试 Gmail 发来的 multipart 邮件是否正常解析
  • 测试缺失 Date 头的邮件是否显示正确日期
  • 测试发件箱邮件是否正常显示

🤖 Generated with Claude Code


PR Type

Bug fix, Enhancement


Description

  • Fixed UTF-8 mojibake in nested emails with fallback mechanisms.

    • Added line-by-line encoding fix for mixed content.
  • Resolved issues with empty headers in Gmail emails.

    • Implemented global cleanup for empty headers, including nested MIME parts.
  • Enhanced handling of missing Date headers in emails.

    • Auto-filled Date header using created_at field with locale-independent formatting.
  • Improved IMAP server functionality and compatibility.

    • Accepted CREATE requests silently and handled client-created mailboxes.
    • Used MIMEText for sent mail generation and preserved original CTE encoding.

Diagram Walkthrough

flowchart LR
  BackendClient["BackendClient password handling"]
  EmailParsing["Email parsing enhancements"]
  IMAPServer["IMAP server improvements"]
  BackendClient -- "strip whitespace from password" --> EmailParsing
  EmailParsing -- "fix mojibake, clean headers, handle missing Date" --> IMAPServer
  IMAPServer -- "accept CREATE requests, improve mailbox handling" --> BackendClient
Loading

File Walkthrough

Relevant files
Bug fix
3 files
imap_http_client.py
Strip whitespace from IMAP password                                           
+1/-1     
imap_mailbox.py
Fix mojibake and clean headers in raw emails                         
+5/-2     
parse_email.py
Enhance email parsing with mojibake fix and header cleanup
+69/-26 
Enhancement
2 files
imap_message.py
Add locale-independent date formatting and header filling
+55/-3   
imap_server.py
Accept CREATE requests and improve mailbox handling           
+11/-5   
Documentation
2 files
CHANGELOG.md
Document IMAP fixes and enhancements in changelog               
+4/-0     
CHANGELOG_EN.md
Document IMAP fixes and enhancements in English changelog
+4/-0     

Summary by CodeRabbit

  • Bug Fixes

    • 修复嵌套回复邮件中 UTF-8 解码乱码问题
    • 修复 Gmail 在缺失或空 Content-Type 时导致的 multipart 解析失败
    • 在缺失 Date 头时使用创建时间填充以修正显示时间
    • 避免依赖系统语言导致的 IMAP INTERNALDATE 解析失败
  • Security

    • 改进密码输入的预处理以减少意外空白带来的问题
  • Improvements

    • 优化原始邮件头清理与解析流程,改进邮件构建与日期格式化处理

…dling

- Add line-by-line mojibake fix fallback for complex emails with mixed content
- Apply empty header cleanup globally to fix nested message/rfc822 parts
- Add locale-independent date formatting (format_imap_date, format_rfc2822_date)
- Fill missing Date header from created_at field
- Fix getSubPart for non-multipart messages
- Accept CREATE requests from clients (e.g. Gmail creating Drafts)
- Strip whitespace from IMAP password
- Use MIMEText instead of MIMEMultipart for sent mail generation
- Keep body in original CTE encoding for correct BODYSTRUCTURE
- Update CHANGELOG (zh/en)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Mar 22, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 95f8653d-26b0-4786-a647-f35628fe2c70

📥 Commits

Reviewing files that changed from the base of the PR and between 09c9993 and eaef083.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • CHANGELOG_EN.md

📝 Walkthrough

Walkthrough

本次变更在 IMAP 邮件处理链中引入多项修复与增强:修正嵌套回复的 UTF‑8 乱码、清理空 Content‑Type 导致的 multipart 解析失败、在缺失 Date 时使用 created_at 补齐,并修复因 locale 导致的 INTERNALDATE 解析问题,同时调整账号/邮箱创建行为与若干解析/模型接口。

Changes

Cohort / File(s) Summary
变更日志
CHANGELOG.md, CHANGELOG_EN.md
新增 v1.5.0 IMAP 修复项,列出 mojibake 修正、空 Content‑Type 清理、Date 补填与 INTERNALDATE locale 修复。
HTTP 客户端 密码清理
smtp_proxy_server/imap_http_client.py
BackendClient.__init__ 在存储/使用密码前调用 .strip()
邮箱消息预处理
smtp_proxy_server/imap_mailbox.py
在解析 INBOX 原始消息前调用 fix_mojibake()clean_raw_headers();构造 SimpleMessage 时填充 created_at
日期与邮件模型增强
smtp_proxy_server/imap_message.py
新增 parse_created_atformat_imap_dateformat_rfc2822_dateSimpleMessage 构造器增加 created_at 参数并用其补填缺失 DategetInternalDate() 优先使用 created_atgetSubPart() 处理非 multipart 情况。
IMAP 账号/邮箱行为调整
smtp_proxy_server/imap_server.py
Account._emptyMailboxcreate() 不再拒绝未知 CREATE,请求返回可用 SimpleMailbox 并返回 True;新增/覆盖 listMailboxes() 仅公开 INBOXSENT;认证时将后端 client 关联到 Account
邮件解析重构与预处理
smtp_proxy_server/parse_email.py
新增 clean_raw_headers()fix_mojibake();非 multipart 提取保留原始 payload(不再自动按 charset 解码),body size 改为 UTF‑8 字节长度;parse_email() 先清理原始头,generate_email_model() 改为构建单一 MIMEText 并用 created_at 生成 RFC‑2822 Date。

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 我是调试的小兔子,钻进邮箱洞,
解开编码的结,文字不再糊,
空头被清理,Date 从 created_at 补上,
月份固定语言稳,INTERNALDATE 不再慌,
欢蹦乱跳,邮件又温暖又通畅。

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.83% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR标题准确总结了主要变更:修复嵌套邮件中的mojibake、空header和日期处理问题,与代码改动完全匹配。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/imap-mojibake-nested

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🔒 No security concerns identified
⚡ Recommended focus areas for review

Password Handling

The password.strip() addition ensures no leading/trailing whitespace in the password, but it should be confirmed that this change does not inadvertently affect any existing functionality or authentication mechanisms.

self.password = password.strip()
Mojibake and Header Cleaning

The addition of fix_mojibake and clean_raw_headers functions to process raw email content should be validated for edge cases, especially for emails with complex or non-standard formats.

raw = fix_mojibake(raw)
raw = clean_raw_headers(raw)
Date Header Filling Logic

The _fill_date_header method introduces logic to populate missing Date headers using created_at. Ensure this logic handles all edge cases and does not overwrite valid Date headers.

def _fill_date_header(self):
    """Fill empty/missing Date header from created_at."""
    date_val = self.email.headers.get("Date", "").strip()
    if date_val or not self._created_at:
        return
    dt = parse_created_at(self._created_at)
    if dt:
        self.email.headers["Date"] = format_rfc2822_date(dt)

@github-actions
Copy link

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
General
Use current UTC time as fallback

Replace the hardcoded default date with a more meaningful fallback, such as the
current date and time in UTC, to avoid misleading information in email clients.

smtp_proxy_server/imap_message.py [116]

-return self.email.headers.get("Date", "Mon, 1 Jan 1900 00:00:00 +0000")
+return self.email.headers.get("Date", format_rfc2822_date(datetime.now(timezone.utc)))
Suggestion importance[1-10]: 8

__

Why: Replacing the hardcoded default date with the current UTC time provides a more meaningful fallback, reducing the risk of misleading information in email clients. This change enhances the user experience and aligns with best practices for handling missing data.

Medium
Possible issue
Reorder header cleaning and encoding fixes

Swap the order of fix_mojibake and clean_raw_headers to ensure headers are cleaned
before attempting to fix encoding issues, as malformed headers might interfere with
encoding fixes.

smtp_proxy_server/imap_mailbox.py [249-250]

+raw = clean_raw_headers(raw)
 raw = fix_mojibake(raw)
-raw = clean_raw_headers(raw)
Suggestion importance[1-10]: 7

__

Why: Reordering the functions ensures that malformed headers are cleaned before attempting to fix encoding issues, which could prevent potential errors during the encoding fix process. This change improves robustness without altering functionality.

Medium
Avoid stripping critical password characters

Ensure that password.strip() does not unintentionally remove critical whitespace
from passwords that might be valid in some systems. Consider validating the password
format instead of stripping it.

smtp_proxy_server/imap_http_client.py [20]

-self.password = password.strip()
+self.password = password
Suggestion importance[1-10]: 3

__

Why: While the suggestion raises a valid concern about potential unintended consequences of stripping whitespace from passwords, it does not account for the fact that the PR explicitly introduces this change. The suggestion could be valid in certain contexts, but it lacks evidence or justification specific to this PR's requirements.

Low

@dreamhunter2333 dreamhunter2333 merged commit 03965f3 into main Mar 22, 2026
1 check passed
@dreamhunter2333 dreamhunter2333 deleted the fix/imap-mojibake-nested branch March 22, 2026 12:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant