diff --git a/app/courrier/page.tsx b/app/courrier/page.tsx index 7da31660..d9c4ec4a 100644 --- a/app/courrier/page.tsx +++ b/app/courrier/page.tsx @@ -323,13 +323,18 @@ function getReplyBody(email: Email | null, type: 'reply' | 'replyAll' | 'forward 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 our MIME decoder + // Parse headers using Infomaniak MIME decoder const headerInfo = parseEmailHeaders(headersPart); const boundary = extractBoundary(headersPart); let content = ''; + let isHtml = false; // If it's a multipart email if (boundary) { @@ -339,23 +344,76 @@ function getReplyBody(email: Email | null, type: 'reply' | 'replyAll' | 'forward if (!part.trim()) continue; const [partHeaders, ...partBodyParts] = part.split('\r\n\r\n'); + if (!partHeaders || partBodyParts.length === 0) continue; + const partBody = partBodyParts.join('\r\n\r\n'); - const partHeaderInfo = parseEmailHeaders(partHeaders); + const contentType = extractHeader(partHeaders, 'Content-Type').toLowerCase(); + const encoding = extractHeader(partHeaders, 'Content-Transfer-Encoding').toLowerCase(); + const charset = extractHeader(partHeaders, 'charset') || 'utf-8'; - if (partHeaderInfo.contentType.includes('text/plain')) { - content = decodeQuotedPrintable(partBody, partHeaderInfo.charset); - break; - } else if (partHeaderInfo.contentType.includes('text/html') && !content) { - content = cleanHtml(decodeQuotedPrintable(partBody, partHeaderInfo.charset)); + try { + let decodedContent = ''; + if (encoding === 'base64') { + decodedContent = decodeBase64(partBody, charset); + } else if (encoding === 'quoted-printable') { + decodedContent = decodeQuotedPrintable(partBody, charset); + } else { + decodedContent = convertCharset(partBody, charset); + } + + if (contentType.includes('text/html')) { + // For HTML content, preserve the HTML structure + content = decodedContent + .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, ''); + isHtml = true; + break; // Prefer HTML content + } else if (contentType.includes('text/plain') && !content) { + content = decodedContent; + } + } catch (partError) { + console.error('Error processing email part:', partError); + continue; } } } // If no content found or not multipart, try to decode the whole body if (!content) { - content = decodeQuotedPrintable(body, headerInfo.charset); + const encoding = extractHeader(headersPart, 'Content-Transfer-Encoding').toLowerCase(); + const charset = extractHeader(headersPart, 'charset') || 'utf-8'; + + if (encoding === 'base64') { + content = decodeBase64(body, charset); + } else if (encoding === 'quoted-printable') { + content = decodeQuotedPrintable(body, charset); + } else { + content = convertCharset(body, charset); + } + if (headerInfo.contentType.includes('text/html')) { - content = cleanHtml(content); + 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, ''); + isHtml = true; } } @@ -370,31 +428,46 @@ function getReplyBody(email: Email | null, type: 'reply' | 'replyAll' | 'forward hour12: false }); - let replyHeader = ''; + const from = email.fromName || email.from; + const subject = email.subject || '(No subject)'; + if (type === 'forward') { - replyHeader = `\n\n---------- Forwarded message ----------\n`; - replyHeader += `From: ${email.from}\n`; - replyHeader += `Date: ${formattedDate}\n`; - replyHeader += `Subject: ${email.subject}\n`; - replyHeader += `To: ${email.to}\n`; - if (email.cc) { - replyHeader += `Cc: ${email.cc}\n`; + if (isHtml) { + return `---------- Forwarded message ---------
+From: ${from}
+Date: ${formattedDate}
+Subject: ${subject}
+To: ${email.to}
+
+${content}`; + } else { + return `---------- Forwarded message --------- +From: ${from} +Date: ${formattedDate} +Subject: ${subject} +To: ${email.to} + +${content}`; } - replyHeader += `\n`; } else { - replyHeader = `\n\nOn ${formattedDate}, ${email.from} wrote:\n`; + if (isHtml) { + return `

+`; + } else { + return `\n\nOn ${formattedDate}, ${from} wrote: +> ${content.split('\n').join('\n> ')}`; + } } - - // Add reply prefix to each line - const prefixedContent = content - .split('\n') - .map(line => `> ${line}`) - .join('\n'); - - return replyHeader + prefixedContent; } catch (error) { - console.error('Error getting reply body:', error); - return email.body; + console.error('Error formatting reply:', error); + return ''; } }