Files
fossbilling-mailgateway/ImapConnectionTest.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));
}
}