Files
smartmta/dist_ts/mail/delivery/smtpserver/utils/helpers.js
2026-02-10 15:54:09 +00:00

208 lines
14 KiB
JavaScript

/**
* SMTP Helper Functions
* Provides utility functions for SMTP server implementation
*/
import * as plugins from '../../../../plugins.js';
import { SMTP_DEFAULTS } from '../constants.js';
/**
* Formats a multi-line SMTP response according to RFC 5321
* @param code - Response code
* @param lines - Response lines
* @returns Formatted SMTP response
*/
export function formatMultilineResponse(code, lines) {
if (!lines || lines.length === 0) {
return `${code} `;
}
if (lines.length === 1) {
return `${code} ${lines[0]}`;
}
let response = '';
for (let i = 0; i < lines.length - 1; i++) {
response += `${code}-${lines[i]}${SMTP_DEFAULTS.CRLF}`;
}
response += `${code} ${lines[lines.length - 1]}`;
return response;
}
/**
* Generates a unique session ID
* @returns Unique session ID
*/
export function generateSessionId() {
return `${Date.now()}-${Math.floor(Math.random() * 10000)}`;
}
/**
* 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 function safeParseInt(value, defaultValue) {
if (!value) {
return defaultValue;
}
const parsed = parseInt(value, 10);
return isNaN(parsed) ? defaultValue : parsed;
}
/**
* Safely gets the socket details
* @param socket - Socket to get details from
* @returns Socket details object
*/
export function getSocketDetails(socket) {
return {
remoteAddress: socket.remoteAddress || 'unknown',
remotePort: socket.remotePort || 0,
remoteFamily: socket.remoteFamily || 'unknown',
localAddress: socket.localAddress || 'unknown',
localPort: socket.localPort || 0,
encrypted: socket instanceof plugins.tls.TLSSocket
};
}
/**
* Gets TLS details if socket is TLS
* @param socket - Socket to get TLS details from
* @returns TLS details or undefined if not TLS
*/
export function getTlsDetails(socket) {
if (!(socket instanceof plugins.tls.TLSSocket)) {
return undefined;
}
return {
protocol: socket.getProtocol(),
cipher: socket.getCipher()?.name,
authorized: socket.authorized
};
}
/**
* Merges default options with provided options
* @param options - User provided options
* @returns Merged options with defaults
*/
export function mergeWithDefaults(options) {
return {
port: options.port || SMTP_DEFAULTS.SMTP_PORT,
key: options.key || '',
cert: options.cert || '',
hostname: options.hostname || SMTP_DEFAULTS.HOSTNAME,
host: options.host,
securePort: options.securePort,
ca: options.ca,
maxSize: options.size || SMTP_DEFAULTS.MAX_MESSAGE_SIZE,
maxConnections: options.maxConnections || SMTP_DEFAULTS.MAX_CONNECTIONS,
socketTimeout: options.socketTimeout || SMTP_DEFAULTS.SOCKET_TIMEOUT,
connectionTimeout: options.connectionTimeout || SMTP_DEFAULTS.CONNECTION_TIMEOUT,
cleanupInterval: options.cleanupInterval || SMTP_DEFAULTS.CLEANUP_INTERVAL,
maxRecipients: options.maxRecipients || SMTP_DEFAULTS.MAX_RECIPIENTS,
size: options.size || SMTP_DEFAULTS.MAX_MESSAGE_SIZE,
dataTimeout: options.dataTimeout || SMTP_DEFAULTS.DATA_TIMEOUT,
auth: options.auth,
};
}
/**
* Creates a text response formatter for the SMTP server
* @param socket - Socket to send responses to
* @returns Function to send formatted response
*/
export function createResponseFormatter(socket) {
return (response) => {
try {
socket.write(`${response}${SMTP_DEFAULTS.CRLF}`);
console.log(`${response}`);
}
catch (error) {
console.error(`Error sending response: ${error instanceof Error ? error.message : String(error)}`);
socket.destroy();
}
};
}
/**
* Extracts SMTP command name from a command line
* @param commandLine - Full command line
* @returns Command name in uppercase
*/
export function extractCommandName(commandLine) {
if (!commandLine || typeof commandLine !== 'string') {
return '';
}
// Handle specific command patterns first
const ehloMatch = commandLine.match(/^(EHLO|HELO)\b/i);
if (ehloMatch) {
return ehloMatch[1].toUpperCase();
}
const mailMatch = commandLine.match(/^MAIL\b/i);
if (mailMatch) {
return 'MAIL';
}
const rcptMatch = commandLine.match(/^RCPT\b/i);
if (rcptMatch) {
return 'RCPT';
}
// Default handling
const parts = commandLine.trim().split(/\s+/);
return (parts[0] || '').toUpperCase();
}
/**
* Extracts SMTP command arguments from a command line
* @param commandLine - Full command line
* @returns Arguments string
*/
export function extractCommandArgs(commandLine) {
if (!commandLine || typeof commandLine !== 'string') {
return '';
}
const command = extractCommandName(commandLine);
if (!command) {
return commandLine.trim();
}
// Special handling for specific commands
if (command === 'EHLO' || command === 'HELO') {
const match = commandLine.match(/^(?:EHLO|HELO)\s+(.+)$/i);
return match ? match[1].trim() : '';
}
if (command === 'MAIL') {
return commandLine.replace(/^MAIL\s+/i, '');
}
if (command === 'RCPT') {
return commandLine.replace(/^RCPT\s+/i, '');
}
// Default extraction
const firstSpace = commandLine.indexOf(' ');
if (firstSpace === -1) {
return '';
}
return commandLine.substring(firstSpace + 1).trim();
}
/**
* Sanitizes data for logging (hides sensitive info)
* @param data - Data to sanitize
* @returns Sanitized data
*/
export function sanitizeForLogging(data) {
if (!data) {
return data;
}
if (typeof data !== 'object') {
return data;
}
const result = Array.isArray(data) ? [] : {};
for (const key in data) {
if (Object.prototype.hasOwnProperty.call(data, key)) {
// Sanitize sensitive fields
if (key.toLowerCase().includes('password') ||
key.toLowerCase().includes('token') ||
key.toLowerCase().includes('secret') ||
key.toLowerCase().includes('credential')) {
result[key] = '********';
}
else if (typeof data[key] === 'object' && data[key] !== null) {
result[key] = sanitizeForLogging(data[key]);
}
else {
result[key] = data[key];
}
}
}
return result;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVscGVycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3RzL21haWwvZGVsaXZlcnkvc210cHNlcnZlci91dGlscy9oZWxwZXJzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUVILE9BQU8sS0FBSyxPQUFPLE1BQU0sd0JBQXdCLENBQUM7QUFDbEQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBR2hEOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLHVCQUF1QixDQUFDLElBQVksRUFBRSxLQUFlO0lBQ25FLElBQUksQ0FBQyxLQUFLLElBQUksS0FBSyxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUNqQyxPQUFPLEdBQUcsSUFBSSxHQUFHLENBQUM7SUFDcEIsQ0FBQztJQUVELElBQUksS0FBSyxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUN2QixPQUFPLEdBQUcsSUFBSSxJQUFJLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO0lBQy9CLENBQUM7SUFFRCxJQUFJLFFBQVEsR0FBRyxFQUFFLENBQUM7SUFDbEIsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLEtBQUssQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDMUMsUUFBUSxJQUFJLEdBQUcsSUFBSSxJQUFJLEtBQUssQ0FBQyxDQUFDLENBQUMsR0FBRyxhQUFhLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDekQsQ0FBQztJQUNELFFBQVEsSUFBSSxHQUFHLElBQUksSUFBSSxLQUFLLENBQUMsS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsRUFBRSxDQUFDO0lBRWpELE9BQU8sUUFBUSxDQUFDO0FBQ2xCLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxNQUFNLFVBQVUsaUJBQWlCO0lBQy9CLE9BQU8sR0FBRyxJQUFJLENBQUMs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