ACC SHELL

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

/**
 * File:	modules/SourceManager.ycp
 * Package:	Package Repository Management
 * Summary:	SourceManager settings, input and output functions
 * Authors:	Anas Nashif <nashif@suse.de>
 *		Lukas Ocilka <locilka@suse.cz>
 *		Martin Vidner <mvidner@suse.cz>
 * Status:      Work in Progress
 *
 * $Id: SourceManager.ycp 60257 2010-01-07 12:46:56Z lslezak $
 *
 * Representation of the configuration of source-manager.
 * Input and output routines.
 */

{

textdomain "packager";
module "SourceManager";

import "Progress";
import "Report";
import "Popup";
import "Label";
import "Summary";
import "HTML";
import "Mode";
import "URL";
import "Linuxrc";
import "Installation";
import "String";


global list<integer> newSources = [];

global integer numSources = 0;
    
global list<integer> sourceStates = [];

global list<map<string,any> > sourceStatesIn = [];

global list<map<string,any> > sourceStatesOut = [];


global map url_tokens = $[];

global string currentUrl = "";

// Sources that are removed in memory but still not in libzypp
// They will be removed in Write() at the end
global list <integer> just_removed_sources = [];

/**
 * Prototypes
 */
global boolean Modified();
global symbol createSource( string url );

/**
 * Data was modified?
 */
global boolean modified = false;

/**
 */
global boolean proposal_valid = false;

/**
 * Abort function
 * return boolean return true if abort
 */
global boolean AbortFunction() {
    return false;
    }

/**
 * Abort function
 * @return boolean return true if abort
 */
global define boolean Abort() ``{
    if(AbortFunction != nil)
    {
	return AbortFunction () == true;
    }
    return false;
}

/**
 * Data was modified?
 * @return true if modified
 */
global boolean Modified() {
    y2debug("modified=%1",modified);
    //return modified;
    return (sourceStatesIn != sourceStatesOut);
}

global boolean ReadSources()
{
    boolean success = Pkg::SourceStartManager( false );
    if (!success)
        return success;
    sourceStates = Pkg::SourceStartCache ( false );
    sourceStatesIn = Pkg::SourceEditGet();
    sourceStatesOut = sourceStatesIn;
    return true;
}

/**
 * Read all source-manager settings
 * @return true on success
 */
global boolean Read() {

    /* SourceManager read dialog caption */
    string caption = _("Initializing Available Repositories");

    integer steps = 2;


    // We do not set help text here, because it was set outside
    Progress::New( caption, " ", steps, [
	    /* Progress stage 1/3 */
	    _("Read configured repositories"),
	    /* Progress stage 2/3 */
	    _("Detect available repositories via SLP")
	], [
	    /* Progress step 1/3 */
	    _("Reading configured repositories..."),
	    /* Progress step 2/3 */
	    _("Detecting available repositories..."),
	    /* Progress finished */
	    _("Finished")
	],
	""
    );

    // read database
    if(Abort()) return false;
    Progress::NextStage();


    /* Error message */
    if(!ReadSources()) Report::Error(_("Cannot read repositories."));

    // read another database
    if(Abort()) return false;
    Progress::NextStep();


    /* Error message */
    if(false) Report::Error(_("Cannot detect available repositories."));

    if(Abort()) return false;
    /* Progress finished */
    Progress::NextStage();

    if(Abort()) return false;
    modified = false;
    return true;
}

/**
 * Commit changed repositories
 */ 
global boolean CommitSources() {
    y2debug("In: %1  Out: %2",   sourceStatesIn, sourceStatesOut );
    boolean success = false;
    while (true)
    {
        success = Pkg::SourceEditSet( sourceStatesOut );
        if ( !success ) {
	    // popup message header
            string _msg1 = _("Unable to save changes to the repository.
");
	    // popup message, after message header, header of details
            string _msg2 = _("Details:") + "\n" + Pkg::LastError();
	    // end of popup message, question
            _msg2 = _msg2 + "\n" + _("Try again?");

            boolean tryagain = Popup::YesNo( _msg1 + "\n" + _msg2 );
            if (tryagain )
                continue;
            else
                break;
        }
        else
        {
            break;
        }
    }
    return success;
}

/**
 * Write all repository-manager settings
 * @return true on success
 */
global boolean Write() {
    
    /* SourceManager read dialog caption */
    string caption = _("Saving Repository Configuration");

    integer steps = 1;

    // We do not set help text here, because it was set outside
    Progress::New(caption, " ", steps, [
	    /* Progress stage 1/1 */
	    _("Write repository settings"),
	], [
	    /* Progress step 1/1 */
	    _("Writing the settings..."),

	    /* Progress finished */
	    _("Finished")
	],
	""
    );

    // write settings
    if(Abort()) return false;
   
    Progress::NextStage();
    /* Error message */

    boolean exit = CommitSources();

        
    
    if(Abort()) return false;
    /* Progress finished */
    Progress::NextStage();

    if(Abort()) return false;
    
    return exit;
}

/**
 * Get all repository-manager settings from the first parameter
 * (For use by autoinstallation.)
 * @param settings The YCP structure to be imported.
 * @return boolean True on success
 */
global boolean Import (map settings) {
    return true;
}

/**
 * Dump the repository-manager settings to a single map
 * (For use by autoinstallation.)
 * @return map Dumped settings (later acceptable by Import ())
 */
global map Export () {
    return $[];
}

/**
 * Get Repository ID by index 
 */
global integer GetSrcIdByIndex(integer idx)   {
    
    integer SrcId = sourceStatesOut[idx, "SrcId"]:-1;
  
    return SrcId;
}

/**
 * Set current used repository URL by index 
 */
global void SetUrlByIndex(integer idx)   {
    
    integer SrcId = sourceStatesOut[idx, "SrcId"]:-1;
    currentUrl = Pkg::SourceGeneralData(SrcId)["url"]:"";
    return;
}


/**
 * Get Repository ID when only URL is known 
 */
global define integer getSourceId( string url ) {
    
    numSources = size( sourceStatesOut );
    integer i = 0;
    integer id = -1;
    while ( i < numSources )
    { 
        map generalData = Pkg::SourceGeneralData(sourceStatesOut[i, "SrcId"]:-1 );
	if ( generalData[ "url" ]:"" == url )
	{
	    id = sourceStatesOut[i, "SrcId"]:-1;
	    break;
	}

	i = i + 1;
    }
    
    return id;
}






/**
 * Gather Repository Metadata
 */
global define map SourceData(integer source) ``{
    map g =  Pkg::SourceGeneralData( source );
    y2milestone("generalData: %1", g);
    map p =  Pkg::SourceProductData( source );
    if (p == nil)
    {
	p = $[];
    }

    y2milestone("productData: %1", p);
    return ((map)union(g,p));
}

/**
 * Create a repository from an URL
 */
global symbol createSource( string url ) {
    
    if ( url != "" )
    {
	if (!Mode::commandline())
	{
	    UI::OpenDialog(
			   `VBox(
				 `VSpacing( 0.2 ),
				 `Label( _("Adding repository...") ),
				 `VSpacing( 0.2 )
				 )
			   );
	}
	newSources = Pkg::SourceScan( url, "" );

	if (!Mode::commandline())	
	    UI::CloseDialog();

	
	if ( size( newSources ) == 0  )
	{
	    string _msg1 = sformat( _("Unable to create repository
from URL '%1'."), URL::HidePassword(url) );

	    string _msg2 = _("Details:") + "\n" + Pkg::LastError();
	    // end of popup message, question
	    _msg2 = _msg2 + "\n" + _("Try again?");

	    boolean tryagain = Popup::YesNo( _msg1 + "\n" + _msg2 );
	    if ( tryagain ) return `again;
	    else return `cancel;
	}
	else
	{
	    list<integer> ul_sources = filter (integer s, newSources, {
		map src_data = Pkg::SourceGeneralData (s);
		string src_type = src_data["type"]:"";
		return src_type == "YaST";
	    });
	    if (size (ul_sources) == 0)
	    {
		if (! Popup::AnyQuestion (
					  Popup::NoHeadline (),
// continue-back popup
					  _("There is no product information available at the given location.
If you expected to address a product, return back and enter
the correct location.
To make rpm packages located at the specified location available
in the packages selection, continue."),
					  Label::ContinueButton (),
					  Label::BackButton (),
					  `focus_yes))
		{
		    return `again;
		}
	    }
	    foreach( integer id, newSources, ``{
		map<string, any> sourceState = $[ "SrcId": id, "enabled": true ];
		sourceStatesOut = add( sourceStatesOut, sourceState ); 
	    } );
	    return `ok;
	}
    }
    return `cancel;
}

/**
 * Delete repository by Repository ID
 */
global void deleteSourceBySrcId( integer SrcId )   {

    y2debug("removing repository: %1 %2", SrcId,   sourceStatesOut );
    numSources = size( sourceStatesOut );
    integer i = 0;
    
    while ( i < numSources )
    {

	if ( sourceStatesOut[i, "SrcId"]:-1 == SrcId )
	{	    
	    sourceStatesOut = remove( sourceStatesOut, i );
	    break;
	}        

	i = i + 1;
    }
    return;
    
}


/**
 * Delete Repository by the repository index
 */
global void deleteSourceByIndex (integer idx ) {
    
    sourceStatesOut = remove( sourceStatesOut, idx );
    return;
}
    
/**
 * Delete Repository by repository URL
 */
global void deleteSourceByUrl (string url ) {
    deleteSourceBySrcId(getSourceId(url));    
    return;
}

/**
 * Create Summary Item
 */
define string createItem( integer index, map source ) {
    integer id = source[ "SrcId" ]:0;
    map generalData = Pkg::SourceGeneralData( id );
    map productData = Pkg::SourceProductData( id );
    string sitem = "";
    string status = source[ "enabled" ]:true
	// status info, to be used inside summary
	? _("Enabled")
	// status info, to be used inside summary
	: _("Disabled");
    string color = source[ "enabled" ]:true ? "#006600" : "#FF0000";
    sitem = sitem + HTML::Colorize("["+status+"] ",
            color);
    // translators: name of a repository if no other idenfication found
    sitem = sitem + productData[ "label" ]:generalData["type"]:_("unknown");
    sitem = sitem + " ( " + generalData[ "url" ]:"" + ")";
    return sitem;
}


/**
 * Create Repository Item for Overview
 */
define term  createOverviewItem( integer index, map source ) {
    integer id = source[ "SrcId" ]:0;
    map generalData = Pkg::SourceGeneralData( id );
    map productData = Pkg::SourceProductData( id );
      

    term item = `item(
		      `id(index ),
		      source[ "enabled" ]:true
		      // corresponds to the "Enable/Disable" button
			? _("On")
		      // corresponds to the "Enable/Disable" button
			: _("Off"),
		      productData[ "label" ]:generalData["type"]:_("Unknown"),
		      generalData[ "url" ]:""
		      );    
   
    return item;
}


/**
 * Handle Multiple repositories URLs (order/instorder)
 */ 
boolean HandleMultipleSources( string url )	{

    boolean metadir_used = false;
    list<string> theSourceDirectories = [];
    map<integer,integer> theSourceOrder = $[];

    list theSources=[];
    string tmpdir = (string) SCR::Read(.target.tmpdir );
    string metadir = tmpdir + "/yast-install";


    Pkg::SourceStartManager( false );
    integer initial_source = Pkg::SourceScan(url, "")[0]:nil;
    if (initial_source == nil)
    {
        y2error ("No repository in '%1'", url);
        return false;
    }

    return false;
}


/**
 * Create a textual summary and a list of unconfigured cards
 * @return summary of the current configuration
 */
global list Summary() {

    string summary = "";
    // summary header
    summary = Summary::AddHeader(summary, _("Configured Repositories"));
    summary = Summary::OpenList(summary);
    numSources = size( sourceStatesOut );
    integer i = 0;
    while ( i < numSources ) {
       summary = Summary::AddListItem(summary,createItem(i, sourceStatesOut[ i ]:$[]));
        i = i + 1;
    }
    summary = Summary::CloseList(summary);


    return [summary, [] ];
}

/**
 * Create an overview table with all configured cards
 * @return table items
 */
global list Overview() {
    numSources = size( sourceStatesOut );
    integer i = 0;
    list source_overview = [];
    while ( i < numSources ) {
	source_overview = add(source_overview,
                createOverviewItem(i, sourceStatesOut[ i ]:$[] ));
        i = i + 1;
    }
    return source_overview;
}

    /**
     * Obsoleted (empty implementation), ZMD (ZLM7.3) on sle11 supports only HTTP and FTP repositories,
     * sync would fail for other types. See bnc#480845 for more details.
     *
     * @param added_src_ids dummy parameter
     * @param deleted_src_ids dummy parameter
     * @return success - always true
     */
    /* TODO FIXME: remove when it's not used in other modules */
    global boolean SyncAddedAndDeleted (list<integer> added_src_ids,
					list<integer> deleted_src_ids) {

	y2warning(-1, "SourceManager::SyncAddedAndDeleted() is obsoleted (does nothing). ZMD sychronization has been removed (see bnc#480845 for details). Backtrace follows:");
	return true;
    }


    /**
     * Parse a URL query (already unescaped) to a map.
     * If no equal sign, the value will be nil.
     * @param query foo=bar&baz=qux
     * @return $["foo": "bar", "baz": "qux"]
     */
    map<string, string> ParseUrlQuery (string query) {
	list<string> q_items = splitstring (query, "&");
	map<string, string> q_map = listmap (string q_item, q_items, {
	    integer eqpos = search (q_item, "=");
	    if (eqpos == nil)
	    {
		return $[ q_item: nil ];
	    }
	    else
	    {
		string key = substring (q_item, 0, eqpos);
		string val = substring (q_item, eqpos + 1);
		return $[ key: val ];
	    }
	});
	return q_map;
    }

    /**
     * @param attr SourceGeneralData item
     * @return For existing repositories, get a mapping from an attribute to the id
     */
    map<string,integer> get_attr_to_id (string attr) {
	list<integer> src_ids = Pkg::SourceGetCurrent (false/*enabled only?*/);
	map<string,integer> a2i = listmap (integer src_id, src_ids, {
	    map gendata = Pkg::SourceGeneralData (src_id);
	    string alias = gendata[attr]:"";
	    return $[ alias: src_id ];
	});
	return a2i;
    }

    /**
     * @return For existing repositories, get a mapping from the alias to the id
     */
    map<string,integer> get_alias_to_id () {
	return get_attr_to_id ("alias");
    }

    /**
     * @return For existing repositories, get a mapping from the URL to the id
     */
    map<string,integer> get_url_to_id () {
	return get_attr_to_id ("url");
    }

    /**
     * Extract an alias parameter from the URL and check whether we have
     * such a repository already.
     * @param url a repository with an alias parameter (actually optional)
     * @param alias_to_id a premade mapping, @see get_alias_to_id
     * @return the repository id or -1
     */
    integer SourceByAliasOrUrl (string url,
				map<string,integer> alias_to_id,
				map<string,integer> url_to_id) {
	// parse the URL
	map parsed_url = URL::Parse (url);
	y2milestone ("parsed: %1", parsed_url);
	// (reassemble and warn if it differs)
	string reassembled = URL::Build (parsed_url);
	if (url != reassembled)
	{
	    y2warning ("reassembled differs: %1", reassembled);
	}
	// get the alias
	map<string, string> q_map = ParseUrlQuery (parsed_url["query"]:"");
	y2milestone ("query: %1", q_map);
	string alias = q_map["alias"]:"";

	// (empty: box safeguard)
	if (alias != "" && haskey (alias_to_id, alias))
	{
	    return alias_to_id[alias]:-1;
	}
	// #188572: if no match by alias, try url
	return url_to_id[url]:-1;
    }

    /**
     * Used by registration. ZMD sync has been disabled - ZLM7.3 on sle11 supports
     * only HTTP and FTP repositories, sync would fail for other types.
     * See bnc#480845 for more details.
     *
     * @param urls URLs to add
     * @return a list of added URLs
     */
    global list<string> AddUpdateSources (list<string> urls) {
	list<string> ret = [];

	// prepare for lookup of known aliases
	map<string,integer> aliases = get_alias_to_id ();
	y2milestone ("alias mapping: %1", aliases);
	map<string,integer> by_url = get_url_to_id ();
	y2milestone ("url mapping: %1", by_url);

	// add the repositories
	// but do not make duplicates (#168740)
	// we detect them based on alias that suse_register gives us (#158850#c17)
	/// (but only for SLE... :-/ )
	/// Need to test what happens when we get two different update
	/// servers for SL
	/// Anyway that means only that #168740 remains unfixed for SL
	foreach (string url, urls, {
	    y2milestone ("Should add an update repository: %1", url);
	    // inst_addon_update_sources also calls Pkg::SourceCreate
	    // but it already skips duplicates

	    // check if alias already there
	    // if yes, delete the old one
	    integer todel = SourceByAliasOrUrl (url, aliases, by_url);
	    if (todel != -1)
	    {
		y2milestone ("deleting the old copy, repository %1", todel); 
		Pkg::SourceDelete (todel);
	    }
	    // then add the new one
	    y2milestone ("Adding update repository...");
	    integer toadd = Pkg::SourceCreate (url, "/");

	    // adding failed, try http fallback for ftp repo (#227059)
	    if (toadd == nil || toadd < 0)
	    {
		map parsed_url = URL::Parse(url);
		string scheme = parsed_url["scheme"]:"";

		if (tolower(scheme) == "ftp")
		{
		    y2milestone("Cannot add FTP update repository, trying HTTP...");

		    parsed_url["scheme"] = "http";
		    string fallback_url = URL::Build(parsed_url);

		    toadd = Pkg::SourceCreate (fallback_url, "/");
		    url = fallback_url;
		}
	    }

	    if (toadd != -1 && toadd != nil)
	    {
		ret = add (ret, url); // #180820#c26

		// is there any patch available?
		list patches = Pkg::ResolvableProperties("", `patch, "");

		if (size(patches) > 0)
		{
		    // loaded target is required to get list of applicable patches (#270919)
		    y2milestone("Repository %1 provides %2 patches, loading target...", url, size(patches));
		    // suppose that we are running in an installed system and use "/" directory
		    Pkg::TargetInitialize("/");
		    Pkg::TargetLoad();
		}
	    }
	});

	return ret;
    }


/**
 *
 */
global map<string,any> AskForCD (string message) {
    list<map<string,any> > cdroms = (list<map<string,any> >)
	SCR::Read (.probe.cdrom);
    boolean multiple_drives = size (cdroms) > 1;
    term drives_sel = `Empty ();
    list<term> devices = maplist (map<string,any> d, cdroms, {
	return `item(`id(d["dev_name"]:""), d["model"]:"" + " (" + (d["dev_name"]:"") + ")" );
    });
    if (multiple_drives)
    {
	drives_sel = `SelectionBox (`id (`drives), _("&Drive to eject"),
	     devices);
    }
    term contents = `HBox (`HSpacing (1), `VBox (
	`VSpacing (0.5),
	`Label (message),
	`VSpacing (0.5),
	drives_sel,
	`VSpacing (0.5),
	`HBox (
	    `HStretch (),
	    `HWeight (1, `PushButton (`id (`cont), Label::ContinueButton ())),
	    `HWeight (1, `PushButton (`id (`cancel), Label::CancelButton ())),
	    `HWeight (1, `PushButton (`id (`eject), _("&Eject"))),
	    `HStretch ()
	),
	`VSpacing (0.5)
    ), `HSpacing (1));
    UI::OpenDialog (contents);
    if (multiple_drives)
	UI::ChangeWidget (`id (`drives), `CurrentItem, cdroms[0,"dev_name"]:"");
    UI::SetFocus (`id (`cont));
    symbol ret = nil;
    while (true)
    {
	ret = (symbol)UI::UserInput ();
	if (ret == `cont || ret == `cancel)
	{
	    break;
	}
	if (ret == `eject)
	{
	    if (multiple_drives)
	    {
		string device = (string)UI::QueryWidget (`id (`drives), `Value);
		SCR::Execute (.target.bash, sformat ("/bin/eject %1", device));
	    }
	    else
	    {
		SCR::Execute (.target.bash, sformat ("/bin/eject %1",
		    cdroms[0,"dev_name"]:""));
	    }
	}
	ret = nil;
    }

    map<string,any> result = $[ "continue" : (ret == `cont) ];

    if (multiple_drives)
    {
	result = add(result, "device", (string)UI::QueryWidget (`id (`drives), `Value));
    }

    UI::CloseDialog ();

    return result;
}

/**
 * Function returns the partiton name which is used as a repository for the installation
 * (IF any partition is used as a repository for installation, of course).
 * Otherwise it returns an empty string "". See bugzilla #208222 for more information.
 *
 * @return string partition name
 */
global string InstallationSourceOnPartition () {
    string install_mode = Linuxrc::InstallInf ("InstMode");

    // Hard Disk is used for the installation
    if (install_mode == "hd") {
	string install_partition = Linuxrc::InstallInf ("Partition");

	// No partiton is defined - error
	if (install_partition == "" || install_partition == nil) {
	    y2error ("Despite the fact that the install-mode is '%1', install-partition is '%2'",
		install_mode, install_partition
	    );
	    return "";
	} else {
	    return install_partition;
	}
    } else {
	return "";
    }
}

    /**
     * Finds the biggest temporary directory and uses it as
     * packager download area.
     */
    global void InstInitSourceMoveDownloadArea () {
        map spaces = Pkg::TargetGetDU ();
        list root_info = spaces["/tmp"]:spaces["/tmp/"]:spaces["/"]:[];
        integer total = root_info[0]:0;
        integer current = root_info[1]:0;
        integer future = root_info[2]:0;
        if (future < current)
            future = current;
        integer tmp_space = total - future;
        // no temp space left or read-only
        if (tmp_space < 0 || root_info[3]:1 == 1)
            tmp_space = 0;

        list var_info = spaces["/var/tmp"]:spaces["/var/tmp/"]:spaces["/var"]:spaces["/var/"]:spaces["/"]:[];
        total = var_info[0]:0;
        current = var_info[1]:0;
        future = var_info[2]:0;
        if (future < current)
            future = current;
        integer var_tmp_space = total - future;
        // no temp space left or read-only
        if (var_tmp_space < 0 || var_info[3]:1 == 1)
            var_tmp_space = 0;

        //-------
        // /tmp or /var/tmp ?

        string download_dir = tmp_space > var_tmp_space
            ? "/tmp"
            : "/var/tmp";
        download_dir = Installation::destdir + download_dir;
        integer space = tmp_space > var_tmp_space
            ? tmp_space
            : var_tmp_space;
        if (true) // TODO check the size of the largest package on CD1
        {
            integer successful = (integer) SCR::Execute (.target.bash, sformat ("test -d '%1' || mkdir -p '%1'", String::Quote (download_dir)));
	    if (successful == 0) {
        	Pkg::SourceMoveDownloadArea (download_dir);
	    } else {
		y2error ("Unable to create %1 directory", download_dir);
	    }
        }
    }

/* EOF */
}

ACC SHELL 2018