Preserve QOutlook priority headers in EML

This commit is contained in:
Курнат Андрей
2026-06-01 19:59:53 +03:00
parent 9bd8827549
commit 15ea571395
5 changed files with 65 additions and 2 deletions
+2 -2
View File
@@ -34,7 +34,7 @@ npm run build
- QWord открывает `.docx` и сохраняет `.docx`; при импорте базовых DOCX-абзацев читает выравнивание, переносы строк, разрывы страниц, табуляцию, закладки, внешние и внутренние гиперссылки-якоря, простые таблицы, базовые растровые изображения, явные цвета текста, подсветку, размер и семейство шрифта из OOXML, а при экспорте DOCX сохраняются заголовки, абзацы, переносы строк, разрывы страниц, табуляция, выравнивание абзацев, закладки из HTML `id`, списки, простые таблицы, внешние и внутренние гиперссылки-якоря, базовые растровые изображения и базовое inline-форматирование текста: полужирный, курсив, подчеркивание, зачеркивание, цвет текста, подсветка, размер и семейство шрифта. - QWord открывает `.docx` и сохраняет `.docx`; при импорте базовых DOCX-абзацев читает выравнивание, переносы строк, разрывы страниц, табуляцию, закладки, внешние и внутренние гиперссылки-якоря, простые таблицы, базовые растровые изображения, явные цвета текста, подсветку, размер и семейство шрифта из OOXML, а при экспорте DOCX сохраняются заголовки, абзацы, переносы строк, разрывы страниц, табуляция, выравнивание абзацев, закладки из HTML `id`, списки, простые таблицы, внешние и внутренние гиперссылки-якоря, базовые растровые изображения и базовое inline-форматирование текста: полужирный, курсив, подчеркивание, зачеркивание, цвет текста, подсветка, размер и семейство шрифта.
- QExcell открывает `.xlsx` и сохраняет `.xlsx`; сетка расширяется по фактически заполненным ячейкам, поддерживает несколько листов, скрытые и veryHidden листы через `sheet state`, закрепленные области через `sheetViews pane`, скрытие сетки через `sheetView showGridLines`, масштаб листа через `sheetView zoomScale`, автофильтр через `autoFilter ref`, область печати через `_xlnm.Print_Area`, цвет вкладки листа через `sheetPr tabColor`, ширину колонок через `cols/col width`, высоту строк через `row ht/customHeight`, активный лист через `workbookView activeTab`, формулы за пределами стартового диапазона A-H, shared formulas XLSX с относительными ссылками, базовые merged cells, внешние и внутренние гиперссылки ячеек, базовые диаграммы листа и базовое форматирование ячеек: полужирный, курсив, подчеркивание, зачеркивание, размер и семейство шрифта, прямые RGB/theme/indexed цвета текста с учетом `xl/theme/theme1.xml`, заливку, горизонтальное и вертикальное выравнивание, перенос текста, границы и цвет границ ячеек, числовые форматы XLSX для дат, процентов, денежных и дробных значений. - QExcell открывает `.xlsx` и сохраняет `.xlsx`; сетка расширяется по фактически заполненным ячейкам, поддерживает несколько листов, скрытые и veryHidden листы через `sheet state`, закрепленные области через `sheetViews pane`, скрытие сетки через `sheetView showGridLines`, масштаб листа через `sheetView zoomScale`, автофильтр через `autoFilter ref`, область печати через `_xlnm.Print_Area`, цвет вкладки листа через `sheetPr tabColor`, ширину колонок через `cols/col width`, высоту строк через `row ht/customHeight`, активный лист через `workbookView activeTab`, формулы за пределами стартового диапазона A-H, shared formulas XLSX с относительными ссылками, базовые merged cells, внешние и внутренние гиперссылки ячеек, базовые диаграммы листа и базовое форматирование ячеек: полужирный, курсив, подчеркивание, зачеркивание, размер и семейство шрифта, прямые RGB/theme/indexed цвета текста с учетом `xl/theme/theme1.xml`, заливку, горизонтальное и вертикальное выравнивание, перенос текста, границы и цвет границ ячеек, числовые форматы XLSX для дат, процентов, денежных и дробных значений.
- QPowerPoint открывает `.pptx` и сохраняет `.pptx`; импорт учитывает порядок слайдов из `presentation.xml`, переносы абзацев и ручные переносы строк `a:br` внутри текстовых блоков, скрытые слайды через атрибут `show`, а также заметки докладчика через relationships слайда, сохраняет скрытые слайды, заметки докладчика, базовые растровые изображения слайдов, пользовательский цвет фона слайда, а также цвет, размер и семейство шрифта текста заголовка/подзаголовка. - QPowerPoint открывает `.pptx` и сохраняет `.pptx`; импорт учитывает порядок слайдов из `presentation.xml`, переносы абзацев и ручные переносы строк `a:br` внутри текстовых блоков, скрытые слайды через атрибут `show`, а также заметки докладчика через relationships слайда, сохраняет скрытые слайды, заметки докладчика, базовые растровые изображения слайдов, пользовательский цвет фона слайда, а также цвет, размер и семейство шрифта текста заголовка/подзаголовка.
- QOutlook открывает `.eml` и одиночные сообщения Outlook `.msg`, сохраняет письма в `.eml`; импорт EML поддерживает простые текстовые письма, `multipart/alternative`, `multipart/mixed`, `multipart/related`, `text/plain`, fallback из `text/html` в читаемый текст, base64, quoted-printable, MIME encoded-word заголовки UTF-8/windows-1251, MIME-вложения и inline-вложения с `Content-ID`, `To`, `Cc`, `Bcc`, `Reply-To`, `Message-ID`, `In-Reply-To` и `References`. Импорт MSG читает тему, отправителя, получателей `To/Cc/Bcc`, `Reply-To`, `Message-ID`, `In-Reply-To` и `References` из транспортных заголовков, текст/HTML body и вложения, если эти поля доступны в MSG-файле. Новые письма можно адресовать через `To/Cc/Bcc`, задавать `Reply-To` и сохранять в EML вместе с добавленными файлами. `.pst` и экспорт в `.msg` пока не реализованы. - QOutlook открывает `.eml` и одиночные сообщения Outlook `.msg`, сохраняет письма в `.eml`; импорт EML поддерживает простые текстовые письма, `multipart/alternative`, `multipart/mixed`, `multipart/related`, `text/plain`, fallback из `text/html` в читаемый текст, base64, quoted-printable, MIME encoded-word заголовки UTF-8/windows-1251, MIME-вложения и inline-вложения с `Content-ID`, `To`, `Cc`, `Bcc`, `Reply-To`, `Message-ID`, `In-Reply-To`, `References`, `Importance`, `X-Priority` и `Priority`. Импорт MSG читает тему, отправителя, получателей `To/Cc/Bcc`, `Reply-To`, `Message-ID`, `In-Reply-To`, `References`, `Importance`, `X-Priority` и `Priority` из транспортных заголовков, текст/HTML body и вложения, если эти поля доступны в MSG-файле. Новые письма можно адресовать через `To/Cc/Bcc`, задавать `Reply-To` и сохранять в EML вместе с добавленными файлами. `.pst` и экспорт в `.msg` пока не реализованы.
В desktop-режиме Electron используются системные диалоги открытия и сохранения файлов. В web-режиме остаются браузерный выбор файла и скачивание результата. В desktop-режиме Electron используются системные диалоги открытия и сохранения файлов. В web-режиме остаются браузерный выбор файла и скачивание результата.
Экспорт PDF в desktop-режиме выполняется через Electron `webContents.printToPDF`; в web-режиме команда открывает браузерную печать, где пользователь может выбрать сохранение в PDF. Экспорт PDF в desktop-режиме выполняется через Electron `webContents.printToPDF`; в web-режиме команда открывает браузерную печать, где пользователь может выбрать сохранение в PDF.
@@ -46,7 +46,7 @@ Desktop-меню поддерживает системные команды дл
- `Ctrl/Cmd+Shift+E`: экспортировать текущую рабочую область в PDF. - `Ctrl/Cmd+Shift+E`: экспортировать текущую рабочую область в PDF.
- `Ctrl/Cmd+P`: печать текущей рабочей области. - `Ctrl/Cmd+P`: печать текущей рабочей области.
Импорт и экспорт реализуют базовую совместимость с Office Open XML, EML и чтением MSG: текст, базовое inline-форматирование, явные `w:br`, `w:br w:type="page"`, `w:tab`, `w:color`, `w:shd`, `w:highlight`, `w:sz`, `w:rFonts` и `w:strike` для переносов строк/разрывов страниц/табуляции/цвета/подсветки/размера/семейства/зачеркивания текста DOCX, выравнивание абзацев DOCX, закладки DOCX через `w:bookmarkStart`/`w:bookmarkEnd`, внешние `w:hyperlink r:id` и внутренние `w:hyperlink w:anchor` гиперссылки DOCX, внешние и внутренние гиперссылки XLSX, базовые растровые изображения DOCX/PPTX, простые таблицы, несколько листов, скрытые/veryHidden листы через `sheet state`, закрепленные области через `sheetViews pane`, скрытие сетки через `sheetView showGridLines`, масштаб листа через `sheetView zoomScale`, автофильтр через `autoFilter ref`, область печати XLSX через `_xlnm.Print_Area`, цвет вкладки листа через `sheetPr tabColor`, ширину колонок через `cols/col width`, высоту строк через `row ht/customHeight` и активный лист XLSX через `workbookView activeTab`, ячейки, формулы, shared formulas XLSX с относительными ссылками, базовые merged cells, базовые стили, RGB/theme/indexed цвета XLSX с учетом `xl/theme/theme1.xml`, размеры и семейства шрифта, зачеркивание ячеек, горизонтальное и вертикальное выравнивание, перенос текста, границы и цвет границ ячеек, числовые форматы и простые bar/line/pie диаграммы ячеек XLSX, текстовые слайды PPTX с переносами абзацев и ручными переносами строк `a:br` при импорте, скрытые слайды PPTX через атрибут `show`, пользовательский фон слайдов PPTX, цвет, размер и семейство шрифта текста заголовка/подзаголовка PPTX, импорт и экспорт заметок докладчика, адресацию `To/Cc/Bcc/Reply-To`, заголовки цепочки писем `Message-ID`/`In-Reply-To`/`References`, MIME encoded-word заголовки UTF-8/windows-1251, MIME-вложения EML, inline-вложения EML с `Content-ID` и базовые поля одиночных MSG-писем. Сложные стили, макросы, сложные диаграммы, сложное позиционирование и обтекание изображений, PST, экспорт MSG и полная fidelity документов Microsoft Office в текущем инкременте не заявлены. Импорт и экспорт реализуют базовую совместимость с Office Open XML, EML и чтением MSG: текст, базовое inline-форматирование, явные `w:br`, `w:br w:type="page"`, `w:tab`, `w:color`, `w:shd`, `w:highlight`, `w:sz`, `w:rFonts` и `w:strike` для переносов строк/разрывов страниц/табуляции/цвета/подсветки/размера/семейства/зачеркивания текста DOCX, выравнивание абзацев DOCX, закладки DOCX через `w:bookmarkStart`/`w:bookmarkEnd`, внешние `w:hyperlink r:id` и внутренние `w:hyperlink w:anchor` гиперссылки DOCX, внешние и внутренние гиперссылки XLSX, базовые растровые изображения DOCX/PPTX, простые таблицы, несколько листов, скрытые/veryHidden листы через `sheet state`, закрепленные области через `sheetViews pane`, скрытие сетки через `sheetView showGridLines`, масштаб листа через `sheetView zoomScale`, автофильтр через `autoFilter ref`, область печати XLSX через `_xlnm.Print_Area`, цвет вкладки листа через `sheetPr tabColor`, ширину колонок через `cols/col width`, высоту строк через `row ht/customHeight` и активный лист XLSX через `workbookView activeTab`, ячейки, формулы, shared formulas XLSX с относительными ссылками, базовые merged cells, базовые стили, RGB/theme/indexed цвета XLSX с учетом `xl/theme/theme1.xml`, размеры и семейства шрифта, зачеркивание ячеек, горизонтальное и вертикальное выравнивание, перенос текста, границы и цвет границ ячеек, числовые форматы и простые bar/line/pie диаграммы ячеек XLSX, текстовые слайды PPTX с переносами абзацев и ручными переносами строк `a:br` при импорте, скрытые слайды PPTX через атрибут `show`, пользовательский фон слайдов PPTX, цвет, размер и семейство шрифта текста заголовка/подзаголовка PPTX, импорт и экспорт заметок докладчика, адресацию `To/Cc/Bcc/Reply-To`, заголовки цепочки писем `Message-ID`/`In-Reply-To`/`References`, заголовки приоритета `Importance`/`X-Priority`/`Priority`, MIME encoded-word заголовки UTF-8/windows-1251, MIME-вложения EML, inline-вложения EML с `Content-ID` и базовые поля одиночных MSG-писем. Сложные стили, макросы, сложные диаграммы, сложное позиционирование и обтекание изображений, PST, экспорт MSG и полная fidelity документов Microsoft Office в текущем инкременте не заявлены.
## Сборка пакета ## Сборка пакета
+12
View File
@@ -68,6 +68,9 @@ export function QOutlook({ mail, onChange, onFolderChange }: QOutlookProps) {
message.messageId ?? "", message.messageId ?? "",
message.inReplyTo ?? "", message.inReplyTo ?? "",
message.references ?? "", message.references ?? "",
message.importance ?? "",
message.xPriority ?? "",
message.priority ?? "",
message.subject, message.subject,
message.body, message.body,
...(message.attachments ?? []).map((attachment) => attachment.fileName) ...(message.attachments ?? []).map((attachment) => attachment.fileName)
@@ -329,6 +332,15 @@ export function QOutlook({ mail, onChange, onFolderChange }: QOutlookProps) {
{activeMessage.references ? `References: ${activeMessage.references}` : ""} {activeMessage.references ? `References: ${activeMessage.references}` : ""}
</p> </p>
) : null} ) : null}
{activeMessage.importance || activeMessage.xPriority || activeMessage.priority ? (
<p className="muted">
{activeMessage.importance ? `Importance: ${activeMessage.importance}` : ""}
{activeMessage.importance && activeMessage.xPriority ? " · " : ""}
{activeMessage.xPriority ? `X-Priority: ${activeMessage.xPriority}` : ""}
{(activeMessage.importance || activeMessage.xPriority) && activeMessage.priority ? " · " : ""}
{activeMessage.priority ? `Priority: ${activeMessage.priority}` : ""}
</p>
) : null}
<p>{activeMessage.body}</p> <p>{activeMessage.body}</p>
{activeAttachments.length > 0 ? ( {activeAttachments.length > 0 ? (
<section className="attachments-panel" aria-label="Вложения письма"> <section className="attachments-panel" aria-label="Вложения письма">
+24
View File
@@ -180,6 +180,9 @@ describe("outlookMail helpers", () => {
"Message-ID: <message-1@example.com>", "Message-ID: <message-1@example.com>",
"In-Reply-To: <parent@example.com>", "In-Reply-To: <parent@example.com>",
"References: <root@example.com> <parent@example.com>", "References: <root@example.com> <parent@example.com>",
"Importance: high",
"X-Priority: 1",
"Priority: urgent",
"Subject: =?UTF-8?B?0JzRg9C70YzRgtC40L/QsNGA0YI=?=", "Subject: =?UTF-8?B?0JzRg9C70YzRgtC40L/QsNGA0YI=?=",
"Date: Mon, 01 Jun 2026 10:00:00 +0300", "Date: Mon, 01 Jun 2026 10:00:00 +0300",
'Content-Type: multipart/alternative; boundary="alt"', 'Content-Type: multipart/alternative; boundary="alt"',
@@ -208,6 +211,9 @@ describe("outlookMail helpers", () => {
expect(imported.messageId).toBe("<message-1@example.com>"); expect(imported.messageId).toBe("<message-1@example.com>");
expect(imported.inReplyTo).toBe("<parent@example.com>"); expect(imported.inReplyTo).toBe("<parent@example.com>");
expect(imported.references).toBe("<root@example.com> <parent@example.com>"); expect(imported.references).toBe("<root@example.com> <parent@example.com>");
expect(imported.importance).toBe("high");
expect(imported.xPriority).toBe("1");
expect(imported.priority).toBe("urgent");
}); });
it("импортирует multipart/mixed EML с вложением", async () => { it("импортирует multipart/mixed EML с вложением", async () => {
@@ -262,6 +268,9 @@ describe("outlookMail helpers", () => {
"Message-ID: <msg-1@example.com>", "Message-ID: <msg-1@example.com>",
"In-Reply-To: <msg-parent@example.com>", "In-Reply-To: <msg-parent@example.com>",
"References: <msg-root@example.com> <msg-parent@example.com>", "References: <msg-root@example.com> <msg-parent@example.com>",
"Importance: low",
"X-Priority: 5",
"Priority: non-urgent",
"From: sender@example.com" "From: sender@example.com"
].join("\r\n"), ].join("\r\n"),
bodyHtml: "<p>HTML&nbsp;текст</p>", bodyHtml: "<p>HTML&nbsp;текст</p>",
@@ -285,6 +294,9 @@ describe("outlookMail helpers", () => {
messageId: "<msg-1@example.com>", messageId: "<msg-1@example.com>",
inReplyTo: "<msg-parent@example.com>", inReplyTo: "<msg-parent@example.com>",
references: "<msg-root@example.com> <msg-parent@example.com>", references: "<msg-root@example.com> <msg-parent@example.com>",
importance: "low",
xPriority: "5",
priority: "non-urgent",
subject: "MSG отчет", subject: "MSG отчет",
body: "HTML текст", body: "HTML текст",
time: "Mon, 01 Jun 2026 10:00:00 GMT", time: "Mon, 01 Jun 2026 10:00:00 GMT",
@@ -317,6 +329,9 @@ describe("outlookMail helpers", () => {
messageId: "<message-1@example.com>", messageId: "<message-1@example.com>",
inReplyTo: "<parent@example.com>", inReplyTo: "<parent@example.com>",
references: "<root@example.com> <parent@example.com>", references: "<root@example.com> <parent@example.com>",
importance: "high",
xPriority: "1",
priority: "urgent",
subject: "Тема", subject: "Тема",
body: "Текст", body: "Текст",
time: "Mon, 01 Jan 2024 10:00:00 +0000", time: "Mon, 01 Jan 2024 10:00:00 +0000",
@@ -333,6 +348,9 @@ describe("outlookMail helpers", () => {
expect(eml).toContain("Message-ID: <message-1@example.com>"); expect(eml).toContain("Message-ID: <message-1@example.com>");
expect(eml).toContain("In-Reply-To: <parent@example.com>"); expect(eml).toContain("In-Reply-To: <parent@example.com>");
expect(eml).toContain("References: <root@example.com> <parent@example.com>"); expect(eml).toContain("References: <root@example.com> <parent@example.com>");
expect(eml).toContain("Importance: high");
expect(eml).toContain("X-Priority: 1");
expect(eml).toContain("Priority: urgent");
expect(eml).toContain("Subject: =?UTF-8?B?"); expect(eml).toContain("Subject: =?UTF-8?B?");
expect(eml.endsWith("Текст")).toBe(true); expect(eml.endsWith("Текст")).toBe(true);
}); });
@@ -349,6 +367,9 @@ describe("outlookMail helpers", () => {
messageId: "<message-1@example.com>", messageId: "<message-1@example.com>",
inReplyTo: "<parent@example.com>", inReplyTo: "<parent@example.com>",
references: "<root@example.com> <parent@example.com>", references: "<root@example.com> <parent@example.com>",
importance: "high",
xPriority: "1",
priority: "urgent",
subject: "Тема", subject: "Тема",
body: "Текст", body: "Текст",
time: "Mon, 01 Jan 2024 10:00:00 +0000", time: "Mon, 01 Jan 2024 10:00:00 +0000",
@@ -377,6 +398,9 @@ describe("outlookMail helpers", () => {
expect(eml).toContain("Message-ID: <message-1@example.com>"); expect(eml).toContain("Message-ID: <message-1@example.com>");
expect(eml).toContain("In-Reply-To: <parent@example.com>"); expect(eml).toContain("In-Reply-To: <parent@example.com>");
expect(eml).toContain("References: <root@example.com> <parent@example.com>"); expect(eml).toContain("References: <root@example.com> <parent@example.com>");
expect(eml).toContain("Importance: high");
expect(eml).toContain("X-Priority: 1");
expect(eml).toContain("Priority: urgent");
expect(eml).toContain("filename*=utf-8''%D0%BE%D1%82%D1%87%D0%B5%D1%82%2E%74%78%74"); expect(eml).toContain("filename*=utf-8''%D0%BE%D1%82%D1%87%D0%B5%D1%82%2E%74%78%74");
expect(extractMessageBody(parsed.headers, parsed.body)).toBe("Текст"); expect(extractMessageBody(parsed.headers, parsed.body)).toBe("Текст");
expect(attachments[0]).toMatchObject({ expect(attachments[0]).toMatchObject({
+24
View File
@@ -91,6 +91,7 @@ export async function importEmlFile(file: File): Promise<ImportedMailMessage> {
...(parsed.headers.bcc ? { bcc: decodeMimeWords(parsed.headers.bcc) } : {}), ...(parsed.headers.bcc ? { bcc: decodeMimeWords(parsed.headers.bcc) } : {}),
...(parsed.headers["reply-to"] ? { replyTo: decodeMimeWords(parsed.headers["reply-to"]) } : {}), ...(parsed.headers["reply-to"] ? { replyTo: decodeMimeWords(parsed.headers["reply-to"]) } : {}),
...mailThreadHeaders(parsed.headers), ...mailThreadHeaders(parsed.headers),
...mailPriorityHeaders(parsed.headers),
subject: decodeMimeWords(parsed.headers.subject || "Без темы"), subject: decodeMimeWords(parsed.headers.subject || "Без темы"),
body: extractMessageBody(parsed.headers, parsed.body), body: extractMessageBody(parsed.headers, parsed.body),
time: parsed.headers.date || new Date().toLocaleString("ru-RU"), time: parsed.headers.date || new Date().toLocaleString("ru-RU"),
@@ -129,6 +130,7 @@ export function msgDataToMailMessage(reader: Pick<MsgReaderInstance, "getAttachm
...(bcc ? { bcc } : {}), ...(bcc ? { bcc } : {}),
...(replyTo ? { replyTo } : {}), ...(replyTo ? { replyTo } : {}),
...mailThreadHeaders(headerFields), ...mailThreadHeaders(headerFields),
...mailPriorityHeaders(headerFields),
subject: firstText(data.subject, decodeMimeWords(headerFields.subject || ""), "Без темы"), subject: firstText(data.subject, decodeMimeWords(headerFields.subject || ""), "Без темы"),
body: msgBodyText(data), body: msgBodyText(data),
time: firstText(data.messageDeliveryTime, data.clientSubmitTime, data.creationTime, data.lastModificationTime, headerFields.date, new Date().toLocaleString("ru-RU")), time: firstText(data.messageDeliveryTime, data.clientSubmitTime, data.creationTime, data.lastModificationTime, headerFields.date, new Date().toLocaleString("ru-RU")),
@@ -424,6 +426,7 @@ export function buildEmlSource(message: MailMessage): string {
`Subject: ${encodeHeaderValue(message.subject || "Без темы")}`, `Subject: ${encodeHeaderValue(message.subject || "Без темы")}`,
`Date: ${sanitizeHeaderValue(message.time || new Date().toUTCString())}`, `Date: ${sanitizeHeaderValue(message.time || new Date().toUTCString())}`,
...optionalThreadHeaders(message), ...optionalThreadHeaders(message),
...optionalPriorityHeaders(message),
"MIME-Version: 1.0", "MIME-Version: 1.0",
`Content-Type: multipart/mixed; boundary="${boundary}"`, `Content-Type: multipart/mixed; boundary="${boundary}"`,
"", "",
@@ -447,6 +450,7 @@ export function buildEmlSource(message: MailMessage): string {
`Subject: ${encodeHeaderValue(message.subject || "Без темы")}`, `Subject: ${encodeHeaderValue(message.subject || "Без темы")}`,
`Date: ${sanitizeHeaderValue(message.time || new Date().toUTCString())}`, `Date: ${sanitizeHeaderValue(message.time || new Date().toUTCString())}`,
...optionalThreadHeaders(message), ...optionalThreadHeaders(message),
...optionalPriorityHeaders(message),
"MIME-Version: 1.0", "MIME-Version: 1.0",
"Content-Type: text/plain; charset=utf-8", "Content-Type: text/plain; charset=utf-8",
"Content-Transfer-Encoding: 8bit", "Content-Transfer-Encoding: 8bit",
@@ -485,6 +489,26 @@ function mailThreadHeaders(headers: MailHeaders): Pick<MailMessage, "messageId"
}; };
} }
function optionalPriorityHeaders(message: MailMessage): string[] {
return [
...(message.importance?.trim() ? [`Importance: ${sanitizeHeaderValue(message.importance)}`] : []),
...(message.xPriority?.trim() ? [`X-Priority: ${sanitizeHeaderValue(message.xPriority)}`] : []),
...(message.priority?.trim() ? [`Priority: ${sanitizeHeaderValue(message.priority)}`] : [])
];
}
function mailPriorityHeaders(headers: MailHeaders): Pick<MailMessage, "importance" | "xPriority" | "priority"> {
const importance = sanitizeHeaderValue(headers.importance || "");
const xPriority = sanitizeHeaderValue(headers["x-priority"] || "");
const priority = sanitizeHeaderValue(headers.priority || "");
return {
...(importance ? { importance } : {}),
...(xPriority ? { xPriority } : {}),
...(priority ? { priority } : {})
};
}
export function decodeMimeWords(value: string): string { export function decodeMimeWords(value: string): string {
return value return value
.replace(/(=\?[^?]+\?[bqBQ]\?[^?]+\?=)\s+(?==\?[^?]+\?[bqBQ]\?[^?]+\?=)/g, "$1") .replace(/(=\?[^?]+\?[bqBQ]\?[^?]+\?=)\s+(?==\?[^?]+\?[bqBQ]\?[^?]+\?=)/g, "$1")
+3
View File
@@ -129,6 +129,9 @@ export interface MailMessage {
messageId?: string; messageId?: string;
inReplyTo?: string; inReplyTo?: string;
references?: string; references?: string;
importance?: string;
xPriority?: string;
priority?: string;
subject: string; subject: string;
body: string; body: string;
time: string; time: string;