feat: MSK, MSK CONNECT and SES
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
export function decodeRawMime(rawData: string): string {
|
||||
try {
|
||||
return atob(rawData)
|
||||
} catch {
|
||||
return rawData
|
||||
}
|
||||
}
|
||||
|
||||
export function parseMimeAttachments(rawMime: string): Array<{filename: string | null; contentType: string; disposition: string; contentId: string | null}> {
|
||||
const boundary = /^Content-Type:\s*multipart\/[^;]+;\s*boundary="?([^";\r\n]+)"?/im.exec(rawMime)?.[1]
|
||||
if (!boundary) return []
|
||||
return rawMime.split(`--${boundary}`).slice(1, -1).flatMap((part) => {
|
||||
const headerBlock = part.replace(/^\r?\n/, '').split(/\r?\n\r?\n/, 1)[0]
|
||||
const headers = Object.fromEntries(headerBlock.replace(/\r?\n[ \t]+/g, ' ').split(/\r?\n/).flatMap((line) => {
|
||||
const separator = line.indexOf(':')
|
||||
return separator === -1 ? [] : [[line.slice(0, separator).toLowerCase(), line.slice(separator + 1).trim()]]
|
||||
})) as Record<string, string>
|
||||
const contentType = headers['content-type'] ?? 'application/octet-stream'
|
||||
const dispositionHeader = headers['content-disposition'] ?? ''
|
||||
const filename = parameter(dispositionHeader, 'filename') ?? parameter(contentType, 'name')
|
||||
if (!filename && !/attachment|inline/i.test(dispositionHeader)) return []
|
||||
return [{filename, contentType: contentType.split(';', 1)[0], disposition: /^inline/i.test(dispositionHeader) ? 'inline' : 'attachment', contentId: headers['content-id']?.replace(/^<|>$/g, '') ?? null}]
|
||||
})
|
||||
}
|
||||
|
||||
export function inlineMimeImages(rawMime: string): Record<string, string> {
|
||||
const boundary = /^Content-Type:\s*multipart\/[^;]+;\s*boundary="?([^";\r\n]+)"?/im.exec(rawMime)?.[1]
|
||||
if (!boundary) return {}
|
||||
return Object.fromEntries(rawMime.split(`--${boundary}`).slice(1, -1).flatMap((part) => {
|
||||
const [headerBlock, body] = part.replace(/^\r?\n/, '').split(/\r?\n\r?\n/, 2)
|
||||
const contentType = /^Content-Type:\s*([^;\r\n]+)/im.exec(headerBlock)?.[1]?.toLowerCase()
|
||||
const contentId = /^Content-ID:\s*<([^>]+)>/im.exec(headerBlock)?.[1]
|
||||
const encoding = /^Content-Transfer-Encoding:\s*base64/im.test(headerBlock)
|
||||
if (!contentType?.startsWith('image/') || !contentId || !encoding || !body) return []
|
||||
return [[contentId, `data:${contentType};base64,${body.replace(/\s/g, '')}`]]
|
||||
}))
|
||||
}
|
||||
|
||||
export function htmlMimePart(rawMime: string): string | null {
|
||||
const boundary = /^Content-Type:\s*multipart\/[^;]+;\s*boundary="?([^";\r\n]+)"?/im.exec(rawMime)?.[1]
|
||||
if (!boundary) return null
|
||||
for (const part of rawMime.split(`--${boundary}`).slice(1, -1)) {
|
||||
const [headerBlock, body] = part.replace(/^\r?\n/, '').split(/\r?\n\r?\n/, 2)
|
||||
if (!/^Content-Type:\s*text\/html(?:;|\s|$)/im.test(headerBlock) || !body) continue
|
||||
if (/^Content-Transfer-Encoding:\s*base64/im.test(headerBlock)) {
|
||||
try {
|
||||
return atob(body.replace(/\s/g, ''))
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
return body
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function parameter(value: string, name: string): string | null {
|
||||
const match = new RegExp(`${name}\\*?=(?:UTF-8''|")?([^;"]+)`, 'i').exec(value)
|
||||
return match?.[1]?.trim() ?? null
|
||||
}
|
||||
Reference in New Issue
Block a user