/*
 * bonobo-moniker-util.c
 *
 * Authors:
 *	Michael Meeks    (michael@helixcode.com)
 *	Ettore Perazzoli (ettore@helixcode.com)
 *
 * Copyright (C) 2000 Ximian, Inc.
 */
#include "config.h"
#include <string.h>

#include <glib/gi18n-lib.h>

#include <ORBitservices/CosNaming.h>
#include <bonobo/bonobo-object.h>
#include <bonobo/bonobo-exception.h>
#include <bonobo/bonobo-moniker-util.h>

static struct {
	char *prefix;
	char *oafiid;
} const fast_prefix [] = {
	{ "!",       "OAFIID:Bonobo_Moniker_Item"  },
	{ "OAFIID:", "OAFIID:Bonobo_Moniker_Oaf"   },
	{ "OAFAID:", "OAFIID:Bonobo_Moniker_Oaf"   },
	{ "cache:",  "OAFIID:Bonobo_Moniker_Cache" },
	{ "query:(", "OAFIID:Bonobo_Moniker_Query" },
	{ "new:",    "OAFIID:Bonobo_Moniker_New"   },
	{ "conf:",   "OAFIID:GNOME_Moniker_Config" },
	{ NULL, NULL }
};

/**
 * bonobo_moniker_util_parse_name:
 * @name: a moniker name
 * @plen: an optional pointer to store the parent length
 * 
 *  This routine finds the rightmost moniker name. For example
 * it will return "cache:" if you pass in "file:/tmp.txt#cache:". It will 
 * also store the length of the parent string in @plen (13 for the above 
 * example)
 * 
 * Return value: the name of the rightmost moniker
 **/
const char *
bonobo_moniker_util_parse_name (const char *name, int *plen)
{
	int i, c, l;
	const char *rval;

	g_return_val_if_fail (name != NULL, NULL);

	l = strlen (name);

	for (i = l - 1; i >= 0; i--) {

		if (name [i] == '!' || name [i] == '#') {

			if (name [i] == '!')
				rval = &name [i];
			else
				rval = &name [i + 1];

			if (!i || (name [i-1] == '!' || name [i-1] == '#')) {
				if (plen)
					*plen = i;
				return rval;
			}

			if (i)
				--i;

			c = 0;
			while (i && name [i] == '\\') {
				c++;
				i--;
			}
			
			if (plen)
				*plen = i + c + 1;

			if (!(c % 2)) 
				return rval;
		}
	}

	if (plen)
		*plen = 0;

	return name;
}

static char *
moniker_id_from_nickname (const CORBA_char *name)
{
	int i;

	for (i = 0; fast_prefix [i].prefix; i++) {
		int len = strlen (fast_prefix [i].prefix);

		if (!g_ascii_strncasecmp (fast_prefix [i].prefix,
					  name, len)) {

			return fast_prefix [i].oafiid;
		}
	}

	return NULL;
}

/*
 * get_full_interface_name:
 * @ifname: original name: can be in form Bonobo/Control
 *
 * Return value: full name eg. IDL:Bonobo/Control:1.0
 */
static gchar *
get_full_interface_name (const char *ifname)
{
	int len, had_ver;
	const char *a;
	char *retval, *b;

	g_return_val_if_fail (ifname != NULL, NULL);

	len = strlen (ifname);
	retval = g_new (char, len + 4 + 4 + 1);

	strcpy (retval, "IDL:");
	a = ifname;
	b = retval + 4;

	if (ifname [0] == 'I' &&
	    ifname [1] == 'D' &&
	    ifname [2] == 'L' &&
	    ifname [3] == ':')
		a += 4;

	for (had_ver = 0; (*b = *a); a++, b++) {
		if (*a == ':')
			had_ver = 1;
	}

	if (!had_ver)
		strcpy (b, ":1.0");

	return retval;
}

static gchar *
query_from_name (const char *name)
{
	char *prefix, *query;
	int   len;

	for (len = 0; name [len]; len++) {
		if (name [len] == ':') {
			len++;
			break;
		}
	}


	prefix = g_strndup (name, len);
		
	query = g_strdup_printf (
		"repo_ids.has ('IDL:Bonobo/Moniker:1.0') AND "
		"bonobo:moniker.has ('%s')", prefix);
	g_free (prefix);

	return query;
}

/**
 * bonobo_moniker_util_get_parent_name:
 * @moniker: the moniker
 * @opt_ev: an optional corba exception environment
 * 
 *  This gets the name of the parent moniker ( recursively
 * all of the parents of this moniker ).
 * 
 * Return value: the name; use CORBA_free to release it.
 **/
CORBA_char *
bonobo_moniker_util_get_parent_name (Bonobo_Moniker     moniker,
				     CORBA_Environment *opt_ev)
{
	CORBA_Environment ev, *my_ev;
	Bonobo_Moniker parent;
	CORBA_char    *name;

	bonobo_return_val_if_fail (moniker != CORBA_OBJECT_NIL, NULL, opt_ev);

	if (!opt_ev) {
		CORBA_exception_init (&ev);
		my_ev = &ev;
	} else
		my_ev = opt_ev;

	parent = Bonobo_Moniker_getParent (moniker, my_ev);

	if (BONOBO_EX (my_ev) ||
	    parent == CORBA_OBJECT_NIL) {
		if (!opt_ev)
			CORBA_exception_free (&ev);
		return NULL;
	}

	name = Bonobo_Moniker_getName (parent, my_ev);

	if (BONOBO_EX (my_ev))
		name = NULL;

	bonobo_object_release_unref (parent, NULL);

	if (!opt_ev)
		CORBA_exception_free (&ev);
	
	return name;
}

/**
 * bonobo_moniker_util_qi_return:
 * @object: the unknown to query
 * @requested_interface: the desired interface
 * @ev: a corba exception environment 
 * 
 *  A helper function to share code from the end of a resolve
 * implementation; this ensures that the returned object is of
 * the correct interface by doing a queryInterface on the object.
 * 
 * Return value: an handle to the requested interface
 **/
Bonobo_Unknown
bonobo_moniker_util_qi_return (Bonobo_Unknown     object,
			       const CORBA_char  *requested_interface,
			       CORBA_Environment *ev)
{
	Bonobo_Unknown retval = CORBA_OBJECT_NIL;

	if (BONOBO_EX (ev))
		return CORBA_OBJECT_NIL;
	
	if (object == CORBA_OBJECT_NIL) {
		bonobo_exception_general_error_set (
			ev, NULL, _("Failed to activate object"));
		return CORBA_OBJECT_NIL;
	}

	retval = Bonobo_Unknown_queryInterface (
		object, requested_interface, ev);

	if (BONOBO_EX (ev)) {
		retval = CORBA_OBJECT_NIL;
		goto release_unref_object;
	}
	
	if (retval == CORBA_OBJECT_NIL) {
		CORBA_exception_set (ev, CORBA_USER_EXCEPTION,
				     ex_Bonobo_Moniker_InterfaceNotFound, NULL);
		goto release_unref_object;
	}

 release_unref_object:	
	bonobo_object_release_unref (object, ev);

	return retval;
}

/**
 * bonobo_moniker_util_seek_std_separator:
 * @str: the string to scan
 * @min_idx: the minimum offset at which a separator can be found.
 * 
 *  This looks for a moniker separator in a moniker's name string.
 *
 *  See also bonobo_moniker_util_escape
 * 
 * Return value: the position of the separator, or a
 * pointer to the end of the string.
 **/
int
bonobo_moniker_util_seek_std_separator (const CORBA_char *str,
					int               min_idx)
{
	int i;

	g_return_val_if_fail (str != NULL, 0);
	g_return_val_if_fail (min_idx >= 0, 0);

	for (i = 0; i < min_idx; i++) {
		if (!str [i]) {
			g_warning ("Serious separator error, seeking in '%s' < %d",
				   str, min_idx);
			return i;
		}
	}

	for (; str [i]; i++) {

		if (str [i] == '\\' && str [i + 1])
			i++;
		else if (str [i] == '!' ||
			 str [i] == '#')
			break;
	}
	
	return i;
}

/**
 * bonobo_moniker_util_escape:
 * @string: an unescaped string
 * @offset: an offset of characters to ignore
 * 
 *  Escapes possible separator characters inside a moniker
 * these include '!' and '#', the '\' escaping character is
 * used.
 * 
 * Return value: an escaped sub-string.
 **/
char *
bonobo_moniker_util_escape (const char *string, int offset)
{
	gchar *escaped, *p;
	guint  backslashes = 0;
	int    i, len;

	g_return_val_if_fail (string != NULL, NULL);

	len = strlen (string);
	g_return_val_if_fail (offset < len, NULL);

	for (i = offset; i < len; i++) {
		if (string [i] == '\0')
			break;
		else if (string [i] == '\\' ||
			 string [i] == '#'  ||
			 string [i] == '!')
			backslashes ++;
	}
	
	if (!backslashes)
		return g_strdup (&string [offset]);

	p = escaped = g_new (gchar, len - offset + backslashes + 1);

	for (i = offset; i < len; i++) {
		if (string [i] == '\\' ||
		    string [i] == '#'  ||
		    string [i] == '!')
			*p++ = '\\';
		*p++ = string [i];
	}
	*p = '\0';

	return escaped;
}

/**
 * bonobo_moniker_util_unescape:
 * @string: a string
 * @num_chars: the number of chars to process.
 * 
 *  This routine strips @num_chars: from the start of
 * @string, discards the rest, and proceeds to un-escape
 * characters escaped with '\'.
 * 
 * Return value: the unescaped sub string.
 **/
char *
bonobo_moniker_util_unescape (const char *string, int num_chars)
{
	gchar *escaped, *p;
	guint  backslashes = 0;
	int    i;

	g_return_val_if_fail (string != NULL, NULL);

	for (i = 0; i < num_chars; i++) {
		if (string [i] == '\0')
			break;
		else if (string [i] == '\\') {
			if (string [i + 1] == '\\')
				i++;
			backslashes ++;
		}
	}

	if (!backslashes)
		return g_strndup (string, num_chars);

	p = escaped = g_new (gchar, strlen (string) - backslashes + 1);

	for (i = 0; i < num_chars; i++) {
		if (string [i] == '\\') {
			if (!string [++i])
				break;
			*p++ = string [i];
		} else
			*p++ = string [i];
	}
	*p = '\0';

	return escaped;
}

/**
 * bonobo_moniker_client_new_from_name:
 * @name: the name of a moniker
 * @opt_ev: an optional corba exception environment 
 * 
 *  This routine tries to parse a Moniker in string form
 *
 * eg. file:/tmp/a.tar.gz#gzip:#tar:
 *
 * into a CORBA_Object representation of this that can
 * later be resolved against an interface.
 * 
 * Return value: a new Moniker handle
 **/
Bonobo_Moniker
bonobo_moniker_client_new_from_name (const CORBA_char  *name,
				     CORBA_Environment *opt_ev)
{
	CORBA_Environment ev, *my_ev;
	const char *mname;
	const char *iid;
	Bonobo_Unknown object;
	Bonobo_Moniker moniker;

	bonobo_return_val_if_fail (name != NULL || name [0], CORBA_OBJECT_NIL,
				   opt_ev);

	if (!opt_ev) {
		CORBA_exception_init (&ev);
		my_ev = &ev;
	} else
		my_ev = opt_ev;

	mname = bonobo_moniker_util_parse_name (name, NULL);

	if (!(iid = moniker_id_from_nickname (mname))) {
		char *query;

		query = query_from_name (mname);

		object = bonobo_activation_activate (query, NULL, 0, NULL, my_ev);

		g_free (query);
		
		if (BONOBO_EX (my_ev)) {
			if (!opt_ev)
				CORBA_exception_free (&ev);
			return CORBA_OBJECT_NIL;
		}

		if (object == CORBA_OBJECT_NIL) {
			bonobo_exception_set (opt_ev, 
					      ex_Bonobo_Moniker_UnknownPrefix);
			if (!opt_ev)
				CORBA_exception_free (&ev);

			return CORBA_OBJECT_NIL;
		}
	} else {
		object = bonobo_activation_activate_from_id ((gchar *) iid, 0, NULL, my_ev);

		if (BONOBO_EX (my_ev)) {
			if (!opt_ev)
				CORBA_exception_free (&ev);

			return CORBA_OBJECT_NIL;
		}

		if (object == CORBA_OBJECT_NIL) {
			g_warning ("Activating '%s' returned nothing", iid);
			if (!opt_ev)
				CORBA_exception_free (&ev);
			return CORBA_OBJECT_NIL;
		}
	}

	moniker = Bonobo_Unknown_queryInterface (object, 
						 "IDL:Bonobo/Moniker:1.0", 
						 my_ev);

	if (BONOBO_EX (my_ev) || moniker == CORBA_OBJECT_NIL) {
		bonobo_object_release_unref (object, NULL);
		if (moniker == CORBA_OBJECT_NIL)
			g_warning ("Moniker object '%s' doesn't implement "
				   "the Moniker interface", iid);
		if (!opt_ev)
			CORBA_exception_free (&ev);
		return CORBA_OBJECT_NIL;
	}

	bonobo_object_release_unref (object, NULL);

	Bonobo_Moniker_setName (moniker, name, my_ev);

	if (BONOBO_EX (my_ev)) {
		bonobo_object_release_unref (moniker, NULL);	
		if (!opt_ev)
			CORBA_exception_free (&ev);
		return CORBA_OBJECT_NIL;
	}

	return moniker;
}

/**
 * bonobo_moniker_client_get_name:
 * @moniker: a moniker handle
 * @opt_ev: a corba exception environment 
 * 
 * Return value: the name of the moniker.
 **/
CORBA_char *
bonobo_moniker_client_get_name (Bonobo_Moniker     moniker,
				CORBA_Environment *opt_ev)
{
	CORBA_Environment ev, *my_ev;
	CORBA_char *name;

	bonobo_return_val_if_fail (moniker != CORBA_OBJECT_NIL, NULL, opt_ev);

	if (!opt_ev) {
		CORBA_exception_init (&ev);
		my_ev = &ev;
	} else
		my_ev = opt_ev;

	name = Bonobo_Moniker_getName (moniker, my_ev);

	if (BONOBO_EX (my_ev))
		name = NULL;

	if (!opt_ev)
		CORBA_exception_free (&ev);
	
	return name;
}

static void
init_default_resolve_options (Bonobo_ResolveOptions *options)
{
	options->flags = 0;
	options->timeout = -1;
}

/**
 * bonobo_moniker_client_resolve_default:
 * @moniker: a moniker
 * @interface_name: the name of the interface we want returned as the result 
 * @opt_ev: an optional corba exception environment 
 * 
 *  This resolves the moniker object against the given interface,
 * with a default set of resolve options.
 * 
 * Return value: the interfaces resolved to or CORBA_OBJECT_NIL
 **/
Bonobo_Unknown
bonobo_moniker_client_resolve_default (Bonobo_Moniker     moniker,
				       const char        *interface_name,
				       CORBA_Environment *opt_ev)
{
	CORBA_Environment ev, *my_ev;
	Bonobo_ResolveOptions options;
	Bonobo_Unknown        retval;
	char                 *real_if;
	
	g_return_val_if_fail (interface_name != NULL, CORBA_OBJECT_NIL);
	g_return_val_if_fail (moniker != CORBA_OBJECT_NIL, CORBA_OBJECT_NIL);

	real_if = get_full_interface_name (interface_name);

	init_default_resolve_options (&options);

	if (!opt_ev) {
		CORBA_exception_init (&ev);
		my_ev = &ev;
	} else
		my_ev = opt_ev;

	retval = Bonobo_Moniker_resolve (moniker, &options, real_if, my_ev);
	
	if (!opt_ev)
		CORBA_exception_free (&ev);

	g_free (real_if);

	return retval;
}

/**
 * bonobo_get_object:
 * @name: the name of a moniker
 * @interface_name: the name of the interface we want returned as the result 
 * @opt_ev: an optional corba exception environment 
 * 
 *  This encapsulates both the parse stage and resolve process of using
 * a moniker, providing a simple VisualBasic like mechanism for using the
 * object name space.
 * 
 * Return value: the requested interface or CORBA_OBJECT_NIL
 **/
Bonobo_Unknown
bonobo_get_object (const CORBA_char *name,
		   const char        *interface_name,
		   CORBA_Environment *opt_ev)
{
	CORBA_Environment ev, *my_ev;
	Bonobo_Moniker moniker;
	Bonobo_Unknown retval;

	bonobo_return_val_if_fail (name != NULL, CORBA_OBJECT_NIL, opt_ev);
	bonobo_return_val_if_fail (interface_name != NULL, CORBA_OBJECT_NIL, 
				   opt_ev);

	if (!opt_ev) {
		CORBA_exception_init (&ev);
		my_ev = &ev;
	} else
		my_ev = opt_ev;

	moniker = bonobo_moniker_client_new_from_name (name, my_ev);

	if (BONOBO_EX (my_ev) || moniker == CORBA_OBJECT_NIL) {
		if (!opt_ev)
			CORBA_exception_free (&ev);
		return CORBA_OBJECT_NIL;
	}

	retval = bonobo_moniker_client_resolve_default (
		moniker, interface_name, my_ev);

	bonobo_object_release_unref (moniker, NULL);

	if (BONOBO_EX (my_ev)) {
		if (!opt_ev)
			CORBA_exception_free (&ev);
		return CORBA_OBJECT_NIL;
	}

	if (!opt_ev)
		CORBA_exception_free (&ev);

	return retval;
}

static ORBit_IMethod *set_name_method = NULL;
static ORBit_IMethod *resolve_method = NULL;

static void
setup_methods (void)
{
	set_name_method = &Bonobo_Moniker__iinterface.methods._buffer[3];
	resolve_method  = &Bonobo_Moniker__iinterface.methods._buffer[4];

	/* If these blow the IDL changed order, and the above
	   indexes need updating */
	g_assert (!strcmp (set_name_method->name, "setName"));
	g_assert (!strcmp (resolve_method->name, "resolve"));
}

typedef struct {
	char                *name;
	BonoboMonikerAsyncFn cb;
	gpointer             user_data;
	Bonobo_Unknown       moniker;
} parse_async_ctx_t;

static void
parse_async_ctx_free (parse_async_ctx_t *ctx)
{
	if (ctx) {
		g_free (ctx->name);
		g_free (ctx);
	}
}

static void
async_parse_cb (CORBA_Object          object,
		ORBit_IMethod        *m_data,
		ORBitAsyncQueueEntry *aqe,
		gpointer              user_data, 
		CORBA_Environment    *ev)
{
	parse_async_ctx_t *ctx = user_data;

	if (BONOBO_EX (ev))
		ctx->cb (CORBA_OBJECT_NIL, ev, ctx->user_data);
	else {
		ORBit_small_demarshal_async (aqe, NULL, NULL, ev);

		ctx->cb (ctx->moniker, ev, ctx->user_data);
	}

	bonobo_object_release_unref (ctx->moniker, ev);

	parse_async_ctx_free (ctx);
}

static void
async_activation_cb (CORBA_Object activated_object, 
		     const char  *error_reason, 
		     gpointer     user_data)
{
	parse_async_ctx_t *ctx = user_data;
	CORBA_Environment ev;

	CORBA_exception_init (&ev);

	if (error_reason) { /* badly designed oaf interface */

		CORBA_exception_set (&ev, CORBA_USER_EXCEPTION,
				     ex_Bonobo_Moniker_UnknownPrefix, NULL);

		ctx->cb (CORBA_OBJECT_NIL, &ev, ctx->user_data);
		parse_async_ctx_free (ctx);
	} else {
		ctx->moniker = Bonobo_Unknown_queryInterface (
			activated_object, "IDL:Bonobo/Moniker:1.0", &ev);

		if (BONOBO_EX (&ev)) {
			ctx->cb (CORBA_OBJECT_NIL, &ev, ctx->user_data);
			parse_async_ctx_free (ctx);
		
		} else if (ctx->moniker == CORBA_OBJECT_NIL) {
			CORBA_exception_set (&ev, CORBA_USER_EXCEPTION,
					     ex_Bonobo_Moniker_InterfaceNotFound, NULL);
			ctx->cb (CORBA_OBJECT_NIL, &ev, ctx->user_data);
			parse_async_ctx_free (ctx);

		} else {
			gpointer args [] = { NULL };
	
			args[0] = &ctx->name;

			if (!set_name_method)
				setup_methods ();
				
			ORBit_small_invoke_async (
				ctx->moniker, set_name_method,
				async_parse_cb, ctx, args, NULL, &ev);
			
			if (BONOBO_EX (&ev)) {
				ctx->cb (CORBA_OBJECT_NIL, &ev, ctx->user_data);
				parse_async_ctx_free (ctx);
			}

			bonobo_object_release_unref (activated_object, &ev);
		}
	}

	CORBA_exception_free (&ev);
}

/**
 * bonobo_moniker_client_new_from_name_async:
 * @name: the name
 * @ev: a corba exception environment 
 * @cb: the async callback that gets the response
 * @user_data: user context data to pass to that callback
 * 
 * An asynchronous version of new_from_name
 **/
void
bonobo_moniker_client_new_from_name_async (const CORBA_char    *name,
					   CORBA_Environment   *ev,
					   BonoboMonikerAsyncFn cb,
					   gpointer             user_data)
{
	parse_async_ctx_t *ctx;
	const char        *iid;
	const char        *mname;

	g_return_if_fail (ev != NULL);
	g_return_if_fail (cb != NULL);
	g_return_if_fail (name != NULL);

	if (!name [0]) {
		cb (CORBA_OBJECT_NIL, ev, user_data);
		return;
	}

	mname = bonobo_moniker_util_parse_name (name, NULL);

	ctx = g_new0 (parse_async_ctx_t, 1);
	ctx->name         = g_strdup (name);
	ctx->cb           = cb;
	ctx->user_data    = user_data;
	ctx->moniker      = CORBA_OBJECT_NIL;

	if (!(iid = moniker_id_from_nickname (mname))) {
		char *query;

		query = query_from_name (mname);

		bonobo_activation_activate_async (query, NULL, 0,
				    async_activation_cb, ctx, ev);

		g_free (query);
	} else
		bonobo_activation_activate_from_id_async ((gchar *) iid, 0,
					    async_activation_cb, ctx, ev);
}

typedef struct {
	Bonobo_Moniker       moniker;
	BonoboMonikerAsyncFn cb;
	gpointer             user_data;
} resolve_async_ctx_t;

static void
resolve_async_cb (CORBA_Object          object,
		  ORBit_IMethod        *m_data,
		  ORBitAsyncQueueEntry *aqe,
		  gpointer              user_data, 
		  CORBA_Environment    *ev)
{
	resolve_async_ctx_t *ctx = user_data;

	if (BONOBO_EX (ev))
		ctx->cb (CORBA_OBJECT_NIL, ev, ctx->user_data);
	else {
		ORBit_small_demarshal_async (aqe, &object, NULL, ev);

		if (BONOBO_EX (ev))
			object = CORBA_OBJECT_NIL;

		ctx->cb (object, ev, ctx->user_data);
	}

	bonobo_object_release_unref (ctx->moniker, ev);
	g_free (ctx);
}

/**
 * bonobo_moniker_resolve_async:
 * @moniker: the moniker to resolve
 * @options: resolve options
 * @interface_name: the name of the interface we want returned as the result 
 * @ev: a corba exception environment 
 * @cb: the async callback that gets the response 
 * @user_data: user context data to pass to that callback 
 * 
 * An async version of bonobo_moniker_client_resolve
 **/
void
bonobo_moniker_resolve_async (Bonobo_Moniker         moniker,
			      Bonobo_ResolveOptions *options,
			      const char            *interface_name,
			      CORBA_Environment     *ev,
			      BonoboMonikerAsyncFn   cb,
			      gpointer               user_data)
{
	resolve_async_ctx_t *ctx;
	gpointer args [] = { NULL, NULL };

	args[0] = &options;
	args[1] = &interface_name;

	g_return_if_fail (ev != NULL);
	g_return_if_fail (cb != NULL);
	g_return_if_fail (moniker != CORBA_OBJECT_NIL);
	g_return_if_fail (options != CORBA_OBJECT_NIL);
	g_return_if_fail (interface_name != CORBA_OBJECT_NIL);

	ctx = g_new0 (resolve_async_ctx_t, 1);
	ctx->cb = cb;
	ctx->user_data = user_data;
	ctx->moniker = bonobo_object_dup_ref (moniker, ev);
	
	if (!resolve_method)
		setup_methods ();
				
	ORBit_small_invoke_async (
		ctx->moniker, resolve_method,
		resolve_async_cb, ctx, args, NULL, ev);
}

/**
 * bonobo_moniker_resolve_async_default:
 * @moniker: 
 * @interface_name: the name of the interface we want returned as the result 
 * @ev: a corba exception environment 
 * @cb: the async callback that gets the response 
 * @user_data: user context data to pass to that callback 
 * 
 * An async version of bonobo_moniker_client_resolve_default
 **/
void
bonobo_moniker_resolve_async_default (Bonobo_Moniker       moniker,
				      const char          *interface_name,
				      CORBA_Environment   *ev,
				      BonoboMonikerAsyncFn cb,
				      gpointer             user_data)
{
	Bonobo_ResolveOptions options;

	g_return_if_fail (ev != NULL);
	g_return_if_fail (cb != NULL);
	g_return_if_fail (moniker != CORBA_OBJECT_NIL);
	g_return_if_fail (interface_name != CORBA_OBJECT_NIL);

	init_default_resolve_options (&options);

	bonobo_moniker_resolve_async (moniker, &options,
				      interface_name,
				      ev, cb, user_data);
}


typedef struct {
	char                *interface_name;
	BonoboMonikerAsyncFn cb;
	gpointer             user_data;
} get_object_async_ctx_t;

static void
get_object_async_ctx_free (get_object_async_ctx_t *ctx)
{
	if (ctx) {
		g_free (ctx->interface_name);
		g_free (ctx);
	}
}

static void
get_async2_cb (Bonobo_Unknown     object,
	       CORBA_Environment *ev,
	       gpointer           user_data)
{
	get_object_async_ctx_t *ctx = user_data;

	ctx->cb (object, ev, ctx->user_data);

	get_object_async_ctx_free (ctx);
}	

static void
get_async1_cb (Bonobo_Unknown     object,
	       CORBA_Environment *ev,
	       gpointer           user_data)
{
	get_object_async_ctx_t *ctx = user_data;

	if (BONOBO_EX (ev)) {
		ctx->cb (CORBA_OBJECT_NIL, ev, ctx->user_data);
		get_object_async_ctx_free (ctx);
	} else {
                bonobo_moniker_resolve_async_default (
			object, ctx->interface_name, ev,
			get_async2_cb, ctx);

		if (BONOBO_EX (ev)) {
			ctx->cb (CORBA_OBJECT_NIL, ev, ctx->user_data);
			get_object_async_ctx_free (ctx);
		}
	}
}	

/**
 * bonobo_get_object_async:
 * @name: 
 * @interface_name: the name of the interface we want returned as the result 
 * @ev: a corba exception environment 
 * @cb: the async callback that gets the response 
 * @user_data: user context data to pass to that callback 
 * 
 * An async version of bonobo_get_object
 **/
void
bonobo_get_object_async (const CORBA_char    *name,
			 const char          *interface_name,
			 CORBA_Environment   *ev,
			 BonoboMonikerAsyncFn cb,
			 gpointer             user_data)
{
	get_object_async_ctx_t *ctx;

	g_return_if_fail (ev != NULL);
	g_return_if_fail (cb != NULL);
	g_return_if_fail (name != NULL);
	g_return_if_fail (interface_name != NULL);

	ctx = g_new0 (get_object_async_ctx_t, 1);
	ctx->cb = cb;
	ctx->user_data = user_data;
	ctx->interface_name = get_full_interface_name (interface_name);

	bonobo_moniker_client_new_from_name_async (
		name, ev, get_async1_cb, ctx);
}

/**
 * bonobo_moniker_client_equal:
 * @moniker: The moniker
 * @name: a moniker name eg. file:/demo/a.jpeg
 * @opt_ev: optional CORBA_Environment or NULL.
 * 
 * Compare a full @moniker with the given @name
 * 
 * Return value: TRUE if they are the same
 **/
gboolean
bonobo_moniker_client_equal (Bonobo_Moniker     moniker,
			     const CORBA_char  *name,
			     CORBA_Environment *opt_ev)
{
	CORBA_long l;
	CORBA_Environment *real_ev, tmp_ev;
	
	g_return_val_if_fail (name != NULL, FALSE);
	g_return_val_if_fail (moniker != CORBA_OBJECT_NIL, FALSE);

	if (opt_ev)
		real_ev = opt_ev;
	else {
		CORBA_exception_init (&tmp_ev);
		real_ev = &tmp_ev;
	}

	l = Bonobo_Moniker_equal (moniker, name, real_ev);

	if (BONOBO_EX (real_ev))
		l = 0;

	if (!opt_ev)
		CORBA_exception_free (&tmp_ev);

	return l != 0;
}

/* A product of dire API design ...  */
static CosNaming_Name*
bonobo_string_to_CosNaming_Name (const CORBA_char *string,
				 CORBA_Environment * ev)
{
	CosNaming_Name *retval = CosNaming_Name__alloc ();
	GPtrArray *ids = g_ptr_array_new ();
	GPtrArray *kinds = g_ptr_array_new ();
	gint pos = 0, i, len;
	gboolean used = FALSE;
	GPtrArray *append_to;

	g_ptr_array_add (ids, g_string_new (NULL));
	g_ptr_array_add (kinds, g_string_new (NULL));

	append_to = ids;

	while (*string) {
		gchar append;
		switch (*string) {
		case '.':
			used = TRUE;
			g_return_val_if_fail (append_to != kinds, NULL);  
			append_to = kinds;
			append = '\0';
			break;
		case '/':
			if (used) {
				pos++;
				g_ptr_array_add (ids, g_string_new (NULL));
				g_ptr_array_add (kinds, g_string_new (NULL));
				g_assert (ids->len == pos + 1 && kinds->len == pos + 1);
			}
			used = FALSE;
			append_to = ids;
			append = '\0';
			break;
		case '\\':
			string++;
			g_return_val_if_fail (*string == '.' || 
					      *string == '/' || *string == '\\', NULL);  
			append = *string;
			break;
		default:
			append = *string;
			used = TRUE;
			break;
		}

		if (append)
			g_string_append_c (g_ptr_array_index (append_to, pos), append);

		string++;
	}

	len = used ? pos + 1 : pos;

	retval->_buffer = CORBA_sequence_CosNaming_NameComponent_allocbuf (len);
	retval->_length = len;
	retval->_maximum = len;

	for (i = 0; i < len; i++) {  
		GString *id = g_ptr_array_index (ids, i);
		GString *kind = g_ptr_array_index (kinds, i);
      
		retval->_buffer[i].id = CORBA_string_dup (id->str);
		retval->_buffer[i].kind = CORBA_string_dup (kind->str);
	}
  
	for (i = 0; i <= pos; i++) {  
		g_string_free (g_ptr_array_index (ids, i), TRUE);
		g_string_free (g_ptr_array_index (kinds, i), TRUE);
	}

	g_ptr_array_free (ids, TRUE);
	g_ptr_array_free (kinds, TRUE);

	return retval;
}

static CosNaming_NamingContext
lookup_naming_context (GList *path,
		       CORBA_Environment *ev)
{
	CosNaming_NamingContext ns, ctx, new_ctx;
	CosNaming_Name *cn;
	GList          *l;

	g_return_val_if_fail (path != NULL, CORBA_OBJECT_NIL);
	g_return_val_if_fail (path->data != NULL, CORBA_OBJECT_NIL);
	g_return_val_if_fail (ev != NULL, CORBA_OBJECT_NIL);

	ns =  bonobo_activation_name_service_get (ev);
	if (BONOBO_EX (ev) || ns == CORBA_OBJECT_NIL)
		return CORBA_OBJECT_NIL;

	ctx = ns;

	for (l = path; l != NULL; l = l->next) {

		cn = bonobo_string_to_CosNaming_Name (l->data, ev);

		if (BONOBO_EX (ev) || !cn)
			break;

		new_ctx = CosNaming_NamingContext_resolve (ctx, cn, ev);
		if (BONOBO_USER_EX (ev, ex_CosNaming_NamingContext_NotFound)) {
			CORBA_exception_init (ev);
			new_ctx = CosNaming_NamingContext_bind_new_context (
				ctx, cn, ev);
			if (BONOBO_EX (ev) || new_ctx == CORBA_OBJECT_NIL) {
				CORBA_free (cn);
				break;
			}
		}

		CORBA_free (cn);

		if (BONOBO_EX (ev))
			new_ctx = CORBA_OBJECT_NIL;
		
		CORBA_Object_release (ctx, ev);

		ctx = new_ctx;

		if (!ctx)
			break;
	}

	return ctx;
}

static CosNaming_Name *
url_to_name (char *url, char *opt_kind)
{
	CosNaming_Name *retval;

	g_return_val_if_fail (url != NULL, NULL);

	retval = CosNaming_Name__alloc ();
	retval->_length = retval->_maximum = 1;
	retval->_buffer = CORBA_sequence_CosNaming_NameComponent_allocbuf (1);
	retval->_buffer[0].id = CORBA_string_dup (url);
	retval->_buffer[0].kind = CORBA_string_dup (opt_kind ? opt_kind : "");

	return retval;
}

static CosNaming_NamingContext
get_url_context (char *oafiid,
		 CORBA_Environment *ev)
{
	CosNaming_NamingContext  ctx = NULL;
	GList                   *path = NULL;

	path = g_list_append (path, "GNOME");
	path = g_list_append (path, "URL");
	path = g_list_append (path, oafiid);

	ctx = lookup_naming_context (path, ev);

	g_list_free (path);
		
	return ctx;
}

void
bonobo_url_register (char              *oafiid, 
		     char              *url, 
		     char              *mime_type,
		     Bonobo_Unknown     object,
		     CORBA_Environment *ev)
{
	CosNaming_NamingContext  ctx = NULL;
	CosNaming_Name          *cn;

	bonobo_return_if_fail (ev != NULL, NULL);
	bonobo_return_if_fail (oafiid != NULL, ev);
	bonobo_return_if_fail (url != NULL, ev);
	bonobo_return_if_fail (object != CORBA_OBJECT_NIL, ev);
	
	ctx = get_url_context (oafiid, ev);
		
	if (BONOBO_EX (ev) || ctx == CORBA_OBJECT_NIL)
		return;
	
	cn = url_to_name (url, mime_type);

	CosNaming_NamingContext_bind (ctx, cn, object, ev);

	CORBA_free (cn);

	CORBA_Object_release (ctx, NULL);
}

void
bonobo_url_unregister (char              *oafiid, 
		       char              *url,
		       CORBA_Environment *ev)
{
	CosNaming_NamingContext  ctx = NULL;
	CosNaming_Name          *cn;

	bonobo_return_if_fail (ev != NULL, NULL);
	bonobo_return_if_fail (oafiid != NULL, ev);
	bonobo_return_if_fail (url != NULL, ev);

	ctx = get_url_context (oafiid, ev);
		
	if (BONOBO_EX (ev) || ctx == CORBA_OBJECT_NIL)
		return;
	
	cn = url_to_name (url, NULL);

	CosNaming_NamingContext_unbind (ctx, cn, ev);

	CORBA_free (cn);

	CORBA_Object_release (ctx, NULL);
}

Bonobo_Unknown
bonobo_url_lookup (char              *oafiid, 
		   char              *url,
		   CORBA_Environment *ev)
{
	CosNaming_NamingContext  ctx = NULL;
	CosNaming_Name          *cn;
	Bonobo_Unknown           retval;

	bonobo_return_val_if_fail (ev != NULL, CORBA_OBJECT_NIL, NULL);
	bonobo_return_val_if_fail (oafiid != NULL, CORBA_OBJECT_NIL, ev);
	bonobo_return_val_if_fail (url != NULL, CORBA_OBJECT_NIL, ev);

	ctx = get_url_context (oafiid, ev);
		
	if (BONOBO_EX (ev) || ctx == CORBA_OBJECT_NIL)
		return CORBA_OBJECT_NIL;
	
	cn = url_to_name (url, NULL);

	retval = CosNaming_NamingContext_resolve (ctx, cn, ev);

	CORBA_free (cn);

	CORBA_Object_release (ctx, NULL);

	return retval;
}



syntax highlighted by Code2HTML, v. 0.9.1