// 	Copyright 05/10/99 Sun Microsystems, Inc. All Rights Reserved.

#pragma ident  "@(#)proxy_agent_finder_impl.cc	1.1 99/05/10 Sun Microsystems"

#include <crypt.h>

#include <cgw_utils/cgw_global.hh>
#include <cgw_utils/cgw_utils.hh>
#include <auth_helper/auth_server_handle.hh>
#include <em_c++utils/auto_ptr.hh>
#include <tspmi_scheduler/mis_login_wrapper.hh>

#include "osi_mgmt_ext_proxy_agent_impl.hh"
#include "proxy_agent_finder_impl.hh"
#include "rgw_debug.hh"
#include "rgw_utils.hh"

// ProxyAgentFinderImpl ctor activates an associated ProxyAgentController
// Object.
ProxyAgentFinderImpl::ProxyAgentFinderImpl(const char* finder_name) throw()
    : POA_JIDM::ProxyAgentFinder(),
      host_name_(CGWGlobal::mis_host_name.data())
{
    rgw_trace.print("JIDM::ProxyAgentFinder - created\n");

    key_.length(1);
    key_[0].id = CORBA::string_dup(CGWGlobal::JIDM_OSI_KEY_ID);
    key_[0].kind = CORBA::string_dup(CGWGlobal::JIDM_OSI_KEY_KIND);
    
    //proxy_agent_controller_ = new ProxyAgentControllerImpl(this);
    static ProxyAgentControllerImpl proxyagent_controller(this);

    //	CGWGlobal::boa_->obj_is_ready(proxyagent_controller);
    // Decide on the ID for the ProxyAgentController servant
    PortableServer::ObjectId_var ControllerId =
    PortableServer::string_to_ObjectId("ProxyAgentController");

    // Create myPOA with the right policies
    // Activate the ProxyAgentController servant with the ID on cgwPOA
    CGWGlobal::cgwPOA_->activate_object(&proxyagent_controller);
	
    proxy_agent_controller_ = proxyagent_controller._this();

    /*
    CORBA::Object_var reference =
    CGWGlobal::cgwPOA_->servant_to_reference(&proxyagent_controller);
    */

	rwlock_init(&proxy_agent_list_lock_,NULL,NULL);
    rgw_debug.print("JIDM::ProxyAgentController is ready\n");
}


// ProxyAgentFinderImpl destructor removes all enlisted proxy agents from list
ProxyAgentFinderImpl::~ProxyAgentFinderImpl() throw ()
{
    rgw_trace.print("ProxyAgentFinderImpl::~ProxyAgentFinderImpl() starts\n");

    //RWReadersWriterLock::WriteLockGuard write_lock(proxy_agent_list_lock_);
	rw_wrlock(&proxy_agent_list_lock_);
	proxy_agent_list_.clear();
	rw_unlock(&proxy_agent_list_lock_);

	rwlock_destroy(&proxy_agent_list_lock_);
    rgw_trace.print("ProxyAgentFinderImpl::~ProxyAgentFinderImpl() ends\n");
}


// access_domain method returns to the client, a proxy_agent object
// which matches the access criteria specified by the client if the client
// is authorized else return JIDM::ProxyAgent::_nil()
JIDM::ProxyAgent_ptr
ProxyAgentFinderImpl::access_domain(
    const JIDM::Key& key,
    const JIDM::Criteria& criteria
) throw (
    JIDM::InvalidKey,
    JIDM::CannotAccess,
    JIDM::InvalidCriteria,
    JIDM::CannotMeetCriteria,
    CORBA::SystemException
) {
    rgw_trace.print("ProxyAgentFinderImpl::access_domain() starts\n");

    // Validate passed parameters JIDM::Key and JIDM::Criteria
    validate_key_n_criteria(key, criteria);
    
    CORBA::ULong len = criteria.length();
    
    RWCString user_name;
    // Extract and Authenticate the user using em_login
    for (CORBA::ULong i = 0; i < len; ++i) {
        if(CGWUtils::equal(criteria[i].name,
                           CGWGlobal::JIDM_USER_PROFILE)) {
	    CORBA::Any user_profile(criteria[i].value);
	    authenticate_user_profile(user_profile, user_name);
	    break;
        }
    }

    // Search for a matching proxy agent object in all enlisted proxy agents
    JIDM::Criteria client_access_criteria(len);
    client_access_criteria.length(len);
    for (i = 0; i < len; ++i) {
	client_access_criteria[i] = criteria[i];
    }

	OSI_ProxyAgent_tie_ptr agent =
	find_matching_proxy_agent(client_access_criteria);

	if(agent != NULL ) {
		OSIMgmtExtProxyAgentImpl *tied_agent = agent->_tied_object() ;
		tied_agent->add_reference_();
		rgw_trace.print("ProxyAgentFinderImpl::access_domain() ends\n");
        JIDM::ProxyAgent_var temp_agent = JIDM::ProxyAgent::_duplicate((JIDM::ProxyAgent*)agent->_this());
        return temp_agent._retn() ; 
	} 
   
	 // Append a system ProxyAgentController object to the client supplied
    // access_criteria
    JIDM::Criteria new_criteria;
    new_criteria.length(len+1);
    for (i = 0; i < len; ++i) {
	new_criteria[i] = criteria[i];
    }

    new_criteria[i].name = CGWGlobal::JIDM_CONTROLLER_REFERENCE;
    new_criteria[i].value <<= 
	JIDM::ProxyAgentController::_duplicate(proxy_agent_controller_);
    OSIMgmtExtProxyAgentImpl *proxy_agent =
	new OSIMgmtExtProxyAgentImpl(new_criteria, user_name);


    //OSIMgmtExt_tie_ProxyAgent<OSIMgmtExtProxyAgentImpl>* tie_agent =
#ifdef ORBACUS
     POA_OSIMgmtExt::ProxyAgent_tie<OSIMgmtExtProxyAgentImpl>* tie_agent=
    	new POA_OSIMgmtExt::ProxyAgent_tie<OSIMgmtExtProxyAgentImpl>(
#else
     POA_OSIMgmtExt_ProxyAgent_tie<OSIMgmtExtProxyAgentImpl>* tie_agent =
    	new POA_OSIMgmtExt_ProxyAgent_tie<OSIMgmtExtProxyAgentImpl>(
#endif
    	    proxy_agent, (PortableServer::POA_ptr) NULL, 1);

    // Decide on the ID for the ProxyAgentController servant
    PortableServer::ObjectId_var serverId =
    PortableServer::string_to_ObjectId("ProxyAgent");


    // Activate the ProxyAgentController servant with the ID on cgwPOA

    CGWGlobal::cgwPOA_->activate_object( tie_agent);

    rgw_debug.print("OSIMgmtExt::ProxyAgent is ready\n");
    
    // Add new ProxyAgent to the existing proxy agents' list
    //RWReadersWriterLock::WriteLockGuard write_lock(proxy_agent_list_lock_);
	rw_wrlock(&proxy_agent_list_lock_);
    proxy_agent_list_.append(tie_agent);
	rw_unlock(&proxy_agent_list_lock_);
    rgw_trace.print("ProxyAgentFinderImpl::access_domain() ends\n");

#ifdef ORBIX
    return (tie_agent->_this() ) ; 
#else
    return (JIDM::ProxyAgent*) tie_agent->_this();
#endif

}


// remove_proxy_agent_from_list deletes the associated proxy_agent
// from internal list
bool
ProxyAgentFinderImpl::remove_proxy_agent_from_list(
    const JIDM::Criteria& client_access_criteria
) throw(
    CORBA::SystemException
) {
    rgw_trace.print(
	"ProxyAgentFinderImpl::remove_proxy_agent_from_list() starts\n");

	OSI_ProxyAgent_tie_ptr agent = find_matching_proxy_agent(client_access_criteria);
    
	bool result = true;
    if(agent != NULL ) {
	//RWReadersWriterLock::WriteLockGuard write_lock(proxy_agent_list_lock_);
	rw_wrlock(&proxy_agent_list_lock_);
	if(proxy_agent_list_.remove( agent )) {
		try{
			PortableServer::ObjectId_var  oid = 
            CGWGlobal::cgwPOA_->servant_to_id(agent);	
	    	CGWGlobal::cgwPOA_->deactivate_object(oid);
		}
		catch(CORBA::Exception  &e)
		{
			rgw_trace.print("ProxyAgentFinderImpl::remove_proxy_agent_from_list() -- Got Exception\n" ) ;
		}
 	    rgw_trace.print("ProxyAgentFinder is removed\n");
	}
	else {
    	rgw_error.print("Unable to remove proxy agent from list\n");
		result = false;
    }
	rw_unlock(&proxy_agent_list_lock_);
	}
    rgw_trace.print(
	"ProxyAgentFinderImpl::remove_proxy_agent_from_list() ends\n");
    return result;
}


// shutdown removes all proxy agents from the internal list and calls the
// destroyed method in their proxy_agent_controllers
void
ProxyAgentFinderImpl::shutdown() throw (
) {
    rgw_trace.print("ProxyAgentFinderImpl::shutdown() starts\n");

    RWTValSlistIterator<OSI_ProxyAgent_tie_ptr> itr(proxy_agent_list_);
    

    //RWReadersWriterLock::WriteLockGuard write_lock(proxy_agent_list_lock_);
	rw_wrlock(&proxy_agent_list_lock_);
    
    JIDM::Criteria_var agent_access_criteria;

    rgw_debug.print(
	"Registered proxy agent count: %d\n", proxy_agent_list_.entries()
    );
    
    for(;itr();) {
	OSI_ProxyAgent_tie_ptr agent = itr.key() ; 
	try {
	    if(agent->_non_existent()) {
		// Fatal Error
		rgw_error.print(
		    "Internal Error: Proxy Agent is non existent\n"
		);
		continue;
	    }
	    agent_access_criteria = agent->access_criteria();
	}
	catch (const CORBA::SystemException& se) {
	    CGWUtils::print_corba_exception(
		rgw_error,
		"Exception during Shutdown: ",
		se
	    );
	    continue;
	}
	
	for (int i = 0; i < agent_access_criteria->length(); i++) {
	    if (CGWUtils::equal(agent_access_criteria[i].name,
				CGWGlobal::JIDM_CONTROLLER_REFERENCE)
	    ) {
		try {
		    JIDM::ProxyAgentController_ptr pac;
		    if (!(agent_access_criteria[i].value >>= pac)) {
			rgw_error.print(
			    "Internal Error : Unable to extract Controller\n"
			);
			continue;
		    }
		    pac->destroyed(agent_access_criteria);
		}
		catch (const CORBA::SystemException& se) {
		    CGWUtils::print_corba_exception(
			rgw_error,
			"Exception during Shutdown: ",
			se
		    );
		}
	    }
	}
    }
	rw_unlock(&proxy_agent_list_lock_);
    rgw_trace.print("ProxyAgentFinderImpl::shutdown() ends\n");
}


// authenticate_user_profile connects to em_login daemon and validates
// user profile associated with the client 
void
ProxyAgentFinderImpl::authenticate_user_profile(
    const CORBA::Any& user_profile,
    RWCString& user_name
) throw (
    JIDM::CannotAccess,
    JIDM::InvalidCriteria,
    JIDM::CannotMeetCriteria,
    CORBA::SystemException
) {
    rgw_trace.print(
	"JIDM::ProxyAgentFinder - authenticating user profile in [%s MIS]\n",
	host_name_.data()
    );
    
    unsigned int status_code = PMI_SUCCESS;
    DataUnit error_string;

	MisLoginWrapper misloginclient(host_name_);

    AutoPtr<AuthenticationServer> as =
	new AuthenticationServerHandle();
    
    RWCString password;
    RWCString encrypted_password;
    bool crypted;
    DU salt;
    if(!as->decrypt_user_profile(user_profile, user_name, password,crypted)) {
	rgw_error.print("User is denied access to EM domain\n");
	throw JIDM::CannotAccess();
    }
    if (misloginclient.IsPasswordRequired(NULL))
    {
	   if (!crypted) {
		salt=misloginclient.GetPasswordSalt(user_name.data());
		if (!salt){
               		 rgw_error.print( "Unable to verify user password information : \n");
			throw JIDM::CannotMeetCriteria();
		}
	       encrypted_password = crypt(password, salt.chp());
           }	
    	   else
		encrypted_password =password;
    	if(!misloginclient.VerifyPassword( user_name.data(), encrypted_password.data()))
    	{
        	rgw_error.print(
	    	"Unable to verify user password information : \n");
        	throw JIDM::CannotMeetCriteria();
    	}

    }
    
     bool auth_enabled = misloginclient.IsAccessControlEnabled((char *) user_name.data());

    // If Authentication is disabled or Password is not required, do not
    // perform password authentication.
    if(!auth_enabled)
	return;
     else
	throw JIDM::CannotMeetCriteria();
}


// find_matching_proxy_agent returns the procy agent (if any) from the 
// internal list which matches the client's access criteria

OSI_ProxyAgent_tie_ptr 
ProxyAgentFinderImpl::find_matching_proxy_agent(
    const JIDM::Criteria& client_access_criteria
) throw (
    CORBA::SystemException
) {
    rgw_trace.print(
	"ProxyAgentFinderImpl::find_matching_proxy_agent() starts\n");
    RWTValSlistIterator<OSI_ProxyAgent_tie_ptr> itr(proxy_agent_list_);

    //RWReadersWriterLock::ReadLockGuard read_lock(proxy_agent_list_lock_);


	rw_rdlock(&proxy_agent_list_lock_);
    

    JIDM::Criteria_var agent_access_criteria;
    
    for(;itr();) {
	OSI_ProxyAgent_tie_ptr agent = itr.key();
	// Fatal Error
	if(agent->_non_existent()) {
	    rgw_error.print("Internal Error: Proxy Agent is non existent\n");
	    continue;
	}

	agent_access_criteria = agent->access_criteria();
	
	if(RGWUtils::jidm_criterias_match(
	    agent_access_criteria,
	    client_access_criteria
	)) {
	    rgw_debug.print(
		"Able to find the proxy agent in the list: \n"
	    );
	    rw_unlock(&proxy_agent_list_lock_);
		return agent ; 	
	}
    }
	rw_unlock(&proxy_agent_list_lock_);
    return NULL ;
}


// Validate_key_n_criteria validates the client supplied key and criteria
void
ProxyAgentFinderImpl::validate_key_n_criteria(
    const JIDM::Key& the_key,
    const JIDM::Criteria& the_criteria
) throw (
	JIDM::InvalidKey,
	JIDM::InvalidCriteria,
	CORBA::SystemException
) {
    rgw_trace.print(
	"JIDM::ProxyAgentFinder - validating key and access criteria\n"
    );
    
    if(!RGWUtils::jidm_keys_match(key_, the_key)) {
	throw JIDM::InvalidKey();
    }
    
    bool domain_title = false;
    bool user_profile = false;
    CORBA::ULong len = the_criteria.length();
    
    for (CORBA::ULong i = 0; i < len; ++i) {
	if(CGWUtils::equal(the_criteria[i].name,
			    CGWGlobal::JIDM_MANAGER_TITLE)
	   && !domain_title
	) {
	    domain_title = true;
	    continue;
	}
	
	if(CGWUtils::equal(the_criteria[i].name,
			    CGWGlobal::JIDM_CONTROLLER_REFERENCE)
	) {
	    continue;
	}

	if(CGWUtils::equal(the_criteria[i].name,
			    CGWGlobal::JIDM_USER_PROFILE)
	   && !user_profile
	) {
	    user_profile = true;
	    continue;
	}

	if (domain_title) {
	    rgw_error.print(" More than one domain titles received.\n");
	    throw  JIDM::InvalidCriteria();
	}
	else if (user_profile) {
	    rgw_error.print(" More than one user profiles received.\n");
	    throw  JIDM::InvalidCriteria();
	}
	else {
	    rgw_error.print(
		"Unexpected Criteria Entry: %s\n",
		(const char*)(the_criteria[i].name)
	    );
	    throw  JIDM::InvalidCriteria();
	}
    }

    if((!domain_title) || (!user_profile)) {
	if(!domain_title) {
	    rgw_error.print(" Mandatory field : No domain title received.\n");
	    throw  JIDM::InvalidCriteria();
	}
	if(!user_profile) {
	    rgw_error.print(" Mandatory field : No user profile received.\n");
	    throw  JIDM::InvalidCriteria();
	}
    }
    return;
}



/******************************************************************************/
ProxyAgentControllerImpl::ProxyAgentControllerImpl(
    ProxyAgentFinderImpl* paf
) throw() : proxy_agent_finder_impl_(paf)
{
    rgw_trace.print("JIDM::ProxyAgentController - created\n");
}


ProxyAgentControllerImpl::~ProxyAgentControllerImpl(
) throw() {
    rgw_trace.print("JIDM::ProxyAgentController - deleted\n");
}


// the_criteria contains criteria for proxy agent controller sent by
// the proxy agent
JIDM::Criteria*
ProxyAgentControllerImpl::destruction_is_allowed(
    const JIDM::Criteria& the_criteria
) throw (
    JIDM::InvalidCriteria,
    JIDM::CannotMeetCriteria,
    CORBA::SystemException
) {
    rgw_trace.print(
	"JIDM::ProxyAgentController - verfiying if destruction is allowed\n"
    );
    return new JIDM::Criteria(the_criteria);
}


// the_criteria contains JIDM::ProxyAgent_ptr to be destroyed
void
ProxyAgentControllerImpl::destroyed(
    const JIDM::Criteria& the_criteria
) throw(
    CORBA::SystemException
) {
    proxy_agent_finder_impl_->remove_proxy_agent_from_list(the_criteria);
}

