/*
 * UserCreation.java,v 1.0 5 juil. 2004
 * Copyright (c) 2004 Esup Portail (www.esup-portail.org)
 * Classes: UserCreation.java
 * Original Author: tbellemb
 */

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

import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpURL;
import org.apache.commons.httpclient.URIException;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.esupportail.portal.utils.injac.userCreation.config.Config;
import org.esupportail.portal.utils.injac.userCreation.exception.ConfigException;

import com.novell.ldap.*;
import org.apache.log4j.*;
import org.apache.webdav.lib.Property;
import org.apache.webdav.lib.PropertyName;
import org.apache.webdav.lib.ResponseEntity;
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.io.StringReader;
import java.util.*;

/**
 * @author : tbellemb
 * date : 5 juil. 2004
 */
/**
 * THE FILE config.xml configures this application, read it with attention !<br/>
 * 
 * Creates user in the WebDAV server<br/>
 * UserCreation -help<br/>
 * UserCreation -user [username]<br/>Create the user [username]
 * UserCreation -all<br/>Create a series of users registered in the LDAP directory specified in the config.xml file
 * 
 */
public class UserCreation {
	
	private HttpURL httpUrl;
	private WebdavResource webDavResource;
	
	private String ldapUrl;
	private String ldapPort;
	private String ldapBaseDn;
	private String ldapRequestFilter;
	private String ldapUserUidAttribut;
	private String ldapMaxResults;
	
	private String webdavServerUrl;
	private String webdavServerLogin;
	private String webdavServerPassword;
	private String webdavServerTrustedPassword;
	private String webdavServerUsersRole;
	
	private String userDirectoryRootDirectory;
	private String roleDirectoryRootDirectory;
	
	private Logger logger = Logger.getLogger("UserCreation");
	
	/**
	 * 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(){
		
		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
		int scope = LDAPConnection.SCOPE_SUB;  // The scope of the entries to search
		String[] attrs = new String[1]; 		// The names of attributes to retrieve
		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));
			if(ldapConnection.isConnected()){logger.info("Connected 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, scope, ldapRequestFilter, attrs, typesOnly, LdapSearchConstraints); 
						
			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("User "+uid+" retrieved");}
			}// while
			
			// Disconnecting from the LDAP server
			ldapConnection.disconnect();
			if(!ldapConnection.isConnected()){logger.info("Disconnected from "+ldapUrl);}
			
		}catch(LDAPException ldapException){logger.info("UserCreation::getLdapRegisteredUsers::"+ldapException);}
		return ldapRegisteredUsers;
		
	}// getLdapRegisteredUsers
	
	/**
	 * Verify if a user exits
	 * @param webDavResource
	 * @param user
	 * @return true if the user exists, else false
	 */
	private boolean userExist(WebdavResource webDavResource, String user){
		try {
			webDavResource.setPath(userDirectoryRootDirectory+"/"+user);
		} catch (HttpException httpException) {
			return false;
		} catch (IOException ioException) {
			logger.info("UserCreation::userExist::"+ioException);
		}
		return true;
	}// isExist
	
	/**
	 * Get all the properties of a ressource 
	 * @param webDavResource
	 * @param path
	 * @return String of properties
	 */
	private String listAllProperties(WebdavResource webDavResource, String path){
		String result = new String("");
		
		try {
			Enumeration e = webDavResource.propfindMethod(path, DepthSupport.DEPTH_INFINITY);
			
			while(e.hasMoreElements()){
				ResponseEntity responseEntity = (ResponseEntity)(e.nextElement());
				Enumeration e1 = responseEntity.getProperties();
				while(e1.hasMoreElements()){
					result+=(e1.nextElement().toString()+"\n");
				}// while
			}// while
			
		} catch (HttpException httpException) {
			logger.info("UserCreation::listAllProperties::"+httpException);
		} catch (IOException ioException) {
			logger.info("UserCreation::listAllProperties::"+ioException);
		}
		
		result = result.replaceAll(" ", "");
		result = result.replaceAll("\t", "");
		return result;
	}// listAllProperties
	
	/**
	 * Create a user
	 * @param webDavResource
	 * @param user
	 */
	private void createUser(WebdavResource webDavResource, String user){
		try{
			// Verifying if the user exist and if not creating him
			if (userExist(webDavResource,user)){
				logger.info(user+" exists");
			}// if
			else{
				// Creating the user
				if(logger.isDebugEnabled()){logger.debug("Creation of "+user);}	
				
				boolean successMkcol = webDavResource.mkcolMethod(userDirectoryRootDirectory+"/"+user);
				if(logger.isDebugEnabled()){logger.debug(">Mkcol user "+user+" :"+successMkcol+"");}
				// Setting the password
				boolean successPatchUser = webDavResource.proppatchMethod(userDirectoryRootDirectory+"/"+user, new PropertyName("http://jakarta.apache.org/slide/", "password"), webdavServerTrustedPassword, true);				
				if(logger.isDebugEnabled()){logger.debug(">Proppatch password for user "+user+" :"+successPatchUser+"");}	
			}// else
		}
		catch(HttpException httpException){logger.info("UserCreation::createUser::"+httpException);}
		catch(IOException ioException){logger.info("UserCreation::createUser::"+ioException);}
		
	}// createUser
	
	/**
	 * Initialize all the variables and connect to the WebDAV server
	 */
	private void init(){
		try{			
			ldapUrl = Config.getInstance().getLdapUrl();     				 	 // LDAP directory name
			ldapPort = Config.getInstance().getLdapPort();                       // LDAP server port
			ldapBaseDn = Config.getInstance().getLdapBaseDn();                   // LDAP server suffix
			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
			
			webdavServerUrl = Config.getInstance().getWebdavServerUrl();		   // WebDav server URL
			webdavServerLogin = Config.getInstance().getWebdavServerLogin();	   // WebDav server login
			webdavServerPassword = Config.getInstance().getWebdavServerPassword(); // WebDav server password
			webdavServerTrustedPassword = Config.getInstance().getWebdavServerTrustedPassword(); //WebDav server trusted password
			webdavServerUsersRole = Config.getInstance().getWebdavServerUsersRole();	         //Role assigned to users
			
			// Connecting to the Webdav server
			httpUrl = new HttpURL(webdavServerUrl+"/");
			httpUrl.setUserinfo(webdavServerLogin, webdavServerPassword);
			String context = httpUrl.getCurrentHierPath();
			webDavResource = new WebdavResource(httpUrl);
			logger.info("Connected to "+webDavResource.getPath());
			
			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
			
		}
		catch(ConfigException configException){logger.info("UserCreation::init::"+configException);}
		catch (IOException ioException){logger.info("UserCreation::init::"+ioException);}
	}// init
	
	public static void main(String[] args) {
		
		UserCreation userCreation = new UserCreation();
		
		///////////////////////////
		//
		// Verifying the parameters
		//
		if(args.length==0){
			userCreation.logger.error("Parameter needed");
			System.exit(0);
		}
		if(args.length>2){
			userCreation.logger.error("Too many arguments");
			System.exit(0);
		}
		if(args[0].equals("-help")){
			userCreation.logger.info("HomedirCreation -all|-user <username>");
			System.exit(0);
		}
		if(!args[0].equals("-all") && !args[0].equals("-user")){
			userCreation.logger.error("Wrong first parameter");
			System.exit(0);
		}
		if(args[0].equals("-user") && !(args.length==2)){
			userCreation.logger.error("Wrong username");
			System.exit(0);
		}
		
		userCreation.logger.info("Starting process...");
		
		// Initializing variables - conencting to the WebDAV server
		userCreation.init();

		try{
			StringBuffer rolePatch = new StringBuffer();
			
			if(args[0].equals("-all")){
				// Creating user of every registered user (in the LDAP directory)
				Vector registeredUsers = userCreation.getLdapRegisteredUsers();
				String currentUser;
				
				userCreation.logger.info("***** CREATING USERS *****");
				for (Enumeration enum = registeredUsers.elements(); enum.hasMoreElements();){
					currentUser = (String)enum.nextElement();
					userCreation.createUser(userCreation.webDavResource, currentUser);
					rolePatch.append("<D:href xmlns:D='DAV:'>");
					rolePatch.append(userCreation.userDirectoryRootDirectory+"/"+currentUser);
					rolePatch.append("</D:href>");
				}// for
			}// -all
			else{
				// Creating user of one user				
				userCreation.logger.info("***** CREATING USER *****");
				userCreation.createUser(userCreation.webDavResource, args[1]);
				rolePatch.append("<D:href xmlns:D='DAV:'>");
				rolePatch.append(userCreation.userDirectoryRootDirectory+"/"+args[1]);
				rolePatch.append("</D:href>");
				
				// Getting the current value of the "group-member-set" property
				// --Calling the propFinMethod method
				Vector properties = new Vector();
				String propName = "group-member-set";
				PropertyName property = new PropertyName("DAV:", "group-member-set");
				properties.addElement(property);
				//Enumeration propertyValues = userCreation.webDavResource.propfindMethodV2(userCreation.roleDirectoryRootDirectory+"/"+userCreation.webdavServerUsersRole, properties);
				Enumeration propertyValues = userCreation.getProperty(userCreation.webDavResource, userCreation.roleDirectoryRootDirectory+"/"+userCreation.webdavServerUsersRole, properties);
				
				
				// --Parsing the answer
				StringBuffer oldGroupMemberSetValue = new StringBuffer("");
				StringReader stringReader;
				SAXReader reader = new SAXReader();
				String currentEntry = new String();
				Document document;
				
				while(propertyValues.hasMoreElements()){
					currentEntry = ((Property)(propertyValues.nextElement())).toString();
					stringReader = new StringReader(currentEntry);
					document = reader.read(stringReader);
					Element element = document.getRootElement();
					
					Iterator iter = element.elementIterator();
					while(iter.hasNext()){
						Element e = (Element)iter.next();
						oldGroupMemberSetValue.append("<D:href xmlns:D='DAV:'>");
						oldGroupMemberSetValue.append(e.getStringValue());
						oldGroupMemberSetValue.append("</D:href>");
					}// while
				}// while	
				
				// Adding the new user to the group-member-set
				oldGroupMemberSetValue.append(rolePatch);
				rolePatch = oldGroupMemberSetValue;
			}// -user
			
			boolean successPatchRole = userCreation.webDavResource.proppatchMethod(userCreation.roleDirectoryRootDirectory+"/"+userCreation.webdavServerUsersRole, new PropertyName("DAV:", "group-member-set"), rolePatch.toString(), true);	
			if(userCreation.logger.isDebugEnabled()){userCreation.logger.debug(">Proppatch role :"+successPatchRole+"");}
			if(userCreation.logger.isDebugEnabled()){userCreation.logger.debug(rolePatch.toString());}	
			
			// Disconnecting from the Webdav server
			userCreation.webDavResource.close();
			userCreation.logger.info("Disconnected from "+userCreation.webDavResource.getPath());	
		}
		catch (URIException e) {userCreation.logger.info("UserCreation::main::"+e);}
		catch (NullPointerException e1) {userCreation.logger.info("UserCreation::main::"+e1);}
		catch (IOException e2){userCreation.logger.info("UserCreation::main::"+e2);}
		catch (DocumentException e3){userCreation.logger.info("UserCreation::main::"+e3);}
		
		userCreation.logger.info("Process finished...");
		
	}
}
