opds: Send full xml structure with TODO placeholders
This commit is contained in:
96
src/main.c
96
src/main.c
@@ -15,11 +15,13 @@ This will ignore any auth headers and always log in with username demo.
|
|||||||
|
|
||||||
Noh_Arena temp;
|
Noh_Arena temp;
|
||||||
|
|
||||||
static enum MHD_Result send_response(struct MHD_Connection *connection, int status, SV message) {
|
static enum MHD_Result send_response(struct MHD_Connection *connection, int status, SV message, bool xml) {
|
||||||
struct MHD_Response *response = MHD_create_response_from_buffer(
|
struct MHD_Response *response = MHD_create_response_from_buffer(
|
||||||
message.count, (void*)message.elems, MHD_RESPMEM_PERSISTENT);
|
message.count, (void*)message.elems, MHD_RESPMEM_MUST_COPY);
|
||||||
if (!response) return MHD_NO;
|
if (!response) return MHD_NO;
|
||||||
|
|
||||||
|
if (xml) MHD_add_response_header(response, "content-type", "application/xml");
|
||||||
|
|
||||||
enum MHD_Result ret = MHD_queue_response(connection, status, response);
|
enum MHD_Result ret = MHD_queue_response(connection, status, response);
|
||||||
MHD_destroy_response(response);
|
MHD_destroy_response(response);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -28,6 +30,7 @@ static enum MHD_Result send_response(struct MHD_Connection *connection, int stat
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
bool is_dir;
|
bool is_dir;
|
||||||
SV name;
|
SV name;
|
||||||
|
SV full_path;
|
||||||
struct tm modified_at;
|
struct tm modified_at;
|
||||||
} Library_Entry;
|
} Library_Entry;
|
||||||
|
|
||||||
@@ -75,6 +78,48 @@ bool create_entry(struct dirent *dir_entry, const char *parent_path, Library_Ent
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *format_library_entry(Library_Entry entry, char time_buffer[80]) {
|
||||||
|
strftime(time_buffer, 80, "%Y-%m-%d %H:%M:%S", &entry.modified_at);
|
||||||
|
|
||||||
|
return noh_arena_sprintf(
|
||||||
|
&temp,
|
||||||
|
"Entry: %s \npath:'"Nsv_Fmt"'\nName: '"Nsv_Fmt"'\nModified at: %s.\n",
|
||||||
|
entry.is_dir ? "Dir" : "File",
|
||||||
|
Nsv_Arg(entry.full_path),
|
||||||
|
Nsv_Arg(entry.name),
|
||||||
|
time_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void append_entry(Noh_String *response, Library_Entry entry) {
|
||||||
|
noh_string_append_cstr(response, "<entry>");
|
||||||
|
noh_string_append_cstr(response, "<id>TODO</id>");
|
||||||
|
noh_string_append_cstr(response, "<title>TODO</title>");
|
||||||
|
noh_string_append_cstr(response, "<updated>TODO</updated>");
|
||||||
|
if (entry.is_dir) {
|
||||||
|
noh_string_append_cstr(response, "<link rel=\"subsection\" href=\"TODO\" type=\"application/atom+xml;profile=opds-catalog;kind=navigation\"/>");
|
||||||
|
} else {
|
||||||
|
noh_string_append_cstr(response, "<link rel=\"http://opds-spec.org/acquisition\" href=\"TODO\" type=\"application/epub+zip\"/>");
|
||||||
|
}
|
||||||
|
noh_string_append_cstr(response, "</entry>");
|
||||||
|
}
|
||||||
|
|
||||||
|
static Noh_String create_feed_response(Library_Entry self_path, Library_Entries list) {
|
||||||
|
(void)self_path;
|
||||||
|
Noh_String response = {0};
|
||||||
|
|
||||||
|
noh_string_append_cstr(&response, "<feed>");
|
||||||
|
noh_string_append_cstr(&response, "<id>TODO</id>");
|
||||||
|
noh_string_append_cstr(&response, "<title>TODO</title>");
|
||||||
|
noh_string_append_cstr(&response, "<updated>TODO</updated>");
|
||||||
|
noh_string_append_cstr(&response, "<link rel=\"self\" href=\"TODO\" type=\"application/atom+xml;profile=opds-catalog;kind=navigation\"/>");
|
||||||
|
noh_string_append_cstr(&response, "<link rel=\"root\" href=\"TODO\" type=\"application/atom+xml;profile=opds-catalog;kind=navigation\"/>");
|
||||||
|
|
||||||
|
for (size_t i = 0; i < list.count; i++) append_entry(&response, list.elems[i]);
|
||||||
|
noh_string_append_cstr(&response, "</feed>");
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
static enum MHD_Result handle_opds(struct MHD_Connection *connection, SV url, SV base_path, SV username) {
|
static enum MHD_Result handle_opds(struct MHD_Connection *connection, SV url, SV base_path, SV username) {
|
||||||
Noh_String fs_path = {0};
|
Noh_String fs_path = {0};
|
||||||
noh_string_append_sv(&fs_path, base_path);
|
noh_string_append_sv(&fs_path, base_path);
|
||||||
@@ -84,12 +129,20 @@ static enum MHD_Result handle_opds(struct MHD_Connection *connection, SV url, SV
|
|||||||
noh_string_append_sv(&fs_path, url);
|
noh_string_append_sv(&fs_path, url);
|
||||||
noh_string_append_null(&fs_path);
|
noh_string_append_null(&fs_path);
|
||||||
|
|
||||||
noh_log(NOH_INFO, "Physical path: "Nsv_Fmt, Nsv_Arg(fs_path));
|
Library_Entry self_entry = {
|
||||||
|
.is_dir = true,
|
||||||
|
.full_path = noh_sv_from_string(&fs_path),
|
||||||
|
.modified_at = {0},
|
||||||
|
};
|
||||||
|
(void)self_entry;
|
||||||
|
|
||||||
|
noh_log(NOH_INFO, "Physical path: "Nsv_Fmt, Nsv_Arg(self_entry.name));
|
||||||
|
|
||||||
DIR *dir = opendir(fs_path.elems);
|
DIR *dir = opendir(fs_path.elems);
|
||||||
if (dir) {
|
if (dir) {
|
||||||
Library_Entries list = {0};
|
Library_Entries list = {0};
|
||||||
|
|
||||||
|
// Build up list of entries.
|
||||||
noh_arena_save(&temp);
|
noh_arena_save(&temp);
|
||||||
struct dirent *entry = noh_arena_alloc(&temp, sizeof(struct dirent));
|
struct dirent *entry = noh_arena_alloc(&temp, sizeof(struct dirent));
|
||||||
while ((entry = readdir(dir)) != NULL) {
|
while ((entry = readdir(dir)) != NULL) {
|
||||||
@@ -100,42 +153,33 @@ static enum MHD_Result handle_opds(struct MHD_Connection *connection, SV url, SV
|
|||||||
// Sort entries, folders first, then by name.
|
// Sort entries, folders first, then by name.
|
||||||
if (list.count > 1) qsort(list.elems, list.count, sizeof(list.elems[0]), compare_library_entries);
|
if (list.count > 1) qsort(list.elems, list.count, sizeof(list.elems[0]), compare_library_entries);
|
||||||
|
|
||||||
// Print sorted entries.
|
// Create and send response.
|
||||||
Noh_String response = {0};
|
Noh_String response = create_feed_response(self_entry, list);
|
||||||
char *time_buffer = noh_arena_alloc(&temp, 80);
|
bool result = send_response(connection, 200, noh_sv_from_string(&response), true);
|
||||||
for (size_t i = 0; i < list.count; i++) {
|
|
||||||
Library_Entry *le = &list.elems[i];
|
|
||||||
strftime(time_buffer, 80, "%Y-%m-%d %H:%M:%S", &le->modified_at);
|
|
||||||
noh_string_append_cstr(&response,
|
|
||||||
noh_arena_sprintf(
|
|
||||||
&temp,
|
|
||||||
"Entry: %s '"Nsv_Fmt"' (modified at %s).\n", le->is_dir ? "Dir" : "File", Nsv_Arg(le->name), time_buffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
return send_response(connection, 200, noh_sv_from_string(&response));
|
|
||||||
|
|
||||||
|
// Cleanup.
|
||||||
noh_da_free(&list);
|
noh_da_free(&list);
|
||||||
|
noh_string_free(&response);
|
||||||
noh_arena_reset(&temp);
|
noh_arena_reset(&temp);
|
||||||
|
|
||||||
closedir(dir);
|
closedir(dir);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
} else if (errno == ENOENT) {
|
} else if (errno == ENOENT) {
|
||||||
closedir(dir);
|
closedir(dir);
|
||||||
return send_response(connection, 404, sv(""));
|
return send_response(connection, 404, sv(""), false);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
noh_log(NOH_ERROR, "Opening directory failed.");
|
noh_log(NOH_ERROR, "Opening directory failed.");
|
||||||
return send_response(connection, 500, sv(""));
|
return send_response(connection, 500, sv(""), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
static enum MHD_Result handle_get(struct MHD_Connection *connection, SV url, SV base_path, SV username) {
|
||||||
(void)url;
|
(void)url;
|
||||||
(void)username;
|
(void)username;
|
||||||
(void)base_path;
|
(void)base_path;
|
||||||
return send_response(connection, 200, sv("TODO: Get\n"));
|
return send_response(connection, 200, sv("TODO: Get\n"), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_slash(const char c) { return c == '/'; }
|
bool is_slash(const char c) { return c == '/'; }
|
||||||
@@ -156,18 +200,18 @@ static enum MHD_Result request_handler(
|
|||||||
username = sv("demo");
|
username = sv("demo");
|
||||||
#else
|
#else
|
||||||
struct MHD_BasicAuthInfo *auth = MHD_basic_auth_get_username_password3(connection);
|
struct MHD_BasicAuthInfo *auth = MHD_basic_auth_get_username_password3(connection);
|
||||||
if (!auth) return send_response(connection, 401, sv(""));
|
if (!auth) return send_response(connection, 401, sv(""), false);
|
||||||
|
|
||||||
username = (SV){ .elems = auth->username, .count = auth->username_len };
|
username = (SV){ .elems = auth->username, .count = auth->username_len };
|
||||||
SV password = { .elems = auth->password, .count = auth->password_len };
|
SV password = { .elems = auth->password, .count = auth->password_len };
|
||||||
|
|
||||||
// TODO: Read username from user config.
|
// TODO: Read username from user config.
|
||||||
if (!noh_sv_eq(password, sv("jaja"))) return send_response(connection, 401, sv(""));
|
if (!noh_sv_eq(password, sv("jaja"))) return send_response(connection, 401, sv(""), false);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
noh_log(NOH_INFO, "Authenticated as "Nsv_Fmt, Nsv_Arg(username));
|
noh_log(NOH_INFO, "Authenticated as "Nsv_Fmt, Nsv_Arg(username));
|
||||||
|
|
||||||
if (strcmp(method, "GET") != 0) return send_response(connection, 405, sv(""));
|
if (strcmp(method, "GET") != 0) return send_response(connection, 405, sv(""), false);
|
||||||
|
|
||||||
SV url = sv(_url);
|
SV url = sv(_url);
|
||||||
noh_sv_trim_left(&url, is_slash);
|
noh_sv_trim_left(&url, is_slash);
|
||||||
@@ -175,7 +219,7 @@ static enum MHD_Result request_handler(
|
|||||||
|
|
||||||
if (noh_sv_eq_ci(controller, sv("opds"))) return handle_opds(connection, url, base_path, username);
|
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);
|
if (noh_sv_eq_ci(controller, sv("get"))) return handle_get(connection, url, base_path, username);
|
||||||
return send_response(connection, 404, sv(""));
|
return send_response(connection, 404, sv(""), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_usage(char *program_name, char *error) {
|
void show_usage(char *program_name, char *error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user