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()
|
ISO8601DateFormatter()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
static func parseDate(_ iso: String) -> Date? {
|
nonisolated(unsafe) private static let rfc2822Formatter: DateFormatter = {
|
||||||
isoFormatterWithFractional.date(from: iso) ?? isoFormatter.date(from: iso)
|
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 {
|
public static func toMessageSummary(_ record: MessageRecord) -> MessageSummary {
|
||||||
|
|||||||
@@ -277,6 +277,9 @@ public final class SyncCoordinator {
|
|||||||
) -> MessageRecord {
|
) -> MessageRecord {
|
||||||
let toJson = encodeAddresses(envelope.to)
|
let toJson = encodeAddresses(envelope.to)
|
||||||
let ccJson = encodeAddresses(envelope.cc)
|
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(
|
return MessageRecord(
|
||||||
id: UUID().uuidString,
|
id: UUID().uuidString,
|
||||||
accountId: accountId,
|
accountId: accountId,
|
||||||
@@ -285,9 +288,9 @@ public final class SyncCoordinator {
|
|||||||
messageId: envelope.messageId,
|
messageId: envelope.messageId,
|
||||||
inReplyTo: envelope.inReplyTo,
|
inReplyTo: envelope.inReplyTo,
|
||||||
refs: envelope.references,
|
refs: envelope.references,
|
||||||
subject: envelope.subject,
|
subject: decodedSubject,
|
||||||
fromAddress: envelope.from?.address,
|
fromAddress: envelope.from?.address,
|
||||||
fromName: envelope.from?.name,
|
fromName: decodedFromName,
|
||||||
toAddresses: toJson,
|
toAddresses: toJson,
|
||||||
ccAddresses: ccJson,
|
ccAddresses: ccJson,
|
||||||
date: envelope.date,
|
date: envelope.date,
|
||||||
|
|||||||
Reference in New Issue
Block a user