/**********************************************************************
 *
 * usersaction.cpp -- managing users
 * Copyright (C) 1999  DigiLib Systems Limited, New Zealand
 *
 * A component of the Greenstone digital library software
 * from the New Zealand Digital Library Project at the
 * University of Waikato, New Zealand.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *********************************************************************/

#include "gsdl_modules_cfg.h"
#ifdef GSDL_USE_USERS_ACTION

#include "usersaction.h"
#include "fileutil.h"


///////////////
// usersaction
///////////////

usersaction::usersaction () {
  // this action uses cgi variable "a"
  cgiarginfo arg_ainfo;
  arg_ainfo.shortname = "a";
  arg_ainfo.longname = "action";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = "um"; // user management
  arg_ainfo.savedarginfo = cgiarginfo::must;
  argsinfo.addarginfo (NULL, arg_ainfo);

  // "uma"
  arg_ainfo.shortname = "uma";
  arg_ainfo.longname = "user management action";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = "listusers";
  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
  argsinfo.addarginfo (NULL, arg_ainfo);

  // "umun"
  arg_ainfo.shortname = "umun";
  arg_ainfo.longname = "user management user name";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = g_EmptyText;
  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
  argsinfo.addarginfo (NULL, arg_ainfo);

  // "umpw"
  arg_ainfo.shortname = "umpw";
  arg_ainfo.longname = "user management password";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = g_EmptyText;
  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
  argsinfo.addarginfo (NULL, arg_ainfo);

  // "umnpw1"
  arg_ainfo.shortname = "umnpw1";
  arg_ainfo.longname = "user management new password 1";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = g_EmptyText;
  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
  argsinfo.addarginfo (NULL, arg_ainfo);

  // "umnpw2"
  arg_ainfo.shortname = "umnpw2";
  arg_ainfo.longname = "user management new password 2";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = g_EmptyText;
  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
  argsinfo.addarginfo (NULL, arg_ainfo);

  // "umus"
  arg_ainfo.shortname = "umus";
  arg_ainfo.longname = "user management account status";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = g_EmptyText;
  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
  argsinfo.addarginfo (NULL, arg_ainfo);

  // "umug"
  arg_ainfo.shortname = "umug";
  arg_ainfo.longname = "user management groups"; // comma seperated list
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = g_EmptyText;
  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
  argsinfo.addarginfo (NULL, arg_ainfo);

  // "umc"
  arg_ainfo.shortname = "umc";
  arg_ainfo.longname = "user management comment";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = g_EmptyText;
  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
  argsinfo.addarginfo (NULL, arg_ainfo);

  // "bcp"
  arg_ainfo.shortname = "bcp";
  arg_ainfo.longname = "change password submit button";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = g_EmptyText;
  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
  argsinfo.addarginfo (NULL, arg_ainfo);

  // "beu"
  arg_ainfo.shortname = "beu";
  arg_ainfo.longname = "edit user submit button";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = g_EmptyText;
  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
  argsinfo.addarginfo (NULL, arg_ainfo);

  // "cm"
  arg_ainfo.shortname = "cm";
  arg_ainfo.longname = "confirm an action";
  arg_ainfo.multiplechar = true;
  arg_ainfo.multiplevalue = false;
  arg_ainfo.defaultstatus = cgiarginfo::weak;
  arg_ainfo.argdefault = g_EmptyText;
  arg_ainfo.savedarginfo = cgiarginfo::mustnot;
  argsinfo.addarginfo (NULL, arg_ainfo);
}

void usersaction::configure (const text_t &key, const text_tarray &cfgline) {
  action::configure (key, cfgline);
}

bool usersaction::init (ostream &logout) {

  if (dbhome.empty()) {
    logout << "ERROR (usersaction::init) dbhome is not set\n";
    return false;
  }

  return action::init (logout);
}

bool usersaction::check_cgiargs (cgiargsinfoclass &/*argsinfo*/, cgiargsclass &args, 
				 recptprotolistclass * /*protos*/, ostream &/*logout*/) {
  args["uan"] = "1"; // user authentication is needed

  if (args["uma"] == "changepasswd") {
    // no particular group is needed to change a password
    args["ug"].clear(); 
  } else {
    // administrator is needed for all other management tasks
    args["ug"] = "administrator";
  }
  
  return true;
}

void usersaction::get_cgihead_info (cgiargsclass &/*args*/, recptprotolistclass * /*protos*/,
				    response_t &response, text_t &response_data, 
				    ostream &/*logout*/) {
  response = content;
  response_data = "text/html";
}

bool usersaction::do_action (cgiargsclass &args, recptprotolistclass * /*protos*/, 
			     browsermapclass * /*browsers*/, displayclass &disp, 
			     outconvertclass &outconvert, ostream &textout, 
			     ostream &logout) {

  if (args["uma"] == "adduser" || args["uma"] == "edituser") {
    // adduser is handled by edituser
    return do_edituser (args, disp, outconvert, textout, logout);
    
  } else if (args["uma"] == "deleteuser") {
    return do_deleteuser (args, disp, outconvert, textout, logout);
    
  } else if (args["uma"] == "changepasswd") {
    return do_changepasswd (args, disp, outconvert, textout, logout);  
  }

  // default
  return do_listusers (args, disp, outconvert, textout, logout);
}

bool usersaction::do_listusers (cgiargsclass &/*args*/, displayclass &disp, 
				outconvertclass &outconvert, ostream &textout, 
				ostream &/*logout*/) {

  textout << outconvert << disp
	  << "_userslistusers:header_\n_userslistusers:contentstart_\n";

  text_tarray userlist;
  // get user list
  user_database->get_user_list (userlist);

  // sort the list
  sort(userlist.begin(), userlist.end());
  
  // output the information for each user
  userinfo_t userinfo;
  text_tarray::iterator users_here = userlist.begin();
  text_tarray::iterator users_end = userlist.end();
  while (users_here != users_end) {
    if (user_database->get_user_info(*users_here, userinfo) == ERRNO_SUCCEED) {
      textout << outconvert << disp
	      << "<tr><td bgcolor=\"\\#eeeeee\">" << encodeForHTML(userinfo.username) << "</td>\n"
       	      << "<td bgcolor=\"\\#eeeeee\">" << (char *) (userinfo.enabled ? "enabled" : "disabled") << "</td>\n"
	      << "<td bgcolor=\"\\#eeeeee\">" << encodeForHTML(userinfo.groups) << "&nbsp;</td>\n"
	      << "<td bgcolor=\"\\#eeeeee\">" << encodeForHTML(userinfo.comment) << "&nbsp;</td>\n"
	      << "<td><a href=\"_httpcurrentdocument_&a=um&uma=edituser&umun="
	      << encodeForHTML(userinfo.username) << "\">_userslistusers:textedituser_</a> "
	      << "<a href=\"_httpcurrentdocument_&a=um&uma=deleteuser&umun="
	      << encodeForHTML(userinfo.username) << "\">_userslistusers:textdeleteuser_</a>"
	      << "</td></tr>\n\n";

    } else {
      textout << outconvert << disp
	      << "<tr><td bgcolor=\"\\#eeeeee\">" << *users_here << "</td>\n"
	      << "<td bgcolor=\"\\#eeeeee\">&nbsp;</td>\n"
	      << "<td bgcolor=\"\\#eeeeee\">&nbsp;</td>\n"
	      << "<td bgcolor=\"\\#eeeeee\">&nbsp;</td>\n"
	      << "<td>&nbsp;</td></tr>\n\n";
    }
    
    ++users_here;
  }
  
  textout << outconvert << disp
	  << "_userslistusers:contentend_\n_userslistusers:footer_\n";
  return true;
}

void usersaction::define_user_macros (cgiargsclass &args, displayclass &disp) {
  disp.setmacro ("usersargun", "users", args["umun"]);
  disp.setmacro ("usersargpw", "users", args["umpw"]);
  disp.setmacro ("usersargus", "users", args["umus"]);
  disp.setmacro ("usersargug", "users", args["umug"]);
  disp.setmacro ("usersargc", "users", args["umc"]);

  disp.setmacro ("usersargunAttrsafe", "users", encodeForHTMLAttr(args["umun"]));
  disp.setmacro ("usersargpwAttrsafe", "users", encodeForHTMLAttr(args["umpw"]));
  disp.setmacro ("usersargusAttrsafe", "users", encodeForHTMLAttr(args["umus"])); // unused in users.dm or other macro files, but setting this attrsafe'd macro in parallel with the other usersarg* values here.
  disp.setmacro ("usersargugAttrsafe", "users", encodeForHTMLAttr(args["umug"]));
  disp.setmacro ("usersargcAttrsafe", "users", encodeForHTMLAttr(args["umc"]));

}


bool usersaction::do_edituser (cgiargsclass &args, displayclass &disp, 
			       outconvertclass &outconvert, ostream &textout, 
			       ostream &logout) {

  userinfo_t userinfo;
  text_t messagestatus;
  bool noproblems = true;
  
  // fill in defaults from user database (if appropriate)
  if (args["umun"].empty()) {
    noproblems = false;
    
  } else if (!userdbclass::username_ok (args["umun"])) {
    // problem with username
    noproblems = false;
    messagestatus += "_users:messageinvalidusername_";
    
  } else if (user_database->get_user_info (args["umun"], userinfo) == ERRNO_SUCCEED) {
    if (args["uma"] == "adduser") {
      // must not add a user that has the same name as another user
      noproblems = false;
      messagestatus += "_users:messageuserexists_";
      
    } else {
      // only fill in the data if there is no user status defined
      if (args["umus"].empty()) {
	args["umus"] = userinfo.enabled ? "enabled" : "disabled";
	if (args["umug"].empty()) args["umug"] = userinfo.groups;
	if (args["umc"].empty()) args["umc"] = userinfo.comment;
      }
    }
  }

  // fill in the user status default
  if (args["umus"].empty()) {
    noproblems = false;
    args["umus"] = "enabled";
  }
  
  // make sure the password is ok
  if (args["umpw"].empty()) {
    // password must not be empty if none were supplied from database
    // and we have had no other problems
    if (userinfo.password.empty() && noproblems) {
      noproblems = false;
      messagestatus += "_users:messageemptypassword_";
    }
    
  } else if (!userdbclass::password_ok(args["umpw"])) {
    noproblems = false;
    messagestatus += "_users:messageinvalidpassword_";
  }

  // set this info if no problems have been encounted
  // and the submit button was pressed
  if (noproblems && !args["beu"].empty()) {
    userinfo.username = args["umun"];
    if (!args["umpw"].empty()) {
      // only set the password if it is not empty
      userinfo.password = userdbclass::crypt_text(args["umpw"]);
    }
    userinfo.enabled = (args["umus"] == "enabled");    
    userinfo.groups = userdbclass::format_user_groups(args["umug"]);
    userinfo.comment = args["umc"];
    
    user_database->set_user_info (args["umun"], userinfo);
    
    // show list of users
    return do_listusers (args, disp, outconvert, textout, logout);
  }
  
  // define the macros for the user
  define_user_macros (args, disp);
  disp.setmacro ("messagestatus", "users", messagestatus);
  
  textout << outconvert << disp
	  << "_usersedituser:header_\n_usersedituser:content_\n_usersedituser:footer_\n";

  return true;
}

bool usersaction::do_deleteuser (cgiargsclass &args, displayclass &disp, 
				 outconvertclass &outconvert, ostream &textout, 
				 ostream &logout) {
  if (!args["cm"].empty()) {
    if (args["cm"] == "yes" && !args["umun"].empty()) {
      // user confirmed the deletion of the user
      user_database->delete_user(args["umun"]);
    }
    
    // redirect the user back to the listusers page
    return do_listusers (args, disp, outconvert, textout, logout);
  }

  define_user_macros (args, disp);
  textout << outconvert << disp
	  << "_usersdeleteuser:header_\n_usersdeleteuser:content_\n_usersdeleteuser:footer_\n";
  
  return true;
}

bool usersaction::do_changepasswd (cgiargsclass &args, displayclass &disp, 
				   outconvertclass &outconvert, ostream &textout, 
				   ostream &/*logout*/) {
  text_t messagestatus;
  
  if (!args["bcp"].empty()) {
    if (args["un"].empty()) {
      messagestatus = "_users:messageusernameempty_";
    } else if (args["umpw"].empty()) {
      messagestatus = "_users:messagepasswordempty_";
    } else if (args["umnpw1"].empty()) {
      messagestatus = "_users:messagenewpass1empty_";
    } else if (args["umnpw2"].empty()) {
      messagestatus = "_users:messagenewpass2empty_";
    } else if (args["umnpw1"] != args["umnpw2"]) {
      messagestatus = "_users:messagenewpassmismatch_";
    } else if (!userdbclass::password_ok(args["umnpw1"])) {
      messagestatus = "_users:messageinvalidpassword_";
    } else {
      userinfo_t userinfo;
      if (user_database->get_user_info (args["un"], userinfo) == ERRNO_SUCCEED) {
	// check old password
	if (userinfo.password != userdbclass::crypt_text(args["umpw"])) {
	  messagestatus = "_users:messagefailed_";
	  
	} else {
	  userinfo.password = userdbclass::crypt_text(args["umnpw1"]);
	  if (user_database->set_user_info (args["un"], userinfo) == ERRNO_SUCCEED) {
	    // everything is ok
	    textout << outconvert << disp
		    << "_userschangepasswdok:header_\n"
	      "_userschangepasswdok:content_\n"
	      "_userschangepasswdok:footer_\n";
	    return true;
	  }
	}
      }
    }
  }

  disp.setmacro ("messagestatus", "users", messagestatus);

  textout << outconvert << disp
	  << "_userschangepasswd:header_\n"
    "_userschangepasswd:content_\n"
    "_userschangepasswd:footer_\n";

  return true;
}

#endif //GSDL_USE_USERS_ACTION
