#include #include #define NOH_IMPLEMENTATION #include "noh.h" #define NO_AUTH /* To disable Basic auth for testing: #define NO_AUTH This will ignore any auth headers and always log in with username demo. */ #define SV Noh_String_View #define sv noh_sv_from_cstr static enum MHD_Result send_response(struct MHD_Connection *connection, int status, SV message) { struct MHD_Response *response = MHD_create_response_from_buffer( message.count, (void*)message.elems, MHD_RESPMEM_PERSISTENT); if (!response) return MHD_NO; enum MHD_Result ret = MHD_queue_response(connection, status, response); MHD_destroy_response(response); return ret; } static enum MHD_Result handle_opds(struct MHD_Connection *connection, SV url, SV base_path, SV username) { Noh_String fs_path = {0}; noh_string_append_sv(&fs_path, base_path); noh_string_append_cstr(&fs_path, "/"); noh_string_append_sv(&fs_path, username); noh_string_append_cstr(&fs_path, "/"); noh_string_append_sv(&fs_path, url); noh_log(NOH_INFO, "Physical path: "Nsv_Fmt, Nsv_Arg(fs_path)); return send_response(connection, 200, sv("TODO: OPDS\n")); } static enum MHD_Result handle_get(struct MHD_Connection *connection, SV url, SV base_path, SV username) { (void)url; (void)username; (void)base_path; return send_response(connection, 200, sv("TODO: Get\n")); } bool is_slash(const char c) { return c == '/'; } static enum MHD_Result request_handler( void *cls, struct MHD_Connection *connection, const char *_url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls) { (void)con_cls; (void)upload_data; (void)upload_data_size; SV base_path = *(SV*)cls; noh_log(NOH_INFO, "Request: %s %s %s", version, method, _url); SV username; #ifdef NO_AUTH username = sv("demo"); #else struct MHD_BasicAuthInfo *auth = MHD_basic_auth_get_username_password3(connection); if (!auth) return send_response(connection, 401, sv("")); username = (SV){ .elems = auth->username, .count = auth->username_len }; SV password = { .elems = auth->password, .count = auth->password_len }; // TODO: Read username from user config. if (!noh_sv_eq(password, sv("jaja"))) return send_response(connection, 401, sv("")); #endif noh_log(NOH_INFO, "Authenticated as "Nsv_Fmt, Nsv_Arg(username)); if (strcmp(method, "GET") != 0) return send_response(connection, 405, sv("")); SV url = sv(_url); noh_sv_trim_left(&url, is_slash); SV controller = noh_sv_chop_by_delim(&url, '/'); if (noh_sv_eq_ci(controller, sv("opds"))) return handle_opds(connection, url, base_path, username); if (noh_sv_eq_ci(controller, sv("get"))) return handle_get(connection, url, base_path, username); return send_response(connection, 404, sv("")); } void show_usage(char *program_name, char *error) { noh_log(NOH_INFO, "Usage:"); noh_log(NOH_INFO, "%s port library_path", program_name); noh_log(NOH_INFO, " port: The port to listen to, between 1000 and 65535."); noh_log(NOH_INFO, " library_path: The path where the epub libraries are located."); noh_log(NOH_INFO, " The root of this path should have a users.txt file with the usernames and passwords of valid users."); noh_log(NOH_INFO, " For each user, ther must be a subdirectory where this user's library is located."); noh_log(NOH_INFO, ""); noh_log(NOH_ERROR, "%s", error); exit(1); } int main(int argc, char **argv) { // Parse input parameters. char *program_name = noh_shift_args(&argc, &argv); if (argc < 1) show_usage(program_name, "Port not provided."); char *port_str = noh_shift_args(&argc, &argv); int port = atoi(port_str); if (port < 1000 || port > 65535) show_usage(program_name, "Invalid port."); if (argc < 1) show_usage(program_name, "Library path not provided."); char *library_path = noh_shift_args(&argc, &argv); SV base_path = sv(library_path); struct MHD_Daemon *server; // Start the HTTP server server = MHD_start_daemon( MHD_USE_INTERNAL_POLLING_THREAD, port, NULL, NULL, &request_handler, &base_path, MHD_OPTION_END); if (!server) { fprintf(stderr, "Failed to start server\n"); return 1; } printf("Server is running on http://0.0.0.0:%d\n", port); // Keep the server running getchar(); // Stop the server MHD_stop_daemon(server); return 0; }