diff --git a/src/Orbit/Config.php b/src/Orbit/Config.php index 78e1af2..ba98871 100644 --- a/src/Orbit/Config.php +++ b/src/Orbit/Config.php @@ -17,4 +17,6 @@ class Config public $verbose = false; public $root_dir = "."; + public $index_file = "index.gmi"; + public $enable_directory_index = true; } diff --git a/src/Orbit/Response.php b/src/Orbit/Response.php index 499db05..f6d11b3 100644 --- a/src/Orbit/Response.php +++ b/src/Orbit/Response.php @@ -49,8 +49,12 @@ class Response if (false === $fp) { throw new \Exception("Error reading file '{$this->filepath}'"); } - while (!feof($fp)) { - fwrite($client, fread($fp, 8192)); + + $result = 1; + while (!feof($fp) && $result) { + // If the client cancels, bail out before trying large files + // So, result will be 0 if the client cancels (broken socket) + $result = fwrite($client, fread($fp, 8192)); } return $size; } else { diff --git a/src/Orbit/Server.php b/src/Orbit/Server.php index 68d0084..6d36106 100644 --- a/src/Orbit/Server.php +++ b/src/Orbit/Server.php @@ -131,14 +131,33 @@ class Server } // Check if index file exists - if (file_exists($resource_path . DIRECTORY_SEPARATOR . "index.gmi")) { - $resource_path = $resource_path . "/index.gmi"; + if (file_exists($resource_path . DIRECTORY_SEPARATOR . $this->config->index_file)) { + $resource_path = $resource_path . DIRECTORY_SEPARATOR . $this->config->index_file; + } else { + if (!$this->config->enable_directory_index) { + $response->setStatus(Response::STATUS_BAD_REQUEST); + $response->setMeta('Path not available'); + return $response; + } else { + $response->setStatus(Response::STATUS_SUCCESS); + $response->setMeta('text/gemini'); + $response->setBody($this->makeDirectoryIndex($resource_path, $dir)); + return $response; + } } } - if (file_exists($resource_path)) { + // File exists and is world readable + if (file_exists($resource_path) && (fileperms($resource_path) & 0x0004)) { $response->setStatus(Response::STATUS_SUCCESS); + $pathinfo = pathinfo($resource_path); + if (!isset($pathinfo['extension'])) { + $response->setStatus(Response::STATUS_TEMPORARY_FAILURE); + $response->setMeta('Error reading resource'); + return $response; + } + if ($pathinfo['extension'] == 'gmi' || $pathinfo['extension'] == 'gemini') { $response->setMeta('text/gemini'); } elseif ($pathinfo['extension'] == 'md' || $pathinfo['extension'] == 'markdown') { @@ -179,6 +198,37 @@ class Server return sprintf('tls://%s:%s', $this->config->host, $this->config->port); } + public function makeDirectoryIndex($path, $root) + { + $files = glob($path . "*"); + + $body = "# Directory listing " . str_replace($root, '', $path) . "\n\n"; + $body .= "=> " . str_replace($root, '', dirname($path)) . " ..\n"; + + foreach ($files as $file) { + $relative_path = str_replace($path, '', $file); + + $is_dir = false; + if (is_dir($file)) { + $is_dir = true; + $size = ''; + } else { + $size = filesize($file); + } + + $body .= sprintf( + "=> %s%s %s%s%s\n", + urlencode($relative_path), + ($is_dir ? '/' : ''), + $relative_path, + ($is_dir ? '/' : ''), + ($size ? " ($size)" : '') + ); + } + + return $body; + } + private function generateCert() { // Certificate data