'59', 'meta' => 'Invalid scheme. Only gemini:// is supported.', 'body' => ''); } $host = $parsed_url['host']; $port = isset($parsed_url['port']) ? $parsed_url['port'] : 1965; $path = isset($parsed_url['path']) ? $parsed_url['path'] : '/'; $query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : ''; // Создаем SSL контекст с проверкой сертификатов $context = stream_context_create(array( 'ssl' => array( 'verify_peer' => false, 'verify_peer_name' => false, 'allow_self_signed' => true ) )); // Открываем соединение $socket = @stream_socket_client( "tls://$host:$port", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context ); if (!$socket) { return array('status' => '59', 'meta' => "Could not connect: $errno $errstr", 'body' => ''); } // Отправляем запрос fwrite($socket, "$url\r\n"); // Читаем статусную строку $status_line = fgets($socket, 1024); if ($status_line === false) { return array('status' => '59', 'meta' => 'Failed to read response', 'body' => ''); } // Парсим статусную строку $status = substr($status_line, 0, 2); $meta = trim(substr($status_line, 3)); // Читаем тело ответа $body = ''; if ($status[0] == '2') { // Только статусы 2x имеют тело while (!feof($socket)) { $body .= fread($socket, 8192); } } fclose($socket); return array('status' => $status, 'meta' => $meta, 'body' => $body); } // Получаем запрошенный URL $url = isset($_GET['url']) ? $_GET['url'] : ''; if (empty($url)) { header('Content-Type: text/plain'); echo "ERROR: No URL provided\n"; exit; } // Проверка на относительный URL и добавление протокола если нужно if (strpos($url, '://') === false) { $url = 'gemini://' . $url; } // Получаем содержимое $response = fetch_gemini_content($url); // Если клиент запросил получение изображения if (isset($_GET['get_image']) && $_GET['get_image'] == '1') { if ($response['status'] == '20' && ( strpos($response['meta'], 'image/') === 0 || strpos($response['meta'], 'IMAGE/') === 0 )) { // Определяем тип изображения $mime = strtolower($response['meta']); header('Content-Type: ' . $mime); echo $response['body']; } else { header('Content-Type: text/plain'); echo "ERROR: Not an image or error fetching image\n"; } exit; } // Если контент является изображением, сразу отправляем его if ($response['status'] == '20' && ( strpos($response['meta'], 'image/') === 0 || strpos($response['meta'], 'IMAGE/') === 0 )) { $mime = strtolower($response['meta']); header('Content-Type: ' . $mime); echo $response['body']; exit; } // Обработка обычного контента header('Content-Type: text/plain; charset=UTF-8'); if ($response['status'] == '20') { // Успешный ответ с контентом // Обычный текстовый/gemtext контент echo $response['body']; } else { // Для ошибок или перенаправлений - отображаем статус и мета-информацию echo "Status: " . $response['status'] . "\n"; echo "Meta: " . $response['meta'] . "\n"; // Дополнительная информация для пользователя switch ($response['status'][0]) { case '1': // ВХОДНОЙ ЗАПРОС echo "\nThis page requires input. Please enter:\n"; break; case '3': // ПЕРЕНАПРАВЛЕНИЕ echo "\nRedirecting to: " . $response['meta'] . "\n"; break; case '4': // ВРЕМЕННАЯ ОШИБКА echo "\nTemporary error. Please try again later.\n"; break; case '5': // ПОСТОЯННАЯ ОШИБКА echo "\nPermanent error: " . $response['meta'] . "\n"; break; case '6': // СЕРТИФИКАТЫ echo "\nClient certificate required.\n"; break; } } ?>