decode RFC 2047 subjects/sender names, parse RFC 2822 dates from IMAP envelopes
- decode RFC 2047 encoded words in subject and fromName via RFC2047Decoder before storing in MessageRecord (fixes =?utf-8?Q?...?= raw display) - add RFC 2822 date formatters to Queries.parseDate (EEE, dd MMM yyyy HH:mm:ss Z) so IMAP envelope dates parse correctly instead of falling back to distantPast - parseDate now tries ISO 8601, RFC 2822 with day-of-week, RFC 2822 without Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -82,8 +82,29 @@ extension MailStore {
|
||||
ISO8601DateFormatter()
|
||||
}()
|
||||
|
||||
static func parseDate(_ iso: String) -> Date? {
|
||||
isoFormatterWithFractional.date(from: iso) ?? isoFormatter.date(from: iso)
|
||||
nonisolated(unsafe) private static let rfc2822Formatter: DateFormatter = {
|
||||
let f = DateFormatter()
|
||||
f.locale = Locale(identifier: "en_US_POSIX")
|
||||
f.dateFormat = "EEE, dd MMM yyyy HH:mm:ss Z"
|
||||
return f
|
||||
}()
|
||||
|
||||
nonisolated(unsafe) private static let rfc2822FormatterNoDow: DateFormatter = {
|
||||
let f = DateFormatter()
|
||||
f.locale = Locale(identifier: "en_US_POSIX")
|
||||
f.dateFormat = "dd MMM yyyy HH:mm:ss Z"
|
||||
return f
|
||||
}()
|
||||
|
||||
static func parseDate(_ dateString: String) -> Date? {
|
||||
let trimmed = dateString.trimmingCharacters(in: .whitespaces)
|
||||
// Try ISO 8601 first
|
||||
if let d = isoFormatterWithFractional.date(from: trimmed) { return d }
|
||||
if let d = isoFormatter.date(from: trimmed) { return d }
|
||||
// Try RFC 2822 (IMAP envelope date format)
|
||||
if let d = rfc2822Formatter.date(from: trimmed) { return d }
|
||||
if let d = rfc2822FormatterNoDow.date(from: trimmed) { return d }
|
||||
return nil
|
||||
}
|
||||
|
||||
public static func toMessageSummary(_ record: MessageRecord) -> MessageSummary {
|
||||
|
||||
@@ -277,6 +277,9 @@ public final class SyncCoordinator {
|
||||
) -> MessageRecord {
|
||||
let toJson = encodeAddresses(envelope.to)
|
||||
let ccJson = encodeAddresses(envelope.cc)
|
||||
// Decode RFC 2047 encoded words in subject and sender name
|
||||
let decodedSubject = envelope.subject.map { RFC2047Decoder.decode($0) }
|
||||
let decodedFromName = envelope.from?.name.map { RFC2047Decoder.decode($0) }
|
||||
return MessageRecord(
|
||||
id: UUID().uuidString,
|
||||
accountId: accountId,
|
||||
@@ -285,9 +288,9 @@ public final class SyncCoordinator {
|
||||
messageId: envelope.messageId,
|
||||
inReplyTo: envelope.inReplyTo,
|
||||
refs: envelope.references,
|
||||
subject: envelope.subject,
|
||||
subject: decodedSubject,
|
||||
fromAddress: envelope.from?.address,
|
||||
fromName: envelope.from?.name,
|
||||
fromName: decodedFromName,
|
||||
toAddresses: toJson,
|
||||
ccAddresses: ccJson,
|
||||
date: envelope.date,
|
||||
|
||||
Reference in New Issue
Block a user