////////////////////////////////////////////////////////////
//
//     	Implementation of a class
//	DceServer
//
//      Copyright 2001 
//
////////////////////////////////////////////////////////////

#include <string.h>
#include <stdio.h>
#include <dce/dce_error.h>
#include "AWait.h"
#include "DceKey.h"
#include "DceServer.h"

static
void get_key_fn( void  *arg, unsigned_char_t *server_princ_name, 
        unsigned32  key_type,  unsigned32  key_ver,  void   **key, unsigned32  *status);


const error_status_t rpc_e_access = 10001;

	// DceServerAttributes

DceServerAttributes::DceServerAttributes():
max_exec_threads(rpc_c_listen_max_calls_default),
max_call_requests(rpc_c_protseq_max_reqs_default),
protocol_sequence((unsigned_char_t*)"ncacn_ip_tcp"),
entry_name_syntax(rpc_c_ns_syntax_default),
entry_name(0),
principal_name(0),
login_flags(sec_login_no_flags),
key_service(rpc_c_authn_dce_secret),
key_file(0),
key_file_flag(false),
key_version_number(sec_c_key_version_none), //latest
own_identity_flag(false),
annotation(0),
unexport_names_flag(false),
no_endpoint_replace_flag(false),
no_names_export_flag(false)
{
		
}

DceServerAttributes::DceServerAttributes(const DceServerAttributes& z):
max_exec_threads(z.max_exec_threads),
max_call_requests(z.max_call_requests),
protocol_sequence(z.protocol_sequence),
entry_name_syntax(rpc_c_ns_syntax_default),
entry_name(0),
principal_name(0),
login_flags(z.login_flags),
key_service(z.key_service),
key_file(0),
key_file_flag(z.key_file_flag),
key_version_number(z.key_version_number),
own_identity_flag(z.own_identity_flag),
id(z.id),
unexport_names_flag(z.unexport_names_flag),
no_endpoint_replace_flag(z.no_endpoint_replace_flag),
no_names_export_flag(z.no_names_export_flag)
{
	set_entry_name((char*)z.entry_name);
	set_principal_name((char*)z.entry_name);
	use_key_file(z.key_file);
	set_annotation((char*)z.annotation);
}

DceServerAttributes::~DceServerAttributes()
{
	delete []entry_name;
	delete []principal_name;
	delete []key_file;
	delete []annotation;
}
	
DceServerAttributes& DceServerAttributes::operator=(const DceServerAttributes& z)
{
	max_exec_threads=z.max_exec_threads;
	
	max_call_requests=z.max_call_requests;
	
	protocol_sequence=z.protocol_sequence;
	
	entry_name_syntax=rpc_c_ns_syntax_default;
	
	entry_name=0;
	
	principal_name=0;
	
	login_flags=z.login_flags;
	
	key_service=z.key_service;
	
	key_file=0;
	
	key_file_flag=z.key_file_flag;
	
	key_version_number=z.key_version_number;
		
	own_identity_flag=z.own_identity_flag;
	
	id=z.id;
	
	unexport_names_flag=z.unexport_names_flag;
	
	no_endpoint_replace_flag=z.no_endpoint_replace_flag;
	
	no_names_export_flag=z.no_names_export_flag;
	
	set_entry_name((char*)z.entry_name);
	
	set_principal_name((char*)z.principal_name);
	
	use_key_file(z.key_file);
	
	set_annotation((char*)z.annotation);

	return *this;
}


int DceServerAttributes::set_entry_name(const char *name)
{
	if(!name) return 1;
	
	delete []entry_name; entry_name=0;
	
	int size = strlen( name ) + 1 ;
	
	entry_name = new unsigned_char_t[ size ];
	
	memcpy(entry_name,name,size);
		
	return 0;
}

int DceServerAttributes::set_principal_name(const char *name)
{
	if(!name) return 1;
	
	delete []principal_name; principal_name=0;
	
	int size = strlen( name ) + 1 ;
	
	principal_name = new unsigned_char_t[ size ];
	
	memcpy(principal_name,name,size);
		
	return 0;
}

int DceServerAttributes::use_key_file(const char *name)
{
	key_file_flag=true;
	
	if(!name) return 0;
	
	delete []key_file; key_file=0;
	
	key_file = new char[strlen(name) + sizeof("FILE:") + 1];
	
	strcpy(key_file,"FILE:");
	
	strcat(key_file,name);
			
	return 0;
}

int DceServerAttributes::set_annotation(const char *name)
{
	if(!name) return 1;
	
	delete []annotation; annotation=0;
	
	int size = strlen( name ) + 1 ;
	
	annotation = new unsigned_char_t[ size ];
	
	memcpy(annotation,name,size);
		
	return 0;
}



	// KeyManagerThread
	
KeyManagerThread::KeyManagerThread(DceServerAttributes& att, DceKeyManager * k):
attributes(&att),
arg(0)
{
	if(k) arg=k;
	else if(att.key_file_flag)
		arg = att.key_file;

}



int KeyManagerThread::loop(bool& continue_loop)
{
	continue_loop=false;
	
	error_status_t s;
	
	sec_key_mgmt_manage_key
	(
	attributes->key_service,
	arg,
	attributes->principal_name,
	&s);
	
	return 0;
}




	// DceServer

DceServer::DceServer(DceServerAttributes& z, DceKeyManager * k):
status(0),
attributes(z),
key_manager(k),
login_context(0),
login_service(sec_login_auth_src_network),
key_manager_thread(z,k)
{
	error_status_t s=0;
	
	if(!attributes.entry_name)
	{	
		status=1;
		return;
	}
	
		
	if(attributes.id.is_nil())	
		linked_id.set_random();
	else
		linked_id=z.id;
	
	if(linked_id.status)
	{ 
		log("can't generate random id",s);
		status = 2;
		return;
	}
	
	rpc_server_use_protseq(
	attributes.protocol_sequence,
	attributes.max_call_requests,
	&s);
	
	if(s!=rpc_s_ok)
	{ 
		log("can't register protocol sequence",s);
		status = 3;
		return;
	}
	
	if(!attributes.associated_id.is_nil())
	{
	
		int g=0;
		error_status_t s=0;
		uuid_t type_id, server_id;
	
		g=attributes.associated_id.get_uuid_t(type_id);
		if(!g) g=linked_id.get_uuid_t(server_id);
	
	
		if(!g) rpc_object_set_type(&server_id,&type_id,&s);
		
		if(g || s != rpc_s_ok)
		{
			status=1;
			return;
		}
	
	}


   if(attributes.own_identity_flag)
   {
		
	if(!attributes.principal_name)
	{	
		status=1;
		return;
	}
	
	
	boolean32 r=sec_login_setup_identity(
	attributes.principal_name,
	attributes.login_flags,
	&login_context,
	&s);
	
	if(s!=error_status_ok || r!=TRUE)
	{ 
		log("can't set identity",s);
		status = 4;
		return;
	}
	
	DceKey key;
	boolean32 reset_password=FALSE;
			
	if(attributes.key_file_flag)
	sec_key_mgmt_get_key(
	attributes.key_service,
	attributes.key_file,
	attributes.principal_name,
	attributes.key_version_number,
	&key.data,
	&s);
	else if(key_manager)
	sec_key_mgmt_get_key(
	attributes.key_service,
	key_manager,
	attributes.principal_name,
	attributes.key_version_number,
	&key.data,
	&s);
	else
	{
		log("use a key file or a driver",rpc_e_access );
		status=5;
		return;
	}
	
	if(s!=error_status_ok)
	{
		log("can't get a key",s );
		status=6;
		return;	
	}
	
	login_service=sec_login_auth_src_network;
	
	r=sec_login_validate_identity(
	login_context,
	(sec_passwd_rec_t*)key.data,
	&reset_password,
	&login_service,
	&s);
	
	if(reset_password==TRUE)
		log("password expired",s);	
	
		
	if(s!=error_status_ok || r!=TRUE)
	{ 
		log("can't validate a key",s);
		status = 7;
		return;
	}
	
	r=sec_login_certify_identity(
	login_context,
	&s);
	
	if(s!=error_status_ok || r!=TRUE)
	{ 
		log("can't certify identity",s);
		status = 8;
		return;
	}
	
	
	int g=thread_control.launch(key_manager_thread);
	
   }

	
	// a cache can have some NULL handles
	for(int j=0;j<5;++j)
	{
		sec_login_get_current_context(&login_context,&s);
		
		if(s==error_status_ok && login_context!=0)
			break;
	}
	
	if(s!=error_status_ok )
	{ 
		log("can't get current login context",s);
		status = 9;
		return;
	}	
		
	// sec_login_release_context() ?
}

DceServer::~DceServer()
{
	int g=0;
	error_status_t s=0;
	node n;
	uuid_t server_id;
	uuid_vector_t object_uuid_vector;
	object_uuid_vector.count=1;
	
	key_manager_thread.selfdestruct();
	
	g=linked_id.get_uuid_t(server_id);
	
	if(g) return;
	
	while(!interfaces.pop(n))
	{	
		uuid_t type_id;
		DceCalleeInterface& z = n.interface;
		rpc_binding_vector_t *& binding_vector = n.binding_vector;
	
		if(! z.linked_id.is_nil())
		{
			g=z.linked_id.get_uuid_t(type_id);
		
			if(g) 
			{
				log("cleanup: can't get type id",rpc_s_ok);
				continue;
			}
		
		
			object_uuid_vector.uuid[0]=&type_id;
		}
		else
			object_uuid_vector.uuid[0]=&server_id;
		
		if(attributes.unexport_names_flag)
		{
	
			rpc_ns_binding_unexport(
			attributes.entry_name_syntax,
			attributes.entry_name,
			z.interface_handle,
			&object_uuid_vector,
			&s);
		
			if(s!=rpc_s_ok)
			{
				log("can't unexport",s);
			}
		
		}
		
		rpc_ep_unregister(
		z.interface_handle,
		binding_vector,
		&object_uuid_vector,
		&s);
		
		if(s!=rpc_s_ok)
		{ 
			log("can't unregister",s);
		}
		
		if(! z.linked_id.is_nil() )
		rpc_server_unregister_if(
		z.interface_handle,
		&type_id,
		&s);
		else
		rpc_server_unregister_if(
		z.interface_handle,
		0,
		&s);
		
		
		if(binding_vector)
		rpc_binding_vector_free(&binding_vector,&s);
		
		if(s!=rpc_s_ok)
		{ 
			log("can't free binding",s);
		}			
	
	}
	

	if(!status)
	sec_login_release_context(&login_context,&s);
	
	thread_control.terminate();
}

int DceServer::add_typeless(rpc_binding_vector_t *& binding_vector,const DceCalleeInterface& z)
{
	if(status) return 1;
	
	if(!z.interface_is_set()) return 2;
	
	int g=0;
	error_status_t s=0;
	uuid_t server_id;
	uuid_vector_t object_uuid_vector;
	
	g=linked_id.get_uuid_t(server_id);
		
	object_uuid_vector.count=1;
	object_uuid_vector.uuid[0]=&server_id;
	
	
	rpc_server_register_if(
	z.interface_handle,
	0,
	z.entry_point,
	&s);
	
	// allocates binding_vector
	if(s==rpc_s_ok)
	rpc_server_inq_bindings(&binding_vector,&s);
	
	if(s==rpc_s_ok)
	{
		if(attributes.no_endpoint_replace_flag)
		rpc_ep_register_no_replace(
		z.interface_handle,
		binding_vector,
		&object_uuid_vector,
		attributes.annotation,
		&s);
		else
		rpc_ep_register(
		z.interface_handle,
		binding_vector,
		&object_uuid_vector,
		attributes.annotation,
		&s);	
	}
	
	if(s==rpc_s_ok && !attributes.no_names_export_flag)
	rpc_ns_binding_export(
	attributes.entry_name_syntax,
	attributes.entry_name,
	z.interface_handle,
	binding_vector,
	&object_uuid_vector,
	&s);
	
		
	if(s!=rpc_s_ok)
	{ 
		log("can't register interface",s);
		return 3;
	}
	
	return 0;
}

int DceServer::add_typed(rpc_binding_vector_t *& binding_vector,const DceCalleeInterface& z)
{
	if(status) return 1;
	
	if(!z.interface_is_set()) return 2;
	
	if(!attributes.associated_id.is_nil()) return 3;
	
	int g=0;
	error_status_t s=0;
	uuid_t type_id, server_id;
	
	g=z.linked_id.get_uuid_t(type_id);
	if(!g) g=linked_id.get_uuid_t(server_id);
	
	if(g) return 4;
	
	rpc_object_set_type(&server_id,&type_id,&s);
		
	if(s==rpc_s_ok)
	rpc_server_register_if(
	z.interface_handle,
	&type_id,
	z.entry_point,
	&s);
	
	uuid_vector_t object_uuid_vector;
		
	object_uuid_vector.uuid[0]=&type_id;
	object_uuid_vector.count=1;
	
	// allocates binding_vector
	if(s==rpc_s_ok)
	rpc_server_inq_bindings(&binding_vector,&s);
	
	if(s==rpc_s_ok)
	{
		if(attributes.no_endpoint_replace_flag)
		rpc_ep_register_no_replace(
		z.interface_handle,
		binding_vector,
		&object_uuid_vector,
		attributes.annotation,
		&s);
		else
		rpc_ep_register(
		z.interface_handle,
		binding_vector,
		&object_uuid_vector,
		attributes.annotation,
		&s);	
	}
			
	if(s==rpc_s_ok && !attributes.no_names_export_flag)
	rpc_ns_binding_export(
	attributes.entry_name_syntax,
	attributes.entry_name,
	z.interface_handle,
	binding_vector,
	&object_uuid_vector,
	&s);
	
	if(s!=rpc_s_ok)
	{ 
		log("can't register interface",s);
		return 5;
	}
		
	return 0;
}

int DceServer::add_interface(const DceCalleeInterface& z)
{ 	
	int g=0;
	node n(z);

	if(z.linked_id.is_nil())
		g=add_typeless(n.binding_vector,n.interface);
	else
		g=add_typed(n.binding_vector,n.interface);
		
	if(!g)
		interfaces.push(n);
	
	return g;
}

int DceServer::add_authentication_service(unsigned authentication_service)
{
	if(status) return 1;
	
	error_status_t s=0;
	
	// key file is in format FILE:<absolute path> or
	// zero for a defaualt key file /krb/v5srvtab
	
	if(key_manager)
	rpc_server_register_auth_info(
	attributes.principal_name,
	(unsigned32)authentication_service,
	get_key_fn,
	this,
	&s);
	else if(attributes.key_file_flag)  
	rpc_server_register_auth_info(
	attributes.principal_name,
	(unsigned32)authentication_service,
	0,
	attributes.key_file, 		
	&s);	
	else 
	{
		log("use key file or a driver",rpc_e_access );
		return 2;
	}
	
	if(s!=rpc_s_ok)
	{ 
		log("can't set authentication service",s);
		return 3;
	}
	
	return 0;
}


int DceServer::launch()
{
	if(status) return 1;
	
	error_status_t s=0;
	
	rpc_server_listen(attributes.max_exec_threads,&s);
	
	if(s!=rpc_s_ok)
	{
		log("exited listen",s);
		return 2;
	}
	
	return 0;
}


int DceServer::stop()
{
	if(status) return 1;
	
	error_status_t s=0;
	
	rpc_mgmt_stop_server_listening(0,&s);
	
	if(s!=rpc_s_ok)
	return 1;
	
	return 0;
}	

int DceServer::log(const char *message, error_status_t ss)
{
	dce_error_string_t error_text;
	int s=0;
	
	dce_error_inq_text(ss,error_text,&s);
	
	if(!s)
	fprintf(stdout,"%s; dce call error: %s\n",message,(char*)error_text);
	else
	fprintf(stdout,"%s; dce call error: %d\n",message,(int)ss);
	
	
	return 0;
}

static
void get_key_fn( void  *arg, unsigned_char_t *server_princ_name, 
        unsigned32  key_type,  unsigned32  key_ver,  void   **key, unsigned32  *status)
{
	*status=rpc_s_ok;
	
	if(!arg)
	{
		*status=rpc_e_access;
		return;
	}
	
	DceKeyManager * z = (DceKeyManager *)arg;
	
	int g=z->get_key(
	key,
	(char*)server_princ_name,
	(unsigned)key_type,
	(unsigned)key_ver);
	
	if(g) *status=rpc_e_access;

}