librelad 875a60f90f LibrePortal v0.1.0 — initial release
A free, open, self-hosted app platform (GNU AGPLv3): one-click app deploys,
Traefik reverse proxy with automatic SSL, rootless Docker support, gluetun
VPN routing, and a web dashboard to manage it all.

Free & open forever to self-host; optional paid hosted services fund it.
See PROMISE.md.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-21 20:37:54 +01:00

143 lines
7.1 KiB
JavaScript
Executable File

const net = require('net');
/* =========================
Mail Connection Test
========================= */
async function testConnection(req, res) {
try {
const { host, port, secure, username, password, from } = req.body;
// Validate required fields
if (!host || !port || !username || !password) {
return res.status(400).json({
success: false,
message: 'Missing required fields: host, port, username, password'
});
}
const result = await new Promise((resolve, reject) => {
const socket = new net.Socket();
let response = '';
let connected = false;
socket.connect(parseInt(port), host, () => {
connected = true;
//console.log(`Connected to ${host}:${port}`);
// Wait for SMTP greeting
socket.once('data', (data) => {
response = data.toString();
//console.log('SMTP greeting:', response.trim());
if (response.startsWith('220')) {
// Send EHLO
socket.write('EHLO test.example.com\r\n');
// Wait for EHLO response
socket.once('data', (ehloData) => {
const ehloResponse = ehloData.toString();
//console.log('EHLO response:', ehloResponse.trim());
if (ehloResponse.startsWith('250')) {
// Try to authenticate if username/password provided
if (username && password) {
socket.write(`AUTH LOGIN\r\n`);
socket.once('data', (authChallenge) => {
const authResponse = authChallenge.toString();
//console.log('AUTH response:', authResponse.trim());
if (authResponse.startsWith('334')) {
// Send username (base64 encoded)
const usernameB64 = Buffer.from(username).toString('base64');
socket.write(usernameB64 + '\r\n');
socket.once('data', (userChallenge) => {
const userResponse = userChallenge.toString();
//console.log('Username response:', userResponse.trim());
if (userResponse.startsWith('334')) {
// Send password (base64 encoded)
const passwordB64 = Buffer.from(password).toString('base64');
socket.write(passwordB64 + '\r\n');
socket.once('data', (passResponse) => {
const passResult = passResponse.toString();
//console.log('Password response:', passResult.trim());
if (passResult.startsWith('235')) {
resolve({
success: true,
message: `Successfully connected and authenticated to ${host}:${port} with ${secure || 'none'} security`
});
} else {
reject(new Error('Authentication failed: Invalid username or password'));
}
socket.end();
});
} else {
reject(new Error('Authentication failed: Invalid username'));
socket.end();
}
});
} else {
reject(new Error('Server does not support authentication'));
socket.end();
}
});
} else {
resolve({
success: true,
message: `Successfully connected to ${host}:${port} with ${secure || 'none'} security (no authentication tested)`
});
socket.end();
}
} else {
reject(new Error('EHLO command failed'));
socket.end();
}
});
} else {
reject(new Error('Server did not send proper SMTP greeting'));
socket.end();
}
});
});
socket.on('error', (err) => {
if (!connected) {
reject(new Error(`Connection failed: ${err.message}`));
} else {
reject(new Error(`SMTP error: ${err.message}`));
}
});
socket.setTimeout(10000, () => {
socket.destroy();
reject(new Error('Connection timeout'));
});
});
res.json(result);
} catch (error) {
console.error('Mail connection test error:', error);
res.status(500).json({
success: false,
message: error.message || 'Mail connection test failed',
details: error.stack || 'No stack trace available',
error: error.toString(),
config: {
host: host,
port: port,
secure: secure,
username: username ? '[REDACTED]' : 'not provided',
password: password ? '[REDACTED]' : 'not provided',
from: from
}
});
}
}
module.exports = { testConnection };