aboutsummaryrefslogtreecommitdiff
path: root/maple/maple.cc
diff options
context:
space:
mode:
Diffstat (limited to 'maple/maple.cc')
-rw-r--r--maple/maple.cc230
1 files changed, 152 insertions, 78 deletions
diff --git a/maple/maple.cc b/maple/maple.cc
index 0bae0e9..6382fa1 100644
--- a/maple/maple.cc
+++ b/maple/maple.cc
@@ -21,29 +21,78 @@
#include <arpa/inet.h>
#include <csignal>
#include <filesystem>
-#include <fstream>
#include <iostream>
+#include <map>
#include <openssl/err.h>
-#include <openssl/ssl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <vector>
-static int maple_socket;
-static SSL_CTX *ssl_context;
-
-auto exit_with[[noreturn]](const char *, bool) -> void;
+#include "gemini.hh"
+#include "maple.hh"
+#include "titan.hh"
auto main() -> int {
sockaddr_in socket_address {};
std::vector<std::string> gemini_files;
+ bool titan = false;
+ std::string titan_token;
+ size_t titan_max_size = 0;
+
+ // Check if the user is want to support Titan
+ {
+ char *titan_environment = std::getenv("TITAN");
+
+ if (titan_environment == nullptr) {
+ titan = false;
+ } else {
+ std::string valid_titan_environment(titan_environment);
+
+ std::transform(
+ valid_titan_environment.begin(),
+ valid_titan_environment.end(),
+ valid_titan_environment.begin(),
+ [](unsigned char c) -> int { return std::tolower(c); }
+ );
+
+ if (valid_titan_environment == "true" || valid_titan_environment == "1") {
+ char *unvalidated_titan_token = std::getenv("TITAN_TOKEN");
+ char *unvalidated_titan_max_size = std::getenv("TITAN_MAX_SIZE");
+
+ if (unvalidated_titan_token == nullptr) {
+ titan_token = "";
+ } else {
+ titan_token = std::string(unvalidated_titan_token);
+ }
+
+ if (unvalidated_titan_max_size == nullptr) {
+ titan_max_size = 1024;
+
+ std::cout << "no TITAN_MAX_SIZE set, defaulting to 1024" << std::endl;
+ } else {
+ try {
+ titan_max_size = static_cast<size_t>(
+ std::stoi(unvalidated_titan_max_size)
+ );
+ } catch (...) {
+ maple::exit_with(
+ "TITAN_MAX_SIZE could not be interpreted as an integer",
+ false
+ );
+ }
+ }
+
+ titan = true;
+ }
+ }
+ }
// Try a graceful shutdown when a SIGINT is detected
signal(SIGINT, [](int signal_) -> void {
std::cout << "shutdown(" << signal_ << ")" << std::endl;
- close(maple_socket);
- SSL_CTX_free(ssl_context);
+ close(maple::maple_socket);
+ SSL_CTX_free(maple::ssl_context);
});
// Find and keep track of all Gemini files to serve
@@ -61,8 +110,7 @@ auto main() -> int {
file_extension.end(),
gemini_file_extension.begin(),
gemini_file_extension.end(),
- [](auto a, auto b) -> bool {
- return std::tolower(a) == std::tolower(b);
+ [](auto a, auto b) -> bool {return std::tolower(a) == std::tolower(b);
}
)) {
gemini_files.push_back(entry.path());
@@ -78,57 +126,58 @@ auto main() -> int {
SSL_library_init();
SSL_load_error_strings();
- ssl_context = SSL_CTX_new(TLS_server_method());
- if (!ssl_context) {
- exit_with("unable to create ssl context", true);
+ maple::ssl_context = SSL_CTX_new(TLS_server_method());
+ if (!maple::ssl_context) {
+ maple::exit_with("unable to create ssl context", true);
}
if (SSL_CTX_use_certificate_file(
- ssl_context,
+ maple::ssl_context,
".maple/public.pem",
SSL_FILETYPE_PEM
) <= 0) {
- exit_with("unable to use certificate file", true);
+ maple::exit_with("unable to use certificate file", true);
}
if (SSL_CTX_use_PrivateKey_file(
- ssl_context,
+ maple::ssl_context,
".maple/private.pem",
SSL_FILETYPE_PEM
) <= 0) {
- exit_with("unable to use private key file", true);
+ maple::exit_with("unable to use private key file", true);
}
socket_address.sin_family = AF_INET;
socket_address.sin_port = htons(1965);
socket_address.sin_addr.s_addr = htonl(INADDR_ANY);
- maple_socket = socket(AF_INET, SOCK_STREAM, 0);
- if (maple_socket < 0) {
- exit_with("unable to create socket", false);
+ maple::maple_socket = socket(AF_INET, SOCK_STREAM, 0);
+
+ if (maple::maple_socket < 0) {
+ maple::exit_with("unable to create socket", false);
}
// Reuse address. Allows the use of the address instantly after a SIGINT
// without having to wait for the socket to die.
int reuse_addr = 1;
if (setsockopt(
- maple_socket,
+ maple::maple_socket,
SOL_SOCKET,
SO_REUSEADDR,
&reuse_addr,
sizeof(int)
) < 0) {
- exit_with("unable to set socket options (SO_LINGER)", false);
+ maple::exit_with("unable to set socket options (SO_LINGER)", false);
}
if (bind(
- maple_socket,
+ maple::maple_socket,
reinterpret_cast<sockaddr *>(&socket_address),
sizeof(socket_address)
) < 0) {
- exit_with("unable to bind", false);
+ maple::exit_with("unable to bind", false);
}
- if (listen(maple_socket, 1) < 0) {
- exit_with("unable to listen", false);
+ if (listen(maple::maple_socket, 1) < 0) {
+ maple::exit_with("unable to listen", false);
}
// Listen and serve connections
@@ -137,15 +186,15 @@ auto main() -> int {
unsigned int socket_address_length = sizeof(socket_address_);
SSL *ssl;
int client = accept(
- maple_socket,
+ maple::maple_socket,
reinterpret_cast<sockaddr *>(&socket_address_),
&socket_address_length
);
char request[1024];
- if (client < 0) { exit_with("unable to accept", false); }
+ if (client < 0) { maple::exit_with("unable to accept", false); }
- ssl = SSL_new(ssl_context);
+ ssl = SSL_new(maple::ssl_context);
SSL_set_fd(ssl, client);
if (SSL_accept(ssl) <= 0) {
@@ -153,65 +202,88 @@ auto main() -> int {
} else {
std::stringstream response;
size_t index_of_junk;
+ int request_scheme; // Gemini = 1, Titan = 2, Error = 0
+ size_t bytes_read;
- SSL_read(ssl, request, sizeof(request));
+ SSL_read_ex(ssl, request, sizeof(request), &bytes_read);
std::string path(request);
- path = path.substr(0, path.size() - 2); // Remove "\r\n"
- path.erase(0, 9); // Remove "gemini://"
-
- // Try to remove the host, if you cannot; it must be a trailing slash-less
- // hostname, so we will respond with the index.
- size_t found_first = path.find_first_of('/');
- if (found_first != std::string::npos) {
- path = path.substr(
- found_first,
- path.size() - 1
- ); // Remove host
+ if (path.starts_with("gemini://")) {
+ request_scheme = 1;
+ } else if (path.starts_with("titan://")) {
+ request_scheme = 2;
} else {
- path = "/index.gmi";
+ request_scheme = 0;
}
- // Remove junk, if any
- index_of_junk = path.find_first_of('\n');
- if (index_of_junk != std::string::npos) {
- path.erase(
- path.find_first_of('\n') - 1,
- path.size() - 1
- );
- }
-
- // Check if the route is a file being served
- if (std::find(
- gemini_files.begin(),
- gemini_files.end(),
- ".maple/gmi" + path
- ) != gemini_files.end()) {
- // If the route is a file being served; get the file contents
-
- std::ifstream file(".maple/gmi" + path);
- std::stringstream buffer;
-
- buffer << file.rdbuf();
+ if (request_scheme != 0) {
+ path = path.substr(0, bytes_read);
- response << "20 text/gemini\r\n" << buffer.str();
- } else {
- if (path.empty() || path.at(path.length() - 1) == '/') {
- std::ifstream file(".maple/gmi" + path + "index.gmi");
- std::stringstream buffer;
+ // Remove "\r\n" if Gemini
+ if (request_scheme == 1) {
+ path = path.substr(0, path.size() - 2);
+ }
- buffer << file.rdbuf();
+ if (request_scheme == 1) {
+ path.erase(0, 9); // Remove "gemini://"
+ } else {
+ path.erase(0, 8); // Remove "titan://"
+ }
- response << "20 text/gemini\r\n" << buffer.str();
+ // Try to remove the host, if you cannot; it must be a trailing
+ // slash-less hostname, so we will respond with the index.
+ size_t found_first = path.find_first_of('/');
+ if (found_first != std::string::npos) {
+ path = path.substr(
+ found_first,
+ path.size() - 1
+ ); // Remove host
} else {
- response << "51 The server (Maple) could not find the specified file.\r\n";
+ path = "/index.gmi";
+ }
+
+// std::cout << "1: \"" << path << "\"" << std::endl;
+
+ if (request_scheme == 1) {
+ // Remove junk, if any
+ index_of_junk = path.find_first_of('\n');
+ if (index_of_junk != std::string::npos) {
+ path.erase(
+ path.find_first_of('\n') - 1,
+ path.size() - 1
+ );
+ }
}
- }
- std::cout << "requested " << path << std::endl;
+// std::cout << "2: \"" << path << "\"" << std::endl;
+
+ // Gemini
+ if (request_scheme == 1) {
+ maple::gemini::handle_client(gemini_files, path, response);
+ } else { // Titan
+ if (!titan) {
+ response << "20 text/gemini\r\nThe server (Maple) does not have "
+ "Titan support enabled!";
+ } else {
+ maple::titan::handle_client(
+ response,
+ path,
+ titan_token,
+ titan_max_size
+ );
+ }
+ }
- SSL_write(ssl, response.str().c_str(), static_cast<int>(response.str().size()));
+ SSL_write(
+ ssl,
+ response.str().c_str(),
+ static_cast<int>(response.str().size())
+ );
+ } else {
+ std::cout << "received a request with an unsupported url scheme"
+ << std::endl;
+ }
}
SSL_shutdown(ssl);
@@ -220,8 +292,10 @@ auto main() -> int {
}
}
-auto exit_with[[noreturn]](const char *message, bool ssl) -> void {
- perror(message);
- if (ssl) { ERR_print_errors_fp(stderr); }
- std::exit(EXIT_FAILURE);
+namespace maple {
+ auto exit_with[[noreturn]](const char *message, bool ssl) -> void {
+ perror(message);
+ if (ssl) { ERR_print_errors_fp(stderr); }
+ std::exit(EXIT_FAILURE);
+ }
}