diff --git a/src/DistroqueryAPI.cpp b/src/DistroqueryAPI.cpp index c7465fc..bac2296 100644 --- a/src/DistroqueryAPI.cpp +++ b/src/DistroqueryAPI.cpp @@ -56,54 +56,6 @@ void DistroqueryAPI::sendErrorResponse(string message) { exit(0); } -json DistroqueryAPI::getPackageSourceDetailsById(configTag* ct, long id) { - json j; - string sql; - sqlite3_stmt *stmt; - - auto db = openRepositoryDatabase(ct); - if (!db) { - j["error"] = "error opening sources database for repository " + string(ct->tag); - return j; - } - sql = "SELECT * FROM sources,packagers WHERE sources.id=" + to_string(id) + - " AND sources.id_packager=packagers.id"; - if (!sqlite3_prepare_v2(db, sql.c_str(), sql.length(), &stmt, NULL) == SQLITE_OK) { - j["error"] = "error preparing query '" + sql + "'"; - return j; - } - if (sqlite3_step(stmt) == SQLITE_ROW) { - j["id"] = sqlite3_column_int(stmt,sqlite3_find_column_id(stmt, NULL, "id")); - j["name"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "name"))); - j["summary"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "summary"))); - j["epoch"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "epoch"))); - j["version"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "version"))); - j["release"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "release"))); - j["description"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "description"))); - j["group"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "groupdescr"))); - j["license"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "license"))); - j["url"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "url"))); - //get_favicon_from_url((const char*)sqlite3_column_text(stmt1,sqlite3_find_column_id(stmt1, NULL, "url")),buffer,PATH_MAX); - j["size"] = sqlite3_column_int(stmt,sqlite3_find_column_id(stmt, NULL, "size")); - j["maintainer"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, "packagers", "name"))); - // Convert buildtime to ISO-8601 - auto itt = (time_t)sqlite3_column_int(stmt,sqlite3_find_column_id(stmt, NULL, "buildtime")); - ostringstream ss; - ss << std::put_time(gmtime(&itt), "%FT%TZ"); - j["buildtime"] = ss.str(); - // Download URL - j["download_url"] = string(ct->download_prefix) + ct->download_dir + "/SRPMS.base/" + - string(j["name"]) + "-" + string(j["version"]) + "-" + string(j["release"]) + ".src.rpm"; - // Source URL - string name = string(j["name"]); - j["source_url"] = "https://src.openmamba.org/rpms/" + replaceAll(name, "+", "Plus"); - } else { - j["error"] = "no results from query '" + sql + "'"; - return j; - } - return j; -} - json DistroqueryAPI::getRepositoryPackages(string repository, int per_page, int page, string query) { json j; string sql; @@ -196,11 +148,60 @@ json DistroqueryAPI::getRepositoryPackages(string repository, int per_page, int return j; } -json DistroqueryAPI::getBuiltPackagesFromSourceID(configTag* ct, long id) { - json j = json::array(); +json DistroqueryAPI::getPackageSourceDetailsById(configTag* ct, long id) { + json j; string sql; sqlite3_stmt *stmt; + auto db = openRepositoryDatabase(ct); + if (!db) { + j["error"] = "error opening sources database for repository " + string(ct->tag); + return j; + } + sql = "SELECT * FROM sources,packagers WHERE sources.id=" + to_string(id) + + " AND sources.id_packager=packagers.id"; + if (!sqlite3_prepare_v2(db, sql.c_str(), sql.length(), &stmt, NULL) == SQLITE_OK) { + j["error"] = "error preparing query '" + sql + "': " + sqlite3_errmsg(db); + return j; + } + if (sqlite3_step(stmt) == SQLITE_ROW) { + j["id"] = sqlite3_column_int(stmt,sqlite3_find_column_id(stmt, NULL, "id")); + j["name"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "name"))); + j["summary"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "summary"))); + j["epoch"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "epoch"))); + j["version"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "version"))); + j["release"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "release"))); + j["description"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "description"))); + j["group"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "groupdescr"))); + j["license"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "license"))); + j["url"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "url"))); + //get_favicon_from_url((const char*)sqlite3_column_text(stmt1,sqlite3_find_column_id(stmt1, NULL, "url")),buffer,PATH_MAX); + j["size"] = sqlite3_column_int(stmt,sqlite3_find_column_id(stmt, NULL, "size")); + j["maintainer"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, "packagers", "name"))); + // Convert buildtime to ISO-8601 + auto itt = (time_t)sqlite3_column_int(stmt,sqlite3_find_column_id(stmt, NULL, "buildtime")); + ostringstream ss; + ss << std::put_time(gmtime(&itt), "%FT%TZ"); + j["buildtime"] = ss.str(); + // Download URL + j["download_url"] = string(ct->download_prefix) + ct->download_dir + "/SRPMS.base/" + + string(j["name"]) + "-" + string(j["version"]) + "-" + string(j["release"]) + ".src.rpm"; + // Source URL + string name = string(j["name"]); + j["source_url"] = "https://src.openmamba.org/rpms/" + replaceAll(name, "+", "Plus"); + } else { + j["error"] = "no results from query '" + sql + "'"; + return j; + } + return j; +} + +json DistroqueryAPI::getBuiltPackagesFromSourceID(configTag* ct, long id) { + json j = {}; + string sql; + sqlite3_stmt *stmt; + + json archs = {}; for (auto arch: ct->arch) { if (arch == NULL) break; auto db = openRepositoryDatabase(ct, arch); @@ -213,18 +214,91 @@ json DistroqueryAPI::getBuiltPackagesFromSourceID(configTag* ct, long id) { sql = "SELECT * FROM packages WHERE id_source = " + to_string(id) + " ORDER BY name"; if (sqlite3_prepare_v2(db, sql.c_str(), sql.length(), &stmt, NULL) == SQLITE_OK) { while (sqlite3_step(stmt) == SQLITE_ROW) { + if (!archs.contains(arch)) { + archs[arch] = json::array(); + } json package = {}; - package["arch"] = arch; package["name"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "name"))); - j.push_back(package); + archs[arch].push_back(package); } sqlite3_finalize(stmt); } else { j = {}; - j["error"] = "error preparing query '" + sql + "'"; + j["error"] = "error preparing query '" + sql + "': " + sqlite3_errmsg(db); return j; } } + j["archs"] = archs; + return j; +} + +json DistroqueryAPI::getProvidersForRequirement(string repository, string requirement, string flags, string version) { + json j; + sqlite3_stmt *stmt; + + struct configTag* ct = findRepositoryByTag(repository.c_str()); + if (ct == NULL) { + j["error"] = "repository with tag '" + repository + "' does not exist"; + return j; + } + + auto db = openRepositoryDatabase(ct); + if (!db) { + j["error"] = "error opening sources database for repository " + string(ct->tag); + return j; + } + + string sql = "SELECT u.* FROM ("; + + int intflags = rpmSenseStringToFlags(flags); + + int a = 0; + for (auto arch: ct->arch) { + if (arch == NULL) break; + attachCtDatabases(ct, db, arch); + int i = 0; + while (ct->repository[i]) { + if (a + i > 0) sql += " UNION "; + sql += "SELECT *,'" + string(ct->repository[i]->tag) + "' AS repository,'" + + arch + "' AS arch " + "FROM '" + string(ct->repository[i]->tag) + "_" + arch + "'.provides,'" + + string(ct->repository[i]->tag) + "_" + arch + "'.packages " + "WHERE providename='" + requirement + "' AND packages.id=provides.id_package"; + if (flags != "") { + sql += " AND provideflags='" + to_string(intflags) + "' AND provideversion='" + version + "'"; + } + i++; + } + a++; + } + + sql += ") AS u ORDER BY providename COLLATE NOCASE ASC"; + + json archs = {}; + + if (sqlite3_prepare_v2(db, sql.c_str(), sql.length(), &stmt, NULL) == SQLITE_OK) { + json providers = json::array(); + while (sqlite3_step(stmt) == SQLITE_ROW) { + string arch = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "arch"))); + if (!archs.contains(arch)) { + archs[arch] = json::array(); + } + json provider = {}; + provider["repository"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "repository"))); + provider["name"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "name"))); + provider["flags"] = rpmSenseFlagsToString(sqlite3_column_int(stmt,sqlite3_find_column_id(stmt, NULL, "provideflags"))); + provider["version"] = reinterpret_cast(sqlite3_column_text(stmt,sqlite3_find_column_id(stmt, NULL, "provideversion"))); + archs[arch].push_back(provider); + } + //j["providers"] = providers; + sqlite3_finalize(stmt); + } else { + j = {}; + j["error"] = "error preparing query '" + sql + "': " + sqlite3_errmsg(db); + return j; + } + j["providers"] = {}; + j["providers"]["archs"] = archs; return j; } @@ -267,6 +341,8 @@ json DistroqueryAPI::getPackageSourceDetails(string repository, string package) buildrequire["name"] = reinterpret_cast(sqlite3_column_text(stmt2,sqlite3_find_column_id(stmt2, NULL, "buildrequirename"))); buildrequire["flags"] = rpmSenseFlagsToString(sqlite3_column_int(stmt2,sqlite3_find_column_id(stmt2, NULL, "buildrequireflags"))); buildrequire["version"] = reinterpret_cast(sqlite3_column_text(stmt2,sqlite3_find_column_id(stmt2, NULL, "buildrequireversion"))); + json jproviders = getProvidersForRequirement(repository, buildrequire["name"], buildrequire["flags"], buildrequire["version"]); + buildrequire["providers"] = jproviders["providers"]; j["buildrequires"].push_back(buildrequire); } sqlite3_finalize(stmt2); @@ -504,6 +580,18 @@ void DistroqueryAPI::getApiResponse(string path_info) { details = getPackageDetails(path_split[1], package, path_split[3]); } cout << details.dump(); + } else if (path_split[0] == "providers") { + // API service: package + if (path_split.size() != 3 && path_split.size() != 5) + sendErrorResponse("expected 3 or 5 arguments for " + path_split[0]); + json providers; + if (path_split.size() == 3) + providers = getProvidersForRequirement(path_split[1], urlDecode(path_split[2]), + "", ""); + else if (path_split.size() == 5) + providers = getProvidersForRequirement(path_split[1], urlDecode(path_split[2]), + urlDecode(path_split[3]), urlDecode(path_split[4])); + cout << providers.dump(); } else { sendErrorResponse("invalid request for service '" + path_split[0]); } diff --git a/src/DistroqueryAPI.hpp b/src/DistroqueryAPI.hpp index 4edb947..41d146f 100644 --- a/src/DistroqueryAPI.hpp +++ b/src/DistroqueryAPI.hpp @@ -43,6 +43,7 @@ class DistroqueryAPI { void sendErrorResponse(string message); json getRepositoryPackages(string repository, int per_page, int page, string query); json getPackageSourceDetailsById(configTag* ct, long id); + json getProvidersForRequirement(string repository, string requirement, string flags, string version); json getPackageSourceDetails(string repository, string package); json getBuiltPackagesFromSourceID(configTag* ct, long id); json getPackageDetails(string repository, string package, string arch); diff --git a/src/distroquery_functions.cpp b/src/distroquery_functions.cpp index 9db5628..42ba21d 100644 --- a/src/distroquery_functions.cpp +++ b/src/distroquery_functions.cpp @@ -45,6 +45,15 @@ string rpmSenseFlagsToString(int flags) { return ret; } +int rpmSenseStringToFlags(string flags) { + if (flags == "=") return RPMSENSE_EQUAL; + else if (flags == "<") return RPMSENSE_LESS; + else if (flags == ">") return RPMSENSE_GREATER; + else if (flags == "<=") return RPMSENSE_LESS | RPMSENSE_EQUAL; + else if (flags == ">=") return RPMSENSE_GREATER | RPMSENSE_EQUAL; + return RPMSENSE_ANY; +} + sqlite3* openRepositoryDatabase(struct configTag* ct, string arch, string append) { string dbname; sqlite3* db; diff --git a/src/include/distroquery_functions.hpp b/src/include/distroquery_functions.hpp index 8294ff5..f9f0a5f 100644 --- a/src/include/distroquery_functions.hpp +++ b/src/include/distroquery_functions.hpp @@ -27,6 +27,7 @@ using namespace std; string rpmSenseFlagsToString(int flags); +int rpmSenseStringToFlags(string flags); vector split(string str, string token); sqlite3* openRepositoryDatabase(struct configTag* ct, string arch = "", string append = ""); void attachCtDatabases(struct configTag* ct, sqlite3 *db, string arch = "");