/** * Unified Email Utilities * * This file provides backward compatibility for email utilities. * New code should import directly from the specialized modules: * - email-content.ts (content processing) * - text-direction.ts (direction handling) * - dom-purify-config.ts (sanitization) */ // Import from specialized modules import { sanitizeHtml } from './dom-purify-config'; import { detectTextDirection, applyTextDirection } from './text-direction'; import { extractEmailContent, formatEmailContent, processHtmlContent, formatPlainTextToHtml, isHtmlContent, extractTextFromHtml } from './email-content'; import { EmailMessage, EmailContent, EmailAddress, LegacyEmailMessage } from '@/types/email'; import { adaptLegacyEmail } from '@/lib/utils/email-adapters'; import { decodeInfomaniakEmail, adaptMimeEmail, isMimeFormat } from './email-mime-decoder'; import { format } from 'date-fns'; // Re-export important functions for backward compatibility export { sanitizeHtml, extractEmailContent, formatEmailContent, processHtmlContent, formatPlainTextToHtml, detectTextDirection, applyTextDirection }; /** * Standard interface for formatted email responses */ export interface FormattedEmail { to: string; cc?: string; subject: string; content: EmailContent; attachments?: Array<{ filename: string; contentType: string; content?: string; }>; } /** * Format email addresses for display * Can handle both array of EmailAddress objects or a string */ export function formatEmailAddresses(addresses: EmailAddress[] | string | undefined): string { if (!addresses) return ''; // If already a string, return as is if (typeof addresses === 'string') { return addresses; } // If array, format each address if (Array.isArray(addresses) && addresses.length > 0) { return addresses.map(addr => addr.name && addr.name !== addr.address ? `${addr.name} <${addr.address}>` : addr.address ).join(', '); } return ''; } /** * Format date for display */ export function formatEmailDate(date: Date | string | undefined): string { if (!date) return ''; try { const dateObj = typeof date === 'string' ? new Date(date) : date; return dateObj.toLocaleString('en-US', { weekday: 'short', year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } catch (e) { return typeof date === 'string' ? date : date.toString(); } } /** * Normalize email content to our standard format regardless of input format */ export function normalizeEmailContent(email: any): EmailMessage { if (!email) { throw new Error('Cannot normalize null or undefined email'); } // First check if this is a MIME format email that needs decoding if (email.content && isMimeFormat(email.content)) { try { console.log('Detected MIME format email, decoding...'); // We need to force cast here due to type incompatibility between EmailMessage and the mime result const adaptedEmail = adaptMimeEmail(email); return { ...adaptedEmail, flags: adaptedEmail.flags || [] // Ensure flags is always an array } as EmailMessage; } catch (error) { console.error('Error decoding MIME email:', error); // Continue with regular normalization if MIME decoding fails } } // Check if it's already in the standardized format if (email.content && typeof email.content === 'object' && (email.content.html !== undefined || email.content.text !== undefined)) { // Already in the correct format return email as EmailMessage; } // Otherwise, adapt from legacy format // We need to force cast here due to type incompatibility const adaptedEmail = adaptLegacyEmail(email); return { ...adaptedEmail, flags: adaptedEmail.flags || [] // Ensure flags is always an array } as EmailMessage; } /** * Render normalized email content into HTML for display */ export function renderEmailContent(content: EmailContent | null): string { if (!content) { return '
${sanitizeHtml(htmlContent)}`; } if (textContent) { // Format plain text reply const lines = textContent.split(/\r\n|\r|\n/); textContent = `On ${date}, ${sender} wrote:\n\n${lines.map(line => `> ${line}`).join('\n')}`; } const result = { to, cc: cc || undefined, subject, content: { html: htmlContent, text: textContent, isHtml: true, direction, }, attachments: email.attachments?.map(att => { // Create properly typed attachment if ('name' in att) { return { filename: att.filename || att.name || 'attachment', contentType: att.contentType || 'application/octet-stream', content: att.content }; } return { filename: att.filename || 'attachment', contentType: att.contentType || 'application/octet-stream', content: att.content }; }) }; console.log('formatReplyEmail result:', { to: result.to, subject: result.subject, hasHtml: !!result.content.html, htmlLength: result.content.html?.length || 0, hasText: !!result.content.text, textLength: result.content.text?.length || 0 }); return result; } /** * Format email for forwarding */ export function formatForwardedEmail(originalEmail: EmailMessage | LegacyEmailMessage | null): FormattedEmail { console.log('formatForwardedEmail called:', { emailId: originalEmail?.id }); if (!originalEmail) { console.warn('formatForwardedEmail: No original email provided'); return { to: '', subject: '', content: { text: '', html: '', isHtml: false, direction: 'ltr' } }; } // Adapt legacy format if needed const email = 'content' in originalEmail ? originalEmail : adaptLegacyEmail(originalEmail); // Format subject with Fwd: prefix const subject = email.subject ? (email.subject.toLowerCase().startsWith('fwd:') ? email.subject : `Fwd: ${email.subject}`) : 'Fwd: '; // Get original email info for headers const { fromStr, toStr, ccStr, dateStr } = getFormattedHeaderInfo(email); console.log('Forward header info:', { fromStr, toStr, dateStr, subject }); // Original sent date const date = dateStr; // Get email content const originalContent = email.content; // Extract text and html content let htmlContent = ''; let textContent = ''; let direction: 'ltr' | 'rtl' = 'ltr'; // Handle different content formats if (typeof originalContent === 'string') { console.log('formatForwardedEmail: content is string, length:', originalContent.length); // Simple string content textContent = originalContent; const isHtml = isHtmlContent(originalContent); if (isHtml) { htmlContent = originalContent; } else { // If it's plain text, convert to HTML htmlContent = formatPlainTextToHtml(originalContent); } } else if (originalContent) { console.log('formatForwardedEmail: content is object:', { hasHtml: !!originalContent.html, htmlLength: originalContent.html?.length || 0, hasText: !!originalContent.text, textLength: originalContent.text?.length || 0, direction: originalContent.direction }); // Standard EmailContent object htmlContent = originalContent.html || ''; textContent = originalContent.text || ''; direction = originalContent.direction || 'ltr' as const; // If no HTML but has text, convert text to HTML if (!htmlContent && textContent) { htmlContent = formatPlainTextToHtml(textContent); } } // Create the forwarded email HTML content if (htmlContent) { console.log('Formatting HTML forward, original content length:', htmlContent.length); // Important: First sanitize the content portion only const sanitizedOriginalContent = sanitizeHtml(htmlContent); console.log('Sanitized original content length:', sanitizedOriginalContent.length); // Create the complete forwarded email with header info const fullForwardedEmail = `
| From: | ${fromStr} |
| Date: | ${date} |
| Subject: | ${email.subject || ''} |
| To: | ${toStr} |
| Cc: | ${ccStr} |