diff --git a/.vscode/settings.json b/.vscode/settings.json index 79e1088..98fa0f9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,6 +5,9 @@ "vector": "cpp", "string": "cpp", "format": "cpp", - "string.h": "c" + "string.h": "c", + "chrono": "cpp", + "text_encoding": "cpp", + "typeinfo": "cpp" } } \ No newline at end of file diff --git a/src/DistroqueryAPI.cpp b/src/DistroqueryAPI.cpp index cd57c05..0d3a161 100644 --- a/src/DistroqueryAPI.cpp +++ b/src/DistroqueryAPI.cpp @@ -416,13 +416,90 @@ json DistroqueryAPI::getPackageSourceDetails(string repository, string package) } } else { - j["error"] = "error preparing query '" + sql + "'"; + j["error"] = "error preparing query '" + sql + "': " + sqlite3_errmsg(db); return j; } sqlite3_close(db); return j; } +string DistroqueryAPI::resolvePackageFilePathById(sqlite3 *db, long id) { + string sql; + sqlite3_stmt *stmt; + string outpath=""; + static long cachedid = -1; + static string cachedpath = ""; + + if (id == cachedid) + return cachedpath; + + cachedid = id; + + while (id >= 0) { + sql = "SELECT parent,name FROM files WHERE id=" + to_string(id); + if (sqlite3_prepare_v2(db, sql.c_str(), sql.length(), &stmt, NULL) == SQLITE_OK) { + if (sqlite3_step(stmt) == SQLITE_ROW) { + id = sqlite3_column_int(stmt,sqlite3_find_column_id(stmt, NULL, "parent")); + string name = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "name"))); + outpath = "/" + name + outpath; + } + } else { + return "?"; + } + } + + cachedpath = outpath; + return outpath; +} + +json DistroqueryAPI::getPackageFiles(string repository, string package, string arch) { + json j; + string sql; + sqlite3_stmt *stmt; + + struct configTag* ct = findRepositoryByTag(repository.c_str()); + if (ct == NULL) { + j["error"] = "repository with tag '" + repository + "' does not exist"; + return j; + } + + if (ct->repodata_url == NULL) { + j["error"] = "repository with tag '" + repository + "' is not supported by this API"; + return j; + } + + auto db = openRepositoryDatabase(ct, arch, "-files"); + if (!db) { + j["error"] = "error opening database for repository " + repository + " and arch " + arch; + return j; + } + + sql = "SELECT files.id,files.parent,files.name FROM files,packages_files_rel WHERE packages_files_rel.name = '" + package + "' " + + "AND files.id=packages_files_rel.id_file"; + if (sqlite3_prepare_v2(db, sql.c_str(), sql.length(), &stmt, NULL) == SQLITE_OK) { + json files = json::array(); + while (sqlite3_step(stmt) == SQLITE_ROW) { + json file; + long parent = sqlite3_column_int(stmt,sqlite3_find_column_id(stmt, NULL, "parent")); + file["path"] = resolvePackageFilePathById(db, parent) + "/" + + reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "name"))); + // TODO: unusable/missing values + /*file["flags"] = sqlite3_column_int(stmt,sqlite3_find_column_id(stmt, "packages_files_rel", "flags")); + file["user"] = sqlite3_column_int(stmt,sqlite3_find_column_id(stmt, "packages_files_rel", "user")); + file["group"] = sqlite3_column_int(stmt,sqlite3_find_column_id(stmt, "packages_files_rel", "group"));*/ + files.push_back(file); + } + j["files"] = files; + } else { + j["error"] = "error preparing query '" + sql + "': " + sqlite3_errmsg(db); + return j; + } + + sqlite3_close(db); + return j; + +} + json DistroqueryAPI::getPackageDetails(string repository, string package, string arch) { json j; string sql; @@ -534,8 +611,15 @@ json DistroqueryAPI::getPackageDetails(string repository, string package, string sqlite3_finalize(stmt2); } + // Package files + json files = getPackageFiles(repository, package, arch); + if (files.contains("files")) + j["files"] = files["files"]; + else + j["files"] = files; + } else { - j["error"] = "error preparing query '" + sql + "'"; + j["error"] = "error preparing query '" + sql + "': " + sqlite3_errmsg(db); return j; } @@ -698,6 +782,14 @@ void DistroqueryAPI::getApiResponse(string path_info) { details = getPackageDetails(path_split[1], package, path_split[3]); } cout << details.dump(); + } else if (path_split[0] == "package_files") { + // API service: package_files + if (path_split.size() != 4) + sendErrorResponse("expected 4 arguments for " + path_split[0]); + json details; + string package = urlDecode(path_split[2]); + details = getPackageFiles(path_split[1], package, path_split[3]); + cout << details.dump(); } else if (path_split[0] == "providers") { // API service: providers if (path_split.size() != 3 && path_split.size() != 5) @@ -717,7 +809,7 @@ void DistroqueryAPI::getApiResponse(string path_info) { json problems = getRepositoryProblems(path_split[1]); cout << problems.dump(); } else { - sendErrorResponse("invalid request for service '" + path_split[0]); + sendErrorResponse("unknown service: '" + path_split[0]); } } else { sendErrorResponse("empty request"); diff --git a/src/DistroqueryAPI.hpp b/src/DistroqueryAPI.hpp index dbe2e29..ddb8e96 100644 --- a/src/DistroqueryAPI.hpp +++ b/src/DistroqueryAPI.hpp @@ -48,7 +48,9 @@ class DistroqueryAPI { json getProvidersForRequirement(string repository, string requirement, string flags, string version, string onlyarch=""); json getPackageSourceDetails(string repository, string package); + string resolvePackageFilePathById(sqlite3 *db, long id); json getBuiltPackagesFromSourceID(configTag* ct, long id); + json getPackageFiles(string repository, string package, string arch); json getPackageDetails(string repository, string package, string arch); json getRepositoryProblems(string repository); };