246 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			246 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
/**
 | 
						|
 * SMTP Logging Utilities
 | 
						|
 * Provides structured logging for SMTP server components
 | 
						|
 */
 | 
						|
 | 
						|
import * as plugins from '../../../../plugins.ts';
 | 
						|
import { logger } from '../../../../logger.ts';
 | 
						|
import { SecurityLogLevel, SecurityEventType } from '../constants.ts';
 | 
						|
import type { ISmtpSession } from '../interfaces.ts';
 | 
						|
 | 
						|
/**
 | 
						|
 * SMTP connection metadata to include in logs
 | 
						|
 */
 | 
						|
export interface IConnectionMetadata {
 | 
						|
  remoteAddress?: string;
 | 
						|
  remotePort?: number;
 | 
						|
  socketId?: string;
 | 
						|
  secure?: boolean;
 | 
						|
  sessionId?: string;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Log levels for SMTP server
 | 
						|
 */
 | 
						|
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
 | 
						|
 | 
						|
/**
 | 
						|
 * Options for SMTP log
 | 
						|
 */
 | 
						|
export interface ISmtpLogOptions {
 | 
						|
  level?: LogLevel;
 | 
						|
  sessionId?: string;
 | 
						|
  sessionState?: string;
 | 
						|
  remoteAddress?: string;
 | 
						|
  remotePort?: number;
 | 
						|
  command?: string;
 | 
						|
  error?: Error;
 | 
						|
  [key: string]: any;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * SMTP logger - provides structured logging for SMTP server
 | 
						|
 */
 | 
						|
export class SmtpLogger {
 | 
						|
  /**
 | 
						|
   * Log a message with context
 | 
						|
   * @param level - Log level
 | 
						|
   * @param message - Log message
 | 
						|
   * @param options - Additional log options
 | 
						|
   */
 | 
						|
  public static log(level: LogLevel, message: string, options: ISmtpLogOptions = {}): void {
 | 
						|
    // Extract error information if provided
 | 
						|
    const errorInfo = options.error ? {
 | 
						|
      errorMessage: options.error.message,
 | 
						|
      errorStack: options.error.stack,
 | 
						|
      errorName: options.error.name
 | 
						|
    } : {};
 | 
						|
    
 | 
						|
    // Structure log data
 | 
						|
    const logData = {
 | 
						|
      component: 'smtp-server',
 | 
						|
      ...options,
 | 
						|
      ...errorInfo
 | 
						|
    };
 | 
						|
    
 | 
						|
    // Remove error from log data to avoid duplication
 | 
						|
    if (logData.error) {
 | 
						|
      delete logData.error;
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Log through the main logger
 | 
						|
    logger.log(level, message, logData);
 | 
						|
    
 | 
						|
    // Also console log for immediate visibility during development
 | 
						|
    if (level === 'error' || level === 'warn') {
 | 
						|
      console[level](`[SMTP] ${message}`, logData);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  
 | 
						|
  /**
 | 
						|
   * Log debug level message
 | 
						|
   * @param message - Log message
 | 
						|
   * @param options - Additional log options
 | 
						|
   */
 | 
						|
  public static debug(message: string, options: ISmtpLogOptions = {}): void {
 | 
						|
    this.log('debug', message, options);
 | 
						|
  }
 | 
						|
  
 | 
						|
  /**
 | 
						|
   * Log info level message
 | 
						|
   * @param message - Log message
 | 
						|
   * @param options - Additional log options
 | 
						|
   */
 | 
						|
  public static info(message: string, options: ISmtpLogOptions = {}): void {
 | 
						|
    this.log('info', message, options);
 | 
						|
  }
 | 
						|
  
 | 
						|
  /**
 | 
						|
   * Log warning level message
 | 
						|
   * @param message - Log message
 | 
						|
   * @param options - Additional log options
 | 
						|
   */
 | 
						|
  public static warn(message: string, options: ISmtpLogOptions = {}): void {
 | 
						|
    this.log('warn', message, options);
 | 
						|
  }
 | 
						|
  
 | 
						|
  /**
 | 
						|
   * Log error level message
 | 
						|
   * @param message - Log message
 | 
						|
   * @param options - Additional log options
 | 
						|
   */
 | 
						|
  public static error(message: string, options: ISmtpLogOptions = {}): void {
 | 
						|
    this.log('error', message, options);
 | 
						|
  }
 | 
						|
  
 | 
						|
  /**
 | 
						|
   * Log command received from client
 | 
						|
   * @param command - The command string
 | 
						|
   * @param socket - The client socket
 | 
						|
   * @param session - The SMTP session
 | 
						|
   */
 | 
						|
  public static logCommand(command: string, socket: plugins.net.Socket | plugins.tls.TLSSocket, session?: ISmtpSession): void {
 | 
						|
    const clientInfo = {
 | 
						|
      remoteAddress: socket.remoteAddress,
 | 
						|
      remotePort: socket.remotePort,
 | 
						|
      secure: socket instanceof plugins.tls.TLSSocket,
 | 
						|
      sessionId: session?.id,
 | 
						|
      sessionState: session?.state
 | 
						|
    };
 | 
						|
    
 | 
						|
    this.info(`Command received: ${command}`, {
 | 
						|
      ...clientInfo,
 | 
						|
      command: command.split(' ')[0]?.toUpperCase()
 | 
						|
    });
 | 
						|
    
 | 
						|
    // Also log to console for easy debugging
 | 
						|
    console.log(`← ${command}`);
 | 
						|
  }
 | 
						|
  
 | 
						|
  /**
 | 
						|
   * Log response sent to client
 | 
						|
   * @param response - The response string
 | 
						|
   * @param socket - The client socket
 | 
						|
   */
 | 
						|
  public static logResponse(response: string, socket: plugins.net.Socket | plugins.tls.TLSSocket): void {
 | 
						|
    const clientInfo = {
 | 
						|
      remoteAddress: socket.remoteAddress,
 | 
						|
      remotePort: socket.remotePort,
 | 
						|
      secure: socket instanceof plugins.tls.TLSSocket
 | 
						|
    };
 | 
						|
    
 | 
						|
    // Get the response code from the beginning of the response
 | 
						|
    const responseCode = response.substring(0, 3);
 | 
						|
    
 | 
						|
    // Log different levels based on response code
 | 
						|
    if (responseCode.startsWith('2') || responseCode.startsWith('3')) {
 | 
						|
      this.debug(`Response sent: ${response}`, clientInfo);
 | 
						|
    } else if (responseCode.startsWith('4')) {
 | 
						|
      this.warn(`Temporary error response: ${response}`, clientInfo);
 | 
						|
    } else if (responseCode.startsWith('5')) {
 | 
						|
      this.error(`Permanent error response: ${response}`, clientInfo);
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Also log to console for easy debugging
 | 
						|
    console.log(`→ ${response}`);
 | 
						|
  }
 | 
						|
  
 | 
						|
  /**
 | 
						|
   * Log client connection event
 | 
						|
   * @param socket - The client socket
 | 
						|
   * @param eventType - Type of connection event (connect, close, error)
 | 
						|
   * @param session - The SMTP session
 | 
						|
   * @param error - Optional error object for error events
 | 
						|
   */
 | 
						|
  public static logConnection(
 | 
						|
    socket: plugins.net.Socket | plugins.tls.TLSSocket,
 | 
						|
    eventType: 'connect' | 'close' | 'error',
 | 
						|
    session?: ISmtpSession,
 | 
						|
    error?: Error
 | 
						|
  ): void {
 | 
						|
    const clientInfo = {
 | 
						|
      remoteAddress: socket.remoteAddress,
 | 
						|
      remotePort: socket.remotePort,
 | 
						|
      secure: socket instanceof plugins.tls.TLSSocket,
 | 
						|
      sessionId: session?.id,
 | 
						|
      sessionState: session?.state
 | 
						|
    };
 | 
						|
    
 | 
						|
    switch (eventType) {
 | 
						|
      case 'connect':
 | 
						|
        this.info(`New ${clientInfo.secure ? 'secure ' : ''}connection from ${clientInfo.remoteAddress}:${clientInfo.remotePort}`, clientInfo);
 | 
						|
        break;
 | 
						|
        
 | 
						|
      case 'close':
 | 
						|
        this.info(`Connection closed from ${clientInfo.remoteAddress}:${clientInfo.remotePort}`, clientInfo);
 | 
						|
        break;
 | 
						|
        
 | 
						|
      case 'error':
 | 
						|
        this.error(`Connection error from ${clientInfo.remoteAddress}:${clientInfo.remotePort}`, {
 | 
						|
          ...clientInfo,
 | 
						|
          error
 | 
						|
        });
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  
 | 
						|
  /**
 | 
						|
   * Log security event
 | 
						|
   * @param level - Security log level
 | 
						|
   * @param type - Security event type
 | 
						|
   * @param message - Log message
 | 
						|
   * @param details - Event details
 | 
						|
   * @param ipAddress - Client IP address
 | 
						|
   * @param domain - Optional domain involved
 | 
						|
   * @param success - Whether the security check was successful
 | 
						|
   */
 | 
						|
  public static logSecurityEvent(
 | 
						|
    level: SecurityLogLevel,
 | 
						|
    type: SecurityEventType,
 | 
						|
    message: string,
 | 
						|
    details: Record<string, any>,
 | 
						|
    ipAddress?: string,
 | 
						|
    domain?: string,
 | 
						|
    success?: boolean
 | 
						|
  ): void {
 | 
						|
    // Map security log level to system log level
 | 
						|
    const logLevel: LogLevel = level === SecurityLogLevel.DEBUG ? 'debug' :
 | 
						|
                               level === SecurityLogLevel.INFO ? 'info' :
 | 
						|
                               level === SecurityLogLevel.WARN ? 'warn' : 'error';
 | 
						|
    
 | 
						|
    // Log the security event
 | 
						|
    this.log(logLevel, message, {
 | 
						|
      component: 'smtp-security',
 | 
						|
      eventType: type,
 | 
						|
      success,
 | 
						|
      ipAddress,
 | 
						|
      domain,
 | 
						|
      ...details
 | 
						|
    });
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Default instance for backward compatibility
 | 
						|
 */
 | 
						|
export const smtpLogger = SmtpLogger; |