Lisää SMTP-testaustyökalu postilaatikon asetuksiin
Korvaa mailbox_debug-endpoint kattavalla smtp_test-endpointilla, joka testaa yhteyden, TLS:n ja autentikoinnin vaihe vaiheelta. "Testaa SMTP" -nappi lomakkeessa näyttää tulokset. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
157
api.php
157
api.php
@@ -2975,26 +2975,149 @@ switch ($action) {
|
|||||||
echo json_encode($mbs);
|
echo json_encode($mbs);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'mailbox_debug':
|
case 'smtp_test':
|
||||||
requireAdmin();
|
requireAdmin();
|
||||||
$companyId = requireCompany();
|
$companyId = requireCompany();
|
||||||
$mailboxes = dbLoadMailboxes($companyId);
|
$input = json_decode(file_get_contents('php://input'), true);
|
||||||
$debug = array_map(function($mb) {
|
$mailboxId = $input['mailbox_id'] ?? '';
|
||||||
return [
|
$mailbox = dbGetMailbox($mailboxId);
|
||||||
'id' => $mb['id'],
|
if (!$mailbox) {
|
||||||
'nimi' => $mb['nimi'],
|
echo json_encode(['error' => 'Postilaatikkoa ei löydy']);
|
||||||
'imap_host' => $mb['imap_host'] ?? '',
|
break;
|
||||||
'imap_user' => $mb['imap_user'] ?? '',
|
}
|
||||||
'imap_pass_len' => strlen($mb['imap_password'] ?? ''),
|
// Näytä tietokannan raakatiedot
|
||||||
'smtp_host' => $mb['smtp_host'] ?? '',
|
$dbInfo = [
|
||||||
'smtp_port' => $mb['smtp_port'] ?? '',
|
'imap_user' => $mailbox['imap_user'] ?? '',
|
||||||
'smtp_user' => $mb['smtp_user'] ?? '',
|
'imap_pass_len' => strlen($mailbox['imap_password'] ?? ''),
|
||||||
'smtp_pass_len' => strlen($mb['smtp_password'] ?? ''),
|
'smtp_host' => $mailbox['smtp_host'] ?? '',
|
||||||
'smtp_encryption' => $mb['smtp_encryption'] ?? '',
|
'smtp_port' => $mailbox['smtp_port'] ?? '',
|
||||||
'smtp_from_email' => $mb['smtp_from_email'] ?? '',
|
'smtp_user' => $mailbox['smtp_user'] ?? '',
|
||||||
|
'smtp_pass_len' => strlen($mailbox['smtp_password'] ?? ''),
|
||||||
|
'smtp_encryption' => $mailbox['smtp_encryption'] ?? '',
|
||||||
|
'smtp_from_email' => $mailbox['smtp_from_email'] ?? '',
|
||||||
];
|
];
|
||||||
}, $mailboxes);
|
// Laske fallback-arvot
|
||||||
echo json_encode($debug, JSON_PRETTY_PRINT);
|
$effectiveUser = $mailbox['smtp_user'] ?? '';
|
||||||
|
if ($effectiveUser === '') $effectiveUser = $mailbox['imap_user'] ?? '';
|
||||||
|
if ($effectiveUser === '') $effectiveUser = $mailbox['smtp_from_email'] ?? '';
|
||||||
|
$effectivePass = $mailbox['smtp_password'] ?? '';
|
||||||
|
if ($effectivePass === '') $effectivePass = $mailbox['imap_password'] ?? '';
|
||||||
|
$host = $mailbox['smtp_host'] ?? '';
|
||||||
|
$port = (int)($mailbox['smtp_port'] ?? 587);
|
||||||
|
$encryption = $mailbox['smtp_encryption'] ?? 'tls';
|
||||||
|
|
||||||
|
$result = [
|
||||||
|
'db_values' => $dbInfo,
|
||||||
|
'effective_user' => $effectiveUser,
|
||||||
|
'effective_pass_len' => strlen($effectivePass),
|
||||||
|
'steps' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
if (empty($host)) {
|
||||||
|
$result['steps'][] = '❌ SMTP-palvelin puuttuu';
|
||||||
|
echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (empty($effectivePass)) {
|
||||||
|
$result['steps'][] = '❌ Salasana puuttuu (tietokannassa ei SMTP- eikä IMAP-salasanaa)';
|
||||||
|
echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Testaa yhteys
|
||||||
|
$connStr = ($encryption === 'ssl' ? 'ssl' : 'tcp') . "://{$host}:{$port}";
|
||||||
|
$result['steps'][] = "🔌 Yhdistetään: {$connStr}";
|
||||||
|
$ctx = stream_context_create(['ssl' => ['verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true]]);
|
||||||
|
$fp = @stream_socket_client($connStr, $errno, $errstr, 15, STREAM_CLIENT_CONNECT, $ctx);
|
||||||
|
if (!$fp) {
|
||||||
|
$result['steps'][] = "❌ Yhteys epäonnistui: {$errstr} ({$errno})";
|
||||||
|
echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
stream_set_timeout($fp, 15);
|
||||||
|
$result['steps'][] = '✅ Yhteys muodostettu';
|
||||||
|
|
||||||
|
// Banner
|
||||||
|
$resp = smtpReadResponse($fp);
|
||||||
|
$result['steps'][] = '📨 Banner: ' . trim($resp);
|
||||||
|
if (smtpCode($resp) !== '220') {
|
||||||
|
$result['steps'][] = '❌ Virheellinen banner';
|
||||||
|
fclose($fp);
|
||||||
|
echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EHLO
|
||||||
|
$ehlo = smtpCommand($fp, "EHLO " . gethostname());
|
||||||
|
$result['steps'][] = '📨 EHLO: ' . smtpCode($ehlo);
|
||||||
|
$result['ehlo_capabilities'] = trim($ehlo);
|
||||||
|
if (smtpCode($ehlo) !== '250') {
|
||||||
|
$result['steps'][] = '❌ EHLO hylätty';
|
||||||
|
fclose($fp);
|
||||||
|
echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STARTTLS
|
||||||
|
if ($encryption === 'tls') {
|
||||||
|
$resp = smtpCommand($fp, "STARTTLS");
|
||||||
|
$result['steps'][] = '🔒 STARTTLS: ' . smtpCode($resp);
|
||||||
|
if (smtpCode($resp) !== '220') {
|
||||||
|
$result['steps'][] = '❌ STARTTLS hylätty: ' . trim($resp);
|
||||||
|
fclose($fp);
|
||||||
|
echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$crypto = @stream_socket_enable_crypto($fp, true,
|
||||||
|
STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT | STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT);
|
||||||
|
if (!$crypto) {
|
||||||
|
$result['steps'][] = '❌ TLS-neuvottelu epäonnistui';
|
||||||
|
fclose($fp);
|
||||||
|
echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$result['steps'][] = '✅ TLS OK';
|
||||||
|
// Re-EHLO
|
||||||
|
$ehlo = smtpCommand($fp, "EHLO " . gethostname());
|
||||||
|
$result['steps'][] = '📨 EHLO2: ' . smtpCode($ehlo);
|
||||||
|
$result['ehlo_capabilities_tls'] = trim($ehlo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// AUTH PLAIN
|
||||||
|
$cred = base64_encode("\0{$effectiveUser}\0{$effectivePass}");
|
||||||
|
$resp = smtpCommand($fp, "AUTH PLAIN {$cred}");
|
||||||
|
$result['steps'][] = '🔑 AUTH PLAIN: ' . trim($resp);
|
||||||
|
if (smtpCode($resp) === '235') {
|
||||||
|
$result['steps'][] = '✅ Kirjautuminen onnistui (AUTH PLAIN)!';
|
||||||
|
smtpCommand($fp, "QUIT");
|
||||||
|
fclose($fp);
|
||||||
|
echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AUTH LOGIN fallback
|
||||||
|
$resp = smtpCommand($fp, "AUTH LOGIN");
|
||||||
|
$result['steps'][] = '🔑 AUTH LOGIN: ' . smtpCode($resp);
|
||||||
|
if (smtpCode($resp) === '334') {
|
||||||
|
$resp = smtpCommand($fp, base64_encode($effectiveUser));
|
||||||
|
$result['steps'][] = '🔑 Käyttäjä: ' . smtpCode($resp);
|
||||||
|
if (smtpCode($resp) === '334') {
|
||||||
|
$resp = smtpCommand($fp, base64_encode($effectivePass));
|
||||||
|
$result['steps'][] = '🔑 Salasana: ' . trim($resp);
|
||||||
|
if (smtpCode($resp) === '235') {
|
||||||
|
$result['steps'][] = '✅ Kirjautuminen onnistui (AUTH LOGIN)!';
|
||||||
|
smtpCommand($fp, "QUIT");
|
||||||
|
fclose($fp);
|
||||||
|
echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$result['steps'][] = "❌ Kirjautuminen epäonnistui (käyttäjä: {$effectiveUser}, salasanan pituus: " . strlen($effectivePass) . ")";
|
||||||
|
smtpCommand($fp, "QUIT");
|
||||||
|
fclose($fp);
|
||||||
|
echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'mailbox_save':
|
case 'mailbox_save':
|
||||||
|
|||||||
@@ -859,8 +859,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div style="display:flex;gap:0.5rem;margin-top:0.75rem;">
|
<div style="display:flex;gap:0.5rem;margin-top:0.75rem;">
|
||||||
<button class="btn-primary" id="btn-save-mailbox">Tallenna</button>
|
<button class="btn-primary" id="btn-save-mailbox">Tallenna</button>
|
||||||
|
<button class="btn-secondary" id="btn-test-smtp" style="background:#2196F3;color:#fff;border:none;">🔌 Testaa SMTP</button>
|
||||||
<button class="btn-secondary" id="btn-cancel-mailbox">Peruuta</button>
|
<button class="btn-secondary" id="btn-cancel-mailbox">Peruuta</button>
|
||||||
</div>
|
</div>
|
||||||
|
<pre id="smtp-test-result" style="display:none;background:#1a1a2e;color:#0f0;padding:0.75rem;border-radius:6px;font-size:0.8rem;max-height:300px;overflow:auto;margin-top:0.5rem;white-space:pre-wrap;"></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Sijainnit (Sites) -->
|
<!-- Sijainnit (Sites) -->
|
||||||
|
|||||||
35
script.js
35
script.js
@@ -2600,6 +2600,41 @@ document.getElementById('btn-cancel-mailbox').addEventListener('click', () => {
|
|||||||
document.getElementById('mailbox-form-container').style.display = 'none';
|
document.getElementById('mailbox-form-container').style.display = 'none';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// SMTP-testaus
|
||||||
|
document.getElementById('btn-test-smtp').addEventListener('click', async () => {
|
||||||
|
const mailboxId = document.getElementById('mailbox-form-id').value;
|
||||||
|
const resultEl = document.getElementById('smtp-test-result');
|
||||||
|
if (!mailboxId) {
|
||||||
|
resultEl.style.display = '';
|
||||||
|
resultEl.textContent = '⚠️ Tallenna postilaatikko ensin, sitten testaa.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resultEl.style.display = '';
|
||||||
|
resultEl.textContent = '⏳ Testataan SMTP-yhteyttä...';
|
||||||
|
try {
|
||||||
|
const res = await apiCall('smtp_test', 'POST', { mailbox_id: mailboxId });
|
||||||
|
let output = '=== TIETOKANNAN ARVOT ===\n';
|
||||||
|
if (res.db_values) {
|
||||||
|
for (const [k, v] of Object.entries(res.db_values)) {
|
||||||
|
output += ` ${k}: ${v === '' ? '(tyhjä)' : v}\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output += `\n=== KÄYTETTÄVÄT ARVOT ===\n`;
|
||||||
|
output += ` Käyttäjä: ${res.effective_user || '(tyhjä)'}\n`;
|
||||||
|
output += ` Salasanan pituus: ${res.effective_pass_len}\n\n`;
|
||||||
|
output += `=== TESTIN VAIHEET ===\n`;
|
||||||
|
if (res.steps) {
|
||||||
|
res.steps.forEach(s => { output += ` ${s}\n`; });
|
||||||
|
}
|
||||||
|
if (res.ehlo_capabilities_tls) {
|
||||||
|
output += `\n=== EHLO (TLS jälkeen) ===\n${res.ehlo_capabilities_tls}\n`;
|
||||||
|
}
|
||||||
|
resultEl.textContent = output;
|
||||||
|
} catch (e) {
|
||||||
|
resultEl.textContent = '❌ Virhe: ' + e.message;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// ==================== YRITYKSEN KÄYTTÄJÄOIKEUDET ====================
|
// ==================== YRITYKSEN KÄYTTÄJÄOIKEUDET ====================
|
||||||
|
|
||||||
async function loadCompanyUsers(companyId) {
|
async function loadCompanyUsers(companyId) {
|
||||||
|
|||||||
Reference in New Issue
Block a user