static SV path_to_name(SV path) { while (true) { SV component = noh_sv_chop_by_delim(&path, '/'); if (path.count == 0) return component; } } static void append_entry(Noh_String *response, SV url, Library_Entry entry) { noh_string_append_cstr(response, ""); noh_string_append_cstr(response, noh_arena_sprintf(&temp, ""Nsv_Fmt"", Nsv_Arg(get_id(entry.full_path)))); noh_string_append_cstr(response, noh_arena_sprintf(&temp, ""Nsv_Fmt"", entry.name)); noh_string_append_cstr(response, noh_arena_sprintf(&temp, "%s", format_time(entry))); if (entry.is_dir) { SV opds = sv("opds"); Noh_String entry_url = build_path(true, &opds, &url, &entry.name); noh_string_append_cstr(response, noh_arena_sprintf(&temp, "", Nsv_Arg(entry_url))); noh_string_free(&entry_url); } else { SV get = sv("get"); Noh_String entry_url = build_path(true, &get, &url, &entry.name); noh_string_append_cstr(response, noh_arena_sprintf(&temp, "", Nsv_Arg(entry_url))); noh_string_free(&entry_url); } noh_string_append_cstr(response, ""); } static Noh_String create_feed_response(Library_Entry self_path, SV url, Library_Entries list) { (void)self_path; Noh_String response = {0}; noh_string_append_cstr(&response, ""); noh_string_append_cstr(&response, noh_arena_sprintf(&temp, ""Nsv_Fmt"", Nsv_Arg(get_id(self_path.full_path)))); noh_string_append_cstr(&response, noh_arena_sprintf(&temp, ""Nsv_Fmt"", self_path.name)); noh_string_append_cstr(&response, noh_arena_sprintf(&temp, "%s", format_time(self_path))); SV opds = sv("opds"); Noh_String self_url = build_path(true, &opds, &url); noh_string_append_cstr(&response, noh_arena_sprintf(&temp, "", Nsv_Arg(self_url))); noh_string_free(&self_url); noh_string_append_cstr(&response, ""); for (size_t i = 0; i < list.count; i++) append_entry(&response, url, list.elems[i]); noh_string_append_cstr(&response, ""); return response; } static enum MHD_Result handle_opds(struct MHD_Connection *connection, SV url, Settings settings, SV username) { noh_arena_save(&temp); Noh_String fs_path = build_path(false, &settings.base_path, &username, &url); Noh_String fs_path_cstr = fs_path; noh_string_append_null(&fs_path_cstr); Library_Entry self_entry = { .is_dir = true, .full_path = noh_sv_from_string(fs_path), .name = (url.count == 0) ? settings.library_name : path_to_name(noh_sv_from_string(fs_path)), .modified_at = {0}, }; set_modified_time(&self_entry); noh_log(NOH_INFO, "Physical path: "Nsv_Fmt, Nsv_Arg(self_entry.full_path)); DIR *dir = opendir(fs_path_cstr.elems); if (dir) { Library_Entries list = {0}; // Build up list of entries. struct dirent *entry = noh_arena_alloc(&temp, sizeof(struct dirent)); while ((entry = readdir(dir)) != NULL) { Library_Entry result = {0}; if (create_entry(entry, fs_path_cstr.elems, &result)) noh_da_append(&list, result); } // Sort entries, folders first, then by name. if (list.count > 1) qsort(list.elems, list.count, sizeof(list.elems[0]), compare_library_entries); // Create and send response. Noh_String response = create_feed_response(self_entry, url, list); bool result = send_response(connection, 200, noh_sv_from_string(response), true); // Cleanup. noh_da_free(&list); noh_string_free(&response); noh_arena_reset(&temp); closedir(dir); return result; } else if (errno == ENOENT) { closedir(dir); noh_arena_reset(&temp); return send_response(connection, 404, sv(""), false); } else { noh_log(NOH_ERROR, "Opening directory failed."); noh_arena_reset(&temp); return send_response(connection, 500, sv(""), false); } }