Files
kaffeekasse-saas/scripts/check-imap-support.php
T
clemensandClaude Opus 5 2fd606a3aa Diagnose fuer den PayPal-Postfachzugang
Vor dem ersten Abruf soll klar sein, ob der Host das Postfach ueberhaupt
erreicht - und auf welchem Weg. Das Skript meldet PHP-Version, ob die
imap-Erweiterung geladen ist, ob die PAYPAL_*-Einstellungen gesetzt sind,
und oeffnet das Postfach testweise ueber eine reine TLS-Verbindung.

Damit ist auch ohne die Erweiterung belegbar, dass ein Abruf moeglich
waere - seit PHP 8.4 ist imap kein Bestandteil von PHP mehr und laesst
sich auf einem Webhosting nicht einfach nachruesten.

Bewusst nur lesend: EXAMINE statt SELECT, kein Flag wird gesetzt. Das
Passwort wird nie ausgegeben, nur seine Laenge als Tippfehler-Probe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 21:13:27 +02:00

177 lines
6.6 KiB
PHP

<?php
declare(strict_types=1);
/**
* Diagnose fuer den PayPal-Mailabruf: klaert in einem Lauf, ob dieser Host
* das Postfach ueberhaupt erreichen kann - und auf welchem Weg.
*
* Geprueft wird:
* 1. PHP-Version und ob die imap-Erweiterung geladen ist (seit PHP 8.4 ist
* sie nicht mehr Teil von PHP, sondern nur noch ueber PECL zu haben).
* 2. Ob die PAYPAL_*-Einstellungen in der env.local.php gesetzt sind.
* 3. Ob sich das Postfach ohne die Erweiterung erreichen laesst - ueber
* eine reine TLS-Verbindung, wie sie auch ein Fallback nutzen wuerde.
*
* Der Test ist bewusst nur lesend: EXAMINE statt SELECT, kein Flag wird
* gesetzt, keine Mail veraendert. Das Passwort wird nie ausgegeben.
*
* Aufruf (Plesk: Geplante Aufgabe vom Typ "PHP-Skript"):
* /httpdocs/scripts/check-imap-support.php
*/
if (PHP_SAPI !== 'cli') {
http_response_code(403);
exit("Dieses Skript ist nur fuer die Kommandozeile gedacht.\n");
}
require_once __DIR__ . '/../app/bootstrap.php';
function imap_check_line(string $label, string $value): void
{
printf("%-28s %s\n", $label . ':', $value);
}
echo "=== PHP ===\n";
imap_check_line('Version', PHP_VERSION);
imap_check_line('imap-Erweiterung', function_exists('imap_open') ? 'geladen' : 'FEHLT');
imap_check_line('openssl', extension_loaded('openssl') ? 'vorhanden' : 'FEHLT');
imap_check_line('Sockets/Streams', function_exists('stream_socket_client') ? 'vorhanden' : 'FEHLT');
if (!function_exists('imap_open') && version_compare(PHP_VERSION, '8.4', '>=')) {
echo "Hinweis: Ab PHP 8.4 ist imap kein Bestandteil von PHP mehr. Entweder den\n"
. "PHP-Handler dieses Vhosts auf 8.3 stellen oder den Abruf ueber eine reine\n"
. "TLS-Verbindung laufen lassen (siehe Test unten).\n";
}
echo "\n=== Einstellungen (env.local.php) ===\n";
$host = app_env('PAYPAL_IMAP_HOST');
$user = app_env('PAYPAL_IMAP_USER');
$pass = app_env('PAYPAL_IMAP_PASS');
$port = (int) app_env('PAYPAL_IMAP_PORT', '993');
$flags = (string) app_env('PAYPAL_IMAP_FLAGS', '/imap/ssl');
$mailbox = (string) app_env('PAYPAL_IMAP_MAILBOX', 'INBOX');
imap_check_line('PAYPAL_INBOX_BASE', (string) (app_env('PAYPAL_INBOX_BASE') ?? '(nicht gesetzt)'));
imap_check_line('PAYPAL_IMAP_HOST', (string) ($host ?? '(nicht gesetzt)'));
imap_check_line('PAYPAL_IMAP_PORT', (string) $port);
imap_check_line('PAYPAL_IMAP_FLAGS', $flags);
imap_check_line('PAYPAL_IMAP_USER', (string) ($user ?? '(nicht gesetzt)'));
// Das Passwort selbst gehoert nicht in ein Protokoll, das per Mail
// verschickt oder weitergereicht wird - nur die Laenge als Tippfehler-Probe.
imap_check_line('PAYPAL_IMAP_PASS', $pass === null ? '(nicht gesetzt)' : 'gesetzt (' . strlen($pass) . ' Zeichen)');
imap_check_line('PAYPAL_IMAP_MAILBOX', $mailbox);
if ($host === null || $user === null || $pass === null) {
echo "\nOhne Host, Benutzer und Passwort ist kein Verbindungstest moeglich.\n";
exit(1);
}
/**
* Liest die Antwort bis zur Zeile mit dem gesuchten Tag.
*
* @return array{ok: bool, lines: list<string>}
*/
function imap_check_read($sock, string $tag): array
{
$lines = [];
while (($line = fgets($sock)) !== false) {
$line = rtrim($line, "\r\n");
$lines[] = $line;
if (str_starts_with($line, $tag . ' ')) {
return ['ok' => str_starts_with($line, $tag . ' OK'), 'lines' => $lines];
}
}
return ['ok' => false, 'lines' => $lines];
}
function imap_check_send($sock, string $tag, string $command): array
{
fwrite($sock, $tag . ' ' . $command . "\r\n");
return imap_check_read($sock, $tag);
}
echo "\n=== Verbindungstest ohne imap-Erweiterung (nur lesend) ===\n";
// Port 993 = TLS von Anfang an; alles andere (typisch 143) = STARTTLS.
$implizitesTls = $port === 993 || str_contains($flags, '/ssl');
$adresse = ($implizitesTls ? 'ssl://' : 'tcp://') . $host . ':' . $port;
imap_check_line('Verbindung', $adresse . ($implizitesTls ? ' (TLS sofort)' : ' (STARTTLS)'));
$fehlerNr = 0;
$fehlerText = '';
$sock = @stream_socket_client($adresse, $fehlerNr, $fehlerText, 15);
if ($sock === false) {
echo "FEHLER: Verbindung nicht moeglich ({$fehlerNr}: {$fehlerText})\n";
echo "Moegliche Ursachen: Host/Port falsch, ausgehende Verbindungen gesperrt,\n"
. "oder TLS-Zertifikat nicht akzeptiert.\n";
exit(1);
}
stream_set_timeout($sock, 15);
$greeting = rtrim((string) fgets($sock), "\r\n");
imap_check_line('Begruessung', $greeting !== '' ? $greeting : '(keine)');
if (!$implizitesTls) {
$start = imap_check_send($sock, 'a0', 'STARTTLS');
if (!$start['ok'] || !@stream_socket_enable_crypto($sock, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
echo "FEHLER: STARTTLS fehlgeschlagen - ohne Verschluesselung wird nicht angemeldet.\n";
exit(1);
}
imap_check_line('STARTTLS', 'ok');
}
// Anfuehrungszeichen und Backslashes im Passwort maskieren (IMAP-Quoting).
$quote = static fn(string $wert): string => '"' . str_replace(['\\', '"'], ['\\\\', '\\"'], $wert) . '"';
$login = imap_check_send($sock, 'a1', 'LOGIN ' . $quote($user) . ' ' . $quote($pass));
if (!$login['ok']) {
echo "FEHLER: Anmeldung abgelehnt.\n";
echo ' Antwort: ' . end($login['lines']) . "\n";
fwrite($sock, "a9 LOGOUT\r\n");
fclose($sock);
exit(1);
}
imap_check_line('Anmeldung', 'ok');
// EXAMINE statt SELECT: oeffnet das Postfach ausdruecklich nur lesend.
$examine = imap_check_send($sock, 'a2', 'EXAMINE ' . $quote($mailbox));
if (!$examine['ok']) {
echo "FEHLER: Postfach '{$mailbox}' nicht zu oeffnen.\n";
echo ' Antwort: ' . end($examine['lines']) . "\n";
fwrite($sock, "a9 LOGOUT\r\n");
fclose($sock);
exit(1);
}
$gesamt = 0;
foreach ($examine['lines'] as $line) {
if (preg_match('/^\* (\d+) EXISTS/', $line, $m)) {
$gesamt = (int) $m[1];
}
}
imap_check_line('Postfach', $mailbox . ' geoeffnet');
imap_check_line('Mails gesamt', (string) $gesamt);
$search = imap_check_send($sock, 'a3', 'SEARCH UNSEEN');
$ungelesen = 0;
foreach ($search['lines'] as $line) {
if (str_starts_with($line, '* SEARCH')) {
$ids = trim(substr($line, strlen('* SEARCH')));
$ungelesen = $ids === '' ? 0 : count(preg_split('/\s+/', $ids) ?: []);
}
}
imap_check_line('davon ungelesen', (string) $ungelesen);
fwrite($sock, "a9 LOGOUT\r\n");
fclose($sock);
echo "\nErgebnis: Postfach erreichbar und Zugangsdaten gueltig.\n";
if (function_exists('imap_open')) {
echo "Die imap-Erweiterung ist vorhanden - scripts/fetch-paypal-payments.php\n"
. "laeuft ohne weitere Aenderung. Naechster Schritt: derselbe Task mit --dry-run.\n";
} else {
echo "Die imap-Erweiterung fehlt, das Postfach ist aber ueber die reine\n"
. "TLS-Verbindung erreichbar - ein Abruf ohne Erweiterung ist also moeglich.\n";
}