Files
mail-hub/lib/core/models/address.dart
T
hz4th_coder 6c1225f588 MailHub v1.0.0: 跨平台邮箱客户端(纯 Dart IMAP/SMTP/MIME 协议栈)
- 登录: QQ/163/126/189/搜狐/Gmail/Outlook/Yahoo/iCloud/企业邮/自定义, 自动识别+测试连接
- 收信: IMAP 多文件夹同步/未读/星标/搜索(本地+服务器)
- 读信: HTML渲染/CID内嵌图/原文查看/附件下载
- 写信: 收件人/抄送/密送/附件/富文本/草稿/回复转发
- 存储: SQLite + 系统安全存储(DPAPI/Keychain/Keystore)
- 测试: 单元测试 + Mock IMAP/SMTP 服务器集成测试(23项全过)
- 可迁移: Windows/Android/iOS/macOS/Linux 一套代码
2026-08-18 01:10:11 +08:00

115 lines
3.5 KiB
Dart

/// 地址(发件人/收件人)与邮件相关模型
library;
class MailAddress {
final String name;
final String email;
const MailAddress(this.name, this.email);
bool get isEmpty => email.trim().isEmpty;
String get display => name.trim().isNotEmpty ? '$name <$email>' : email;
factory MailAddress.parse(String raw) {
final s = raw.trim();
if (s.isEmpty) return const MailAddress('', '');
// "Name" <email@x.com>
final m1 = RegExp(r'"([^"]*)"\s*<([^>]+)>').firstMatch(s);
if (m1 != null) return MailAddress(m1.group(1)!, m1.group(2)!.trim());
// Name <email@x.com>
final m2 = RegExp(r'([^<]*?)\s*<([^>]+)>').firstMatch(s);
if (m2 != null) {
return MailAddress(m2.group(1)!.trim(), m2.group(2)!.trim());
}
// 纯 email
return MailAddress('', s);
}
static List<MailAddress> parseList(String? raw) {
if (raw == null || raw.trim().isEmpty) return const [];
// 按逗号分割但跳过引号内的逗号
final result = <MailAddress>[];
final buf = StringBuffer();
var inQuote = false;
for (var i = 0; i < raw.length; i++) {
final c = raw[i];
if (c == '"') inQuote = !inQuote;
if (c == ',' && !inQuote) {
final a = MailAddress.parse(buf.toString());
if (!a.isEmpty) result.add(a);
buf.clear();
} else {
buf.write(c);
}
}
final last = MailAddress.parse(buf.toString());
if (!last.isEmpty) result.add(last);
return result;
}
Map<String, Object?> toJson() => {'name': name, 'email': email};
factory MailAddress.fromJson(Map<String, Object?> j) =>
MailAddress((j['name'] as String?) ?? '', (j['email'] as String?) ?? '');
}
class MailAttachment {
final String fileName;
final String contentType;
final String? contentId; // 内嵌图片 CID
final bool inline;
final int size;
final String? localPath; // 已下载到本地的路径
final String? encoding; // base64 / quoted-printable / 7bit / 8bit / binary
final Uint8ListBytes? data; // 内存中的原始数据(可选, 大附件不常驻)
const MailAttachment({
required this.fileName,
this.contentType = 'application/octet-stream',
this.contentId,
this.inline = false,
this.size = 0,
this.localPath,
this.encoding,
this.data,
});
MailAttachment copyWith({String? localPath, Uint8ListBytes? data}) =>
MailAttachment(
fileName: fileName,
contentType: contentType,
contentId: contentId,
inline: inline,
size: size,
localPath: localPath ?? this.localPath,
encoding: encoding,
data: data ?? this.data,
);
Map<String, Object?> toJson() => {
'fileName': fileName,
'contentType': contentType,
'contentId': contentId,
'inline': inline ? 1 : 0,
'size': size,
'localPath': localPath,
'encoding': encoding,
};
factory MailAttachment.fromJson(Map<String, Object?> j) => MailAttachment(
fileName: (j['fileName'] as String?) ?? '',
contentType: (j['contentType'] as String?) ?? 'application/octet-stream',
contentId: j['contentId'] as String?,
inline: (j['inline'] as int?) == 1,
size: (j['size'] as int?) ?? 0,
localPath: j['localPath'] as String?,
encoding: j['encoding'] as String?,
);
}
/// 用于避免直接引入 dart:typed_data 到模型层的轻量包装(可序列化)
class Uint8ListBytes {
final List<int> bytes;
const Uint8ListBytes(this.bytes);
}