ACC SHELL

Path : /usr/share/YaST2/modules/
File Upload :
Current File : //usr/share/YaST2/modules/WebpinPackageSearch.ycp

/**
 * 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