113 lines
5.3 KiB
PHP
113 lines
5.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Box\Mod\Mailgateway;
|
|
|
|
use FOSSBilling\InformationException;
|
|
|
|
class ImapConnectionTest
|
|
{
|
|
public function __construct(private ?\Closure $factory = null) {}
|
|
|
|
public function test(array $settings, bool $createFolders = false): array
|
|
{
|
|
if ($settings['password'] === '') {
|
|
throw new InformationException('Save a mailbox password before testing the connection');
|
|
}
|
|
$client = null;
|
|
$connected = false;
|
|
$moveSupported = null;
|
|
$folders = [];
|
|
$errors = [];
|
|
try {
|
|
if ($this->factory === null) {
|
|
if (!is_file(__DIR__ . '/vendor/autoload.php')) {
|
|
throw new InformationException('Install the Mail Gateway Composer dependencies first');
|
|
}
|
|
require_once __DIR__ . '/vendor/autoload.php';
|
|
}
|
|
$config = [
|
|
'host' => $settings['host'], 'port' => (int) $settings['port'],
|
|
'protocol' => 'imap', 'encryption' => $settings['encryption'],
|
|
'validate_cert' => true, 'username' => $settings['username'],
|
|
'password' => $settings['password'], 'authentication' => null,
|
|
'timeout' => RuntimeConfig::get('imap_timeout_seconds'),
|
|
];
|
|
$client = $this->factory !== null ? ($this->factory)($config)
|
|
: (new \Webklex\PHPIMAP\ClientManager(['options' => ['debug' => false]]))->make($config);
|
|
$client->connect();
|
|
$connected = true;
|
|
try {
|
|
$capabilities = $client->getConnection()->getCapabilities()->validatedData();
|
|
$moveSupported = in_array('MOVE', array_map('strtoupper', $capabilities), true);
|
|
if (!$moveSupported) {
|
|
$errors[] = 'IMAP login succeeded, but the server does not support native MOVE. Importing requires MOVE to archive individual messages safely.';
|
|
}
|
|
} catch (\Throwable) {
|
|
$errors[] = 'IMAP login succeeded, but checking server MOVE support failed. Check server access and try again.';
|
|
}
|
|
$paths = $this->paths($client);
|
|
foreach (['inbox_folder', 'processed_folder', 'ignored_folder', 'failed_folder'] as $field) {
|
|
$path = $settings[$field];
|
|
$exists = $this->exists($path, $paths);
|
|
$created = false;
|
|
$error = null;
|
|
if (!$exists && $field === 'inbox_folder') {
|
|
$error = 'The configured inbox does not exist. Correct its path or create it on the mail server.';
|
|
} elseif (!$exists && $createFolders) {
|
|
try {
|
|
// Never expunge messages while creating a destination folder.
|
|
$client->createFolder($path, false);
|
|
} catch (\Throwable) {
|
|
$error = 'Folder creation failed. Check the folder path, parent folders, and mailbox permissions or quota.';
|
|
}
|
|
if ($error === null) {
|
|
try {
|
|
$paths = $this->paths($client);
|
|
$exists = $this->exists($path, $paths);
|
|
$created = $exists;
|
|
if (!$exists) {
|
|
$error = 'The server accepted folder creation, but the folder could not be found afterwards.';
|
|
}
|
|
} catch (\Throwable) {
|
|
$error = 'The folder creation request completed, but listing folders to verify it failed. Check server access and try again.';
|
|
}
|
|
}
|
|
} elseif (!$exists) {
|
|
$error = 'The configured destination folder does not exist.';
|
|
}
|
|
$folders[] = ['path' => $path, 'exists' => $exists, 'created' => $created, 'error' => $error];
|
|
}
|
|
} catch (\Throwable) {
|
|
// Never return IMAP exception messages or traces (may contain credentials).
|
|
$errors[] = $connected
|
|
? 'IMAP login succeeded, but listing folders failed. Check mailbox permissions and server access.'
|
|
: 'IMAP connection or login failed. Check the saved host, port, security, username, password, TLS certificate, and network access.';
|
|
} finally {
|
|
if ($client !== null) {
|
|
try { $client->disconnect(); } catch (\Throwable) {}
|
|
}
|
|
}
|
|
$success = $connected && $errors === [] && count($folders) === 4
|
|
&& array_filter($folders, fn ($folder) => !$folder['exists'] || $folder['error'] !== null) === [];
|
|
return ['success' => $success, 'connected' => $connected, 'move_supported' => $moveSupported, 'folders' => $folders, 'errors' => $errors];
|
|
}
|
|
|
|
private function paths(object $client): array
|
|
{
|
|
$paths = [];
|
|
foreach ($client->getFolders(false) as $folder) {
|
|
$paths[] = $folder->path;
|
|
$paths[] = $folder->full_name ?? $folder->path;
|
|
}
|
|
return $paths;
|
|
}
|
|
|
|
private function exists(string $path, array $paths): bool
|
|
{
|
|
return in_array($path, $paths, true)
|
|
|| (strcasecmp($path, 'INBOX') === 0 && in_array('INBOX', array_map('strtoupper', $paths), true));
|
|
}
|
|
}
|