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 ( +{line}
+ ))} +{line}
+ ))} +