/**
 * File:	OSRPkgVerify.ycp
 * Module:	repair
 * Summary:	Packages check
 * Authors:	Johannes Buchhold <jbuch@suse.de>
 *
 * $Id: OSRPkgVerify.ycp 20504 2004-12-06 14:55:51Z jsuchome $
 *
 * Provide osr mode information.
 */

{
    module "OSRPkgVerify";

    textdomain "repair";

    import "Kernel";
    import "OSRExecute";
    import "OSRLogFile";
    import "OSRMode";

    /**
     * Missing key. For parsing rpm output.
     */
    global string missing		 = "missing";

    /**
     * Unsatisfied key. For parsing rpm output.
     */
    global string unsatisfied		 = "Unsatisfied dependencies for";

    /**
     * Rpm symbol for all tests.
     */
    string all_tests			= "SM5DLUGT";

    /**
     * All allowed rpm test options.
     */
    string possible			= "5SLTDUGM?.";

    /**
     * Key seperate packagename with not valid file count.
     */
    global string separator             = "____KEY____";

    map unimportant_file_problems = $[
	"file_type" : $[
	    ".log"		: [ all_tests ],
	    ".html"		: [ all_tests, missing ],
	    ".png"		: [ all_tests ],
	    ".protocol"		: [ all_tests ]
	],
	"directory" : $[
	    "/usr/share/doc"	: [ all_tests, missing ],
	    "/var/log"		: [ all_tests ],
	    "/var/cache"	: [ all_tests ],
	    "/usr/share/man"	: [ all_tests, missing ],
	    "/usr/X11R6/man"	: [ all_tests, missing ],
	    "/usr/share/man"	: [ all_tests, missing ],
	    "/usr/share/info"	: [ all_tests, missing ],
	    "/var/spool"	: [ all_tests ],
	    "/dev"		: [ "MUG..." ]
	],
	"config_file" : $[
	    "all"		: [ "SM5....T" ]
	],
	"you-installed" : $[
	    "/var/lib/YaST2/you/installed"	: [ all_tests ]
	],
	"build_problems": $[
	    "all"		: []
	]
    ];

    global map unimportant2help = $[
	// helptext: why the error is not so important
	"file_type"	: _("The file is a noncritical type."),
	// helptext: why the error is not so important
	"directory"	: _("The file is located in a noncritical directory."),
	// helptext: why the error is not so important
	"config_file"	: _("The file is a configuration file."),
	"you-installed" :
	// helptext: why the error is not so important
_("The package has been updated with the online update."),
	"build_problems":
	// helptext: why the error is not so important
_("The test failure seems to be caused by an incorrectly built package.")
    ];

    /**
     * index of all currently verified packages
     */
    map verified_packages			= $[];

    // number of packages verified so far
    integer verified_counter			= 0;

    // data of package currently verified
    global list<map> verify_data		= [];

    global list<string> not_successful_verified	= [];

    /**
     * maximum of file entries shown in detailed pkg verification summary
     * (necessary because of potential large memory requirements)
     */
    global integer max_files		= 10;

    // mount point to root fs
    global string root_for_verification	= "";

    /**
     * Verified packages after a new installation.
     */
    global map initial_verified_packages	= $[];

    map update_problems_map		= $[];


    /**
     *
     */
    global define void Reset()``{
	verified_packages	= $[];
	verify_data		= [];
	verified_counter	= 0;
	not_successful_verified = [];
    }

    /**
     * module constructor: read packages with build problems
     */
    global define void OSRPkgVerify()``{

	initial_verified_packages = (map)
	    WFM::CallFunction ("osr_verified_packages");
	Reset();
    }

    // has the package been verified?
    global define boolean PackageVerified (string pkg_name) {

	return verified_packages[pkg_name]:false;
    }

    // return number of packages verified so far
    global define integer VerifiedSize () {

	return verified_counter;
    }

    define void	build_update_problems_map()``{

	if( update_problems_map == $[] || update_problems_map == nil )
	{
	    foreach (string dir, list<string> allowed_erros,
	      unimportant_file_problems["you-installed" ]:$[], ``{

		list<string> you_installed_pkgs =
		    (list<string>) SCR::Read (.target.dir, dir);

		if( you_installed_pkgs == nil || you_installed_pkgs == [] )
		{
		    return;
		}
		foreach (string patch_file, you_installed_pkgs, ``{

		    string file  = (string)
			SCR::Read(.target.string , dir + "/" + patch_file);
		    list<string> lines = filter (string line,
			splitstring (file, "\n"), ``(
			    substring (line, 0,size("Filename:")) == "Filename:"
		    ));

		    foreach (string line, lines, ``{
			line = substring (line, size("Filename:"));
			line = mergestring (splitstring (line, " "), "");
			if (issubstring (line, ".rpm") ||
			    issubstring (line, ".RPM"))
			{
			    line = substring (line,0,size(line) - size(".rpm"));
			}
			if( line != nil && line != "" )
			{
			    update_problems_map[ line ] = allowed_erros;
			}
		    });
		});
	    });
	    y2milestone(" update_problems packages %1", update_problems_map );
	}
    }

    define boolean check_allowed (list<string> allowed_erros, string status)``{

	boolean found = false;
	foreach (string allowed, allowed_erros, ``{

	    if ((allowed == missing && status == missing) ||
		(allowed != missing &&
		size (deletechars (status, allowed + "." )) == 0))
	    {
		found = true;
		return;
	    }
	});
	y2debug ("all allowed: %1; current status %2; found: %3",
	    allowed_erros, status, found);

	return found;
    }

    /**
     *
     */
    define string unimportant_file_type (string file, string status)``{

	boolean found = false;
	foreach (string type, list<string> allowed_erros,
	  unimportant_file_problems["file_type"]:$[], ``{

	    if (size (file) > size (type) &&
		substring (file, size (file) - size (type)) == type)
	    {
		if (check_allowed( allowed_erros, status ))
		{
		    found = true;
		    return;
		}
	    }
	});
	return ( found ) ? "file_type" : "";
    }

    /*
     *
     */
    define string unimportant_directory( string file, string status )``{

	boolean found = false;
	foreach (string dir, list<string> allowed_erros,
	  unimportant_file_problems["directory"]:$[] , ``{

	    if (substring( file, 0, size(dir)) == dir)
	    {
		if ( check_allowed( allowed_erros, status ))
		{
		    found = true;
		    return;
		}
	    }
	});
	return ( found ) ? "directory" : "";
    }

    /**
     *
     */
    define string config_file_allowed( map pkg_data ) ``{

	if (!pkg_data["config_file"]:false)
	    return "";

	return check_allowed (
	    unimportant_file_problems ["config_file","all"]:[],
	    pkg_data ["status"]:"")
	? "config_file" : "";
    }

    /**
     *
     */
    define string update_problems (string pkg_name, string file, string status)``{
	boolean found = false;

	map you_installed = filter (string you_installed_pkg,
	  list<string> allowed_erros, (map<string,list<string> >)update_problems_map,
	    ``(substring (you_installed_pkg,0, size( pkg_name)) == pkg_name));

	foreach (string you_installed_pkg, list<string> allowed_erros,
	  (map<string,list<string> >) you_installed, ``{

	    if( check_allowed( allowed_erros, status ))
	    {
		found = true;
		return;
	    }
	});
	return ( found ) ? "you-installed" : "";
    }

    /**
     *
     */
    define string build_problems( string pkg_name, map pkg_data )``{

	map org_data = initial_verified_packages[pkg_name]:$[];
	if (size (filter(string key, map d, (map<string,map<any,any> >)org_data,``( d == pkg_data ))) > 0)
	{
	    return "build_problems";
	}
	return "";
    }


    /**
     * Return if a package was not build correctly.
     */
    global define string GreenPackageProblems(string pkg_name, map pkg_data )``{

	if (haskey (pkg_data , "green_status" ))
	    return pkg_data["green_status"]:"";

	string green_status =  "";

	// set config files to green status
	if ( green_status == "" )
	    green_status = config_file_allowed( pkg_data  );

	if ( green_status == "" )
	    green_status = update_problems (
		pkg_name, pkg_data["file"]:"", pkg_data["status"]:"" );

	if ( green_status == "" )
	    green_status = unimportant_file_type (
		pkg_data["file"]:"" , pkg_data["status"]:"" );

	if ( green_status == "" )
	    green_status = unimportant_directory (
		pkg_data["file"]:"" , pkg_data["status"]:"" );

	if ( green_status == "" )
	    green_status = build_problems( pkg_name,  pkg_data );

	if ( green_status != "" )
	{
	    y2milestone("Found green package problems. Name of package: %1 Type: %2 File: %3 ",
		pkg_name, green_status , pkg_data["file"]:"");
	}

	return green_status;
    }

    /**
     * Convert rpm output to a map.
     * @param pkg_name The package name e.g.: filesystem
     * @param verify_output_string The rpm output: .....UG.   /etc/cups
     *                                             .M......   /proc
     *
     * @return  e.g.: $["filesystem____KEY____1":
     *                                      $["config_file":false,
     *					      "file":"/etc/cups",
     *					      "status":".....UG."
     *					      ],
     *                  "filesystem____KEY____2"   :$["config_file":false, "
     *		                               file":"/proc",
     *					       "status":".M......"]]
     *
     *
     */
//    define boolean convert_verify_output ( string pkg_name, string verify_output_string)
    global define map convert_verify_output (
	string pkg_name, string verify_output_string, boolean full_list)
    {

	list pkg_data			= [];
	integer build_problem_counter   = 0;
	integer verify_output_counter   = 0;
	list<string> verify_output_list =
	    splitstring (verify_output_string , "\n");

	foreach (string verify_output, verify_output_list, {

	    if (size (verify_output) > 1)
	    {
		verify_output_counter = verify_output_counter  +1 ;
		string pkg_key =
		    sformat("%1%2%3",pkg_name, separator,verify_output_counter);
		map file_data	= $[];
		if (substring (verify_output, 0, size(missing) )  == missing )
		{
		    file_data = $[
			"config_file"	: issubstring (
			    substring (verify_output, size (missing),
				findfirstof(verify_output, "/")- size(missing)),
			    "c"),
			"file"		: substring (
			    verify_output, findfirstof(verify_output, "/")),
			"status"	: missing
		    ];
		}
		else if (substring(verify_output, 0, size(unsatisfied ))
			 == unsatisfied )
		{
		    file_data = $[
			"config_file"	: false,
			"file"	: substring(verify_output, size(unsatisfied)),
			"status"	: unsatisfied
		    ];
		}
		else if (findfirstnotof(substring(verify_output,0,8), possible)
			 == nil )
		{
		    file_data = $[
			"config_file"	: issubstring (
			    substring (verify_output, 8,
			        findfirstof(verify_output, "/") - 8 ),
			    "c" ),
			"file"		: substring (verify_output,
			    findfirstof (verify_output, "/")),
			"status"	: substring (verify_output, 0,8)
		    ];
		}
		else
		{
		    y2error ("unknown output");
		    file_data	=  $[ "status" : "unknown" ];
		}

		file_data ["green_status"] =
		    GreenPackageProblems (pkg_name, file_data);

		if (file_data["green_status"]:"" != "")
		{
		    build_problem_counter = build_problem_counter +1;
		}

		if (full_list || verify_output_counter <= max_files)
		{
		    pkg_data = add (pkg_data, file_data);
		}
		else if (verify_output_counter == max_files + 1)
		{
		    pkg_data = add (pkg_data, $["more": true]);
		}
	    }
	});


	return $[
	    // false if the package was not verified successfully.
	    "ok"		:
		(build_problem_counter == verify_output_counter ||
		(build_problem_counter == 0 && size(verify_output_string)<= 1)),
	    "verify_data"	: pkg_data
	];
    }

    /**
     *
     */
    global define boolean Verify (string p, string root_mountpoint) ``{

	root_for_verification	= root_mountpoint;
	build_update_problems_map();

	y2milestone("-----------verifying package %1", p );

	OSRExecute::CommandOutput (.local.bash,
	    sformat("LANG=ENG /bin/rpm --root %2 -V %1", p, root_mountpoint));

	y2milestone("-----------package %1 verified", p);

	map converted	= convert_verify_output (p, OSRExecute::stdout, false);
	boolean ok	= converted["ok"]:false;
	verify_data	= converted["verify_data"]:[];

	verified_packages [p]	= true;
	verified_counter	= verified_counter + 1;

	if (!ok)
	    not_successful_verified = add (not_successful_verified, p);
	return ok;
    }



    /**
     * Debug the verified packages informations.
     */
    global define boolean WriteVerifiedPackages( list installed, boolean debug )``{
	string ypath  = OSRLogFile::GetTmpDir()+ "/initial_verified_packages";
	string spath  = OSRLogFile::GetTmpDir()+ "/initial_verified_packages_text";

	if ( OSRMode::save )
	{
	    y2milestone("Try to save data of all verified packages");
	    // not ok packages
	    map t_verified_packages = filter(string pkg_name, map data,
		(map<string,map<any,any> >)verified_packages, ``( data != $[] ));

	    // write as text file!!
	    foreach (string pkg_name, map<string,map<string,any> > pkg_data, (map<string, map<string,map<string,any> > >) t_verified_packages, ``{

		WFM::Execute(.local.bash,
		    sformat("/bin/echo %1 >> %2", pkg_name, spath));
		foreach (string key, map file_data,  pkg_data, ``{

		    WFM::Execute (.local.bash,
			sformat("/bin/echo \"  %1  %2  %3\" >> %4",
			    file_data["status"]:"",
			    ( file_data["config_file"]:false) ? "c" : " ",
			    file_data["file"]:"" , spath ));
		});
	    });
	    // write as ycp-file
	    return WFM::Write(.local.ycp, ypath, t_verified_packages);
	}

	if (debug)
	    return WFM::Write(.local.ycp , ypath , verified_packages);

	return true;
    }
}//EOF
