/**
* Unified Email Utilities
*
* This file contains all email-related utility functions:
* - Content normalization
* - Content sanitization
* - Email formatting (replies, forwards)
* - Text direction detection
*/
import {
EmailMessage,
EmailContent,
EmailAddress,
LegacyEmailMessage
} from '@/types/email';
import { adaptLegacyEmail } from '@/lib/utils/email-adapters';
import { decodeInfomaniakEmail, adaptMimeEmail, isMimeFormat } from './email-mime-decoder';
import { detectTextDirection, applyTextDirection } from '@/lib/utils/text-direction';
import { sanitizeHtml } from '@/lib/utils/dom-sanitizer';
// Remove all local DOMPurify configuration - now using centralized version
/**
* 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();
}
}
// Re-export sanitizeHtml for convenience
export { sanitizeHtml };
/**
* Format plain text for HTML display with proper line breaks
*/
export function formatPlainTextToHtml(text: string | null | undefined): string {
if (!text) return '';
// Escape HTML characters to prevent XSS
const escapedText = text
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
// Format plain text with proper line breaks and paragraphs
return escapedText
.replace(/\r\n|\r|\n/g, '
') // Convert all newlines to
.replace(/((?:
){2,})/g, '
') // Convert multiple newlines to paragraphs
.replace(/
<\/p>/g, '
/g, '
'); // Fix any
combinations
}
/**
* Normalize email content to our standard format regardless of input format
* This is the key function that handles all the different email content formats
*/
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...');
return adaptMimeEmail(email);
} 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
return adaptLegacyEmail(email);
}
/**
* Render normalized email content into HTML for display
*/
export function renderEmailContent(content: EmailContent | null): string {
if (!content) {
return '
`; // Apply consistent text direction const htmlContent = applyTextDirection(replyBody); // Create plain text content const textContent = ` On ${dateStr}, ${fromStr} wrote: > ${originalTextContent.split('\n').join('\n> ')} `; return { to, cc, subject, content: { text: textContent, html: htmlContent, isHtml: true, direction: 'ltr' as const // Reply is LTR, but original content keeps its direction in the blockquote } }; } /** * Format email for forwarding */ export function formatForwardedEmail(originalEmail: EmailMessage | LegacyEmailMessage | null): FormattedEmail { if (!originalEmail) { return { to: '', subject: '', content: { text: '', html: '', isHtml: false, direction: 'ltr' as const } }; } // Format the subject const subject = originalEmail.subject && !originalEmail.subject.startsWith('Fwd:') ? `Fwd: ${originalEmail.subject}` : originalEmail.subject || ''; // Format from, to, cc for the header const fromStr = Array.isArray(originalEmail.from) ? originalEmail.from.map((addr: any) => { if (typeof addr === 'string') return addr; return addr.name ? `${addr.name} <${addr.address}>` : addr.address; }).join(', ') : typeof originalEmail.from === 'string' ? originalEmail.from : 'Unknown Sender'; const toStr = Array.isArray(originalEmail.to) ? originalEmail.to.map((addr: any) => { if (typeof addr === 'string') return addr; return addr.name ? `${addr.name} <${addr.address}>` : addr.address; }).join(', ') : typeof originalEmail.to === 'string' ? originalEmail.to : ''; const ccStr = Array.isArray(originalEmail.cc) ? originalEmail.cc.map((addr: any) => { if (typeof addr === 'string') return addr; return addr.name ? `${addr.name} <${addr.address}>` : addr.address; }).join(', ') : typeof originalEmail.cc === 'string' ? originalEmail.cc : ''; const dateStr = originalEmail.date ? new Date(originalEmail.date).toLocaleString() : 'Unknown Date'; // Extract original content const originalTextContent = typeof originalEmail?.content === 'object' ? originalEmail.content.text : typeof originalEmail?.content === 'string' ? originalEmail.content : originalEmail?.text || ''; const originalHtmlContent = typeof originalEmail?.content === 'object' ? originalEmail.content.html : originalEmail?.html || (typeof originalEmail?.content === 'string' && originalEmail?.content.includes('<') ? originalEmail.content : ''); // Get the direction from the original email const originalDirection = typeof originalEmail?.content === 'object' ? originalEmail.content.direction : detectTextDirection(originalTextContent); // Create forwarded content with header information const forwardBody = `On ${dateStr}, ${fromStr} wrote:
${originalHtmlContent || originalTextContent.replace(/\n/g, '
')}
---------- Forwarded message ---------
From: ${fromStr}
Date: ${dateStr}
Subject: ${originalEmail?.subject || ''}
To: ${toStr}
${ccStr ? `Cc: ${ccStr}
` : ''}