Implement IMAP email-to-ticket gateway
This commit is contained in:
1 parent
b7e5884457
commit
6f25869345
33 files changed
+5619
-5
No files matched your search
@@ -0,0 +1,112 @@
|
||||
<?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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user