/**
 * File:	include/users/cmdline.ycp
 * Package:	Configuration of users and groups
 * Summary:	Command line interface functions.
 * Authors:	Jiri Suchomel <jsuchome@suse.cz>
 *
 * $Id: cmdline.ycp 21050 2005-01-28 16:58:06Z jsuchome $
 *
 * All command line interface functions.
 */
{

textdomain "users";

import "CommandLine";
import "Ldap";
import "Users";
import "UsersCache";
import "UsersLDAP";
import "Report";

// --------------------------------------------------------------------------
// --------------------------------- helper functions -----------------------

/**
 * set LDAP admin password and read LDAP users and groups
 */
define boolean bind_and_read_LDAP (string pw) {

    if (Users::LDAPAvailable() && Users::LDAPNotRead())
    {
	if (Ldap::bind_pass == nil)
	{
	    if (pw == "")
	    {
		// password entering label
		pw = CommandLine::PasswordInput (_("LDAP Server Password:"));
	    }
	    Ldap::SetBindPassword (pw);
	    // TODO check bind...
	}
	string error = UsersLDAP::ReadSettings ();
	if (error != "")
	{
	    CommandLine::Print (error);
	    return false;
	}
	error = Users::ReadLDAPSet ("Users");
	if (error != "")
	{
	    CommandLine::Print (error);
	    return false;
	}
    }
    return true;
}

define map<string,any> convert_keys (map<string,any> input) ``{

    map<string,any> ret	= $[];
    map keys = $[
	"username"	: "uid",
	"password"	: "userpassword",
	"home"		: "homedirectory",
	"shell"		: "loginshell",
	"fullname"	: "cn",
	"gid"		: "gidnumber",
	"uid"		: "uidnumber",
	"no_home"	: "create_home",
	"groupname"	: "cn",
	"new_username"	: "uid",
	"new_groupname"	: "cn",
	"new_uid"	: "uidnumber",
	"new_gid"	: "gidnumber",
	UsersLDAP::GetMemberAttribute (): "userlist",
    ];
    foreach (string key, any value, input, ``{
	string new_key	= keys[key]:key;
	if (new_key == "create_home")
	    value = false;
	if (new_key == "gidnumber" && value != "" &&
	    (haskey (input,"username") || haskey (input,"uid")))
	{
	    // check group existence!
	    if (!UsersCache::GIDExists (tointeger ((string)value)))
		return;
	}
	if (new_key == "grouplist" && is (value, string))
	{
	    value = listmap (string g, splitstring ((string)value,","),
		``($[g : 1]));
	}
	if (new_key == "userlist" && is (value, string))
	{
	    string sep = ",";
	    if (issubstring ((string)value,"="))//for DN
		sep = ":";
	    value = listmap (
		string u, splitstring ((string)value,sep), ``($[u:1]));
	}

	if (new_key == "ldap_password")
	    return;
	if ((key == "username" && haskey (input, "new_username")) ||
	    (key == "uid" && haskey (input, "new_uid")) ||
	    (key == "groupname" && haskey (input, "new_groupname")) ||
	    (key == "gid" && haskey (input, "new_gid")))
	    return;
	ret [ new_key ] = value;
    });
    return ret;
}

// --------------------------------------------------------------------------
// --------------------------------- cmd-line handlers for users ------------

/**
 * List users
 * @return boolean false
 */
define boolean UsersListHandler (map<string,any> options ) ``{

    list<string> sets		= [];
    list<string> attributes	= [];
    map<string,any> amap	= $[];
    foreach (string key, any val, options, ``{
	if (contains (["local","system","ldap","nis"], key))
	    sets = add (sets, key);
	else
//	    attributes	= (list<string>) union (attributes, [type]);
	    amap [key]	= val;
    });
    if (sets == [])
	sets		= ["local"];
    attributes = maplist (string k,any val, convert_keys (amap), ``(k));
    if (!contains (attributes, "uid"))
	attributes	= prepend (attributes, "uid");

    foreach (string type, sets, ``{
	if (type == "nis" && Users::NISAvailable () && Users::NISNotRead ())
	{
	    Users::ReadNewSet ("nis");
	}
	if (type == "ldap" && Users::LDAPAvailable () && Users::LDAPNotRead ())
	{
	    Ldap::SetAnonymous (true);
	    Users::ReadNewSet ("ldap");
	}
	foreach (string uname, map user, (map<string,map>) Users::GetUsers ("uid",type), ``{
	    string out = "";
	    // FIXME when using convert_keys, the order is broken...
	    foreach (string attr, attributes, ``{
		if (haskey (user,attr))
		{
		    if (is (user[attr]:nil, string))
			out = out + user[attr]:"" + " ";
		    if (is (user[attr]:nil, map))
			out = out + mergestring (
			    maplist (string k, any v,user[attr]:$[],``(k)),",")
			+ " ";
		}
	    });
	    CommandLine::Print (out);
	});
    });
    return false; // do not call Write...
}

/**
 * Show one user information
 * @return boolean false
 */
define boolean UserShowHandler (map options ) ``{

    map<string,any> user = $[];
    integer uid = tointeger (options ["uidnumber"]:options["uid"]:"-1");
    string username = options ["username"]:"";

    string type = options ["type"]:"local";

    if (type == "nis" && Users::NISAvailable () && Users::NISNotRead ())
    {
	Users::ReadNewSet ("nis");
    }
    if (type == "ldap" && Users::LDAPAvailable () && Users::LDAPNotRead ())
    {
	Ldap::SetAnonymous (true);
	Users::ReadNewSet ("ldap");
    }

    if (uid != -1 && uid != nil)
    {
	user = Users::GetUser (uid, "");
    }
    else if (username != "")
    {
	user = Users::GetUserByName (username, "");
    }
    if (user == $[])
    {
	// error message
	CommandLine::Print (_("There is no such user."));
	return false;
    }

    string out = "";
    map keys = $[
	// label shown at command line (user attribute)
	"cn"		: _("Full Name:"),
	// label shown at command line (user attribute)
	"uid"		: _("Login Name:"),
	// label shown at command line (user attribute)
	"homedirectory"	: _("Home Directory:"),
	// label shown at command line (user attribute)
	"loginshell"	: _("Login Shell:"),
	// label shown at command line (user attribute)
	"uidnumber"	: _("UID:"),
	// label shown at command line (user attribute)
	"groupname"	: _("Default Group:"),
	// label shown at command line (user attribute)
	"grouplist"	: _("List of Groups:"),
    ];
    foreach (string key, any value, user, ``{
	key = keys[key]:"";
	if (key == "") return;
	string svalue = sformat ("%1", value);
	if (is (value, map))
	{
	    svalue = mergestring ((list<string>)
		maplist (string k, any v, (map<string,any>) value, ``(k)), ",");
	}
	CommandLine::Print (sformat ("%1\n\t%2", key, svalue));
    });

    return false; // do not call Write...
}

/**
 * Add user
 * @return boolean false
 */
define boolean UserAddHandler (map <string, any> options ) ``{

    if (options["username"]:"" == "")
    {
	// error message
	CommandLine::Print (_("Enter a user name."));
	return false;
    }

    map <string, any> user = convert_keys (options);
    string type = user["type"]:"local";
    if (type == "ldap")
    {
	if (!bind_and_read_LDAP (options["ldap_password"]:""))
	    return false;
	if (!haskey (user,"sn"))
	    user["sn"] = user["uid"]:"";
    }

    Users::ResetCurrentUser ();

    if (!haskey(user,"userpassword"))
    {
	// question label
	string pw = CommandLine::PasswordInput (_("Password for New User:"));
	user["userpassword"]	= pw;
    }

    string error = Users::AddUser (user);

    if (error != "")
    {
	CommandLine::Print (error);
	return false;
    }

    if (user["type"]:"local" == "ldap")
    {
	Users::SubstituteUserValues ();
    }

    error = Users::CheckUser ($[]);
    if (error != "")
    {
	CommandLine::Print (error);
	return false;
    }

    return Users::CommitUser ();
}

/**
 * Delete user
 * @return boolean false
 */
define boolean UserDeleteHandler (map  <string, any>options ) ``{

    integer uid = tointeger (options ["uidnumber"]:options["uid"]:"-1");
    string username = options ["username"]:"";
    boolean delete_home = haskey (options, "delete_home");

    string type = options["type"]:"local";
    if (type == "ldap")
    {
	if (!bind_and_read_LDAP (options["ldap_password"]:""))
	    return false;
    }
    if (uid != -1 && uid != nil)
    {
	Users::SelectUser (uid);
    }
    else if (username != "")
    {
	Users::SelectUserByName (username);
	map u	= Users::GetCurrentUser();
	if (is (u["uidnumber"]:nil, integer))
	    uid	= u["uidnumber"]:-1;
	else if (is (u["uidnumber"]:nil, string))
	    uid	= tointeger (u["uidnumber"]:"-1");
    }
    if (Users::GetCurrentUser () == $[])
    {
	// error message
	CommandLine::Print (_("There is no such user."));
	return false;
    }
    // if the user has log on system
    map out	= (map)SCR::Execute (.target.bash_output,
	sformat ("ps --no-headers -u %1", uid));
    if (size (out["stdout"]:"") != 0)
    {
        // error popup
        Report::Error(_("You can not delete this user, because the user is present.
Please log off the user first."));
        return false;
    }
    return Users::DeleteUser (delete_home) && Users::CommitUser ();
}

/**
 * Edit user
 * @return boolean false
 */
define boolean UserEditHandler (map<string,any> options) {

    integer uid = tointeger (options ["uidnumber"]:options["uid"]:"-1");
    string username = options ["username"]:"";

    string type = options["type"]:"local";
    if (type == "ldap")
    {
	if (!bind_and_read_LDAP (options["ldap_password"]:""))
	    return false;
    }
    if (uid != -1 && uid != nil)
    {
	Users::SelectUser (uid);
    }
    else if (username != "")
    {
	Users::SelectUserByName (username);
    }
    map user = Users::GetCurrentUser ();
    if (user == $[])
    {
	// error message
	CommandLine::Print (_("There is no such user."));
	return false;
    }

    map changes = convert_keys (options);
    if (type == "ldap" && !haskey (changes,"dn"))
    {
	changes["dn"]	= user["dn"]:"";
	// for username changes...
    }
    string error = Users::EditUser ((map<string,any>)changes);
    if (error != "")
    {
	CommandLine::Print (error);
	return false;
    }
    error = Users::CheckUser ($[]);
    if (error != "")
    {
	CommandLine::Print (error);
	return false;
    }
    return Users::CommitUser ();
}


// --------------------------------------------------------------------------
// --------------------------------- cmd-line handlers for groups -----------

/**
 * List groups
 * @return boolean false
 */
define boolean GroupsListHandler (map<string,string> options ) ``{

    list<string> sets		= [];
    list<string> attributes	= [];
    map<string,any> amap	= $[];
    foreach (string key, any val, options, ``{
	if (contains (["local","system","ldap","nis"], key))
	    sets = add (sets, key);
	else
	    amap [key]	= val;
    });
    if (sets == [])
	sets		= ["local"];

    attributes = maplist (string k,any val, convert_keys (amap), ``(k));
    if (!contains (attributes, "cn"))
	attributes	= prepend (attributes, "cn");
    if (contains (attributes, "userlist"))
	attributes	= add (attributes, UsersLDAP::GetMemberAttribute ());

    foreach (string type, sets, {
	if (type == "nis" && Users::NISAvailable () && Users::NISNotRead ())
	{
	    Users::ReadNewSet ("nis");
	}
	if (type == "ldap" && Users::LDAPAvailable () && Users::LDAPNotRead ())
	{
	    Ldap::SetAnonymous (true);
	    Users::ReadNewSet ("ldap");
	}
	foreach (string name, map group, (map<string,map>) Users::GetGroups ("cn",type), {
	    string out = "";
	    foreach (string attr, attributes, ``{
		if (haskey (group,attr))
		{
		    if (is (group[attr]:nil, string))
			out = out + group[attr]:"" + " ";
		    if (is (group[attr]:nil, map))
			out = out + mergestring (
			    maplist (string k, any v,group[attr]:$[],``(k)),",")
			+ " ";
		}
	    });
	    CommandLine::Print (out);
	});
    });
    return false; // do not call Write...
}

/**
 * Show one group information
 * @return boolean false
 */
define boolean GroupShowHandler (map options ) ``{

    map<string,any> group = $[];
    integer gid = tointeger (options ["gidnumber"]:options["gid"]:"-1");
    string groupname = options ["groupname"]:"";

    string type = options ["type"]:"local";

    if (type == "nis" && Users::NISAvailable () && Users::NISNotRead ())
    {
	Users::ReadNewSet ("nis");
    }
    if (type == "ldap" && Users::LDAPAvailable () && Users::LDAPNotRead ())
    {
	Ldap::SetAnonymous (true);
	Users::ReadNewSet ("ldap");
    }

    if (gid != -1 && gid != nil)
    {
	group = Users::GetGroup (gid, "");
    }
    else if (groupname != "")
    {
	group = Users::GetGroupByName (groupname, "");
    }
    if (group == $[])
    {
	// error message
	CommandLine::Print (_("There is no such group."));
	return false;
    }

    string out = "";
    map keys = $[
	// label shown at command line (user attribute)
	"cn"		: _("Group Name:"),
	// label shown at command line (user attribute)
	"gidnumber"	: _("GID:"),
	// label shown at command line (user attribute)
	"userlist"	: _("List of Members:"),
	// label shown at command line (user attribute)
	UsersLDAP::GetMemberAttribute ():	_("List of Members:"),
    ];
    foreach (string key, any value, group, ``{
	key = keys[key]:"";
	if (key == "") return;
	string svalue = sformat ("%1", value);
	if (is (value, map))
	{
	    svalue = mergestring ((list<string>)
		maplist (string k, any v, (map<string,any>) value, ``(k)), ",");
	}
	CommandLine::Print (sformat ("%1\n\t%2", key, svalue));
    });

    return false; // do not call Write...
}

/**
 * Delete group
 * @return boolean false
 */
define boolean GroupDeleteHandler (map<string,any> options ) {

    integer gid = tointeger (options ["gidnumber"]:options["gid"]:"-1");
    string groupname = options ["cn"]:options["groupname"]:"";

    string type = options["type"]:"local";
    if (type == "ldap")
    {
	if (!bind_and_read_LDAP (options["ldap_password"]:""))
	    return false;
    }
    if (gid != -1 && gid != nil)
    {
	Users::SelectGroup (gid);
    }
    else if (groupname != "")
    {
	Users::SelectGroupByName (groupname);
    }
    if (Users::GetCurrentGroup () == $[])
    {
	// error message
	CommandLine::Print (_("There is no such group."));
	return false;
    }
    return Users::DeleteGroup () && Users::CommitGroup ();
}

/**
 * Add group
 * @return boolean false
 */
define boolean GroupAddHandler (map <string, any> options ) {

    if (options["groupname"]:"" == "")
    {
	// error message
	CommandLine::Print (_("Enter a group name."));
	return false;
    }

    map <string, any> group = convert_keys (options);
    string type = group["type"]:"local";
    if (type == "ldap")
    {
	if (!bind_and_read_LDAP (options["ldap_password"]:""))
	    return false;
    }
    string member_attr	= (type == "ldap") ? UsersLDAP::GetMemberAttribute () :
	"userlist";
    if (haskey (group, "userlist"))
    {
	if (type == "ldap")
	{
	    group [member_attr]	= group["userlist"]:$[];
	    group		= remove (group, "userlist");
	}
    }
    else
    {
	group [member_attr]     = $[];
    }

    Users::ResetCurrentGroup ();
    string error = Users::AddGroup (group);

    if (error != "")
    {
	CommandLine::Print (error);
	return false;
    }

    if (group["type"]:"local" == "ldap")
    {
	Users::SubstituteGroupValues ();
    }

    error = Users::CheckGroup ($[]);
    if (error != "")
    {
	CommandLine::Print (error);
	return false;
    }
    return Users::CommitGroup ();
}

/**
 * Edit group
 * @return boolean false
 */
define boolean GroupEditHandler (map<string,any> options) {

    integer gid = tointeger (options ["gidnumber"]:options["gid"]:"-1");
    string groupname = options ["cn"]:options["groupname"]:"";

    string type = options["type"]:"local";
    if (type == "ldap")
    {
	if (!bind_and_read_LDAP (options["ldap_password"]:""))
	    return false;
    }
    if (gid != -1 && gid != nil)
    {
	Users::SelectGroup (gid);
    }
    else if (groupname != "")
    {
	Users::SelectGroupByName (groupname);
    }
    map group = Users::GetCurrentGroup ();
    if (group == $[])
    {
	// error message
	CommandLine::Print (_("There is no such group."));
	return false;
    }

    map changes = convert_keys (options);
    if (type == "ldap" && !haskey (changes,"dn"))
    {
	changes["dn"]	= group["dn"]:"";
	// for groupname changes...
    }
    if (type == "ldap" && haskey (changes, "userlist"))
    {
	changes [UsersLDAP::GetMemberAttribute ()] = changes["userlist"]:$[];
	changes	= remove (changes, "userlist");
    }
    string error = Users::EditGroup ((map<string,any>)changes);
    if (error != "")
    {
	CommandLine::Print (error);
	return false;
    }
    error = Users::CheckGroup ($[]);
    if (error != "")
    {
	CommandLine::Print (error);
	return false;
    }
    return Users::CommitGroup ();
}

// --------------------------------------------------------------------------
// --------------------------------- common cmd-line handlers ---------------


define boolean UsersRead () ``{

    Users::SetGUI (false);
    return Users::Read () == "";
}

define boolean UsersWrite () ``{

    Users::SetGUI (false);
    string error = Users::Write ();
    if (error != "")
    {
	CommandLine::Print (error);
	return false;
    }
    return true;
}

define boolean UsersGUI () {
    return (UsersSequence (Users::GetStartDialog()) == `next);
}

} //EOF
