/*
 * Copyright (c) 1998, 1999 The University of Utah and
 * the Computer Systems Laboratory at the University of Utah (CSL).
 * All rights reserved.
 *
 * Permission to use, copy, modify and distribute this software is hereby
 * granted provided that (1) source code retains these copyright, permission,
 * and disclaimer notices, and (2) redistributions including binaries
 * reproduce the notices in supporting documentation.
 *
 * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
 * IS" CONDITION.  THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
 * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * CSL requests users of this software to return to csl-dist@cs.utah.edu any
 * improvements that they make and grant CSL redistribution rights.
 */

#include "mach3mig-link.h"
#include "flick/encode/mach3mig.h"

CORBA_ReferenceData *CORBA_BOA_get_id(CORBA_BOA ths, CORBA_Object obj,
				      CORBA_Environment *ev)
{
	CORBA_ReferenceData *res;
	int i;
	char *name = 0;

	for (i = 0; i < ths->obj_count; i++) {
		if (ths->objs[i] == obj) {
			name = ths->names[i];
			break;
		}
	}
	
	res = t_malloc(CORBA_ReferenceData, 1);
	if (!res) {
		flick_set_exception(ths, ev, ex_CORBA_NO_MEMORY,
				    0, CORBA_COMPLETED_NO);
		return 0;
	}
	
	if (name) {
		res->_length = strlen(name);
		res->_maximum = res->_length;
		res->_buffer = t_malloc(CORBA_octet, res->_maximum);
		if (!res->_buffer) {
			free(res);
			flick_set_exception(ths, ev, ex_CORBA_NO_MEMORY,
					    0, CORBA_COMPLETED_NO);
			return 0;
		}
		memcpy(res->_buffer, name, res->_length);
	} else {
		res->_length = res->_maximum = 0;
		res->_buffer = 0;
	}
	
	CORBA_BOA_set_exception(ths, ev, CORBA_NO_EXCEPTION, 0, 0);
	
	return res;
}

/* used to be called CORBA_BOA_obj_is_ready, which was entirely wrong.
   see Section 8.2 of the CORBA 2.0 Spec.   
   This function will create an object of the given type and implementation. */
CORBA_Object CORBA_BOA_create(CORBA_BOA ths, CORBA_ReferenceData *obj_key, 
			      const char *type_id, FLICK_SERVER impl,
			      CORBA_Environment *ev)
{
	CORBA_Object *new_objs;
	char **new_names;
	char **new_types;
	FLICK_SERVER *new_impls;
	
	char *ref_key_buffer;
	char *ref_type_id;
	
	int type_id_len;
	
	mach_port_t new_right;
	
	/* Allocate the memory we need for the new object reference. */
	type_id_len = strlen(type_id);

	ref_key_buffer = t_malloc(char, obj_key->_length + 1);
	ref_type_id = t_malloc(char, (type_id_len + 1));
	
	if (!ref_key_buffer || !ref_type_id) {
		if (ref_key_buffer) free(ref_key_buffer);
		if (ref_type_id) free(ref_type_id);
		flick_set_exception(ths, ev, ex_CORBA_NO_MEMORY,
				    0, CORBA_COMPLETED_NO);
		fprintf(stderr,
			"Error: can't malloc memory for new object.\n");
		return 0;
	}
	
	/* Reallocate our BOA's vector of references. */
	new_objs = t_realloc(ths->objs,
			     CORBA_Object,
			     (ths->obj_count + 1));
	new_names = t_realloc(ths->names,
			      char *,
			      (ths->obj_count + 1));
	new_types = t_realloc(ths->types,
			      char *,
			      (ths->obj_count + 1));
	new_impls = t_realloc(ths->impls,
			      FLICK_SERVER,
			      (ths->obj_count + 1));
	
	if (!new_objs || !new_names || !new_types || !new_impls) {
		free(ref_key_buffer);
		free(ref_type_id);
		if (new_objs) free(new_objs);
		if (new_names) free(new_names);
		if (new_types) free(new_types);
		if (new_impls) free(new_impls);
		flick_set_exception(ths, ev, ex_CORBA_NO_MEMORY,
				    0, CORBA_COMPLETED_NO);
		fprintf(stderr,
			"Error: can't realloc memory for CORBA_BOA object "
			"references.\n");
		return 0;
	}
	
	/* allocate a port */
	if (mach_port_allocate(mach_task_self(),
			       MACH_PORT_RIGHT_RECEIVE,
			       &new_right)
	    != KERN_SUCCESS) {
		free(ref_key_buffer);
		free(ref_type_id);
		free(new_objs);
		free(new_names);
		free(new_types);
		flick_set_exception(ths, ev, ex_CORBA_INTERNAL,
				    0, CORBA_COMPLETED_NO);
		fprintf(stderr,
			"Error: can't allocate a receive port for object "
			"reference.\n");
		return 0;
	}
	
	memcpy(ref_key_buffer, obj_key->_buffer, obj_key->_length);
	ref_key_buffer[obj_key->_length] = 0; /* null-terminate */
	strncpy(ref_type_id, type_id, (type_id_len + 1));

	/* check this port into the nameserver */
	if (netname_check_in(ths->name_server_port,
			     ref_key_buffer,
			     mach_task_self(),
			     new_right)
	    != KERN_SUCCESS) {
		free(ref_key_buffer);
		free(ref_type_id);
		free(new_objs);
		free(new_names);
		free(new_types);
		flick_set_exception(ths, ev, ex_CORBA_INTERNAL,
				    0, CORBA_COMPLETED_NO);
		fprintf(stderr,
			"Error: can't check in object to nameserver.\n");
		return 0;
	}
	
	ths->objs = new_objs;
	ths->names = new_names;
	ths->types = new_types;
	ths->impls = new_impls;

	ths->impls[ths->obj_count] = impl;
	ths->types[ths->obj_count] = ref_type_id;
	ths->names[ths->obj_count] = ref_key_buffer;
	ths->objs[ths->obj_count] = new_right;
	ths->obj_count++;
	
	/* Finally, print the URL--style stringified object ref. */
	{
		char *url;
		
		url = CORBA_ORB_object_to_string(ths->orb, new_right, ev);
		if (ev->_major != CORBA_NO_EXCEPTION)
			return 0;
		
		printf("Object `%s' is ready.\n", ref_key_buffer);
		printf("  URL:\t%s\n", url);
		
		free(url);
	}
	
	CORBA_BOA_set_exception(ths, ev, CORBA_NO_EXCEPTION, 0, 0);
	
	return new_right;
}

void CORBA_BOA_impl_is_ready(CORBA_BOA ths, CORBA_Environment *ev)
{
	mach_port_t task_self = mach_task_self();
	mach_port_t portset;
	int i;
	
	kern_return_t res;
	char *buffer;
	mach_msg_header_t *request;
	mig_reply_header_t *reply;
	/*
	 * XXX - Here we specify the largest size message the server
	 * can receive.  If necessary, change the value of
	 * MAX_REPLY_BUFFER_SIZE in runtime/headers/flick/link/mach3mig.h.
	 */
	int bufsize = MAX_REPLY_BUFFER_SIZE;
	
	/* Allocate the send/receive buffer */
	buffer = t_malloc(char, bufsize);
	if (!buffer) {
		flick_set_exception(ths, ev, ex_CORBA_NO_MEMORY,
				    0, CORBA_COMPLETED_NO);
		return;
	}
	request = (mach_msg_header_t *) buffer;
	reply = (mig_reply_header_t *) buffer;
	
	/*
	 * Under normal conditions, we will never return.  If we ever
	 * do, it is because of an unrecoverable error.  Set the
	 * exception now, so handling the error is easy.
	 */
	flick_set_exception(ths, ev, ex_CORBA_INTERNAL,
			    0, CORBA_COMPLETED_NO);
	
	/* Create a port set for all the objects' ports. */
	if (mach_port_allocate(task_self,
			       MACH_PORT_RIGHT_PORT_SET,
			       &portset)
	    != KERN_SUCCESS)
		return;
	
	/* Advertise our server name so clients can connect. */
	for (i = 0; i < ths->obj_count; i++) {
		/* place the object's port into our port set */
		if (mach_port_move_member(task_self,
					  ths->objs[i],
					  portset)
		    != KERN_SUCCESS)
			return;
	}
	
	while (1) {
		res = mach_msg(request, MACH_RCV_MSG, 0, bufsize,
			       portset, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
		
		if (res != MACH_MSG_SUCCESS) {
			fprintf(stderr, "Unsuccessful msg receive: %s",
				mach_error_string(res));
			goto bail;
		}
		while (res == MACH_MSG_SUCCESS) {
			boolean_t ok;
			
			/* XXX - linear search through objects to find the
			   correct server function to call. */
			for (i = 0; i < ths->obj_count; i++) {
				if (ths->objs[i] == request->msgh_local_port) {
					ok = ths->impls[i](
						request,
						(mach_msg_header_t *) reply);
					break;
				}
			}
			
			/* check to make sure we found the object */
			if (i == ths->obj_count || !ok) {
				/* build an appropriate error */
				struct flick_stub_state _stub_state = { 0, 0 };
				mach_msg_header_t *OutHeadP
					= (mach_msg_header_t *) reply;
				mach_msg_header_t *InHeadP = request;
				void *_buf_current, *_buf_end;
				mig_reply_header_t *_buf_start;
				unsigned int exstrlen
					= sizeof(ex_CORBA_NO_IMPLEMENT);
				unsigned int len = * (unsigned int *)
						   &InHeadP[1];
				unsigned int msgsize = 24 + (exstrlen + 3) % 4;
				unsigned int i;
				
				flick_mach3mig_server_start_encode();
				flick_mach3mig_encode_new_glob(msgsize
							       + (len + 2) * 8,
							       _onerror1);
				/*
				 * Copy the operation ID from the request.
				 * We have to prepend '$' for the reply name.
				 */
				flick_mach3mig_encode_new_chunk((len + 2) * 8);
				flick_mach3mig_encode_unsigned32(
					0, len + 1, unsigned int);
				flick_mach3mig_encode_char8(
					8, '$', char);
				for (i = 0; i < len; i++)
					flick_mach3mig_encode_char8(
						i * 8 + 16,
						*((char *)&InHeadP[1] + 12),
						char);
				flick_mach3mig_encode_end_chunk((len + 2) * 8);
				flick_mach3mig_encode_new_chunk(msgsize);
				flick_mach3mig_encode_unsigned32(
					0,
					CORBA_SYSTEM_EXCEPTION,
					unsigned int);
				flick_mach3mig_array_encode_string_c_type(
					8, exstrlen, 1, 0, _type);
				flick_mach3mig_array_encode_bcopy(
					12,
					ex_CORBA_NO_IMPLEMENT,
					exstrlen);
				flick_mach3mig_encode_unsigned32(
					msgsize - 16, 0 /* minor code */,
					unsigned int); 
				flick_mach3mig_encode_unsigned32(
					msgsize - 8, CORBA_COMPLETED_NO,
					unsigned int);
				flick_mach3mig_encode_end_chunk(msgsize);
				flick_mach3mig_encode_end_glob(msgsize +
							       (len + 2) * 8);
			  _onerror1:
				flick_mach3mig_server_end_encode();
			}
			
			if (reply->Head.msgh_remote_port == MACH_PORT_NULL) {
				/* no reply port, just get another request */
				break;
			}
			
			if (ok && reply->RetCode == MIG_NO_REPLY) {
				/* deallocate reply port right */
				(void) mach_port_deallocate(
					task_self,
					reply->Head.msgh_remote_port);
				break;
			}
			
			/* Send reply to prev request and receive another: */
			
			res = mach_msg(&reply->Head,
				       MACH_SEND_MSG | MACH_RCV_MSG,
				       reply->Head.msgh_size,
				       bufsize,
				       portset,
				       MACH_MSG_TIMEOUT_NONE,
				       MACH_PORT_NULL);
			if (res != MACH_MSG_SUCCESS) {
				if (res == MACH_SEND_INVALID_DEST) {
				/* deallocate reply port right */
					(void) mach_port_deallocate(
						task_self,
						reply->Head.msgh_remote_port);
				} else {
					fprintf(stderr,
						"Unsuccessful msg "
						"send/receive: %s",
						mach_error_string(res));
					goto bail;
				}
			}

			/*
			 * XXX - check for reallocated buffer?
			 * The dispatch funcions should never realloc
			 * the buffer, so we shouldn't have to check.
			 */
		}
	}
	
  bail:
	/* Should never be reached. */
	/* but just in case, clean up everything... */
	mach_port_destroy(task_self,
			  portset);
}

/* End of file. */



syntax highlighted by Code2HTML, v. 0.9.1