2025-05-23 19:09:30 +00:00
|
|
|
|
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
2025-05-23 19:03:44 +00:00
|
|
|
|
import * as net from 'net';
|
2025-05-24 00:23:35 +00:00
|
|
|
|
import { startTestServer, stopTestServer, type ITestServer } from '../../helpers/server.loader.js';
|
2025-05-23 19:03:44 +00:00
|
|
|
|
|
2025-05-24 00:23:35 +00:00
|
|
|
|
const TEST_PORT = 30050;
|
|
|
|
|
|
|
|
|
|
let testServer: ITestServer;
|
2025-05-23 19:03:44 +00:00
|
|
|
|
|
|
|
|
|
tap.test('setup - start test server', async () => {
|
2025-05-24 00:23:35 +00:00
|
|
|
|
testServer = await startTestServer({ port: TEST_PORT, hostname: 'localhost' });
|
|
|
|
|
expect(testServer).toBeDefined();
|
2025-05-23 19:03:44 +00:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
tap.test('Special Character Handling - Comprehensive Unicode test', async (tools) => {
|
|
|
|
|
const done = tools.defer();
|
|
|
|
|
|
|
|
|
|
const socket = net.createConnection({
|
|
|
|
|
host: 'localhost',
|
|
|
|
|
port: TEST_PORT,
|
|
|
|
|
timeout: 30000
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let dataBuffer = '';
|
|
|
|
|
let step = 'greeting';
|
|
|
|
|
let completed = false;
|
|
|
|
|
|
|
|
|
|
socket.on('data', (data) => {
|
|
|
|
|
dataBuffer += data.toString();
|
|
|
|
|
console.log('Server response:', data.toString());
|
|
|
|
|
|
|
|
|
|
if (step === 'greeting' && dataBuffer.includes('220 ')) {
|
|
|
|
|
step = 'ehlo';
|
|
|
|
|
socket.write('EHLO testclient\r\n');
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'ehlo' && dataBuffer.includes('250')) {
|
|
|
|
|
step = 'mail';
|
|
|
|
|
socket.write('MAIL FROM:<sender@example.com>\r\n');
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'mail' && dataBuffer.includes('250')) {
|
|
|
|
|
step = 'rcpt';
|
|
|
|
|
socket.write('RCPT TO:<recipient@example.com>\r\n');
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'rcpt' && dataBuffer.includes('250')) {
|
|
|
|
|
step = 'data';
|
|
|
|
|
socket.write('DATA\r\n');
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'data' && dataBuffer.includes('354')) {
|
|
|
|
|
const email = [
|
|
|
|
|
`From: sender@example.com`,
|
|
|
|
|
`To: recipient@example.com`,
|
|
|
|
|
`Subject: Special Character Test - Unicode & Symbols ñáéíóú`,
|
|
|
|
|
`Date: ${new Date().toUTCString()}`,
|
|
|
|
|
`Message-ID: <special-chars-${Date.now()}@example.com>`,
|
|
|
|
|
`MIME-Version: 1.0`,
|
|
|
|
|
`Content-Type: text/plain; charset=utf-8`,
|
|
|
|
|
`Content-Transfer-Encoding: 8bit`,
|
|
|
|
|
'',
|
|
|
|
|
'This email tests special character handling:',
|
|
|
|
|
'',
|
|
|
|
|
'=== UNICODE CHARACTERS ===',
|
|
|
|
|
'Accented letters: àáâãäåæçèéêëìíîïñòóôõöøùúûüý',
|
|
|
|
|
'German umlauts: äöüÄÖÜß',
|
|
|
|
|
'Scandinavian: åäöÅÄÖ',
|
|
|
|
|
'French: àâéèêëïîôœùûüÿç',
|
|
|
|
|
'Spanish: ñáéíóúü¿¡',
|
|
|
|
|
'Polish: ąćęłńóśźż',
|
|
|
|
|
'Russian: абвгдеёжзийклмнопрстуфхцчшщъыьэюя',
|
|
|
|
|
'Greek: αβγδεζηθικλμνξοπρστυφχψω',
|
|
|
|
|
'Arabic: العربية',
|
|
|
|
|
'Hebrew: עברית',
|
|
|
|
|
'Chinese: 中文测试',
|
|
|
|
|
'Japanese: 日本語テスト',
|
|
|
|
|
'Korean: 한국어 테스트',
|
|
|
|
|
'Thai: ภาษาไทย',
|
|
|
|
|
'',
|
|
|
|
|
'=== MATHEMATICAL SYMBOLS ===',
|
|
|
|
|
'Math: ∑∏∫∆∇∂∞±×÷≠≤≥≈∝∪∩⊂⊃∈∀∃',
|
|
|
|
|
'Greek letters: αβγδεζηθικλμνξοπρστυφχψω',
|
|
|
|
|
'Arrows: ←→↑↓↔↕⇐⇒⇑⇓⇔⇕',
|
|
|
|
|
'',
|
|
|
|
|
'=== CURRENCY & SYMBOLS ===',
|
|
|
|
|
'Currency: $€£¥¢₹₽₩₪₫₨₦₡₵₴₸₼₲₱',
|
|
|
|
|
'Symbols: ©®™§¶†‡•…‰‱°℃℉№',
|
2025-05-24 01:00:30 +00:00
|
|
|
|
`Punctuation: «»""''‚„‹›–—―‖‗''""‚„…‰′″‴‵‶‷‸‹›※‼‽⁇⁈⁉⁏⁐⁑⁒⁓⁔⁕⁖⁗⁘⁙⁚⁛⁜⁝⁞`,
|
2025-05-23 19:03:44 +00:00
|
|
|
|
'',
|
|
|
|
|
'=== EMOJI & SYMBOLS ===',
|
|
|
|
|
'Common: ☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓☔☕☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷',
|
|
|
|
|
'Smileys: ☺☻☹☿♀♁♂♃♄♅♆♇',
|
|
|
|
|
'Hearts: ♡♢♣♤♥♦♧♨♩♪♫♬♭♮♯',
|
|
|
|
|
'',
|
|
|
|
|
'=== SPECIAL FORMATTING ===',
|
|
|
|
|
'Zero-width chars: ',
|
|
|
|
|
'Combining: e̊åa̋o̧ç',
|
|
|
|
|
'Ligatures: fffiflffifflſtst',
|
|
|
|
|
'Fractions: ½⅓⅔¼¾⅛⅜⅝⅞',
|
|
|
|
|
'Superscript: ⁰¹²³⁴⁵⁶⁷⁸⁹',
|
|
|
|
|
'Subscript: ₀₁₂₃₄₅₆₇₈₉',
|
|
|
|
|
'',
|
|
|
|
|
'End of special character test.',
|
|
|
|
|
'.',
|
|
|
|
|
''
|
|
|
|
|
].join('\r\n');
|
|
|
|
|
|
|
|
|
|
console.log('Sending email with comprehensive Unicode characters');
|
|
|
|
|
socket.write(email);
|
|
|
|
|
step = 'sent';
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'sent' && dataBuffer.includes('250 ') && dataBuffer.includes('message queued')) {
|
|
|
|
|
if (!completed) {
|
|
|
|
|
completed = true;
|
|
|
|
|
console.log('Email with special characters accepted successfully');
|
2025-05-23 21:20:39 +00:00
|
|
|
|
expect(true).toEqual(true);
|
2025-05-23 19:03:44 +00:00
|
|
|
|
|
|
|
|
|
socket.write('QUIT\r\n');
|
|
|
|
|
socket.end();
|
|
|
|
|
done.resolve();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
socket.on('error', (err) => {
|
|
|
|
|
console.error('Socket error:', err);
|
|
|
|
|
done.reject(err);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await done.promise;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
tap.test('Special Character Handling - Control characters', async (tools) => {
|
|
|
|
|
const done = tools.defer();
|
|
|
|
|
|
|
|
|
|
const socket = net.createConnection({
|
|
|
|
|
host: 'localhost',
|
|
|
|
|
port: TEST_PORT,
|
|
|
|
|
timeout: 30000
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let dataBuffer = '';
|
|
|
|
|
let step = 'greeting';
|
|
|
|
|
let completed = false;
|
|
|
|
|
|
|
|
|
|
socket.on('data', (data) => {
|
|
|
|
|
dataBuffer += data.toString();
|
|
|
|
|
console.log('Server response:', data.toString());
|
|
|
|
|
|
|
|
|
|
if (step === 'greeting' && dataBuffer.includes('220 ')) {
|
|
|
|
|
step = 'ehlo';
|
|
|
|
|
socket.write('EHLO testclient\r\n');
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'ehlo' && dataBuffer.includes('250')) {
|
|
|
|
|
step = 'mail';
|
|
|
|
|
socket.write('MAIL FROM:<sender@example.com>\r\n');
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'mail' && dataBuffer.includes('250')) {
|
|
|
|
|
step = 'rcpt';
|
|
|
|
|
socket.write('RCPT TO:<recipient@example.com>\r\n');
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'rcpt' && dataBuffer.includes('250')) {
|
|
|
|
|
step = 'data';
|
|
|
|
|
socket.write('DATA\r\n');
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'data' && dataBuffer.includes('354')) {
|
|
|
|
|
const email = [
|
|
|
|
|
`From: sender@example.com`,
|
|
|
|
|
`To: recipient@example.com`,
|
|
|
|
|
`Subject: Control Character Test`,
|
|
|
|
|
`Date: ${new Date().toUTCString()}`,
|
|
|
|
|
`Message-ID: <control-chars-${Date.now()}@example.com>`,
|
|
|
|
|
`MIME-Version: 1.0`,
|
|
|
|
|
`Content-Type: text/plain; charset=utf-8`,
|
|
|
|
|
'',
|
|
|
|
|
'=== CONTROL CHARACTERS TEST ===',
|
|
|
|
|
'Tab character: (between words)',
|
|
|
|
|
'Non-breaking space: word word',
|
|
|
|
|
'Soft hyphen: supercalifragilisticexpialidocious',
|
|
|
|
|
'Vertical tab: word\x0Bword',
|
|
|
|
|
'Form feed: word\x0Cword',
|
|
|
|
|
'Backspace: word\x08word',
|
|
|
|
|
'',
|
|
|
|
|
'=== LINE ENDING TESTS ===',
|
|
|
|
|
'Unix LF: Line1\nLine2',
|
|
|
|
|
'Windows CRLF: Line3\r\nLine4',
|
|
|
|
|
'Mac CR: Line5\rLine6',
|
|
|
|
|
'',
|
|
|
|
|
'=== BOUNDARY CHARACTERS ===',
|
|
|
|
|
'SMTP boundary test: . (dot at start)',
|
|
|
|
|
'Double dots: .. (escaped in SMTP)',
|
|
|
|
|
'CRLF.CRLF sequence test',
|
|
|
|
|
'.',
|
|
|
|
|
''
|
|
|
|
|
].join('\r\n');
|
|
|
|
|
|
|
|
|
|
socket.write(email);
|
|
|
|
|
step = 'sent';
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'sent' && dataBuffer.includes('250 ') && dataBuffer.includes('message queued')) {
|
|
|
|
|
if (!completed) {
|
|
|
|
|
completed = true;
|
|
|
|
|
console.log('Email with control characters accepted');
|
2025-05-23 21:20:39 +00:00
|
|
|
|
expect(true).toEqual(true);
|
2025-05-23 19:03:44 +00:00
|
|
|
|
|
|
|
|
|
socket.write('QUIT\r\n');
|
|
|
|
|
socket.end();
|
|
|
|
|
done.resolve();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
socket.on('error', (err) => {
|
|
|
|
|
console.error('Socket error:', err);
|
|
|
|
|
done.reject(err);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await done.promise;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
tap.test('Special Character Handling - Subject header encoding', async (tools) => {
|
|
|
|
|
const done = tools.defer();
|
|
|
|
|
|
|
|
|
|
const socket = net.createConnection({
|
|
|
|
|
host: 'localhost',
|
|
|
|
|
port: TEST_PORT,
|
|
|
|
|
timeout: 30000
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let dataBuffer = '';
|
|
|
|
|
let step = 'greeting';
|
|
|
|
|
let completed = false;
|
|
|
|
|
|
|
|
|
|
socket.on('data', (data) => {
|
|
|
|
|
dataBuffer += data.toString();
|
|
|
|
|
console.log('Server response:', data.toString());
|
|
|
|
|
|
|
|
|
|
if (step === 'greeting' && dataBuffer.includes('220 ')) {
|
|
|
|
|
step = 'ehlo';
|
|
|
|
|
socket.write('EHLO testclient\r\n');
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'ehlo' && dataBuffer.includes('250')) {
|
|
|
|
|
step = 'mail';
|
|
|
|
|
socket.write('MAIL FROM:<sender@example.com>\r\n');
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'mail' && dataBuffer.includes('250')) {
|
|
|
|
|
step = 'rcpt';
|
|
|
|
|
socket.write('RCPT TO:<recipient@example.com>\r\n');
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'rcpt' && dataBuffer.includes('250')) {
|
|
|
|
|
step = 'data';
|
|
|
|
|
socket.write('DATA\r\n');
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'data' && dataBuffer.includes('354')) {
|
|
|
|
|
const email = [
|
|
|
|
|
`From: sender@example.com`,
|
|
|
|
|
`To: recipient@example.com`,
|
|
|
|
|
`Subject: =?UTF-8?B?8J+YgCBFbW9qaSBpbiBTdWJqZWN0IOKcqCDwn4yI?=`,
|
|
|
|
|
`Subject: =?UTF-8?Q?Quoted=2DPrintable=20Subject=20=C3=A1=C3=A9=C3=AD=C3=B3=C3=BA?=`,
|
|
|
|
|
`Date: ${new Date().toUTCString()}`,
|
|
|
|
|
`Message-ID: <encoded-subject-${Date.now()}@example.com>`,
|
|
|
|
|
'',
|
|
|
|
|
'Testing encoded subject headers with special characters.',
|
|
|
|
|
'.',
|
|
|
|
|
''
|
|
|
|
|
].join('\r\n');
|
|
|
|
|
|
|
|
|
|
socket.write(email);
|
|
|
|
|
step = 'sent';
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'sent' && dataBuffer.includes('250 ') && dataBuffer.includes('message queued')) {
|
|
|
|
|
if (!completed) {
|
|
|
|
|
completed = true;
|
|
|
|
|
console.log('Email with encoded subject headers accepted');
|
2025-05-23 21:20:39 +00:00
|
|
|
|
expect(true).toEqual(true);
|
2025-05-23 19:03:44 +00:00
|
|
|
|
|
|
|
|
|
socket.write('QUIT\r\n');
|
|
|
|
|
socket.end();
|
|
|
|
|
done.resolve();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
socket.on('error', (err) => {
|
|
|
|
|
console.error('Socket error:', err);
|
|
|
|
|
done.reject(err);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await done.promise;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
tap.test('Special Character Handling - Address headers with special chars', async (tools) => {
|
|
|
|
|
const done = tools.defer();
|
|
|
|
|
|
|
|
|
|
const socket = net.createConnection({
|
|
|
|
|
host: 'localhost',
|
|
|
|
|
port: TEST_PORT,
|
|
|
|
|
timeout: 30000
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let dataBuffer = '';
|
|
|
|
|
let step = 'greeting';
|
|
|
|
|
let completed = false;
|
|
|
|
|
|
|
|
|
|
socket.on('data', (data) => {
|
|
|
|
|
dataBuffer += data.toString();
|
|
|
|
|
console.log('Server response:', data.toString());
|
|
|
|
|
|
|
|
|
|
if (step === 'greeting' && dataBuffer.includes('220 ')) {
|
|
|
|
|
step = 'ehlo';
|
|
|
|
|
socket.write('EHLO testclient\r\n');
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'ehlo' && dataBuffer.includes('250')) {
|
|
|
|
|
step = 'mail';
|
|
|
|
|
socket.write('MAIL FROM:<sender@example.com>\r\n');
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'mail' && dataBuffer.includes('250')) {
|
|
|
|
|
step = 'rcpt';
|
|
|
|
|
socket.write('RCPT TO:<recipient@example.com>\r\n');
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'rcpt' && dataBuffer.includes('250')) {
|
|
|
|
|
step = 'data';
|
|
|
|
|
socket.write('DATA\r\n');
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'data' && dataBuffer.includes('354')) {
|
|
|
|
|
const email = [
|
|
|
|
|
`From: "José García" <jose@example.com>`,
|
|
|
|
|
`To: "François Müller" <francois@example.com>, "北京用户" <beijing@example.com>`,
|
|
|
|
|
`Cc: =?UTF-8?B?IkFubmEgw4XDpMO2Ig==?= <anna@example.com>`,
|
|
|
|
|
`Reply-To: "Søren Ñoño" <soren@example.com>`,
|
|
|
|
|
`Subject: Special names in address headers`,
|
|
|
|
|
`Date: ${new Date().toUTCString()}`,
|
|
|
|
|
`Message-ID: <special-addrs-${Date.now()}@example.com>`,
|
|
|
|
|
'',
|
|
|
|
|
'Testing special characters in email addresses and display names.',
|
|
|
|
|
'.',
|
|
|
|
|
''
|
|
|
|
|
].join('\r\n');
|
|
|
|
|
|
|
|
|
|
socket.write(email);
|
|
|
|
|
step = 'sent';
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'sent' && dataBuffer.includes('250 ') && dataBuffer.includes('message queued')) {
|
|
|
|
|
if (!completed) {
|
|
|
|
|
completed = true;
|
|
|
|
|
console.log('Email with special characters in addresses accepted');
|
2025-05-23 21:20:39 +00:00
|
|
|
|
expect(true).toEqual(true);
|
2025-05-23 19:03:44 +00:00
|
|
|
|
|
|
|
|
|
socket.write('QUIT\r\n');
|
|
|
|
|
socket.end();
|
|
|
|
|
done.resolve();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
socket.on('error', (err) => {
|
|
|
|
|
console.error('Socket error:', err);
|
|
|
|
|
done.reject(err);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await done.promise;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
tap.test('Special Character Handling - Mixed encodings', async (tools) => {
|
|
|
|
|
const done = tools.defer();
|
|
|
|
|
|
|
|
|
|
const socket = net.createConnection({
|
|
|
|
|
host: 'localhost',
|
|
|
|
|
port: TEST_PORT,
|
|
|
|
|
timeout: 30000
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let dataBuffer = '';
|
|
|
|
|
let step = 'greeting';
|
|
|
|
|
let completed = false;
|
|
|
|
|
|
|
|
|
|
socket.on('data', (data) => {
|
|
|
|
|
dataBuffer += data.toString();
|
|
|
|
|
console.log('Server response:', data.toString());
|
|
|
|
|
|
|
|
|
|
if (step === 'greeting' && dataBuffer.includes('220 ')) {
|
|
|
|
|
step = 'ehlo';
|
|
|
|
|
socket.write('EHLO testclient\r\n');
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'ehlo' && dataBuffer.includes('250')) {
|
|
|
|
|
step = 'mail';
|
|
|
|
|
socket.write('MAIL FROM:<sender@example.com>\r\n');
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'mail' && dataBuffer.includes('250')) {
|
|
|
|
|
step = 'rcpt';
|
|
|
|
|
socket.write('RCPT TO:<recipient@example.com>\r\n');
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'rcpt' && dataBuffer.includes('250')) {
|
|
|
|
|
step = 'data';
|
|
|
|
|
socket.write('DATA\r\n');
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'data' && dataBuffer.includes('354')) {
|
|
|
|
|
const boundary = 'mixed-encoding-boundary';
|
|
|
|
|
|
|
|
|
|
const email = [
|
|
|
|
|
`From: sender@example.com`,
|
|
|
|
|
`To: recipient@example.com`,
|
|
|
|
|
`Subject: Mixed Encoding Test`,
|
|
|
|
|
`Date: ${new Date().toUTCString()}`,
|
|
|
|
|
`Message-ID: <mixed-enc-${Date.now()}@example.com>`,
|
|
|
|
|
`MIME-Version: 1.0`,
|
|
|
|
|
`Content-Type: multipart/mixed; boundary="${boundary}"`,
|
|
|
|
|
'',
|
|
|
|
|
`--${boundary}`,
|
|
|
|
|
`Content-Type: text/plain; charset=utf-8`,
|
|
|
|
|
`Content-Transfer-Encoding: 8bit`,
|
|
|
|
|
'',
|
|
|
|
|
'UTF-8 part: ñáéíóú 中文 日本語',
|
|
|
|
|
'',
|
|
|
|
|
`--${boundary}`,
|
|
|
|
|
`Content-Type: text/plain; charset=iso-8859-1`,
|
|
|
|
|
`Content-Transfer-Encoding: quoted-printable`,
|
|
|
|
|
'',
|
|
|
|
|
'ISO-8859-1 part: =F1=E1=E9=ED=F3=FA',
|
|
|
|
|
'',
|
|
|
|
|
`--${boundary}`,
|
|
|
|
|
`Content-Type: text/plain; charset=windows-1252`,
|
|
|
|
|
'',
|
|
|
|
|
'Windows-1252 part: €‚ƒ„…†‡',
|
|
|
|
|
'',
|
|
|
|
|
`--${boundary}`,
|
|
|
|
|
`Content-Type: text/plain; charset=utf-16`,
|
|
|
|
|
`Content-Transfer-Encoding: base64`,
|
|
|
|
|
'',
|
|
|
|
|
Buffer.from('UTF-16 text: ñoño', 'utf16le').toString('base64'),
|
|
|
|
|
'',
|
|
|
|
|
`--${boundary}--`,
|
|
|
|
|
'.',
|
|
|
|
|
''
|
|
|
|
|
].join('\r\n');
|
|
|
|
|
|
|
|
|
|
socket.write(email);
|
|
|
|
|
step = 'sent';
|
|
|
|
|
dataBuffer = '';
|
|
|
|
|
} else if (step === 'sent' && dataBuffer.includes('250 ') && dataBuffer.includes('message queued')) {
|
|
|
|
|
if (!completed) {
|
|
|
|
|
completed = true;
|
|
|
|
|
console.log('Email with mixed character encodings accepted');
|
2025-05-23 21:20:39 +00:00
|
|
|
|
expect(true).toEqual(true);
|
2025-05-23 19:03:44 +00:00
|
|
|
|
|
|
|
|
|
socket.write('QUIT\r\n');
|
|
|
|
|
socket.end();
|
|
|
|
|
done.resolve();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
socket.on('error', (err) => {
|
|
|
|
|
console.error('Socket error:', err);
|
|
|
|
|
done.reject(err);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await done.promise;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
tap.test('cleanup - stop test server', async () => {
|
2025-05-24 00:23:35 +00:00
|
|
|
|
await stopTestServer(testServer);
|
|
|
|
|
expect(true).toEqual(true);
|
2025-05-23 19:03:44 +00:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
tap.start();
|