/*
 * Copyright (c) 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 <flick/encode/cdr.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>

/*****************************************************************************/

/*
 * This function converts an object into an IOR.  It is used by
 * `CORBA_ORB_object_to_string()' and `flick_cdr_encode_IOR_internal()'.
 * `buf' is a destination buffer which may be reallocated if necessary.
 * `buffer_len' is an inout parameter which is how much memory is
 * allocated for buf.
 * `start_pos' is an index into buf, which is where the ior will be placed.
 * The function returns the actual size of the ior in bytes or 0 for Error.
 */

const unsigned int null_obj_ior[] = {1, 0, 0};
const unsigned int null_obj_ior_len = 12; /* in bytes */

unsigned int flick_cdr_make_ior(/*  in   */ FLICK_TARGET obj,
				/* inout */ char **buf,
				/* inout */ unsigned int *buffer_len,
				/*  in   */ unsigned int start_pos)
{
	unsigned int obj_type_id_len = 0;
	unsigned int obj_key_len = 0;
	unsigned int host_name_len = 0;
	unsigned int profile_len = 0;
	unsigned int ior_len, pos, len;
	
	unsigned int endian_test = 1;
	char native_endianness = ((char *) &endian_test)[0];
	
	char *buffer;
	
	/* buffer_len and buf must be valid pointers */
	assert(buffer_len);
	assert(buf);
	
	buffer = *buf;
	
	if (!obj) {
		/* ensure there's enough space in the buffer */
		if (*buffer_len < (len = start_pos + null_obj_ior_len)) {
			buffer = t_realloc(buffer, char, len);
			if (!buffer)
				return 0; /* ERROR */
			*buf = buffer;
			*buffer_len = len;
		}
		
		/* place the null_obj_ior into the buffer */
		memcpy(buffer, (char *)null_obj_ior, null_obj_ior_len);

		/* done */
		return null_obj_ior_len;
	}
	
	/*
	 * Compute the lengths of the various strings that go into the
	 * IOR.
	 */
	obj_type_id_len = obj->type_id_len;
	obj_key_len = obj->key._length;
	host_name_len = strlen(obj->boa->hostname);
	
	/*
	 * Compute the number of bytes in the `IIOP::ProfileBody'.
	 * (Refer to Section 12.7.2 of the CORBA 2.0 specification.
	 * Also note that this same calculation is made in the runtime
	 * function `CORBA_ORB_object_to_string'.)
	 */
	profile_len = (  1 /* encapsulation (1 octet) */
			 + 2 /* `IIOP::Version' (2 chars) */
			 + 1 /* pad byte before host name length */
			 + 4 /* host name length */
			 + (host_name_len + 1) /* host name + NUL */
			 + ((host_name_len % 2) ? 0 : 1)
			 /* pad to get to 2-byte alignment */
			 + 2 /* port number */
			 + (((host_name_len % 4) >= 2) ? 2 : 0)
			 /* pad to get to 4-byte alignment */
			 + 4 /* object key length */
			 + obj_key_len /* object key len (incl. NUL) */
		);
	
	/*
	 * Compute the number of bytes in the entire IOR, including the
	 * preceding pad.
	 */
	ior_len = (4 /* `IOP::IOR.type_id' string length */
		   + (((obj_type_id_len + 1) + 3) & ~3)
		   /* `IOR.type_id' data + pad to 4-byte alignment */
		   + 4 /* `IOP::IOR.profiles' sequence length (1) */
		   + 4 /* `IOP::TaggedProfile.tag' */
		   + 4 /* `IOP::TaggedProfile.profile_data' seq len */
		   + profile_len /* `profile_data' sequence data*/
		);
	
	if (*buffer_len < (len = start_pos + ior_len)) {
		buffer = t_realloc(buffer, char, len);
		if (!buffer)
			return 0; /* ERROR */
		*buf = buffer;
		*buffer_len = len;
	}
	
	/* go to the correct location for storing the IOR */
	buffer = (char *)buffer + start_pos;
	
	/* Clear the buffer so we don't have to zero out pad bytes. */
	memset(buffer, 0, (ior_len + 3) & ~3);
	pos = 0;
	
	/* Encode the object `type_id'. */
	len = obj_type_id_len + 1;
	*(unsigned int *) &buffer[pos] = len;
	pos += 4;
	strncpy((&buffer[pos]), obj->type_id, len);
	pos += (len + 3) & ~3;
	
	/*
	 * Encode the number of TaggedProfiles the IOR TaggedProfile sequence
	 * (1 or 0).
	 */
	*(unsigned int *) &buffer[pos] = (!!obj);
	pos += 4;
	
	/* Encode the ProfileID and the length of the profile body. */
	*(unsigned int *) &buffer[pos] = IOP_TAG_INTERNET_IOP;
	pos += 4;
	*(unsigned int *) &buffer[pos] = profile_len;
	pos += 4;
	
	/* Enocde the encapsulation flag. */
	buffer[pos] = native_endianness;
	pos += 1;
	/* Encode the `IIOP::Version'. */
	buffer[pos] = 1;
	pos += 1;
	buffer[pos] = 0;
	pos += 1 + 1 /* pad byte */;
	
	/* Encode the host name. */
	*(unsigned int *) &buffer[pos] = host_name_len + 1;
	pos += 4;
	strncpy(&(buffer[pos]), obj->boa->hostname,
		(host_name_len + 1));
	pos += (host_name_len + 1 /* NUL */ + 1 /* opt pad byte */)
	       & ~1;
	
	/* Encode the port number. */
	*(unsigned short *) &buffer[pos] = obj->boa->hostport;
	pos = (pos + 2 /* port */ + 3 /* opt pad */) & ~3;
	
	/* Encode the object key. */
	*(unsigned int *) &buffer[pos] = obj_key_len;
	pos += 4;
	memcpy(&(buffer[pos]), obj->key._buffer, obj_key_len);
	pos += obj_key_len;
	
	assert(pos == ior_len);		
	
	return ior_len;
}


/*
 * `flick_cdr_encode_IOR_internal' is the runtime function that encodes an
 * object reference as a CORBA-style Interoperable Object Reference (IOR).
 * It takes a significant amount of computes to produce an IOR, so it's not
 * worthwhile to inline this code.
 */
int flick_cdr_encode_IOR_internal(
	flick_marshal_stream_t _stream,
	FLICK_TARGET obj,
	const char *link,
	int ref_adjust)
{
	unsigned int ior_prefix_pad, ior_len;
	
	/*
	 * We only know how to do this for IIOP runtime buffers; our stream
	 * code is adapted from `flick_iiop_encode_new_glob'.
	 */
	if (strcmp(link, "iiop"))
		assert(!"Can't marshal a CORBA IOR to a non-IIOP buffer.");
		
	/*
	 * Compute the number of pad bytes that must precede the IOR.
	 */
	if ((unsigned long) _stream->buf_current % 4)
		ior_prefix_pad = 4 - ((unsigned long)_stream->buf_current % 4);
	else
		ior_prefix_pad = 0;
	
	/* Encode the precomputed IOR */
	if (obj) {
		ior_len = ior_prefix_pad + obj->ior_len;
		flick_iiop_encode_new_glob(ior_len, error);
		flick_iiop_encode_new_chunk(ior_len);
		flick_cdr_encode_bcopy(ior_prefix_pad, obj->ior, obj->ior_len);
		flick_iiop_encode_end_chunk(ior_len);
		flick_iiop_encode_end_glob(ior_len);
	} else {
		flick_iiop_encode_new_glob(null_obj_ior_len, error);
		flick_iiop_encode_new_chunk(null_obj_ior_len);
		flick_cdr_encode_bcopy(ior_prefix_pad,
				       null_obj_ior, null_obj_ior_len);
		flick_iiop_encode_end_chunk(null_obj_ior_len);
		flick_iiop_encode_end_glob(null_obj_ior_len);
	}
	return 0;
  error:
	return 1;
}


/*****************************************************************************/

/* extracts the data from an IOR.  Returns the length of the data */
int flick_cdr_parse_IOR(char *buf, int cdr_swap /* 1 if swapping is active */,
	      char **out_OAaddr,
	      unsigned short *out_boa_port,
	      char **out_obj_type_id,
	      unsigned int *out_obj_type_id_len,
	      CORBA_octet **out_obj_key,
	      unsigned int *out_obj_key_len,
	      CORBA_Environment *ev) 
{
	/* buf does *not* include any `IOR:' prefix */
	
	/* We're using the CORBA 2.0 p10.17 stringified object. */
	
	unsigned int pos, len, res, iiop_profile_pos;
	int bail;
		
	unsigned int i;
	unsigned int endian_test = 1;
	char native_endianness = ((char *) &endian_test)[0];
	
	char encapsulation_endianness;
	unsigned int profile_count;
	
	/* No object --> no BOA --> no `CORBA_BOA_set_exception'. */
	ev->_major = CORBA_NO_EXCEPTION;
	
	/*
	 * `buf' now holds a CORBA encapsulation of an IOR as described
	 * in Section 10.6.2 of the CORBA 2.0 specification.
	 *
	 * `pos' is our index into `buf'.
	 */
	pos = 0;
	bail = 0;	
	
	/*
	 * First, get the object `type_id', a string.
	 */
	if (!cdr_swap)
		len = (*(unsigned int *) &buf[pos]);
	else
		len = swap_long(*(unsigned int *) &buf[pos]);
	pos += 4;
	
	if (len) {
		*out_obj_type_id_len = len - 1;
		*out_obj_type_id = t_malloc(char, len);
		if (!*out_obj_type_id) {
			flick_set_exception(0 /*boa*/, ev, ex_CORBA_NO_MEMORY,
					    0, CORBA_COMPLETED_NO);
			fprintf(stderr,
				"Error: can't malloc memory for object "
				"type_id.\n");
			bail = 1;
		} else {		
			strncpy(*out_obj_type_id, &(buf[pos]), len);
			if ((*out_obj_type_id)[len - 1] != 0) {
				/* XXX --- This should never happen. */
				fprintf(stderr,
					"Warning: IOR object type_id was not "
					"NUL terminated.  Terminating it.\n");
				(*out_obj_type_id)[len - 1] = 0;
			}
		}	
		pos += (len + 3) & ~3;
	} else {		
		*out_obj_type_id_len = 0;
		*out_obj_type_id = 0;
	}
	
	/*
	 * Now, find the IOP Profile within our IOR's sequence of
	 * TaggedProfiles.
	 */
	if (!cdr_swap)
		profile_count = (*(unsigned int *) &buf[pos]);
	else
		profile_count = swap_long(*(unsigned int *) &buf[pos]);
	pos += 4;
	if (profile_count == 0) {
		/*
		 * No profiles?  Then this is a null object reference
		 * (Section 10.6.2).
		 */
		if (*out_obj_type_id) free(*out_obj_type_id);
		
		/* nullify the data pointers */
		*out_OAaddr = *out_obj_type_id = 0;
		*out_obj_key = 0;
		return pos; /* return the total size of the ior */
	}
	
	iiop_profile_pos = 0;
	for (i = 0; i < profile_count; ++i) {
		IOP_ProfileId tag;
		
		/* Align to read next 4-byte tag. */
		pos = (pos + 3) & ~3;
		
		if (!cdr_swap) {
			tag = (*(unsigned int *) &buf[pos]);
			pos += 4;
			len = (*(unsigned int *) &buf[pos]);
			pos += 4;
		} else {
			tag = swap_long(*(unsigned int *) &buf[pos]);
			pos += 4;
			len = swap_long(*(unsigned int *) &buf[pos]);
			pos += 4;
		}
		
		if (tag == IOP_TAG_INTERNET_IOP) {
			/*
			 * Is this an IIOP 1.0 Profile?  Note that `buf[pos]'
			 * currently points to an encapsulation header
			 * (a char).
			 */
			if ((buf[pos + 1] == 1) && buf[pos + 2] == 0)
				/*
				 * Don't break yet --- just remember this `pos'
				 * and keep scanning until we reach the end of
				 * the IOR.
				 */
				iiop_profile_pos = pos;
		}
		/* Skip over this profile. */
		pos += len;
	}
	/*
	 * At this point, `pos' points to the byte after the IOR.  We will
	 * return this value if we bail out so that if we are decoding from
	 * a stream, we can adjust the stream pointer as if we decoded
	 * the complete IOR.)
	 */
	res = pos;
		
	if (bail)
		/*
		 * We're bailing because we couldn't allocate memory for the
		 * `obj_type_id' string.  We waited until here to bail out so
		 * that the stream pointer would point past the IOR.
		 */
		return res;
	
	if (!iiop_profile_pos) {
		/* XXX --- Is `BAD_PARAM' the right exception? */
		flick_set_exception(0 /*boa*/, ev, ex_CORBA_BAD_PARAM,
				    0, CORBA_COMPLETED_NO);
		fprintf(stderr,
			"Warning: no IIOP Profile in IOR.  Returning a null "
			"object reference.\n");
		if (*out_obj_type_id) free(*out_obj_type_id);
		return res;
	}
	pos = iiop_profile_pos;
	
	/*
	 * At this point, `pos' is the index of the IIOP Profile
	 * encapsulation in `buf'.  `pos' is also 4-byte aligned, since
	 * we just read past the (unsigned int) ProfileId.
	 *
	 * By adding 4 to `pos' here, we skip over the endianness
	 * indicator (1 octet) for the IIOP Profile encapsulation, the
	 * `IIOP::Version' struct (2 chars), and the pad byte preceding
	 * the `host' string length.
	 */
	encapsulation_endianness = buf[pos];
	pos += 4;
	
	/* Get the host name. */
	if (encapsulation_endianness == native_endianness)
		len = (*(unsigned int *) &buf[pos]);
	else
		len = swap_long(*(unsigned int *) &buf[pos]);
	pos += 4;
	
	*out_OAaddr = t_malloc(char, len);
	if (!*out_OAaddr) {
		flick_set_exception(0 /*boa*/, ev, ex_CORBA_NO_MEMORY,
				    0, CORBA_COMPLETED_NO);
		fprintf(stderr,
			"Error: can't malloc memory for host name.\n");
		if (*out_obj_type_id) free(*out_obj_type_id);
		return res;
	}
	strncpy(*out_OAaddr, &(buf[pos]), len);
		
	if ((*out_OAaddr)[len - 1] != 0) {
		/* XXX --- This should never happen. */
		fprintf(stderr,
			"Warning: IIOP host name was not NUL "
			"terminated.  Terminating it.\n");
		(*out_OAaddr)[len - 1] = 0;
	}
	pos += (len + 1) & ~1;
	
	/* Get the BOA port. */
	if (encapsulation_endianness == native_endianness)
		*out_boa_port = (*(unsigned short *) &buf[pos]);
	else
		*out_boa_port = swap_short(*(unsigned short *) &buf[pos]);
	pos += 2;
	
	/* Get the object key. */
	/* Align for object key length. */
	pos = (pos + 3) & ~3;
	if (encapsulation_endianness == native_endianness)
		*out_obj_key_len = (*(unsigned int *) &buf[pos]);
	else
		*out_obj_key_len = swap_long(*(unsigned int *) &buf[pos]);
	pos += 4;
	
	*out_obj_key = t_malloc(CORBA_octet, *out_obj_key_len);
	if (!*out_obj_key) {
		flick_set_exception(0 /*boa*/, ev, ex_CORBA_NO_MEMORY,
				    0, CORBA_COMPLETED_NO);
		fprintf(stderr,
			"Error: can't malloc memory for object "
			"key.\n");
		if (*out_obj_type_id) free(*out_obj_type_id);
		if (*out_OAaddr) free(*out_OAaddr);
		return res;
	}
	memcpy(*out_obj_key, &(buf[pos]), *out_obj_key_len);
	/*
	 * Note that the object key is a sequence of octets, *not* a
	 * NUL-terminated string.  So do not check for termination.
	 */
	
	return res;
}

/*
 * `flick_cdr_decode_IOR_internal' is the runtime function that decodes an
 * Interoperable Object Reference (IOR) from a stream.  It takes a significant
 * amount of work to decode an IOR, so it's not wothwhile to inline this code.
 */
FLICK_TARGET flick_cdr_decode_IOR_internal(
	flick_marshal_stream_t _stream,
	int cdr_swap,
	const char *link,
	int ref_adjust)
{
	char *OAaddr;
	unsigned short boa_port;
	char *obj_type_id;
	CORBA_octet *obj_key;
	unsigned int obj_type_id_len, obj_key_len;
	
	CORBA_Environment _ev;
	FLICK_TARGET res;
	
	unsigned int ior_prefix_pad, ior_size;
	
	/*
	 * Make sure that we're unmarshaling from an IIOP runtime stream, since
	 * that's the only kind we know how to manipulate after decoding the
	 * IOR.
	 */
	if (strcmp(link, "iiop"))
		assert(!"Can't unmarshal a CORBA IOR from a non-IIOP buffer.");
	
	/*
	 * Compute the number of pad bytes that must precede the IOR.
	 */
	if ((unsigned long) _stream->buf_current % 4)
		ior_prefix_pad = 4 - ((unsigned long)_stream->buf_current % 4);
	else
		ior_prefix_pad = 0;
	
	/*
	 * We are ready to decode the IOR from `stream'.
	 */
	ior_size = flick_cdr_parse_IOR(((char *) _stream->buf_current)
				       + ior_prefix_pad,  
				       cdr_swap, &OAaddr,
				       &boa_port, &obj_type_id,
				       &obj_type_id_len,
				       &obj_key, &obj_key_len, &_ev);
	/*
	 * Update our stream pointer before we try to parse the IIOP 1.0
	 * Profile.  (This way, if we bail out, our stream still shows that
	 * we decoded the complete IOR.)
	 */
	ior_size += ior_prefix_pad;
	flick_iiop_check_span(ior_size, error);
	flick_iiop_decode_new_glob(ior_size);
	flick_iiop_decode_new_chunk(ior_size);
	flick_iiop_decode_end_chunk(ior_size);
	flick_iiop_decode_end_glob(ior_size);
	
	if (_ev._major != CORBA_NO_EXCEPTION) 
		return 0;
	
	res = flick_create_object(0 /*use default ORBid*/,
				  0 /*use default OAid*/,
				  OAaddr, boa_port, obj_type_id,
				  obj_type_id_len,
				  obj_key, obj_key_len, &_ev);
        /* was copied by CORBA_ORB_BOA_init_internal */
	if (OAaddr) free(OAaddr); 
	if (_ev._major != CORBA_NO_EXCEPTION) {
		if (obj_type_id) free(obj_type_id);
		if (obj_key) free(obj_key);
		return 0;
	}
		
        /* return the object */
	return res;

  error:
	return 0;
}


/*****************************************************************************/

/* End of file. */



syntax highlighted by Code2HTML, v. 0.9.1