diff --git a/app/courrier/page.tsx b/app/courrier/page.tsx index 1bf92b5c..65658a44 100644 --- a/app/courrier/page.tsx +++ b/app/courrier/page.tsx @@ -331,15 +331,24 @@ const initialSidebarItems = [ function getReplyBody(email: Email, type: 'reply' | 'reply-all' | 'forward'): string { if (!email.body) return ''; - const { headers, body } = splitEmailHeadersAndBody(email.body); - const { contentType, encoding, charset } = parseEmailHeaders(headers); - - let content = ''; - - if (contentType.includes('multipart/')) { - const boundary = contentType.match(/boundary="([^"]+)"/)?.[1]; + try { + // Split email into headers and body + const [headersPart, ...bodyParts] = email.body.split('\r\n\r\n'); + if (!headersPart || bodyParts.length === 0) { + throw new Error('Invalid email format: missing headers or body'); + } + + const body = bodyParts.join('\r\n\r\n'); + + // Parse headers using Infomaniak MIME decoder + const headerInfo = parseEmailHeaders(headersPart); + const boundary = extractBoundary(headersPart); + + let content = ''; + + // If it's a multipart email if (boundary) { - const parts = body.split('--' + boundary).filter(part => part.trim()); + const parts = body.split(`--${boundary}`); // Find HTML part first, fallback to text part const htmlPart = parts.find(part => part.toLowerCase().includes('content-type: text/html')); @@ -347,84 +356,64 @@ function getReplyBody(email: Email, type: 'reply' | 'reply-all' | 'forward'): st const selectedPart = htmlPart || textPart; if (selectedPart) { - const partHeaders = selectedPart.split('\r\n\r\n')[0]; - const partBody = selectedPart.split('\r\n\r\n').slice(1).join('\r\n\r\n'); - const { encoding: partEncoding } = parseEmailHeaders(partHeaders); + const [partHeaders, ...partBodyParts] = selectedPart.split('\r\n\r\n'); + const partBody = partBodyParts.join('\r\n\r\n'); + const partHeaderInfo = parseEmailHeaders(partHeaders); - content = partEncoding === 'quoted-printable' - ? decodeQuotedPrintable(partBody, charset) + content = partHeaderInfo.encoding === 'quoted-printable' + ? decodeQuotedPrintable(partBody, partHeaderInfo.charset) : partBody; } + } else { + content = headerInfo.encoding === 'quoted-printable' + ? decodeQuotedPrintable(body, headerInfo.charset) + : body; } - } else { - content = encoding === 'quoted-printable' - ? decodeQuotedPrintable(body, charset) - : body; - } - - // Convert plain text to HTML if needed, preserving line breaks - if (!contentType.includes('text/html')) { - content = content - .split('\n') - .map(line => { - // Preserve empty lines - if (!line.trim()) return '
'; - // Handle quoted text - if (line.startsWith('>')) { - return `

${line}

`; - } - return `

${line}

`; - }) - .join(''); - } - - // Clean HTML content while preserving formatting - content = content - .replace(/]*>[\s\S]*?<\/style>/gi, '') - .replace(/]*>[\s\S]*?<\/script>/gi, '') - .replace(/]*>/gi, '') - .replace(/]*>/gi, '') - .replace(/]*>/gi, '') - .replace(/]*>[\s\S]*?<\/title>/gi, '') - .replace(/]*>[\s\S]*?<\/head>/gi, '') - .replace(/]*>/gi, '') - .replace(/<\/body>/gi, '') - .replace(/]*>/gi, '') - .replace(/<\/html>/gi, ''); - - // Sanitize HTML content while preserving formatting - content = DOMPurify.sanitize(content, { - ALLOWED_TAGS: [ - 'p', 'br', 'div', 'span', 'b', 'i', 'u', 'strong', 'em', - 'blockquote', 'ul', 'ol', 'li', 'a', 'h1', 'h2', 'h3', 'h4', - 'table', 'thead', 'tbody', 'tr', 'td', 'th', 'pre', 'code' - ], - ALLOWED_ATTR: ['href', 'style', 'class', 'target'], - }); - - const date = new Date(email.date).toLocaleString(); - - if (type === 'forward') { - return ` -
-
-

From: ${email.from}

-

Date: ${date}

-

Subject: ${email.subject}

-

To: ${Array.isArray(email.to) ? email.to.join(', ') : email.to}

-
${content}
+ + // Convert plain text to HTML if needed + if (!headerInfo.contentType.includes('text/html')) { + content = content + .split('\n') + .map(line => { + if (!line.trim()) return '
'; + if (line.startsWith('>')) { + return `

${line}

`; + } + return `

${line}

`; + }) + .join(''); + } + + // Clean HTML content + content = cleanHtml(content); + + const date = new Date(email.date).toLocaleString(); + + if (type === 'forward') { + return ` +
+
+

From: ${email.from}

+

Date: ${date}

+

Subject: ${email.subject}

+

To: ${Array.isArray(email.to) ? email.to.join(', ') : email.to}

+
${content}
+
-
- `; - } else { - return ` -
-
-

On ${date}, ${email.from} wrote:

-
${content}
+ `; + } else { + return ` +
+
+

On ${date}, ${email.from} wrote:

+
${content}
+
-
- `; + `; + } + } catch (error) { + console.error('Error processing email body:', error); + return ''; } }