/*
 *    ModifyUsersDB.java
 *    Copyright (C) 2008 New Zealand Digital Library, http://www.nzdl.org
 *
 *    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.
 */
package org.greenstone.cli;

import java.sql.SQLException;
import java.util.Iterator;
import org.greenstone.gsdl3.util.AuthenticationHelper;
import org.greenstone.gsdl3.util.DerbyWrapper;
import org.greenstone.gsdl3.util.UserTermInfo;
import org.greenstone.gsdl3.util.UserQueryResult;

//import org.greenstone.admin.guiext.PropertiesStep;

/** 
    To run this from the command-line, first make sure that the networked derby server is running (ant start-derby),
    then run:

    java -Dgsdl3.writablehome=/Scratch/ak19/gs3-svn-2Sep2015/web -cp bin/java/gs3-cli.jar:web/WEB-INF/lib/gsdl3.jar:web/WEB-INF/lib/gutil.jar:web/WEB-INF/lib/derby.jar:web/WEB-INF/lib/derbyclient.jar:web/WEB-INF/lib/log4j-1.2.8.jar:web/WEB-INF/lib/commons-codec-1.7.jar:web/WEB-INF/classes org.greenstone.cli.ModifyUsersDB usersDB <username or 'ALL'> [options specifying user fields to change, e.g.: password=me!]
    
    Now just pass in "usersDB" for networked derby server, previously web/etc/usersDB/ was needed.

    Don't forget to stop the networked derby server again at the end, if you had started it: ant stop-derby

*/ 
public class ModifyUsersDB
{
    public static int PWD_MIN_LENGTH = 3;
    public static int PWD_MAX_LENGTH = 20;

	public static void main(String[] args) throws SQLException
	{

		if (args.length < 3)
		{ // at minimum one field belonging to a username has to be changed
			System.out.println("Usage: java org.greenstone.cli.ModifyUsersDB <full_path_of_the_usersDB> <username|ALL> [-noAdd] [password=pwd] [groups=grp] [addgroups=grp] [accounstatus=status] [comment=cmt] [email=address]");
			System.exit(0);
		}

		String usersDB = args[0];
		String username = args[1];

		String password = null;
		String groups = null;
                String addgroups = null;
		String accountstatus = null;
		String comment = null;
		String email = null;

		boolean noAdd = false;

		// If the user specifically sets any of the fields on the cmdline, they'll be overwritten in the db, 
		// even if the user had set them to empty. Except the password which must be between PWD_MIN_LENGTH and PWD_MAX_LENGTH characters.
		for (int i = 2; i < args.length; i++)
		{
			if (args[i].startsWith("password="))
			{
				password = args[i].substring("password=".length());

				if (password.length() < PWD_MIN_LENGTH || password.length() > PWD_MAX_LENGTH)
				{
                                  System.out.println("Password not updated. It should be between " + PWD_MIN_LENGTH + " and " + PWD_MAX_LENGTH + " characters (inclusive).");
                                  
                                  password = null;
				}
				else
				{
					// Use the same encryption technique used by the Admin Authentication page
					// This ensures that the password generated for a string remains consistent
					//System.err.println("**** Password entered was: " + password);
					password = AuthenticationHelper.hashPassword(password);
				}

			}
			else if (args[i].startsWith("groups="))
			{
				groups = args[i].substring("groups=".length());
                                groups = UserTermInfo.expandGroups(groups);
			}
                        else if (args[i].startsWith("addgroups="))
                        {
                          addgroups = args[i].substring("addgroups=".length());
                          addgroups = UserTermInfo.expandGroups(addgroups);
                        }
			else if (args[i].startsWith("accountstatus="))
			{
				accountstatus = args[i].substring("accountstatus=".length());
			}
			else if (args[i].startsWith("status="))
			{
				accountstatus = args[i].substring("status=".length());
			}
			else if (args[i].startsWith("comment="))
			{
				comment = args[i].substring("comment=".length());
			}
			else if (args[i].startsWith("email="))
			{
				email = args[i].substring("email=".length());
			}
			else if (args[i].equals("-noAdd"))
			{
				noAdd = true;
			}
		}

                if (groups != null && addgroups != null) {
                  System.err.println("You can't use groups and addgroup at the same time");
                  System.exit(0);
                }
		// find the user to modify
                DerbyWrapper.createDatabaseIfNeeded(); // in case we have no existing db
		DerbyWrapper dw = new DerbyWrapper(usersDB);
                if (!dw.isConnected()) {
                  System.out.println("Couldn't connect to database! Exiting without modification");
                  return;
                }
                if (username.equals("ALL")) {
                  // modify all users
                  UserQueryResult findUserResult = dw.findUser(null, null);// this returns all users
                  Iterator<UserTermInfo> it = findUserResult.getUserTerms().iterator();
                  while (it.hasNext()) {
                    UserTermInfo uti = it.next();
                    String un = uti.getUsername();
                    modifyUser(dw, uti, un, password, groups, addgroups, accountstatus, comment, email);
                  }
                } else {                  
                  UserQueryResult findUserResult = dw.findUser(username, null);

                  if (findUserResult == null)
                  {
                    if (noAdd)
                    {
                      System.out.println("Failed to update user. Cannot find user " + username + " in " + usersDB + " database.");
                    }
                    else
                    { // add new user

                      //System.err.println("**** Trying to add user: ");
                      //System.err.println("**** " + username + " " + password + " " + groups + " " + accountstatus + " " + comment + " " + email);

                      if (password == null) {
                        System.err.println("cannot add a user without a password");
                       
                      } else {
                        if (groups == null) {
                          if (addgroups != null) {
                            groups = addgroups;
                          } else {
                            groups = "";
                          }
                        }
                        if (accountstatus == null) {
                          accountstatus = "true";
                        }
                        if (comment == null) {
                          comment = "";
                        }
                        if (email == null) {
                          email = "";
                        }
                        System.out.println("Adding new user: un=" + username + ", pw=" + password + ", groups=" + groups + ", status=" + accountstatus + ", comment=" + comment + ", email=" + email);
                        dw.addUser(username, password, groups, accountstatus, comment, email);
                      }
                    }
                  }
                  else
                  { // modify existing user data
                    UserTermInfo user = findUserResult.getUserTerms().get(0);
                    modifyUser(dw, user, username, password, groups, addgroups, accountstatus, comment, email);
                  }
		
		}

		dw.closeDatabase();

	}

  private static void modifyUser(DerbyWrapper dw, UserTermInfo user, String username, String password, String groups, String addgroups, String accountstatus, String comment, String email) {

      // Copied code back from svn rev=35298 into this function, as without it, modifying users/admin pwd
      // wiped out rest of its details from userdb. Notably groups, as groups below now needs to be null
      // for code to read groups' values back in from db
      if (groups.equals(""))
	{
	    // groups should be expandedGroups because we no longer store the groups in userDB
	    // as user-entered or compacted, but as programmatically expanded.
	    // This allows HttpServletRequest.isUserInRole() to now automatically retrieve the
	    // expandedGroups list of a user to check collectionConfig.xml security elements against.
	    
	    groups = user.getExpandedGroups(); // get from database
	} //else {
    //groups = UserTermInfo.expandGroups(groups); // ensure groups are stored expanded in userDB
    //} // Covered: groups var comes in expanded when called from ModifyUsersDB.java::main()
    // Only should be done if anyone else can call this modifyUser() function and if they don't ensure
    // groups expanded first
    // in case any of fields other than username are not specified, get fallbacks from the database
    

      // groups can never be null at this point if called by ModifyUsersDB.java::main() above,
      // as main() does groups=expandGroups() which never returns null, only "" at minimum.      
    if (groups == null && addgroups != null) {
      groups = user.getExpandedGroups(); // get the groups from db, as we want to add on to what is already there
    }

    if (password.equals(""))
	{
	    password = user.getPassword(); // already stored hashed-and-hexed in DB
	}
    
    if (accountstatus.equals(""))
	{
	    accountstatus = user.getAccountStatus().equals("") ? "true" : user.getAccountStatus();
	}
    if (comment.equals(""))
	{
	    comment = user.getComment();
	}
    if (email.equals(""))
	{
	    email = user.getEmail();
	}
    
    if (addgroups != null) {
      if (!groups.equals("")) {
        groups += ",";
      }
      groups += addgroups;
    }
    System.out.println("Modifying existing user: un=" + username + ", pw=" + password + ", groups=" + groups + ", status=" + accountstatus + ", comment=" + comment + ", email=" + email);
    dw.modifyUserInfo(username, password, groups, accountstatus, comment, email);
  }
}


