/**
 * @author : Thomas BELLEMBOIS
 * @version : 1.6
 * date de création : 29 juin 2004
 * date de dernière modification : 29 avril 2005
 * modifications:
 * 31.01.2005 Thomas Bellembois (Rennes1) : modification des paramètres d'appel de la routine (filtre LDAP) - ajout de logs - correction de bugs 
 * 01.02.2005 Thomas Bellembois (Rennes1) : modification du paramètre ldapScope
 * 09.02.2005 Thomas Bellembois (Rennes1) : prise en compte des roles locaux et uPortal
 * 04.03.2005 Thomas Bellembois (Rennes1) : mise à jour de la librairie esup-acl-manager - passage en 1.0.3
 * 06.04.2005 Thomas Bellembois (Rennes1) : correction du bug quand le contexte est "/"
 * 29.04.2005 Thomas Bellembois (Rennes1) : correction accès en bind non anonyme impossible
 **/

package org.esupportail.portal.utils.injac.homeDirCreation;

import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpURL;
import org.apache.commons.httpclient.URIException;
import org.esupportail.portal.utils.injac.homeDirCreation.config.Config;
import org.esupportail.portal.utils.injac.homeDirCreation.exception.ConfigException;
import org.esupportail.portal.utils.injac.homeDirCreation.exception.WrongPathException;
import org.esupportail.portal.utils.webdav.acl.ACLManager;
import org.esupportail.portal.utils.webdav.acl.EsupPermissions;
import org.esupportail.portal.utils.webdav.acl.exception.ReadAclException;
import org.esupportail.portal.utils.webdav.acl.exception.WriteAclException;

import com.novell.ldap.*;
import org.apache.log4j.*;
import org.apache.webdav.lib.WebdavResource;
import org.apache.webdav.lib.methods.DepthSupport;
import org.apache.webdav.lib.methods.PropFindMethod;

import java.io.IOException;
import java.util.*;

/**
 * THE FILE config.xml configures this application, read it with attention !<br/>
 * 
 * Creates homedirs for users of the WebDAV server<br/>
 * HomedirCreation -help<br/>
 * HomedirCreation -user [username]<br/>Create homedir of the user [username]
 * HomedirCreation -all<br/>Create a series of homedir for users registered in the LDAP directory specified in the config.xml file
 * 
 */
public class HomedirCreation {
	
	private HttpURL httpUrl;
	private WebdavResource webDavResource;
	
	private String ldapUrl;
	private String ldapPort;
	private String ldapBaseDn;
	private int ldapScope;
	private String ldapPrincipal;
	private String ldapCredential;
	private String ldapRequestFilter; // not from the config file but given in argument
	private String ldapUserUidAttribut;
	private String ldapMaxResults;
	
	private String webdavServerUrl;
	private String webdavServerLogin;
	private String webdavServerPassword;
	// DEPRECATED
	//private String webdavServerUsersRole;
	
	private String userDirectoryRootDirectory;
	
	private String roleDirectoryRootDirectory;
	private String localRolesDirectory;
	private String uPortalRolesDirectory;
	
	private ACLManager aclManager;
	
	private String directoriesStructureRootDirectory;
	private String directoriesStructureHashType;
	
	private Logger logger = Logger.getLogger("org.esupportail.portal.utils.injac.homeDirCreation.HomedirCreation");
	
	/**
	 * Get a ressource property
	 * @param webdavResource
	 * @param path
	 * @param properties
	 * @return Property Enumeration
	 * @throws URIException
	 * @throws IOException
	 */
	private Enumeration getProperty(WebdavResource webdavResource, String path, Vector properties) throws URIException, IOException {
		PropFindMethod method = new PropFindMethod(path, DepthSupport.DEPTH_INFINITY, properties.elements());
		webdavResource.retrieveSessionInstance().executeMethod(method);
		return method.getResponseProperties(path);
	}// getProperty
	
	/**
	 * @return String Vector of registered user in the LDAP directory specified in the config.xml file
	 */
	private Vector getLdapRegisteredUsers(){
		
		logger.info(getClass().getName()+":getLdapRegisteredUsers");
		
		Vector ldapRegisteredUsers = new Vector();
		////////////////////////////////////////////////////////
		//
		// Variables used to send requests to the LDAP directory
		//
		int msLimit=0; 		 								// The maximum time in milliseconds to wait for results
		int serverTimeLimit=0; 								// The maximum time in seconds that the server should spend returning search results
		int dereference=LDAPSearchConstraints.DEREF_NEVER; 	// Specifies when aliases should be dereferenced
		int maxResults=Integer.parseInt(ldapMaxResults);	// The maximum number of search results to return for a search request
		boolean doReferrals=false; 				// Determines whether to automatically follow referrals or not
		int batchSize=0; 	 	 				// The number of results to return in a batch
		LDAPReferralHandler handler=null; 		// The custom authentication handler called when LDAPConnection needs to authenticate, typically on following a referral
		int hop_limit=0; 				 		// The maximum number of referrals to follow in a sequence during automatic referral following
		String[] attrs = new String[1]; 		// The names of attributes to retrieve - only the ldapUserUidAttribut
		attrs[0]=ldapUserUidAttribut;
		boolean typesOnly = false;				// If true, returns the names but not the values of the attributes found, if false, returns the names and values for attributes found
		
		try{
			// Connecting to the LDAP directory
			LDAPConnection ldapConnection = new LDAPConnection();
			ldapConnection.connect(ldapUrl, Integer.parseInt(ldapPort));
			ldapConnection.bind(LDAPConnection.LDAP_V3, ldapPrincipal, ldapCredential.getBytes());
			if(ldapConnection.isConnected()){logger.info(getClass().getName()+":getLdapRegisteredUsers:connected to "+ldapUrl);}
			else logger.fatal(getClass().getName()+":getLdapRegisteredUsers:failed to connect to "+ldapUrl);
			
			// Getting all the entries in the LDAP directory
			LDAPSearchConstraints LdapSearchConstraints = new LDAPSearchConstraints(msLimit, serverTimeLimit, dereference, maxResults, doReferrals, batchSize, handler, hop_limit); 	           
			LDAPSearchResults ldapSearchResults = ldapConnection.search(ldapBaseDn, ldapScope, ldapRequestFilter, attrs, typesOnly, LdapSearchConstraints); 
			
			if (ldapSearchResults == null) throw new ConfigException("HomedirCreation", "getLdapRegisteredUsers", "no users retrieved from LDAP");
			
			while (ldapSearchResults.hasMore())
			{
				LDAPEntry ldapEntry=ldapSearchResults.next();
				LDAPAttributeSet ldapAttributeSet = ldapEntry.getAttributeSet();
				String uid = ldapAttributeSet.getAttribute(ldapUserUidAttribut).getStringValue();
				
				ldapRegisteredUsers.add(uid);
				if(logger.isDebugEnabled()){logger.debug(getClass().getName()+":getLdapRegisteredUsers:..user "+uid+" retrieved");}
			}// while
			
			// Disconnecting from the LDAP directory
			ldapConnection.disconnect();
			if(!ldapConnection.isConnected()){logger.info(getClass().getName()+":getLdapRegisteredUsers:disconected from "+ldapUrl);}
			else logger.fatal(getClass().getName()+":getLdapRegisteredUsers:failed to disconnect from "+ldapUrl);
		}
		catch(LDAPException ldapException){logger.error("HomedirCreation::getLdapRegisteredUsers()::"+ldapException);}
		catch(ConfigException configException){logger.error(configException.toString());}
		return ldapRegisteredUsers;
		
	}// getLdapRegisteredUsers
	
	/**
	 * Initialize all the variables and connect to the WebDAV server
	 */
	private void init(){
		try{
			logger.info(getClass().getName()+":init");
			// Getting all the entries of the configuration file
			ldapUrl = Config.getInstance().getLdapUrl();     					 // LDAP directory name
			ldapPort = Config.getInstance().getLdapPort();                       // LDAP server port
			ldapBaseDn = Config.getInstance().getLdapBaseDn();                   // LDAP server suffix
			ldapPrincipal = Config.getInstance().getLdapPrincipal();
			ldapCredential = Config.getInstance().getLdapCredential();
			// DEPRECATED
			//ldapRequestFilter = Config.getInstance().getLdapRequestFilter();     // LDAP request filter
			ldapUserUidAttribut = Config.getInstance().getLdapUserUidAttribut(); // The attribut of users UIDs in the LDAP directory
			ldapMaxResults = Config.getInstance().getLdapMaxResults();			 // Max number of results returned by the LDAP directory
			String ldapScopeTemp =  Config.getInstance().getLdapScope();		 // LDAP scope
			
			logger.debug(getClass().getName()+":init:..ldapUrl="+ldapUrl);
			logger.debug(getClass().getName()+":init:..ldapPort="+ldapPort);
			logger.debug(getClass().getName()+":init:..ldapBaseDn="+ldapBaseDn);
			logger.debug(getClass().getName()+":init:..ldapPrincipal="+ldapPrincipal);
			logger.debug(getClass().getName()+":init:..ldapCredential="+"****hidden****");
			logger.debug(getClass().getName()+":init:..ldapUserUidAttribut="+ldapUserUidAttribut);
			
			if (ldapScopeTemp.equals("SUBTREE_SCOPE")){
				ldapScope = LDAPConnection.SCOPE_SUB;
				logger.debug(getClass().getName()+":init:..ldapScope=SUBTREE_SCOPE");
			}else if (ldapScopeTemp.equals("ONELEVEL_SCOPE")){
				ldapScope = LDAPConnection.SCOPE_ONE;
				logger.debug(getClass().getName()+":init:..ldapScope=ONELEVEL_SCOPE");
			}else if (ldapScopeTemp.equals("OBJECT_SCOPE")){
				ldapScope = LDAPConnection.SCOPE_BASE;
				logger.debug(getClass().getName()+":init:..ldapScope=OBJECT_SCOPE");
			}else{
				throw new ConfigException("HomedirCreation", "init", "LDAP scope wrong");
			}// if (ldapScopeTemp.equals("SUBTREE"))
						
			webdavServerUrl = Config.getInstance().getWebdavServerUrl();		    // WebDav server URL
			webdavServerLogin = Config.getInstance().getWebdavServerLogin();	    // WebDav server login
			webdavServerPassword = Config.getInstance().getWebdavServerPassword();  // WebDav server password
			// DEPRECATED
			//webdavServerUsersRole = Config.getInstance().getWebdavServerUsersRole();// Role assigned to users
			
			logger.debug(getClass().getName()+":init:..webdavServerUrl="+webdavServerUrl);
			logger.debug(getClass().getName()+":init:..webdavServerLogin="+webdavServerLogin);
			logger.debug(getClass().getName()+":init:..webdavServerPassword=****hidden****");

			// context == /slide for example
			httpUrl = new HttpURL(webdavServerUrl+"/");
			httpUrl.setUserinfo(webdavServerLogin, webdavServerPassword);
			String context = httpUrl.getCurrentHierPath();
			if (context.equalsIgnoreCase("/")) context = "";
			directoriesStructureRootDirectory = context+"/"+Config.getInstance().getDirectoriesStructureRootDirectory(); // Root directory to create the directories on the WebDav server
			directoriesStructureHashType = Config.getInstance().getDirectoriesStructureHashType(); // Hash type to create the directories on the WebDav server	
			userDirectoryRootDirectory = context+"/"+Config.getInstance().getUserDirectoryRootDirectory(); // Root directory to create homedirs on the WebDav server
			roleDirectoryRootDirectory = context+"/"+Config.getInstance().getRoleDirectoryRootDirectory(); // Root directory to create roles on the WebDav server
			localRolesDirectory = context+"/"+Config.getInstance().getRoleDirectoryLocalRolesDirectory();  // Root directory to create local roles on the WebDav server
			uPortalRolesDirectory = context+"/"+Config.getInstance().getRoleDirectoryUPortalRolesDirectory(); // Root directory to create uPortal roles on the WebDav server
			
			logger.debug(getClass().getName()+":init:..context="+context);
			logger.debug(getClass().getName()+":init:..userDirectoryRootDirectory="+userDirectoryRootDirectory);
			logger.debug(getClass().getName()+":init:..roleDirectoryRootDirectory="+roleDirectoryRootDirectory);
			logger.debug(getClass().getName()+":init:..localRolesDirectory="+localRolesDirectory);
			logger.debug(getClass().getName()+":init:..uPortalRolesDirectory="+uPortalRolesDirectory);
			logger.debug(getClass().getName()+":init:..directoriesStructureRootDirectory="+directoriesStructureRootDirectory);
			logger.debug(getClass().getName()+":init:..directoriesStructureHashType="+directoriesStructureHashType);
			
			// Connecting to the Webdav server
			webDavResource = new WebdavResource(httpUrl);
			logger.info(getClass().getName()+":init:connected to "+webDavResource.getPath());
		
			logger.info(getClass().getName()+":init:creating the ACL manager");
			aclManager = new ACLManager(webDavResource, ACLManager.DEFAULT_SLIDE_NAMESPACE, userDirectoryRootDirectory, roleDirectoryRootDirectory);
			
			
		}
		catch (ConfigException configException){logger.error("HomedirCreation::init()::"+configException);}
		catch (HttpException httpException){logger.error("HomedirCreation::init()::"+httpException);}
		catch (IOException ioException){logger.error("HomedirCreation::init()::"+ioException);}
	}// init
	
	/**
	 * Verify if the WebDAV ressource exits
	 * @param webDavResource
	 * @param path syntax: /{context}/path - exemple: /slide/files/myRessource
	 * @return true if the path exists, else false
	 */
	private boolean pathExist(WebdavResource webDavResource, String path){
		try {
			webDavResource.setPath(path);
		} catch (HttpException httpException) {
			return false;
		} catch (IOException ioException) {
			logger.info("HomedirCreation::pathExist::"+ioException);
		}
		return true;
	}// isExist
	
	/**
	 * Create and protect a user homedir
	 * @param webDavResource
	 * @param user
	 * @throws WrongPathException
	 */
	private void createHomedir(WebdavResource webDavResource, String user) throws WrongPathException{
		
		logger.info(getClass().getName()+":createHomedir:user="+user);
		
		try{
			String userFirstSubdirectory = new String();
			String userSecondSubdirectory = new String();
			int userLength = user.length();
			
			// Creating the homedir path depending on the hash method
			if(directoriesStructureHashType.equals("with_hash")){
				if(logger.isDebugEnabled()){logger.debug(getClass().getName()+":createHomedir:..hash method=with_hash");}
				userFirstSubdirectory = user.charAt(0)+"";
				userSecondSubdirectory = user.charAt(0)+""+user.charAt(1)+"";
			}// if
			else if(directoriesStructureHashType.equals("with_hash_reverse")){
				if(logger.isDebugEnabled()){logger.debug(getClass().getName()+":createHomedir:..hash method=with_hash_reverse");}
				userFirstSubdirectory = user.charAt(userLength-1)+"";
				userSecondSubdirectory = user.charAt(userLength-1)+""+user.charAt(userLength-2)+"";
			}//else
			
			// Verifying if the directoriesStructureRootDirectory exits - must be created by the administrator
			if (!pathExist(webDavResource,directoriesStructureRootDirectory)){
				throw new WrongPathException("HomedirCreation", "createHomedir", directoriesStructureRootDirectory);
			}// if
			
			// Creating the homedir (and the full path if not created)
			if (pathExist(webDavResource,directoriesStructureRootDirectory+"/"+userFirstSubdirectory)){
				if(logger.isDebugEnabled()){logger.debug(directoriesStructureRootDirectory+"/"+userFirstSubdirectory+"/"+" exists");}
				if (pathExist(webDavResource,directoriesStructureRootDirectory+"/"+userFirstSubdirectory+"/"+userSecondSubdirectory)){
					if(logger.isDebugEnabled()){logger.debug(directoriesStructureRootDirectory+"/"+userFirstSubdirectory+"/"+userSecondSubdirectory+"/"+" exists");}
					if(pathExist(webDavResource,directoriesStructureRootDirectory+"/"+userFirstSubdirectory+"/"+userSecondSubdirectory+"/"+user)){
						logger.debug(getClass().getName()+":createHomedir:....homedir of "+user+" already exists !!!");
					}// if
					else{
						if(logger.isDebugEnabled()){logger.debug(getClass().getName()+":createHomedir:....creation of "+directoriesStructureRootDirectory+"/"+userFirstSubdirectory+"/"+userSecondSubdirectory+"/"+user);}
						webDavResource.mkcolMethod(directoriesStructureRootDirectory+"/"+userFirstSubdirectory+"/"+userSecondSubdirectory+"/"+user);
					}// else
				}// if
				else{
					if(logger.isDebugEnabled()){logger.debug(getClass().getName()+":createHomedir:....creation of "+directoriesStructureRootDirectory+"/"+userFirstSubdirectory+"/"+userSecondSubdirectory);}	
					webDavResource.mkcolMethod(directoriesStructureRootDirectory+"/"+userFirstSubdirectory+"/"+userSecondSubdirectory);
					if(logger.isDebugEnabled()){logger.debug(getClass().getName()+":createHomedir:....creation of "+directoriesStructureRootDirectory+"/"+userFirstSubdirectory+"/"+userSecondSubdirectory+"/"+user);}	
					webDavResource.mkcolMethod(directoriesStructureRootDirectory+"/"+userFirstSubdirectory+"/"+userSecondSubdirectory+"/"+user);
				}// else
			}// if
			else{
				if(logger.isDebugEnabled()){logger.debug(getClass().getName()+":createHomedir:....creation of "+directoriesStructureRootDirectory+"/"+userFirstSubdirectory);}	
				webDavResource.mkcolMethod(directoriesStructureRootDirectory+"/"+userFirstSubdirectory);
				if(logger.isDebugEnabled()){logger.debug(getClass().getName()+":createHomedir:....creation of "+directoriesStructureRootDirectory+"/"+userFirstSubdirectory+"/"+userSecondSubdirectory);}
				webDavResource.mkcolMethod(directoriesStructureRootDirectory+"/"+userFirstSubdirectory+"/"+userSecondSubdirectory);
				if(logger.isDebugEnabled()){logger.debug(getClass().getName()+":createHomedir:....creation of "+directoriesStructureRootDirectory+"/"+userFirstSubdirectory+"/"+userSecondSubdirectory+"/"+user);}
				webDavResource.mkcolMethod(directoriesStructureRootDirectory+"/"+userFirstSubdirectory+"/"+userSecondSubdirectory+"/"+user);
			}// else
			
			// Protecting the homedir with an ACL
			try {
				EsupPermissions esupPermissions;
				if(logger.isDebugEnabled()){logger.debug(getClass().getName()+":createHomedir:....granting all on "+directoriesStructureRootDirectory+"/"+userFirstSubdirectory+"/"+userSecondSubdirectory+"/"+user+" for "+userDirectoryRootDirectory+"/"+user);}
				esupPermissions = aclManager.grant(directoriesStructureRootDirectory+"/"+userFirstSubdirectory+"/"+userSecondSubdirectory+"/"+user, userDirectoryRootDirectory+"/"+user, "all");
				if(logger.isDebugEnabled()){logger.debug(getClass().getName()+":createHomedir:....granting all on "+directoriesStructureRootDirectory+"/"+userFirstSubdirectory+"/"+userSecondSubdirectory+"/"+user+" for "+localRolesDirectory+"/root");}
				esupPermissions = aclManager.grant(directoriesStructureRootDirectory+"/"+userFirstSubdirectory+"/"+userSecondSubdirectory+"/"+user, localRolesDirectory+"/root", "all");
				if(logger.isDebugEnabled()){logger.debug(getClass().getName()+":createHomedir:....denying all on "+directoriesStructureRootDirectory+"/"+userFirstSubdirectory+"/"+userSecondSubdirectory+"/"+user+" for all");}
				esupPermissions = aclManager.deny(directoriesStructureRootDirectory+"/"+userFirstSubdirectory+"/"+userSecondSubdirectory+"/"+user, "all", "all");
				if(logger.isDebugEnabled()){logger.debug(getClass().getName()+":createHomedir:......ACLs="+esupPermissions.toString());}
			} catch (ReadAclException e) {
				logger.error("HomedirCreation:createHomedir:"+e.getMessage());
			} catch (WriteAclException e) {
				logger.error("HomedirCreation:createHomedir:"+e.getMessage());
			}
		}
		catch(HttpException httpException){logger.error("HomedirCreation::createHomedir::"+httpException);}
		catch(IOException ioException){logger.error("HomedirCreation::createHomedir::"+ioException);}
				
	}// createHomedir
	
	public static void main(String[] args) {
		
		HomedirCreation homedirCreation = new HomedirCreation();
		
		///////////////////////////
		//
		// Verifying the parameters
		//
		if(args.length==0){
			homedirCreation.logger.error("Parameter needed");
			System.exit(0);
		}
		if(args.length>2){
			homedirCreation.logger.error("Too many arguments");
			System.exit(0);
		}
		if(args[0].equals("-help")){
			homedirCreation.logger.info("HomedirCreation -all|-user <username>");
			System.exit(0);
		}
		if(!args[0].equals("-all") && !args[0].equals("-user")){
			homedirCreation.logger.error("Wrong first parameter");
			System.exit(0);
		}
		if(args[0].equals("-user") && !(args.length==2)){
			homedirCreation.logger.error("Wrong username");
			System.exit(0);
		}
		
		// Initializing variables - connecting to the WebDAV server
		homedirCreation.logger.info(homedirCreation.getClass().getName()+":main:initializing the process");
		homedirCreation.init();
		
		try{			
			if(args[0].equals("-all")){
				
				homedirCreation.logger.info(homedirCreation.getClass().getName()+":main:param. -all entered");
				
				// Getting the LDAP filter argument
				homedirCreation.ldapRequestFilter = args[1];
				homedirCreation.logger.debug(homedirCreation.getClass().getName()+":main:..LDAP filter="+homedirCreation.ldapRequestFilter);
				
				// Creating homedir of every registered user (in the LDAP directory)
				homedirCreation.logger.info(homedirCreation.getClass().getName()+":main:..getting users from LDAP");
				Vector registeredUsers = homedirCreation.getLdapRegisteredUsers();
				String currentUser;
				
				homedirCreation.logger.info(homedirCreation.getClass().getName()+":main:..creating homedirs");
				
				for (Enumeration enum = registeredUsers.elements(); enum.hasMoreElements();) {
					
					currentUser = (String)enum.nextElement();
					homedirCreation.createHomedir(homedirCreation.webDavResource, currentUser);
					
				}// for
				
			}// -all
			else{
				
				homedirCreation.logger.info(homedirCreation.getClass().getName()+":main:param. -user entered user="+args[1]);
				
				// Creating homedir of one user
				homedirCreation.logger.info(homedirCreation.getClass().getName()+":main:..creating homedir");
				homedirCreation.createHomedir(homedirCreation.webDavResource, args[1]);	
			}// -user
			
			// Disconnecting from the Webdav server
			homedirCreation.webDavResource.close();
			homedirCreation.logger.info("Disconnected from "+homedirCreation.webDavResource.getPath());
		}
		catch (URIException e) {homedirCreation.logger.error("HomedirCreation::main()::"+e);}
		catch (NullPointerException e1) {homedirCreation.logger.error("HomedirCreation::main()::"+e1);}
		catch (IOException e2){homedirCreation.logger.error("HomedirCreation::main()::"+e2);}
		catch (WrongPathException e3){homedirCreation.logger.error("HomedirCreation::main()::"+e3);}
		homedirCreation.logger.info("Process finished...");
		
	}// main
	
}// HomedirCreation
