Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ffe294643c | |||
| f1071faf3d | |||
| 6b082cee8f | |||
| 9185242530 | |||
| 8293663619 | |||
| 199b9b79d2 | |||
| 91b49182bb | |||
| 564b65c7f2 | |||
| 8bd8c295b0 | |||
| 237dba3bab | |||
| bcde137332 | |||
| 14be3cdb9a | |||
| f262f602a0 | |||
| 0ac598818f | |||
| c7b3206140 | |||
| 17f5661636 | |||
| 6523c55516 | |||
| 9cd15342e0 | |||
| 0018b19164 | |||
| 7ecdd9f1e4 | |||
| 1698df3a53 | |||
| d7f37afc30 | |||
| 27b6bb779e |
@@ -6,7 +6,7 @@ Pre-compiled binaries for multiple platforms.
|
||||
|
||||
#### Option 1: Via npm (recommended)
|
||||
```bash
|
||||
npm install -g @serve.zone/mailer
|
||||
npm install -g @push.rocks/smartmta
|
||||
```
|
||||
|
||||
#### Option 2: Direct binary download
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,6 +1,8 @@
|
||||
node_modules/
|
||||
.nogit/
|
||||
dist/
|
||||
dist_rust/
|
||||
rust/target/
|
||||
deno.lock
|
||||
*.log
|
||||
.env
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* MAILER npm wrapper
|
||||
* This script executes the appropriate pre-compiled binary based on the current platform
|
||||
*/
|
||||
|
||||
import { spawn } from 'child_process';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname, join } from 'path';
|
||||
import { existsSync } from 'fs';
|
||||
import { platform, arch } from 'os';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
/**
|
||||
* Get the binary name for the current platform
|
||||
*/
|
||||
function getBinaryName() {
|
||||
const plat = platform();
|
||||
const architecture = arch();
|
||||
|
||||
// Map Node's platform/arch to our binary naming
|
||||
const platformMap = {
|
||||
'darwin': 'macos',
|
||||
'linux': 'linux',
|
||||
'win32': 'windows'
|
||||
};
|
||||
|
||||
const archMap = {
|
||||
'x64': 'x64',
|
||||
'arm64': 'arm64'
|
||||
};
|
||||
|
||||
const mappedPlatform = platformMap[plat];
|
||||
const mappedArch = archMap[architecture];
|
||||
|
||||
if (!mappedPlatform || !mappedArch) {
|
||||
console.error(`Error: Unsupported platform/architecture: ${plat}/${architecture}`);
|
||||
console.error('Supported platforms: Linux, macOS, Windows');
|
||||
console.error('Supported architectures: x64, arm64');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Construct binary name
|
||||
let binaryName = `mailer-${mappedPlatform}-${mappedArch}`;
|
||||
if (plat === 'win32') {
|
||||
binaryName += '.exe';
|
||||
}
|
||||
|
||||
return binaryName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the binary
|
||||
*/
|
||||
function executeBinary() {
|
||||
const binaryName = getBinaryName();
|
||||
const binaryPath = join(__dirname, '..', 'dist', 'binaries', binaryName);
|
||||
|
||||
// Check if binary exists
|
||||
if (!existsSync(binaryPath)) {
|
||||
console.error(`Error: Binary not found at ${binaryPath}`);
|
||||
console.error('This might happen if:');
|
||||
console.error('1. The postinstall script failed to run');
|
||||
console.error('2. The platform is not supported');
|
||||
console.error('3. The package was not installed correctly');
|
||||
console.error('');
|
||||
console.error('Try reinstalling the package:');
|
||||
console.error(' npm uninstall -g @serve.zone/mailer');
|
||||
console.error(' npm install -g @serve.zone/mailer');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Spawn the binary with all arguments passed through
|
||||
const child = spawn(binaryPath, process.argv.slice(2), {
|
||||
stdio: 'inherit',
|
||||
shell: false
|
||||
});
|
||||
|
||||
// Handle child process events
|
||||
child.on('error', (err) => {
|
||||
console.error(`Error executing mailer: ${err.message}`);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
child.on('exit', (code, signal) => {
|
||||
if (signal) {
|
||||
process.kill(process.pid, signal);
|
||||
} else {
|
||||
process.exit(code || 0);
|
||||
}
|
||||
});
|
||||
|
||||
// Forward signals to child process
|
||||
const signals = ['SIGINT', 'SIGTERM', 'SIGHUP'];
|
||||
signals.forEach(signal => {
|
||||
process.on(signal, () => {
|
||||
if (!child.killed) {
|
||||
child.kill(signal);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Execute
|
||||
executeBinary();
|
||||
49
changelog.md
49
changelog.md
@@ -1,5 +1,54 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-02-10 - 2.0.1 - fix(docs/readme)
|
||||
update README: clarify APIs, document RustSecurityBridge, update examples and architecture diagram
|
||||
|
||||
- Documented RustSecurityBridge: startup/shutdown, automatic delegation, compound verifyEmail API, and individual operations
|
||||
- Clarified verification APIs: SpfVerifier.verify() and DmarcVerifier.verify() examples now take an Email object as the first argument
|
||||
- Updated example method names/usages: scanEmail, createEmail, evaluateRoutes, checkMessageLimit, isEmailSuppressed, DKIMCreator rotation and output formatting
|
||||
- Reformatted architecture diagram and added Rust Security Bridge and expanded Rust Acceleration details
|
||||
- Rate limiter example updated: renamed/standardized config keys (maxMessagesPerMinute, domains) and added additional limits (maxRecipientsPerMessage, maxConnectionsPerIP, etc.)
|
||||
- DNS management documentation reorganized: UnifiedEmailServer now handles DNS record setup automatically; DNSManager usage clarified for standalone checks
|
||||
- Minor wording/formatting tweaks throughout README (arrow styles, headings, test counts)
|
||||
|
||||
## 2026-02-10 - 2.0.0 - BREAKING CHANGE(smartmta)
|
||||
Rebrand package to @push.rocks/smartmta, add consolidated email security verification and IPC handler
|
||||
|
||||
- Package renamed from @serve.zone/mailer to @push.rocks/smartmta (package.json, commitinfo, README and homepage/bugs/repository URLs updated) — breaking for consumers who import by package name.
|
||||
- Added new compound email security API verify_email_security that runs DKIM, SPF and DMARC in a single call (rust/crates/mailer-security/src/verify.rs) and exposed it from the mailer-security crate.
|
||||
- Added IPC handler "verifyEmail" in mailer-bin to call the new verify_email_security function from the Rust side.
|
||||
- Refactored DKIM and SPF code to convert mail-auth outputs to serializable results (dkim_outputs_to_results and SpfResult::from_output) and wired them into the combined verifier.
|
||||
- Updated TypeScript plugin exports and dependencies: added @push.rocks/smartrust and exported smartrust in ts/plugins.ts.
|
||||
- Large README overhaul to reflect rebranding, install instructions, architecture and legal/company info.
|
||||
|
||||
## 2026-02-10 - 1.3.1 - fix(deps)
|
||||
add workspace dependency entries for multiple crates across mailer-bin, mailer-core, and mailer-security
|
||||
|
||||
- rust/crates/mailer-bin/Cargo.toml: add clap.workspace = true
|
||||
- rust/crates/mailer-core/Cargo.toml: add regex.workspace = true, base64.workspace = true, uuid.workspace = true
|
||||
- rust/crates/mailer-security/Cargo.toml: add serde_json.workspace = true, tokio.workspace = true, hickory-resolver.workspace = true, ipnet.workspace = true, rustls-pki-types.workspace = true, psl.workspace = true
|
||||
- Purpose: align and enable workspace-managed dependencies for the affected crates
|
||||
|
||||
## 2026-02-10 - 1.3.0 - feat(mail/delivery)
|
||||
add error-count based blocking to rate limiter; improve test SMTP server port selection; add tsbuild scripts and devDependency; remove stale backup file
|
||||
|
||||
- Add TokenBucket error tracking (errors, firstErrorTime) and initialize fields for global and per-key buckets
|
||||
- Introduce RateLimiter.recordError(key, window, threshold) to track errors and decide blocking when threshold exceeded
|
||||
- Update test SMTP server loader to dynamically find a free port using smartnetwork and add a recordError stub to the mock server
|
||||
- Add build and check scripts to package.json and add @git.zone/tsbuild to devDependencies
|
||||
- Remove a large backup file (classes.emailsendjob.ts.backup) from the repo; minor whitespace and logging cleanups in SMTP server code
|
||||
|
||||
## 2025-10-24 - 1.2.1 - fix(mail/delivery)
|
||||
Centralize runtime/plugin imports and switch modules to use plugins exports; unify EventEmitter usage; update Deno dependencies and small path/server refactors
|
||||
|
||||
- Centralized Node and third-party imports in ts/plugins.ts and re-exported commonly used utilities (net, tls, dns, fs, smartfile, smartdns, smartmail, mailauth, uuid, ip, LRUCache, etc).
|
||||
- Replaced direct EventEmitter / Node built-in imports with plugins.EventEmitter across delivery, smtpclient, routing and the unified email server to standardize runtime integration.
|
||||
- Updated deno.json dependency map: added @push.rocks/smartfile, @push.rocks/smartdns, @tsclass/tsclass and ip; reordered lru-cache entry.
|
||||
- Stopped exporting ./dns/index.ts from ts/index.ts (DNS is available via mail/routing) to avoid duplicate exports.
|
||||
- Added keysDir alias and dnsRecordsDir in ts/paths.ts and small path-related fixes.
|
||||
- Added a placeholder SmtpServer and other minor delivery/smtpserver refactors and sanitizations.
|
||||
- Added a local .claude/settings.local.json for development permissions (local-only configuration).
|
||||
|
||||
## 2025-10-24 - 1.2.0 - feat(plugins)
|
||||
Add smartmail, mailauth and uuid to Deno dependencies and export them from plugins; include local dev permissions file
|
||||
|
||||
|
||||
49
deno.json
49
deno.json
@@ -1,49 +0,0 @@
|
||||
{
|
||||
"name": "@serve.zone/mailer",
|
||||
"version": "1.2.0",
|
||||
"exports": "./mod.ts",
|
||||
"nodeModulesDir": "auto",
|
||||
"tasks": {
|
||||
"dev": "deno run --allow-all mod.ts",
|
||||
"compile": "deno task compile:all",
|
||||
"compile:all": "bash scripts/compile-all.sh",
|
||||
"test": "deno test --allow-all test/",
|
||||
"test:watch": "deno test --allow-all --watch test/",
|
||||
"check": "deno check mod.ts",
|
||||
"fmt": "deno fmt",
|
||||
"lint": "deno lint"
|
||||
},
|
||||
"lint": {
|
||||
"rules": {
|
||||
"tags": [
|
||||
"recommended"
|
||||
]
|
||||
}
|
||||
},
|
||||
"fmt": {
|
||||
"useTabs": false,
|
||||
"lineWidth": 100,
|
||||
"indentWidth": 2,
|
||||
"semiColons": true,
|
||||
"singleQuote": true
|
||||
},
|
||||
"compilerOptions": {
|
||||
"lib": [
|
||||
"deno.window"
|
||||
],
|
||||
"strict": true
|
||||
},
|
||||
"imports": {
|
||||
"@std/cli": "jsr:@std/cli@^1.0.0",
|
||||
"@std/fmt": "jsr:@std/fmt@^1.0.0",
|
||||
"@std/path": "jsr:@std/path@^1.0.0",
|
||||
"@std/http": "jsr:@std/http@^1.0.0",
|
||||
"@std/crypto": "jsr:@std/crypto@^1.0.0",
|
||||
"@std/assert": "jsr:@std/assert@^1.0.0",
|
||||
"@apiclient.xyz/cloudflare": "npm:@apiclient.xyz/cloudflare@latest",
|
||||
"lru-cache": "npm:lru-cache@^11.0.0",
|
||||
"@push.rocks/smartmail": "npm:@push.rocks/smartmail@^2.0.0",
|
||||
"mailauth": "npm:mailauth@^4.0.0",
|
||||
"uuid": "npm:uuid@^9.0.0"
|
||||
}
|
||||
}
|
||||
8
dist_ts/00_commitinfo_data.d.ts
vendored
Normal file
8
dist_ts/00_commitinfo_data.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* autocreated commitinfo by @push.rocks/commitinfo
|
||||
*/
|
||||
export declare const commitinfo: {
|
||||
name: string;
|
||||
version: string;
|
||||
description: string;
|
||||
};
|
||||
9
dist_ts/00_commitinfo_data.js
Normal file
9
dist_ts/00_commitinfo_data.js
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* autocreated commitinfo by @push.rocks/commitinfo
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartmta',
|
||||
version: '2.0.0',
|
||||
description: 'A high-performance, enterprise-grade Mail Transfer Agent (MTA) built from scratch in TypeScript with Rust acceleration.'
|
||||
};
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSxzQkFBc0I7SUFDNUIsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLHlIQUF5SDtDQUN2SSxDQUFBIn0=
|
||||
41
dist_ts/errors/index.d.ts
vendored
Normal file
41
dist_ts/errors/index.d.ts
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* MTA error classes for SMTP client operations
|
||||
*/
|
||||
export declare class MtaConnectionError extends Error {
|
||||
code: string;
|
||||
details?: any;
|
||||
constructor(message: string, detailsOrCode?: any);
|
||||
static timeout(host: string, port: number, timeoutMs?: number): MtaConnectionError;
|
||||
static refused(host: string, port: number): MtaConnectionError;
|
||||
static dnsError(host: string, err?: any): MtaConnectionError;
|
||||
}
|
||||
export declare class MtaAuthenticationError extends Error {
|
||||
code: string;
|
||||
details?: any;
|
||||
constructor(message: string, detailsOrCode?: any);
|
||||
static invalidCredentials(host?: string, user?: string): MtaAuthenticationError;
|
||||
}
|
||||
export declare class MtaDeliveryError extends Error {
|
||||
code: string;
|
||||
responseCode?: number;
|
||||
details?: any;
|
||||
constructor(message: string, detailsOrCode?: any, responseCode?: number);
|
||||
static temporary(message: string, ...args: any[]): MtaDeliveryError;
|
||||
static permanent(message: string, ...args: any[]): MtaDeliveryError;
|
||||
}
|
||||
export declare class MtaConfigurationError extends Error {
|
||||
code: string;
|
||||
details?: any;
|
||||
constructor(message: string, detailsOrCode?: any);
|
||||
}
|
||||
export declare class MtaTimeoutError extends Error {
|
||||
code: string;
|
||||
details?: any;
|
||||
constructor(message: string, detailsOrCode?: any);
|
||||
static commandTimeout(command: string, hostOrTimeout?: any, timeoutMs?: number): MtaTimeoutError;
|
||||
}
|
||||
export declare class MtaProtocolError extends Error {
|
||||
code: string;
|
||||
details?: any;
|
||||
constructor(message: string, detailsOrCode?: any);
|
||||
}
|
||||
120
dist_ts/errors/index.js
Normal file
120
dist_ts/errors/index.js
Normal file
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* MTA error classes for SMTP client operations
|
||||
*/
|
||||
export class MtaConnectionError extends Error {
|
||||
code;
|
||||
details;
|
||||
constructor(message, detailsOrCode) {
|
||||
super(message);
|
||||
this.name = 'MtaConnectionError';
|
||||
if (typeof detailsOrCode === 'string') {
|
||||
this.code = detailsOrCode;
|
||||
}
|
||||
else {
|
||||
this.code = 'CONNECTION_ERROR';
|
||||
this.details = detailsOrCode;
|
||||
}
|
||||
}
|
||||
static timeout(host, port, timeoutMs) {
|
||||
return new MtaConnectionError(`Connection to ${host}:${port} timed out${timeoutMs ? ` after ${timeoutMs}ms` : ''}`, 'TIMEOUT');
|
||||
}
|
||||
static refused(host, port) {
|
||||
return new MtaConnectionError(`Connection to ${host}:${port} refused`, 'REFUSED');
|
||||
}
|
||||
static dnsError(host, err) {
|
||||
const errMsg = typeof err === 'string' ? err : err?.message || '';
|
||||
return new MtaConnectionError(`DNS resolution failed for ${host}${errMsg ? `: ${errMsg}` : ''}`, 'DNS_ERROR');
|
||||
}
|
||||
}
|
||||
export class MtaAuthenticationError extends Error {
|
||||
code;
|
||||
details;
|
||||
constructor(message, detailsOrCode) {
|
||||
super(message);
|
||||
this.name = 'MtaAuthenticationError';
|
||||
if (typeof detailsOrCode === 'string') {
|
||||
this.code = detailsOrCode;
|
||||
}
|
||||
else {
|
||||
this.code = 'AUTH_ERROR';
|
||||
this.details = detailsOrCode;
|
||||
}
|
||||
}
|
||||
static invalidCredentials(host, user) {
|
||||
const detail = host && user ? `${user}@${host}` : host || user || '';
|
||||
return new MtaAuthenticationError(`Authentication failed${detail ? `: ${detail}` : ''}`, 'INVALID_CREDENTIALS');
|
||||
}
|
||||
}
|
||||
export class MtaDeliveryError extends Error {
|
||||
code;
|
||||
responseCode;
|
||||
details;
|
||||
constructor(message, detailsOrCode, responseCode) {
|
||||
super(message);
|
||||
this.name = 'MtaDeliveryError';
|
||||
if (typeof detailsOrCode === 'string') {
|
||||
this.code = detailsOrCode;
|
||||
this.responseCode = responseCode;
|
||||
}
|
||||
else {
|
||||
this.code = 'DELIVERY_ERROR';
|
||||
this.details = detailsOrCode;
|
||||
}
|
||||
}
|
||||
static temporary(message, ...args) {
|
||||
return new MtaDeliveryError(message, 'TEMPORARY');
|
||||
}
|
||||
static permanent(message, ...args) {
|
||||
return new MtaDeliveryError(message, 'PERMANENT');
|
||||
}
|
||||
}
|
||||
export class MtaConfigurationError extends Error {
|
||||
code;
|
||||
details;
|
||||
constructor(message, detailsOrCode) {
|
||||
super(message);
|
||||
this.name = 'MtaConfigurationError';
|
||||
if (typeof detailsOrCode === 'string') {
|
||||
this.code = detailsOrCode;
|
||||
}
|
||||
else {
|
||||
this.code = 'CONFIG_ERROR';
|
||||
this.details = detailsOrCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
export class MtaTimeoutError extends Error {
|
||||
code;
|
||||
details;
|
||||
constructor(message, detailsOrCode) {
|
||||
super(message);
|
||||
this.name = 'MtaTimeoutError';
|
||||
if (typeof detailsOrCode === 'string') {
|
||||
this.code = detailsOrCode;
|
||||
}
|
||||
else {
|
||||
this.code = 'TIMEOUT';
|
||||
this.details = detailsOrCode;
|
||||
}
|
||||
}
|
||||
static commandTimeout(command, hostOrTimeout, timeoutMs) {
|
||||
const timeout = typeof hostOrTimeout === 'number' ? hostOrTimeout : timeoutMs;
|
||||
return new MtaTimeoutError(`Command '${command}' timed out${timeout ? ` after ${timeout}ms` : ''}`, 'COMMAND_TIMEOUT');
|
||||
}
|
||||
}
|
||||
export class MtaProtocolError extends Error {
|
||||
code;
|
||||
details;
|
||||
constructor(message, detailsOrCode) {
|
||||
super(message);
|
||||
this.name = 'MtaProtocolError';
|
||||
if (typeof detailsOrCode === 'string') {
|
||||
this.code = detailsOrCode;
|
||||
}
|
||||
else {
|
||||
this.code = 'PROTOCOL_ERROR';
|
||||
this.details = detailsOrCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9lcnJvcnMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxNQUFNLE9BQU8sa0JBQW1CLFNBQVEsS0FBSztJQUNwQyxJQUFJLENBQVM7SUFDYixPQUFPLENBQU87SUFDckIsWUFBWSxPQUFlLEVBQUUsYUFBbUI7UUFDOUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ2YsSUFBSSxDQUFDLElBQUksR0FBRyxvQkFBb0IsQ0FBQztRQUNqQyxJQUFJLE9BQU8sYUFBYSxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQ3RDLElBQUksQ0FBQyxJQUFJLEdBQUcsYUFBYSxDQUFDO1FBQzVCLENBQUM7YUFBTSxDQUFDO1lBQ04sSUFBSSxDQUFDLElBQUksR0FBRyxrQkFBa0IsQ0FBQztZQUMvQixJQUFJLENBQUMsT0FBTyxHQUFHLGFBQWEsQ0FBQztRQUMvQixDQUFDO0lBQ0gsQ0FBQztJQUNELE1BQU0sQ0FBQyxPQUFPLENBQUMsSUFBWSxFQUFFLElBQVksRUFBRSxTQUFrQjtRQUMzRCxPQUFPLElBQUksa0JBQWtCLENBQUMsaUJBQWlCLElBQUksSUFBSSxJQUFJLGFBQWEsU0FBUyxDQUFDLENBQUMsQ0FBQyxVQUFVLFNBQVMsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsRUFBRSxTQUFTLENBQUMsQ0FBQztJQUNqSSxDQUFDO0lBQ0QsTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFZLEVBQUUsSUFBWTtRQUN2QyxPQUFPLElBQUksa0JBQWtCLENBQUMsaUJBQWlCLElBQUksSUFBSSxJQUFJLFVBQVUsRUFBRSxTQUFTLENBQUMsQ0FBQztJQUNwRixDQUFDO0lBQ0QsTUFBTSxDQUFDLFFBQVEsQ0FBQyxJQUFZLEVBQUUsR0FBUztRQUNyQyxNQUFNLE1BQU0sR0FBRyxPQUFPLEdBQUcsS0FBSyxRQUFRLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxD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
|
||||
17
dist_ts/logger.d.ts
vendored
Normal file
17
dist_ts/logger.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
declare class StandardLogger {
|
||||
private defaultContext;
|
||||
private correlationId;
|
||||
constructor();
|
||||
log(level: 'error' | 'warn' | 'info' | 'success' | 'debug', message: string, context?: Record<string, any>): void;
|
||||
error(message: string, context?: Record<string, any>): void;
|
||||
warn(message: string, context?: Record<string, any>): void;
|
||||
info(message: string, context?: Record<string, any>): void;
|
||||
success(message: string, context?: Record<string, any>): void;
|
||||
debug(message: string, context?: Record<string, any>): void;
|
||||
setContext(context: Record<string, any>, overwrite?: boolean): void;
|
||||
setCorrelationId(id?: string | null): string;
|
||||
getCorrelationId(): string | null;
|
||||
clearCorrelationId(): void;
|
||||
}
|
||||
export declare const logger: StandardLogger;
|
||||
export {};
|
||||
76
dist_ts/logger.js
Normal file
76
dist_ts/logger.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import * as plugins from './plugins.js';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
// Map NODE_ENV to valid TEnvironment
|
||||
const nodeEnv = process.env.NODE_ENV || 'production';
|
||||
const envMap = {
|
||||
'development': 'local',
|
||||
'test': 'test',
|
||||
'staging': 'staging',
|
||||
'production': 'production'
|
||||
};
|
||||
// Default Smartlog instance
|
||||
const baseLogger = new plugins.smartlog.Smartlog({
|
||||
logContext: {
|
||||
environment: envMap[nodeEnv] || 'production',
|
||||
runtime: 'node',
|
||||
zone: 'serve.zone',
|
||||
}
|
||||
});
|
||||
// Extended logger compatible with the original enhanced logger API
|
||||
class StandardLogger {
|
||||
defaultContext = {};
|
||||
correlationId = null;
|
||||
constructor() { }
|
||||
// Log methods
|
||||
log(level, message, context = {}) {
|
||||
const combinedContext = {
|
||||
...this.defaultContext,
|
||||
...context
|
||||
};
|
||||
if (this.correlationId) {
|
||||
combinedContext.correlation_id = this.correlationId;
|
||||
}
|
||||
baseLogger.log(level, message, combinedContext);
|
||||
}
|
||||
error(message, context = {}) {
|
||||
this.log('error', message, context);
|
||||
}
|
||||
warn(message, context = {}) {
|
||||
this.log('warn', message, context);
|
||||
}
|
||||
info(message, context = {}) {
|
||||
this.log('info', message, context);
|
||||
}
|
||||
success(message, context = {}) {
|
||||
this.log('success', message, context);
|
||||
}
|
||||
debug(message, context = {}) {
|
||||
this.log('debug', message, context);
|
||||
}
|
||||
// Context management
|
||||
setContext(context, overwrite = false) {
|
||||
if (overwrite) {
|
||||
this.defaultContext = context;
|
||||
}
|
||||
else {
|
||||
this.defaultContext = {
|
||||
...this.defaultContext,
|
||||
...context
|
||||
};
|
||||
}
|
||||
}
|
||||
// Correlation ID management
|
||||
setCorrelationId(id = null) {
|
||||
this.correlationId = id || randomUUID();
|
||||
return this.correlationId;
|
||||
}
|
||||
getCorrelationId() {
|
||||
return this.correlationId;
|
||||
}
|
||||
clearCorrelationId() {
|
||||
this.correlationId = null;
|
||||
}
|
||||
}
|
||||
// Export a singleton instance
|
||||
export const logger = new StandardLogger();
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9nZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvbG9nZ2VyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFFekMscUNBQXFDO0FBQ3JDLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsUUFBUSxJQUFJLFlBQVksQ0FBQztBQUNyRCxNQUFNLE1BQU0sR0FBZ0U7SUFDMUUsYUFBYSxFQUFFLE9BQU87SUFDdEIsTUFBTSxFQUFFLE1BQU07SUFDZCxTQUFTLEVBQUUsU0FBUztJQUNwQixZQUFZLEVBQUUsWUFBWTtDQUMzQixDQUFDO0FBRUYsNEJBQTRCO0FBQzVCLE1BQU0sVUFBVSxHQUFHLElBQUksT0FBTyxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUM7SUFDL0MsVUFBVSxFQUFFO1FBQ1YsV0FBVyxFQUFFLE1BQU0sQ0FBQyxPQUFPLENBQUMsSUFBSSxZQUFZO1FBQzVDLE9BQU8sRUFBRSxNQUFNO1FBQ2YsSUFBSSxFQUFFLFlBQVk7S0FDbkI7Q0FDRixDQUFDLENBQUM7QUFFSCxtRUFBbUU7QUFDbkUsTUFBTSxjQUFjO0lBQ1YsY0FBYyxHQUF3QixFQUFFLENBQUM7SUFDekMsYUFBYSxHQUFrQixJQUFJLENBQUM7SUFFNUMsZ0JBQWUsQ0FBQztJQUVoQixjQUFjO0lBQ1AsR0FBRyxDQUFDLEtBQXNELEVBQUUsT0FBZSxFQUFFLFVBQStCLEVBQUU7UUFDbkgsTUFBTSxlQUFlLEdBQUc7WUFDdEIsR0FBRyxJQUFJLENBQUMsY0FBYztZQUN0QixHQUFHLE9BQU87U0FDWCxDQUFDO1FBRUYsSUFBSSxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7WUFDdkIsZUFBZSxDQUFDLGNBQWMsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDO1FBQ3RELENBQUM7UUFFRCxVQUFVLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxPQUFPLEVBQUUsZUFBZSxDQUFDLENBQUM7SUFDbEQsQ0FBQztJQUVNLEtBQUssQ0FBQyxPQUFlLEVBQUUsVUFBK0IsRUFBRTtRQUM3RCxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDdEMsQ0FBQztJQUVNLElBQUksQ0FBQyxPQUFlLEVBQUUsVUFBK0IsRUFBRTtRQUM1RCxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDckMsQ0FBQztJQUVNLElBQUksQ0FBQyxPQUFlLEVBQUUsVUFBK0IsRUFBRTtRQUM1RCxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDckMsQ0FBQztJQUVNLE9BQU8sQ0FBQyxPQUFlLEVBQUUsVUFBK0IsRUFBRTtRQUMvRCxJQUFJLENBQUMsR0FBRyxDQUFDLFNBQVMsRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDeEMsQ0FBQztJQUVNLEtBQUssQ0FBQyxPQUFlLEVBQUUsVUFBK0IsRUFBRTtRQUM3RCxJQUFJLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDdEMsQ0FBQztJQUVELHFCQUFxQjtJQUNkLFVBQVUsQ0FBQyxPQUE0QixFQUFFLFlBQXFCLEtBQUs7UUFDeEUsSUFBSSxTQUFTLEVBQUUsQ0FBQztZQUNkLElBQUksQ0FBQyxjQUFjLEdBQUcsT0FBTyxDQUFDO1FBQ2hDLENBQUM7YUFBTSxDQUFDO1lBQ04sSUFBSSxDQUFDLGNBQWMsR0FBRztnQkFDcEIsR0FBRyxJQUFJLENBQUMsY0FBYztnQkFDdEIsR0FBRyxPQUFPO2FBQ1gsQ0FBQztRQUNKLENBQUM7SUFDSCxDQUFDO0lBRUQsNEJBQTRCO0lBQ3JCLGdCQUFnQixDQUFDLEtBQW9CLElBQUk7UUFDOUMsSUFBSSxDQUFDLGFBQWEsR0FBRyxFQUFFLElBQUksVUFBVSxFQUFFLENBQUM7UUFDeEMsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDO0lBQzVCLENBQUM7SUFFTSxnQkFBZ0I7UUFDckIsT0FBTyxJQUFJLENBQUMsYUFBYSxDQUFDO0lBQzVCLENBQUM7SUFFTSxrQkFBa0I7UUFDdkIsSUFBSSxDQUFDLGFBQWEsR0FBRyxJQUFJLENBQUM7SUFDNUIsQ0FBQztDQUNGO0FBRUQsOEJBQThCO0FBQzlCLE1BQU0sQ0FBQyxNQUFNLE1BQU0sR0FBRyxJQUFJLGNBQWMsRUFBRSxDQUFDIn0=
|
||||
200
dist_ts/mail/core/classes.bouncemanager.d.ts
vendored
Normal file
200
dist_ts/mail/core/classes.bouncemanager.d.ts
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
import type { Email } from './classes.email.js';
|
||||
/**
|
||||
* Bounce types for categorizing the reasons for bounces
|
||||
*/
|
||||
export declare enum BounceType {
|
||||
INVALID_RECIPIENT = "invalid_recipient",
|
||||
DOMAIN_NOT_FOUND = "domain_not_found",
|
||||
MAILBOX_FULL = "mailbox_full",
|
||||
MAILBOX_INACTIVE = "mailbox_inactive",
|
||||
BLOCKED = "blocked",
|
||||
SPAM_RELATED = "spam_related",
|
||||
POLICY_RELATED = "policy_related",
|
||||
SERVER_UNAVAILABLE = "server_unavailable",
|
||||
TEMPORARY_FAILURE = "temporary_failure",
|
||||
QUOTA_EXCEEDED = "quota_exceeded",
|
||||
NETWORK_ERROR = "network_error",
|
||||
TIMEOUT = "timeout",
|
||||
AUTO_RESPONSE = "auto_response",
|
||||
CHALLENGE_RESPONSE = "challenge_response",
|
||||
UNKNOWN = "unknown"
|
||||
}
|
||||
/**
|
||||
* Hard vs soft bounce classification
|
||||
*/
|
||||
export declare enum BounceCategory {
|
||||
HARD = "hard",
|
||||
SOFT = "soft",
|
||||
AUTO_RESPONSE = "auto_response",
|
||||
UNKNOWN = "unknown"
|
||||
}
|
||||
/**
|
||||
* Bounce data structure
|
||||
*/
|
||||
export interface BounceRecord {
|
||||
id: string;
|
||||
originalEmailId?: string;
|
||||
recipient: string;
|
||||
sender: string;
|
||||
domain: string;
|
||||
subject?: string;
|
||||
bounceType: BounceType;
|
||||
bounceCategory: BounceCategory;
|
||||
timestamp: number;
|
||||
smtpResponse?: string;
|
||||
diagnosticCode?: string;
|
||||
statusCode?: string;
|
||||
headers?: Record<string, string>;
|
||||
processed: boolean;
|
||||
retryCount?: number;
|
||||
nextRetryTime?: number;
|
||||
}
|
||||
/**
|
||||
* Retry strategy configuration for soft bounces
|
||||
*/
|
||||
interface RetryStrategy {
|
||||
maxRetries: number;
|
||||
initialDelay: number;
|
||||
maxDelay: number;
|
||||
backoffFactor: number;
|
||||
}
|
||||
/**
|
||||
* Manager for handling email bounces
|
||||
*/
|
||||
export declare class BounceManager {
|
||||
private retryStrategy;
|
||||
private bounceStore;
|
||||
private bounceCache;
|
||||
private suppressionList;
|
||||
private storageManager?;
|
||||
constructor(options?: {
|
||||
retryStrategy?: Partial<RetryStrategy>;
|
||||
maxCacheSize?: number;
|
||||
cacheTTL?: number;
|
||||
storageManager?: any;
|
||||
});
|
||||
/**
|
||||
* Process a bounce notification
|
||||
* @param bounceData Bounce data to process
|
||||
* @returns Processed bounce record
|
||||
*/
|
||||
processBounce(bounceData: Partial<BounceRecord>): Promise<BounceRecord>;
|
||||
/**
|
||||
* Process an SMTP failure as a bounce
|
||||
* @param recipient Recipient email
|
||||
* @param smtpResponse SMTP error response
|
||||
* @param options Additional options
|
||||
* @returns Processed bounce record
|
||||
*/
|
||||
processSmtpFailure(recipient: string, smtpResponse: string, options?: {
|
||||
sender?: string;
|
||||
originalEmailId?: string;
|
||||
statusCode?: string;
|
||||
headers?: Record<string, string>;
|
||||
}): Promise<BounceRecord>;
|
||||
/**
|
||||
* Process a bounce notification email
|
||||
* @param bounceEmail The email containing bounce information
|
||||
* @returns Processed bounce record or null if not a bounce
|
||||
*/
|
||||
processBounceEmail(bounceEmail: Email): Promise<BounceRecord | null>;
|
||||
/**
|
||||
* Handle a hard bounce by adding to suppression list
|
||||
* @param bounce The bounce record
|
||||
*/
|
||||
private handleHardBounce;
|
||||
/**
|
||||
* Handle a soft bounce by scheduling a retry if eligible
|
||||
* @param bounce The bounce record
|
||||
*/
|
||||
private handleSoftBounce;
|
||||
/**
|
||||
* Add an email address to the suppression list
|
||||
* @param email Email address to suppress
|
||||
* @param reason Reason for suppression
|
||||
* @param expiresAt Expiration timestamp (undefined for permanent)
|
||||
*/
|
||||
addToSuppressionList(email: string, reason: string, expiresAt?: number): void;
|
||||
/**
|
||||
* Remove an email address from the suppression list
|
||||
* @param email Email address to remove
|
||||
*/
|
||||
removeFromSuppressionList(email: string): void;
|
||||
/**
|
||||
* Check if an email is on the suppression list
|
||||
* @param email Email address to check
|
||||
* @returns Whether the email is suppressed
|
||||
*/
|
||||
isEmailSuppressed(email: string): boolean;
|
||||
/**
|
||||
* Get suppression information for an email
|
||||
* @param email Email address to check
|
||||
* @returns Suppression information or null if not suppressed
|
||||
*/
|
||||
getSuppressionInfo(email: string): {
|
||||
reason: string;
|
||||
timestamp: number;
|
||||
expiresAt?: number;
|
||||
} | null;
|
||||
/**
|
||||
* Save suppression list to disk
|
||||
*/
|
||||
private saveSuppressionList;
|
||||
/**
|
||||
* Load suppression list from disk
|
||||
*/
|
||||
private loadSuppressionList;
|
||||
/**
|
||||
* Save bounce record to disk
|
||||
* @param bounce Bounce record to save
|
||||
*/
|
||||
private saveBounceRecord;
|
||||
/**
|
||||
* Update bounce cache with new bounce information
|
||||
* @param bounce Bounce record to update cache with
|
||||
*/
|
||||
private updateBounceCache;
|
||||
/**
|
||||
* Check bounce history for an email address
|
||||
* @param email Email address to check
|
||||
* @returns Bounce information or null if no bounces
|
||||
*/
|
||||
getBounceInfo(email: string): {
|
||||
lastBounce: number;
|
||||
count: number;
|
||||
type: BounceType;
|
||||
category: BounceCategory;
|
||||
} | null;
|
||||
/**
|
||||
* Analyze SMTP response and diagnostic codes to determine bounce type
|
||||
* @param smtpResponse SMTP response string
|
||||
* @param diagnosticCode Diagnostic code from bounce
|
||||
* @param statusCode Status code from bounce
|
||||
* @returns Detected bounce type and category
|
||||
*/
|
||||
private detectBounceType;
|
||||
/**
|
||||
* Check if text matches any pattern for a bounce type
|
||||
* @param text Text to check against patterns
|
||||
* @param bounceType Bounce type to get patterns for
|
||||
* @returns Whether the text matches any pattern
|
||||
*/
|
||||
private matchesPattern;
|
||||
/**
|
||||
* Get all known hard bounced addresses
|
||||
* @returns Array of hard bounced email addresses
|
||||
*/
|
||||
getHardBouncedAddresses(): string[];
|
||||
/**
|
||||
* Get suppression list
|
||||
* @returns Array of suppressed email addresses
|
||||
*/
|
||||
getSuppressionList(): string[];
|
||||
/**
|
||||
* Clear old bounce records (for maintenance)
|
||||
* @param olderThan Timestamp to remove records older than
|
||||
* @returns Number of records removed
|
||||
*/
|
||||
clearOldBounceRecords(olderThan: number): number;
|
||||
}
|
||||
export {};
|
||||
781
dist_ts/mail/core/classes.bouncemanager.js
Normal file
781
dist_ts/mail/core/classes.bouncemanager.js
Normal file
File diff suppressed because one or more lines are too long
291
dist_ts/mail/core/classes.email.d.ts
vendored
Normal file
291
dist_ts/mail/core/classes.email.d.ts
vendored
Normal file
@@ -0,0 +1,291 @@
|
||||
import * as plugins from '../../plugins.js';
|
||||
export interface IAttachment {
|
||||
filename: string;
|
||||
content: Buffer;
|
||||
contentType: string;
|
||||
contentId?: string;
|
||||
encoding?: string;
|
||||
}
|
||||
export interface IEmailOptions {
|
||||
from: string;
|
||||
to?: string | string[];
|
||||
cc?: string | string[];
|
||||
bcc?: string | string[];
|
||||
subject: string;
|
||||
text: string;
|
||||
html?: string;
|
||||
attachments?: IAttachment[];
|
||||
headers?: Record<string, string>;
|
||||
mightBeSpam?: boolean;
|
||||
priority?: 'high' | 'normal' | 'low';
|
||||
skipAdvancedValidation?: boolean;
|
||||
variables?: Record<string, any>;
|
||||
}
|
||||
/**
|
||||
* Email class represents a complete email message.
|
||||
*
|
||||
* This class takes IEmailOptions in the constructor and normalizes the data:
|
||||
* - 'to', 'cc', 'bcc' are always converted to arrays
|
||||
* - Optional properties get default values
|
||||
* - Additional properties like messageId and envelopeFrom are generated
|
||||
*/
|
||||
export declare class Email {
|
||||
from: string;
|
||||
to: string[];
|
||||
cc: string[];
|
||||
bcc: string[];
|
||||
subject: string;
|
||||
text: string;
|
||||
html?: string;
|
||||
attachments: IAttachment[];
|
||||
headers: Record<string, string>;
|
||||
mightBeSpam: boolean;
|
||||
priority: 'high' | 'normal' | 'low';
|
||||
variables: Record<string, any>;
|
||||
private envelopeFrom;
|
||||
private messageId;
|
||||
private static emailValidator;
|
||||
constructor(options: IEmailOptions);
|
||||
/**
|
||||
* Validates an email address using smartmail's EmailAddressValidator
|
||||
* For constructor validation, we only check syntax to avoid delays
|
||||
* Supports RFC-compliant addresses including display names and bounce addresses.
|
||||
*
|
||||
* @param email The email address to validate
|
||||
* @returns boolean indicating if the email is valid
|
||||
*/
|
||||
private isValidEmail;
|
||||
/**
|
||||
* Extracts the email address from a string that may contain a display name.
|
||||
* Handles formats like:
|
||||
* - simple@example.com
|
||||
* - "John Doe" <john@example.com>
|
||||
* - John Doe <john@example.com>
|
||||
*
|
||||
* @param emailString The email string to parse
|
||||
* @returns The extracted email address or null
|
||||
*/
|
||||
private extractEmailAddress;
|
||||
/**
|
||||
* Parses and validates recipient email addresses
|
||||
* @param recipients A string or array of recipient emails
|
||||
* @returns Array of validated email addresses
|
||||
*/
|
||||
private parseRecipients;
|
||||
/**
|
||||
* Basic sanitization for strings to prevent header injection
|
||||
* @param input The string to sanitize
|
||||
* @returns Sanitized string
|
||||
*/
|
||||
private sanitizeString;
|
||||
/**
|
||||
* Gets the domain part of the from email address
|
||||
* @returns The domain part of the from email or null if invalid
|
||||
*/
|
||||
getFromDomain(): string | null;
|
||||
/**
|
||||
* Gets the clean from email address without display name
|
||||
* @returns The email address without display name
|
||||
*/
|
||||
getFromAddress(): string;
|
||||
/**
|
||||
* Converts IDN (International Domain Names) to ASCII
|
||||
* @param email The email address to convert
|
||||
* @returns The email with ASCII-converted domain
|
||||
*/
|
||||
private convertIDNToASCII;
|
||||
/**
|
||||
* Gets clean to email addresses without display names
|
||||
* @returns Array of email addresses without display names
|
||||
*/
|
||||
getToAddresses(): string[];
|
||||
/**
|
||||
* Gets clean cc email addresses without display names
|
||||
* @returns Array of email addresses without display names
|
||||
*/
|
||||
getCcAddresses(): string[];
|
||||
/**
|
||||
* Gets clean bcc email addresses without display names
|
||||
* @returns Array of email addresses without display names
|
||||
*/
|
||||
getBccAddresses(): string[];
|
||||
/**
|
||||
* Gets all recipients (to, cc, bcc) as a unique array
|
||||
* @returns Array of all unique recipient email addresses
|
||||
*/
|
||||
getAllRecipients(): string[];
|
||||
/**
|
||||
* Gets primary recipient (first in the to field)
|
||||
* @returns The primary recipient email or null if none exists
|
||||
*/
|
||||
getPrimaryRecipient(): string | null;
|
||||
/**
|
||||
* Checks if the email has attachments
|
||||
* @returns Boolean indicating if the email has attachments
|
||||
*/
|
||||
hasAttachments(): boolean;
|
||||
/**
|
||||
* Add a recipient to the email
|
||||
* @param email The recipient email address
|
||||
* @param type The recipient type (to, cc, bcc)
|
||||
* @returns This instance for method chaining
|
||||
*/
|
||||
addRecipient(email: string, type?: 'to' | 'cc' | 'bcc'): this;
|
||||
/**
|
||||
* Add an attachment to the email
|
||||
* @param attachment The attachment to add
|
||||
* @returns This instance for method chaining
|
||||
*/
|
||||
addAttachment(attachment: IAttachment): this;
|
||||
/**
|
||||
* Add a custom header to the email
|
||||
* @param name The header name
|
||||
* @param value The header value
|
||||
* @returns This instance for method chaining
|
||||
*/
|
||||
addHeader(name: string, value: string): this;
|
||||
/**
|
||||
* Set the email priority
|
||||
* @param priority The priority level
|
||||
* @returns This instance for method chaining
|
||||
*/
|
||||
setPriority(priority: 'high' | 'normal' | 'low'): this;
|
||||
/**
|
||||
* Set a template variable
|
||||
* @param key The variable key
|
||||
* @param value The variable value
|
||||
* @returns This instance for method chaining
|
||||
*/
|
||||
setVariable(key: string, value: any): this;
|
||||
/**
|
||||
* Set multiple template variables at once
|
||||
* @param variables The variables object
|
||||
* @returns This instance for method chaining
|
||||
*/
|
||||
setVariables(variables: Record<string, any>): this;
|
||||
/**
|
||||
* Get the subject with variables applied
|
||||
* @param variables Optional additional variables to apply
|
||||
* @returns The processed subject
|
||||
*/
|
||||
getSubjectWithVariables(variables?: Record<string, any>): string;
|
||||
/**
|
||||
* Get the text content with variables applied
|
||||
* @param variables Optional additional variables to apply
|
||||
* @returns The processed text content
|
||||
*/
|
||||
getTextWithVariables(variables?: Record<string, any>): string;
|
||||
/**
|
||||
* Get the HTML content with variables applied
|
||||
* @param variables Optional additional variables to apply
|
||||
* @returns The processed HTML content or undefined if none
|
||||
*/
|
||||
getHtmlWithVariables(variables?: Record<string, any>): string | undefined;
|
||||
/**
|
||||
* Apply template variables to a string
|
||||
* @param template The template string
|
||||
* @param additionalVariables Optional additional variables to apply
|
||||
* @returns The processed string
|
||||
*/
|
||||
private applyVariables;
|
||||
/**
|
||||
* Gets the total size of all attachments in bytes
|
||||
* @returns Total size of all attachments in bytes
|
||||
*/
|
||||
getAttachmentsSize(): number;
|
||||
/**
|
||||
* Perform advanced validation on sender and recipient email addresses
|
||||
* This should be called separately after instantiation when ready to check MX records
|
||||
* @param options Validation options
|
||||
* @returns Promise resolving to validation results for all addresses
|
||||
*/
|
||||
validateAddresses(options?: {
|
||||
checkMx?: boolean;
|
||||
checkDisposable?: boolean;
|
||||
checkSenderOnly?: boolean;
|
||||
checkFirstRecipientOnly?: boolean;
|
||||
}): Promise<{
|
||||
sender: {
|
||||
email: string;
|
||||
result: any;
|
||||
};
|
||||
recipients: Array<{
|
||||
email: string;
|
||||
result: any;
|
||||
}>;
|
||||
isValid: boolean;
|
||||
}>;
|
||||
/**
|
||||
* Convert this email to a smartmail instance
|
||||
* @returns A new Smartmail instance
|
||||
*/
|
||||
toSmartmail(): Promise<plugins.smartmail.Smartmail<any>>;
|
||||
/**
|
||||
* Get the from email address
|
||||
* @returns The from email address
|
||||
*/
|
||||
getFromEmail(): string;
|
||||
/**
|
||||
* Get the subject (Smartmail compatibility method)
|
||||
* @returns The email subject
|
||||
*/
|
||||
getSubject(): string;
|
||||
/**
|
||||
* Get the body content (Smartmail compatibility method)
|
||||
* @param isHtml Whether to return HTML content if available
|
||||
* @returns The email body (HTML if requested and available, otherwise plain text)
|
||||
*/
|
||||
getBody(isHtml?: boolean): string;
|
||||
/**
|
||||
* Get the from address (Smartmail compatibility method)
|
||||
* @returns The sender email address
|
||||
*/
|
||||
getFrom(): string;
|
||||
/**
|
||||
* Get the message ID
|
||||
* @returns The message ID
|
||||
*/
|
||||
getMessageId(): string;
|
||||
/**
|
||||
* Convert the Email instance back to IEmailOptions format.
|
||||
* Useful for serialization or passing to APIs that expect IEmailOptions.
|
||||
* Note: This loses some Email-specific properties like messageId and envelopeFrom.
|
||||
*
|
||||
* @returns IEmailOptions representation of this email
|
||||
*/
|
||||
toEmailOptions(): IEmailOptions;
|
||||
/**
|
||||
* Set a custom message ID
|
||||
* @param id The message ID to set
|
||||
* @returns This instance for method chaining
|
||||
*/
|
||||
setMessageId(id: string): this;
|
||||
/**
|
||||
* Get the envelope from address (return-path)
|
||||
* @returns The envelope from address
|
||||
*/
|
||||
getEnvelopeFrom(): string;
|
||||
/**
|
||||
* Set the envelope from address (return-path)
|
||||
* @param address The envelope from address to set
|
||||
* @returns This instance for method chaining
|
||||
*/
|
||||
setEnvelopeFrom(address: string): this;
|
||||
/**
|
||||
* Creates an RFC822 compliant email string
|
||||
* @param variables Optional template variables to apply
|
||||
* @returns The email formatted as an RFC822 compliant string
|
||||
*/
|
||||
toRFC822String(variables?: Record<string, any>): string;
|
||||
/**
|
||||
* Convert to simple Smartmail-compatible object (for backward compatibility)
|
||||
* @returns A Promise with a simple Smartmail-compatible object
|
||||
*/
|
||||
toSmartmailBasic(): Promise<any>;
|
||||
/**
|
||||
* Create an Email instance from a Smartmail object
|
||||
* @param smartmail The Smartmail instance to convert
|
||||
* @returns A new Email instance
|
||||
*/
|
||||
static fromSmartmail(smartmail: plugins.smartmail.Smartmail<any>): Email;
|
||||
}
|
||||
802
dist_ts/mail/core/classes.email.js
Normal file
802
dist_ts/mail/core/classes.email.js
Normal file
File diff suppressed because one or more lines are too long
61
dist_ts/mail/core/classes.emailvalidator.d.ts
vendored
Normal file
61
dist_ts/mail/core/classes.emailvalidator.d.ts
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
export interface IEmailValidationResult {
|
||||
isValid: boolean;
|
||||
hasMx: boolean;
|
||||
hasSpamMarkings: boolean;
|
||||
score: number;
|
||||
details?: {
|
||||
formatValid?: boolean;
|
||||
mxRecords?: string[];
|
||||
disposable?: boolean;
|
||||
role?: boolean;
|
||||
spamIndicators?: string[];
|
||||
errorMessage?: string;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Advanced email validator class using smartmail's capabilities
|
||||
*/
|
||||
export declare class EmailValidator {
|
||||
private validator;
|
||||
private dnsCache;
|
||||
constructor(options?: {
|
||||
maxCacheSize?: number;
|
||||
cacheTTL?: number;
|
||||
});
|
||||
/**
|
||||
* Validates an email address using comprehensive checks
|
||||
* @param email The email to validate
|
||||
* @param options Validation options
|
||||
* @returns Validation result with details
|
||||
*/
|
||||
validate(email: string, options?: {
|
||||
checkMx?: boolean;
|
||||
checkDisposable?: boolean;
|
||||
checkRole?: boolean;
|
||||
checkSyntaxOnly?: boolean;
|
||||
}): Promise<IEmailValidationResult>;
|
||||
/**
|
||||
* Gets MX records for a domain with caching
|
||||
* @param domain Domain to check
|
||||
* @returns Array of MX records
|
||||
*/
|
||||
private getMxRecords;
|
||||
/**
|
||||
* Validates multiple email addresses in batch
|
||||
* @param emails Array of emails to validate
|
||||
* @param options Validation options
|
||||
* @returns Object with email addresses as keys and validation results as values
|
||||
*/
|
||||
validateBatch(emails: string[], options?: {
|
||||
checkMx?: boolean;
|
||||
checkDisposable?: boolean;
|
||||
checkRole?: boolean;
|
||||
checkSyntaxOnly?: boolean;
|
||||
}): Promise<Record<string, IEmailValidationResult>>;
|
||||
/**
|
||||
* Quick check if an email format is valid (synchronous, no DNS checks)
|
||||
* @param email Email to check
|
||||
* @returns Boolean indicating if format is valid
|
||||
*/
|
||||
isValidFormat(email: string): boolean;
|
||||
}
|
||||
184
dist_ts/mail/core/classes.emailvalidator.js
Normal file
184
dist_ts/mail/core/classes.emailvalidator.js
Normal file
File diff suppressed because one or more lines are too long
95
dist_ts/mail/core/classes.templatemanager.d.ts
vendored
Normal file
95
dist_ts/mail/core/classes.templatemanager.d.ts
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
import { Email } from './classes.email.js';
|
||||
/**
|
||||
* Email template type definition
|
||||
*/
|
||||
export interface IEmailTemplate<T = any> {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
from: string;
|
||||
subject: string;
|
||||
bodyHtml: string;
|
||||
bodyText?: string;
|
||||
category?: string;
|
||||
sampleData?: T;
|
||||
attachments?: Array<{
|
||||
name: string;
|
||||
path: string;
|
||||
contentType?: string;
|
||||
}>;
|
||||
}
|
||||
/**
|
||||
* Email template context - data used to render the template
|
||||
*/
|
||||
export interface ITemplateContext {
|
||||
[key: string]: any;
|
||||
}
|
||||
/**
|
||||
* Template category definitions
|
||||
*/
|
||||
export declare enum TemplateCategory {
|
||||
NOTIFICATION = "notification",
|
||||
TRANSACTIONAL = "transactional",
|
||||
MARKETING = "marketing",
|
||||
SYSTEM = "system"
|
||||
}
|
||||
/**
|
||||
* Enhanced template manager using Email class for template rendering
|
||||
*/
|
||||
export declare class TemplateManager {
|
||||
private templates;
|
||||
private defaultConfig;
|
||||
constructor(defaultConfig?: {
|
||||
from?: string;
|
||||
replyTo?: string;
|
||||
footerHtml?: string;
|
||||
footerText?: string;
|
||||
});
|
||||
/**
|
||||
* Register built-in email templates
|
||||
*/
|
||||
private registerBuiltinTemplates;
|
||||
/**
|
||||
* Register a new email template
|
||||
* @param template The email template to register
|
||||
*/
|
||||
registerTemplate<T = any>(template: IEmailTemplate<T>): void;
|
||||
/**
|
||||
* Get an email template by ID
|
||||
* @param templateId The template ID
|
||||
* @returns The template or undefined if not found
|
||||
*/
|
||||
getTemplate<T = any>(templateId: string): IEmailTemplate<T> | undefined;
|
||||
/**
|
||||
* List all available templates
|
||||
* @param category Optional category filter
|
||||
* @returns Array of email templates
|
||||
*/
|
||||
listTemplates(category?: TemplateCategory): IEmailTemplate[];
|
||||
/**
|
||||
* Create an Email instance from a template
|
||||
* @param templateId The template ID
|
||||
* @param context The template context data
|
||||
* @returns A configured Email instance
|
||||
*/
|
||||
createEmail<T = any>(templateId: string, context?: ITemplateContext): Promise<Email>;
|
||||
/**
|
||||
* Create and completely process an Email instance from a template
|
||||
* @param templateId The template ID
|
||||
* @param context The template context data
|
||||
* @returns A complete, processed Email instance ready to send
|
||||
*/
|
||||
prepareEmail<T = any>(templateId: string, context?: ITemplateContext): Promise<Email>;
|
||||
/**
|
||||
* Create a MIME-formatted email from a template
|
||||
* @param templateId The template ID
|
||||
* @param context The template context data
|
||||
* @returns A MIME-formatted email string
|
||||
*/
|
||||
createMimeEmail(templateId: string, context?: ITemplateContext): Promise<string>;
|
||||
/**
|
||||
* Load templates from a directory
|
||||
* @param directory The directory containing template JSON files
|
||||
*/
|
||||
loadTemplatesFromDirectory(directory: string): Promise<void>;
|
||||
}
|
||||
240
dist_ts/mail/core/classes.templatemanager.js
Normal file
240
dist_ts/mail/core/classes.templatemanager.js
Normal file
File diff suppressed because one or more lines are too long
4
dist_ts/mail/core/index.d.ts
vendored
Normal file
4
dist_ts/mail/core/index.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './classes.email.js';
|
||||
export * from './classes.emailvalidator.js';
|
||||
export * from './classes.templatemanager.js';
|
||||
export * from './classes.bouncemanager.js';
|
||||
6
dist_ts/mail/core/index.js
Normal file
6
dist_ts/mail/core/index.js
Normal file
@@ -0,0 +1,6 @@
|
||||
// Core email components
|
||||
export * from './classes.email.js';
|
||||
export * from './classes.emailvalidator.js';
|
||||
export * from './classes.templatemanager.js';
|
||||
export * from './classes.bouncemanager.js';
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL2NvcmUvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsd0JBQXdCO0FBQ3hCLGNBQWMsb0JBQW9CLENBQUM7QUFDbkMsY0FBYyw2QkFBNkIsQ0FBQztBQUM1QyxjQUFjLDhCQUE4QixDQUFDO0FBQzdDLGNBQWMsNEJBQTRCLENBQUMifQ==
|
||||
163
dist_ts/mail/delivery/classes.delivery.queue.d.ts
vendored
Normal file
163
dist_ts/mail/delivery/classes.delivery.queue.d.ts
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
import { EventEmitter } from 'node:events';
|
||||
import { type EmailProcessingMode } from '../routing/classes.email.config.js';
|
||||
import type { IEmailRoute } from '../routing/interfaces.js';
|
||||
/**
|
||||
* Queue item status
|
||||
*/
|
||||
export type QueueItemStatus = 'pending' | 'processing' | 'delivered' | 'failed' | 'deferred';
|
||||
/**
|
||||
* Queue item interface
|
||||
*/
|
||||
export interface IQueueItem {
|
||||
id: string;
|
||||
processingMode: EmailProcessingMode;
|
||||
processingResult: any;
|
||||
route: IEmailRoute;
|
||||
status: QueueItemStatus;
|
||||
attempts: number;
|
||||
nextAttempt: Date;
|
||||
lastError?: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
deliveredAt?: Date;
|
||||
}
|
||||
/**
|
||||
* Queue options interface
|
||||
*/
|
||||
export interface IQueueOptions {
|
||||
storageType?: 'memory' | 'disk';
|
||||
persistentPath?: string;
|
||||
checkInterval?: number;
|
||||
maxQueueSize?: number;
|
||||
maxPerDestination?: number;
|
||||
maxRetries?: number;
|
||||
baseRetryDelay?: number;
|
||||
maxRetryDelay?: number;
|
||||
}
|
||||
/**
|
||||
* Queue statistics interface
|
||||
*/
|
||||
export interface IQueueStats {
|
||||
queueSize: number;
|
||||
status: {
|
||||
pending: number;
|
||||
processing: number;
|
||||
delivered: number;
|
||||
failed: number;
|
||||
deferred: number;
|
||||
};
|
||||
modes: {
|
||||
forward: number;
|
||||
mta: number;
|
||||
process: number;
|
||||
};
|
||||
oldestItem?: Date;
|
||||
newestItem?: Date;
|
||||
averageAttempts: number;
|
||||
totalProcessed: number;
|
||||
processingActive: boolean;
|
||||
}
|
||||
/**
|
||||
* A unified queue for all email modes
|
||||
*/
|
||||
export declare class UnifiedDeliveryQueue extends EventEmitter {
|
||||
private options;
|
||||
private queue;
|
||||
private checkTimer?;
|
||||
private stats;
|
||||
private processing;
|
||||
private totalProcessed;
|
||||
/**
|
||||
* Create a new unified delivery queue
|
||||
* @param options Queue options
|
||||
*/
|
||||
constructor(options: IQueueOptions);
|
||||
/**
|
||||
* Initialize the queue
|
||||
*/
|
||||
initialize(): Promise<void>;
|
||||
/**
|
||||
* Start queue processing
|
||||
*/
|
||||
private startProcessing;
|
||||
/**
|
||||
* Stop queue processing
|
||||
*/
|
||||
private stopProcessing;
|
||||
/**
|
||||
* Check for items that need to be processed
|
||||
*/
|
||||
private processQueue;
|
||||
/**
|
||||
* Add an item to the queue
|
||||
* @param processingResult Processing result to queue
|
||||
* @param mode Processing mode
|
||||
* @param route Email route
|
||||
*/
|
||||
enqueue(processingResult: any, mode: EmailProcessingMode, route: IEmailRoute): Promise<string>;
|
||||
/**
|
||||
* Get an item from the queue
|
||||
* @param id Item ID
|
||||
*/
|
||||
getItem(id: string): IQueueItem | undefined;
|
||||
/**
|
||||
* Mark an item as being processed
|
||||
* @param id Item ID
|
||||
*/
|
||||
markProcessing(id: string): Promise<boolean>;
|
||||
/**
|
||||
* Mark an item as delivered
|
||||
* @param id Item ID
|
||||
*/
|
||||
markDelivered(id: string): Promise<boolean>;
|
||||
/**
|
||||
* Mark an item as failed
|
||||
* @param id Item ID
|
||||
* @param error Error message
|
||||
*/
|
||||
markFailed(id: string, error: string): Promise<boolean>;
|
||||
/**
|
||||
* Remove an item from the queue
|
||||
* @param id Item ID
|
||||
*/
|
||||
removeItem(id: string): Promise<boolean>;
|
||||
/**
|
||||
* Persist an item to disk
|
||||
* @param item Item to persist
|
||||
*/
|
||||
private persistItem;
|
||||
/**
|
||||
* Remove an item from disk
|
||||
* @param id Item ID
|
||||
*/
|
||||
private removeItemFromDisk;
|
||||
/**
|
||||
* Load queue items from disk
|
||||
*/
|
||||
private loadFromDisk;
|
||||
/**
|
||||
* Update queue statistics
|
||||
*/
|
||||
private updateStats;
|
||||
/**
|
||||
* Get queue statistics
|
||||
*/
|
||||
getStats(): IQueueStats;
|
||||
/**
|
||||
* Pause queue processing
|
||||
*/
|
||||
pause(): void;
|
||||
/**
|
||||
* Resume queue processing
|
||||
*/
|
||||
resume(): void;
|
||||
/**
|
||||
* Clean up old delivered and failed items
|
||||
* @param maxAge Maximum age in milliseconds (default: 7 days)
|
||||
*/
|
||||
cleanupOldItems(maxAge?: number): Promise<number>;
|
||||
/**
|
||||
* Shutdown the queue
|
||||
*/
|
||||
shutdown(): Promise<void>;
|
||||
}
|
||||
488
dist_ts/mail/delivery/classes.delivery.queue.js
Normal file
488
dist_ts/mail/delivery/classes.delivery.queue.js
Normal file
File diff suppressed because one or more lines are too long
186
dist_ts/mail/delivery/classes.delivery.system.d.ts
vendored
Normal file
186
dist_ts/mail/delivery/classes.delivery.system.d.ts
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
import { EventEmitter } from 'node:events';
|
||||
import { UnifiedDeliveryQueue, type IQueueItem } from './classes.delivery.queue.js';
|
||||
import type { UnifiedEmailServer } from '../routing/classes.unified.email.server.js';
|
||||
/**
|
||||
* Delivery status enumeration
|
||||
*/
|
||||
export declare enum DeliveryStatus {
|
||||
PENDING = "pending",
|
||||
DELIVERING = "delivering",
|
||||
DELIVERED = "delivered",
|
||||
DEFERRED = "deferred",
|
||||
FAILED = "failed"
|
||||
}
|
||||
/**
|
||||
* Delivery handler interface
|
||||
*/
|
||||
export interface IDeliveryHandler {
|
||||
deliver(item: IQueueItem): Promise<any>;
|
||||
}
|
||||
/**
|
||||
* Delivery options
|
||||
*/
|
||||
export interface IMultiModeDeliveryOptions {
|
||||
connectionPoolSize?: number;
|
||||
socketTimeout?: number;
|
||||
concurrentDeliveries?: number;
|
||||
sendTimeout?: number;
|
||||
verifyCertificates?: boolean;
|
||||
tlsMinVersion?: string;
|
||||
forwardHandler?: IDeliveryHandler;
|
||||
deliveryHandler?: IDeliveryHandler;
|
||||
processHandler?: IDeliveryHandler;
|
||||
globalRateLimit?: number;
|
||||
perPatternRateLimit?: Record<string, number>;
|
||||
processBounces?: boolean;
|
||||
bounceHandler?: {
|
||||
processSmtpFailure: (recipient: string, smtpResponse: string, options: any) => Promise<any>;
|
||||
};
|
||||
onDeliveryStart?: (item: IQueueItem) => Promise<void>;
|
||||
onDeliverySuccess?: (item: IQueueItem, result: any) => Promise<void>;
|
||||
onDeliveryFailed?: (item: IQueueItem, error: string) => Promise<void>;
|
||||
}
|
||||
/**
|
||||
* Delivery system statistics
|
||||
*/
|
||||
export interface IDeliveryStats {
|
||||
activeDeliveries: number;
|
||||
totalSuccessful: number;
|
||||
totalFailed: number;
|
||||
avgDeliveryTime: number;
|
||||
byMode: {
|
||||
forward: {
|
||||
successful: number;
|
||||
failed: number;
|
||||
};
|
||||
mta: {
|
||||
successful: number;
|
||||
failed: number;
|
||||
};
|
||||
process: {
|
||||
successful: number;
|
||||
failed: number;
|
||||
};
|
||||
};
|
||||
rateLimiting: {
|
||||
currentRate: number;
|
||||
globalLimit: number;
|
||||
throttled: number;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Handles delivery for all email processing modes
|
||||
*/
|
||||
export declare class MultiModeDeliverySystem extends EventEmitter {
|
||||
private queue;
|
||||
private options;
|
||||
private stats;
|
||||
private deliveryTimes;
|
||||
private activeDeliveries;
|
||||
private running;
|
||||
private throttled;
|
||||
private rateLimitLastCheck;
|
||||
private rateLimitCounter;
|
||||
private emailServer?;
|
||||
/**
|
||||
* Create a new multi-mode delivery system
|
||||
* @param queue Unified delivery queue
|
||||
* @param options Delivery options
|
||||
* @param emailServer Optional reference to unified email server for SmtpClient access
|
||||
*/
|
||||
constructor(queue: UnifiedDeliveryQueue, options: IMultiModeDeliveryOptions, emailServer?: UnifiedEmailServer);
|
||||
/**
|
||||
* Start the delivery system
|
||||
*/
|
||||
start(): Promise<void>;
|
||||
/**
|
||||
* Stop the delivery system
|
||||
*/
|
||||
stop(): Promise<void>;
|
||||
/**
|
||||
* Process ready items from the queue
|
||||
* @param items Queue items ready for processing
|
||||
*/
|
||||
private processItems;
|
||||
/**
|
||||
* Deliver an item from the queue
|
||||
* @param item Queue item to deliver
|
||||
*/
|
||||
private deliverItem;
|
||||
/**
|
||||
* Default handler for forward mode delivery
|
||||
* @param item Queue item
|
||||
*/
|
||||
private handleForwardDelivery;
|
||||
/**
|
||||
* Legacy forward delivery using raw sockets (fallback)
|
||||
* @param item Queue item
|
||||
*/
|
||||
private handleForwardDeliveryLegacy;
|
||||
/**
|
||||
* Complete the SMTP exchange after connection and initial setup
|
||||
* @param socket Network socket
|
||||
* @param email Email to send
|
||||
* @param rule Domain rule
|
||||
*/
|
||||
private completeSMTPExchange;
|
||||
/**
|
||||
* Default handler for MTA mode delivery
|
||||
* @param item Queue item
|
||||
*/
|
||||
private handleMtaDelivery;
|
||||
/**
|
||||
* Default handler for process mode delivery
|
||||
* @param item Queue item
|
||||
*/
|
||||
private handleProcessDelivery;
|
||||
/**
|
||||
* Get file extension from filename
|
||||
*/
|
||||
private getFileExtension;
|
||||
/**
|
||||
* Apply DKIM signing to an email
|
||||
*/
|
||||
private applyDkimSigning;
|
||||
/**
|
||||
* Format email for SMTP transmission
|
||||
* @param email Email to format
|
||||
*/
|
||||
private getFormattedEmail;
|
||||
/**
|
||||
* Send SMTP command and wait for response
|
||||
* @param socket Socket connection
|
||||
* @param command SMTP command to send
|
||||
*/
|
||||
private smtpCommand;
|
||||
/**
|
||||
* Send SMTP DATA command with content
|
||||
* @param socket Socket connection
|
||||
* @param data Email content to send
|
||||
*/
|
||||
private smtpData;
|
||||
/**
|
||||
* Upgrade socket to TLS
|
||||
* @param socket Socket connection
|
||||
* @param hostname Target hostname for TLS
|
||||
*/
|
||||
private upgradeTls;
|
||||
/**
|
||||
* Update delivery time statistics
|
||||
*/
|
||||
private updateDeliveryTimeStats;
|
||||
/**
|
||||
* Check if rate limit is exceeded
|
||||
* @returns True if rate limited, false otherwise
|
||||
*/
|
||||
private checkRateLimit;
|
||||
/**
|
||||
* Update delivery options
|
||||
* @param options New options
|
||||
*/
|
||||
updateOptions(options: Partial<IMultiModeDeliveryOptions>): void;
|
||||
/**
|
||||
* Get delivery statistics
|
||||
*/
|
||||
getStats(): IDeliveryStats;
|
||||
}
|
||||
855
dist_ts/mail/delivery/classes.delivery.system.js
Normal file
855
dist_ts/mail/delivery/classes.delivery.system.js
Normal file
File diff suppressed because one or more lines are too long
84
dist_ts/mail/delivery/classes.emailsendjob.d.ts
vendored
Normal file
84
dist_ts/mail/delivery/classes.emailsendjob.d.ts
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
import * as plugins from '../../plugins.js';
|
||||
import { Email } from '../core/classes.email.js';
|
||||
import type { UnifiedEmailServer } from '../routing/classes.unified.email.server.js';
|
||||
export interface IEmailSendOptions {
|
||||
maxRetries?: number;
|
||||
retryDelay?: number;
|
||||
connectionTimeout?: number;
|
||||
tlsOptions?: plugins.tls.ConnectionOptions;
|
||||
debugMode?: boolean;
|
||||
}
|
||||
export declare enum DeliveryStatus {
|
||||
PENDING = "pending",
|
||||
SENDING = "sending",
|
||||
DELIVERED = "delivered",
|
||||
FAILED = "failed",
|
||||
DEFERRED = "deferred"
|
||||
}
|
||||
export interface DeliveryInfo {
|
||||
status: DeliveryStatus;
|
||||
attempts: number;
|
||||
error?: Error;
|
||||
lastAttempt?: Date;
|
||||
nextAttempt?: Date;
|
||||
mxServer?: string;
|
||||
deliveryTime?: Date;
|
||||
logs: string[];
|
||||
}
|
||||
export declare class EmailSendJob {
|
||||
emailServerRef: UnifiedEmailServer;
|
||||
private email;
|
||||
private mxServers;
|
||||
private currentMxIndex;
|
||||
private options;
|
||||
deliveryInfo: DeliveryInfo;
|
||||
constructor(emailServerRef: UnifiedEmailServer, emailArg: Email, options?: IEmailSendOptions);
|
||||
/**
|
||||
* Send the email to its recipients
|
||||
*/
|
||||
send(): Promise<DeliveryStatus>;
|
||||
/**
|
||||
* Validate the email before sending
|
||||
*/
|
||||
private validateEmail;
|
||||
/**
|
||||
* Resolve MX records for the recipient domain
|
||||
*/
|
||||
private resolveMxRecords;
|
||||
/**
|
||||
* Attempt to deliver the email with retries
|
||||
*/
|
||||
private attemptDelivery;
|
||||
/**
|
||||
* Connect to a specific MX server and send the email using SmtpClient
|
||||
*/
|
||||
private connectAndSend;
|
||||
/**
|
||||
* Record delivery event for monitoring
|
||||
*/
|
||||
private recordDeliveryEvent;
|
||||
/**
|
||||
* Check if an error represents a permanent failure
|
||||
*/
|
||||
private isPermanentFailure;
|
||||
/**
|
||||
* Resolve MX records for a domain
|
||||
*/
|
||||
private resolveMx;
|
||||
/**
|
||||
* Log a message with timestamp
|
||||
*/
|
||||
private log;
|
||||
/**
|
||||
* Save successful email to storage
|
||||
*/
|
||||
private saveSuccess;
|
||||
/**
|
||||
* Save failed email to storage
|
||||
*/
|
||||
private saveFailed;
|
||||
/**
|
||||
* Delay for specified milliseconds
|
||||
*/
|
||||
private delay;
|
||||
}
|
||||
366
dist_ts/mail/delivery/classes.emailsendjob.js
Normal file
366
dist_ts/mail/delivery/classes.emailsendjob.js
Normal file
File diff suppressed because one or more lines are too long
18
dist_ts/mail/delivery/classes.emailsignjob.d.ts
vendored
Normal file
18
dist_ts/mail/delivery/classes.emailsignjob.d.ts
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { UnifiedEmailServer } from '../routing/classes.unified.email.server.js';
|
||||
interface Headers {
|
||||
[key: string]: string;
|
||||
}
|
||||
interface IEmailSignJobOptions {
|
||||
domain: string;
|
||||
selector: string;
|
||||
headers: Headers;
|
||||
body: string;
|
||||
}
|
||||
export declare class EmailSignJob {
|
||||
emailServerRef: UnifiedEmailServer;
|
||||
jobOptions: IEmailSignJobOptions;
|
||||
constructor(emailServerRef: UnifiedEmailServer, options: IEmailSignJobOptions);
|
||||
loadPrivateKey(): Promise<string>;
|
||||
getSignatureHeader(emailMessage: string): Promise<string>;
|
||||
}
|
||||
export {};
|
||||
36
dist_ts/mail/delivery/classes.emailsignjob.js
Normal file
36
dist_ts/mail/delivery/classes.emailsignjob.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import * as plugins from '../../plugins.js';
|
||||
export class EmailSignJob {
|
||||
emailServerRef;
|
||||
jobOptions;
|
||||
constructor(emailServerRef, options) {
|
||||
this.emailServerRef = emailServerRef;
|
||||
this.jobOptions = options;
|
||||
}
|
||||
async loadPrivateKey() {
|
||||
const keyInfo = await this.emailServerRef.dkimCreator.readDKIMKeys(this.jobOptions.domain);
|
||||
return keyInfo.privateKey;
|
||||
}
|
||||
async getSignatureHeader(emailMessage) {
|
||||
const privateKey = await this.loadPrivateKey();
|
||||
const signResult = await plugins.dkimSign(emailMessage, {
|
||||
signingDomain: this.jobOptions.domain,
|
||||
selector: this.jobOptions.selector,
|
||||
privateKey,
|
||||
canonicalization: 'relaxed/relaxed',
|
||||
algorithm: 'rsa-sha256',
|
||||
signTime: new Date(),
|
||||
signatureData: [
|
||||
{
|
||||
signingDomain: this.jobOptions.domain,
|
||||
selector: this.jobOptions.selector,
|
||||
privateKey,
|
||||
algorithm: 'rsa-sha256',
|
||||
canonicalization: 'relaxed/relaxed',
|
||||
},
|
||||
],
|
||||
});
|
||||
const signature = signResult.signatures;
|
||||
return signature;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5lbWFpbHNpZ25qb2IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL2RlbGl2ZXJ5L2NsYXNzZXMuZW1haWxzaWduam9iLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFjNUMsTUFBTSxPQUFPLFlBQVk7SUFDdkIsY0FBYyxDQUFxQjtJQUNuQyxVQUFVLENBQXVCO0lBRWpDLFlBQVksY0FBa0MsRUFBRSxPQUE2QjtRQUMzRSxJQUFJLENBQUMsY0FBYyxHQUFHLGNBQWMsQ0FBQztRQUNyQyxJQUFJLENBQUMsVUFBVSxHQUFHLE9BQU8sQ0FBQztJQUM1QixDQUFDO0lBRUQsS0FBSyxDQUFDLGNBQWM7UUFDbEIsTUFBTSxPQUFPLEdBQUcsTUFBTSxJQUFJLENBQUMsY0FBYyxDQUFDLFdBQVcsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUMzRixPQUFPLE9BQU8sQ0FBQyxVQUFVLENBQUM7SUFDNUIsQ0FBQztJQUVNLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxZQUFvQjtRQUNsRCxNQUFNLFVBQVUsR0FBRyxNQUFNLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztRQUMvQyxNQUFNLFVBQVUsR0FBRyxNQUFNLE9BQU8sQ0FBQyxRQUFRLENBQUMsWUFBWSxFQUFFO1lBQ3RELGFBQWEsRUFBRSxJQUFJLENBQUMsVUFBVSxDQUFDLE1BQU07WUFDckMsUUFBUSxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUMsUUFBUTtZQUNsQyxVQUFVO1lBQ1YsZ0JBQWdCLEVBQUUsaUJBQWlCO1lBQ25DLFNBQVMsRUFBRSxZQUFZO1lBQ3ZCLFFBQVEsRUFBRSxJQUFJLElBQUksRUFBRTtZQUNwQixhQUFhLEVBQUU7Z0JBQ2I7b0JBQ0UsYUFBYSxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUMsTUFBTTtvQkFDckMsUUFBUSxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUMsUUFBUTtvQkFDbEMsVUFBVTtvQkFDVixTQUFTLEVBQUUsWUFBWTtvQkFDdkIsZ0JBQWdCLEVBQUUsaUJBQWlCO2lCQUNwQzthQUNGO1NBQ0YsQ0FBQyxDQUFDO1FBQ0gsTUFBTSxTQUFTLEdBQUcsVUFBVSxDQUFDLFVBQVUsQ0FBQztRQUN4QyxPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0NBQ0YifQ==
|
||||
22
dist_ts/mail/delivery/classes.mta.config.d.ts
vendored
Normal file
22
dist_ts/mail/delivery/classes.mta.config.d.ts
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
import type { UnifiedEmailServer } from '../routing/classes.unified.email.server.js';
|
||||
/**
|
||||
* Configures email server storage settings
|
||||
* @param emailServer Reference to the unified email server
|
||||
* @param options Configuration options containing storage paths
|
||||
*/
|
||||
export declare function configureEmailStorage(emailServer: UnifiedEmailServer, options: any): Promise<void>;
|
||||
/**
|
||||
* Configure email server with port and storage settings
|
||||
* @param emailServer Reference to the unified email server
|
||||
* @param config Configuration settings for email server
|
||||
*/
|
||||
export declare function configureEmailServer(emailServer: UnifiedEmailServer, config: {
|
||||
ports?: number[];
|
||||
hostname?: string;
|
||||
tls?: {
|
||||
certPath?: string;
|
||||
keyPath?: string;
|
||||
caPath?: string;
|
||||
};
|
||||
storagePath?: string;
|
||||
}): Promise<boolean>;
|
||||
51
dist_ts/mail/delivery/classes.mta.config.js
Normal file
51
dist_ts/mail/delivery/classes.mta.config.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import * as plugins from '../../plugins.js';
|
||||
import * as paths from '../../paths.js';
|
||||
/**
|
||||
* Configures email server storage settings
|
||||
* @param emailServer Reference to the unified email server
|
||||
* @param options Configuration options containing storage paths
|
||||
*/
|
||||
export async function configureEmailStorage(emailServer, options) {
|
||||
// Extract the receivedEmailsPath if available
|
||||
if (options?.emailPortConfig?.receivedEmailsPath) {
|
||||
const receivedEmailsPath = options.emailPortConfig.receivedEmailsPath;
|
||||
// Ensure the directory exists
|
||||
await plugins.smartfs.directory(receivedEmailsPath).recursive().create();
|
||||
// Set path for received emails
|
||||
if (emailServer) {
|
||||
// Storage paths are now handled by the unified email server system
|
||||
await plugins.smartfs.directory(paths.receivedEmailsDir).recursive().create();
|
||||
console.log(`Configured email server to store received emails to: ${receivedEmailsPath}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Configure email server with port and storage settings
|
||||
* @param emailServer Reference to the unified email server
|
||||
* @param config Configuration settings for email server
|
||||
*/
|
||||
export async function configureEmailServer(emailServer, config) {
|
||||
if (!emailServer) {
|
||||
console.error('Email server not available');
|
||||
return false;
|
||||
}
|
||||
// Configure the email server with updated options
|
||||
const serverOptions = {
|
||||
ports: config.ports || [25, 587, 465],
|
||||
hostname: config.hostname || 'localhost',
|
||||
tls: config.tls
|
||||
};
|
||||
// Update the email server options
|
||||
emailServer.updateOptions(serverOptions);
|
||||
console.log(`Configured email server on ports ${serverOptions.ports.join(', ')}`);
|
||||
// Set up storage path if provided
|
||||
if (config.storagePath) {
|
||||
await configureEmailStorage(emailServer, {
|
||||
emailPortConfig: {
|
||||
receivedEmailsPath: config.storagePath
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5tdGEuY29uZmlnLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvbWFpbC9kZWxpdmVyeS9jbGFzc2VzLm10YS5jb25maWcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxrQkFBa0IsQ0FBQztBQUM1QyxPQUFPLEtBQUssS0FBSyxNQUFNLGdCQUFnQixDQUFDO0FBR3hDOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLHFCQUFxQixDQUFDLFdBQStCLEVBQUUsT0FBWTtJQUN2Riw4Q0FBOEM7SUFDOUMsSUFBSSxPQUFPLEVBQUUsZUFBZSxFQUFFLGtCQUFrQixFQUFFLENBQUM7UUFDakQsTUFBTSxrQkFBa0IsR0FBRyxPQUFPLENBQUMsZUFBZSxDQUFDLGtCQUFrQixDQUFDO1FBRXRFLDhCQUE4QjtRQUM5QixNQUFNLE9BQU8sQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLGtCQUFrQixDQUFDLENBQUMsU0FBUyxFQUFFLENBQUMsTUFBTSxFQUFFLENBQUM7UUFFekUsK0JBQStCO1FBQy9CLElBQUksV0FBVyxFQUFFLENBQUM7WUFDaEIsbUVBQW1FO1lBQ25FLE1BQU0sT0FBTyxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLGlCQUFpQixDQUFDLENBQUMsU0FBUyxFQUFFLENBQUMsTUFBTSxFQUFFLENBQUM7WUFFOUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyx3REFBd0Qsa0JBQWtCLEVBQUUsQ0FBQyxDQUFDO1FBQzVGLENBQUM7SUFDSCxDQUFDO0FBQ0gsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLG9CQUFvQixDQUN4QyxXQUErQixFQUMvQixNQVNDO0lBRUQsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ2pCLE9BQU8sQ0FBQyxLQUFLLENBQUMsNEJBQTRCLENBQUMsQ0FBQztRQUM1QyxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7SUFFRCxrREFBa0Q7SUFDbEQsTUFBTSxhQUFhLEdBQUc7UUFDcEIsS0FBSyxFQUFFLE1BQU0sQ0FBQyxLQUFLLElBQUksQ0FBQyxFQUFFLEVBQUUsR0FBRyxFQUFFLEdBQUcsQ0FBQztRQUNyQyxRQUFRLEVBQUUsTUFBTSxDQUFDLFFBQVEsSUFBSSxXQUFXO1FBQ3hDLEdBQUcsRUFBRSxNQUFNLENBQUMsR0FBRztLQUNoQixDQUFDO0lBRUYsa0NBQWtDO0lBQ2xDLFdBQVcsQ0FBQyxhQUFhLENBQUMsYUFBYSxDQUFDLENBQUM7SUFFekMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxvQ0FBb0MsYUFBYSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0lBRWxGLGtDQUFrQztJQUNsQyxJQUFJLE1BQU0sQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUN2QixNQUFNLHFCQUFxQixDQUFDLFdBQVcsRUFBRTtZQUN2QyxlQUFlLEVBQUU7Z0JBQ2Ysa0JBQWtCLEVBQUUsTUFBTSxDQUFDLFdBQVc7YUFDdkM7U0FDRixDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQsT0FBTyxJQUFJLENBQUM7QUFDZCxDQUFDIn0=
|
||||
108
dist_ts/mail/delivery/classes.ratelimiter.d.ts
vendored
Normal file
108
dist_ts/mail/delivery/classes.ratelimiter.d.ts
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Configuration options for rate limiter
|
||||
*/
|
||||
export interface IRateLimitConfig {
|
||||
/** Maximum tokens per period */
|
||||
maxPerPeriod: number;
|
||||
/** Time period in milliseconds */
|
||||
periodMs: number;
|
||||
/** Whether to apply per domain/key (vs globally) */
|
||||
perKey: boolean;
|
||||
/** Initial token count (defaults to max) */
|
||||
initialTokens?: number;
|
||||
/** Grace tokens to allow occasional bursts */
|
||||
burstTokens?: number;
|
||||
/** Apply global limit in addition to per-key limits */
|
||||
useGlobalLimit?: boolean;
|
||||
}
|
||||
/**
|
||||
* Rate limiter using token bucket algorithm
|
||||
* Provides more sophisticated rate limiting with burst handling
|
||||
*/
|
||||
export declare class RateLimiter {
|
||||
/** Rate limit configuration */
|
||||
private config;
|
||||
/** Token buckets per key */
|
||||
private buckets;
|
||||
/** Global bucket for non-keyed rate limiting */
|
||||
private globalBucket;
|
||||
/**
|
||||
* Create a new rate limiter
|
||||
* @param config Rate limiter configuration
|
||||
*/
|
||||
constructor(config: IRateLimitConfig);
|
||||
/**
|
||||
* Check if a request is allowed under rate limits
|
||||
* @param key Key to check rate limit for (e.g. domain, user, IP)
|
||||
* @param cost Token cost (defaults to 1)
|
||||
* @returns Whether the request is allowed
|
||||
*/
|
||||
isAllowed(key?: string, cost?: number): boolean;
|
||||
/**
|
||||
* Check if a bucket has enough tokens and consume them
|
||||
* @param bucket The token bucket to check
|
||||
* @param cost Token cost
|
||||
* @returns Whether tokens were consumed
|
||||
*/
|
||||
private checkBucket;
|
||||
/**
|
||||
* Consume tokens for a request (if available)
|
||||
* @param key Key to consume tokens for
|
||||
* @param cost Token cost (defaults to 1)
|
||||
* @returns Whether tokens were consumed
|
||||
*/
|
||||
consume(key?: string, cost?: number): boolean;
|
||||
/**
|
||||
* Get the remaining tokens for a key
|
||||
* @param key Key to check
|
||||
* @returns Number of remaining tokens
|
||||
*/
|
||||
getRemainingTokens(key?: string): number;
|
||||
/**
|
||||
* Get stats for a specific key
|
||||
* @param key Key to get stats for
|
||||
* @returns Rate limit statistics
|
||||
*/
|
||||
getStats(key?: string): {
|
||||
remaining: number;
|
||||
limit: number;
|
||||
resetIn: number;
|
||||
allowed: number;
|
||||
denied: number;
|
||||
};
|
||||
/**
|
||||
* Get or create a token bucket for a key
|
||||
* @param key The rate limit key
|
||||
* @returns Token bucket
|
||||
*/
|
||||
private getBucket;
|
||||
/**
|
||||
* Refill tokens in a bucket based on elapsed time
|
||||
* @param bucket Token bucket to refill
|
||||
*/
|
||||
private refillBucket;
|
||||
/**
|
||||
* Reset rate limits for a specific key
|
||||
* @param key Key to reset
|
||||
*/
|
||||
reset(key?: string): void;
|
||||
/**
|
||||
* Reset all rate limiters
|
||||
*/
|
||||
resetAll(): void;
|
||||
/**
|
||||
* Cleanup old buckets to prevent memory leaks
|
||||
* @param maxAge Maximum age in milliseconds
|
||||
*/
|
||||
cleanup(maxAge?: number): void;
|
||||
/**
|
||||
* Record an error for a key (e.g., IP address) and determine if blocking is needed
|
||||
* RFC 5321 Section 4.5.4.1 suggests limiting errors to prevent abuse
|
||||
*
|
||||
* @param key Key to record error for (typically an IP address)
|
||||
* @param errorWindow Time window for error tracking in ms (default: 5 minutes)
|
||||
* @param errorThreshold Maximum errors before blocking (default: 10)
|
||||
* @returns true if the key should be blocked due to excessive errors
|
||||
*/
|
||||
recordError(key: string, errorWindow?: number, errorThreshold?: number): boolean;
|
||||
}
|
||||
241
dist_ts/mail/delivery/classes.ratelimiter.js
Normal file
241
dist_ts/mail/delivery/classes.ratelimiter.js
Normal file
File diff suppressed because one or more lines are too long
275
dist_ts/mail/delivery/classes.smtp.client.legacy.d.ts
vendored
Normal file
275
dist_ts/mail/delivery/classes.smtp.client.legacy.d.ts
vendored
Normal file
@@ -0,0 +1,275 @@
|
||||
import { Email } from '../core/classes.email.js';
|
||||
import type { EmailProcessingMode } from './interfaces.js';
|
||||
/**
|
||||
* SMTP client connection options
|
||||
*/
|
||||
export type ISmtpClientOptions = {
|
||||
/**
|
||||
* Hostname of the SMTP server
|
||||
*/
|
||||
host: string;
|
||||
/**
|
||||
* Port to connect to
|
||||
*/
|
||||
port: number;
|
||||
/**
|
||||
* Whether to use TLS for the connection
|
||||
*/
|
||||
secure?: boolean;
|
||||
/**
|
||||
* Connection timeout in milliseconds
|
||||
*/
|
||||
connectionTimeout?: number;
|
||||
/**
|
||||
* Socket timeout in milliseconds
|
||||
*/
|
||||
socketTimeout?: number;
|
||||
/**
|
||||
* Command timeout in milliseconds
|
||||
*/
|
||||
commandTimeout?: number;
|
||||
/**
|
||||
* TLS options
|
||||
*/
|
||||
tls?: {
|
||||
/**
|
||||
* Whether to verify certificates
|
||||
*/
|
||||
rejectUnauthorized?: boolean;
|
||||
/**
|
||||
* Minimum TLS version
|
||||
*/
|
||||
minVersion?: string;
|
||||
/**
|
||||
* CA certificate path
|
||||
*/
|
||||
ca?: string;
|
||||
};
|
||||
/**
|
||||
* Authentication options
|
||||
*/
|
||||
auth?: {
|
||||
/**
|
||||
* Authentication user
|
||||
*/
|
||||
user: string;
|
||||
/**
|
||||
* Authentication password
|
||||
*/
|
||||
pass: string;
|
||||
/**
|
||||
* Authentication method
|
||||
*/
|
||||
method?: 'PLAIN' | 'LOGIN' | 'OAUTH2';
|
||||
};
|
||||
/**
|
||||
* Domain name for EHLO
|
||||
*/
|
||||
domain?: string;
|
||||
/**
|
||||
* DKIM options for signing outgoing emails
|
||||
*/
|
||||
dkim?: {
|
||||
/**
|
||||
* Whether to sign emails with DKIM
|
||||
*/
|
||||
enabled: boolean;
|
||||
/**
|
||||
* Domain name for DKIM
|
||||
*/
|
||||
domain: string;
|
||||
/**
|
||||
* Selector for DKIM
|
||||
*/
|
||||
selector: string;
|
||||
/**
|
||||
* Private key for DKIM signing
|
||||
*/
|
||||
privateKey: string;
|
||||
/**
|
||||
* Headers to sign
|
||||
*/
|
||||
headers?: string[];
|
||||
};
|
||||
};
|
||||
/**
|
||||
* SMTP delivery result
|
||||
*/
|
||||
export type ISmtpDeliveryResult = {
|
||||
/**
|
||||
* Whether the delivery was successful
|
||||
*/
|
||||
success: boolean;
|
||||
/**
|
||||
* Message ID if successful
|
||||
*/
|
||||
messageId?: string;
|
||||
/**
|
||||
* Error message if failed
|
||||
*/
|
||||
error?: string;
|
||||
/**
|
||||
* SMTP response code
|
||||
*/
|
||||
responseCode?: string;
|
||||
/**
|
||||
* Recipients successfully delivered to
|
||||
*/
|
||||
acceptedRecipients: string[];
|
||||
/**
|
||||
* Recipients rejected during delivery
|
||||
*/
|
||||
rejectedRecipients: string[];
|
||||
/**
|
||||
* Server response
|
||||
*/
|
||||
response?: string;
|
||||
/**
|
||||
* Timestamp of the delivery attempt
|
||||
*/
|
||||
timestamp: number;
|
||||
/**
|
||||
* Whether DKIM signing was applied
|
||||
*/
|
||||
dkimSigned?: boolean;
|
||||
/**
|
||||
* Whether this was a TLS secured delivery
|
||||
*/
|
||||
secure?: boolean;
|
||||
/**
|
||||
* Whether authentication was used
|
||||
*/
|
||||
authenticated?: boolean;
|
||||
};
|
||||
/**
|
||||
* SMTP client for sending emails to remote mail servers
|
||||
*/
|
||||
export declare class SmtpClient {
|
||||
private options;
|
||||
private connected;
|
||||
private socket?;
|
||||
private supportedExtensions;
|
||||
/**
|
||||
* Create a new SMTP client instance
|
||||
* @param options SMTP client connection options
|
||||
*/
|
||||
constructor(options: ISmtpClientOptions);
|
||||
/**
|
||||
* Connect to the SMTP server
|
||||
*/
|
||||
connect(): Promise<void>;
|
||||
/**
|
||||
* Send EHLO command to the server
|
||||
*/
|
||||
private sendEhlo;
|
||||
/**
|
||||
* Start TLS negotiation
|
||||
*/
|
||||
private startTls;
|
||||
/**
|
||||
* Upgrade socket to TLS
|
||||
* @param socket Original socket
|
||||
*/
|
||||
private upgradeTls;
|
||||
/**
|
||||
* Authenticate with the server
|
||||
*/
|
||||
private authenticate;
|
||||
/**
|
||||
* Authenticate using PLAIN method
|
||||
* @param user Username
|
||||
* @param pass Password
|
||||
*/
|
||||
private authPlain;
|
||||
/**
|
||||
* Authenticate using LOGIN method
|
||||
* @param user Username
|
||||
* @param pass Password
|
||||
*/
|
||||
private authLogin;
|
||||
/**
|
||||
* Authenticate using OAuth2 method
|
||||
* @param user Username
|
||||
* @param token OAuth2 token
|
||||
*/
|
||||
private authOAuth2;
|
||||
/**
|
||||
* Send an email through the SMTP client
|
||||
* @param email Email to send
|
||||
* @param processingMode Optional processing mode
|
||||
*/
|
||||
sendMail(email: Email, processingMode?: EmailProcessingMode): Promise<ISmtpDeliveryResult>;
|
||||
/**
|
||||
* Apply DKIM signature to email
|
||||
* @param email Email to sign
|
||||
*/
|
||||
private applyDkimSignature;
|
||||
/**
|
||||
* Format email for SMTP transmission
|
||||
* @param email Email to format
|
||||
*/
|
||||
private getFormattedEmail;
|
||||
/**
|
||||
* Get size of email in bytes
|
||||
* @param email Email to measure
|
||||
*/
|
||||
private getEmailSize;
|
||||
/**
|
||||
* Send SMTP command and wait for response
|
||||
* @param command SMTP command to send
|
||||
*/
|
||||
private commandQueue;
|
||||
private processingCommands;
|
||||
private supportsPipelining;
|
||||
/**
|
||||
* Send an SMTP command and wait for response
|
||||
* @param command SMTP command to send
|
||||
* @param allowPipelining Whether this command can be pipelined
|
||||
*/
|
||||
private sendCommand;
|
||||
/**
|
||||
* Process the command queue - either one by one or pipelined if supported
|
||||
*/
|
||||
private processCommandQueue;
|
||||
/**
|
||||
* Process the next command in the queue (non-pipelined mode)
|
||||
*/
|
||||
private processNextCommand;
|
||||
/**
|
||||
* Process responses for pipelined commands
|
||||
*/
|
||||
private processResponses;
|
||||
/**
|
||||
* Read response from the server
|
||||
*/
|
||||
private readResponse;
|
||||
/**
|
||||
* Check if the response is complete
|
||||
* @param response Response to check
|
||||
*/
|
||||
private isCompleteResponse;
|
||||
/**
|
||||
* Check if the response is an error
|
||||
* @param response Response to check
|
||||
*/
|
||||
private isErrorResponse;
|
||||
/**
|
||||
* Create appropriate error from response
|
||||
* @param response Error response
|
||||
* @param code SMTP status code
|
||||
*/
|
||||
private createErrorFromResponse;
|
||||
/**
|
||||
* Close the connection to the server
|
||||
*/
|
||||
close(): Promise<void>;
|
||||
/**
|
||||
* Checks if the connection is active
|
||||
*/
|
||||
isConnected(): boolean;
|
||||
/**
|
||||
* Update SMTP client options
|
||||
* @param options New options
|
||||
*/
|
||||
updateOptions(options: Partial<ISmtpClientOptions>): void;
|
||||
}
|
||||
986
dist_ts/mail/delivery/classes.smtp.client.legacy.js
Normal file
986
dist_ts/mail/delivery/classes.smtp.client.legacy.js
Normal file
File diff suppressed because one or more lines are too long
200
dist_ts/mail/delivery/classes.unified.rate.limiter.d.ts
vendored
Normal file
200
dist_ts/mail/delivery/classes.unified.rate.limiter.d.ts
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
import { EventEmitter } from 'node:events';
|
||||
/**
|
||||
* Interface for rate limit configuration
|
||||
*/
|
||||
export interface IRateLimitConfig {
|
||||
maxMessagesPerMinute?: number;
|
||||
maxRecipientsPerMessage?: number;
|
||||
maxConnectionsPerIP?: number;
|
||||
maxErrorsPerIP?: number;
|
||||
maxAuthFailuresPerIP?: number;
|
||||
blockDuration?: number;
|
||||
}
|
||||
/**
|
||||
* Interface for hierarchical rate limits
|
||||
*/
|
||||
export interface IHierarchicalRateLimits {
|
||||
global: IRateLimitConfig;
|
||||
patterns?: Record<string, IRateLimitConfig>;
|
||||
ips?: Record<string, IRateLimitConfig>;
|
||||
domains?: Record<string, IRateLimitConfig>;
|
||||
blocks?: Record<string, number>;
|
||||
}
|
||||
/**
|
||||
* Rate limiter statistics
|
||||
*/
|
||||
export interface IRateLimiterStats {
|
||||
activeCounters: number;
|
||||
totalBlocked: number;
|
||||
currentlyBlocked: number;
|
||||
byPattern: Record<string, {
|
||||
messagesPerMinute: number;
|
||||
totalMessages: number;
|
||||
totalBlocked: number;
|
||||
}>;
|
||||
byIp: Record<string, {
|
||||
messagesPerMinute: number;
|
||||
totalMessages: number;
|
||||
totalBlocked: number;
|
||||
connections: number;
|
||||
errors: number;
|
||||
authFailures: number;
|
||||
blocked: boolean;
|
||||
}>;
|
||||
}
|
||||
/**
|
||||
* Result of a rate limit check
|
||||
*/
|
||||
export interface IRateLimitResult {
|
||||
allowed: boolean;
|
||||
reason?: string;
|
||||
limit?: number;
|
||||
current?: number;
|
||||
resetIn?: number;
|
||||
}
|
||||
/**
|
||||
* Unified rate limiter for all email processing modes
|
||||
*/
|
||||
export declare class UnifiedRateLimiter extends EventEmitter {
|
||||
private config;
|
||||
private counters;
|
||||
private patternCounters;
|
||||
private ipCounters;
|
||||
private domainCounters;
|
||||
private cleanupInterval?;
|
||||
private stats;
|
||||
/**
|
||||
* Create a new unified rate limiter
|
||||
* @param config Rate limit configuration
|
||||
*/
|
||||
constructor(config: IHierarchicalRateLimits);
|
||||
/**
|
||||
* Start the cleanup interval
|
||||
*/
|
||||
private startCleanupInterval;
|
||||
/**
|
||||
* Stop the cleanup interval
|
||||
*/
|
||||
stop(): void;
|
||||
/**
|
||||
* Destroy the rate limiter and clean up all resources
|
||||
*/
|
||||
destroy(): void;
|
||||
/**
|
||||
* Clean up expired counters and blocks
|
||||
*/
|
||||
private cleanup;
|
||||
/**
|
||||
* Check if a message is allowed by rate limits
|
||||
* @param email Email address
|
||||
* @param ip IP address
|
||||
* @param recipients Number of recipients
|
||||
* @param pattern Matched pattern
|
||||
* @param domain Domain name for domain-specific limits
|
||||
* @returns Result of rate limit check
|
||||
*/
|
||||
checkMessageLimit(email: string, ip: string, recipients: number, pattern?: string, domain?: string): IRateLimitResult;
|
||||
/**
|
||||
* Check global message rate limit
|
||||
* @param email Email address
|
||||
*/
|
||||
private checkGlobalMessageLimit;
|
||||
/**
|
||||
* Check pattern-specific message rate limit
|
||||
* @param pattern Pattern to check
|
||||
*/
|
||||
private checkPatternMessageLimit;
|
||||
/**
|
||||
* Check domain-specific message rate limit
|
||||
* @param domain Domain to check
|
||||
*/
|
||||
private checkDomainMessageLimit;
|
||||
/**
|
||||
* Check IP-specific message rate limit
|
||||
* @param ip IP address
|
||||
*/
|
||||
private checkIpMessageLimit;
|
||||
/**
|
||||
* Check recipient limit
|
||||
* @param email Email address
|
||||
* @param recipients Number of recipients
|
||||
* @param pattern Matched pattern
|
||||
* @param domain Domain name
|
||||
*/
|
||||
private checkRecipientLimit;
|
||||
/**
|
||||
* Record a connection from an IP
|
||||
* @param ip IP address
|
||||
* @returns Result of rate limit check
|
||||
*/
|
||||
recordConnection(ip: string): IRateLimitResult;
|
||||
/**
|
||||
* Record an error from an IP
|
||||
* @param ip IP address
|
||||
* @returns True if IP should be blocked
|
||||
*/
|
||||
recordError(ip: string): boolean;
|
||||
/**
|
||||
* Record an authentication failure from an IP
|
||||
* @param ip IP address
|
||||
* @returns True if IP should be blocked
|
||||
*/
|
||||
recordAuthFailure(ip: string): boolean;
|
||||
/**
|
||||
* Block an IP address
|
||||
* @param ip IP address to block
|
||||
* @param duration Override the default block duration (milliseconds)
|
||||
*/
|
||||
blockIp(ip: string, duration?: number): void;
|
||||
/**
|
||||
* Unblock an IP address
|
||||
* @param ip IP address to unblock
|
||||
*/
|
||||
unblockIp(ip: string): void;
|
||||
/**
|
||||
* Check if an IP is blocked
|
||||
* @param ip IP address to check
|
||||
*/
|
||||
isIpBlocked(ip: string): boolean;
|
||||
/**
|
||||
* Get the time until a block is released
|
||||
* @param ip IP address
|
||||
* @returns Milliseconds until release or 0 if not blocked
|
||||
*/
|
||||
getBlockReleaseTime(ip: string): number;
|
||||
/**
|
||||
* Update rate limiter statistics
|
||||
*/
|
||||
private updateStats;
|
||||
/**
|
||||
* Get rate limiter statistics
|
||||
*/
|
||||
getStats(): IRateLimiterStats;
|
||||
/**
|
||||
* Update rate limiter configuration
|
||||
* @param config New configuration
|
||||
*/
|
||||
updateConfig(config: Partial<IHierarchicalRateLimits>): void;
|
||||
/**
|
||||
* Get configuration for debugging
|
||||
*/
|
||||
getConfig(): IHierarchicalRateLimits;
|
||||
/**
|
||||
* Apply domain-specific rate limits
|
||||
* Merges domain limits with existing configuration
|
||||
* @param domain Domain name
|
||||
* @param limits Rate limit configuration for the domain
|
||||
*/
|
||||
applyDomainLimits(domain: string, limits: IRateLimitConfig): void;
|
||||
/**
|
||||
* Remove domain-specific rate limits
|
||||
* @param domain Domain name
|
||||
*/
|
||||
removeDomainLimits(domain: string): void;
|
||||
/**
|
||||
* Get domain-specific rate limits
|
||||
* @param domain Domain name
|
||||
* @returns Domain rate limit config or undefined
|
||||
*/
|
||||
getDomainLimits(domain: string): IRateLimitConfig | undefined;
|
||||
}
|
||||
820
dist_ts/mail/delivery/classes.unified.rate.limiter.js
Normal file
820
dist_ts/mail/delivery/classes.unified.rate.limiter.js
Normal file
File diff suppressed because one or more lines are too long
12
dist_ts/mail/delivery/index.d.ts
vendored
Normal file
12
dist_ts/mail/delivery/index.d.ts
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
export * from './classes.emailsignjob.js';
|
||||
export * from './classes.delivery.queue.js';
|
||||
export * from './classes.delivery.system.js';
|
||||
export { EmailSendJob } from './classes.emailsendjob.js';
|
||||
export { DeliveryStatus } from './classes.delivery.system.js';
|
||||
export { RateLimiter } from './classes.ratelimiter.js';
|
||||
export type { IRateLimitConfig } from './classes.ratelimiter.js';
|
||||
export * from './classes.unified.rate.limiter.js';
|
||||
export * from './classes.mta.config.js';
|
||||
import * as smtpClientMod from './smtpclient/index.js';
|
||||
import * as smtpServerMod from './smtpserver/index.js';
|
||||
export { smtpClientMod, smtpServerMod };
|
||||
18
dist_ts/mail/delivery/index.js
Normal file
18
dist_ts/mail/delivery/index.js
Normal file
@@ -0,0 +1,18 @@
|
||||
// Email delivery components
|
||||
export * from './classes.emailsignjob.js';
|
||||
export * from './classes.delivery.queue.js';
|
||||
export * from './classes.delivery.system.js';
|
||||
// Handle exports with naming conflicts
|
||||
export { EmailSendJob } from './classes.emailsendjob.js';
|
||||
export { DeliveryStatus } from './classes.delivery.system.js';
|
||||
// Rate limiter exports - fix naming conflict
|
||||
export { RateLimiter } from './classes.ratelimiter.js';
|
||||
// Unified rate limiter
|
||||
export * from './classes.unified.rate.limiter.js';
|
||||
// SMTP client and configuration
|
||||
export * from './classes.mta.config.js';
|
||||
// Import and export SMTP modules as namespaces to avoid conflicts
|
||||
import * as smtpClientMod from './smtpclient/index.js';
|
||||
import * as smtpServerMod from './smtpserver/index.js';
|
||||
export { smtpClientMod, smtpServerMod };
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL2RlbGl2ZXJ5L2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLDRCQUE0QjtBQUM1QixjQUFjLDJCQUEyQixDQUFDO0FBQzFDLGNBQWMsNkJBQTZCLENBQUM7QUFDNUMsY0FBYyw4QkFBOEIsQ0FBQztBQUU3Qyx1Q0FBdUM7QUFDdkMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQ3pELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUU5RCw2Q0FBNkM7QUFDN0MsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBR3ZELHVCQUF1QjtBQUN2QixjQUFjLG1DQUFtQyxDQUFDO0FBRWxELGdDQUFnQztBQUNoQyxjQUFjLHlCQUF5QixDQUFDO0FBRXhDLGtFQUFrRTtBQUNsRSxPQUFPLEtBQUssYUFBYSxNQUFNLHVCQUF1QixDQUFDO0FBQ3ZELE9BQU8sS0FBSyxhQUFhLE1BQU0sdUJBQXVCLENBQUM7QUFFdkQsT0FBTyxFQUFFLGFBQWEsRUFBRSxhQUFhLEVBQUUsQ0FBQyJ9
|
||||
243
dist_ts/mail/delivery/interfaces.d.ts
vendored
Normal file
243
dist_ts/mail/delivery/interfaces.d.ts
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
/**
|
||||
* SMTP and email delivery interface definitions
|
||||
*/
|
||||
import type { Email } from '../core/classes.email.js';
|
||||
/**
|
||||
* SMTP session state enumeration
|
||||
*/
|
||||
export declare 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;
|
||||
/**
|
||||
* Chunks of email data for more efficient buffer management
|
||||
*/
|
||||
emailDataChunks?: 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;
|
||||
/**
|
||||
* Timestamp of last activity for session timeout tracking
|
||||
*/
|
||||
lastActivity?: number;
|
||||
/**
|
||||
* Timeout ID for DATA command timeout
|
||||
*/
|
||||
dataTimeoutId?: NodeJS.Timeout;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
/**
|
||||
* Host address to bind to (defaults to all interfaces)
|
||||
*/
|
||||
host?: string;
|
||||
/**
|
||||
* Secure port for dedicated TLS connections
|
||||
*/
|
||||
securePort?: number;
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
17
dist_ts/mail/delivery/interfaces.js
Normal file
17
dist_ts/mail/delivery/interfaces.js
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* SMTP and email delivery interface definitions
|
||||
*/
|
||||
/**
|
||||
* SMTP session state enumeration
|
||||
*/
|
||||
export var SmtpState;
|
||||
(function (SmtpState) {
|
||||
SmtpState["GREETING"] = "GREETING";
|
||||
SmtpState["AFTER_EHLO"] = "AFTER_EHLO";
|
||||
SmtpState["MAIL_FROM"] = "MAIL_FROM";
|
||||
SmtpState["RCPT_TO"] = "RCPT_TO";
|
||||
SmtpState["DATA"] = "DATA";
|
||||
SmtpState["DATA_RECEIVING"] = "DATA_RECEIVING";
|
||||
SmtpState["FINISHED"] = "FINISHED";
|
||||
})(SmtpState || (SmtpState = {}));
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL21haWwvZGVsaXZlcnkvaW50ZXJmYWNlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQUlIOztHQUVHO0FBQ0gsTUFBTSxDQUFOLElBQVksU0FRWDtBQVJELFdBQVksU0FBUztJQUNuQixrQ0FBcUIsQ0FBQTtJQUNyQixzQ0FBeUIsQ0FBQTtJQUN6QixvQ0FBdUIsQ0FBQTtJQUN2QixnQ0FBbUIsQ0FBQTtJQUNuQiwwQkFBYSxDQUFBO0lBQ2IsOENBQWlDLENBQUE7SUFDakMsa0NBQXFCLENBQUE7QUFDdkIsQ0FBQyxFQVJXLFNBQVMsS0FBVCxTQUFTLFFBUXBCIn0=
|
||||
43
dist_ts/mail/delivery/smtpclient/auth-handler.d.ts
vendored
Normal file
43
dist_ts/mail/delivery/smtpclient/auth-handler.d.ts
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* SMTP Client Authentication Handler
|
||||
* Authentication mechanisms implementation
|
||||
*/
|
||||
import type { ISmtpConnection, ISmtpAuthOptions, ISmtpClientOptions } from './interfaces.js';
|
||||
import type { CommandHandler } from './command-handler.js';
|
||||
export declare class AuthHandler {
|
||||
private options;
|
||||
private commandHandler;
|
||||
constructor(options: ISmtpClientOptions, commandHandler: CommandHandler);
|
||||
/**
|
||||
* Authenticate using the configured method
|
||||
*/
|
||||
authenticate(connection: ISmtpConnection): Promise<void>;
|
||||
/**
|
||||
* Authenticate using AUTH PLAIN
|
||||
*/
|
||||
private authenticatePlain;
|
||||
/**
|
||||
* Authenticate using AUTH LOGIN
|
||||
*/
|
||||
private authenticateLogin;
|
||||
/**
|
||||
* Authenticate using OAuth2
|
||||
*/
|
||||
private authenticateOAuth2;
|
||||
/**
|
||||
* Select appropriate authentication method
|
||||
*/
|
||||
private selectAuthMethod;
|
||||
/**
|
||||
* Check if OAuth2 token is expired
|
||||
*/
|
||||
private isTokenExpired;
|
||||
/**
|
||||
* Refresh OAuth2 access token
|
||||
*/
|
||||
private refreshOAuth2Token;
|
||||
/**
|
||||
* Validate authentication configuration
|
||||
*/
|
||||
validateAuthConfig(auth: ISmtpAuthOptions): string[];
|
||||
}
|
||||
190
dist_ts/mail/delivery/smtpclient/auth-handler.js
Normal file
190
dist_ts/mail/delivery/smtpclient/auth-handler.js
Normal file
File diff suppressed because one or more lines are too long
67
dist_ts/mail/delivery/smtpclient/command-handler.d.ts
vendored
Normal file
67
dist_ts/mail/delivery/smtpclient/command-handler.d.ts
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* SMTP Client Command Handler
|
||||
* SMTP command sending and response parsing
|
||||
*/
|
||||
import { EventEmitter } from 'node:events';
|
||||
import type { ISmtpConnection, ISmtpResponse, ISmtpClientOptions, ISmtpCapabilities } from './interfaces.js';
|
||||
export declare class CommandHandler extends EventEmitter {
|
||||
private options;
|
||||
private responseBuffer;
|
||||
private pendingCommand;
|
||||
private commandTimeout;
|
||||
constructor(options: ISmtpClientOptions);
|
||||
/**
|
||||
* Send EHLO command and parse capabilities
|
||||
*/
|
||||
sendEhlo(connection: ISmtpConnection, domain?: string): Promise<ISmtpCapabilities>;
|
||||
/**
|
||||
* Send MAIL FROM command
|
||||
*/
|
||||
sendMailFrom(connection: ISmtpConnection, fromAddress: string): Promise<ISmtpResponse>;
|
||||
/**
|
||||
* Send RCPT TO command
|
||||
*/
|
||||
sendRcptTo(connection: ISmtpConnection, toAddress: string): Promise<ISmtpResponse>;
|
||||
/**
|
||||
* Send DATA command
|
||||
*/
|
||||
sendData(connection: ISmtpConnection): Promise<ISmtpResponse>;
|
||||
/**
|
||||
* Send email data content
|
||||
*/
|
||||
sendDataContent(connection: ISmtpConnection, emailData: string): Promise<ISmtpResponse>;
|
||||
/**
|
||||
* Send RSET command
|
||||
*/
|
||||
sendRset(connection: ISmtpConnection): Promise<ISmtpResponse>;
|
||||
/**
|
||||
* Send NOOP command
|
||||
*/
|
||||
sendNoop(connection: ISmtpConnection): Promise<ISmtpResponse>;
|
||||
/**
|
||||
* Send QUIT command
|
||||
*/
|
||||
sendQuit(connection: ISmtpConnection): Promise<ISmtpResponse>;
|
||||
/**
|
||||
* Send STARTTLS command
|
||||
*/
|
||||
sendStartTls(connection: ISmtpConnection): Promise<ISmtpResponse>;
|
||||
/**
|
||||
* Send AUTH command
|
||||
*/
|
||||
sendAuth(connection: ISmtpConnection, method: string, credentials?: string): Promise<ISmtpResponse>;
|
||||
/**
|
||||
* Send a generic SMTP command
|
||||
*/
|
||||
sendCommand(connection: ISmtpConnection, command: string): Promise<ISmtpResponse>;
|
||||
/**
|
||||
* Send raw data without command formatting
|
||||
*/
|
||||
sendRawData(connection: ISmtpConnection, data: string): Promise<ISmtpResponse>;
|
||||
/**
|
||||
* Wait for server greeting
|
||||
*/
|
||||
waitForGreeting(connection: ISmtpConnection): Promise<ISmtpResponse>;
|
||||
private handleIncomingData;
|
||||
private isCompleteResponse;
|
||||
}
|
||||
277
dist_ts/mail/delivery/smtpclient/command-handler.js
Normal file
277
dist_ts/mail/delivery/smtpclient/command-handler.js
Normal file
File diff suppressed because one or more lines are too long
48
dist_ts/mail/delivery/smtpclient/connection-manager.d.ts
vendored
Normal file
48
dist_ts/mail/delivery/smtpclient/connection-manager.d.ts
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* SMTP Client Connection Manager
|
||||
* Connection pooling and lifecycle management
|
||||
*/
|
||||
import { EventEmitter } from 'node:events';
|
||||
import type { ISmtpClientOptions, ISmtpConnection, IConnectionPoolStatus } from './interfaces.js';
|
||||
export declare class ConnectionManager extends EventEmitter {
|
||||
private options;
|
||||
private connections;
|
||||
private pendingConnections;
|
||||
private idleTimeout;
|
||||
constructor(options: ISmtpClientOptions);
|
||||
/**
|
||||
* Get or create a connection
|
||||
*/
|
||||
getConnection(): Promise<ISmtpConnection>;
|
||||
/**
|
||||
* Create a new connection
|
||||
*/
|
||||
createConnection(): Promise<ISmtpConnection>;
|
||||
/**
|
||||
* Release a connection back to the pool or close it
|
||||
*/
|
||||
releaseConnection(connection: ISmtpConnection): void;
|
||||
/**
|
||||
* Close a specific connection
|
||||
*/
|
||||
closeConnection(connection: ISmtpConnection): void;
|
||||
/**
|
||||
* Close all connections
|
||||
*/
|
||||
closeAllConnections(): void;
|
||||
/**
|
||||
* Get connection pool status
|
||||
*/
|
||||
getPoolStatus(): IConnectionPoolStatus;
|
||||
/**
|
||||
* Update connection activity timestamp
|
||||
*/
|
||||
updateActivity(connection: ISmtpConnection): void;
|
||||
private establishSocket;
|
||||
private setupSocketHandlers;
|
||||
private findIdleConnection;
|
||||
private shouldReuseConnection;
|
||||
private getActiveConnectionCount;
|
||||
private getConnectionId;
|
||||
private setupIdleCleanup;
|
||||
}
|
||||
239
dist_ts/mail/delivery/smtpclient/connection-manager.js
Normal file
239
dist_ts/mail/delivery/smtpclient/connection-manager.js
Normal file
File diff suppressed because one or more lines are too long
129
dist_ts/mail/delivery/smtpclient/constants.d.ts
vendored
Normal file
129
dist_ts/mail/delivery/smtpclient/constants.d.ts
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* SMTP Client Constants and Error Codes
|
||||
* All constants, error codes, and enums for SMTP client operations
|
||||
*/
|
||||
/**
|
||||
* SMTP response codes
|
||||
*/
|
||||
export declare const SMTP_CODES: {
|
||||
readonly SERVICE_READY: 220;
|
||||
readonly SERVICE_CLOSING: 221;
|
||||
readonly AUTHENTICATION_SUCCESSFUL: 235;
|
||||
readonly REQUESTED_ACTION_OK: 250;
|
||||
readonly USER_NOT_LOCAL: 251;
|
||||
readonly CANNOT_VERIFY_USER: 252;
|
||||
readonly START_MAIL_INPUT: 354;
|
||||
readonly SERVICE_NOT_AVAILABLE: 421;
|
||||
readonly MAILBOX_BUSY: 450;
|
||||
readonly LOCAL_ERROR: 451;
|
||||
readonly INSUFFICIENT_STORAGE: 452;
|
||||
readonly UNABLE_TO_ACCOMMODATE: 455;
|
||||
readonly SYNTAX_ERROR: 500;
|
||||
readonly SYNTAX_ERROR_PARAMETERS: 501;
|
||||
readonly COMMAND_NOT_IMPLEMENTED: 502;
|
||||
readonly BAD_SEQUENCE: 503;
|
||||
readonly PARAMETER_NOT_IMPLEMENTED: 504;
|
||||
readonly MAILBOX_UNAVAILABLE: 550;
|
||||
readonly USER_NOT_LOCAL_TRY_FORWARD: 551;
|
||||
readonly EXCEEDED_STORAGE: 552;
|
||||
readonly MAILBOX_NAME_NOT_ALLOWED: 553;
|
||||
readonly TRANSACTION_FAILED: 554;
|
||||
};
|
||||
/**
|
||||
* SMTP command names
|
||||
*/
|
||||
export declare const SMTP_COMMANDS: {
|
||||
readonly HELO: "HELO";
|
||||
readonly EHLO: "EHLO";
|
||||
readonly MAIL_FROM: "MAIL FROM";
|
||||
readonly RCPT_TO: "RCPT TO";
|
||||
readonly DATA: "DATA";
|
||||
readonly RSET: "RSET";
|
||||
readonly NOOP: "NOOP";
|
||||
readonly QUIT: "QUIT";
|
||||
readonly STARTTLS: "STARTTLS";
|
||||
readonly AUTH: "AUTH";
|
||||
};
|
||||
/**
|
||||
* Authentication methods
|
||||
*/
|
||||
export declare const AUTH_METHODS: {
|
||||
readonly PLAIN: "PLAIN";
|
||||
readonly LOGIN: "LOGIN";
|
||||
readonly OAUTH2: "XOAUTH2";
|
||||
readonly CRAM_MD5: "CRAM-MD5";
|
||||
};
|
||||
/**
|
||||
* Common SMTP extensions
|
||||
*/
|
||||
export declare const SMTP_EXTENSIONS: {
|
||||
readonly PIPELINING: "PIPELINING";
|
||||
readonly SIZE: "SIZE";
|
||||
readonly STARTTLS: "STARTTLS";
|
||||
readonly AUTH: "AUTH";
|
||||
readonly EIGHT_BIT_MIME: "8BITMIME";
|
||||
readonly CHUNKING: "CHUNKING";
|
||||
readonly ENHANCED_STATUS_CODES: "ENHANCEDSTATUSCODES";
|
||||
readonly DSN: "DSN";
|
||||
};
|
||||
/**
|
||||
* Default configuration values
|
||||
*/
|
||||
export declare const DEFAULTS: {
|
||||
readonly CONNECTION_TIMEOUT: 60000;
|
||||
readonly SOCKET_TIMEOUT: 300000;
|
||||
readonly COMMAND_TIMEOUT: 30000;
|
||||
readonly MAX_CONNECTIONS: 5;
|
||||
readonly MAX_MESSAGES: 100;
|
||||
readonly PORT_SMTP: 25;
|
||||
readonly PORT_SUBMISSION: 587;
|
||||
readonly PORT_SMTPS: 465;
|
||||
readonly RETRY_ATTEMPTS: 3;
|
||||
readonly RETRY_DELAY: 1000;
|
||||
readonly POOL_IDLE_TIMEOUT: 30000;
|
||||
};
|
||||
/**
|
||||
* Error types for classification
|
||||
*/
|
||||
export declare enum SmtpErrorType {
|
||||
CONNECTION_ERROR = "CONNECTION_ERROR",
|
||||
AUTHENTICATION_ERROR = "AUTHENTICATION_ERROR",
|
||||
PROTOCOL_ERROR = "PROTOCOL_ERROR",
|
||||
TIMEOUT_ERROR = "TIMEOUT_ERROR",
|
||||
TLS_ERROR = "TLS_ERROR",
|
||||
SYNTAX_ERROR = "SYNTAX_ERROR",
|
||||
MAILBOX_ERROR = "MAILBOX_ERROR",
|
||||
QUOTA_ERROR = "QUOTA_ERROR",
|
||||
UNKNOWN_ERROR = "UNKNOWN_ERROR"
|
||||
}
|
||||
/**
|
||||
* Regular expressions for parsing
|
||||
*/
|
||||
export declare const REGEX_PATTERNS: {
|
||||
readonly EMAIL_ADDRESS: RegExp;
|
||||
readonly RESPONSE_CODE: RegExp;
|
||||
readonly ENHANCED_STATUS: RegExp;
|
||||
readonly AUTH_CAPABILITIES: RegExp;
|
||||
readonly SIZE_EXTENSION: RegExp;
|
||||
};
|
||||
/**
|
||||
* Line endings and separators
|
||||
*/
|
||||
export declare const LINE_ENDINGS: {
|
||||
readonly CRLF: "\r\n";
|
||||
readonly LF: "\n";
|
||||
readonly CR: "\r";
|
||||
};
|
||||
/**
|
||||
* Connection states for internal use
|
||||
*/
|
||||
export declare const CONNECTION_STATES: {
|
||||
readonly DISCONNECTED: "disconnected";
|
||||
readonly CONNECTING: "connecting";
|
||||
readonly CONNECTED: "connected";
|
||||
readonly AUTHENTICATED: "authenticated";
|
||||
readonly READY: "ready";
|
||||
readonly BUSY: "busy";
|
||||
readonly CLOSING: "closing";
|
||||
readonly ERROR: "error";
|
||||
};
|
||||
135
dist_ts/mail/delivery/smtpclient/constants.js
Normal file
135
dist_ts/mail/delivery/smtpclient/constants.js
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* SMTP Client Constants and Error Codes
|
||||
* All constants, error codes, and enums for SMTP client operations
|
||||
*/
|
||||
/**
|
||||
* SMTP response codes
|
||||
*/
|
||||
export const SMTP_CODES = {
|
||||
// Positive completion replies
|
||||
SERVICE_READY: 220,
|
||||
SERVICE_CLOSING: 221,
|
||||
AUTHENTICATION_SUCCESSFUL: 235,
|
||||
REQUESTED_ACTION_OK: 250,
|
||||
USER_NOT_LOCAL: 251,
|
||||
CANNOT_VERIFY_USER: 252,
|
||||
// Positive intermediate replies
|
||||
START_MAIL_INPUT: 354,
|
||||
// Transient negative completion replies
|
||||
SERVICE_NOT_AVAILABLE: 421,
|
||||
MAILBOX_BUSY: 450,
|
||||
LOCAL_ERROR: 451,
|
||||
INSUFFICIENT_STORAGE: 452,
|
||||
UNABLE_TO_ACCOMMODATE: 455,
|
||||
// Permanent negative completion replies
|
||||
SYNTAX_ERROR: 500,
|
||||
SYNTAX_ERROR_PARAMETERS: 501,
|
||||
COMMAND_NOT_IMPLEMENTED: 502,
|
||||
BAD_SEQUENCE: 503,
|
||||
PARAMETER_NOT_IMPLEMENTED: 504,
|
||||
MAILBOX_UNAVAILABLE: 550,
|
||||
USER_NOT_LOCAL_TRY_FORWARD: 551,
|
||||
EXCEEDED_STORAGE: 552,
|
||||
MAILBOX_NAME_NOT_ALLOWED: 553,
|
||||
TRANSACTION_FAILED: 554
|
||||
};
|
||||
/**
|
||||
* SMTP command names
|
||||
*/
|
||||
export const SMTP_COMMANDS = {
|
||||
HELO: 'HELO',
|
||||
EHLO: 'EHLO',
|
||||
MAIL_FROM: 'MAIL FROM',
|
||||
RCPT_TO: 'RCPT TO',
|
||||
DATA: 'DATA',
|
||||
RSET: 'RSET',
|
||||
NOOP: 'NOOP',
|
||||
QUIT: 'QUIT',
|
||||
STARTTLS: 'STARTTLS',
|
||||
AUTH: 'AUTH'
|
||||
};
|
||||
/**
|
||||
* Authentication methods
|
||||
*/
|
||||
export const AUTH_METHODS = {
|
||||
PLAIN: 'PLAIN',
|
||||
LOGIN: 'LOGIN',
|
||||
OAUTH2: 'XOAUTH2',
|
||||
CRAM_MD5: 'CRAM-MD5'
|
||||
};
|
||||
/**
|
||||
* Common SMTP extensions
|
||||
*/
|
||||
export const SMTP_EXTENSIONS = {
|
||||
PIPELINING: 'PIPELINING',
|
||||
SIZE: 'SIZE',
|
||||
STARTTLS: 'STARTTLS',
|
||||
AUTH: 'AUTH',
|
||||
EIGHT_BIT_MIME: '8BITMIME',
|
||||
CHUNKING: 'CHUNKING',
|
||||
ENHANCED_STATUS_CODES: 'ENHANCEDSTATUSCODES',
|
||||
DSN: 'DSN'
|
||||
};
|
||||
/**
|
||||
* Default configuration values
|
||||
*/
|
||||
export const DEFAULTS = {
|
||||
CONNECTION_TIMEOUT: 60000, // 60 seconds
|
||||
SOCKET_TIMEOUT: 300000, // 5 minutes
|
||||
COMMAND_TIMEOUT: 30000, // 30 seconds
|
||||
MAX_CONNECTIONS: 5,
|
||||
MAX_MESSAGES: 100,
|
||||
PORT_SMTP: 25,
|
||||
PORT_SUBMISSION: 587,
|
||||
PORT_SMTPS: 465,
|
||||
RETRY_ATTEMPTS: 3,
|
||||
RETRY_DELAY: 1000,
|
||||
POOL_IDLE_TIMEOUT: 30000 // 30 seconds
|
||||
};
|
||||
/**
|
||||
* Error types for classification
|
||||
*/
|
||||
export var SmtpErrorType;
|
||||
(function (SmtpErrorType) {
|
||||
SmtpErrorType["CONNECTION_ERROR"] = "CONNECTION_ERROR";
|
||||
SmtpErrorType["AUTHENTICATION_ERROR"] = "AUTHENTICATION_ERROR";
|
||||
SmtpErrorType["PROTOCOL_ERROR"] = "PROTOCOL_ERROR";
|
||||
SmtpErrorType["TIMEOUT_ERROR"] = "TIMEOUT_ERROR";
|
||||
SmtpErrorType["TLS_ERROR"] = "TLS_ERROR";
|
||||
SmtpErrorType["SYNTAX_ERROR"] = "SYNTAX_ERROR";
|
||||
SmtpErrorType["MAILBOX_ERROR"] = "MAILBOX_ERROR";
|
||||
SmtpErrorType["QUOTA_ERROR"] = "QUOTA_ERROR";
|
||||
SmtpErrorType["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
|
||||
})(SmtpErrorType || (SmtpErrorType = {}));
|
||||
/**
|
||||
* Regular expressions for parsing
|
||||
*/
|
||||
export const REGEX_PATTERNS = {
|
||||
EMAIL_ADDRESS: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
||||
RESPONSE_CODE: /^(\d{3})([ -])(.*)/,
|
||||
ENHANCED_STATUS: /^(\d\.\d\.\d)\s/,
|
||||
AUTH_CAPABILITIES: /AUTH\s+(.+)/i,
|
||||
SIZE_EXTENSION: /SIZE\s+(\d+)/i
|
||||
};
|
||||
/**
|
||||
* Line endings and separators
|
||||
*/
|
||||
export const LINE_ENDINGS = {
|
||||
CRLF: '\r\n',
|
||||
LF: '\n',
|
||||
CR: '\r'
|
||||
};
|
||||
/**
|
||||
* Connection states for internal use
|
||||
*/
|
||||
export const CONNECTION_STATES = {
|
||||
DISCONNECTED: 'disconnected',
|
||||
CONNECTING: 'connecting',
|
||||
CONNECTED: 'connected',
|
||||
AUTHENTICATED: 'authenticated',
|
||||
READY: 'ready',
|
||||
BUSY: 'busy',
|
||||
CLOSING: 'closing',
|
||||
ERROR: 'error'
|
||||
};
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uc3RhbnRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vdHMvbWFpbC9kZWxpdmVyeS9zbXRwY2xpZW50L2NvbnN0YW50cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFFSDs7R0FFRztBQUNILE1BQU0sQ0FBQyxNQUFNLFVBQVUsR0FBRztJQUN4Qiw4QkFBOEI7SUFDOUIsYUFBYSxFQUFFLEdBQUc7SUFDbEIsZUFBZSxFQUFFLEdBQUc7SUFDcEIseUJBQXlCLEVBQUUsR0FBRztJQUM5QixtQkFBbUIsRUFBRSxHQUFHO0lBQ3hCLGNBQWMsRUFBRSxHQUFHO0lBQ25CLGtCQUFrQixFQUFFLEdBQUc7SUFFdkIsZ0NBQWdDO0lBQ2hDLGdCQUFnQixFQUFFLEdBQUc7SUFFckIsd0NBQXdDO0lBQ3hDLHFCQUFxQixFQUFFLEdBQUc7SUFDMUIsWUFBWSxFQUFFLEdBQUc7SUFDakIsV0FBVyxFQUFFLEdBQUc7SUFDaEIsb0JBQW9CLEVBQUUsR0FBRztJQUN6QixxQkFBcUIsRUFBRSxHQUFHO0lBRTFCLHdDQUF3QztJQUN4QyxZQUFZLEVBQUUsR0FBRztJQUNqQix1QkFBdUIsRUFBRSxHQUFHO0lBQzVCLHVCQUF1QixFQUFFLEdBQUc7SUFDNUIsWUFBWSxFQUFFLEdBQUc7SUFDakIseUJBQXlCLEVBQUUsR0FBRztJQUM5QixtQkFBbUIsRUFBRSxHQUFHO0lBQ3hCLDBCQUEwQixFQUFFLEdBQUc7SUFDL0IsZ0JBQWdCLEVBQUUsR0FBRztJQUNyQix3QkFBd0IsRUFBRSxHQUFHO0lBQzdCLGtCQUFrQixFQUFFLEdBQUc7Q0FDZixDQUFDO0FBRVg7O0dBRUc7QUFDSCxNQUFNLENBQUMsTUFBTSxhQUFhLEdBQUc7SUFDM0IsSUFBSSxFQUFFLE1BQU07SUFDWixJQUFJLEVBQUUsTUFBTTtJQUNaLFNBQVMsRUFBRSxXQUFXO0lBQ3RCLE9BQU8sRUFBRSxTQUFTO0lBQ2xCLElBQUksRUFBRSxNQUFNO0lBQ1osSUFB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
|
||||
22
dist_ts/mail/delivery/smtpclient/create-client.d.ts
vendored
Normal file
22
dist_ts/mail/delivery/smtpclient/create-client.d.ts
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* SMTP Client Factory
|
||||
* Factory function for client creation and dependency injection
|
||||
*/
|
||||
import { SmtpClient } from './smtp-client.js';
|
||||
import type { ISmtpClientOptions } from './interfaces.js';
|
||||
/**
|
||||
* Create a complete SMTP client with all components
|
||||
*/
|
||||
export declare function createSmtpClient(options: ISmtpClientOptions): SmtpClient;
|
||||
/**
|
||||
* Create SMTP client with connection pooling enabled
|
||||
*/
|
||||
export declare function createPooledSmtpClient(options: ISmtpClientOptions): SmtpClient;
|
||||
/**
|
||||
* Create SMTP client for high-volume sending
|
||||
*/
|
||||
export declare function createBulkSmtpClient(options: ISmtpClientOptions): SmtpClient;
|
||||
/**
|
||||
* Create SMTP client for transactional emails
|
||||
*/
|
||||
export declare function createTransactionalSmtpClient(options: ISmtpClientOptions): SmtpClient;
|
||||
86
dist_ts/mail/delivery/smtpclient/create-client.js
Normal file
86
dist_ts/mail/delivery/smtpclient/create-client.js
Normal file
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* SMTP Client Factory
|
||||
* Factory function for client creation and dependency injection
|
||||
*/
|
||||
import { SmtpClient } from './smtp-client.js';
|
||||
import { ConnectionManager } from './connection-manager.js';
|
||||
import { CommandHandler } from './command-handler.js';
|
||||
import { AuthHandler } from './auth-handler.js';
|
||||
import { TlsHandler } from './tls-handler.js';
|
||||
import { SmtpErrorHandler } from './error-handler.js';
|
||||
import { validateClientOptions } from './utils/validation.js';
|
||||
import { DEFAULTS } from './constants.js';
|
||||
/**
|
||||
* Create a complete SMTP client with all components
|
||||
*/
|
||||
export function createSmtpClient(options) {
|
||||
// Validate options
|
||||
const errors = validateClientOptions(options);
|
||||
if (errors.length > 0) {
|
||||
throw new Error(`Invalid client options: ${errors.join(', ')}`);
|
||||
}
|
||||
// Apply defaults
|
||||
const clientOptions = {
|
||||
connectionTimeout: DEFAULTS.CONNECTION_TIMEOUT,
|
||||
socketTimeout: DEFAULTS.SOCKET_TIMEOUT,
|
||||
maxConnections: DEFAULTS.MAX_CONNECTIONS,
|
||||
maxMessages: DEFAULTS.MAX_MESSAGES,
|
||||
pool: false,
|
||||
secure: false,
|
||||
debug: false,
|
||||
...options
|
||||
};
|
||||
// Create handlers
|
||||
const errorHandler = new SmtpErrorHandler(clientOptions);
|
||||
const connectionManager = new ConnectionManager(clientOptions);
|
||||
const commandHandler = new CommandHandler(clientOptions);
|
||||
const authHandler = new AuthHandler(clientOptions, commandHandler);
|
||||
const tlsHandler = new TlsHandler(clientOptions, commandHandler);
|
||||
// Create and return SMTP client
|
||||
return new SmtpClient({
|
||||
options: clientOptions,
|
||||
connectionManager,
|
||||
commandHandler,
|
||||
authHandler,
|
||||
tlsHandler,
|
||||
errorHandler
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Create SMTP client with connection pooling enabled
|
||||
*/
|
||||
export function createPooledSmtpClient(options) {
|
||||
return createSmtpClient({
|
||||
...options,
|
||||
pool: true,
|
||||
maxConnections: options.maxConnections || DEFAULTS.MAX_CONNECTIONS,
|
||||
maxMessages: options.maxMessages || DEFAULTS.MAX_MESSAGES
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Create SMTP client for high-volume sending
|
||||
*/
|
||||
export function createBulkSmtpClient(options) {
|
||||
return createSmtpClient({
|
||||
...options,
|
||||
pool: true,
|
||||
maxConnections: Math.max(options.maxConnections || 10, 10),
|
||||
maxMessages: Math.max(options.maxMessages || 1000, 1000),
|
||||
connectionTimeout: options.connectionTimeout || 30000,
|
||||
socketTimeout: options.socketTimeout || 120000
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Create SMTP client for transactional emails
|
||||
*/
|
||||
export function createTransactionalSmtpClient(options) {
|
||||
return createSmtpClient({
|
||||
...options,
|
||||
pool: false, // Use fresh connections for transactional emails
|
||||
maxConnections: 1,
|
||||
maxMessages: 1,
|
||||
connectionTimeout: options.connectionTimeout || 10000,
|
||||
socketTimeout: options.socketTimeout || 30000
|
||||
});
|
||||
}
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLWNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL21haWwvZGVsaXZlcnkvc210cGNsaWVudC9jcmVhdGUtY2xpZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUVILE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUM5QyxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUM1RCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDdEQsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBQ2hELE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUM5QyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUV0RCxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUM5RCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFMUM7O0dBRUc7QUFDSCxNQUFNLFVBQVUsZ0JBQWdCLENBQUMsT0FBMkI7SUFDMUQsbUJBQW1CO0lBQ25CLE1BQU0sTUFBTSxHQUFHLHFCQUFxQixDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQzlDLElBQUksTUFBTSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUN0QixNQUFNLElBQUksS0FBSyxDQUFDLDJCQUEyQixNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUNsRSxDQUFDO0lBRUQsaUJBQWlCO0lBQ2pCLE1BQU0sYUFBYSxHQUF1QjtRQUN4QyxpQkFBaUIsRUFBRSxRQUFRLENBQUMsa0JBQWtCO1FBQzlDLGFBQWEsRUFBRSxRQUFRLENBQUMsY0FBYztRQUN0QyxjQUFjLEVBQUUsUUFBUSxDQUFDLGVBQWU7UUFDeEMsV0FBVyxFQUFFLFFBQVEsQ0FBQyxZQUFZO1FBQ2xDLElBQUksRUFBRSxLQUFLO1FBQ1gsTUFBTSxFQUFFLEtBQUs7UUFDYixLQUFLLEVBQUUsS0FBSztRQUNaLEdBQUcsT0FBTztLQUNYLENBQUM7SUFFRixrQkFBa0I7SUFDbEIsTUFBTSxZQUFZLEdBQUcsSUFBSSxnQkFBZ0IsQ0FBQyxhQUFhLENBQUMsQ0FBQztJQUN6RCxNQUFNLGlCQUFpQixHQUFHLElBQUksaUJBQWlCLENBQUMsYUFBYSxDQUFDLENBQUM7SUFDL0QsTUFBTSxjQUFjLEdBQUcsSUFBSSxjQUFjLENBQUMsYUFBYSxDQUFDLENBQUM7SUFDekQsTUFBTSxXQUFXLEdBQUcsSUFBSSxXQUFXLENBQUMsYUFBYSxFQUFFLGNBQWMsQ0FBQyxDQUFDO0lBQ25FLE1BQU0sVUFBVSxHQUFHLElBQUksVUFBVSxDQUFDLGFBQWEsRUFBRSxjQUFjLENBQUMsQ0FBQztJQUVqRSxnQ0FBZ0M7SUFDaEMsT0FBTyxJQUFJLFVBQVUsQ0FBQztRQUNwQixPQUFPLEVBQUUsYUFBYTtRQUN0QixpQkFBaUI7UUFDakIsY0FBYztRQUNkLFdBQVc7UUFDWCxVQUFVO1FBQ1YsWUFBWTtLQUNiLENBQUMsQ0FBQztBQUNMLENBQUM7QUFFRDs7R0FFRztBQUNILE1BQU0sVUFBVSxzQkFBc0IsQ0FBQyxPQUEyQjtJQUNoRSxPQUFPLGdCQUFnQixDQUFDO1FBQ3RCLEdBQUcsT0FBTztRQUNWLElBQUksRUFBRSxJQUFJO1FBQ1YsY0FBYyxFQUFFLE9BQU8sQ0FBQyxjQUFjLElBQUksUUFBUSxDQUFDLGVBQWU7UUFDbEUsV0FBVyxFQUFFLE9BQU8sQ0FBQyxXQUFXLElBQUksUUFBUSxDQUFDLFlBQVk7S0FDMUQsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVEOztHQUVHO0FBQ0gsTUFBTSxVQUFVLG9CQUFvQixDQUFDLE9BQTJCO0lBQzlELE9BQU8sZ0JBQWdCLENBQUM7UUFDdEIsR0FBRyxPQUFPO1FBQ1YsSUFBSSxFQUFFLElBQUk7UUFDVixjQUFjLEVBQUUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsY0FBYyxJQUFJLEVBQUUsRUFBRSxFQUFFLENBQUM7UUFDMUQsV0FBVyxFQUFFLElBQUksQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLFdBQVcsSUFBSSxJQUFJLEVBQUUsSUFBSSxDQUFDO1FBQ3hELGlCQUFpQixFQUFFLE9BQU8sQ0FBQyxpQkFBaUIsSUFBSSxLQUFLO1FBQ3JELGFBQWEsRUFBRSxPQUFPLENBQUMsYUFBYSxJQUFJLE1BQU07S0FDL0MsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVEOztHQUVHO0FBQ0gsTUFBTSxVQUFVLDZCQUE2QixDQUFDLE9BQTJCO0lBQ3ZFLE9BQU8sZ0JBQWdCLENBQUM7UUFDdEIsR0FBRyxPQUFPO1FBQ1YsSUFBSSxFQUFFLEtBQUssRUFBRSxpREFBaUQ7UUFDOUQsY0FBYyxFQUFFLENBQUM7UUFDakIsV0FBVyxFQUFFLENBQUM7UUFDZCxpQkFBaUIsRUFBRSxPQUFPLENBQUMsaUJBQWlCLElBQUksS0FBSztRQUNyRCxhQUFhLEVBQUUsT0FBTyxDQUFDLGFBQWEsSUFBSSxLQUFLO0tBQzlDLENBQUMsQ0FBQztBQUNMLENBQUMifQ==
|
||||
28
dist_ts/mail/delivery/smtpclient/error-handler.d.ts
vendored
Normal file
28
dist_ts/mail/delivery/smtpclient/error-handler.d.ts
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* SMTP Client Error Handler
|
||||
* Error classification and recovery strategies
|
||||
*/
|
||||
import { SmtpErrorType } from './constants.js';
|
||||
import type { ISmtpResponse, ISmtpErrorContext, ISmtpClientOptions } from './interfaces.js';
|
||||
export declare class SmtpErrorHandler {
|
||||
private options;
|
||||
constructor(options: ISmtpClientOptions);
|
||||
/**
|
||||
* Classify error type based on response or error
|
||||
*/
|
||||
classifyError(error: Error | ISmtpResponse, context?: ISmtpErrorContext): SmtpErrorType;
|
||||
/**
|
||||
* Determine if error is retryable
|
||||
*/
|
||||
isRetryable(errorType: SmtpErrorType, response?: ISmtpResponse): boolean;
|
||||
/**
|
||||
* Get retry delay for error type
|
||||
*/
|
||||
getRetryDelay(attempt: number, errorType: SmtpErrorType): number;
|
||||
/**
|
||||
* Create enhanced error with context
|
||||
*/
|
||||
createError(message: string, errorType: SmtpErrorType, context?: ISmtpErrorContext, originalError?: Error): Error;
|
||||
private classifyErrorByMessage;
|
||||
private classifyErrorByCode;
|
||||
}
|
||||
111
dist_ts/mail/delivery/smtpclient/error-handler.js
Normal file
111
dist_ts/mail/delivery/smtpclient/error-handler.js
Normal file
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* SMTP Client Error Handler
|
||||
* Error classification and recovery strategies
|
||||
*/
|
||||
import { SmtpErrorType } from './constants.js';
|
||||
import { logDebug } from './utils/logging.js';
|
||||
export class SmtpErrorHandler {
|
||||
options;
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
}
|
||||
/**
|
||||
* Classify error type based on response or error
|
||||
*/
|
||||
classifyError(error, context) {
|
||||
logDebug('Classifying error', this.options, { errorMessage: error instanceof Error ? error.message : String(error), context });
|
||||
// Handle Error objects
|
||||
if (error instanceof Error) {
|
||||
return this.classifyErrorByMessage(error);
|
||||
}
|
||||
// Handle SMTP response codes
|
||||
if (typeof error === 'object' && 'code' in error) {
|
||||
return this.classifyErrorByCode(error.code);
|
||||
}
|
||||
return SmtpErrorType.UNKNOWN_ERROR;
|
||||
}
|
||||
/**
|
||||
* Determine if error is retryable
|
||||
*/
|
||||
isRetryable(errorType, response) {
|
||||
switch (errorType) {
|
||||
case SmtpErrorType.CONNECTION_ERROR:
|
||||
case SmtpErrorType.TIMEOUT_ERROR:
|
||||
return true;
|
||||
case SmtpErrorType.PROTOCOL_ERROR:
|
||||
// Only retry on temporary failures (4xx codes)
|
||||
return response ? response.code >= 400 && response.code < 500 : false;
|
||||
case SmtpErrorType.AUTHENTICATION_ERROR:
|
||||
case SmtpErrorType.TLS_ERROR:
|
||||
case SmtpErrorType.SYNTAX_ERROR:
|
||||
case SmtpErrorType.MAILBOX_ERROR:
|
||||
case SmtpErrorType.QUOTA_ERROR:
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get retry delay for error type
|
||||
*/
|
||||
getRetryDelay(attempt, errorType) {
|
||||
const baseDelay = 1000; // 1 second
|
||||
const maxDelay = 30000; // 30 seconds
|
||||
// Exponential backoff with jitter
|
||||
const delay = Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay);
|
||||
const jitter = Math.random() * 0.1 * delay; // 10% jitter
|
||||
return Math.floor(delay + jitter);
|
||||
}
|
||||
/**
|
||||
* Create enhanced error with context
|
||||
*/
|
||||
createError(message, errorType, context, originalError) {
|
||||
const error = new Error(message);
|
||||
error.type = errorType;
|
||||
error.context = context;
|
||||
error.originalError = originalError;
|
||||
return error;
|
||||
}
|
||||
classifyErrorByMessage(error) {
|
||||
const message = error.message.toLowerCase();
|
||||
if (message.includes('timeout') || message.includes('etimedout')) {
|
||||
return SmtpErrorType.TIMEOUT_ERROR;
|
||||
}
|
||||
if (message.includes('connect') || message.includes('econnrefused') ||
|
||||
message.includes('enotfound') || message.includes('enetunreach')) {
|
||||
return SmtpErrorType.CONNECTION_ERROR;
|
||||
}
|
||||
if (message.includes('tls') || message.includes('ssl') ||
|
||||
message.includes('certificate') || message.includes('handshake')) {
|
||||
return SmtpErrorType.TLS_ERROR;
|
||||
}
|
||||
if (message.includes('auth')) {
|
||||
return SmtpErrorType.AUTHENTICATION_ERROR;
|
||||
}
|
||||
return SmtpErrorType.UNKNOWN_ERROR;
|
||||
}
|
||||
classifyErrorByCode(code) {
|
||||
if (code >= 500) {
|
||||
// Permanent failures
|
||||
if (code === 550 || code === 551 || code === 553) {
|
||||
return SmtpErrorType.MAILBOX_ERROR;
|
||||
}
|
||||
if (code === 552) {
|
||||
return SmtpErrorType.QUOTA_ERROR;
|
||||
}
|
||||
if (code === 500 || code === 501 || code === 502 || code === 504) {
|
||||
return SmtpErrorType.SYNTAX_ERROR;
|
||||
}
|
||||
return SmtpErrorType.PROTOCOL_ERROR;
|
||||
}
|
||||
if (code >= 400) {
|
||||
// Temporary failures
|
||||
if (code === 450 || code === 451 || code === 452) {
|
||||
return SmtpErrorType.QUOTA_ERROR;
|
||||
}
|
||||
return SmtpErrorType.PROTOCOL_ERROR;
|
||||
}
|
||||
return SmtpErrorType.UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJyb3ItaGFuZGxlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL21haWwvZGVsaXZlcnkvc210cGNsaWVudC9lcnJvci1oYW5kbGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUVILE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUUvQyxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFFOUMsTUFBTSxPQUFPLGdCQUFnQjtJQUNuQixPQUFPLENBQXFCO0lBRXBDLFlBQVksT0FBMkI7UUFDckMsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7SUFDekIsQ0FBQztJQUVEOztPQUVHO0lBQ0ksYUFBYSxDQUFDLEtBQTRCLEVBQUUsT0FBMkI7UUFDNUUsUUFBUSxDQUFDLG1CQUFtQixFQUFFLElBQUksQ0FBQyxPQUFPLEVBQUUsRUFBRSxZQUFZLEVBQUUsS0FBSyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQUM7UUFFL0gsdUJBQXVCO1FBQ3ZCLElBQUksS0FBSyxZQUFZLEtBQUssRUFBRSxDQUFDO1lBQzNCLE9BQU8sSUFBSSxDQUFDLHNCQUFzQixDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzVDLENBQUM7UUFFRCw2QkFBNkI7UUFDN0IsSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksTUFBTSxJQUFJLEtBQUssRUFBRSxDQUFDO1lBQ2pELE9BQU8sSUFBSSxDQUFDLG1CQUFtQixDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM5QyxDQUFDO1FBRUQsT0FBTyxhQUFhLENBQUMsYUFBYSxDQUFDO0lBQ3JDLENBQUM7SUFFRDs7T0FFRztJQUNJLFdBQVcsQ0FBQyxTQUF3QixFQUFFLFFBQXdCO1FBQ25F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
|
||||
16
dist_ts/mail/delivery/smtpclient/index.d.ts
vendored
Normal file
16
dist_ts/mail/delivery/smtpclient/index.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* SMTP Client Module Exports
|
||||
* Modular SMTP client implementation for robust email delivery
|
||||
*/
|
||||
export * from './smtp-client.js';
|
||||
export * from './create-client.js';
|
||||
export * from './connection-manager.js';
|
||||
export * from './command-handler.js';
|
||||
export * from './auth-handler.js';
|
||||
export * from './tls-handler.js';
|
||||
export * from './error-handler.js';
|
||||
export * from './interfaces.js';
|
||||
export * from './constants.js';
|
||||
export * from './utils/validation.js';
|
||||
export * from './utils/logging.js';
|
||||
export * from './utils/helpers.js';
|
||||
21
dist_ts/mail/delivery/smtpclient/index.js
Normal file
21
dist_ts/mail/delivery/smtpclient/index.js
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* SMTP Client Module Exports
|
||||
* Modular SMTP client implementation for robust email delivery
|
||||
*/
|
||||
// Main client class and factory
|
||||
export * from './smtp-client.js';
|
||||
export * from './create-client.js';
|
||||
// Core handlers
|
||||
export * from './connection-manager.js';
|
||||
export * from './command-handler.js';
|
||||
export * from './auth-handler.js';
|
||||
export * from './tls-handler.js';
|
||||
export * from './error-handler.js';
|
||||
// Interfaces and types
|
||||
export * from './interfaces.js';
|
||||
export * from './constants.js';
|
||||
// Utilities
|
||||
export * from './utils/validation.js';
|
||||
export * from './utils/logging.js';
|
||||
export * from './utils/helpers.js';
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi90cy9tYWlsL2RlbGl2ZXJ5L3NtdHBjbGllbnQvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBRUgsZ0NBQWdDO0FBQ2hDLGNBQWMsa0JBQWtCLENBQUM7QUFDakMsY0FBYyxvQkFBb0IsQ0FBQztBQUVuQyxnQkFBZ0I7QUFDaEIsY0FBYyx5QkFBeUIsQ0FBQztBQUN4QyxjQUFjLHNCQUFzQixDQUFDO0FBQ3JDLGNBQWMsbUJBQW1CLENBQUM7QUFDbEMsY0FBYyxrQkFBa0IsQ0FBQztBQUNqQyxjQUFjLG9CQUFvQixDQUFDO0FBRW5DLHVCQUF1QjtBQUN2QixjQUFjLGlCQUFpQixDQUFDO0FBQ2hDLGNBQWMsZ0JBQWdCLENBQUM7QUFFL0IsWUFBWTtBQUNaLGNBQWMsdUJBQXVCLENBQUM7QUFDdEMsY0FBYyxvQkFBb0IsQ0FBQztBQUNuQyxjQUFjLG9CQUFvQixDQUFDIn0=
|
||||
183
dist_ts/mail/delivery/smtpclient/interfaces.d.ts
vendored
Normal file
183
dist_ts/mail/delivery/smtpclient/interfaces.d.ts
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
/**
|
||||
* SMTP Client Interfaces and Types
|
||||
* All interface definitions for the modular SMTP client
|
||||
*/
|
||||
import type * as tls from 'node:tls';
|
||||
import type * as net from 'node:net';
|
||||
/**
|
||||
* SMTP client connection options
|
||||
*/
|
||||
export interface ISmtpClientOptions {
|
||||
/** Hostname of the SMTP server */
|
||||
host: string;
|
||||
/** Port to connect to */
|
||||
port: number;
|
||||
/** Whether to use TLS for the connection */
|
||||
secure?: boolean;
|
||||
/** Connection timeout in milliseconds */
|
||||
connectionTimeout?: number;
|
||||
/** Socket timeout in milliseconds */
|
||||
socketTimeout?: number;
|
||||
/** Domain name for EHLO command */
|
||||
domain?: string;
|
||||
/** Authentication options */
|
||||
auth?: ISmtpAuthOptions;
|
||||
/** TLS options */
|
||||
tls?: tls.ConnectionOptions;
|
||||
/** Maximum number of connections in pool */
|
||||
pool?: boolean;
|
||||
maxConnections?: number;
|
||||
maxMessages?: number;
|
||||
/** Enable debug logging */
|
||||
debug?: boolean;
|
||||
/** Proxy settings */
|
||||
proxy?: string;
|
||||
}
|
||||
/**
|
||||
* Authentication options for SMTP
|
||||
*/
|
||||
export interface ISmtpAuthOptions {
|
||||
/** Username */
|
||||
user?: string;
|
||||
/** Password */
|
||||
pass?: string;
|
||||
/** OAuth2 settings */
|
||||
oauth2?: IOAuth2Options;
|
||||
/** Authentication method preference */
|
||||
method?: 'PLAIN' | 'LOGIN' | 'OAUTH2' | 'AUTO';
|
||||
}
|
||||
/**
|
||||
* OAuth2 authentication options
|
||||
*/
|
||||
export interface IOAuth2Options {
|
||||
/** OAuth2 user identifier */
|
||||
user: string;
|
||||
/** OAuth2 client ID */
|
||||
clientId: string;
|
||||
/** OAuth2 client secret */
|
||||
clientSecret: string;
|
||||
/** OAuth2 refresh token */
|
||||
refreshToken: string;
|
||||
/** OAuth2 access token */
|
||||
accessToken?: string;
|
||||
/** Token expiry time */
|
||||
expires?: number;
|
||||
}
|
||||
/**
|
||||
* Result of an email send operation
|
||||
*/
|
||||
export interface ISmtpSendResult {
|
||||
/** Whether the send was successful */
|
||||
success: boolean;
|
||||
/** Message ID from server */
|
||||
messageId?: string;
|
||||
/** List of accepted recipients */
|
||||
acceptedRecipients: string[];
|
||||
/** List of rejected recipients */
|
||||
rejectedRecipients: string[];
|
||||
/** Error information if failed */
|
||||
error?: Error;
|
||||
/** Server response */
|
||||
response?: string;
|
||||
/** Envelope information */
|
||||
envelope?: ISmtpEnvelope;
|
||||
}
|
||||
/**
|
||||
* SMTP envelope information
|
||||
*/
|
||||
export interface ISmtpEnvelope {
|
||||
/** Sender address */
|
||||
from: string;
|
||||
/** Recipient addresses */
|
||||
to: string[];
|
||||
}
|
||||
/**
|
||||
* Connection pool status
|
||||
*/
|
||||
export interface IConnectionPoolStatus {
|
||||
/** Total connections in pool */
|
||||
total: number;
|
||||
/** Active connections */
|
||||
active: number;
|
||||
/** Idle connections */
|
||||
idle: number;
|
||||
/** Pending connection requests */
|
||||
pending: number;
|
||||
}
|
||||
/**
|
||||
* SMTP command response
|
||||
*/
|
||||
export interface ISmtpResponse {
|
||||
/** Response code */
|
||||
code: number;
|
||||
/** Response message */
|
||||
message: string;
|
||||
/** Enhanced status code */
|
||||
enhancedCode?: string;
|
||||
/** Raw response */
|
||||
raw: string;
|
||||
}
|
||||
/**
|
||||
* Connection state
|
||||
*/
|
||||
export declare enum ConnectionState {
|
||||
DISCONNECTED = "disconnected",
|
||||
CONNECTING = "connecting",
|
||||
CONNECTED = "connected",
|
||||
AUTHENTICATED = "authenticated",
|
||||
READY = "ready",
|
||||
BUSY = "busy",
|
||||
CLOSING = "closing",
|
||||
ERROR = "error"
|
||||
}
|
||||
/**
|
||||
* SMTP capabilities
|
||||
*/
|
||||
export interface ISmtpCapabilities {
|
||||
/** Supported extensions */
|
||||
extensions: Set<string>;
|
||||
/** Maximum message size */
|
||||
maxSize?: number;
|
||||
/** Supported authentication methods */
|
||||
authMethods: Set<string>;
|
||||
/** Support for pipelining */
|
||||
pipelining: boolean;
|
||||
/** Support for STARTTLS */
|
||||
starttls: boolean;
|
||||
/** Support for 8BITMIME */
|
||||
eightBitMime: boolean;
|
||||
}
|
||||
/**
|
||||
* Internal connection interface
|
||||
*/
|
||||
export interface ISmtpConnection {
|
||||
/** Socket connection */
|
||||
socket: net.Socket | tls.TLSSocket;
|
||||
/** Connection state */
|
||||
state: ConnectionState;
|
||||
/** Server capabilities */
|
||||
capabilities?: ISmtpCapabilities;
|
||||
/** Connection options */
|
||||
options: ISmtpClientOptions;
|
||||
/** Whether connection is secure */
|
||||
secure: boolean;
|
||||
/** Connection creation time */
|
||||
createdAt: Date;
|
||||
/** Last activity time */
|
||||
lastActivity: Date;
|
||||
/** Number of messages sent */
|
||||
messageCount: number;
|
||||
}
|
||||
/**
|
||||
* Error context for detailed error reporting
|
||||
*/
|
||||
export interface ISmtpErrorContext {
|
||||
/** Command that caused the error */
|
||||
command?: string;
|
||||
/** Server response */
|
||||
response?: ISmtpResponse;
|
||||
/** Connection state */
|
||||
connectionState?: ConnectionState;
|
||||
/** Additional context data */
|
||||
data?: Record<string, any>;
|
||||
}
|
||||
19
dist_ts/mail/delivery/smtpclient/interfaces.js
Normal file
19
dist_ts/mail/delivery/smtpclient/interfaces.js
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* SMTP Client Interfaces and Types
|
||||
* All interface definitions for the modular SMTP client
|
||||
*/
|
||||
/**
|
||||
* Connection state
|
||||
*/
|
||||
export var ConnectionState;
|
||||
(function (ConnectionState) {
|
||||
ConnectionState["DISCONNECTED"] = "disconnected";
|
||||
ConnectionState["CONNECTING"] = "connecting";
|
||||
ConnectionState["CONNECTED"] = "connected";
|
||||
ConnectionState["AUTHENTICATED"] = "authenticated";
|
||||
ConnectionState["READY"] = "ready";
|
||||
ConnectionState["BUSY"] = "busy";
|
||||
ConnectionState["CLOSING"] = "closing";
|
||||
ConnectionState["ERROR"] = "error";
|
||||
})(ConnectionState || (ConnectionState = {}));
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL21haWwvZGVsaXZlcnkvc210cGNsaWVudC9pbnRlcmZhY2VzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQTZKSDs7R0FFRztBQUNILE1BQU0sQ0FBTixJQUFZLGVBU1g7QUFURCxXQUFZLGVBQWU7SUFDekIsZ0RBQTZCLENBQUE7SUFDN0IsNENBQXlCLENBQUE7SUFDekIsMENBQXVCLENBQUE7SUFDdkIsa0RBQStCLENBQUE7SUFDL0Isa0NBQWUsQ0FBQTtJQUNmLGdDQUFhLENBQUE7SUFDYixzQ0FBbUIsQ0FBQTtJQUNuQixrQ0FBZSxDQUFBO0FBQ2pCLENBQUMsRUFUVyxlQUFlLEtBQWYsZUFBZSxRQVMxQiJ9
|
||||
58
dist_ts/mail/delivery/smtpclient/smtp-client.d.ts
vendored
Normal file
58
dist_ts/mail/delivery/smtpclient/smtp-client.d.ts
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* SMTP Client Core Implementation
|
||||
* Main client class with delegation to handlers
|
||||
*/
|
||||
import { EventEmitter } from 'node:events';
|
||||
import type { Email } from '../../core/classes.email.js';
|
||||
import type { ISmtpClientOptions, ISmtpSendResult, IConnectionPoolStatus } from './interfaces.js';
|
||||
import type { ConnectionManager } from './connection-manager.js';
|
||||
import type { CommandHandler } from './command-handler.js';
|
||||
import type { AuthHandler } from './auth-handler.js';
|
||||
import type { TlsHandler } from './tls-handler.js';
|
||||
import type { SmtpErrorHandler } from './error-handler.js';
|
||||
interface ISmtpClientDependencies {
|
||||
options: ISmtpClientOptions;
|
||||
connectionManager: ConnectionManager;
|
||||
commandHandler: CommandHandler;
|
||||
authHandler: AuthHandler;
|
||||
tlsHandler: TlsHandler;
|
||||
errorHandler: SmtpErrorHandler;
|
||||
}
|
||||
export declare class SmtpClient extends EventEmitter {
|
||||
private options;
|
||||
private connectionManager;
|
||||
private commandHandler;
|
||||
private authHandler;
|
||||
private tlsHandler;
|
||||
private errorHandler;
|
||||
private isShuttingDown;
|
||||
constructor(dependencies: ISmtpClientDependencies);
|
||||
/**
|
||||
* Send an email
|
||||
*/
|
||||
sendMail(email: Email): Promise<ISmtpSendResult>;
|
||||
/**
|
||||
* Test connection to SMTP server
|
||||
*/
|
||||
verify(): Promise<boolean>;
|
||||
/**
|
||||
* Check if client is connected
|
||||
*/
|
||||
isConnected(): boolean;
|
||||
/**
|
||||
* Get connection pool status
|
||||
*/
|
||||
getPoolStatus(): IConnectionPoolStatus;
|
||||
/**
|
||||
* Update client options
|
||||
*/
|
||||
updateOptions(newOptions: Partial<ISmtpClientOptions>): void;
|
||||
/**
|
||||
* Close all connections and shutdown client
|
||||
*/
|
||||
close(): Promise<void>;
|
||||
private formatEmailData;
|
||||
private extractMessageId;
|
||||
private setupEventForwarding;
|
||||
}
|
||||
export {};
|
||||
285
dist_ts/mail/delivery/smtpclient/smtp-client.js
Normal file
285
dist_ts/mail/delivery/smtpclient/smtp-client.js
Normal file
File diff suppressed because one or more lines are too long
33
dist_ts/mail/delivery/smtpclient/tls-handler.d.ts
vendored
Normal file
33
dist_ts/mail/delivery/smtpclient/tls-handler.d.ts
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* SMTP Client TLS Handler
|
||||
* TLS and STARTTLS client functionality
|
||||
*/
|
||||
import * as tls from 'node:tls';
|
||||
import type { ISmtpConnection, ISmtpClientOptions } from './interfaces.js';
|
||||
import type { CommandHandler } from './command-handler.js';
|
||||
export declare class TlsHandler {
|
||||
private options;
|
||||
private commandHandler;
|
||||
constructor(options: ISmtpClientOptions, commandHandler: CommandHandler);
|
||||
/**
|
||||
* Upgrade connection to TLS using STARTTLS
|
||||
*/
|
||||
upgradeToTLS(connection: ISmtpConnection): Promise<void>;
|
||||
/**
|
||||
* Create a direct TLS connection
|
||||
*/
|
||||
createTLSConnection(host: string, port: number): Promise<tls.TLSSocket>;
|
||||
/**
|
||||
* Validate TLS certificate
|
||||
*/
|
||||
validateCertificate(socket: tls.TLSSocket): boolean;
|
||||
/**
|
||||
* Get TLS connection information
|
||||
*/
|
||||
getTLSInfo(socket: tls.TLSSocket): any;
|
||||
/**
|
||||
* Check if TLS upgrade is required or recommended
|
||||
*/
|
||||
shouldUseTLS(connection: ISmtpConnection): boolean;
|
||||
private performTLSUpgrade;
|
||||
}
|
||||
204
dist_ts/mail/delivery/smtpclient/tls-handler.js
Normal file
204
dist_ts/mail/delivery/smtpclient/tls-handler.js
Normal file
File diff suppressed because one or more lines are too long
77
dist_ts/mail/delivery/smtpclient/utils/helpers.d.ts
vendored
Normal file
77
dist_ts/mail/delivery/smtpclient/utils/helpers.d.ts
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* SMTP Client Helper Functions
|
||||
* Protocol helper functions and utilities
|
||||
*/
|
||||
import type { ISmtpResponse, ISmtpCapabilities } from '../interfaces.js';
|
||||
/**
|
||||
* Parse SMTP server response
|
||||
*/
|
||||
export declare function parseSmtpResponse(data: string): ISmtpResponse;
|
||||
/**
|
||||
* Parse EHLO response and extract capabilities
|
||||
*/
|
||||
export declare function parseEhloResponse(response: string): ISmtpCapabilities;
|
||||
/**
|
||||
* Format SMTP command with proper line ending
|
||||
*/
|
||||
export declare function formatCommand(command: string, ...args: string[]): string;
|
||||
/**
|
||||
* Encode authentication string for AUTH PLAIN
|
||||
*/
|
||||
export declare function encodeAuthPlain(username: string, password: string): string;
|
||||
/**
|
||||
* Encode authentication string for AUTH LOGIN
|
||||
*/
|
||||
export declare function encodeAuthLogin(value: string): string;
|
||||
/**
|
||||
* Generate OAuth2 authentication string
|
||||
*/
|
||||
export declare function generateOAuth2String(username: string, accessToken: string): string;
|
||||
/**
|
||||
* Check if response code indicates success
|
||||
*/
|
||||
export declare function isSuccessCode(code: number): boolean;
|
||||
/**
|
||||
* Check if response code indicates temporary failure
|
||||
*/
|
||||
export declare function isTemporaryFailure(code: number): boolean;
|
||||
/**
|
||||
* Check if response code indicates permanent failure
|
||||
*/
|
||||
export declare function isPermanentFailure(code: number): boolean;
|
||||
/**
|
||||
* Escape email address for SMTP commands
|
||||
*/
|
||||
export declare function escapeEmailAddress(email: string): string;
|
||||
/**
|
||||
* Extract email address from angle brackets
|
||||
*/
|
||||
export declare function extractEmailAddress(email: string): string;
|
||||
/**
|
||||
* Generate unique connection ID
|
||||
*/
|
||||
export declare function generateConnectionId(): string;
|
||||
/**
|
||||
* Format timeout duration for human readability
|
||||
*/
|
||||
export declare function formatTimeout(milliseconds: number): string;
|
||||
/**
|
||||
* Validate and normalize email data size
|
||||
*/
|
||||
export declare function validateEmailSize(emailData: string, maxSize?: number): boolean;
|
||||
/**
|
||||
* Clean sensitive data from logs
|
||||
*/
|
||||
export declare function sanitizeForLogging(data: any): any;
|
||||
/**
|
||||
* Calculate exponential backoff delay
|
||||
*/
|
||||
export declare function calculateBackoffDelay(attempt: number, baseDelay?: number): number;
|
||||
/**
|
||||
* Parse enhanced status code
|
||||
*/
|
||||
export declare function parseEnhancedStatusCode(code: string): {
|
||||
class: number;
|
||||
subject: number;
|
||||
detail: number;
|
||||
} | null;
|
||||
196
dist_ts/mail/delivery/smtpclient/utils/helpers.js
Normal file
196
dist_ts/mail/delivery/smtpclient/utils/helpers.js
Normal file
File diff suppressed because one or more lines are too long
46
dist_ts/mail/delivery/smtpclient/utils/logging.d.ts
vendored
Normal file
46
dist_ts/mail/delivery/smtpclient/utils/logging.d.ts
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* SMTP Client Logging Utilities
|
||||
* Client-side logging utilities for SMTP operations
|
||||
*/
|
||||
import type { ISmtpResponse, ISmtpClientOptions } from '../interfaces.js';
|
||||
export interface ISmtpClientLogData {
|
||||
component: string;
|
||||
host?: string;
|
||||
port?: number;
|
||||
secure?: boolean;
|
||||
command?: string;
|
||||
response?: ISmtpResponse;
|
||||
error?: Error;
|
||||
connectionId?: string;
|
||||
messageId?: string;
|
||||
duration?: number;
|
||||
[key: string]: any;
|
||||
}
|
||||
/**
|
||||
* Log SMTP client connection events
|
||||
*/
|
||||
export declare function logConnection(event: 'connecting' | 'connected' | 'disconnected' | 'error', options: ISmtpClientOptions, data?: Partial<ISmtpClientLogData>): void;
|
||||
/**
|
||||
* Log SMTP command execution
|
||||
*/
|
||||
export declare function logCommand(command: string, response?: ISmtpResponse, options?: ISmtpClientOptions, data?: Partial<ISmtpClientLogData>): void;
|
||||
/**
|
||||
* Log authentication events
|
||||
*/
|
||||
export declare function logAuthentication(event: 'start' | 'success' | 'failure', method: string, options: ISmtpClientOptions, data?: Partial<ISmtpClientLogData>): void;
|
||||
/**
|
||||
* Log TLS/STARTTLS events
|
||||
*/
|
||||
export declare function logTLS(event: 'starttls_start' | 'starttls_success' | 'starttls_failure' | 'tls_connected', options: ISmtpClientOptions, data?: Partial<ISmtpClientLogData>): void;
|
||||
/**
|
||||
* Log email sending events
|
||||
*/
|
||||
export declare function logEmailSend(event: 'start' | 'success' | 'failure', recipients: string[], options: ISmtpClientOptions, data?: Partial<ISmtpClientLogData>): void;
|
||||
/**
|
||||
* Log performance metrics
|
||||
*/
|
||||
export declare function logPerformance(operation: string, duration: number, options: ISmtpClientOptions, data?: Partial<ISmtpClientLogData>): void;
|
||||
/**
|
||||
* Log debug information (only when debug is enabled)
|
||||
*/
|
||||
export declare function logDebug(message: string, options: ISmtpClientOptions, data?: Partial<ISmtpClientLogData>): void;
|
||||
153
dist_ts/mail/delivery/smtpclient/utils/logging.js
Normal file
153
dist_ts/mail/delivery/smtpclient/utils/logging.js
Normal file
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* SMTP Client Logging Utilities
|
||||
* Client-side logging utilities for SMTP operations
|
||||
*/
|
||||
import { logger } from '../../../../logger.js';
|
||||
/**
|
||||
* Log SMTP client connection events
|
||||
*/
|
||||
export function logConnection(event, options, data) {
|
||||
const logData = {
|
||||
component: 'smtp-client',
|
||||
event,
|
||||
host: options.host,
|
||||
port: options.port,
|
||||
secure: options.secure,
|
||||
...data
|
||||
};
|
||||
switch (event) {
|
||||
case 'connecting':
|
||||
logger.info('SMTP client connecting', logData);
|
||||
break;
|
||||
case 'connected':
|
||||
logger.info('SMTP client connected', logData);
|
||||
break;
|
||||
case 'disconnected':
|
||||
logger.info('SMTP client disconnected', logData);
|
||||
break;
|
||||
case 'error':
|
||||
logger.error('SMTP client connection error', logData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Log SMTP command execution
|
||||
*/
|
||||
export function logCommand(command, response, options, data) {
|
||||
const logData = {
|
||||
component: 'smtp-client',
|
||||
command,
|
||||
response,
|
||||
host: options?.host,
|
||||
port: options?.port,
|
||||
...data
|
||||
};
|
||||
if (response && response.code >= 400) {
|
||||
logger.warn('SMTP command failed', logData);
|
||||
}
|
||||
else {
|
||||
logger.debug('SMTP command executed', logData);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Log authentication events
|
||||
*/
|
||||
export function logAuthentication(event, method, options, data) {
|
||||
const logData = {
|
||||
component: 'smtp-client',
|
||||
event: `auth_${event}`,
|
||||
authMethod: method,
|
||||
host: options.host,
|
||||
port: options.port,
|
||||
...data
|
||||
};
|
||||
switch (event) {
|
||||
case 'start':
|
||||
logger.debug('SMTP authentication started', logData);
|
||||
break;
|
||||
case 'success':
|
||||
logger.info('SMTP authentication successful', logData);
|
||||
break;
|
||||
case 'failure':
|
||||
logger.error('SMTP authentication failed', logData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Log TLS/STARTTLS events
|
||||
*/
|
||||
export function logTLS(event, options, data) {
|
||||
const logData = {
|
||||
component: 'smtp-client',
|
||||
event,
|
||||
host: options.host,
|
||||
port: options.port,
|
||||
...data
|
||||
};
|
||||
if (event.includes('failure')) {
|
||||
logger.error('SMTP TLS operation failed', logData);
|
||||
}
|
||||
else {
|
||||
logger.info('SMTP TLS operation', logData);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Log email sending events
|
||||
*/
|
||||
export function logEmailSend(event, recipients, options, data) {
|
||||
const logData = {
|
||||
component: 'smtp-client',
|
||||
event: `send_${event}`,
|
||||
recipientCount: recipients.length,
|
||||
recipients: recipients.slice(0, 5), // Only log first 5 recipients for privacy
|
||||
host: options.host,
|
||||
port: options.port,
|
||||
...data
|
||||
};
|
||||
switch (event) {
|
||||
case 'start':
|
||||
logger.info('SMTP email send started', logData);
|
||||
break;
|
||||
case 'success':
|
||||
logger.info('SMTP email send successful', logData);
|
||||
break;
|
||||
case 'failure':
|
||||
logger.error('SMTP email send failed', logData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Log performance metrics
|
||||
*/
|
||||
export function logPerformance(operation, duration, options, data) {
|
||||
const logData = {
|
||||
component: 'smtp-client',
|
||||
operation,
|
||||
duration,
|
||||
host: options.host,
|
||||
port: options.port,
|
||||
...data
|
||||
};
|
||||
if (duration > 10000) { // Log slow operations (>10s)
|
||||
logger.warn('SMTP slow operation detected', logData);
|
||||
}
|
||||
else {
|
||||
logger.debug('SMTP operation performance', logData);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Log debug information (only when debug is enabled)
|
||||
*/
|
||||
export function logDebug(message, options, data) {
|
||||
if (!options.debug) {
|
||||
return;
|
||||
}
|
||||
const logData = {
|
||||
component: 'smtp-client-debug',
|
||||
host: options.host,
|
||||
port: options.port,
|
||||
...data
|
||||
};
|
||||
logger.debug(`[SMTP Client Debug] ${message}`, logData);
|
||||
}
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9nZ2luZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3RzL21haWwvZGVsaXZlcnkvc210cGNsaWVudC91dGlscy9sb2dnaW5nLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUVILE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQWlCL0M7O0dBRUc7QUFDSCxNQUFNLFVBQVUsYUFBYSxDQUMzQixLQUE0RCxFQUM1RCxPQUEyQixFQUMzQixJQUFrQztJQUVsQyxNQUFNLE9BQU8sR0FBdUI7UUFDbEMsU0FBUyxFQUFFLGFBQWE7UUFDeEIsS0FBSztRQUNMLElBQUksRUFBRSxPQUFPLENBQUMsSUFBSTtRQUNsQixJQUFJLEVBQUUsT0FBTyxDQUFDLElBQUk7UUFDbEIsTUFBTSxFQUFFLE9BQU8sQ0FBQyxNQUFNO1FBQ3RCLEdBQUcsSUFBSTtLQUNSLENBQUM7SUFFRixRQUFRLEtBQUssRUFBRSxDQUFDO1FBQ2QsS0FBSyxZQUFZO1lBQ2YsTUFBTSxDQUFDLElBQUksQ0FBQyx3QkFBd0IsRUFBRSxPQUFPLENBQUMsQ0FBQztZQUMvQyxNQUFNO1FBQ1IsS0FBSyxXQUFXO1lBQ2QsTUFBTSxDQUFDLElBQUksQ0FBQyx1QkFBdUIsRUFBRSxPQUFPLENBQUMsQ0FBQztZQUM5QyxNQUFNO1FBQ1IsS0FBSyxjQUFjO1lBQ2pCLE1BQU0sQ0FBQyxJQUFJLENBQUMsMEJBQTBCLEVBQUUsT0FBTyxDQUFDLENBQUM7WUFDakQsTUFBTTtRQUNSLEtBQUssT0FBTztZQUNWLE1BQU0sQ0FBQyxLQUFLLENBQUMsOEJBQThCLEVBQUUsT0FBTyxDQUFDLENBQUM7WUFDdEQsTUFBTTtJQUNWLENBQUM7QUFDSCxDQUFDO0FBRUQ7O0dBRUc7QUFDSCxNQUFNLFVBQVUsVUFBVSxDQUN4QixPQUFlLEVBQ2YsUUFBd0IsRUFDeEIsT0FBNEIsRUFDNUIsSUFBa0M7SUFFbEMsTUFBTSxPQUFPLEdBQXVCO1FB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
|
||||
38
dist_ts/mail/delivery/smtpclient/utils/validation.d.ts
vendored
Normal file
38
dist_ts/mail/delivery/smtpclient/utils/validation.d.ts
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* SMTP Client Validation Utilities
|
||||
* Input validation functions for SMTP client operations
|
||||
*/
|
||||
import type { ISmtpClientOptions, ISmtpAuthOptions } from '../interfaces.js';
|
||||
/**
|
||||
* Validate email address format
|
||||
* Supports RFC-compliant addresses including empty return paths for bounces
|
||||
*/
|
||||
export declare function validateEmailAddress(email: string): boolean;
|
||||
/**
|
||||
* Validate SMTP client options
|
||||
*/
|
||||
export declare function validateClientOptions(options: ISmtpClientOptions): string[];
|
||||
/**
|
||||
* Validate authentication options
|
||||
*/
|
||||
export declare function validateAuthOptions(auth: ISmtpAuthOptions): string[];
|
||||
/**
|
||||
* Validate hostname format
|
||||
*/
|
||||
export declare function validateHostname(hostname: string): boolean;
|
||||
/**
|
||||
* Validate port number
|
||||
*/
|
||||
export declare function validatePort(port: number): boolean;
|
||||
/**
|
||||
* Sanitize and validate domain name for EHLO
|
||||
*/
|
||||
export declare function validateAndSanitizeDomain(domain: string): string;
|
||||
/**
|
||||
* Validate recipient list
|
||||
*/
|
||||
export declare function validateRecipients(recipients: string | string[]): string[];
|
||||
/**
|
||||
* Validate sender address
|
||||
*/
|
||||
export declare function validateSender(sender: string): boolean;
|
||||
139
dist_ts/mail/delivery/smtpclient/utils/validation.js
Normal file
139
dist_ts/mail/delivery/smtpclient/utils/validation.js
Normal file
File diff suppressed because one or more lines are too long
45
dist_ts/mail/delivery/smtpserver/certificate-utils.d.ts
vendored
Normal file
45
dist_ts/mail/delivery/smtpserver/certificate-utils.d.ts
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Certificate Utilities for SMTP Server
|
||||
* Provides utilities for managing TLS certificates
|
||||
*/
|
||||
import * as tls from 'tls';
|
||||
/**
|
||||
* Certificate data
|
||||
*/
|
||||
export interface ICertificateData {
|
||||
key: Buffer;
|
||||
cert: Buffer;
|
||||
ca?: Buffer;
|
||||
}
|
||||
/**
|
||||
* Load certificates from PEM format strings
|
||||
* @param options - Certificate options
|
||||
* @returns Certificate data with Buffer format
|
||||
*/
|
||||
export declare function loadCertificatesFromString(options: {
|
||||
key: string | Buffer;
|
||||
cert: string | Buffer;
|
||||
ca?: string | Buffer;
|
||||
}): ICertificateData;
|
||||
/**
|
||||
* Load certificates from files
|
||||
* @param options - Certificate file paths
|
||||
* @returns Certificate data with Buffer format
|
||||
*/
|
||||
export declare function loadCertificatesFromFiles(options: {
|
||||
keyPath: string;
|
||||
certPath: string;
|
||||
caPath?: string;
|
||||
}): ICertificateData;
|
||||
/**
|
||||
* Generate self-signed certificates for testing
|
||||
* @returns Certificate data with Buffer format
|
||||
*/
|
||||
export declare function generateSelfSignedCertificates(): ICertificateData;
|
||||
/**
|
||||
* Create TLS options for secure server or STARTTLS
|
||||
* @param certificates - Certificate data
|
||||
* @param isServer - Whether this is for server (true) or client (false)
|
||||
* @returns TLS options
|
||||
*/
|
||||
export declare function createTlsOptions(certificates: ICertificateData, isServer?: boolean): tls.TlsOptions;
|
||||
345
dist_ts/mail/delivery/smtpserver/certificate-utils.js
Normal file
345
dist_ts/mail/delivery/smtpserver/certificate-utils.js
Normal file
File diff suppressed because one or more lines are too long
156
dist_ts/mail/delivery/smtpserver/command-handler.d.ts
vendored
Normal file
156
dist_ts/mail/delivery/smtpserver/command-handler.d.ts
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* SMTP Command Handler
|
||||
* Responsible for parsing and handling SMTP commands
|
||||
*/
|
||||
import * as plugins from '../../../plugins.js';
|
||||
import type { ISmtpSession } from './interfaces.js';
|
||||
import type { ICommandHandler, ISmtpServer } from './interfaces.js';
|
||||
import { SmtpCommand } from './constants.js';
|
||||
/**
|
||||
* Handles SMTP commands and responses
|
||||
*/
|
||||
export declare class CommandHandler implements ICommandHandler {
|
||||
/**
|
||||
* Reference to the SMTP server instance
|
||||
*/
|
||||
private smtpServer;
|
||||
/**
|
||||
* Creates a new command handler
|
||||
* @param smtpServer - SMTP server instance
|
||||
*/
|
||||
constructor(smtpServer: ISmtpServer);
|
||||
/**
|
||||
* Process a command from the client
|
||||
* @param socket - Client socket
|
||||
* @param commandLine - Command line from client
|
||||
*/
|
||||
processCommand(socket: plugins.net.Socket | plugins.tls.TLSSocket, commandLine: string): Promise<void>;
|
||||
/**
|
||||
* Send a response to the client
|
||||
* @param socket - Client socket
|
||||
* @param response - Response to send
|
||||
*/
|
||||
sendResponse(socket: plugins.net.Socket | plugins.tls.TLSSocket, response: string): void;
|
||||
/**
|
||||
* Check if a socket error is potentially recoverable
|
||||
* @param error - The error that occurred
|
||||
* @returns Whether the error is potentially recoverable
|
||||
*/
|
||||
private isRecoverableSocketError;
|
||||
/**
|
||||
* Handle recoverable socket errors with retry logic
|
||||
* @param socket - Client socket
|
||||
* @param error - The error that occurred
|
||||
* @param response - The response that failed to send
|
||||
*/
|
||||
private handleSocketError;
|
||||
/**
|
||||
* Handle EHLO command
|
||||
* @param socket - Client socket
|
||||
* @param clientHostname - Client hostname from EHLO command
|
||||
*/
|
||||
handleEhlo(socket: plugins.net.Socket | plugins.tls.TLSSocket, clientHostname: string): void;
|
||||
/**
|
||||
* Handle MAIL FROM command
|
||||
* @param socket - Client socket
|
||||
* @param args - Command arguments
|
||||
*/
|
||||
handleMailFrom(socket: plugins.net.Socket | plugins.tls.TLSSocket, args: string): void;
|
||||
/**
|
||||
* Handle RCPT TO command
|
||||
* @param socket - Client socket
|
||||
* @param args - Command arguments
|
||||
*/
|
||||
handleRcptTo(socket: plugins.net.Socket | plugins.tls.TLSSocket, args: string): void;
|
||||
/**
|
||||
* Handle DATA command
|
||||
* @param socket - Client socket
|
||||
*/
|
||||
handleData(socket: plugins.net.Socket | plugins.tls.TLSSocket): void;
|
||||
/**
|
||||
* Handle RSET command
|
||||
* @param socket - Client socket
|
||||
*/
|
||||
handleRset(socket: plugins.net.Socket | plugins.tls.TLSSocket): void;
|
||||
/**
|
||||
* Handle NOOP command
|
||||
* @param socket - Client socket
|
||||
*/
|
||||
handleNoop(socket: plugins.net.Socket | plugins.tls.TLSSocket): void;
|
||||
/**
|
||||
* Handle QUIT command
|
||||
* @param socket - Client socket
|
||||
*/
|
||||
handleQuit(socket: plugins.net.Socket | plugins.tls.TLSSocket, args?: string): void;
|
||||
/**
|
||||
* Handle AUTH command
|
||||
* @param socket - Client socket
|
||||
* @param args - Command arguments
|
||||
*/
|
||||
private handleAuth;
|
||||
/**
|
||||
* Handle AUTH PLAIN authentication
|
||||
* @param socket - Client socket
|
||||
* @param session - Session
|
||||
* @param initialResponse - Optional initial response
|
||||
*/
|
||||
private handleAuthPlain;
|
||||
/**
|
||||
* Handle AUTH LOGIN authentication
|
||||
* @param socket - Client socket
|
||||
* @param session - Session
|
||||
* @param initialResponse - Optional initial response
|
||||
*/
|
||||
private handleAuthLogin;
|
||||
/**
|
||||
* Handle AUTH LOGIN response
|
||||
* @param socket - Client socket
|
||||
* @param session - Session
|
||||
* @param response - Response from client
|
||||
*/
|
||||
private handleAuthLoginResponse;
|
||||
/**
|
||||
* Handle HELP command
|
||||
* @param socket - Client socket
|
||||
* @param args - Command arguments
|
||||
*/
|
||||
private handleHelp;
|
||||
/**
|
||||
* Handle VRFY command (Verify user/mailbox)
|
||||
* RFC 5321 Section 3.5.1: Server MAY respond with 252 to avoid disclosing sensitive information
|
||||
* @param socket - Client socket
|
||||
* @param args - Command arguments (username to verify)
|
||||
*/
|
||||
private handleVrfy;
|
||||
/**
|
||||
* Handle EXPN command (Expand mailing list)
|
||||
* RFC 5321 Section 3.5.2: Server MAY disable this for security
|
||||
* @param socket - Client socket
|
||||
* @param args - Command arguments (mailing list to expand)
|
||||
*/
|
||||
private handleExpn;
|
||||
/**
|
||||
* Reset session to after-EHLO state
|
||||
* @param session - SMTP session to reset
|
||||
*/
|
||||
private resetSession;
|
||||
/**
|
||||
* Validate command sequence based on current state
|
||||
* @param command - Command to validate
|
||||
* @param session - Current session
|
||||
* @returns Whether the command is valid in the current state
|
||||
*/
|
||||
private validateCommandSequence;
|
||||
/**
|
||||
* Handle an SMTP command (interface requirement)
|
||||
*/
|
||||
handleCommand(socket: plugins.net.Socket | plugins.tls.TLSSocket, command: SmtpCommand, args: string, session: ISmtpSession): Promise<void>;
|
||||
/**
|
||||
* Get supported commands for current session state (interface requirement)
|
||||
*/
|
||||
getSupportedCommands(session: ISmtpSession): SmtpCommand[];
|
||||
/**
|
||||
* Clean up resources
|
||||
*/
|
||||
destroy(): void;
|
||||
}
|
||||
1163
dist_ts/mail/delivery/smtpserver/command-handler.js
Normal file
1163
dist_ts/mail/delivery/smtpserver/command-handler.js
Normal file
File diff suppressed because one or more lines are too long
159
dist_ts/mail/delivery/smtpserver/connection-manager.d.ts
vendored
Normal file
159
dist_ts/mail/delivery/smtpserver/connection-manager.d.ts
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* SMTP Connection Manager
|
||||
* Responsible for managing socket connections to the SMTP server
|
||||
*/
|
||||
import * as plugins from '../../../plugins.js';
|
||||
import type { IConnectionManager, ISmtpServer } from './interfaces.js';
|
||||
/**
|
||||
* Manager for SMTP connections
|
||||
* Handles connection setup, event listeners, and lifecycle management
|
||||
* Provides resource management, connection tracking, and monitoring
|
||||
*/
|
||||
export declare class ConnectionManager implements IConnectionManager {
|
||||
/**
|
||||
* Reference to the SMTP server instance
|
||||
*/
|
||||
private smtpServer;
|
||||
/**
|
||||
* Set of active socket connections
|
||||
*/
|
||||
private activeConnections;
|
||||
/**
|
||||
* Connection tracking for resource management
|
||||
*/
|
||||
private connectionStats;
|
||||
/**
|
||||
* Per-IP connection tracking for rate limiting
|
||||
*/
|
||||
private ipConnections;
|
||||
/**
|
||||
* Resource monitoring interval
|
||||
*/
|
||||
private resourceCheckInterval;
|
||||
/**
|
||||
* Track cleanup timers so we can clear them
|
||||
*/
|
||||
private cleanupTimers;
|
||||
/**
|
||||
* SMTP server options with enhanced resource controls
|
||||
*/
|
||||
private options;
|
||||
/**
|
||||
* Creates a new connection manager with enhanced resource management
|
||||
* @param smtpServer - SMTP server instance
|
||||
*/
|
||||
constructor(smtpServer: ISmtpServer);
|
||||
/**
|
||||
* Start resource monitoring interval to check resource usage
|
||||
*/
|
||||
private startResourceMonitoring;
|
||||
/**
|
||||
* Monitor resource usage and log statistics
|
||||
*/
|
||||
private monitorResourceUsage;
|
||||
/**
|
||||
* Clean up expired IP rate limits and perform additional resource monitoring
|
||||
*/
|
||||
private cleanupIpRateLimits;
|
||||
/**
|
||||
* Validate and repair resource tracking to prevent leaks
|
||||
*/
|
||||
private validateResourceTracking;
|
||||
/**
|
||||
* Handle a new connection with resource management
|
||||
* @param socket - Client socket
|
||||
*/
|
||||
handleNewConnection(socket: plugins.net.Socket): Promise<void>;
|
||||
/**
|
||||
* Check if an IP has exceeded the rate limit
|
||||
* @param ip - Client IP address
|
||||
* @returns True if rate limited
|
||||
*/
|
||||
private isIPRateLimited;
|
||||
/**
|
||||
* Track a new connection from an IP
|
||||
* @param ip - Client IP address
|
||||
*/
|
||||
private trackIPConnection;
|
||||
/**
|
||||
* Check if an IP has reached its connection limit
|
||||
* @param ip - Client IP address
|
||||
* @returns True if limit reached
|
||||
*/
|
||||
private hasReachedIPConnectionLimit;
|
||||
/**
|
||||
* Handle a new secure TLS connection with resource management
|
||||
* @param socket - Client TLS socket
|
||||
*/
|
||||
handleNewSecureConnection(socket: plugins.tls.TLSSocket): Promise<void>;
|
||||
/**
|
||||
* Set up event handlers for a socket with enhanced resource management
|
||||
* @param socket - Client socket
|
||||
*/
|
||||
setupSocketEventHandlers(socket: plugins.net.Socket | plugins.tls.TLSSocket): void;
|
||||
/**
|
||||
* Get the current connection count
|
||||
* @returns Number of active connections
|
||||
*/
|
||||
getConnectionCount(): number;
|
||||
/**
|
||||
* Check if the server has reached the maximum number of connections
|
||||
* @returns True if max connections reached
|
||||
*/
|
||||
hasReachedMaxConnections(): boolean;
|
||||
/**
|
||||
* Close all active connections
|
||||
*/
|
||||
closeAllConnections(): void;
|
||||
/**
|
||||
* Handle socket close event
|
||||
* @param socket - Client socket
|
||||
* @param hadError - Whether the socket was closed due to error
|
||||
*/
|
||||
private handleSocketClose;
|
||||
/**
|
||||
* Handle socket error event
|
||||
* @param socket - Client socket
|
||||
* @param error - Error object
|
||||
*/
|
||||
private handleSocketError;
|
||||
/**
|
||||
* Handle socket timeout event
|
||||
* @param socket - Client socket
|
||||
*/
|
||||
private handleSocketTimeout;
|
||||
/**
|
||||
* Reject a connection
|
||||
* @param socket - Client socket
|
||||
* @param reason - Reason for rejection
|
||||
*/
|
||||
private rejectConnection;
|
||||
/**
|
||||
* Send greeting message
|
||||
* @param socket - Client socket
|
||||
*/
|
||||
private sendGreeting;
|
||||
/**
|
||||
* Send service closing notification
|
||||
* @param socket - Client socket
|
||||
*/
|
||||
private sendServiceClosing;
|
||||
/**
|
||||
* Send response to client
|
||||
* @param socket - Client socket
|
||||
* @param response - Response to send
|
||||
*/
|
||||
private sendResponse;
|
||||
/**
|
||||
* Handle a new connection (interface requirement)
|
||||
*/
|
||||
handleConnection(socket: plugins.net.Socket | plugins.tls.TLSSocket, secure: boolean): Promise<void>;
|
||||
/**
|
||||
* Check if accepting new connections (interface requirement)
|
||||
*/
|
||||
canAcceptConnection(): boolean;
|
||||
/**
|
||||
* Clean up resources
|
||||
*/
|
||||
destroy(): void;
|
||||
}
|
||||
918
dist_ts/mail/delivery/smtpserver/connection-manager.js
Normal file
918
dist_ts/mail/delivery/smtpserver/connection-manager.js
Normal file
File diff suppressed because one or more lines are too long
130
dist_ts/mail/delivery/smtpserver/constants.d.ts
vendored
Normal file
130
dist_ts/mail/delivery/smtpserver/constants.d.ts
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* SMTP Server Constants
|
||||
* This file contains all constants and enums used by the SMTP server
|
||||
*/
|
||||
import { SmtpState } from '../interfaces.js';
|
||||
export { SmtpState };
|
||||
/**
|
||||
* SMTP Response Codes
|
||||
* Based on RFC 5321 and common SMTP practice
|
||||
*/
|
||||
export declare enum SmtpResponseCode {
|
||||
SUCCESS = 250,// Requested mail action okay, completed
|
||||
SYSTEM_STATUS = 211,// System status, or system help reply
|
||||
HELP_MESSAGE = 214,// Help message
|
||||
SERVICE_READY = 220,// <domain> Service ready
|
||||
SERVICE_CLOSING = 221,// <domain> Service closing transmission channel
|
||||
AUTHENTICATION_SUCCESSFUL = 235,// Authentication successful
|
||||
OK = 250,// Requested mail action okay, completed
|
||||
FORWARD = 251,// User not local; will forward to <forward-path>
|
||||
CANNOT_VRFY = 252,// Cannot VRFY user, but will accept message and attempt delivery
|
||||
MORE_INFO_NEEDED = 334,// Server challenge for authentication
|
||||
START_MAIL_INPUT = 354,// Start mail input; end with <CRLF>.<CRLF>
|
||||
SERVICE_NOT_AVAILABLE = 421,// <domain> Service not available, closing transmission channel
|
||||
MAILBOX_TEMPORARILY_UNAVAILABLE = 450,// Requested mail action not taken: mailbox unavailable
|
||||
LOCAL_ERROR = 451,// Requested action aborted: local error in processing
|
||||
INSUFFICIENT_STORAGE = 452,// Requested action not taken: insufficient system storage
|
||||
TLS_UNAVAILABLE_TEMP = 454,// TLS not available due to temporary reason
|
||||
SYNTAX_ERROR = 500,// Syntax error, command unrecognized
|
||||
SYNTAX_ERROR_PARAMETERS = 501,// Syntax error in parameters or arguments
|
||||
COMMAND_NOT_IMPLEMENTED = 502,// Command not implemented
|
||||
BAD_SEQUENCE = 503,// Bad sequence of commands
|
||||
COMMAND_PARAMETER_NOT_IMPLEMENTED = 504,// Command parameter not implemented
|
||||
AUTH_REQUIRED = 530,// Authentication required
|
||||
AUTH_FAILED = 535,// Authentication credentials invalid
|
||||
MAILBOX_UNAVAILABLE = 550,// Requested action not taken: mailbox unavailable
|
||||
USER_NOT_LOCAL = 551,// User not local; please try <forward-path>
|
||||
EXCEEDED_STORAGE = 552,// Requested mail action aborted: exceeded storage allocation
|
||||
MAILBOX_NAME_INVALID = 553,// Requested action not taken: mailbox name not allowed
|
||||
TRANSACTION_FAILED = 554,// Transaction failed
|
||||
MAIL_RCPT_PARAMETERS_INVALID = 555
|
||||
}
|
||||
/**
|
||||
* SMTP Command Types
|
||||
*/
|
||||
export declare enum SmtpCommand {
|
||||
HELO = "HELO",
|
||||
EHLO = "EHLO",
|
||||
MAIL_FROM = "MAIL",
|
||||
RCPT_TO = "RCPT",
|
||||
DATA = "DATA",
|
||||
RSET = "RSET",
|
||||
NOOP = "NOOP",
|
||||
QUIT = "QUIT",
|
||||
STARTTLS = "STARTTLS",
|
||||
AUTH = "AUTH",
|
||||
HELP = "HELP",
|
||||
VRFY = "VRFY",
|
||||
EXPN = "EXPN"
|
||||
}
|
||||
/**
|
||||
* Security log event types
|
||||
*/
|
||||
export declare enum SecurityEventType {
|
||||
CONNECTION = "connection",
|
||||
AUTHENTICATION = "authentication",
|
||||
COMMAND = "command",
|
||||
DATA = "data",
|
||||
IP_REPUTATION = "ip_reputation",
|
||||
TLS_NEGOTIATION = "tls_negotiation",
|
||||
DKIM = "dkim",
|
||||
SPF = "spf",
|
||||
DMARC = "dmarc",
|
||||
EMAIL_VALIDATION = "email_validation",
|
||||
SPAM = "spam",
|
||||
ACCESS_CONTROL = "access_control"
|
||||
}
|
||||
/**
|
||||
* Security log levels
|
||||
*/
|
||||
export declare enum SecurityLogLevel {
|
||||
DEBUG = "debug",
|
||||
INFO = "info",
|
||||
WARN = "warn",
|
||||
ERROR = "error"
|
||||
}
|
||||
/**
|
||||
* SMTP Server Defaults
|
||||
*/
|
||||
export declare const SMTP_DEFAULTS: {
|
||||
CONNECTION_TIMEOUT: number;
|
||||
SOCKET_TIMEOUT: number;
|
||||
DATA_TIMEOUT: number;
|
||||
CLEANUP_INTERVAL: number;
|
||||
MAX_CONNECTIONS: number;
|
||||
MAX_RECIPIENTS: number;
|
||||
MAX_MESSAGE_SIZE: number;
|
||||
SMTP_PORT: number;
|
||||
SUBMISSION_PORT: number;
|
||||
SECURE_PORT: number;
|
||||
HOSTNAME: string;
|
||||
CRLF: string;
|
||||
};
|
||||
/**
|
||||
* SMTP Command Patterns
|
||||
* Regular expressions for parsing SMTP commands
|
||||
*/
|
||||
export declare const SMTP_PATTERNS: {
|
||||
EHLO: RegExp;
|
||||
MAIL_FROM: RegExp;
|
||||
RCPT_TO: RegExp;
|
||||
PARAM: RegExp;
|
||||
EMAIL: RegExp;
|
||||
END_DATA: RegExp;
|
||||
};
|
||||
/**
|
||||
* SMTP Extension List
|
||||
* These extensions are advertised in the EHLO response
|
||||
*/
|
||||
export declare const SMTP_EXTENSIONS: {
|
||||
PIPELINING: string;
|
||||
SIZE: string;
|
||||
EIGHTBITMIME: string;
|
||||
STARTTLS: string;
|
||||
AUTH: string;
|
||||
ENHANCEDSTATUSCODES: string;
|
||||
HELP: string;
|
||||
CHUNKING: string;
|
||||
DSN: string;
|
||||
formatExtension(name: string, parameter?: string | number): string;
|
||||
};
|
||||
162
dist_ts/mail/delivery/smtpserver/constants.js
Normal file
162
dist_ts/mail/delivery/smtpserver/constants.js
Normal file
@@ -0,0 +1,162 @@
|
||||
/**
|
||||
* SMTP Server Constants
|
||||
* This file contains all constants and enums used by the SMTP server
|
||||
*/
|
||||
import { SmtpState } from '../interfaces.js';
|
||||
// Re-export SmtpState enum from the main interfaces file
|
||||
export { SmtpState };
|
||||
/**
|
||||
* SMTP Response Codes
|
||||
* Based on RFC 5321 and common SMTP practice
|
||||
*/
|
||||
export var SmtpResponseCode;
|
||||
(function (SmtpResponseCode) {
|
||||
// Success codes (2xx)
|
||||
SmtpResponseCode[SmtpResponseCode["SUCCESS"] = 250] = "SUCCESS";
|
||||
SmtpResponseCode[SmtpResponseCode["SYSTEM_STATUS"] = 211] = "SYSTEM_STATUS";
|
||||
SmtpResponseCode[SmtpResponseCode["HELP_MESSAGE"] = 214] = "HELP_MESSAGE";
|
||||
SmtpResponseCode[SmtpResponseCode["SERVICE_READY"] = 220] = "SERVICE_READY";
|
||||
SmtpResponseCode[SmtpResponseCode["SERVICE_CLOSING"] = 221] = "SERVICE_CLOSING";
|
||||
SmtpResponseCode[SmtpResponseCode["AUTHENTICATION_SUCCESSFUL"] = 235] = "AUTHENTICATION_SUCCESSFUL";
|
||||
SmtpResponseCode[SmtpResponseCode["OK"] = 250] = "OK";
|
||||
SmtpResponseCode[SmtpResponseCode["FORWARD"] = 251] = "FORWARD";
|
||||
SmtpResponseCode[SmtpResponseCode["CANNOT_VRFY"] = 252] = "CANNOT_VRFY";
|
||||
// Intermediate codes (3xx)
|
||||
SmtpResponseCode[SmtpResponseCode["MORE_INFO_NEEDED"] = 334] = "MORE_INFO_NEEDED";
|
||||
SmtpResponseCode[SmtpResponseCode["START_MAIL_INPUT"] = 354] = "START_MAIL_INPUT";
|
||||
// Temporary error codes (4xx)
|
||||
SmtpResponseCode[SmtpResponseCode["SERVICE_NOT_AVAILABLE"] = 421] = "SERVICE_NOT_AVAILABLE";
|
||||
SmtpResponseCode[SmtpResponseCode["MAILBOX_TEMPORARILY_UNAVAILABLE"] = 450] = "MAILBOX_TEMPORARILY_UNAVAILABLE";
|
||||
SmtpResponseCode[SmtpResponseCode["LOCAL_ERROR"] = 451] = "LOCAL_ERROR";
|
||||
SmtpResponseCode[SmtpResponseCode["INSUFFICIENT_STORAGE"] = 452] = "INSUFFICIENT_STORAGE";
|
||||
SmtpResponseCode[SmtpResponseCode["TLS_UNAVAILABLE_TEMP"] = 454] = "TLS_UNAVAILABLE_TEMP";
|
||||
// Permanent error codes (5xx)
|
||||
SmtpResponseCode[SmtpResponseCode["SYNTAX_ERROR"] = 500] = "SYNTAX_ERROR";
|
||||
SmtpResponseCode[SmtpResponseCode["SYNTAX_ERROR_PARAMETERS"] = 501] = "SYNTAX_ERROR_PARAMETERS";
|
||||
SmtpResponseCode[SmtpResponseCode["COMMAND_NOT_IMPLEMENTED"] = 502] = "COMMAND_NOT_IMPLEMENTED";
|
||||
SmtpResponseCode[SmtpResponseCode["BAD_SEQUENCE"] = 503] = "BAD_SEQUENCE";
|
||||
SmtpResponseCode[SmtpResponseCode["COMMAND_PARAMETER_NOT_IMPLEMENTED"] = 504] = "COMMAND_PARAMETER_NOT_IMPLEMENTED";
|
||||
SmtpResponseCode[SmtpResponseCode["AUTH_REQUIRED"] = 530] = "AUTH_REQUIRED";
|
||||
SmtpResponseCode[SmtpResponseCode["AUTH_FAILED"] = 535] = "AUTH_FAILED";
|
||||
SmtpResponseCode[SmtpResponseCode["MAILBOX_UNAVAILABLE"] = 550] = "MAILBOX_UNAVAILABLE";
|
||||
SmtpResponseCode[SmtpResponseCode["USER_NOT_LOCAL"] = 551] = "USER_NOT_LOCAL";
|
||||
SmtpResponseCode[SmtpResponseCode["EXCEEDED_STORAGE"] = 552] = "EXCEEDED_STORAGE";
|
||||
SmtpResponseCode[SmtpResponseCode["MAILBOX_NAME_INVALID"] = 553] = "MAILBOX_NAME_INVALID";
|
||||
SmtpResponseCode[SmtpResponseCode["TRANSACTION_FAILED"] = 554] = "TRANSACTION_FAILED";
|
||||
SmtpResponseCode[SmtpResponseCode["MAIL_RCPT_PARAMETERS_INVALID"] = 555] = "MAIL_RCPT_PARAMETERS_INVALID";
|
||||
})(SmtpResponseCode || (SmtpResponseCode = {}));
|
||||
/**
|
||||
* SMTP Command Types
|
||||
*/
|
||||
export var SmtpCommand;
|
||||
(function (SmtpCommand) {
|
||||
SmtpCommand["HELO"] = "HELO";
|
||||
SmtpCommand["EHLO"] = "EHLO";
|
||||
SmtpCommand["MAIL_FROM"] = "MAIL";
|
||||
SmtpCommand["RCPT_TO"] = "RCPT";
|
||||
SmtpCommand["DATA"] = "DATA";
|
||||
SmtpCommand["RSET"] = "RSET";
|
||||
SmtpCommand["NOOP"] = "NOOP";
|
||||
SmtpCommand["QUIT"] = "QUIT";
|
||||
SmtpCommand["STARTTLS"] = "STARTTLS";
|
||||
SmtpCommand["AUTH"] = "AUTH";
|
||||
SmtpCommand["HELP"] = "HELP";
|
||||
SmtpCommand["VRFY"] = "VRFY";
|
||||
SmtpCommand["EXPN"] = "EXPN";
|
||||
})(SmtpCommand || (SmtpCommand = {}));
|
||||
/**
|
||||
* Security log event types
|
||||
*/
|
||||
export var SecurityEventType;
|
||||
(function (SecurityEventType) {
|
||||
SecurityEventType["CONNECTION"] = "connection";
|
||||
SecurityEventType["AUTHENTICATION"] = "authentication";
|
||||
SecurityEventType["COMMAND"] = "command";
|
||||
SecurityEventType["DATA"] = "data";
|
||||
SecurityEventType["IP_REPUTATION"] = "ip_reputation";
|
||||
SecurityEventType["TLS_NEGOTIATION"] = "tls_negotiation";
|
||||
SecurityEventType["DKIM"] = "dkim";
|
||||
SecurityEventType["SPF"] = "spf";
|
||||
SecurityEventType["DMARC"] = "dmarc";
|
||||
SecurityEventType["EMAIL_VALIDATION"] = "email_validation";
|
||||
SecurityEventType["SPAM"] = "spam";
|
||||
SecurityEventType["ACCESS_CONTROL"] = "access_control";
|
||||
})(SecurityEventType || (SecurityEventType = {}));
|
||||
/**
|
||||
* Security log levels
|
||||
*/
|
||||
export var SecurityLogLevel;
|
||||
(function (SecurityLogLevel) {
|
||||
SecurityLogLevel["DEBUG"] = "debug";
|
||||
SecurityLogLevel["INFO"] = "info";
|
||||
SecurityLogLevel["WARN"] = "warn";
|
||||
SecurityLogLevel["ERROR"] = "error";
|
||||
})(SecurityLogLevel || (SecurityLogLevel = {}));
|
||||
/**
|
||||
* SMTP Server Defaults
|
||||
*/
|
||||
export const SMTP_DEFAULTS = {
|
||||
// Default timeouts in milliseconds
|
||||
CONNECTION_TIMEOUT: 30000, // 30 seconds
|
||||
SOCKET_TIMEOUT: 300000, // 5 minutes
|
||||
DATA_TIMEOUT: 60000, // 1 minute
|
||||
CLEANUP_INTERVAL: 5000, // 5 seconds
|
||||
// Default limits
|
||||
MAX_CONNECTIONS: 100,
|
||||
MAX_RECIPIENTS: 100,
|
||||
MAX_MESSAGE_SIZE: 10485760, // 10MB
|
||||
// Default ports
|
||||
SMTP_PORT: 25,
|
||||
SUBMISSION_PORT: 587,
|
||||
SECURE_PORT: 465,
|
||||
// Default hostname
|
||||
HOSTNAME: 'mail.lossless.one',
|
||||
// CRLF line ending required by SMTP protocol
|
||||
CRLF: '\r\n',
|
||||
};
|
||||
/**
|
||||
* SMTP Command Patterns
|
||||
* Regular expressions for parsing SMTP commands
|
||||
*/
|
||||
export const SMTP_PATTERNS = {
|
||||
// Match EHLO/HELO command: "EHLO example.com"
|
||||
// Made very permissive to handle various client implementations
|
||||
EHLO: /^(?:EHLO|HELO)\s+(.+)$/i,
|
||||
// Match MAIL FROM command: "MAIL FROM:<user@example.com> [PARAM=VALUE]"
|
||||
// Made more permissive with whitespace and parameter formats
|
||||
MAIL_FROM: /^MAIL\s+FROM\s*:\s*<([^>]*)>((?:\s+[a-zA-Z0-9][a-zA-Z0-9\-]*(?:=[^\s]+)?)*)$/i,
|
||||
// Match RCPT TO command: "RCPT TO:<user@example.com> [PARAM=VALUE]"
|
||||
// Made more permissive with whitespace and parameter formats
|
||||
RCPT_TO: /^RCPT\s+TO\s*:\s*<([^>]*)>((?:\s+[a-zA-Z0-9][a-zA-Z0-9\-]*(?:=[^\s]+)?)*)$/i,
|
||||
// Match parameter format: "PARAM=VALUE"
|
||||
PARAM: /\s+([A-Za-z0-9][A-Za-z0-9\-]*)(?:=([^\s]+))?/g,
|
||||
// Match email address format - basic validation
|
||||
// This pattern rejects common invalid formats while being permissive for edge cases
|
||||
// Checks: no spaces, has @, has domain with dot, no double dots, proper domain format
|
||||
EMAIL: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
|
||||
// Match end of DATA marker: \r\n.\r\n or just .\r\n at the start of a line (to handle various client implementations)
|
||||
END_DATA: /(\r\n\.\r\n$)|(\n\.\r\n$)|(\r\n\.\n$)|(\n\.\n$)|^\.(\r\n|\n)$/,
|
||||
};
|
||||
/**
|
||||
* SMTP Extension List
|
||||
* These extensions are advertised in the EHLO response
|
||||
*/
|
||||
export const SMTP_EXTENSIONS = {
|
||||
// Basic extensions (RFC 1869)
|
||||
PIPELINING: 'PIPELINING',
|
||||
SIZE: 'SIZE',
|
||||
EIGHTBITMIME: '8BITMIME',
|
||||
// Security extensions
|
||||
STARTTLS: 'STARTTLS',
|
||||
AUTH: 'AUTH',
|
||||
// Additional extensions
|
||||
ENHANCEDSTATUSCODES: 'ENHANCEDSTATUSCODES',
|
||||
HELP: 'HELP',
|
||||
CHUNKING: 'CHUNKING',
|
||||
DSN: 'DSN',
|
||||
// Format an extension with a parameter
|
||||
formatExtension(name, parameter) {
|
||||
return parameter !== undefined ? `${name} ${parameter}` : name;
|
||||
}
|
||||
};
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uc3RhbnRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vdHMvbWFpbC9kZWxpdmVyeS9zbXRwc2VydmVyL2NvbnN0YW50cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFFSCxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFFN0MseURBQXlEO0FBQ3pELE9BQU8sRUFBRSxTQUFTLEVBQUUsQ0FBQztBQUVyQjs7O0dBR0c7QUFDSCxNQUFNLENBQU4sSUFBWSxnQkFxQ1g7QUFyQ0QsV0FBWSxnQkFBZ0I7SUFDMUIsc0JBQXNCO0lBQ3RCLCtEQUFhLENBQUE7SUFDYiwyRUFBbUIsQ0FBQTtJQUNuQix5RUFBa0IsQ0FBQTtJQUNsQiwyRUFBbUIsQ0FBQTtJQUNuQiwrRUFBcUIsQ0FBQTtJQUNyQixtR0FBK0IsQ0FBQTtJQUMvQixxREFBUSxDQUFBO0lBQ1IsK0RBQWEsQ0FBQTtJQUNiLHVFQUFpQixDQUFBO0lBRWpCLDJCQUEyQjtJQUMzQixpRkFBc0IsQ0FBQTtJQUN0QixpRkFBc0IsQ0FBQTtJQUV0Qiw4QkFBOEI7SUFDOUIsMkZBQTJCLENBQUE7SUFDM0IsK0dBQXFDLENBQUE7SUFDckMsdUVBQWlCLENBQUE7SUFDakIseUZBQTBCLENBQUE7SUFDMUIseUZBQTBCLENBQUE7SUFFMUIsOEJBQThCO0lBQzlCLHlFQUFrQixDQUFBO0lBQ2xCLCtGQUE2QixDQUFBO0lBQzdCLCtGQUE2QixDQUFBO0lBQzdCLHlFQUFrQixDQUFBO0lBQ2xCLG1IQUF1QyxDQUFBO0lBQ3ZDLDJFQUFtQixDQUFBO0lBQ25CLHVFQUFpQixDQUFBO0lBQ2pCLHVGQUF5QixDQUFBO0lBQ3pCLDZFQUFvQixDQUFBO0lBQ3BCLGlGQUFzQixDQUFBO0lBQ3RCLHlGQUEwQixDQUFBO0lBQzFCLHFGQUF3QixDQUFBO0lBQ3hCLHlHQUFrQyxDQUFBO0FBQ3BDLENBQUMsRUFyQ1csZ0JBQWdCLEtBQWhCLGdCQUFn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
|
||||
14
dist_ts/mail/delivery/smtpserver/create-server.d.ts
vendored
Normal file
14
dist_ts/mail/delivery/smtpserver/create-server.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/**
|
||||
* SMTP Server Creation Factory
|
||||
* Provides a simple way to create a complete SMTP server
|
||||
*/
|
||||
import { SmtpServer } from './smtp-server.js';
|
||||
import type { ISmtpServerOptions } from './interfaces.js';
|
||||
import { UnifiedEmailServer } from '../../routing/classes.unified.email.server.js';
|
||||
/**
|
||||
* Create a complete SMTP server with all components
|
||||
* @param emailServer - Email server reference
|
||||
* @param options - SMTP server options
|
||||
* @returns Configured SMTP server instance
|
||||
*/
|
||||
export declare function createSmtpServer(emailServer: UnifiedEmailServer, options: ISmtpServerOptions): SmtpServer;
|
||||
28
dist_ts/mail/delivery/smtpserver/create-server.js
Normal file
28
dist_ts/mail/delivery/smtpserver/create-server.js
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* SMTP Server Creation Factory
|
||||
* Provides a simple way to create a complete SMTP server
|
||||
*/
|
||||
import { SmtpServer } from './smtp-server.js';
|
||||
import { SessionManager } from './session-manager.js';
|
||||
import { ConnectionManager } from './connection-manager.js';
|
||||
import { CommandHandler } from './command-handler.js';
|
||||
import { DataHandler } from './data-handler.js';
|
||||
import { TlsHandler } from './tls-handler.js';
|
||||
import { SecurityHandler } from './security-handler.js';
|
||||
import { UnifiedEmailServer } from '../../routing/classes.unified.email.server.js';
|
||||
/**
|
||||
* Create a complete SMTP server with all components
|
||||
* @param emailServer - Email server reference
|
||||
* @param options - SMTP server options
|
||||
* @returns Configured SMTP server instance
|
||||
*/
|
||||
export function createSmtpServer(emailServer, options) {
|
||||
// First create the SMTP server instance
|
||||
const smtpServer = new SmtpServer({
|
||||
emailServer,
|
||||
options
|
||||
});
|
||||
// Return the configured server
|
||||
return smtpServer;
|
||||
}
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLXNlcnZlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL21haWwvZGVsaXZlcnkvc210cHNlcnZlci9jcmVhdGUtc2VydmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUVILE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUM5QyxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDdEQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDNUQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ3RELE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUNoRCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDOUMsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBRXhELE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLCtDQUErQyxDQUFDO0FBRW5GOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLGdCQUFnQixDQUFDLFdBQStCLEVBQUUsT0FBMkI7SUFDM0Ysd0NBQXdDO0lBQ3hDLE1BQU0sVUFBVSxHQUFHLElBQUksVUFBVSxDQUFDO1FBQ2hDLFdBQVc7UUFDWCxPQUFPO0tBQ1IsQ0FBQyxDQUFDO0lBRUgsK0JBQStCO0lBQy9CLE9BQU8sVUFBVSxDQUFDO0FBQ3BCLENBQUMifQ==
|
||||
123
dist_ts/mail/delivery/smtpserver/data-handler.d.ts
vendored
Normal file
123
dist_ts/mail/delivery/smtpserver/data-handler.d.ts
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* SMTP Data Handler
|
||||
* Responsible for processing email data during and after DATA command
|
||||
*/
|
||||
import * as plugins from '../../../plugins.js';
|
||||
import type { ISmtpSession, ISmtpTransactionResult } from './interfaces.js';
|
||||
import type { IDataHandler, ISmtpServer } from './interfaces.js';
|
||||
import { Email } from '../../core/classes.email.js';
|
||||
/**
|
||||
* Handles SMTP DATA command and email data processing
|
||||
*/
|
||||
export declare class DataHandler implements IDataHandler {
|
||||
/**
|
||||
* Reference to the SMTP server instance
|
||||
*/
|
||||
private smtpServer;
|
||||
/**
|
||||
* Creates a new data handler
|
||||
* @param smtpServer - SMTP server instance
|
||||
*/
|
||||
constructor(smtpServer: ISmtpServer);
|
||||
/**
|
||||
* Process incoming email data
|
||||
* @param socket - Client socket
|
||||
* @param data - Data chunk
|
||||
* @returns Promise that resolves when the data is processed
|
||||
*/
|
||||
processEmailData(socket: plugins.net.Socket | plugins.tls.TLSSocket, data: string): Promise<void>;
|
||||
/**
|
||||
* Handle raw data chunks during DATA mode (optimized for large messages)
|
||||
* @param socket - Client socket
|
||||
* @param data - Raw data chunk
|
||||
*/
|
||||
handleDataReceived(socket: plugins.net.Socket | plugins.tls.TLSSocket, data: string): Promise<void>;
|
||||
/**
|
||||
* Process email data chunks efficiently for large messages
|
||||
* @param chunks - Array of email data chunks
|
||||
* @returns Processed email data string
|
||||
*/
|
||||
private processEmailDataStreaming;
|
||||
/**
|
||||
* Process a complete email
|
||||
* @param rawData - Raw email data
|
||||
* @param session - SMTP session
|
||||
* @returns Promise that resolves with the Email object
|
||||
*/
|
||||
processEmail(rawData: string, session: ISmtpSession): Promise<Email>;
|
||||
/**
|
||||
* Parse email from raw data
|
||||
* @param rawData - Raw email data
|
||||
* @param session - SMTP session
|
||||
* @returns Email object
|
||||
*/
|
||||
private parseEmailFromData;
|
||||
/**
|
||||
* Process a complete email (legacy method)
|
||||
* @param session - SMTP session
|
||||
* @returns Promise that resolves with the result of the transaction
|
||||
*/
|
||||
processEmailLegacy(session: ISmtpSession): Promise<ISmtpTransactionResult>;
|
||||
/**
|
||||
* Save an email to disk
|
||||
* @param session - SMTP session
|
||||
*/
|
||||
saveEmail(session: ISmtpSession): void;
|
||||
/**
|
||||
* Parse an email into an Email object
|
||||
* @param session - SMTP session
|
||||
* @returns Promise that resolves with the parsed Email object
|
||||
*/
|
||||
parseEmail(session: ISmtpSession): Promise<Email>;
|
||||
/**
|
||||
* Basic fallback method for parsing emails
|
||||
* @param session - SMTP session
|
||||
* @returns The parsed Email object
|
||||
*/
|
||||
private parseEmailBasic;
|
||||
/**
|
||||
* Handle multipart content parsing
|
||||
* @param email - Email object to update
|
||||
* @param bodyText - Body text to parse
|
||||
* @param boundary - MIME boundary
|
||||
*/
|
||||
private handleMultipartContent;
|
||||
/**
|
||||
* Handle end of data marker received
|
||||
* @param socket - Client socket
|
||||
* @param session - SMTP session
|
||||
*/
|
||||
private handleEndOfData;
|
||||
/**
|
||||
* Reset session after email processing
|
||||
* @param session - SMTP session
|
||||
*/
|
||||
private resetSession;
|
||||
/**
|
||||
* Send a response to the client
|
||||
* @param socket - Client socket
|
||||
* @param response - Response message
|
||||
*/
|
||||
private sendResponse;
|
||||
/**
|
||||
* Check if a socket error is potentially recoverable
|
||||
* @param error - The error that occurred
|
||||
* @returns Whether the error is potentially recoverable
|
||||
*/
|
||||
private isRecoverableSocketError;
|
||||
/**
|
||||
* Handle recoverable socket errors with retry logic
|
||||
* @param socket - Client socket
|
||||
* @param error - The error that occurred
|
||||
* @param response - The response that failed to send
|
||||
*/
|
||||
private handleSocketError;
|
||||
/**
|
||||
* Handle email data (interface requirement)
|
||||
*/
|
||||
handleData(socket: plugins.net.Socket | plugins.tls.TLSSocket, data: string, session: ISmtpSession): Promise<void>;
|
||||
/**
|
||||
* Clean up resources
|
||||
*/
|
||||
destroy(): void;
|
||||
}
|
||||
1152
dist_ts/mail/delivery/smtpserver/data-handler.js
Normal file
1152
dist_ts/mail/delivery/smtpserver/data-handler.js
Normal file
File diff suppressed because one or more lines are too long
20
dist_ts/mail/delivery/smtpserver/index.d.ts
vendored
Normal file
20
dist_ts/mail/delivery/smtpserver/index.d.ts
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* SMTP Server Module Exports
|
||||
* This file exports all components of the refactored SMTP server
|
||||
*/
|
||||
export * from './interfaces.js';
|
||||
export { SmtpServer } from './smtp-server.js';
|
||||
export { SessionManager } from './session-manager.js';
|
||||
export { ConnectionManager } from './connection-manager.js';
|
||||
export { CommandHandler } from './command-handler.js';
|
||||
export { DataHandler } from './data-handler.js';
|
||||
export { TlsHandler } from './tls-handler.js';
|
||||
export { SecurityHandler } from './security-handler.js';
|
||||
export * from './constants.js';
|
||||
export { SmtpLogger } from './utils/logging.js';
|
||||
export * from './utils/validation.js';
|
||||
export * from './utils/helpers.js';
|
||||
export * from './certificate-utils.js';
|
||||
export * from './secure-server.js';
|
||||
export * from './starttls-handler.js';
|
||||
export { createSmtpServer } from './create-server.js';
|
||||
27
dist_ts/mail/delivery/smtpserver/index.js
Normal file
27
dist_ts/mail/delivery/smtpserver/index.js
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* SMTP Server Module Exports
|
||||
* This file exports all components of the refactored SMTP server
|
||||
*/
|
||||
// Export interfaces
|
||||
export * from './interfaces.js';
|
||||
// Export server classes
|
||||
export { SmtpServer } from './smtp-server.js';
|
||||
export { SessionManager } from './session-manager.js';
|
||||
export { ConnectionManager } from './connection-manager.js';
|
||||
export { CommandHandler } from './command-handler.js';
|
||||
export { DataHandler } from './data-handler.js';
|
||||
export { TlsHandler } from './tls-handler.js';
|
||||
export { SecurityHandler } from './security-handler.js';
|
||||
// Export constants
|
||||
export * from './constants.js';
|
||||
// Export utilities
|
||||
export { SmtpLogger } from './utils/logging.js';
|
||||
export * from './utils/validation.js';
|
||||
export * from './utils/helpers.js';
|
||||
// Export TLS and certificate utilities
|
||||
export * from './certificate-utils.js';
|
||||
export * from './secure-server.js';
|
||||
export * from './starttls-handler.js';
|
||||
// Factory function to create a complete SMTP server with default components
|
||||
export { createSmtpServer } from './create-server.js';
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi90cy9tYWlsL2RlbGl2ZXJ5L3NtdHBzZXJ2ZXIvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBRUgsb0JBQW9CO0FBQ3BCLGNBQWMsaUJBQWlCLENBQUM7QUFFaEMsd0JBQXdCO0FBQ3hCLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUM5QyxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDdEQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDNUQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ3RELE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUNoRCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDOUMsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBRXhELG1CQUFtQjtBQUNuQixjQUFjLGdCQUFnQixDQUFDO0FBRS9CLG1CQUFtQjtBQUNuQixPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDaEQsY0FBYyx1QkFBdUIsQ0FBQztBQUN0QyxjQUFjLG9CQUFvQixDQUFDO0FBRW5DLHVDQUF1QztBQUN2QyxjQUFjLHdCQUF3QixDQUFDO0FBQ3ZDLGNBQWMsb0JBQW9CLENBQUM7QUFDbkMsY0FBYyx1QkFBdUIsQ0FBQztBQUV0Qyw0RUFBNEU7QUFDNUUsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sb0JBQW9CLENBQUMifQ==
|
||||
530
dist_ts/mail/delivery/smtpserver/interfaces.d.ts
vendored
Normal file
530
dist_ts/mail/delivery/smtpserver/interfaces.d.ts
vendored
Normal file
@@ -0,0 +1,530 @@
|
||||
/**
|
||||
* SMTP Server Interfaces
|
||||
* Defines all the interfaces used by the SMTP server implementation
|
||||
*/
|
||||
import * as plugins from '../../../plugins.js';
|
||||
import type { Email } from '../../core/classes.email.js';
|
||||
import type { UnifiedEmailServer } from '../../routing/classes.unified.email.server.js';
|
||||
import { SmtpState } from '../interfaces.js';
|
||||
import { SmtpCommand } from './constants.js';
|
||||
export { SmtpState, SmtpCommand };
|
||||
export type { IEnvelopeRecipient } from '../interfaces.js';
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
10
dist_ts/mail/delivery/smtpserver/interfaces.js
Normal file
10
dist_ts/mail/delivery/smtpserver/interfaces.js
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* SMTP Server Interfaces
|
||||
* Defines all the interfaces used by the SMTP server implementation
|
||||
*/
|
||||
import * as plugins from '../../../plugins.js';
|
||||
// Re-export types from other modules
|
||||
import { SmtpState } from '../interfaces.js';
|
||||
import { SmtpCommand } from './constants.js';
|
||||
export { SmtpState, SmtpCommand };
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL21haWwvZGVsaXZlcnkvc210cHNlcnZlci9pbnRlcmZhY2VzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUVILE9BQU8sS0FBSyxPQUFPLE1BQU0scUJBQXFCLENBQUM7QUFJL0MsdUNBQXVDO0FBQ3ZDLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUM3QyxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDN0MsT0FBTyxFQUFFLFNBQVMsRUFBRSxXQUFXLEVBQUUsQ0FBQyJ9
|
||||
15
dist_ts/mail/delivery/smtpserver/secure-server.d.ts
vendored
Normal file
15
dist_ts/mail/delivery/smtpserver/secure-server.d.ts
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* Secure SMTP Server Utility Functions
|
||||
* Provides helper functions for creating and managing secure TLS server
|
||||
*/
|
||||
import * as plugins from '../../../plugins.js';
|
||||
/**
|
||||
* Create a secure TLS server for direct TLS connections
|
||||
* @param options - TLS certificate options
|
||||
* @returns A configured TLS server or undefined if TLS is not available
|
||||
*/
|
||||
export declare function createSecureTlsServer(options: {
|
||||
key: string;
|
||||
cert: string;
|
||||
ca?: string;
|
||||
}): plugins.tls.Server | undefined;
|
||||
79
dist_ts/mail/delivery/smtpserver/secure-server.js
Normal file
79
dist_ts/mail/delivery/smtpserver/secure-server.js
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Secure SMTP Server Utility Functions
|
||||
* Provides helper functions for creating and managing secure TLS server
|
||||
*/
|
||||
import * as plugins from '../../../plugins.js';
|
||||
import { loadCertificatesFromString, generateSelfSignedCertificates, createTlsOptions } from './certificate-utils.js';
|
||||
import { SmtpLogger } from './utils/logging.js';
|
||||
/**
|
||||
* Create a secure TLS server for direct TLS connections
|
||||
* @param options - TLS certificate options
|
||||
* @returns A configured TLS server or undefined if TLS is not available
|
||||
*/
|
||||
export function createSecureTlsServer(options) {
|
||||
try {
|
||||
// Log the creation attempt
|
||||
SmtpLogger.info('Creating secure TLS server for direct connections');
|
||||
// Load certificates from strings
|
||||
let certificates;
|
||||
try {
|
||||
certificates = loadCertificatesFromString({
|
||||
key: options.key,
|
||||
cert: options.cert,
|
||||
ca: options.ca
|
||||
});
|
||||
SmtpLogger.info('Successfully loaded TLS certificates for secure server');
|
||||
}
|
||||
catch (certificateError) {
|
||||
SmtpLogger.warn(`Failed to load certificates, using self-signed: ${certificateError instanceof Error ? certificateError.message : String(certificateError)}`);
|
||||
certificates = generateSelfSignedCertificates();
|
||||
}
|
||||
// Create server-side TLS options
|
||||
const tlsOptions = createTlsOptions(certificates, true);
|
||||
// Log details for debugging
|
||||
SmtpLogger.debug('Creating secure server with options', {
|
||||
certificates: {
|
||||
keyLength: certificates.key.length,
|
||||
certLength: certificates.cert.length,
|
||||
caLength: certificates.ca ? certificates.ca.length : 0
|
||||
},
|
||||
tlsOptions: {
|
||||
minVersion: tlsOptions.minVersion,
|
||||
maxVersion: tlsOptions.maxVersion,
|
||||
ciphers: tlsOptions.ciphers?.substring(0, 50) + '...' // Truncate long cipher list
|
||||
}
|
||||
});
|
||||
// Create the TLS server
|
||||
const server = new plugins.tls.Server(tlsOptions);
|
||||
// Set up error handlers
|
||||
server.on('error', (err) => {
|
||||
SmtpLogger.error(`Secure server error: ${err.message}`, {
|
||||
component: 'secure-server',
|
||||
error: err,
|
||||
stack: err.stack
|
||||
});
|
||||
});
|
||||
// Log secure connections
|
||||
server.on('secureConnection', (socket) => {
|
||||
const protocol = socket.getProtocol();
|
||||
const cipher = socket.getCipher();
|
||||
SmtpLogger.info('New direct TLS connection established', {
|
||||
component: 'secure-server',
|
||||
remoteAddress: socket.remoteAddress,
|
||||
remotePort: socket.remotePort,
|
||||
protocol: protocol || 'unknown',
|
||||
cipher: cipher?.name || 'unknown'
|
||||
});
|
||||
});
|
||||
return server;
|
||||
}
|
||||
catch (error) {
|
||||
SmtpLogger.error(`Failed to create secure TLS server: ${error instanceof Error ? error.message : String(error)}`, {
|
||||
component: 'secure-server',
|
||||
error: error instanceof Error ? error : new Error(String(error)),
|
||||
stack: error instanceof Error ? error.stack : 'No stack trace available'
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VjdXJlLXNlcnZlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL21haWwvZGVsaXZlcnkvc210cHNlcnZlci9zZWN1cmUtc2VydmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUVILE9BQU8sS0FBSyxPQUFPLE1BQU0scUJBQXFCLENBQUM7QUFDL0MsT0FBTyxFQUNMLDBCQUEwQixFQUMxQiw4QkFBOEIsRUFDOUIsZ0JBQWdCLEVBRWpCLE1BQU0sd0JBQXdCLENBQUM7QUFDaEMsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBRWhEOzs7O0dBSUc7QUFDSCxNQUFNLFVBQVUscUJBQXFCLENBQUMsT0FJckM7SUFDQyxJQUFJLENBQUM7UUFDSCwyQkFBMkI7UUFDM0IsVUFBVSxDQUFDLElBQUksQ0FBQyxtREFBbUQsQ0FBQyxDQUFDO1FBRXJFLGlDQUFpQztRQUNqQyxJQUFJLFlBQThCLENBQUM7UUFDbkMsSUFBSSxDQUFDO1lBQ0gsWUFBWSxHQUFHLDBCQUEwQixDQUFDO2dCQUN4QyxHQUFHLEVBQUUsT0FBTyxDQUFDLEdBQUc7Z0JBQ2hCLElBQUksRUFBRSxPQUFPLENBQUMsSUFBSTtnQkFDbEIsRUFBRSxFQUFFLE9BQU8sQ0FBQyxFQUFFO2FBQ2YsQ0FBQyxDQUFDO1lBRUgsVUFBVSxDQUFDLElBQUksQ0FBQyx3REFBd0QsQ0FBQyxDQUFDO1FBQzVFLENBQUM7UUFBQyxPQUFPLGdCQUFnQixFQUFFLENBQUM7WUFDMUIsVUFBVSxDQUFDLElBQUksQ0FBQyxtREFBbUQsZ0JBQWdCLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxnQkFBZ0IsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUM5SixZQUFZLEdBQUcsOEJBQThCLEVBQUUsQ0FBQztRQUNsRCxDQUFDO1FBRUQsaUNBQWlDO1FBQ2pDLE1BQU0s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
|
||||
86
dist_ts/mail/delivery/smtpserver/security-handler.d.ts
vendored
Normal file
86
dist_ts/mail/delivery/smtpserver/security-handler.d.ts
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* SMTP Security Handler
|
||||
* Responsible for security aspects including IP reputation checking,
|
||||
* email validation, and authentication
|
||||
*/
|
||||
import * as plugins from '../../../plugins.js';
|
||||
import type { ISmtpAuth } from './interfaces.js';
|
||||
import type { ISecurityHandler, ISmtpServer } from './interfaces.js';
|
||||
/**
|
||||
* Handles security aspects for SMTP server
|
||||
*/
|
||||
export declare class SecurityHandler implements ISecurityHandler {
|
||||
/**
|
||||
* Reference to the SMTP server instance
|
||||
*/
|
||||
private smtpServer;
|
||||
/**
|
||||
* IP reputation checker service
|
||||
*/
|
||||
private ipReputationService;
|
||||
/**
|
||||
* Simple in-memory IP denylist
|
||||
*/
|
||||
private ipDenylist;
|
||||
/**
|
||||
* Cleanup interval timer
|
||||
*/
|
||||
private cleanupInterval;
|
||||
/**
|
||||
* Creates a new security handler
|
||||
* @param smtpServer - SMTP server instance
|
||||
*/
|
||||
constructor(smtpServer: ISmtpServer);
|
||||
/**
|
||||
* Check IP reputation for a connection
|
||||
* @param socket - Client socket
|
||||
* @returns Promise that resolves to true if IP is allowed, false if blocked
|
||||
*/
|
||||
checkIpReputation(socket: plugins.net.Socket | plugins.tls.TLSSocket): Promise<boolean>;
|
||||
/**
|
||||
* Validate an email address
|
||||
* @param email - Email address to validate
|
||||
* @returns Whether the email address is valid
|
||||
*/
|
||||
isValidEmail(email: string): boolean;
|
||||
/**
|
||||
* Validate authentication credentials
|
||||
* @param auth - Authentication credentials
|
||||
* @returns Promise that resolves to true if authenticated
|
||||
*/
|
||||
authenticate(auth: ISmtpAuth): Promise<boolean>;
|
||||
/**
|
||||
* Log a security event
|
||||
* @param event - Event type
|
||||
* @param level - Log level
|
||||
* @param details - Event details
|
||||
*/
|
||||
logSecurityEvent(event: string, level: string, message: string, details: Record<string, any>): void;
|
||||
/**
|
||||
* Add an IP to the denylist
|
||||
* @param ip - IP address
|
||||
* @param reason - Reason for denylisting
|
||||
* @param duration - Duration in milliseconds (optional, indefinite if not specified)
|
||||
*/
|
||||
private addToDenylist;
|
||||
/**
|
||||
* Check if an IP is denylisted
|
||||
* @param ip - IP address
|
||||
* @returns Whether the IP is denylisted
|
||||
*/
|
||||
private isIpDenylisted;
|
||||
/**
|
||||
* Get the reason an IP was denylisted
|
||||
* @param ip - IP address
|
||||
* @returns Reason for denylisting or undefined if not denylisted
|
||||
*/
|
||||
private getDenylistReason;
|
||||
/**
|
||||
* Clean expired denylist entries
|
||||
*/
|
||||
private cleanExpiredDenylistEntries;
|
||||
/**
|
||||
* Clean up resources
|
||||
*/
|
||||
destroy(): void;
|
||||
}
|
||||
242
dist_ts/mail/delivery/smtpserver/security-handler.js
Normal file
242
dist_ts/mail/delivery/smtpserver/security-handler.js
Normal file
File diff suppressed because one or more lines are too long
140
dist_ts/mail/delivery/smtpserver/session-manager.d.ts
vendored
Normal file
140
dist_ts/mail/delivery/smtpserver/session-manager.d.ts
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* SMTP Session Manager
|
||||
* Responsible for creating, managing, and cleaning up SMTP sessions
|
||||
*/
|
||||
import * as plugins from '../../../plugins.js';
|
||||
import { SmtpState } from './interfaces.js';
|
||||
import type { ISmtpSession } from './interfaces.js';
|
||||
import type { ISessionManager, ISessionEvents } from './interfaces.js';
|
||||
/**
|
||||
* Manager for SMTP sessions
|
||||
* Handles session creation, tracking, timeout management, and cleanup
|
||||
*/
|
||||
export declare class SessionManager implements ISessionManager {
|
||||
/**
|
||||
* Map of socket ID to session
|
||||
*/
|
||||
private sessions;
|
||||
/**
|
||||
* Map of socket to socket ID
|
||||
*/
|
||||
private socketIds;
|
||||
/**
|
||||
* SMTP server options
|
||||
*/
|
||||
private options;
|
||||
/**
|
||||
* Event listeners
|
||||
*/
|
||||
private eventListeners;
|
||||
/**
|
||||
* Timer for cleanup interval
|
||||
*/
|
||||
private cleanupTimer;
|
||||
/**
|
||||
* Creates a new session manager
|
||||
* @param options - Session manager options
|
||||
*/
|
||||
constructor(options?: {
|
||||
socketTimeout?: number;
|
||||
connectionTimeout?: number;
|
||||
cleanupInterval?: number;
|
||||
});
|
||||
/**
|
||||
* Creates a new session for a socket connection
|
||||
* @param socket - Client socket
|
||||
* @param secure - Whether the connection is secure (TLS)
|
||||
* @returns New SMTP session
|
||||
*/
|
||||
createSession(socket: plugins.net.Socket | plugins.tls.TLSSocket, secure: boolean): ISmtpSession;
|
||||
/**
|
||||
* Updates the session state
|
||||
* @param session - SMTP session
|
||||
* @param newState - New state
|
||||
*/
|
||||
updateSessionState(session: ISmtpSession, newState: SmtpState): void;
|
||||
/**
|
||||
* Updates the session's last activity timestamp
|
||||
* @param session - SMTP session
|
||||
*/
|
||||
updateSessionActivity(session: ISmtpSession): void;
|
||||
/**
|
||||
* Removes a session
|
||||
* @param socket - Client socket
|
||||
*/
|
||||
removeSession(socket: plugins.net.Socket | plugins.tls.TLSSocket): void;
|
||||
/**
|
||||
* Gets a session for a socket
|
||||
* @param socket - Client socket
|
||||
* @returns SMTP session or undefined if not found
|
||||
*/
|
||||
getSession(socket: plugins.net.Socket | plugins.tls.TLSSocket): ISmtpSession | undefined;
|
||||
/**
|
||||
* Cleans up idle sessions
|
||||
*/
|
||||
cleanupIdleSessions(): void;
|
||||
/**
|
||||
* Gets the current number of active sessions
|
||||
* @returns Number of active sessions
|
||||
*/
|
||||
getSessionCount(): number;
|
||||
/**
|
||||
* Clears all sessions (used when shutting down)
|
||||
*/
|
||||
clearAllSessions(): void;
|
||||
/**
|
||||
* Register an event listener
|
||||
* @param event - Event name
|
||||
* @param listener - Event listener function
|
||||
*/
|
||||
on<K extends keyof ISessionEvents>(event: K, listener: ISessionEvents[K]): void;
|
||||
/**
|
||||
* Remove an event listener
|
||||
* @param event - Event name
|
||||
* @param listener - Event listener function
|
||||
*/
|
||||
off<K extends keyof ISessionEvents>(event: K, listener: ISessionEvents[K]): void;
|
||||
/**
|
||||
* Emit an event to registered listeners
|
||||
* @param event - Event name
|
||||
* @param args - Event arguments
|
||||
*/
|
||||
private emitEvent;
|
||||
/**
|
||||
* Start the cleanup timer
|
||||
*/
|
||||
private startCleanupTimer;
|
||||
/**
|
||||
* Stop the cleanup timer
|
||||
*/
|
||||
private stopCleanupTimer;
|
||||
/**
|
||||
* Replace socket mapping for STARTTLS upgrades
|
||||
* @param oldSocket - Original plain socket
|
||||
* @param newSocket - New TLS socket
|
||||
* @returns Whether the replacement was successful
|
||||
*/
|
||||
replaceSocket(oldSocket: plugins.net.Socket | plugins.tls.TLSSocket, newSocket: plugins.net.Socket | plugins.tls.TLSSocket): boolean;
|
||||
/**
|
||||
* Gets a unique key for a socket
|
||||
* @param socket - Client socket
|
||||
* @returns Socket key
|
||||
*/
|
||||
private getSocketKey;
|
||||
/**
|
||||
* Get all active sessions
|
||||
*/
|
||||
getAllSessions(): ISmtpSession[];
|
||||
/**
|
||||
* Update last activity for a session by socket
|
||||
*/
|
||||
updateLastActivity(socket: plugins.net.Socket | plugins.tls.TLSSocket): void;
|
||||
/**
|
||||
* Check for timed out sessions
|
||||
*/
|
||||
checkTimeouts(timeoutMs: number): ISmtpSession[];
|
||||
/**
|
||||
* Clean up resources
|
||||
*/
|
||||
destroy(): void;
|
||||
}
|
||||
473
dist_ts/mail/delivery/smtpserver/session-manager.js
Normal file
473
dist_ts/mail/delivery/smtpserver/session-manager.js
Normal file
File diff suppressed because one or more lines are too long
137
dist_ts/mail/delivery/smtpserver/smtp-server.d.ts
vendored
Normal file
137
dist_ts/mail/delivery/smtpserver/smtp-server.d.ts
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* SMTP Server
|
||||
* Core implementation for the refactored SMTP server
|
||||
*/
|
||||
import type { ISmtpServerOptions } from './interfaces.js';
|
||||
import type { ISmtpServer, ISmtpServerConfig, ISessionManager, IConnectionManager, ICommandHandler, IDataHandler, ITlsHandler, ISecurityHandler } from './interfaces.js';
|
||||
import { UnifiedEmailServer } from '../../routing/classes.unified.email.server.js';
|
||||
/**
|
||||
* SMTP Server implementation
|
||||
* The main server class that coordinates all components
|
||||
*/
|
||||
export declare class SmtpServer implements ISmtpServer {
|
||||
/**
|
||||
* Email server reference
|
||||
*/
|
||||
private emailServer;
|
||||
/**
|
||||
* Session manager
|
||||
*/
|
||||
private sessionManager;
|
||||
/**
|
||||
* Connection manager
|
||||
*/
|
||||
private connectionManager;
|
||||
/**
|
||||
* Command handler
|
||||
*/
|
||||
private commandHandler;
|
||||
/**
|
||||
* Data handler
|
||||
*/
|
||||
private dataHandler;
|
||||
/**
|
||||
* TLS handler
|
||||
*/
|
||||
private tlsHandler;
|
||||
/**
|
||||
* Security handler
|
||||
*/
|
||||
private securityHandler;
|
||||
/**
|
||||
* SMTP server options
|
||||
*/
|
||||
private options;
|
||||
/**
|
||||
* Net server instance
|
||||
*/
|
||||
private server;
|
||||
/**
|
||||
* Secure server instance
|
||||
*/
|
||||
private secureServer;
|
||||
/**
|
||||
* Whether the server is running
|
||||
*/
|
||||
private running;
|
||||
/**
|
||||
* Server recovery state
|
||||
*/
|
||||
private recoveryState;
|
||||
/**
|
||||
* Creates a new SMTP server
|
||||
* @param config - Server configuration
|
||||
*/
|
||||
constructor(config: ISmtpServerConfig);
|
||||
/**
|
||||
* Start the SMTP server
|
||||
* @returns Promise that resolves when server is started
|
||||
*/
|
||||
listen(): Promise<void>;
|
||||
/**
|
||||
* Stop the SMTP server
|
||||
* @returns Promise that resolves when server is stopped
|
||||
*/
|
||||
close(): Promise<void>;
|
||||
/**
|
||||
* Get the session manager
|
||||
* @returns Session manager instance
|
||||
*/
|
||||
getSessionManager(): ISessionManager;
|
||||
/**
|
||||
* Get the connection manager
|
||||
* @returns Connection manager instance
|
||||
*/
|
||||
getConnectionManager(): IConnectionManager;
|
||||
/**
|
||||
* Get the command handler
|
||||
* @returns Command handler instance
|
||||
*/
|
||||
getCommandHandler(): ICommandHandler;
|
||||
/**
|
||||
* Get the data handler
|
||||
* @returns Data handler instance
|
||||
*/
|
||||
getDataHandler(): IDataHandler;
|
||||
/**
|
||||
* Get the TLS handler
|
||||
* @returns TLS handler instance
|
||||
*/
|
||||
getTlsHandler(): ITlsHandler;
|
||||
/**
|
||||
* Get the security handler
|
||||
* @returns Security handler instance
|
||||
*/
|
||||
getSecurityHandler(): ISecurityHandler;
|
||||
/**
|
||||
* Get the server options
|
||||
* @returns SMTP server options
|
||||
*/
|
||||
getOptions(): ISmtpServerOptions;
|
||||
/**
|
||||
* Get the email server reference
|
||||
* @returns Email server instance
|
||||
*/
|
||||
getEmailServer(): UnifiedEmailServer;
|
||||
/**
|
||||
* Check if the server is running
|
||||
* @returns Whether the server is running
|
||||
*/
|
||||
isRunning(): boolean;
|
||||
/**
|
||||
* Check if we should attempt to recover from an error
|
||||
* @param error - The error that occurred
|
||||
* @returns Whether recovery should be attempted
|
||||
*/
|
||||
private shouldAttemptRecovery;
|
||||
/**
|
||||
* Attempt to recover the server after a critical error
|
||||
* @param serverType - The type of server to recover ('standard' or 'secure')
|
||||
* @param error - The error that triggered recovery
|
||||
*/
|
||||
private attemptServerRecovery;
|
||||
/**
|
||||
* Clean up all component resources
|
||||
*/
|
||||
destroy(): Promise<void>;
|
||||
}
|
||||
698
dist_ts/mail/delivery/smtpserver/smtp-server.js
Normal file
698
dist_ts/mail/delivery/smtpserver/smtp-server.js
Normal file
File diff suppressed because one or more lines are too long
21
dist_ts/mail/delivery/smtpserver/starttls-handler.d.ts
vendored
Normal file
21
dist_ts/mail/delivery/smtpserver/starttls-handler.d.ts
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* STARTTLS Implementation
|
||||
* Provides an improved implementation for STARTTLS upgrades
|
||||
*/
|
||||
import * as plugins from '../../../plugins.js';
|
||||
import type { ISmtpSession, ISessionManager, IConnectionManager } from './interfaces.js';
|
||||
import { SmtpState } from '../interfaces.js';
|
||||
/**
|
||||
* Enhanced STARTTLS handler for more reliable TLS upgrades
|
||||
*/
|
||||
export declare function performStartTLS(socket: plugins.net.Socket, options: {
|
||||
key: string;
|
||||
cert: string;
|
||||
ca?: string;
|
||||
session?: ISmtpSession;
|
||||
sessionManager?: ISessionManager;
|
||||
connectionManager?: IConnectionManager;
|
||||
onSuccess?: (tlsSocket: plugins.tls.TLSSocket) => void;
|
||||
onFailure?: (error: Error) => void;
|
||||
updateSessionState?: (session: ISmtpSession, state: SmtpState) => void;
|
||||
}): Promise<plugins.tls.TLSSocket | undefined>;
|
||||
207
dist_ts/mail/delivery/smtpserver/starttls-handler.js
Normal file
207
dist_ts/mail/delivery/smtpserver/starttls-handler.js
Normal file
File diff suppressed because one or more lines are too long
66
dist_ts/mail/delivery/smtpserver/tls-handler.d.ts
vendored
Normal file
66
dist_ts/mail/delivery/smtpserver/tls-handler.d.ts
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
* SMTP TLS Handler
|
||||
* Responsible for handling TLS-related SMTP functionality
|
||||
*/
|
||||
import * as plugins from '../../../plugins.js';
|
||||
import type { ITlsHandler, ISmtpServer, ISmtpSession } from './interfaces.js';
|
||||
/**
|
||||
* Handles TLS functionality for SMTP server
|
||||
*/
|
||||
export declare class TlsHandler implements ITlsHandler {
|
||||
/**
|
||||
* Reference to the SMTP server instance
|
||||
*/
|
||||
private smtpServer;
|
||||
/**
|
||||
* Certificate data
|
||||
*/
|
||||
private certificates;
|
||||
/**
|
||||
* TLS options
|
||||
*/
|
||||
private options;
|
||||
/**
|
||||
* Creates a new TLS handler
|
||||
* @param smtpServer - SMTP server instance
|
||||
*/
|
||||
constructor(smtpServer: ISmtpServer);
|
||||
/**
|
||||
* Handle STARTTLS command
|
||||
* @param socket - Client socket
|
||||
*/
|
||||
handleStartTls(socket: plugins.net.Socket, session: ISmtpSession): Promise<plugins.tls.TLSSocket | null>;
|
||||
/**
|
||||
* Upgrade a connection to TLS
|
||||
* @param socket - Client socket
|
||||
*/
|
||||
startTLS(socket: plugins.net.Socket): Promise<plugins.tls.TLSSocket>;
|
||||
/**
|
||||
* Create a secure server
|
||||
* @returns TLS server instance or undefined if TLS is not enabled
|
||||
*/
|
||||
createSecureServer(): plugins.tls.Server | undefined;
|
||||
/**
|
||||
* Check if TLS is enabled
|
||||
* @returns Whether TLS is enabled
|
||||
*/
|
||||
isTlsEnabled(): boolean;
|
||||
/**
|
||||
* Send a response to the client
|
||||
* @param socket - Client socket
|
||||
* @param response - Response message
|
||||
*/
|
||||
private sendResponse;
|
||||
/**
|
||||
* Check if TLS is available (interface requirement)
|
||||
*/
|
||||
isTlsAvailable(): boolean;
|
||||
/**
|
||||
* Get TLS options (interface requirement)
|
||||
*/
|
||||
getTlsOptions(): plugins.tls.TlsOptions;
|
||||
/**
|
||||
* Clean up resources
|
||||
*/
|
||||
destroy(): void;
|
||||
}
|
||||
273
dist_ts/mail/delivery/smtpserver/tls-handler.js
Normal file
273
dist_ts/mail/delivery/smtpserver/tls-handler.js
Normal file
File diff suppressed because one or more lines are too long
117
dist_ts/mail/delivery/smtpserver/utils/adaptive-logging.d.ts
vendored
Normal file
117
dist_ts/mail/delivery/smtpserver/utils/adaptive-logging.d.ts
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* Adaptive SMTP Logging System
|
||||
* Automatically switches between logging modes based on server load (active connections)
|
||||
* to maintain performance during high-concurrency scenarios
|
||||
*/
|
||||
import * as plugins from '../../../../plugins.js';
|
||||
import { SecurityLogLevel, SecurityEventType } from '../constants.js';
|
||||
import type { ISmtpSession } from '../interfaces.js';
|
||||
import type { LogLevel, ISmtpLogOptions } from './logging.js';
|
||||
/**
|
||||
* Log modes based on server load
|
||||
*/
|
||||
export declare enum LogMode {
|
||||
VERBOSE = "VERBOSE",// < 20 connections: Full detailed logging
|
||||
REDUCED = "REDUCED",// 20-40 connections: Limited command/response logging, full error logging
|
||||
MINIMAL = "MINIMAL"
|
||||
}
|
||||
/**
|
||||
* Configuration for adaptive logging thresholds
|
||||
*/
|
||||
export interface IAdaptiveLogConfig {
|
||||
verboseThreshold: number;
|
||||
reducedThreshold: number;
|
||||
aggregationInterval: number;
|
||||
maxAggregatedEntries: number;
|
||||
}
|
||||
/**
|
||||
* Connection metadata for aggregation tracking
|
||||
*/
|
||||
interface IConnectionTracker {
|
||||
activeConnections: number;
|
||||
peakConnections: number;
|
||||
totalConnections: number;
|
||||
connectionsPerSecond: number;
|
||||
lastConnectionTime: number;
|
||||
}
|
||||
/**
|
||||
* Adaptive SMTP Logger that scales logging based on server load
|
||||
*/
|
||||
export declare class AdaptiveSmtpLogger {
|
||||
private static instance;
|
||||
private currentMode;
|
||||
private config;
|
||||
private aggregatedEntries;
|
||||
private aggregationTimer;
|
||||
private connectionTracker;
|
||||
private constructor();
|
||||
/**
|
||||
* Get singleton instance
|
||||
*/
|
||||
static getInstance(config?: Partial<IAdaptiveLogConfig>): AdaptiveSmtpLogger;
|
||||
/**
|
||||
* Update active connection count and adjust log mode if needed
|
||||
*/
|
||||
updateConnectionCount(activeConnections: number): void;
|
||||
/**
|
||||
* Track new connection for rate calculation
|
||||
*/
|
||||
trackConnection(): void;
|
||||
/**
|
||||
* Get current logging mode
|
||||
*/
|
||||
getCurrentMode(): LogMode;
|
||||
/**
|
||||
* Get connection statistics
|
||||
*/
|
||||
getConnectionStats(): IConnectionTracker;
|
||||
/**
|
||||
* Log a message with adaptive behavior
|
||||
*/
|
||||
log(level: LogLevel, message: string, options?: ISmtpLogOptions): void;
|
||||
/**
|
||||
* Log command with adaptive behavior
|
||||
*/
|
||||
logCommand(command: string, socket: plugins.net.Socket | plugins.tls.TLSSocket, session?: ISmtpSession): void;
|
||||
/**
|
||||
* Log response with adaptive behavior
|
||||
*/
|
||||
logResponse(response: string, socket: plugins.net.Socket | plugins.tls.TLSSocket): void;
|
||||
/**
|
||||
* Log connection event with adaptive behavior
|
||||
*/
|
||||
logConnection(socket: plugins.net.Socket | plugins.tls.TLSSocket, eventType: 'connect' | 'close' | 'error', session?: ISmtpSession, error?: Error): void;
|
||||
/**
|
||||
* Log security event (always logged regardless of mode)
|
||||
*/
|
||||
logSecurityEvent(level: SecurityLogLevel, type: SecurityEventType, message: string, details: Record<string, any>, ipAddress?: string, domain?: string, success?: boolean): void;
|
||||
/**
|
||||
* Determine appropriate log mode based on connection count
|
||||
*/
|
||||
private determineLogMode;
|
||||
/**
|
||||
* Switch to a new log mode
|
||||
*/
|
||||
private switchLogMode;
|
||||
/**
|
||||
* Add entry to aggregation buffer
|
||||
*/
|
||||
private aggregateEntry;
|
||||
/**
|
||||
* Start the aggregation timer
|
||||
*/
|
||||
private startAggregationTimer;
|
||||
/**
|
||||
* Flush aggregated entries to logs
|
||||
*/
|
||||
private flushAggregatedEntries;
|
||||
/**
|
||||
* Cleanup resources
|
||||
*/
|
||||
destroy(): void;
|
||||
}
|
||||
/**
|
||||
* Default instance for easy access
|
||||
*/
|
||||
export declare const adaptiveLogger: AdaptiveSmtpLogger;
|
||||
export {};
|
||||
413
dist_ts/mail/delivery/smtpserver/utils/adaptive-logging.js
Normal file
413
dist_ts/mail/delivery/smtpserver/utils/adaptive-logging.js
Normal file
File diff suppressed because one or more lines are too long
78
dist_ts/mail/delivery/smtpserver/utils/helpers.d.ts
vendored
Normal file
78
dist_ts/mail/delivery/smtpserver/utils/helpers.d.ts
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* SMTP Helper Functions
|
||||
* Provides utility functions for SMTP server implementation
|
||||
*/
|
||||
import * as plugins from '../../../../plugins.js';
|
||||
import type { ISmtpServerOptions } from '../interfaces.js';
|
||||
/**
|
||||
* Formats a multi-line SMTP response according to RFC 5321
|
||||
* @param code - Response code
|
||||
* @param lines - Response lines
|
||||
* @returns Formatted SMTP response
|
||||
*/
|
||||
export declare function formatMultilineResponse(code: number, lines: string[]): string;
|
||||
/**
|
||||
* Generates a unique session ID
|
||||
* @returns Unique session ID
|
||||
*/
|
||||
export declare function generateSessionId(): string;
|
||||
/**
|
||||
* Safely parses an integer from string with a default value
|
||||
* @param value - String value to parse
|
||||
* @param defaultValue - Default value if parsing fails
|
||||
* @returns Parsed integer or default value
|
||||
*/
|
||||
export declare function safeParseInt(value: string | undefined, defaultValue: number): number;
|
||||
/**
|
||||
* Safely gets the socket details
|
||||
* @param socket - Socket to get details from
|
||||
* @returns Socket details object
|
||||
*/
|
||||
export declare function getSocketDetails(socket: plugins.net.Socket | plugins.tls.TLSSocket): {
|
||||
remoteAddress: string;
|
||||
remotePort: number;
|
||||
remoteFamily: string;
|
||||
localAddress: string;
|
||||
localPort: number;
|
||||
encrypted: boolean;
|
||||
};
|
||||
/**
|
||||
* Gets TLS details if socket is TLS
|
||||
* @param socket - Socket to get TLS details from
|
||||
* @returns TLS details or undefined if not TLS
|
||||
*/
|
||||
export declare function getTlsDetails(socket: plugins.net.Socket | plugins.tls.TLSSocket): {
|
||||
protocol?: string;
|
||||
cipher?: string;
|
||||
authorized?: boolean;
|
||||
} | undefined;
|
||||
/**
|
||||
* Merges default options with provided options
|
||||
* @param options - User provided options
|
||||
* @returns Merged options with defaults
|
||||
*/
|
||||
export declare function mergeWithDefaults(options: Partial<ISmtpServerOptions>): ISmtpServerOptions;
|
||||
/**
|
||||
* Creates a text response formatter for the SMTP server
|
||||
* @param socket - Socket to send responses to
|
||||
* @returns Function to send formatted response
|
||||
*/
|
||||
export declare function createResponseFormatter(socket: plugins.net.Socket | plugins.tls.TLSSocket): (response: string) => void;
|
||||
/**
|
||||
* Extracts SMTP command name from a command line
|
||||
* @param commandLine - Full command line
|
||||
* @returns Command name in uppercase
|
||||
*/
|
||||
export declare function extractCommandName(commandLine: string): string;
|
||||
/**
|
||||
* Extracts SMTP command arguments from a command line
|
||||
* @param commandLine - Full command line
|
||||
* @returns Arguments string
|
||||
*/
|
||||
export declare function extractCommandArgs(commandLine: string): string;
|
||||
/**
|
||||
* Sanitizes data for logging (hides sensitive info)
|
||||
* @param data - Data to sanitize
|
||||
* @returns Sanitized data
|
||||
*/
|
||||
export declare function sanitizeForLogging(data: any): any;
|
||||
208
dist_ts/mail/delivery/smtpserver/utils/helpers.js
Normal file
208
dist_ts/mail/delivery/smtpserver/utils/helpers.js
Normal file
File diff suppressed because one or more lines are too long
106
dist_ts/mail/delivery/smtpserver/utils/logging.d.ts
vendored
Normal file
106
dist_ts/mail/delivery/smtpserver/utils/logging.d.ts
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* SMTP Logging Utilities
|
||||
* Provides structured logging for SMTP server components
|
||||
*/
|
||||
import * as plugins from '../../../../plugins.js';
|
||||
import { SecurityLogLevel, SecurityEventType } from '../constants.js';
|
||||
import type { ISmtpSession } from '../interfaces.js';
|
||||
/**
|
||||
* 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 declare class SmtpLogger {
|
||||
/**
|
||||
* Log a message with context
|
||||
* @param level - Log level
|
||||
* @param message - Log message
|
||||
* @param options - Additional log options
|
||||
*/
|
||||
static log(level: LogLevel, message: string, options?: ISmtpLogOptions): void;
|
||||
/**
|
||||
* Log debug level message
|
||||
* @param message - Log message
|
||||
* @param options - Additional log options
|
||||
*/
|
||||
static debug(message: string, options?: ISmtpLogOptions): void;
|
||||
/**
|
||||
* Log info level message
|
||||
* @param message - Log message
|
||||
* @param options - Additional log options
|
||||
*/
|
||||
static info(message: string, options?: ISmtpLogOptions): void;
|
||||
/**
|
||||
* Log warning level message
|
||||
* @param message - Log message
|
||||
* @param options - Additional log options
|
||||
*/
|
||||
static warn(message: string, options?: ISmtpLogOptions): void;
|
||||
/**
|
||||
* Log error level message
|
||||
* @param message - Log message
|
||||
* @param options - Additional log options
|
||||
*/
|
||||
static error(message: string, options?: ISmtpLogOptions): void;
|
||||
/**
|
||||
* Log command received from client
|
||||
* @param command - The command string
|
||||
* @param socket - The client socket
|
||||
* @param session - The SMTP session
|
||||
*/
|
||||
static logCommand(command: string, socket: plugins.net.Socket | plugins.tls.TLSSocket, session?: ISmtpSession): void;
|
||||
/**
|
||||
* Log response sent to client
|
||||
* @param response - The response string
|
||||
* @param socket - The client socket
|
||||
*/
|
||||
static logResponse(response: string, socket: plugins.net.Socket | plugins.tls.TLSSocket): void;
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
static logConnection(socket: plugins.net.Socket | plugins.tls.TLSSocket, eventType: 'connect' | 'close' | 'error', session?: ISmtpSession, error?: Error): void;
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
static logSecurityEvent(level: SecurityLogLevel, type: SecurityEventType, message: string, details: Record<string, any>, ipAddress?: string, domain?: string, success?: boolean): void;
|
||||
}
|
||||
/**
|
||||
* Default instance for backward compatibility
|
||||
*/
|
||||
export declare const smtpLogger: typeof SmtpLogger;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user