ACC SHELL
/**
* File:
* WebpinPackageSearch.ycp
*
* Module:
* WebpinPackageSearch
*
* Summary:
* YaST API to api.opensuse-community.org
*
* Authors:
* Lukas Ocilka <locilka@suse.cz>
* Katarina Machalkova <kmachalkova@suse.cz>
*/
{
module "WebpinPackageSearch";
textdomain "packager";
import "HTTP";
import "SuSERelease";
import "Directory";
import "FileUtils";
import "URL";
import "XML";
import "Arch";
string temporary_file = Directory::tmpdir + "/package_search_webpin.xml";
/**
* Base search URL, default is "http://api.opensuse-community.org/searchservice/"
*/
string base_url = "http://api.opensuse-community.org/searchservice/";
/**
* Sets base search URL
* WARNING: Use this only if you know what you are doing!
*
* @param string new base_url
*/
global void SetBaseSearchURL (string new_base_url) {
if (new_base_url == "" || new_base_url == nil) {
y2error ("Wrong base URL: '%1'", new_base_url);
return;
}
base_url = new_base_url;
y2milestone ("New base URL has been set: %1", base_url);
}
/**
* Returns base search URL
*
* @string current base_url
*/
global string GetBaseSearchURL () {
return base_url;
}
/**
* List of architecture dependencies. Every row contains one list
* of dependencies sortred from left (the newest) to right (the oldest).
* Architectures are backward-compatible.
*/
list <list <string> > deps = [
// i386 and x86
["x86_64", "i686", "i586", "i486", "i386", "noarch", "src"],
// PPC
["ppc64", "ppc", "noarch", "src"],
// s390
["s390_64", "s390_32", "noarch", "src"],
// IA-64
["ia64", "noarch", "src"],
// Alpha
["alphaev67", "alphaev6", "alphapca56", "alphaev56", "alphaev5", "alpha", "noarch", "src"],
// SPARC
["sparc64", "sparcv9", "sparcv8", "sparc", "noarch", "src"],
// MIPS
["mips64", "mips", "noarch", "src"],
];
/**
* Returns whether the current architecture matches the list of architectures
* got as a parameter. The current architecture is backward compatible, so
* also dependencies are taken into account.
*
* @param list <string> list of architectures to check
* @return boolean whether they match the current architecture
*
* @example
* // current architecture is "i386"
* MatchesCurrentArchitecture (["noarch", "ppc", "i386"]) -> true
* MatchesCurrentArchitecture (["noarch"]) -> true
* MatchesCurrentArchitecture (["x86_64"]) -> false
* MatchesCurrentArchitecture (["ppc"]) -> false
*/
global boolean MatchesCurrentArchitecture (list <string> archs_to_check) {
string current_arch = Arch::architecture();
boolean matches = false;
// one or more archs supported by source
foreach (string one_arch_to_check, archs_to_check, {
// check all arch dependencies
foreach (list <string> one_archlist, deps, {
// both current and checked architectures are in the same list
if (contains (one_archlist, current_arch) && contains (one_archlist, one_arch_to_check)) {
integer cur_arch_row = nil;
integer match_arch_row = nil;
integer str_offset = -1;
// find current_architecture in deps
foreach (string one_arch, one_archlist, {
str_offset = str_offset + 1;
if (one_arch == current_arch) {
cur_arch_row = str_offset;
break;
}
});
str_offset = -1;
// find architecture_to_check in deps
foreach (string one_arch, one_archlist, {
str_offset = str_offset + 1;
if (one_arch == one_arch_to_check) {
match_arch_row = str_offset;
break;
}
});
// compare
if (match_arch_row >= cur_arch_row) {
matches = true;
break;
}
}
});
if (matches) break;
});
return matches;
}
string already_guessed = nil;
/**
* Guesses the current distribution installed
*
* @return string distribution (Webpin format)
*
* @examle
* // Installed openSUSE 11.0
* GuessCurrentDistribution() -> "openSUSE_110"
*/
string GuessCurrentDistribution () {
if (already_guessed != nil)
return already_guessed;
string rel_name = SuSERelease::ReleaseName();
string rel_version = SuSERelease::ReleaseVersion();
if (regexpmatch (rel_version, "\.")) {
rel_version = mergestring (splitstring (rel_version, "\."), "");
}
already_guessed = sformat ("%1_%2", rel_name, rel_version);
return already_guessed;
}
/**
* Changes the Webpin distro format to OneClickInstall format.
*
* @param string distro
* @return string modified distro
*
* @example
* ModifyDistro ("openSUSE_110") -> "openSUSE 11.0"
*/
string ModifyDistro (string distro) {
if (regexpmatch (distro, "^.*_[0123456789]+[0123456789]$")) {
distro = regexpsub (distro, "^(.*)_([0123456789]+)([0123456789])$", "\\1 \\2.\\3");
} else if (regexpmatch (distro, "^.*_[0123456789]+")) {
distro = regexpsub (distro, "^(.*)_([0123456789]+)$", "\\1 \\2");
} else {
y2warning ("'%1' doesn't match any known regexp", distro);
}
return distro;
}
/**
* Writes XML configuration file for OnleClickInstall client.
*
* @param list <map> packages_to_install (in the same format as got from SearchForPackages function)
* @param string save_to_file
* @return boolean if successful
*
* @see SearchForPackages() for the format of <map> package_to_install
*/
global boolean PrepareOneClickInstallDescription (list <map> packages_to_install, string save_to_file) {
if (FileUtils::Exists (save_to_file)) {
y2warning ("File %1 already exists, removing", save_to_file);
SCR::Execute (.target.remove, save_to_file);
}
string distro = "";
string repoURL = "";
map write_xml = $[
"metapackage" : $["xmlns" : "http://opensuse.org/Standards/One_Click_Install"]
];
map repositories = $[];
map packages = $[];
foreach (map one_package, packages_to_install, {
distro = one_package["distro"]:GuessCurrentDistribution();
repoURL = one_package["repoURL"]:"";
// all repositories
repositories[distro] = add (repositories[distro]:[], repoURL);
// all packages
packages[distro] = add (packages[distro]:[], $[
"item" : [$[
"name" : [$[ "content" : one_package["name"]:"" ]],
"summary" : [$[ "content" : one_package["summary"]:"" ]],
"description" : [
$[
"content" :
// TRANSLATORS: package description item
one_package["description"]:one_package["summary"]:_("No further information available.")
]
],
]]
]);
});
foreach (string distro, list <string> distro_repos, (map <string, list <string> >) repositories, {
map group = $[
"distversion" : ModifyDistro (distro),
"remainSubscribed" : $[ "content" : "true" ],
"repositories" : toset (maplist (string one_repo, distro_repos, {
return $[ "repository" : [$[
"name" : [$[ "content" : one_repo ]],
"url" : [$[ "content" : one_repo ]],
// TRANSLATORS: repository summary
"summary" : [$[ "content" : _("Unknown repository") ]],
// TRANSLATORS: repositry description
"description" : [$[ "content" : _("No further information available, use at your own risk.") ]],
]]];
})),
"software" : toset (packages[distro]:[]),
];
write_xml["metapackage", "group"] = group;
});
y2debug ("Writing: %1", write_xml);
boolean success = (boolean) SCR::Write (.anyxml, $[
"xml" : write_xml,
"file" : save_to_file,
"args" : $[
"RootName" : "metapackage",
"KeepRoot" : true,
"XMLDecl" : "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>",
],
]);
if (success != true) {
y2error ("Unable to write XML to %1", save_to_file);
}
return success;
}
/**
* Searches for packages using Webpin XML API.
*
* @param string search_for text
* @param string distribution, e.g., openSUSE_10.3, it is guessed from the system if set to nil
* @param map <string, boolean> search_in which defines in which sources it searches,
* possible keys: name, description, contents
*
* @example
* SearchForPackages (
* "desktop",
* nil,
* $[
* "name" : true,
* "description" : true,
* "contents" : false,
* ]
* )
* ->
* [
* ...
* $[
* "archs":[
* "ppc"
* ],
* "checksum":"e0cbdbf03ce47dfd5c5f885b86706ddfa023d8dc",
* "distro":"openSUSE_110",
* "name":"xfce4-desktop",
* "priority":"5",
* "repoURL":"http://download.opensuse.org/distribution/11.0/repo/oss/suse",
* "summary":"Desktop manager for the Xfce Desktop Environment",
* "version":"4.4.2"
* ],
* ...
* ]
*/
global list <map> SearchForPackages (string search_for, string distribution, map <string, boolean> search_in) {
y2milestone ("Searching for %1 in %2", search_for, search_in);
if (search_for == nil || search_for == "") {
y2error ("empty search string");
return [];
}
// search URL is the same for both
boolean name_or_descr = (search_in["name"]:false || search_in["description"]:false);
string search_path = nil;
if (search_in["contents"]:false && name_or_descr) {
search_path = "Search/Simple/";
} else if (name_or_descr) {
search_path = "Search/ByName/";
} else if (search_in["contents"]:false) {
search_path = "Search/ByContents/";
} else {
y2warning ("empty search result");
return [];
}
// if distro string is nil, try to guess the current one
if (distribution == nil) {
distribution = GuessCurrentDistribution();
y2milestone ("Distribution not set, guessing '%1'", distribution);
}
string url = base_url + search_path + distribution + "/" + URL::EscapeString (search_for, URL::transform_map_passwd);
y2milestone ("HTTP::Get (%1, %2)", url, temporary_file);
map response = HTTP::Get (url, temporary_file);
y2milestone ("Server response: %1", response);
list <map> ret_list = [];
// something's screwed up on server side - this usually means that tmp file
// is full of error messages - we should not let anyxml agent parse those
if ( response["code"]:0 != 200 ) {
y2error ("Cannot retrieve search results from the server");
return nil;
}
if (! FileUtils::Exists (temporary_file)) {
y2error ("Cannot read file: %1", temporary_file);
return nil;
}
if (FileUtils::GetSize (temporary_file) <= 0) {
y2milestone ("Empty file: %1", temporary_file);
return nil;
}
map search_result = (map) SCR::Read (.anyxml, temporary_file);
y2debug ("Search result: %1", search_result);
integer counter = -1;
map one_entry = $[];
foreach (map <string, list> one_package, (list <map <string, list> >) search_result["ns2:packages", 0, "package"]:[], {
one_entry = $[];
foreach (string key, list value, one_package, {
if (haskey (value[0]:$[], "content")) {
one_entry[key] = tostring (value[0,"content"]:nil);
} else if (key == "archs") {
foreach (map one_arch, (list <map>) value, {
foreach (map xone_arch, (list <map>) one_arch["arch"]:[], {
one_entry["archs"] = add (one_entry["archs"]:[], xone_arch["content"]:"");
});
});
} else {
y2error ("Unknown key: %1", key);
}
});
counter = counter + 1;
ret_list[counter] = one_entry;
});
integer nr_packages_found = size (ret_list);
if (nr_packages_found == nil || nr_packages_found == 0) {
y2warning ("Nothing found");
} else {
y2milestone ("%1 packages found", nr_packages_found);
}
return ret_list;
}
} // module end
ACC SHELL 2018