Files
smartmta/dist_ts/mail/delivery/smtpclient/utils/helpers.js

196 lines
13 KiB
JavaScript
Raw Normal View History

2026-02-10 15:54:09 +00:00
/**
* SMTP Client Helper Functions
* Protocol helper functions and utilities
*/
import { SMTP_CODES, REGEX_PATTERNS, LINE_ENDINGS } from '../constants.js';
/**
* Parse SMTP server response
*/
export function parseSmtpResponse(data) {
const lines = data.trim().split(/\r?\n/);
const firstLine = lines[0];
const match = firstLine.match(REGEX_PATTERNS.RESPONSE_CODE);
if (!match) {
return {
code: 500,
message: 'Invalid server response',
raw: data
};
}
const code = parseInt(match[1], 10);
const separator = match[2];
const message = lines.map(line => line.substring(4)).join(' ');
// Check for enhanced status code
const enhancedMatch = message.match(REGEX_PATTERNS.ENHANCED_STATUS);
const enhancedCode = enhancedMatch ? enhancedMatch[1] : undefined;
return {
code,
message: enhancedCode ? message.substring(enhancedCode.length + 1) : message,
enhancedCode,
raw: data
};
}
/**
* Parse EHLO response and extract capabilities
*/
export function parseEhloResponse(response) {
const lines = response.trim().split(/\r?\n/);
const capabilities = {
extensions: new Set(),
authMethods: new Set(),
pipelining: false,
starttls: false,
eightBitMime: false
};
for (const line of lines.slice(1)) { // Skip first line (greeting)
const extensionLine = line.substring(4); // Remove "250-" or "250 "
const parts = extensionLine.split(/\s+/);
const extension = parts[0].toUpperCase();
capabilities.extensions.add(extension);
switch (extension) {
case 'PIPELINING':
capabilities.pipelining = true;
break;
case 'STARTTLS':
capabilities.starttls = true;
break;
case '8BITMIME':
capabilities.eightBitMime = true;
break;
case 'SIZE':
if (parts[1]) {
capabilities.maxSize = parseInt(parts[1], 10);
}
break;
case 'AUTH':
// Parse authentication methods
for (let i = 1; i < parts.length; i++) {
capabilities.authMethods.add(parts[i].toUpperCase());
}
break;
}
}
return capabilities;
}
/**
* Format SMTP command with proper line ending
*/
export function formatCommand(command, ...args) {
const fullCommand = args.length > 0 ? `${command} ${args.join(' ')}` : command;
return fullCommand + LINE_ENDINGS.CRLF;
}
/**
* Encode authentication string for AUTH PLAIN
*/
export function encodeAuthPlain(username, password) {
const authString = `\0${username}\0${password}`;
return Buffer.from(authString, 'utf8').toString('base64');
}
/**
* Encode authentication string for AUTH LOGIN
*/
export function encodeAuthLogin(value) {
return Buffer.from(value, 'utf8').toString('base64');
}
/**
* Generate OAuth2 authentication string
*/
export function generateOAuth2String(username, accessToken) {
const authString = `user=${username}\x01auth=Bearer ${accessToken}\x01\x01`;
return Buffer.from(authString, 'utf8').toString('base64');
}
/**
* Check if response code indicates success
*/
export function isSuccessCode(code) {
return code >= 200 && code < 300;
}
/**
* Check if response code indicates temporary failure
*/
export function isTemporaryFailure(code) {
return code >= 400 && code < 500;
}
/**
* Check if response code indicates permanent failure
*/
export function isPermanentFailure(code) {
return code >= 500;
}
/**
* Escape email address for SMTP commands
*/
export function escapeEmailAddress(email) {
return `<${email.trim()}>`;
}
/**
* Extract email address from angle brackets
*/
export function extractEmailAddress(email) {
const match = email.match(/^<(.+)>$/);
return match ? match[1] : email.trim();
}
/**
* Generate unique connection ID
*/
export function generateConnectionId() {
return `smtp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
/**
* Format timeout duration for human readability
*/
export function formatTimeout(milliseconds) {
if (milliseconds < 1000) {
return `${milliseconds}ms`;
}
else if (milliseconds < 60000) {
return `${Math.round(milliseconds / 1000)}s`;
}
else {
return `${Math.round(milliseconds / 60000)}m`;
}
}
/**
* Validate and normalize email data size
*/
export function validateEmailSize(emailData, maxSize) {
const size = Buffer.byteLength(emailData, 'utf8');
return !maxSize || size <= maxSize;
}
/**
* Clean sensitive data from logs
*/
export function sanitizeForLogging(data) {
if (typeof data !== 'object' || data === null) {
return data;
}
const sanitized = { ...data };
const sensitiveFields = ['password', 'pass', 'accessToken', 'refreshToken', 'clientSecret'];
for (const field of sensitiveFields) {
if (field in sanitized) {
sanitized[field] = '[REDACTED]';
}
}
return sanitized;
}
/**
* Calculate exponential backoff delay
*/
export function calculateBackoffDelay(attempt, baseDelay = 1000) {
return Math.min(baseDelay * Math.pow(2, attempt - 1), 30000); // Max 30 seconds
}
/**
* Parse enhanced status code
*/
export function parseEnhancedStatusCode(code) {
const match = code.match(/^(\d)\.(\d)\.(\d)$/);
if (!match) {
return null;
}
return {
class: parseInt(match[1], 10),
subject: parseInt(match[2], 10),
detail: parseInt(match[3], 10)
};
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVscGVycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3RzL21haWwvZGVsaXZlcnkvc210cGNsaWVudC91dGlscy9oZWxwZXJzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUVILE9BQU8sRUFBRSxVQUFVLEVBQUUsY0FBYyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBRzNFOztHQUVHO0FBQ0gsTUFBTSxVQUFVLGlCQUFpQixDQUFDLElBQVk7SUFDNUMsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUN6QyxNQUFNLFNBQVMsR0FBRyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDM0IsTUFBTSxLQUFLLEdBQUcsU0FBUyxDQUFDLEtBQUssQ0FBQyxjQUFjLENBQUMsYUFBYSxDQUFDLENBQUM7SUFFNUQsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ1gsT0FBTztZQUNMLElBQUksRUFBRSxHQUFHO1lBQ1QsT0FBTyxFQUFFLHlCQUF5QjtZQUNsQyxHQUFHLEVBQUUsSUFBSTtTQUNWLENBQUM7SUFDSixDQUFDO0lBRUQsTUFBTSxJQUFJLEdBQUcsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUNwQyxNQUFNLFNBQVMsR0FBRyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDM0IsTUFBTSxPQUFPLEdBQUcsS0FBSyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7SUFFL0QsaUNBQWlDO0lBQ2pDLE1BQU0sYUFBYSxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUMsY0FBYyxDQUFDLGVBQWUsQ0FBQyxDQUFDO0lBQ3BFLE1B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