From f4502f15fff28ead39f026db9bb35441c11f9164 Mon Sep 17 00:00:00 2001 From: alma Date: Mon, 21 Apr 2025 15:24:59 +0200 Subject: [PATCH] mail page rest --- app/mail/page.tsx | 257 +++++++++++++++++++++++++++------------------- 1 file changed, 154 insertions(+), 103 deletions(-) diff --git a/app/mail/page.tsx b/app/mail/page.tsx index a7b333c3..b8a89f6c 100644 --- a/app/mail/page.tsx +++ b/app/mail/page.tsx @@ -78,10 +78,14 @@ interface EmailAttachment { } interface ParsedEmail { - text: string; - html: string; - attachments: EmailAttachment[]; - headers?: string; + text: string | null; + html: string | null; + attachments: Array<{ + filename: string; + contentType: string; + encoding: string; + content: string; + }>; } interface EmailMessage { @@ -99,47 +103,91 @@ interface EmailMessage { }; } -function parseFullEmail(emailRaw: string): ParsedEmail | EmailMessage { - // Check if this is a multipart message by looking for boundary definition - const boundaryMatch = emailRaw.match(/boundary="?([^"\r\n;]+)"?/i) || - emailRaw.match(/boundary=([^\r\n;]+)/i); - - if (boundaryMatch) { - const boundary = boundaryMatch[1].trim(); +function parseFullEmail(content: string): ParsedEmail { + try { + // First, try to parse the email headers + const headers = parseEmailHeaders(content); - // Check if there's a preamble before the first boundary - let mainHeaders = ''; - let mainContent = emailRaw; - - // Extract the headers before the first boundary if they exist - const firstBoundaryPos = emailRaw.indexOf('--' + boundary); - if (firstBoundaryPos > 0) { - const headerSeparatorPos = emailRaw.indexOf('\r\n\r\n'); - if (headerSeparatorPos > 0 && headerSeparatorPos < firstBoundaryPos) { - mainHeaders = emailRaw.substring(0, headerSeparatorPos); + // If it's a multipart email, process each part + if (headers.contentType?.includes('multipart')) { + const boundary = extractBoundary(headers.contentType); + if (!boundary) { + throw new Error('No boundary found in multipart content'); } - } - - return processMultipartEmail(emailRaw, boundary, mainHeaders); - } else { - // Split headers and body - const [headers, body] = emailRaw.split(/\r?\n\r?\n/, 2); - // If no boundary is found, treat as a single part message - const emailInfo = parseEmailHeaders(headers); - return { - subject: extractHeader(headers, 'Subject'), - from: extractHeader(headers, 'From'), - to: extractHeader(headers, 'To'), - date: extractHeader(headers, 'Date'), - contentType: emailInfo.contentType, - text: emailInfo.contentType.includes('text/plain') ? body : null, - html: emailInfo.contentType.includes('text/html') ? body : null, - attachments: [], // Add empty attachments array for single part messages - raw: { - headers, - body + const parts = content.split(boundary); + const result: ParsedEmail = { + text: null, + html: null, + attachments: [] + }; + + for (const part of parts) { + if (!part.trim()) continue; + + const partHeaders = parseEmailHeaders(part); + const partContent = part.split('\r\n\r\n')[1] || ''; + + // Handle HTML content + if (partHeaders.contentType?.includes('text/html')) { + const decoded = decodeMIME( + partContent, + partHeaders.encoding || '7bit', + partHeaders.charset || 'utf-8' + ); + result.html = cleanHtml(decoded); + } + // Handle plain text content + else if (partHeaders.contentType?.includes('text/plain')) { + const decoded = decodeMIME( + partContent, + partHeaders.encoding || '7bit', + partHeaders.charset || 'utf-8' + ); + result.text = decoded; + } + // Handle attachments + else if (partHeaders.contentType && !partHeaders.contentType.includes('text/')) { + const filename = extractFilename(partHeaders.contentType) || 'attachment'; + result.attachments.push({ + filename, + contentType: partHeaders.contentType, + encoding: partHeaders.encoding || '7bit', + content: partContent + }); + } } + + return result; + } + + // If it's not multipart, handle as a single part + const body = content.split('\r\n\r\n')[1] || ''; + const decoded = decodeMIME( + body, + headers.encoding || '7bit', + headers.charset || 'utf-8' + ); + + if (headers.contentType?.includes('text/html')) { + return { + html: cleanHtml(decoded), + text: null, + attachments: [] + }; + } + + return { + html: null, + text: decoded, + attachments: [] + }; + } catch (e) { + console.error('Error parsing email:', e); + return { + html: null, + text: content, + attachments: [] }; } } @@ -251,14 +299,71 @@ function decodeMimeContent(content: string): string { return cleanHtml(content); } -// Add this helper function -const renderEmailContent = (email: Email) => { - const decodedContent = decodeMimeContent(email.body); - if (email.body.includes('Content-Type: text/html')) { - return
; +function renderEmailContent(email: Email) { + try { + // First, parse the full email to get headers and body + const parsed = parseFullEmail(email.body); + + // If we have HTML content, render it + if (parsed.html) { + return ( +
+ ); + } + + // If we have text content, render it + if (parsed.text) { + return ( +
+ {parsed.text.split('\n').map((line, i) => ( +

{line}

+ ))} +
+ ); + } + + // If we have attachments, display them + if (parsed.attachments && parsed.attachments.length > 0) { + return ( +
+

Attachments

+
+ {parsed.attachments.map((attachment, index) => ( +
+ + + {attachment.filename} + +
+ ))} +
+
+ ); + } + + // If we couldn't parse the content, try to decode and clean the raw body + const decodedBody = decodeMIME(email.body, 'quoted-printable', 'utf-8'); + const cleanedContent = cleanHtml(decodedBody); + + return ( +
+ {cleanedContent.split('\n').map((line, i) => ( +

{line}

+ ))} +
+ ); + } catch (e) { + console.error('Error rendering email content:', e); + return ( +
+ Error rendering email content. Please try again later. +
+ ); } - return
{decodedContent}
; -}; +} // Add this helper function const decodeEmailContent = (content: string, charset: string = 'utf-8') => { @@ -868,61 +973,7 @@ export default function CourrierPage() {
- {(() => { - try { - const parsed = parseFullEmail(selectedEmail.body); - return ( -
- {/* Display HTML content if available, otherwise fallback to text */} - {parsed.html ? ( -
]*>[\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, '') - }} - /> - ) : ( -
- {(parsed.text || '').split('\n').map((line, i) => ( -

{line}

- ))} -
- )} - - {/* Display attachments if present */} - {parsed.attachments && parsed.attachments.length > 0 && ( -
-

Attachments

-
- {parsed.attachments.map((attachment, index) => ( -
- - - {attachment.filename} - -
- ))} -
-
- )} -
- ); - } catch (e) { - console.error('Error parsing email:', e); - return
Error displaying email content
; - } - })()} + {renderEmailContent(selectedEmail)}