/*
 * Copyright (c) 1996, 1997, 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 "iiop-link.h"

#include <ctype.h>

CORBA_ReferenceData *CORBA_BOA_get_id(CORBA_BOA ths, FLICK_TARGET obj,
				      CORBA_Environment *ev)
{
	CORBA_ReferenceData *res;
	FLICK_TARGET dupe = CORBA_Object_duplicate(obj, ev);
	
	if (ev->_major != CORBA_NO_EXCEPTION)
		return 0;
	res = t_malloc(CORBA_ReferenceData, 1);
	if (!res) {
		free(dupe);
		flick_set_exception(ths, ev, ex_CORBA_NO_MEMORY,
				    0, CORBA_COMPLETED_NO);
		return 0;
	}
	*res = dupe->key;
	free(dupe);
	
	/* No need to set `ev'; `CORBA_Object_duplicate' did that for us. */
	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. */
FLICK_TARGET CORBA_BOA_create(CORBA_BOA ths, CORBA_ReferenceData *obj_key, 
			      const char *type_id, FLICK_SERVER impl,
			      CORBA_Environment *ev)
{
	FLICK_TARGET_STRUCT *new_refs;
	FLICK_TARGET_STRUCT *obj_ref;
	
	CORBA_octet *ref_key_buffer;
	char *ref_type_id;
	
	int type_id_len;
	
	if (ths->max_name_len < obj_key->_length)
		ths->max_name_len = obj_key->_length;
	
	/* Allocate the memory we need for the new object reference. */
	type_id_len = strlen(type_id);
	
	ref_key_buffer = t_malloc(CORBA_octet, obj_key->_length);
	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_refs = t_realloc(ths->refs,
			     FLICK_TARGET_STRUCT,
			     (ths->count_servers + 1));
	if (!new_refs) {
		free(ref_key_buffer);
		free(ref_type_id);
		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;
	}
	ths->refs = new_refs;
	ths->count_servers += 1;
	
	obj_ref = &(ths->refs[ths->count_servers - 1]);
	
	obj_ref->boa              = ths;	
	obj_ref->server_func = impl;
	memcpy(ref_key_buffer, obj_key->_buffer, obj_key->_length);
	obj_ref->key._buffer  = ref_key_buffer;
	obj_ref->key._length  = obj_key->_length;
	obj_ref->key._maximum = obj_key->_maximum;
	
	strncpy(ref_type_id, type_id, (type_id_len + 1));
	obj_ref->type_id     = ref_type_id;
	obj_ref->type_id_len = type_id_len;
	obj_ref->ior_len = 0;
	obj_ref->ior = 0;
	if (!flick_cdr_make_ior(obj_ref, &obj_ref->ior,
				&obj_ref->ior_len, 0)) {
		flick_set_exception(boa, ev, ex_CORBA_NO_MEMORY,
				    0, CORBA_COMPLETED_NO);
		fprintf(stderr,
			"Error: can't malloc memory for IOR.\n");
		return 0;
	}
	
	/* Finally, print the URL- and IOR-style stringified object refs. */
	{
		char *url, *ior;
		unsigned int i;
		
		url = CORBA_ORB_object_to_readable_string(ths->orb, obj_ref,
							  ev);
		if (ev->_major != CORBA_NO_EXCEPTION)
			return 0;
		ior = CORBA_ORB_object_to_string(ths->orb, obj_ref, ev);
		if (ev->_major != CORBA_NO_EXCEPTION)
			return 0;
		
		printf("Object `");
		for (i = 0; i < obj_key->_length; ++i) {
			if (isprint(obj_key->_buffer[i]))
				putchar(obj_key->_buffer[i]);
			else
				printf("\\%03o", obj_key->_buffer[i]);
		}
		printf("' is ready.\n");
		printf("  URL:\t%s\n  IOR:\t%s\n", url, ior);
		
		free(url);
		free(ior);
	}
	
	return CORBA_Object_duplicate(obj_ref, ev);
}

#define swap_long(val)				\
	((((val) & 0x000000FFU) << 24) |	\
	 (((val) & 0x0000FF00U) <<  8) |	\
	 (((val) & 0x00FF0000U) >>  8) |	\
	 (((val) & 0xFF000000U) >> 24))

/*
 * This is called from the server loop to (1) find the target object of a
 * message, (2) extract the `GIOP::RequestHeader.request_id' field of the
 * request, and (3) set the message stream pointer to point to the operation
 * key in the `GIOP::RequestHeader'.
 */
FLICK_TARGET find_implementation(CORBA_BOA ths,
				 FLICK_BUFFER *input_buffer,
				 /* OUT */ unsigned int *request_id,
				 CORBA_Environment *ev)
{
	char *cursor;
	
	char message_byte_order;
	char *object_key;
	
	unsigned int service_context_seq_len;
	unsigned int object_key_len;
	unsigned int service_context_len;
	unsigned int i;
	
	/*****/
	
	/* Pluck the byte order out of the GIOP MessageHeader structure. */
	message_byte_order = ((char *) input_buffer->buf_start)[6];
	
	if ((((char *) input_buffer->buf_start) + 4 + 2 + 1 + 1 + 4 + 4)
	    > ((char *) input_buffer->buf_end))
		goto error;
	/* Skip past the `GIOP::MessageHeader' structure. */
	cursor = ((char *) input_buffer->buf_start)
		 + 4 /* GIOP::MessageHeader.magic */
		 + 2 /* GIOP::MessageHeader.GIOP_version */
		 + 1 /* GIOP::MessageHeader.byte_order */
		 + 1 /* GIOP::MessageHeader.message_type */
		 + 4 /* GIOP::MessageHeader.message_size */
		 ;
	
	/* Parse the `GIOP::RequestHeader.service_context' list. */
	service_context_seq_len = *(unsigned int *) cursor;
	if (flick_is_little_endian != message_byte_order)
		service_context_seq_len = swap_long(service_context_seq_len);
	cursor += 4;
	
	for (i = 0; i < service_context_seq_len; ++i) {
		/* Skip the `IOP::ServiceContext.context_id'. */
		cursor += 4;
		
		if( (void *)(cursor + 4) > input_buffer->buf_end )
			goto error;
		/* Skip the `IOP::ServiceContext.context_data' sequence. */
		service_context_len = *(unsigned int *) cursor;
		if (flick_is_little_endian != message_byte_order)
			service_context_len = swap_long(service_context_len);
		cursor += 4;
		/*
		 * Note: It is always right to 4-byte align here, because the
		 * last service context will be followed by the unsigned int
		 * `GIOP::RequestHeader.request_id'.
		 */
		cursor += (service_context_len + 3) & ~3;
	}
	
	if( (void *)(cursor + 4 + 4 + 4) > input_buffer->buf_end )
		goto error;
	/* Save the `GIOP::RequestHeader.request_id'. */
	*request_id = *(unsigned int *) cursor;
	if (flick_is_little_endian != message_byte_order)
		*request_id = swap_long(*request_id);
	cursor += 4;
	
	/* Skip over the `GIOP::RequestHeader.response_expected'. */
	cursor += 4;
	
	/* Get the `GIOP::RequestHeader.object_key'. */
	object_key_len = *(unsigned int *) cursor;
	if (flick_is_little_endian != message_byte_order)
		object_key_len = swap_long(object_key_len);
	cursor += 4;
	
	object_key = cursor;
	cursor += (object_key_len + 3) & ~3;
	
	if( (void *)cursor > input_buffer->buf_end )
		goto error;
	/*
	 * Now `cursor' is pointing to the `GIOP::RequestHeader.operation', and
	 * we can initailize the message stream pointer for the server dispatch
	 * function.
	 */
	input_buffer->buf_current = cursor;
	
	/*
	 * Locate the referenced object and return an appropriate reference.
	 *
	 * XXX --- We do a linear search through the list of objects, and
	 * return a duplicate reference (XXX --- Why duplicate?) to the located
	 * object.  This is painfully inefficient, but hey, it's runtime,
	 * right? :-\
	 */
	for (i = 0; i < (unsigned) ths->count_servers; ++i) {
		if ((ths->refs[i].key._length == object_key_len)
		    && (memcmp(object_key,
			       ths->refs[i].key._buffer,
			       object_key_len)
			== 0)
			)
			/* We've found the object reference. */
			return CORBA_Object_duplicate(&ths->refs[i], ev);
	}
	
	flick_set_exception(ths, ev, ex_CORBA_NO_IMPLEMENT,
			    0, CORBA_COMPLETED_NO);
	return 0;
  error:
	flick_set_exception(ths, ev, ex_CORBA_COMM_FAILURE,
			    0, CORBA_COMPLETED_NO);
	return 0;
}

void CORBA_BOA_impl_is_ready(CORBA_BOA ths, CORBA_Environment *ev)
{
	int maxconn = -1;
	fd_set fds;
	struct sockaddr_in server;
	int sock, i;
	int len = sizeof(struct sockaddr_in);
	FLICK_BUFFER the_buf, return_buf;
	int *clients = 0;
	int client_cur = 0;
	int client_max = 0;
	
	/* allocate our own buffers.  This is in preparation for being
           thread-safe. */
	the_buf.real_buf_start = the_buf.buf_read = the_buf.buf_start =
	         the_buf.buf_current = t_calloc(char, 8192);
	the_buf.real_buf_end = the_buf.buf_end = (((char *) the_buf.buf_start)
						  + 8192);
	
	return_buf.real_buf_start = return_buf.real_buf_end =
	      return_buf.buf_read = 0;
	return_buf.buf_start  = return_buf.buf_current = t_calloc(char, 8192);
	return_buf.buf_end = ((char *) return_buf.buf_start) + 8192;
	
	if(!the_buf.buf_start || !return_buf.buf_start) {
		flick_set_exception(ths, ev, ex_CORBA_NO_MEMORY,
				    0, CORBA_COMPLETED_NO);
		perror("Insufficient memory for server buffers"); 
		return;
	}
	
	FD_ZERO(&fds);
	
	/* Get the listening socket & port for this machine */
	sock = ths->socket_fd;
	memset((char *)&server, 0, sizeof(server));
	server.sin_family = AF_INET;
	
	/* Bind a port for this skelecton function */
	server.sin_port = htons(ths->hostport);
	if (bind(sock, (struct sockaddr *)&server, len) != 0) {
		perror("cannot `bind' to socket");
		flick_set_exception(ths, ev, ex_CORBA_COMM_FAILURE,
				    0, CORBA_COMPLETED_NO);
		return;
	}
	
	if ((getsockname(sock, (struct sockaddr *)&server, &len) != 0)
	    || (listen(sock, 8) != 0)) {
		perror("cannot `getsockname' or `listen', port may be in use");
		flick_set_exception(ths, ev, ex_CORBA_COMM_FAILURE,
				    0, CORBA_COMPLETED_NO);
		return;
	}
	
	FD_SET(sock, &fds);
	
	if (sock > maxconn)
		maxconn = sock;
	
	/* Accept new clients, read client requests & send replies forever. */
	while (1) {
		FLICK_TARGET obj;
		unsigned int request_id;
		
		int qty = select(maxconn + 1, &fds, 0, 0, 0);
		
		for (i = 0; i < client_cur && qty; i++) {
			if ((clients[i] >= 0)
			    && FD_ISSET(clients[i], &fds)) {
				do {
					obj = 0;
					request_id = 0;
					
					if (!flick_server_get_request(
						clients[i],
						&the_buf)) {
						/*
						 * Error receiving the mesg.
						 * Client FD is closed by
						 * `flick_server_get_request'.
						 */
						clients[i] = -1;
					} else {
						obj = find_implementation(
							ths, &the_buf,
							&request_id, ev);
						if (ev->_major
						    != CORBA_NO_EXCEPTION) {
							/* Error finding
							   object. */
							flick_server_send_exception(
								clients[i],
								request_id,
								CORBA_exception_id(ev));
							ev->_major = CORBA_NO_EXCEPTION;
						} else {
							switch ((*obj->
								 server_func)(
									 &the_buf,
									 &return_buf,
									 request_id,
									 obj)) {
							case FLICK_OPERATION_SUCCESS:
								flick_server_send_reply(
									clients[i],
									&return_buf);
								break;
								
							case FLICK_OPERATION_SUCCESS_NOREPLY:
								break;
								
							default:
								/* FLICK_OPERATION_FAILURE */
								/*
								 * XXX --- The server
								 * dispatch function
								 * may have already
								 * encoded an exception
								 * `..._decode_error'.
								 * We should send it,
								 * or the dispatch func
								 * should return FLICK_
								 * OPERATION_SUCCESS in
								 * that case.
								 */
								flick_server_send_exception(
									clients[i],
									request_id,
									ex_CORBA_NO_IMPLEMENT);
								break;
							}
						}
					}
					if (!CORBA_Object_is_nil(obj, ev))
						CORBA_Object_release(obj, ev);
					
				} while (flick_buffer_contains_more(&the_buf));
				qty--;
			}
		}
		
		if (FD_ISSET(sock, &fds)) {
			int pos = -1;
			qty--;
			for (i = 0; i < client_cur; i++)
				if (clients[i] < 0)
					break;
			if (i < client_cur)
				pos = i;
			else {
				pos = client_cur++;
				if (client_cur > client_max) {
					client_max += 10;
					clients = t_realloc(clients, int, client_max);
					if (!clients) {			
						flick_set_exception(
							ths, ev,
							ex_CORBA_NO_MEMORY,
							0, CORBA_COMPLETED_NO);
						return;
					}
				}
			}
			clients[pos] = accept(sock, 0, 0);
			if (clients[pos] > maxconn)
				maxconn = clients[pos];
		}
		/* reset the file descriptor list */
		FD_ZERO(&fds);
		FD_SET(sock, &fds);
		for (i = 0; i < client_cur; i++)
			if (clients[i] >= 0)
				FD_SET(clients[i], &fds);
	}
	
	/* Should never be reached. */
	flick_set_exception(ths, ev, ex_CORBA_INTERNAL,
			    0, CORBA_COMPLETED_NO);
}

/* End of file. */



syntax highlighted by Code2HTML, v. 0.9.1