commit - 86a71674eb8c8c15fcae904bfbc8801629f1742d
commit + 8f317ea03ee469ccab83292584802c52a352ca3e
blob - 950b8283fb07a4289fe6d1a462651a2b76773d33
blob + 578884b5ab19556c2a0a9a5f37ff01eb6bf91c30
--- vostok/Makefile
+++ vostok/Makefile
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
+/** 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
+/** 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
#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>
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, {});