update
This commit is contained in:
1201
ts/mail/delivery/classes.smtp.client.ts
Normal file
1201
ts/mail/delivery/classes.smtp.client.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -11,55 +11,18 @@ import {
|
||||
ReputationThreshold
|
||||
} from '../../security/index.js';
|
||||
|
||||
export interface ISmtpServerOptions {
|
||||
port: number;
|
||||
key: string;
|
||||
cert: string;
|
||||
hostname?: string;
|
||||
}
|
||||
|
||||
// SMTP Session States
|
||||
enum SmtpState {
|
||||
GREETING,
|
||||
AFTER_EHLO,
|
||||
MAIL_FROM,
|
||||
RCPT_TO,
|
||||
DATA,
|
||||
DATA_RECEIVING,
|
||||
FINISHED
|
||||
}
|
||||
|
||||
// Structure to store session information
|
||||
interface SmtpSession {
|
||||
id: string;
|
||||
state: SmtpState;
|
||||
clientHostname: string;
|
||||
mailFrom: string;
|
||||
rcptTo: string[];
|
||||
emailData: string;
|
||||
useTLS: boolean;
|
||||
connectionEnded: boolean;
|
||||
remoteAddress: string;
|
||||
secure: boolean;
|
||||
authenticated: boolean;
|
||||
envelope: {
|
||||
mailFrom: {
|
||||
address: string;
|
||||
args: any;
|
||||
};
|
||||
rcptTo: Array<{
|
||||
address: string;
|
||||
args: any;
|
||||
}>;
|
||||
};
|
||||
processingMode?: 'forward' | 'mta' | 'process';
|
||||
}
|
||||
import type {
|
||||
ISmtpServerOptions,
|
||||
ISmtpSession,
|
||||
EmailProcessingMode
|
||||
} from './interfaces.js';
|
||||
import { SmtpState } from './interfaces.js';
|
||||
|
||||
export class SMTPServer {
|
||||
public emailServerRef: UnifiedEmailServer;
|
||||
private smtpServerOptions: ISmtpServerOptions;
|
||||
private server: plugins.net.Server;
|
||||
private sessions: Map<plugins.net.Socket | plugins.tls.TLSSocket, SmtpSession>;
|
||||
private sessions: Map<plugins.net.Socket | plugins.tls.TLSSocket, ISmtpSession>;
|
||||
private hostname: string;
|
||||
|
||||
constructor(emailServerRefArg: UnifiedEmailServer, optionsArg: ISmtpServerOptions) {
|
||||
@ -722,6 +685,12 @@ export class SMTPServer {
|
||||
try {
|
||||
await this.emailServerRef.processEmailByMode(email, {
|
||||
id: session.id,
|
||||
state: session.state,
|
||||
mailFrom: session.mailFrom,
|
||||
rcptTo: session.rcptTo,
|
||||
emailData: session.emailData,
|
||||
useTLS: session.useTLS,
|
||||
connectionEnded: session.connectionEnded,
|
||||
remoteAddress: session.remoteAddress,
|
||||
clientHostname: session.clientHostname,
|
||||
secure: session.useTLS,
|
||||
|
@ -15,5 +15,6 @@ export type { IRateLimitConfig } from './classes.ratelimiter.js';
|
||||
// Unified rate limiter
|
||||
export * from './classes.unified.rate.limiter.js';
|
||||
|
||||
// MTA configuration helpers
|
||||
export * from './classes.mta.config.js';
|
||||
// SMTP client and configuration
|
||||
export * from './classes.mta.config.js';
|
||||
export * from './classes.smtp.client.js';
|
223
ts/mail/delivery/interfaces.ts
Normal file
223
ts/mail/delivery/interfaces.ts
Normal file
@ -0,0 +1,223 @@
|
||||
/**
|
||||
* SMTP and email delivery interface definitions
|
||||
*/
|
||||
|
||||
import type { Email } from '../core/classes.email.js';
|
||||
|
||||
/**
|
||||
* SMTP session state enumeration
|
||||
*/
|
||||
export enum SmtpState {
|
||||
GREETING = 'GREETING',
|
||||
AFTER_EHLO = 'AFTER_EHLO',
|
||||
MAIL_FROM = 'MAIL_FROM',
|
||||
RCPT_TO = 'RCPT_TO',
|
||||
DATA = 'DATA',
|
||||
DATA_RECEIVING = 'DATA_RECEIVING',
|
||||
FINISHED = 'FINISHED'
|
||||
}
|
||||
|
||||
/**
|
||||
* Email processing mode type
|
||||
*/
|
||||
export type EmailProcessingMode = 'forward' | 'mta' | 'process';
|
||||
|
||||
/**
|
||||
* Envelope recipient information
|
||||
*/
|
||||
export interface IEnvelopeRecipient {
|
||||
/**
|
||||
* Email address of the recipient
|
||||
*/
|
||||
address: string;
|
||||
|
||||
/**
|
||||
* Additional SMTP command arguments
|
||||
*/
|
||||
args: Record<string, string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* SMTP session envelope information
|
||||
*/
|
||||
export interface ISmtpEnvelope {
|
||||
/**
|
||||
* Envelope sender (MAIL FROM) information
|
||||
*/
|
||||
mailFrom: {
|
||||
/**
|
||||
* Email address of the sender
|
||||
*/
|
||||
address: string;
|
||||
|
||||
/**
|
||||
* Additional SMTP command arguments
|
||||
*/
|
||||
args: Record<string, string>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Envelope recipients (RCPT TO) information
|
||||
*/
|
||||
rcptTo: IEnvelopeRecipient[];
|
||||
}
|
||||
|
||||
/**
|
||||
* SMTP Session interface - represents an active SMTP connection
|
||||
*/
|
||||
export interface ISmtpSession {
|
||||
/**
|
||||
* Unique session identifier
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* Current session state in the SMTP conversation
|
||||
*/
|
||||
state: SmtpState;
|
||||
|
||||
/**
|
||||
* Hostname provided by the client in EHLO/HELO command
|
||||
*/
|
||||
clientHostname: string;
|
||||
|
||||
/**
|
||||
* MAIL FROM email address (legacy format)
|
||||
*/
|
||||
mailFrom: string;
|
||||
|
||||
/**
|
||||
* RCPT TO email addresses (legacy format)
|
||||
*/
|
||||
rcptTo: string[];
|
||||
|
||||
/**
|
||||
* Raw email data being received
|
||||
*/
|
||||
emailData: string;
|
||||
|
||||
/**
|
||||
* Whether the connection is using TLS
|
||||
*/
|
||||
useTLS: boolean;
|
||||
|
||||
/**
|
||||
* Whether the connection has ended
|
||||
*/
|
||||
connectionEnded: boolean;
|
||||
|
||||
/**
|
||||
* Remote IP address of the client
|
||||
*/
|
||||
remoteAddress: string;
|
||||
|
||||
/**
|
||||
* Whether the connection is secure (TLS)
|
||||
*/
|
||||
secure: boolean;
|
||||
|
||||
/**
|
||||
* Whether the client has been authenticated
|
||||
*/
|
||||
authenticated: boolean;
|
||||
|
||||
/**
|
||||
* SMTP envelope information (structured format)
|
||||
*/
|
||||
envelope: ISmtpEnvelope;
|
||||
|
||||
/**
|
||||
* Email processing mode to use for this session
|
||||
*/
|
||||
processingMode?: EmailProcessingMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* SMTP authentication data
|
||||
*/
|
||||
export interface ISmtpAuth {
|
||||
/**
|
||||
* Authentication method used
|
||||
*/
|
||||
method: 'PLAIN' | 'LOGIN' | 'OAUTH2' | string;
|
||||
|
||||
/**
|
||||
* Username for authentication
|
||||
*/
|
||||
username: string;
|
||||
|
||||
/**
|
||||
* Password or token for authentication
|
||||
*/
|
||||
password: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* SMTP server options
|
||||
*/
|
||||
export interface ISmtpServerOptions {
|
||||
/**
|
||||
* Port to listen on
|
||||
*/
|
||||
port: number;
|
||||
|
||||
/**
|
||||
* TLS private key (PEM format)
|
||||
*/
|
||||
key: string;
|
||||
|
||||
/**
|
||||
* TLS certificate (PEM format)
|
||||
*/
|
||||
cert: string;
|
||||
|
||||
/**
|
||||
* Server hostname for SMTP banner
|
||||
*/
|
||||
hostname?: string;
|
||||
|
||||
/**
|
||||
* Maximum size of messages in bytes
|
||||
*/
|
||||
maxSize?: number;
|
||||
|
||||
/**
|
||||
* Authentication options
|
||||
*/
|
||||
auth?: {
|
||||
/**
|
||||
* Whether authentication is required
|
||||
*/
|
||||
required: boolean;
|
||||
|
||||
/**
|
||||
* Allowed authentication methods
|
||||
*/
|
||||
methods: ('PLAIN' | 'LOGIN' | 'OAUTH2')[];
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import * as plugins from '../../plugins.js';
|
||||
|
||||
/**
|
||||
* Email processing modes
|
||||
*/
|
||||
export type EmailProcessingMode = 'forward' | 'mta' | 'process';
|
||||
import type { EmailProcessingMode } from '../delivery/interfaces.js';
|
||||
|
||||
// Re-export EmailProcessingMode type
|
||||
export type { EmailProcessingMode };
|
||||
|
||||
/**
|
||||
* Consolidated email configuration interface
|
||||
|
@ -18,7 +18,6 @@ import {
|
||||
import { DomainRouter } from './classes.domain.router.js';
|
||||
import type {
|
||||
IEmailConfig,
|
||||
EmailProcessingMode,
|
||||
IDomainRule
|
||||
} from './classes.email.config.js';
|
||||
import { Email } from '../core/classes.email.js';
|
||||
@ -29,6 +28,18 @@ import * as stream from 'node:stream';
|
||||
import { SMTPServer as MtaSmtpServer } from '../delivery/classes.smtpserver.js';
|
||||
import { MultiModeDeliverySystem, type IMultiModeDeliveryOptions } from '../delivery/classes.delivery.system.js';
|
||||
import { UnifiedDeliveryQueue, type IQueueOptions } from '../delivery/classes.delivery.queue.js';
|
||||
import { SmtpState } from '../delivery/interfaces.js';
|
||||
import type { EmailProcessingMode, ISmtpSession as IBaseSmtpSession } from '../delivery/interfaces.js';
|
||||
|
||||
/**
|
||||
* Extended SMTP session interface with domain rule information
|
||||
*/
|
||||
export interface IExtendedSmtpSession extends ISmtpSession {
|
||||
/**
|
||||
* Matched domain rule for this session
|
||||
*/
|
||||
matchedRule?: IDomainRule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for the unified email server
|
||||
@ -78,41 +89,30 @@ export interface IUnifiedEmailServerOptions {
|
||||
reputationMonitorConfig?: IReputationMonitorConfig;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interface describing SMTP session data
|
||||
* Extended SMTP session interface for UnifiedEmailServer
|
||||
*/
|
||||
export interface ISmtpSession {
|
||||
id: string;
|
||||
remoteAddress: string;
|
||||
clientHostname: string;
|
||||
secure: boolean;
|
||||
authenticated: boolean;
|
||||
export interface ISmtpSession extends IBaseSmtpSession {
|
||||
/**
|
||||
* User information if authenticated
|
||||
*/
|
||||
user?: {
|
||||
username: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
envelope: {
|
||||
mailFrom: {
|
||||
address: string;
|
||||
args: any;
|
||||
};
|
||||
rcptTo: Array<{
|
||||
address: string;
|
||||
args: any;
|
||||
}>;
|
||||
};
|
||||
processingMode?: EmailProcessingMode;
|
||||
|
||||
/**
|
||||
* Matched domain rule for this session
|
||||
*/
|
||||
matchedRule?: IDomainRule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authentication data for SMTP
|
||||
*/
|
||||
export interface IAuthData {
|
||||
method: string;
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
import type { ISmtpAuth } from '../delivery/interfaces.js';
|
||||
export type IAuthData = ISmtpAuth;
|
||||
|
||||
/**
|
||||
* Server statistics
|
||||
@ -330,6 +330,12 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
// Process based on the mode
|
||||
await this.processEmailByMode(email, {
|
||||
id: 'session-' + Math.random().toString(36).substring(2),
|
||||
state: SmtpState.FINISHED,
|
||||
mailFrom: email.from,
|
||||
rcptTo: email.to,
|
||||
emailData: email.toRFC822String(), // Use the proper method to get the full email content
|
||||
useTLS: false,
|
||||
connectionEnded: true,
|
||||
remoteAddress: '127.0.0.1',
|
||||
clientHostname: '',
|
||||
secure: false,
|
||||
@ -431,7 +437,7 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
/**
|
||||
* Handle new SMTP connection with IP reputation checking
|
||||
*/
|
||||
private async onConnect(session: ISmtpSession, callback: (err?: Error) => void): Promise<void> {
|
||||
private async onConnect(session: IExtendedSmtpSession, callback: (err?: Error) => void): Promise<void> {
|
||||
logger.log('info', `New connection from ${session.remoteAddress}`);
|
||||
|
||||
// Update connection statistics
|
||||
@ -498,7 +504,7 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
/**
|
||||
* Handle authentication (stub implementation)
|
||||
*/
|
||||
private onAuth(auth: IAuthData, session: ISmtpSession, callback: (err?: Error, user?: any) => void): void {
|
||||
private onAuth(auth: IAuthData, session: IExtendedSmtpSession, callback: (err?: Error, user?: any) => void): void {
|
||||
if (!this.options.auth || !this.options.auth.users || this.options.auth.users.length === 0) {
|
||||
// No authentication configured, reject
|
||||
const error = new Error('Authentication not supported');
|
||||
@ -564,7 +570,7 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
/**
|
||||
* Handle MAIL FROM command (stub implementation)
|
||||
*/
|
||||
private onMailFrom(address: {address: string}, session: ISmtpSession, callback: (err?: Error) => void): void {
|
||||
private onMailFrom(address: {address: string}, session: IExtendedSmtpSession, callback: (err?: Error) => void): void {
|
||||
logger.log('info', `MAIL FROM: ${address.address}`);
|
||||
|
||||
// Validate the email address
|
||||
@ -614,7 +620,7 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
/**
|
||||
* Handle RCPT TO command (stub implementation)
|
||||
*/
|
||||
private onRcptTo(address: {address: string}, session: ISmtpSession, callback: (err?: Error) => void): void {
|
||||
private onRcptTo(address: {address: string}, session: IExtendedSmtpSession, callback: (err?: Error) => void): void {
|
||||
logger.log('info', `RCPT TO: ${address.address}`);
|
||||
|
||||
// Validate the email address
|
||||
@ -658,7 +664,7 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
/**
|
||||
* Handle incoming email data (stub implementation)
|
||||
*/
|
||||
private onData(stream: stream.Readable, session: ISmtpSession, callback: (err?: Error) => void): void {
|
||||
private onData(stream: stream.Readable, session: IExtendedSmtpSession, callback: (err?: Error) => void): void {
|
||||
logger.log('info', `Processing email data for session ${session.id}`);
|
||||
|
||||
const startTime = Date.now();
|
||||
@ -762,7 +768,7 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
/**
|
||||
* Process email based on the determined mode
|
||||
*/
|
||||
public async processEmailByMode(emailData: Email | Buffer, session: ISmtpSession, mode: EmailProcessingMode): Promise<Email> {
|
||||
public async processEmailByMode(emailData: Email | Buffer, session: IExtendedSmtpSession, mode: EmailProcessingMode): Promise<Email> {
|
||||
// Convert Buffer to Email if needed
|
||||
let email: Email;
|
||||
if (Buffer.isBuffer(emailData)) {
|
||||
@ -833,7 +839,7 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
/**
|
||||
* Handle email in forward mode (SMTP proxy)
|
||||
*/
|
||||
private async handleForwardMode(email: Email, session: ISmtpSession): Promise<void> {
|
||||
private async handleForwardMode(email: Email, session: IExtendedSmtpSession): Promise<void> {
|
||||
logger.log('info', `Handling email in forward mode for session ${session.id}`);
|
||||
|
||||
// Get target server information
|
||||
@ -927,7 +933,7 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
/**
|
||||
* Handle email in MTA mode (programmatic processing)
|
||||
*/
|
||||
private async handleMtaMode(email: Email, session: ISmtpSession): Promise<void> {
|
||||
private async handleMtaMode(email: Email, session: IExtendedSmtpSession): Promise<void> {
|
||||
logger.log('info', `Handling email in MTA mode for session ${session.id}`);
|
||||
|
||||
try {
|
||||
@ -1022,7 +1028,7 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
/**
|
||||
* Handle email in process mode (store-and-forward with scanning)
|
||||
*/
|
||||
private async handleProcessMode(email: Email, session: ISmtpSession): Promise<void> {
|
||||
private async handleProcessMode(email: Email, session: IExtendedSmtpSession): Promise<void> {
|
||||
logger.log('info', `Handling email in process mode for session ${session.id}`);
|
||||
|
||||
try {
|
||||
|
@ -6,8 +6,7 @@ import { TemplateManager } from '../core/classes.templatemanager.js';
|
||||
import { EmailValidator } from '../core/classes.emailvalidator.js';
|
||||
import { BounceManager } from '../core/classes.bouncemanager.js';
|
||||
import { logger } from '../../logger.js';
|
||||
// Import types from platform interfaces
|
||||
import type { default as platformInterfaces } from '../../types/platform.interfaces.js';
|
||||
// Import types from router interfaces
|
||||
import { UnifiedEmailServer } from '../routing/classes.unified.email.server.js';
|
||||
import { DomainRouter } from '../routing/classes.domain.router.js';
|
||||
import { Email } from '../core/classes.email.js';
|
||||
@ -144,7 +143,6 @@ export interface IEmailServiceStats {
|
||||
* Email service with MTA support
|
||||
*/
|
||||
export class EmailService {
|
||||
public platformServiceRef: any; // Reference to platform service
|
||||
|
||||
// typedrouter
|
||||
public typedrouter = new plugins.typedrequest.TypedRouter();
|
||||
@ -166,9 +164,7 @@ export class EmailService {
|
||||
// configuration
|
||||
private config: IEmailConfig;
|
||||
|
||||
constructor(platformServiceRefArg: any, options: IEmailConfig = {}) {
|
||||
this.platformServiceRef = platformServiceRefArg;
|
||||
this.platformServiceRef.typedrouter.addTypedRouter(this.typedrouter);
|
||||
constructor(options: IEmailConfig = {}) {
|
||||
|
||||
// Validate and apply defaults to configuration
|
||||
const validationResult = ConfigValidator.validate(options, emailConfigSchema);
|
||||
|
Reference in New Issue
Block a user