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)); } }