Commit Diff


commit - 86a71674eb8c8c15fcae904bfbc8801629f1742d
commit + 8f317ea03ee469ccab83292584802c52a352ca3e
blob - 950b8283fb07a4289fe6d1a462651a2b76773d33
blob + 578884b5ab19556c2a0a9a5f37ff01eb6bf91c30
--- vostok/Makefile
+++ vostok/Makefile
@@ -6,6 +6,7 @@ CXXFILES	+= error.cc
 CXXFILES	+= gemini.cc
 CXXFILES	+= args.cc
 CXXFILES	+= parse_url.cc
+CXXFILES	+= open_file.cc
 CXXFILES	+= vostok.cc
 OFILES		= ${CXXFILES:.cc=.o}
 
blob - /dev/null
blob + 224e22e17c796b4e3b805df0d6582f1ad60cc932 (mode 644)
--- /dev/null
+++ vostok/open_file.cc
@@ -0,0 +1,69 @@
+/** Open file for Gemini response */
+
+#include "open_file.h"
+#include "error.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+
+
+namespace vostok
+{
+namespace
+{
+not_null<czstring> index_gmi = "index.gmi";
+}   // namespace <unnamed>
+
+
+open_file_result open_file(int directory_fd, not_null<czstring> file_name, unique_fd &opened_file)
+{
+    if (!*file_name)
+        file_name = index_gmi;
+
+    opened_file.reset(openat(directory_fd, file_name, O_RDONLY));
+    if (!opened_file)
+    {
+        const auto error_code = errno;
+        error::occurred(
+            [file_name]
+            {
+                error::g_log << "Open file \"" << file_name << "\"";
+            },
+            error::print{error_code}
+        );
+        return (error_code == ENOENT) ? file_not_found : file_opening_error;
+    }
+    struct stat sb{};
+    if (fstat(opened_file.get(), &sb) == -1)
+    {
+        error::occurred(
+            [file_name]
+            {
+                error::g_log << "Stat file \"" << file_name << "\"";
+            },
+            error::print{}
+        );
+        return file_opening_error;
+    }
+    if (S_ISDIR(sb.st_mode))
+    {
+        const unique_fd new_parent{opened_file.release()};
+        opened_file.reset(openat(new_parent.get(), index_gmi, O_RDONLY));
+        if (!opened_file)
+        {
+            const auto error_code = errno;
+            error::occurred(
+                [file_name]
+                {
+                    error::g_log << "Open file \"" << file_name << "\" /" << index_gmi;
+                },
+                error::print{error_code}
+            );
+            return (error_code == ENOENT) ? file_not_found : file_opening_error;
+        }
+    }
+    return file_opened;
+}
+
+
+}   // namespace vostok
blob - /dev/null
blob + b370afb5847fa49a237389954a908a3b9b1ab47f (mode 644)
--- /dev/null
+++ vostok/open_file.h
@@ -0,0 +1,23 @@
+/** Open file for Gemini response */
+
+#include "utils.h"
+
+#pragma once
+
+
+namespace vostok
+{
+
+
+enum open_file_result
+{
+    file_opened,
+
+    file_not_found,
+    file_opening_error,
+};
+
+open_file_result open_file(int directory_fd, not_null<czstring> file_name, unique_fd &opened_file);
+
+
+}   // namespace vostok
blob - 69ec0c58385e435f9305149e1903048a474c0154
blob + aeb7bca4f490ff9d8be8d2958cef7d6a1063092c
--- vostok/vostok.cc
+++ vostok/vostok.cc
@@ -4,9 +4,8 @@
 #include "transport.h"
 #include "args.h"
 #include "parse_url.h"
+#include "open_file.h"
 
-#include <fcntl.h>
-#include <sys/stat.h>
 #include <sys/socket.h>
 #include <arpa/inet.h>
 #include <netinet/in.h>
@@ -76,59 +75,21 @@ void client_thread(transport::accepted_context::value 
         break;
     }
 
-    const unique_fd opened_file{openat(directory_fd, zs_url_path.data(), O_RDONLY)};
-    if (!opened_file)
+    unique_fd opened_file{};
+    const auto open_file_result = open_file(directory_fd, zs_url_path.data(), opened_file);
+    switch (open_file_result)
     {
-        const auto error_code = errno;
-        error::occurred(
-            [&zs_url_path]
-            {
-                error::g_log << "Open file \"" << zs_url_path.data() << "\"";
-            },
-            error::print{error_code}
-        );
-        switch (error_code)
-        {
-        case ENOTDIR:
-        case ENOENT:
-        case EACCES:
-        case ELOOP:
-        case EISDIR:
-        case ENXIO:
-            transport::send_response(ctx.get_ctx(), gemini::STATUS_51_NOT_FOUND, meta::not_found);
-            break;
-
-        default:
-            transport::send_response(ctx.get_ctx(), gemini::STATUS_40_TEMPORARY_FAILURE, meta::temporary_failure);
-            break;
-        }
+    case file_not_found:
+        transport::send_response(ctx.get_ctx(), gemini::STATUS_51_NOT_FOUND, meta::not_found);
         return;
-    }
-    struct stat sb{};
-    if (fstat(opened_file.get(), &sb) == -1)
-    {
-        error::occurred(
-            [&zs_url_path]
-            {
-                error::g_log << "Stat file \"" << zs_url_path.data() << "\"";
-            },
-            error::print{}
-        );
+    case file_opening_error:
         transport::send_response(ctx.get_ctx(), gemini::STATUS_40_TEMPORARY_FAILURE, meta::temporary_failure);
         return;
+
+    case file_opened:
+        assert(opened_file);
+        break;
     }
-    if (S_ISDIR(sb.st_mode))
-    {
-        error::occurred(
-            [&zs_url_path]
-            {
-                error::g_log << "Open file \"" << zs_url_path.data() << "\"";
-            },
-            error::print{EISDIR}
-        );
-        transport::send_response(ctx.get_ctx(), gemini::STATUS_51_NOT_FOUND, meta::not_found);
-        return;
-    }
 
     // > If <META> is an empty string, the MIME type MUST default to "text/gemini; charset=utf-8".
     transport::send_response(ctx.get_ctx(), gemini::STATUS_20_SUCCESS, {});