655 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
		
		
			
		
	
	
			655 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
|  | /** | ||
|  |  * SMTP Server Interfaces | ||
|  |  * Defines all the interfaces used by the SMTP server implementation | ||
|  |  */ | ||
|  | 
 | ||
|  | import * as plugins from '../../../plugins.ts'; | ||
|  | import type { Email } from '../../core/classes.email.ts'; | ||
|  | import type { UnifiedEmailServer } from '../../routing/classes.unified.email.server.ts'; | ||
|  | 
 | ||
|  | // Re-export types from other modules  
 | ||
|  | import { SmtpState } from '../interfaces.ts'; | ||
|  | import { SmtpCommand } from './constants.ts'; | ||
|  | export { SmtpState, SmtpCommand }; | ||
|  | export type { IEnvelopeRecipient } from '../interfaces.ts'; | ||
|  | 
 | ||
|  | /** | ||
|  |  * Interface for components that need cleanup | ||
|  |  */ | ||
|  | export interface IDestroyable { | ||
|  |   /** | ||
|  |    * Clean up all resources (timers, listeners, etc) | ||
|  |    */ | ||
|  |   destroy(): void | Promise<void>; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * SMTP authentication credentials | ||
|  |  */ | ||
|  | export interface ISmtpAuth { | ||
|  |   /** | ||
|  |    * Username for authentication | ||
|  |    */ | ||
|  |   username: string; | ||
|  |    | ||
|  |   /** | ||
|  |    * Password for authentication | ||
|  |    */ | ||
|  |   password: string; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * SMTP envelope (sender and recipients) | ||
|  |  */ | ||
|  | export interface ISmtpEnvelope { | ||
|  |   /** | ||
|  |    * Mail from address | ||
|  |    */ | ||
|  |   mailFrom: { | ||
|  |     address: string; | ||
|  |     args?: Record<string, string>; | ||
|  |   }; | ||
|  |    | ||
|  |   /** | ||
|  |    * Recipients list | ||
|  |    */ | ||
|  |   rcptTo: Array<{ | ||
|  |     address: string; | ||
|  |     args?: Record<string, string>; | ||
|  |   }>; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * SMTP session representing a client connection | ||
|  |  */ | ||
|  | export interface ISmtpSession { | ||
|  |   /** | ||
|  |    * Unique session identifier | ||
|  |    */ | ||
|  |   id: string; | ||
|  |    | ||
|  |   /** | ||
|  |    * Current state of the SMTP session | ||
|  |    */ | ||
|  |   state: SmtpState; | ||
|  |    | ||
|  |   /** | ||
|  |    * Client's hostname from EHLO/HELO | ||
|  |    */ | ||
|  |   clientHostname: string | null; | ||
|  |    | ||
|  |   /** | ||
|  |    * Whether TLS is active for this session | ||
|  |    */ | ||
|  |   secure: boolean; | ||
|  |    | ||
|  |   /** | ||
|  |    * Authentication status | ||
|  |    */ | ||
|  |   authenticated: boolean; | ||
|  |    | ||
|  |   /** | ||
|  |    * Authentication username if authenticated | ||
|  |    */ | ||
|  |   username?: string; | ||
|  |    | ||
|  |   /** | ||
|  |    * Transaction envelope | ||
|  |    */ | ||
|  |   envelope: ISmtpEnvelope; | ||
|  |    | ||
|  |   /** | ||
|  |    * When the session was created | ||
|  |    */ | ||
|  |   createdAt: Date; | ||
|  |    | ||
|  |   /** | ||
|  |    * Last activity timestamp | ||
|  |    */ | ||
|  |   lastActivity: number; | ||
|  |    | ||
|  |   /** | ||
|  |    * Client's IP address | ||
|  |    */ | ||
|  |   remoteAddress: string; | ||
|  |    | ||
|  |   /** | ||
|  |    * Client's port | ||
|  |    */ | ||
|  |   remotePort: number; | ||
|  |    | ||
|  |   /** | ||
|  |    * Additional session data | ||
|  |    */ | ||
|  |   data?: Record<string, any>; | ||
|  |    | ||
|  |   /** | ||
|  |    * Message size if SIZE extension is used | ||
|  |    */ | ||
|  |   messageSize?: number; | ||
|  |    | ||
|  |   /** | ||
|  |    * Server capabilities advertised to client | ||
|  |    */ | ||
|  |   capabilities?: string[]; | ||
|  |    | ||
|  |   /** | ||
|  |    * Buffer for incomplete data | ||
|  |    */ | ||
|  |   dataBuffer?: string; | ||
|  |    | ||
|  |   /** | ||
|  |    * Flag to track if we're currently receiving DATA | ||
|  |    */ | ||
|  |   receivingData?: boolean; | ||
|  |    | ||
|  |   /** | ||
|  |    * The raw email data being received | ||
|  |    */ | ||
|  |   rawData?: string; | ||
|  |    | ||
|  |   /** | ||
|  |    * Greeting sent to client | ||
|  |    */ | ||
|  |   greeting?: string; | ||
|  |    | ||
|  |   /** | ||
|  |    * Whether EHLO has been sent | ||
|  |    */ | ||
|  |   ehloSent?: boolean; | ||
|  |    | ||
|  |   /** | ||
|  |    * Whether HELO has been sent | ||
|  |    */ | ||
|  |   heloSent?: boolean; | ||
|  |    | ||
|  |   /** | ||
|  |    * TLS options for this session | ||
|  |    */ | ||
|  |   tlsOptions?: any; | ||
|  |    | ||
|  |   /** | ||
|  |    * Whether TLS is being used | ||
|  |    */ | ||
|  |   useTLS?: boolean; | ||
|  |    | ||
|  |   /** | ||
|  |    * Mail from address for this transaction | ||
|  |    */ | ||
|  |   mailFrom?: string; | ||
|  |    | ||
|  |   /** | ||
|  |    * Recipients for this transaction | ||
|  |    */ | ||
|  |   rcptTo?: string[]; | ||
|  |    | ||
|  |   /** | ||
|  |    * Email data being received | ||
|  |    */ | ||
|  |   emailData?: string; | ||
|  |    | ||
|  |   /** | ||
|  |    * Chunks of email data | ||
|  |    */ | ||
|  |   emailDataChunks?: string[]; | ||
|  |    | ||
|  |   /** | ||
|  |    * Timeout ID for data reception | ||
|  |    */ | ||
|  |   dataTimeoutId?: NodeJS.Timeout; | ||
|  |    | ||
|  |   /** | ||
|  |    * Whether connection has ended | ||
|  |    */ | ||
|  |   connectionEnded?: boolean; | ||
|  |    | ||
|  |   /** | ||
|  |    * Size of email data being received | ||
|  |    */ | ||
|  |   emailDataSize?: number; | ||
|  |    | ||
|  |   /** | ||
|  |    * Processing mode for this session | ||
|  |    */ | ||
|  |   processingMode?: string; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Session manager interface | ||
|  |  */ | ||
|  | export interface ISessionManager extends IDestroyable { | ||
|  |   /** | ||
|  |    * Create a new session for a socket | ||
|  |    */ | ||
|  |   createSession(socket: plugins.net.Socket | plugins.tls.TLSSocket, secure?: boolean): ISmtpSession; | ||
|  |    | ||
|  |   /** | ||
|  |    * Get session by socket | ||
|  |    */ | ||
|  |   getSession(socket: plugins.net.Socket | plugins.tls.TLSSocket): ISmtpSession | undefined; | ||
|  |    | ||
|  |   /** | ||
|  |    * Update session state | ||
|  |    */ | ||
|  |   updateSessionState(session: ISmtpSession, newState: SmtpState): void; | ||
|  |    | ||
|  |   /** | ||
|  |    * Remove a session | ||
|  |    */ | ||
|  |   removeSession(socket: plugins.net.Socket | plugins.tls.TLSSocket): void; | ||
|  |    | ||
|  |   /** | ||
|  |    * Clear all sessions | ||
|  |    */ | ||
|  |   clearAllSessions(): void; | ||
|  |    | ||
|  |   /** | ||
|  |    * Get all active sessions | ||
|  |    */ | ||
|  |   getAllSessions(): ISmtpSession[]; | ||
|  |    | ||
|  |   /** | ||
|  |    * Get session count | ||
|  |    */ | ||
|  |   getSessionCount(): number; | ||
|  |    | ||
|  |   /** | ||
|  |    * Update last activity for a session | ||
|  |    */ | ||
|  |   updateLastActivity(socket: plugins.net.Socket | plugins.tls.TLSSocket): void; | ||
|  |    | ||
|  |   /** | ||
|  |    * Check for timed out sessions | ||
|  |    */ | ||
|  |   checkTimeouts(timeoutMs: number): ISmtpSession[]; | ||
|  |    | ||
|  |   /** | ||
|  |    * Update session activity timestamp | ||
|  |    */ | ||
|  |   updateSessionActivity(session: ISmtpSession): void; | ||
|  |    | ||
|  |   /** | ||
|  |    * Replace socket in session (for TLS upgrade) | ||
|  |    */ | ||
|  |   replaceSocket(oldSocket: plugins.net.Socket | plugins.tls.TLSSocket, newSocket: plugins.net.Socket | plugins.tls.TLSSocket): boolean; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Connection manager interface | ||
|  |  */ | ||
|  | export interface IConnectionManager extends IDestroyable { | ||
|  |   /** | ||
|  |    * Handle a new connection | ||
|  |    */ | ||
|  |   handleConnection(socket: plugins.net.Socket | plugins.tls.TLSSocket, secure: boolean): Promise<void>; | ||
|  |    | ||
|  |   /** | ||
|  |    * Close all active connections | ||
|  |    */ | ||
|  |   closeAllConnections(): void; | ||
|  |    | ||
|  |   /** | ||
|  |    * Get active connection count | ||
|  |    */ | ||
|  |   getConnectionCount(): number; | ||
|  |    | ||
|  |   /** | ||
|  |    * Check if accepting new connections | ||
|  |    */ | ||
|  |   canAcceptConnection(): boolean; | ||
|  |    | ||
|  |   /** | ||
|  |    * Handle new connection (legacy method name) | ||
|  |    */ | ||
|  |   handleNewConnection(socket: plugins.net.Socket): Promise<void>; | ||
|  |    | ||
|  |   /** | ||
|  |    * Handle new secure connection (legacy method name) | ||
|  |    */ | ||
|  |   handleNewSecureConnection(socket: plugins.tls.TLSSocket): Promise<void>; | ||
|  |    | ||
|  |   /** | ||
|  |    * Setup socket event handlers | ||
|  |    */ | ||
|  |   setupSocketEventHandlers(socket: plugins.net.Socket | plugins.tls.TLSSocket): void; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Command handler interface | ||
|  |  */ | ||
|  | export interface ICommandHandler extends IDestroyable { | ||
|  |   /** | ||
|  |    * Handle an SMTP command | ||
|  |    */ | ||
|  |   handleCommand( | ||
|  |     socket: plugins.net.Socket | plugins.tls.TLSSocket, | ||
|  |     command: SmtpCommand, | ||
|  |     args: string, | ||
|  |     session: ISmtpSession | ||
|  |   ): Promise<void>; | ||
|  |    | ||
|  |   /** | ||
|  |    * Get supported commands for current session state | ||
|  |    */ | ||
|  |   getSupportedCommands(session: ISmtpSession): SmtpCommand[]; | ||
|  |    | ||
|  |   /** | ||
|  |    * Process command (legacy method name) | ||
|  |    */ | ||
|  |   processCommand(socket: plugins.net.Socket | plugins.tls.TLSSocket, command: string): Promise<void>; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Data handler interface | ||
|  |  */ | ||
|  | export interface IDataHandler extends IDestroyable { | ||
|  |   /** | ||
|  |    * Handle email data | ||
|  |    */ | ||
|  |   handleData( | ||
|  |     socket: plugins.net.Socket | plugins.tls.TLSSocket, | ||
|  |     data: string, | ||
|  |     session: ISmtpSession | ||
|  |   ): Promise<void>; | ||
|  |    | ||
|  |   /** | ||
|  |    * Process a complete email | ||
|  |    */ | ||
|  |   processEmail( | ||
|  |     rawData: string, | ||
|  |     session: ISmtpSession | ||
|  |   ): Promise<Email>; | ||
|  |    | ||
|  |   /** | ||
|  |    * Handle data received (legacy method name) | ||
|  |    */ | ||
|  |   handleDataReceived(socket: plugins.net.Socket | plugins.tls.TLSSocket, data: string): Promise<void>; | ||
|  |    | ||
|  |   /** | ||
|  |    * Process email data (legacy method name) | ||
|  |    */ | ||
|  |   processEmailData(socket: plugins.net.Socket | plugins.tls.TLSSocket, data: string): Promise<void>; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * TLS handler interface | ||
|  |  */ | ||
|  | export interface ITlsHandler extends IDestroyable { | ||
|  |   /** | ||
|  |    * Handle STARTTLS command | ||
|  |    */ | ||
|  |   handleStartTls( | ||
|  |     socket: plugins.net.Socket, | ||
|  |     session: ISmtpSession | ||
|  |   ): Promise<plugins.tls.TLSSocket | null>; | ||
|  |    | ||
|  |   /** | ||
|  |    * Check if TLS is available | ||
|  |    */ | ||
|  |   isTlsAvailable(): boolean; | ||
|  |    | ||
|  |   /** | ||
|  |    * Get TLS options | ||
|  |    */ | ||
|  |   getTlsOptions(): plugins.tls.TlsOptions; | ||
|  |    | ||
|  |   /** | ||
|  |    * Check if TLS is enabled | ||
|  |    */ | ||
|  |   isTlsEnabled(): boolean; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Security handler interface | ||
|  |  */ | ||
|  | export interface ISecurityHandler extends IDestroyable { | ||
|  |   /** | ||
|  |    * Check IP reputation | ||
|  |    */ | ||
|  |   checkIpReputation(socket: plugins.net.Socket | plugins.tls.TLSSocket): Promise<boolean>; | ||
|  |    | ||
|  |   /** | ||
|  |    * Validate email address | ||
|  |    */ | ||
|  |   isValidEmail(email: string): boolean; | ||
|  |    | ||
|  |   /** | ||
|  |    * Authenticate user | ||
|  |    */ | ||
|  |   authenticate(auth: ISmtpAuth): Promise<boolean>; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * SMTP server options | ||
|  |  */ | ||
|  | export interface ISmtpServerOptions { | ||
|  |   /** | ||
|  |    * Port to listen on | ||
|  |    */ | ||
|  |   port: number; | ||
|  |    | ||
|  |   /** | ||
|  |    * Hostname of the server | ||
|  |    */ | ||
|  |   hostname: string; | ||
|  |    | ||
|  |   /** | ||
|  |    * Host to bind to (optional, defaults to 0.0.0.0) | ||
|  |    */ | ||
|  |   host?: string; | ||
|  |    | ||
|  |   /** | ||
|  |    * Secure port for TLS connections | ||
|  |    */ | ||
|  |   securePort?: number; | ||
|  |    | ||
|  |   /** | ||
|  |    * TLS/SSL private key (PEM format) | ||
|  |    */ | ||
|  |   key?: string; | ||
|  |    | ||
|  |   /** | ||
|  |    * TLS/SSL certificate (PEM format) | ||
|  |    */ | ||
|  |   cert?: string; | ||
|  |    | ||
|  |   /** | ||
|  |    * CA certificates for TLS (PEM format) | ||
|  |    */ | ||
|  |   ca?: string; | ||
|  |    | ||
|  |   /** | ||
|  |    * Maximum size of messages in bytes | ||
|  |    */ | ||
|  |   maxSize?: number; | ||
|  |    | ||
|  |   /** | ||
|  |    * Maximum number of concurrent connections | ||
|  |    */ | ||
|  |   maxConnections?: number; | ||
|  |    | ||
|  |   /** | ||
|  |    * Authentication options | ||
|  |    */ | ||
|  |   auth?: { | ||
|  |     /** | ||
|  |      * Whether authentication is required | ||
|  |      */ | ||
|  |     required: boolean; | ||
|  |      | ||
|  |     /** | ||
|  |      * Allowed authentication methods | ||
|  |      */ | ||
|  |     methods: ('PLAIN' | 'LOGIN' | 'OAUTH2')[]; | ||
|  |   }; | ||
|  |    | ||
|  |   /** | ||
|  |    * Socket timeout in milliseconds (default: 5 minutes / 300000ms) | ||
|  |    */ | ||
|  |   socketTimeout?: number; | ||
|  |    | ||
|  |   /** | ||
|  |    * Initial connection timeout in milliseconds (default: 30 seconds / 30000ms) | ||
|  |    */ | ||
|  |   connectionTimeout?: number; | ||
|  |    | ||
|  |   /** | ||
|  |    * Interval for checking idle sessions in milliseconds (default: 5 seconds / 5000ms) | ||
|  |    * For testing, can be set lower (e.g. 1000ms) to detect timeouts more quickly | ||
|  |    */ | ||
|  |   cleanupInterval?: number; | ||
|  |    | ||
|  |   /** | ||
|  |    * Maximum number of recipients allowed per message (default: 100) | ||
|  |    */ | ||
|  |   maxRecipients?: number; | ||
|  |    | ||
|  |   /** | ||
|  |    * Maximum message size in bytes (default: 10MB / 10485760 bytes) | ||
|  |    * This is advertised in the EHLO SIZE extension | ||
|  |    */ | ||
|  |   size?: number; | ||
|  |    | ||
|  |   /** | ||
|  |    * Timeout for the DATA command in milliseconds (default: 60000ms / 1 minute) | ||
|  |    * This controls how long to wait for the complete email data | ||
|  |    */ | ||
|  |   dataTimeout?: number; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Result of SMTP transaction | ||
|  |  */ | ||
|  | export interface ISmtpTransactionResult { | ||
|  |   /** | ||
|  |    * Whether the transaction was successful | ||
|  |    */ | ||
|  |   success: boolean; | ||
|  |    | ||
|  |   /** | ||
|  |    * Error message if failed | ||
|  |    */ | ||
|  |   error?: string; | ||
|  |    | ||
|  |   /** | ||
|  |    * Message ID if successful | ||
|  |    */ | ||
|  |   messageId?: string; | ||
|  |    | ||
|  |   /** | ||
|  |    * Resulting email if successful | ||
|  |    */ | ||
|  |   email?: Email; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Interface for SMTP session events | ||
|  |  * These events are emitted by the session manager | ||
|  |  */ | ||
|  | export interface ISessionEvents { | ||
|  |   created: (session: ISmtpSession, socket: plugins.net.Socket | plugins.tls.TLSSocket) => void; | ||
|  |   stateChanged: (session: ISmtpSession, previousState: SmtpState, newState: SmtpState) => void; | ||
|  |   timeout: (session: ISmtpSession, socket: plugins.net.Socket | plugins.tls.TLSSocket) => void; | ||
|  |   completed: (session: ISmtpSession, socket: plugins.net.Socket | plugins.tls.TLSSocket) => void; | ||
|  |   error: (session: ISmtpSession, error: Error) => void; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * SMTP Server interface | ||
|  |  */ | ||
|  | export interface ISmtpServer extends IDestroyable { | ||
|  |   /** | ||
|  |    * Start the SMTP server | ||
|  |    */ | ||
|  |   listen(): Promise<void>; | ||
|  |    | ||
|  |   /** | ||
|  |    * Stop the SMTP server | ||
|  |    */ | ||
|  |   close(): Promise<void>; | ||
|  |    | ||
|  |   /** | ||
|  |    * Get the session manager | ||
|  |    */ | ||
|  |   getSessionManager(): ISessionManager; | ||
|  |    | ||
|  |   /** | ||
|  |    * Get the connection manager | ||
|  |    */ | ||
|  |   getConnectionManager(): IConnectionManager; | ||
|  |    | ||
|  |   /** | ||
|  |    * Get the command handler | ||
|  |    */ | ||
|  |   getCommandHandler(): ICommandHandler; | ||
|  |    | ||
|  |   /** | ||
|  |    * Get the data handler | ||
|  |    */ | ||
|  |   getDataHandler(): IDataHandler; | ||
|  |    | ||
|  |   /** | ||
|  |    * Get the TLS handler | ||
|  |    */ | ||
|  |   getTlsHandler(): ITlsHandler; | ||
|  |    | ||
|  |   /** | ||
|  |    * Get the security handler | ||
|  |    */ | ||
|  |   getSecurityHandler(): ISecurityHandler; | ||
|  |    | ||
|  |   /** | ||
|  |    * Get the server options | ||
|  |    */ | ||
|  |   getOptions(): ISmtpServerOptions; | ||
|  |    | ||
|  |   /** | ||
|  |    * Get the email server reference | ||
|  |    */ | ||
|  |   getEmailServer(): UnifiedEmailServer; | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Configuration for creating SMTP server | ||
|  |  */ | ||
|  | export interface ISmtpServerConfig { | ||
|  |   /** | ||
|  |    * Email server instance | ||
|  |    */ | ||
|  |   emailServer: UnifiedEmailServer; | ||
|  |    | ||
|  |   /** | ||
|  |    * Server options | ||
|  |    */ | ||
|  |   options: ISmtpServerOptions; | ||
|  |    | ||
|  |   /** | ||
|  |    * Optional custom session manager | ||
|  |    */ | ||
|  |   sessionManager?: ISessionManager; | ||
|  |    | ||
|  |   /** | ||
|  |    * Optional custom connection manager | ||
|  |    */ | ||
|  |   connectionManager?: IConnectionManager; | ||
|  |    | ||
|  |   /** | ||
|  |    * Optional custom command handler | ||
|  |    */ | ||
|  |   commandHandler?: ICommandHandler; | ||
|  |    | ||
|  |   /** | ||
|  |    * Optional custom data handler | ||
|  |    */ | ||
|  |   dataHandler?: IDataHandler; | ||
|  |    | ||
|  |   /** | ||
|  |    * Optional custom TLS handler | ||
|  |    */ | ||
|  |   tlsHandler?: ITlsHandler; | ||
|  |    | ||
|  |   /** | ||
|  |    * Optional custom security handler | ||
|  |    */ | ||
|  |   securityHandler?: ISecurityHandler; | ||
|  | } |