/***************************************************************************
 * CVSID: $Id$
 *
 * libhal.c : HAL daemon C convenience library
 *
 * Copyright (C) 2003 David Zeuthen, <david@fubar.dk>
 * Copyright (C) 2006 Sjoerd Simons, <sjoerd@luon.net>
 * Copyright (C) 2007 Codethink Ltd. Author Rob Taylor <rob.taylor@codethink.co.uk>
 *
 * Licensed under the Academic Free License version 2.1
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307	 USA
 *
 **************************************************************************/

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dbus/dbus.h>

#include "uthash.h"
#include "libhal.h"

#ifdef ENABLE_NLS
# include <libintl.h>
# define _(String) dgettext (GETTEXT_PACKAGE, String)
# ifdef gettext_noop
#   define N_(String) gettext_noop (String)
# else
#   define N_(String) (String)
# endif
#else
/* Stubs that do something close enough.  */
# define textdomain(String) (String)
# define gettext(String) (String)
# define dgettext(Domain,Message) (Message)
# define dcgettext(Domain,Message,Type) (Message)
# define bindtextdomain(Domain,Directory) (Domain)
# define _(String)
# define N_(String) (String)
#endif

/**
 * LIBHAL_CHECK_PARAM_VALID:
 * @_param_: the prameter to check for 
 * @_name_:  the name of the prameter (for debug output) 
 * @_ret_:   what to use for return value if the prameter is NULL
 *
 * Handy macro for checking whether a parameter is valid and not NULL.
 */
#define LIBHAL_CHECK_PARAM_VALID(_param_,_name_,_ret_)				\
	do {									\
		if (_param_ == NULL) {						\
			fprintf (stderr,					\
				 "%s %d : invalid paramater. %s is NULL.\n",  	\
				 __FILE__, __LINE__, _name_);	 		\
			return _ret_;						\
		}								\
	} while(0)

/**
 * LIBHAL_CHECK_UDI_VALID:
 * @_udi_: the UID to check for 
 * @_ret_:   what to use for return value if udi is invalid 
 *
 * Handy macro for checking whether a UID is valid and not NULL.
 */
#define LIBHAL_CHECK_UDI_VALID(_udi_,_ret_)						\
	do {										\
		if (_udi_ == NULL) {							\
			fprintf (stderr,						\
				 "%s %d : invalid udi %s. udi is NULL.\n",  		\
				 __FILE__, __LINE__, _udi_);	 			\
			return _ret_;							\
		} else {								\
			if(strncmp(_udi_, "/org/freedesktop/Hal/devices/", 29) != 0) {	\
				fprintf (stderr,					\
                                 	 "%s %d : invalid udi: %s doesn't start"	\
					 "with '/org/freedesktop/Hal/devices/'. \n",    \
	                                 __FILE__, __LINE__, _udi_);			\
				return _ret_;						\
			}								\
		}									\
	} while(0)

static char **libhal_get_string_array_from_iter (DBusMessageIter *iter, int *num_elements);

static dbus_bool_t libhal_property_fill_value_from_variant (LibHalProperty *p, DBusMessageIter *var_iter);


/**
 * libhal_free_string_array:
 * @str_array: the array to be freed
 *
 * Frees a NULL-terminated array of strings. If passed NULL, does nothing.
 */
void
libhal_free_string_array (char **str_array)
{
	if (str_array != NULL) {
		int i;

		for (i = 0; str_array[i] != NULL; i++) {
			free (str_array[i]);
			str_array[i] = NULL;
		}
		free (str_array);
		str_array = NULL;
	}
}


/**
 * libhal_get_string_array_from_iter:
 * @iter: the message iterator to extract the strings from
 * @num_elements: pointer to an integer where to store number of elements (can be NULL)
 *
 * Creates a NULL terminated array of strings from a dbus message iterator.
 *
 * Returns: pointer to the string array
 */
static char **
libhal_get_string_array_from_iter (DBusMessageIter *iter, int *num_elements)
{
	int count;
	char **buffer;
	char **t;

	count = 0;
	buffer = (char **)malloc (sizeof (char *) * 8);

	if (buffer == NULL)
		goto oom;

	buffer[0] = NULL;
	while (dbus_message_iter_get_arg_type (iter) == DBUS_TYPE_STRING) {
		const char *value;
		char *str;
		
		if ((count % 8) == 0 && count != 0) {
			t = realloc (buffer, sizeof (char *) * (count + 8));
			if (t == NULL)
				goto oom;
			else
				buffer = t;
		}
		
		dbus_message_iter_get_basic (iter, &value);
		str = strdup (value);
		if (str == NULL)
			goto oom;

		buffer[count] = str;

		dbus_message_iter_next(iter);
		count++;
	}

	if ((count % 8) == 0) {
		t = realloc (buffer, sizeof (char *) * (count + 1));
		if (t == NULL)
			goto oom;
		else
			buffer = t;
	}

	buffer[count] = NULL;
	if (num_elements != NULL)
		*num_elements = count;
	return buffer;

oom:
	if (buffer != NULL)
		free (buffer);
	fprintf (stderr, "%s %d : error allocating memory\n", __FILE__, __LINE__);
	return NULL;

}

/**
 * libhal_free_string:
 * @str: the nul-terminated sting to free
 *
 * Used to free strings returned by libhal.
 */
void
libhal_free_string (char *str)
{
	if (str != NULL) {
		free (str);
		str = NULL;
	}
}


/**
 * LibHalPropertySet:
 *
 * Represents a set of properties. Opaque; use the
 * libhal_property_set_*() family of functions to access it.
 */
struct LibHalPropertySet_s {
	LibHalProperty *properties;
};

/**
 * LibHalProperty:
 *
 * Represents a property. Opaque.
 */
struct LibHalProperty_s {
	LibHalPropertyType type;	     	/**< Type of property */
	char *key;		     		/**< ASCII string */

	/** Possible values of the property */
	union {
		char *str_value;     		/**< UTF-8 zero-terminated string */
		dbus_int32_t int_value;         /**< 32-bit signed integer */
		dbus_uint64_t uint64_value;     /**< 64-bit unsigned integer */
		double double_value; 		/**< IEEE754 double precision float */
		dbus_bool_t bool_value;		/**< Truth value */
		char **strlist_value; 		/**< List of UTF-8 zero-terminated strings */
	} v;

	UT_hash_handle hh;		/*makes this hashable*/
};

/**
 * LibHalContext:
 *
 * Context for connection to the HAL daemon. Opaque, use the
 * libhal_ctx_*() family of functions to access it.
 */
struct LibHalContext_s {
	DBusConnection *connection;           /**< D-BUS connection */
	dbus_bool_t is_initialized;           /**< Are we initialised */
	dbus_bool_t is_shutdown;              /**< Have we been shutdown */
	dbus_bool_t cache_enabled;            /**< Is the cache enabled */
	dbus_bool_t is_direct;                /**< Whether the connection to hald is direct */

	/** Device added */
	LibHalDeviceAdded device_added;

	/** Device removed */
	LibHalDeviceRemoved device_removed;

	/** Device got a new capability */
	LibHalDeviceNewCapability device_new_capability;

	/** Device got a new capability */
	LibHalDeviceLostCapability device_lost_capability;

	/** A property of a device changed  */
	LibHalDevicePropertyModified device_property_modified;

	/** A non-continous event on the device occured  */
	LibHalDeviceCondition device_condition;
        
        /** A global interface lock is acquired  */
        LibHalGlobalInterfaceLockAcquired global_interface_lock_acquired;

        /** A global interface lock is released  */
        LibHalGlobalInterfaceLockReleased global_interface_lock_released;

        /** An interface lock is acquired  */
        LibHalInterfaceLockAcquired interface_lock_acquired;

        /** An interface lock is released  */
        LibHalInterfaceLockReleased interface_lock_released;

	/** Singleton device added */
	LibHalSingletonDeviceAdded singleton_device_added;

	/** Singleton device removed*/
	LibHalSingletonDeviceRemoved singleton_device_removed;

	void *user_data;                      /**< User data */
};

/**
 * libhal_ctx_set_user_data:
 * @ctx: the context for the connection to hald
 * @user_data: user data
 *
 * Set user data for the context.
 *
 * Returns: TRUE if user data was successfully set, FALSE if otherwise
 */
dbus_bool_t
libhal_ctx_set_user_data(LibHalContext *ctx, void *user_data)
{
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	ctx->user_data = user_data;
	return TRUE;
}

/**
 * libhal_ctx_get_user_data:
 * @ctx: the context for the connection to hald
 *
 * Get user data for the context.
 *
 * Returns: opaque pointer stored through libhal_ctx_set_user_data() or NULL if not set.
 */
void*
libhal_ctx_get_user_data(LibHalContext *ctx)
{
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, NULL);
	return ctx->user_data;
}


/**
 * libhal_property_fill_value_from_variant: 
 * @p: the property to fill in
 * @var_iter: variant iterator to extract the value from
 *
 * Fills in the value for the LibHalProperty given a variant iterator. 
 *
 * Returns: Whether the value was put in.
 */
static dbus_bool_t
libhal_property_fill_value_from_variant (LibHalProperty *p, DBusMessageIter *var_iter)
{
	DBusMessageIter iter_array;

	LIBHAL_CHECK_PARAM_VALID(p, "LibHalProperty *p", FALSE);
	LIBHAL_CHECK_PARAM_VALID(var_iter, "DBusMessageIter *var_iter", FALSE);

	switch (p->type) {
	case DBUS_TYPE_ARRAY:
		if (dbus_message_iter_get_element_type (var_iter) != DBUS_TYPE_STRING)
			return FALSE;

		dbus_message_iter_recurse (var_iter, &iter_array);
		p->v.strlist_value = libhal_get_string_array_from_iter (&iter_array, NULL);

		p->type = LIBHAL_PROPERTY_TYPE_STRLIST; 

		break;
	case DBUS_TYPE_STRING:
	{
		const char *v;

		dbus_message_iter_get_basic (var_iter, &v);

		p->v.str_value = strdup (v);
		if (p->v.str_value == NULL) 
			return FALSE;
		p->type = LIBHAL_PROPERTY_TYPE_STRING; 

		break;
	}
	case DBUS_TYPE_INT32:
	{
		dbus_int32_t v;
		
		dbus_message_iter_get_basic (var_iter, &v);
		
		p->v.int_value = v;
		p->type = LIBHAL_PROPERTY_TYPE_INT32; 

		break;
	}
	case DBUS_TYPE_UINT64:
	{
		dbus_uint64_t v;
		
		dbus_message_iter_get_basic (var_iter, &v);

		p->v.uint64_value = v;
		p->type = LIBHAL_PROPERTY_TYPE_UINT64; 
		
		break;
	}
	case DBUS_TYPE_DOUBLE:
	{
		double v;

		dbus_message_iter_get_basic (var_iter, &v);

		p->v.double_value = v;
		p->type = LIBHAL_PROPERTY_TYPE_DOUBLE; 

		break;
	}
	case DBUS_TYPE_BOOLEAN:
	{
		double v;

		dbus_message_iter_get_basic (var_iter, &v);

		p->v.double_value = v;
		p->type = LIBHAL_PROPERTY_TYPE_BOOLEAN; 

		break;
	}
	default:
		/** @todo  report error */
		break;
	}

	return TRUE;
}

static LibHalPropertySet *
get_property_set (DBusMessageIter *iter)
{
	LibHalPropertySet *result;
	DBusMessageIter dict_iter;

	result = malloc (sizeof (LibHalPropertySet));
	if (result == NULL) 
		goto oom;

/*
    result->properties = malloc(sizeof(LibHalProperty)*result->num_properties);
    if( result->properties==NULL )
    {
    /// @todo  cleanup
	return NULL;
    }
*/

	result->properties = NULL;

	if (dbus_message_iter_get_arg_type (iter) != DBUS_TYPE_ARRAY  &&
	    dbus_message_iter_get_element_type (iter) != DBUS_TYPE_DICT_ENTRY) {
		fprintf (stderr, "%s %d : error, expecting an array of dict entries\n",
			 __FILE__, __LINE__);
		free (result);
		return NULL;
	}

	dbus_message_iter_recurse (iter, &dict_iter);

	while (dbus_message_iter_get_arg_type (&dict_iter) == DBUS_TYPE_DICT_ENTRY)
	{
		DBusMessageIter dict_entry_iter, var_iter;
		const char *key;
		LibHalProperty *p;

		dbus_message_iter_recurse (&dict_iter, &dict_entry_iter);

		dbus_message_iter_get_basic (&dict_entry_iter, &key);

		p = malloc (sizeof (LibHalProperty));
		if (p == NULL)
			goto oom;

		p->key = strdup (key);
		if (p->key == NULL) {
			free (p);
			goto oom;
		}

		dbus_message_iter_next (&dict_entry_iter);

		dbus_message_iter_recurse (&dict_entry_iter, &var_iter);

		p->type = (LibHalPropertyType) dbus_message_iter_get_arg_type (&var_iter);

		if (!libhal_property_fill_value_from_variant (p, &var_iter)) {
			free (p);
			goto oom;
		}

                HASH_ADD_KEYPTR (hh, result->properties, p->key, strlen (p->key), p);

		dbus_message_iter_next (&dict_iter);
	}

	return result;

oom:
	if (result != NULL)
		libhal_free_property_set (result);

	fprintf (stderr,
		"%s %d : error allocating memory\n",
		 __FILE__, __LINE__);

	return NULL;
}

/**
 * libhal_device_get_all_properties:
 * @ctx: the context for the connection to hald
 * @udi: the Unique id of device
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Retrieve all the properties on a device.
 *
 * Returns: An object represent all properties. Must be freed with libhal_free_property_set().
 */
LibHalPropertySet *
libhal_device_get_all_properties (LibHalContext *ctx, const char *udi, DBusError *error)
{
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter reply_iter;
	LibHalPropertySet *result;
	DBusError _error;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, NULL);
	LIBHAL_CHECK_UDI_VALID(udi, NULL);

	message = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
						"org.freedesktop.Hal.Device",
						"GetAllProperties");

	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return NULL;
	}

	dbus_error_init (&_error);
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   &_error);

	dbus_message_unref (message);

	dbus_move_error (&_error, error);
	if (error != NULL && dbus_error_is_set (error)) {
		fprintf (stderr,
			 "%s %d : %s\n",
			 __FILE__, __LINE__, error->message);
		return NULL;
	}

	if (reply == NULL) {
		return NULL;
	}

	dbus_message_iter_init (reply, &reply_iter);

	result = get_property_set (&reply_iter);

	dbus_message_unref (reply);

	return result;
}

static int
key_sort (LibHalProperty *a, LibHalProperty *b)
{
	return strcmp (a->key, b->key);
}

/**
 * libhal_property_set_sort:
 * @set: property-set to sort
 *
 * Sort all properties according to property name.
 */
void 
libhal_property_set_sort (LibHalPropertySet *set)
{
	if (set == NULL)
		return;

	HASH_SORT (set->properties, key_sort);
}

/**
 * libhal_free_property_set:
 * @set: property-set to free
 *
 * Free a property set earlier obtained with libhal_device_get_all_properties().
 */
void
libhal_free_property_set (LibHalPropertySet * set)
{
	LibHalProperty *p, *p_old;

	if (set == NULL) 
		return;

	for (p = set->properties; p != NULL;) {
		HASH_DELETE (hh, set->properties, p);
		free (p->key);
		if (p->type == DBUS_TYPE_STRING)
			free (p->v.str_value);
		if (p->type == LIBHAL_PROPERTY_TYPE_STRLIST)
			libhal_free_string_array (p->v.strlist_value);
		p_old = p;
		p = p->hh.next;
		free (p_old);
	}
	free (set);
}

/**
 * libhal_property_set_get_num_elems: 
 * @set: property set to consider
 *
 * Get the number of properties in a property set.
 *
 * Returns: number of properties in given property set
 */
unsigned int 
libhal_property_set_get_num_elems (LibHalPropertySet *set)
{
	unsigned int num_elems;
	LibHalProperty *p;

	LIBHAL_CHECK_PARAM_VALID(set, "*set", 0);
	
	num_elems = 0;
	for (p = set->properties; p != NULL; p = p->hh.next)
		num_elems++;

	return num_elems;
}

static LibHalProperty *
property_set_lookup (const LibHalPropertySet *set, const char *key)
{
	LibHalProperty *p;

	LIBHAL_CHECK_PARAM_VALID(set, "*set", NULL);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", NULL);

	HASH_FIND_STR (set->properties, key, p);
	return p;
}

/**
 * libhal_ps_get_type:
 * @set: property set
 * @key: name of property to inspect
 *
 * Get the type of a given property. 
 *
 * Returns: the #LibHalPropertyType of the given property, 
 * LIBHAL_PROPERTY_TYPE_INVALID if property is not in the set
 */
LibHalPropertyType
libhal_ps_get_type (const LibHalPropertySet *set, const char *key)
{
	LibHalProperty *p;

	LIBHAL_CHECK_PARAM_VALID(set, "*set", LIBHAL_PROPERTY_TYPE_INVALID);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", LIBHAL_PROPERTY_TYPE_INVALID);

	p = property_set_lookup (set, key);
	if (p) return p->type;
	else return LIBHAL_PROPERTY_TYPE_INVALID;
}

/**
 * libhal_ps_get_string:
 * @set: property set
 * @key: name of property to inspect
 *
 * Get the value of a property of type string.
 *
 * Returns: UTF8 nul-terminated string. This pointer is only valid
 * until libhal_free_property_set() is invoked on the property set
 * this property belongs to. NULL if property is not in the set or not a string
 */
const char *
libhal_ps_get_string  (const LibHalPropertySet *set, const char *key)
{
	LibHalProperty *p;
	
	LIBHAL_CHECK_PARAM_VALID(set, "*set", NULL);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", NULL);

	p = property_set_lookup (set, key);
	if (p && p->type == LIBHAL_PROPERTY_TYPE_STRING)
		return p->v.str_value;
	else return NULL;
}

/**
 * libhal_ps_get_int:
 * @set: property set
 * @key: name of property to inspect
 *
 * Get the value of a property of type signed integer. 
 *
 * Returns: property value (32-bit signed integer)
 */
dbus_int32_t
libhal_ps_get_int32 (const LibHalPropertySet *set, const char *key)
{
	LibHalProperty *p;
	
	LIBHAL_CHECK_PARAM_VALID(set, "*set", 0);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", 0);
	
	p = property_set_lookup (set, key);
	if (p && p->type == LIBHAL_PROPERTY_TYPE_INT32)
		return p->v.int_value;
	else return 0;
}

/**
 * libhal_ps_get_uint64:
 * @set: property set
 * @key: name of property to inspect
 *
 * Get the value of a property of type unsigned integer. 
 *
 * Returns: property value (64-bit unsigned integer)
 */
dbus_uint64_t
libhal_ps_get_uint64 (const LibHalPropertySet *set, const char *key)
{
	LibHalProperty *p;

	LIBHAL_CHECK_PARAM_VALID(set, "*set", 0);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", 0);

	p = property_set_lookup (set, key);
	if (p && p->type == LIBHAL_PROPERTY_TYPE_UINT64)
		return p->v.uint64_value;
	else return 0;
}

/**
 * libhal_ps_get_double:
 * @set: property set
 * @key: name of property to inspect
 *
 * Get the value of a property of type double.
 *
 * Returns: property value (IEEE754 double precision float)
 */
double
libhal_ps_get_double (const LibHalPropertySet *set, const char *key)
{
	LibHalProperty *p;

	LIBHAL_CHECK_PARAM_VALID(set, "*set", 0.0);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", 0.0);

	p = property_set_lookup (set, key);
	if (p && p->type == LIBHAL_PROPERTY_TYPE_DOUBLE)
		return p->v.double_value;
	else return 0.0;
}

/**
 * libhal_ps_get_bool:
 * @set: property set
 * @key: name of property to inspect
 *
 * Get the value of a property of type bool. 
 *
 * Returns: property value (bool)
 */
dbus_bool_t
libhal_ps_get_bool (const LibHalPropertySet *set, const char *key)
{
	LibHalProperty *p;

	LIBHAL_CHECK_PARAM_VALID(set, "*set", FALSE);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", FALSE);
	
	p = property_set_lookup (set, key);
	if (p && p->type == LIBHAL_PROPERTY_TYPE_BOOLEAN)
		return p->v.bool_value;
	else return FALSE;
}

/**
 * libhal_ps_get_strlist:
 * @set: property set
 * @key: name of property to inspect
 *
 * Get the value of a property of type string list. 
 *
 * Returns: pointer to array of strings, this is owned by the property set
 */
const char *const *
libhal_ps_get_strlist (const LibHalPropertySet *set, const char *key)
{
	LibHalProperty *p;

	LIBHAL_CHECK_PARAM_VALID(set, "*set", NULL);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", NULL);
	
	p = property_set_lookup (set, key);
	if (p && p->type == LIBHAL_PROPERTY_TYPE_STRLIST)
		return (const char *const *) p->v.strlist_value;
	else return NULL;
}


/**
 * libhal_psi_init:
 * @iter: iterator object
 * @set: property set to iterate over
 *
 * Initialize a property set iterator.
 *
 */
void
libhal_psi_init (LibHalPropertySetIterator * iter, LibHalPropertySet * set)
{
	if (set == NULL)
		return;

	iter->set = set;
	iter->idx = -1; //deprecated
	iter->cur_prop = set->properties;
}


/**
 * libhal_psi_has_more:
 * @iter: iterator object
 *
 * Determine whether there are more properties to iterate over.
 *
 * Returns: TRUE if there are more properties, FALSE otherwise.
 */
dbus_bool_t
libhal_psi_has_more (LibHalPropertySetIterator * iter)
{
	return (iter->cur_prop != NULL);
}

/**
 * libhal_psi_next:
 * @iter: iterator object
 *
 * Advance iterator to next property.
 */
void
libhal_psi_next (LibHalPropertySetIterator * iter)
{
	iter->cur_prop = iter->cur_prop->hh.next;
}

/**
 * libhal_psi_get_type:
 * @iter: iterator object
 *
 * Get type of property.
 *
 * Returns: the property type at the iterator's position
 */
LibHalPropertyType
libhal_psi_get_type (LibHalPropertySetIterator * iter)
{
	return iter->cur_prop->type;
}

/**
 * libhal_psi_get_key:
 * @iter: iterator object
 *
 * Get the key of a property. 
 *
 * Returns: ASCII nul-terminated string. This pointer is only valid
 * until libhal_free_property_set() is invoked on the property set
 * this property belongs to.
 */
char *
libhal_psi_get_key (LibHalPropertySetIterator * iter)
{
	return iter->cur_prop->key;
}

/**
 * libhal_psi_get_string:
 * @iter: iterator object
 *
 * Get the value of a property of type string.
 *
 * Returns: UTF8 nul-terminated string. This pointer is only valid
 * until libhal_free_property_set() is invoked on the property set
 * this property belongs to.
 */
char *
libhal_psi_get_string (LibHalPropertySetIterator * iter)
{
	return iter->cur_prop->v.str_value;
}

/**
 * libhal_psi_get_int:
 * @iter: iterator object
 *
 * Get the value of a property of type signed integer. 
 *
 * Returns: property value (32-bit signed integer)
 */
dbus_int32_t
libhal_psi_get_int (LibHalPropertySetIterator * iter)
{
	return iter->cur_prop->v.int_value;
}

/**
 * libhal_psi_get_uint64:
 * @iter: iterator object
 *
 * Get the value of a property of type unsigned integer. 
 *
 * Returns: property value (64-bit unsigned integer)
 */
dbus_uint64_t
libhal_psi_get_uint64 (LibHalPropertySetIterator * iter)
{
	return iter->cur_prop->v.uint64_value;
}

/**
 * libhal_psi_get_double:
 * @iter: iterator object
 *
 * Get the value of a property of type double.
 *
 * Returns: property value (IEEE754 double precision float)
 */
double
libhal_psi_get_double (LibHalPropertySetIterator * iter)
{
	return iter->cur_prop->v.double_value;
}

/**
 * libhal_psi_get_bool:
 * @iter: iterator object
 *
 * Get the value of a property of type bool. 
 *
 * Returns: property value (bool)
 */
dbus_bool_t
libhal_psi_get_bool (LibHalPropertySetIterator * iter)
{
	return iter->cur_prop->v.bool_value;
}

/**
 * libhal_psi_get_strlist:
 * @iter: iterator object
 *
 * Get the value of a property of type string list. 
 *
 * Returns: pointer to array of strings
 */
char **
libhal_psi_get_strlist (LibHalPropertySetIterator * iter)
{
	return iter->cur_prop->v.strlist_value;
}

static DBusHandlerResult
singleton_device_changed (LibHalContext *ctx, DBusConnection *connection, DBusMessage *msg, dbus_bool_t added)
{
	DBusMessage *reply;
	DBusMessageIter iter;
	LibHalPropertySet *set;
	const char *udi;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);

	dbus_message_iter_init (msg, &iter);

	/* First should be the device UDI */
	if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
		goto malformed;

	dbus_message_iter_get_basic (&iter, &udi);

	dbus_message_iter_next (&iter);

	/* then the property set*/
	set = get_property_set (&iter);

	if (!set)
		goto malformed;
	if (added)
		(ctx->singleton_device_added)(ctx, udi, set);
	else
		(ctx->singleton_device_removed)(ctx, udi, set);

	libhal_free_property_set (set);

	reply = dbus_message_new_method_return (msg);
	if (reply == NULL)
		goto oom;

	if (!dbus_connection_send (connection, reply, NULL)) {
		dbus_message_unref (reply);
		goto oom;
	}

	dbus_message_unref (reply);

	return DBUS_HANDLER_RESULT_HANDLED;

malformed:
	fprintf (stderr, "%s %d : singlton device changed message malformed\n", __FILE__, __LINE__);
	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
oom:
	fprintf (stderr, "%s %d : error allocating memory\n", __FILE__, __LINE__);
	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

static DBusHandlerResult
filter_func (DBusConnection * connection,
	     DBusMessage * message, void *user_data)
{
	const char *object_path;
	DBusError error;
	LibHalContext *ctx = (LibHalContext *) user_data;

	if (ctx->is_shutdown)
		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;

	dbus_error_init (&error);

	object_path = dbus_message_get_path (message);

	/*fprintf (stderr, "*** libhal filer_func: connection=%p obj_path=%s interface=%s method=%s\n", 
		   connection,
		   dbus_message_get_path (message), 
		   dbus_message_get_interface (message),
		   dbus_message_get_member (message));
        */

	if (dbus_message_is_signal (message, "org.freedesktop.Hal.Manager",
				    "DeviceAdded")) {
		char *udi;
		if (dbus_message_get_args (message, &error,
					   DBUS_TYPE_STRING, &udi,
					   DBUS_TYPE_INVALID)) {
			if (ctx->device_added != NULL) {
				ctx->device_added (ctx, udi);
			}
		} else {
			LIBHAL_FREE_DBUS_ERROR(&error);
		}
		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
	} else if (dbus_message_is_signal (message, "org.freedesktop.Hal.Manager", "DeviceRemoved")) {
		char *udi;
		if (dbus_message_get_args (message, &error,
					   DBUS_TYPE_STRING, &udi,
					   DBUS_TYPE_INVALID)) {
			if (ctx->device_removed != NULL) {
				ctx->device_removed (ctx, udi);
			}
		} else {
			LIBHAL_FREE_DBUS_ERROR(&error);
		}
		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
	} else if (dbus_message_is_signal (message, "org.freedesktop.Hal.Manager","NewCapability")) {
		char *udi;
		char *capability;
		if (dbus_message_get_args (message, &error,
					   DBUS_TYPE_STRING, &udi,
					   DBUS_TYPE_STRING, &capability,
					   DBUS_TYPE_INVALID)) {
			if (ctx->device_new_capability != NULL) {
				ctx->device_new_capability (ctx, udi, capability);
			}
		} else {
			LIBHAL_FREE_DBUS_ERROR(&error);
		}
		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
	} else if (dbus_message_is_signal (message, "org.freedesktop.Hal.Manager","GlobalInterfaceLockAcquired")) {
		char *lock_name;
		char *lock_owner;
                int num_locks;
		if (dbus_message_get_args (message, &error,
					   DBUS_TYPE_STRING, &lock_name,
					   DBUS_TYPE_STRING, &lock_owner,
					   DBUS_TYPE_INT32, &num_locks,
					   DBUS_TYPE_INVALID)) {
			if (ctx->global_interface_lock_acquired != NULL) {
				ctx->global_interface_lock_acquired (ctx, lock_name, lock_owner, num_locks);
			}
		} else {
			LIBHAL_FREE_DBUS_ERROR(&error);
		}
		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
	} else if (dbus_message_is_signal (message, "org.freedesktop.Hal.Manager","GlobalInterfaceLockReleased")) {
		char *lock_name;
		char *lock_owner;
                int num_locks;
		if (dbus_message_get_args (message, &error,
					   DBUS_TYPE_STRING, &lock_name,
					   DBUS_TYPE_STRING, &lock_owner,
					   DBUS_TYPE_INT32, &num_locks,
					   DBUS_TYPE_INVALID)) {
			if (ctx->global_interface_lock_released != NULL) {
				ctx->global_interface_lock_released (ctx, lock_name, lock_owner, num_locks);
			}
		} else {
			LIBHAL_FREE_DBUS_ERROR(&error);
		}
		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
	} else if (dbus_message_is_signal (message, "org.freedesktop.Hal.Device", "Condition")) {
		char *condition_name;
		char *condition_detail;
		if (dbus_message_get_args (message, &error,
					   DBUS_TYPE_STRING, &condition_name,
					   DBUS_TYPE_STRING, &condition_detail,
					   DBUS_TYPE_INVALID)) {
			if (ctx->device_condition != NULL) {
				ctx->device_condition (ctx, object_path, condition_name, condition_detail);
			}
		} else {
			LIBHAL_FREE_DBUS_ERROR(&error);
		}
		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
	} else if (dbus_message_is_signal (message, "org.freedesktop.Hal.Device","InterfaceLockAcquired")) {
		char *lock_name;
		char *lock_owner;
                int num_locks;
		if (dbus_message_get_args (message, &error,
					   DBUS_TYPE_STRING, &lock_name,
					   DBUS_TYPE_STRING, &lock_owner,
					   DBUS_TYPE_INT32, &num_locks,
					   DBUS_TYPE_INVALID)) {
			if (ctx->interface_lock_acquired != NULL) {
				ctx->interface_lock_acquired (ctx, object_path, lock_name, lock_owner, num_locks);
			}
		} else {
			LIBHAL_FREE_DBUS_ERROR(&error);
		}
		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
	} else if (dbus_message_is_signal (message, "org.freedesktop.Hal.Device","InterfaceLockReleased")) {
		char *lock_name;
		char *lock_owner;
                int num_locks;
		if (dbus_message_get_args (message, &error,
					   DBUS_TYPE_STRING, &lock_name,
					   DBUS_TYPE_STRING, &lock_owner,
					   DBUS_TYPE_INT32, &num_locks,
					   DBUS_TYPE_INVALID)) {
			if (ctx->interface_lock_released != NULL) {
				ctx->interface_lock_released (ctx, object_path, lock_name, lock_owner, num_locks);
			}
		} else {
			LIBHAL_FREE_DBUS_ERROR(&error);
		}
		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
	} else if (dbus_message_is_signal (message, "org.freedesktop.Hal.Device", "PropertyModified")) {
		if (ctx->device_property_modified != NULL) {
			int i;
			char *key;
			dbus_bool_t removed;
			dbus_bool_t added;
			int num_modifications;
			DBusMessageIter iter;
			DBusMessageIter iter_array;
	
			dbus_message_iter_init (message, &iter);
			dbus_message_iter_get_basic (&iter, &num_modifications);
			dbus_message_iter_next (&iter);

			dbus_message_iter_recurse (&iter, &iter_array);

			for (i = 0; i < num_modifications; i++) {
				DBusMessageIter iter_struct;

				dbus_message_iter_recurse (&iter_array, &iter_struct);

				dbus_message_iter_get_basic (&iter_struct, &key);
				dbus_message_iter_next (&iter_struct);
				dbus_message_iter_get_basic (&iter_struct, &removed);
				dbus_message_iter_next (&iter_struct);
				dbus_message_iter_get_basic (&iter_struct, &added);
				
				ctx->device_property_modified (ctx, 
							       object_path,
							       key, removed,
							       added);
				
				dbus_message_iter_next (&iter_array);
			}
			
		}
		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
	} else if (dbus_message_is_method_call (message,
			"org.freedesktop.Hal.SingletonAddon",
			"DeviceAdded") &&
		    strcmp (dbus_message_get_path (message),
			  "/org/freedesktop/Hal/SingletonAddon") == 0) {
		return singleton_device_changed (ctx, connection, message, TRUE);
	} else if (dbus_message_is_method_call (message,
			"org.freedesktop.Hal.SingletonAddon",
			"DeviceRemoved") &&
		    strcmp (dbus_message_get_path (message),
			  "/org/freedesktop/Hal/SingletonAddon") == 0) {
		return singleton_device_changed (ctx, connection, message, FALSE);
	}

	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}

/**
 * libhal_get_all_devices:
 * @ctx: the context for the connection to hald
 * @num_devices: the number of devices will be stored here
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Get all devices in the Global Device List (GDL).
 *
 * Returns: An array of device identifiers terminated with NULL. It is
 * the responsibility of the caller to free with
 * libhal_free_string_array(). If an error occurs NULL is returned.
 */
char **
libhal_get_all_devices (LibHalContext *ctx, int *num_devices, DBusError *error)
{	
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter_array, reply_iter;
	char **hal_device_names;
	DBusError _error;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, NULL);

	*num_devices = 0;

	message = dbus_message_new_method_call ("org.freedesktop.Hal",
						"/org/freedesktop/Hal/Manager",
						"org.freedesktop.Hal.Manager",
						"GetAllDevices");
	if (message == NULL) {
		fprintf (stderr, "%s %d : Could not allocate D-BUS message\n", __FILE__, __LINE__);
		return NULL;
	}

	dbus_error_init (&_error);
	reply = dbus_connection_send_with_reply_and_block (ctx->connection, message, -1, &_error);

	dbus_message_unref (message);

	dbus_move_error (&_error, error);
	if (error != NULL && dbus_error_is_set (error)) {
		return NULL;
	}
	if (reply == NULL) {
		return NULL;
	}

	/* now analyze reply */
	dbus_message_iter_init (reply, &reply_iter);

	if (dbus_message_iter_get_arg_type (&reply_iter) != DBUS_TYPE_ARRAY) {
		fprintf (stderr, "%s %d : wrong reply from hald.  Expecting an array.\n", __FILE__, __LINE__);
		dbus_message_unref (reply);
		return NULL;
	}
	
	dbus_message_iter_recurse (&reply_iter, &iter_array);

	hal_device_names = libhal_get_string_array_from_iter (&iter_array, num_devices);
		      
	dbus_message_unref (reply);
	return hal_device_names;
}

/**
 * libhal_device_get_property_type:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @key: name of the property
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Query a property type of a device.
 *
 * Returns: A LibHalPropertyType. LIBHAL_PROPERTY_TYPE_INVALID is
 * return if the property doesn't exist.
 */
LibHalPropertyType
libhal_device_get_property_type (LibHalContext *ctx, const char *udi, const char *key, DBusError *error)
{
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter, reply_iter;
	LibHalPropertyType type;
	DBusError _error;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, LIBHAL_PROPERTY_TYPE_INVALID); /* or return NULL? */
	LIBHAL_CHECK_UDI_VALID(udi, LIBHAL_PROPERTY_TYPE_INVALID);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", LIBHAL_PROPERTY_TYPE_INVALID);

	message = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
						"org.freedesktop.Hal.Device",
						"GetPropertyType");
	if (message == NULL) {
		fprintf (stderr, "%s %d : Couldn't allocate D-BUS message\n", __FILE__, __LINE__);
		return LIBHAL_PROPERTY_TYPE_INVALID;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &key);

	dbus_error_init (&_error);
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   &_error);

	dbus_message_unref (message);

	dbus_move_error (&_error, error);
	if (error != NULL && dbus_error_is_set (error)) {
		return LIBHAL_PROPERTY_TYPE_INVALID;
	}
	if (reply == NULL) {
		return LIBHAL_PROPERTY_TYPE_INVALID;
	}

	dbus_message_iter_init (reply, &reply_iter);
	dbus_message_iter_get_basic (&reply_iter, &type);

	dbus_message_unref (reply);
	return type;
}

/**
 * libhal_device_get_property_strlist:
 * @ctx: the context for the connection to hald
 * @udi: unique Device Id
 * @key: name of the property
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Get the value of a property of type string list. 
 *
 * Returns: Array of pointers to UTF8 nul-terminated strings
 * terminated by NULL. The caller is responsible for freeing this
 * string array with the function libhal_free_string_array(). Returns
 * NULL if the property didn't exist or we are OOM
 */
char **
libhal_device_get_property_strlist (LibHalContext *ctx, const char *udi, const char *key, DBusError *error)
{	
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter, iter_array, reply_iter;
	char **our_strings;
	DBusError _error;
	
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, NULL);
	LIBHAL_CHECK_UDI_VALID(udi, NULL);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", NULL);

	message = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
						"org.freedesktop.Hal.Device",
						"GetPropertyStringList");
	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return NULL;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &key);

	dbus_error_init (&_error);
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   &_error);

	dbus_message_unref (message);

	dbus_move_error (&_error, error);
	if (error != NULL && dbus_error_is_set (error)) {
		return NULL;
	}
	if (reply == NULL) {
		return NULL;
	}
	/* now analyse reply */
	dbus_message_iter_init (reply, &reply_iter);

	if (dbus_message_iter_get_arg_type (&reply_iter) != DBUS_TYPE_ARRAY) {
		fprintf (stderr, "%s %d : wrong reply from hald.  Expecting an array.\n", __FILE__, __LINE__);
		dbus_message_unref (reply);
		return NULL;
	}
	
	dbus_message_iter_recurse (&reply_iter, &iter_array);

	our_strings = libhal_get_string_array_from_iter (&iter_array, NULL);
		      
	dbus_message_unref (reply);
	return our_strings;
}

/**
 * libhal_device_get_property_string:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @key: the name of the property
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 * 
 * Get the value of a property of type string. 
 *
 * Returns: UTF8 nul-terminated string. The caller is responsible for
 * freeing this string with the function libhal_free_string(). Returns
 * NULL if the property didn't exist or we are OOM.
 */
char *
libhal_device_get_property_string (LibHalContext *ctx,
				   const char *udi, const char *key, DBusError *error)
{	
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter, reply_iter;
	char *value;
	char *dbus_str;
	DBusError _error;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, NULL);
	LIBHAL_CHECK_UDI_VALID(udi, NULL);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", NULL);

	message = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
						"org.freedesktop.Hal.Device",
						"GetPropertyString");

	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return NULL;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &key);

	dbus_error_init (&_error);
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   &_error);

	dbus_message_unref (message);

	dbus_move_error (&_error, error);
	if (error != NULL && dbus_error_is_set (error)) {
		return NULL;
	}
	if (reply == NULL) {
		return NULL;
	}

	dbus_message_iter_init (reply, &reply_iter);

	/* now analyze reply */
	if (dbus_message_iter_get_arg_type (&reply_iter) !=
		   DBUS_TYPE_STRING) {
		dbus_message_unref (reply);
		return NULL;
	}

	dbus_message_iter_get_basic (&reply_iter, &dbus_str);
	value = (char *) ((dbus_str != NULL) ? strdup (dbus_str) : NULL);
	if (value == NULL) {
		fprintf (stderr, "%s %d : error allocating memory\n",
			 __FILE__, __LINE__);
	}

	dbus_message_unref (reply);
	return value;
}

/**
 * libhal_device_get_property_int:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @key: name of the property
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Get the value of a property of type integer. 
 *
 * Returns: Property value (32-bit signed integer)
 */
dbus_int32_t
libhal_device_get_property_int (LibHalContext *ctx, 
				const char *udi, const char *key, DBusError *error)
{
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter, reply_iter;
	dbus_int32_t value;
	DBusError _error;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, -1);
	LIBHAL_CHECK_UDI_VALID(udi, -1);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", -1);

	message = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
						"org.freedesktop.Hal.Device",
						"GetPropertyInteger");
	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return -1;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &key);

	dbus_error_init (&_error);
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   &_error);

	dbus_message_unref (message);

	dbus_move_error (&_error, error);
	if (error != NULL && dbus_error_is_set (error)) {
		return -1;
	}
	if (reply == NULL) {
		return -1;
	}

	dbus_message_iter_init (reply, &reply_iter);

	/* now analyze reply */
	if (dbus_message_iter_get_arg_type (&reply_iter) !=
		   DBUS_TYPE_INT32) {
		fprintf (stderr,
			 "%s %d : property '%s' for device '%s' is not "
			 "of type integer\n", __FILE__, __LINE__, key,
			 udi);
		dbus_message_unref (reply);
		return -1;
	}
	dbus_message_iter_get_basic (&reply_iter, &value);

	dbus_message_unref (reply);
	return value;
}

/**
 * libhal_device_get_property_uint64:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @key: name of the property
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Get the value of a property of type signed integer.
 *
 * Returns: Property value (64-bit unsigned integer)
 */
dbus_uint64_t
libhal_device_get_property_uint64 (LibHalContext *ctx, 
				   const char *udi, const char *key, DBusError *error)
{
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter, reply_iter;
	dbus_uint64_t value;
	DBusError _error;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, -1);
	LIBHAL_CHECK_UDI_VALID(udi, -1);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", -1);

	message = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
						"org.freedesktop.Hal.Device",
						"GetPropertyInteger");
	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return -1;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &key);

	dbus_error_init (&_error);
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   &_error);

	dbus_message_unref (message);

	dbus_move_error (&_error, error);
	if (error != NULL && dbus_error_is_set (error)) {
		return -1;
	}
	if (reply == NULL) {
		return -1;
	}

	dbus_message_iter_init (reply, &reply_iter);
	/* now analyze reply */
	if (dbus_message_iter_get_arg_type (&reply_iter) !=
		   DBUS_TYPE_UINT64) {
		fprintf (stderr,
			 "%s %d : property '%s' for device '%s' is not "
			 "of type integer\n", __FILE__, __LINE__, key,
			 udi);
		dbus_message_unref (reply);
		return -1;
	}
	dbus_message_iter_get_basic (&reply_iter, &value);

	dbus_message_unref (reply);
	return value;
}

/**
 * libhal_device_get_property_double:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @key: name of the property
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Get the value of a property of type double.
 *
 * Returns: Property value (IEEE754 double precision float)
 */
double
libhal_device_get_property_double (LibHalContext *ctx, 
				   const char *udi, const char *key, DBusError *error)
{
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter, reply_iter;
	double value;
	DBusError _error;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, -1.0);
	LIBHAL_CHECK_UDI_VALID(udi, -1.0);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", -1.0);

	message = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
						"org.freedesktop.Hal.Device",
						"GetPropertyDouble");
	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return -1.0f;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &key);

	dbus_error_init (&_error);
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   &_error);

	dbus_message_unref (message);

	dbus_move_error (&_error, error);
	if (error != NULL && dbus_error_is_set (error)) {
		return -1.0f;
	}
	if (reply == NULL) {
		return -1.0f;
	}

	dbus_message_iter_init (reply, &reply_iter);

	/* now analyze reply */
	if (dbus_message_iter_get_arg_type (&reply_iter) !=
		   DBUS_TYPE_DOUBLE) {
		fprintf (stderr,
			 "%s %d : property '%s' for device '%s' is not "
			 "of type double\n", __FILE__, __LINE__, key, udi);
		dbus_message_unref (reply);
		return -1.0f;
	}
	dbus_message_iter_get_basic (&reply_iter, &value);

	dbus_message_unref (reply);
	return (double) value;
}

/**
 * libhal_device_get_property_bool:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @key: name of the property
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Get the value of a property of type bool. 
 *
 * Returns: Property value (boolean)
 */
dbus_bool_t
libhal_device_get_property_bool (LibHalContext *ctx, 
				 const char *udi, const char *key, DBusError *error)
{
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter, reply_iter;
	dbus_bool_t value;
	DBusError _error;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", FALSE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
						"org.freedesktop.Hal.Device",
						"GetPropertyBoolean");
	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &key);
	
	dbus_error_init (&_error);
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   &_error);

	dbus_message_unref (message);

	dbus_move_error (&_error, error);
	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	if (reply == NULL) {
		return FALSE;
	}

	dbus_message_iter_init (reply, &reply_iter);

	/* now analyze reply */
	if (dbus_message_iter_get_arg_type (&reply_iter) !=
		   DBUS_TYPE_BOOLEAN) {
		fprintf (stderr,
			 "%s %d : property '%s' for device '%s' is not "
			 "of type bool\n", __FILE__, __LINE__, key, udi);
		dbus_message_unref (reply);
		return FALSE;
	}
	dbus_message_iter_get_basic (&reply_iter, &value);

	dbus_message_unref (reply);
	return value;
}


/* generic helper */
static dbus_bool_t
libhal_device_set_property_helper (LibHalContext *ctx, 
				   const char *udi,
				   const char *key,
				   int type,
				   const char *str_value,
				   dbus_int32_t int_value,
				   dbus_uint64_t uint64_value,
				   double double_value,
				   dbus_bool_t bool_value,
				   DBusError *error)
{
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter;
	char *method_name = NULL;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", FALSE);

	/** @todo  sanity check incoming params */
	switch (type) {
	case DBUS_TYPE_INVALID:
		method_name = "RemoveProperty";
		break;
	case DBUS_TYPE_STRING:
		method_name = "SetPropertyString";
		break;
	case DBUS_TYPE_INT32:
	case DBUS_TYPE_UINT64:
		method_name = "SetPropertyInteger";
		break;
	case DBUS_TYPE_DOUBLE:
		method_name = "SetPropertyDouble";
		break;
	case DBUS_TYPE_BOOLEAN:
		method_name = "SetPropertyBoolean";
		break;

	default:
		/* cannot happen; is not callable from outside this file */
		break;
	}

	message = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
						"org.freedesktop.Hal.Device",
						method_name);
	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &key);
	switch (type) {
	case DBUS_TYPE_STRING:
		dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &str_value);
		break;
	case DBUS_TYPE_INT32:
		dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &int_value);
		break;
	case DBUS_TYPE_UINT64:
		dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT64, &uint64_value);
		break;
	case DBUS_TYPE_DOUBLE:
		dbus_message_iter_append_basic (&iter, DBUS_TYPE_DOUBLE, &double_value);
		break;
	case DBUS_TYPE_BOOLEAN:
		dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &bool_value);
		break;
	}

	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	if (reply == NULL) {
		return FALSE;
	}

	dbus_message_unref (reply);

	return TRUE;
}

/**
 * libhal_device_set_property_string:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @key: name of the property
 * @value: value of the property; a UTF8 string
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Set a property of type string.
 *
 * Returns: TRUE if the property was set, FALSE if the device didn't
 * exist or the property had a different type.
 */
dbus_bool_t
libhal_device_set_property_string (LibHalContext *ctx, 
				   const char *udi,
				   const char *key, 
				   const char *value,
				   DBusError *error)
{
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", FALSE);
	LIBHAL_CHECK_PARAM_VALID(value, "*value", FALSE);
	
	return libhal_device_set_property_helper (ctx, udi, key,
						  DBUS_TYPE_STRING,
						  value, 0, 0, 0.0f, FALSE, error);
}

/**
 * libhal_device_set_property_int:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @key: name of the property
 * @value: value of the property
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Set a property of type signed integer.
 *
 * Returns: TRUE if the property was set, FALSE if the device didn't
 * exist or the property had a different type.
 */
dbus_bool_t
libhal_device_set_property_int (LibHalContext *ctx, const char *udi,
				const char *key, dbus_int32_t value, DBusError *error)
{
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", FALSE);
	
	return libhal_device_set_property_helper (ctx, udi, key,
						  DBUS_TYPE_INT32,
						  NULL, value, 0, 0.0f, FALSE, error);
}

/**
 * libhal_device_set_property_uint64:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @key: name of the property
 * @value: value of the property
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Set a property of type unsigned integer.
 *
 * Returns: TRUE if the property was set, FALSE if the device didn't
 * exist or the property had a different type.
 */
dbus_bool_t
libhal_device_set_property_uint64 (LibHalContext *ctx, const char *udi,
				   const char *key, dbus_uint64_t value, DBusError *error)
{
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", FALSE);
	
	return libhal_device_set_property_helper (ctx, udi, key,
						  DBUS_TYPE_UINT64,
						  NULL, 0, value, 0.0f, FALSE, error);
}

/**
 * libhal_device_set_property_double:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @key: name of the property
 * @value: value of the property
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Set a property of type double.
 *
 * Returns: TRUE if the property was set, FALSE if the device didn't
 * exist or the property had a different type.
 */
dbus_bool_t
libhal_device_set_property_double (LibHalContext *ctx, const char *udi,
				   const char *key, double value, DBusError *error)
{
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", FALSE);
	
	return libhal_device_set_property_helper (ctx, udi, key,
						  DBUS_TYPE_DOUBLE,
						  NULL, 0, 0, value, FALSE, error);
}

/**
 * libhal_device_set_property_bool:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @key: name of the property
 * @value: value of the property
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Set a property of type bool.
 *
 * Returns: TRUE if the property was set, FALSE if the device didn't
 * exist or the property had a different type.
 */
dbus_bool_t
libhal_device_set_property_bool (LibHalContext *ctx, const char *udi,
				 const char *key, dbus_bool_t value, DBusError *error)
{
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", FALSE);
	
	return libhal_device_set_property_helper (ctx, udi, key,
						  DBUS_TYPE_BOOLEAN,
						  NULL, 0, 0, 0.0f, value, error);
}


/**
 * libhal_device_remove_property:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @key: name of the property
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Remove a property.
 *
 * Returns: TRUE if the property was set, FALSE if the device didn't
 * exist
 */
dbus_bool_t
libhal_device_remove_property (LibHalContext *ctx, 
			       const char *udi, const char *key, DBusError *error)
{
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", FALSE);
	
	return libhal_device_set_property_helper (ctx, udi, key, DBUS_TYPE_INVALID,	
						  /* DBUS_TYPE_INVALID means remove */
						  NULL, 0, 0, 0.0f, FALSE, error);
}

/**
 * libhal_device_property_strlist_append:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @key: name of the property
 * @value: value to append to property
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Append to a property of type strlist.
 *
 * Returns: TRUE if the value was appended, FALSE if the device didn't
 * exist or the property had a different type.
 */
dbus_bool_t
libhal_device_property_strlist_append (LibHalContext *ctx, 
				       const char *udi,
				       const char *key,
				       const char *value,
				       DBusError *error)
{
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", FALSE);
	LIBHAL_CHECK_PARAM_VALID(value, "*value", FALSE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
						"org.freedesktop.Hal.Device",
						"StringListAppend");
	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}
	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &key);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &value);
	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	if (reply == NULL) {
		return FALSE;
	}

	dbus_message_unref (reply);
	return TRUE;
}

/**
 * libhal_device_property_strlist_prepend:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @key: name of the property
 * @value: value to prepend to property
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Prepend to a property of type strlist.
 *
 * Returns: TRUE if the value was prepended, FALSE if the device
 * didn't exist or the property had a different type.
 */
dbus_bool_t
libhal_device_property_strlist_prepend (LibHalContext *ctx, 
					const char *udi,
					const char *key,
					const char *value, 
					DBusError *error)
{
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", FALSE);
	LIBHAL_CHECK_PARAM_VALID(value, "*value", FALSE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
						"org.freedesktop.Hal.Device",
						"StringListPrepend");
	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}
	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &key);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &value);
	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	if (reply == NULL) {
		return FALSE;
	}

	dbus_message_unref (reply);
	return TRUE;
}

/**
 * libhal_device_property_strlist_remove_index:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @key: name of the property
 * @idx: index of string to remove in the strlist
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Remove a specified string from a property of type strlist.
 *
 * Returns: TRUE if the string was removed, FALSE if the device didn't
 * exist or the property had a different type.
 */
dbus_bool_t
libhal_device_property_strlist_remove_index (LibHalContext *ctx, 
					     const char *udi,
					     const char *key,
					     unsigned int idx,
					     DBusError *error)
{
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", FALSE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
						"org.freedesktop.Hal.Device",
						"StringListRemoveIndex");
	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}
	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &key);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &idx);
	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	if (reply == NULL) {
		return FALSE;
	}

	dbus_message_unref (reply);
	return TRUE;
}

/**
 * libhal_device_property_strlist_remove:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @key: name of the property
 * @value: the string to remove
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Remove a specified string from a property of type strlist.
 *
 * Returns: TRUE if the string was removed, FALSE if the device didn't
 * exist or the property had a different type.
 */
dbus_bool_t
libhal_device_property_strlist_remove (LibHalContext *ctx, 
				       const char *udi,
				       const char *key,
				       const char *value, DBusError *error)
{
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", FALSE);
	LIBHAL_CHECK_PARAM_VALID(value, "*value", FALSE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
						"org.freedesktop.Hal.Device",
						"StringListRemove");
	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}
	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &key);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &value);
	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	if (reply == NULL) {
		return FALSE;
	}

	dbus_message_unref (reply);
	return TRUE;
}


/**
 * libhal_device_lock:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @reason_to_lock: a user-presentable reason why the device is locked.
 * @reason_why_locked: a pointer to store the reason why the device cannot be locked on failure, or NULL
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Take an advisory lock on the device.
 *
 * Returns: TRUE if the lock was obtained, FALSE otherwise
 */
dbus_bool_t
libhal_device_lock (LibHalContext *ctx,
		    const char *udi,
		    const char *reason_to_lock,
		    char **reason_why_locked, DBusError *error)
{
	DBusMessage *message;
	DBusMessageIter iter;
	DBusMessage *reply;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);

	if (reason_why_locked != NULL)
		*reason_why_locked = NULL;

	message = dbus_message_new_method_call ("org.freedesktop.Hal",
						udi,
						"org.freedesktop.Hal.Device",
						"Lock");

	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &reason_to_lock);

	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		if (strcmp (error->name,
			    "org.freedesktop.Hal.DeviceAlreadyLocked") == 0) {
			if (reason_why_locked != NULL) {
				*reason_why_locked =
					dbus_malloc0 (strlen (error->message) + 1);
				if (*reason_why_locked == NULL)
					return FALSE;
				strcpy (*reason_why_locked, error->message);
			}
		}

		return FALSE;
	}
	if (reply == NULL)
		return FALSE;

	dbus_message_unref (reply);

	return TRUE;
}

/**
 * libhal_device_unlock:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Release an advisory lock on the device.
 *
 * Returns: TRUE if the device was successfully unlocked,
 *                              FALSE otherwise
 */
dbus_bool_t
libhal_device_unlock (LibHalContext *ctx,
		      const char *udi, DBusError *error)
{
	DBusMessage *message;
	DBusMessage *reply;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal",
						udi,
						"org.freedesktop.Hal.Device",
						"Unlock");

	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}

	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	if (reply == NULL)
		return FALSE;

	dbus_message_unref (reply);

	return TRUE;
}


/**
 * libhal_new_device:
 * @ctx: the context for the connection to hald
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Create a new device object which will be hidden from applications
 * until the CommitToGdl(), ie. libhal_device_commit_to_gdl(), method
 * is called. Note that the program invoking this method needs to run
 * with super user privileges.
 *
 * Returns: Temporary device unique id or NULL if there was a
 * problem. This string must be freed by the caller.
 */
char *
libhal_new_device (LibHalContext *ctx, DBusError *error)
{
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter reply_iter;
	char *value;
	char *dbus_str;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, NULL);

	message = dbus_message_new_method_call ("org.freedesktop.Hal",
						"/org/freedesktop/Hal/Manager",
						"org.freedesktop.Hal.Manager",
						"NewDevice");
	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return NULL;
	}

	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		return NULL;
	}
	if (reply == NULL) {
		return NULL;
	}

	dbus_message_iter_init (reply, &reply_iter);

	/* now analyze reply */
	if (dbus_message_iter_get_arg_type (&reply_iter) != DBUS_TYPE_STRING) {
		fprintf (stderr,
			 "%s %d : expected a string in reply to NewDevice\n",
			 __FILE__, __LINE__);
		dbus_message_unref (reply);
		return NULL;
	}

	dbus_message_iter_get_basic (&reply_iter, &dbus_str);
	value = (char *) ((dbus_str != NULL) ? strdup (dbus_str) : NULL);
	if (value == NULL) {
		fprintf (stderr, "%s %d : error allocating memory\n",
			 __FILE__, __LINE__);
	}

	dbus_message_unref (reply);
	return value;
}


/**
 * libhal_device_commit_to_gdl:
 * @ctx: the context for the connection to hald
 * @temp_udi: the temporary unique device id as returned by libhal_new_device()
 * @udi: the new unique device id.
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * When a hidden device has been built using the NewDevice method,
 * ie. libhal_new_device(), and the org.freedesktop.Hal.Device
 * interface this function will commit it to the global device list.
 *
 * This means that the device object will be visible to applications
 * and the HAL daemon will possibly attempt to boot the device
 * (depending on the property RequireEnable).
 *
 * Note that the program invoking this method needs to run with super
 * user privileges.
 *
 * Returns: FALSE if the given unique device id is already in use.
 */
dbus_bool_t
libhal_device_commit_to_gdl (LibHalContext *ctx, 
			     const char *temp_udi, const char *udi, DBusError *error)
{
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(temp_udi, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal",
						"/org/freedesktop/Hal/Manager",
						"org.freedesktop.Hal.Manager",
						"CommitToGdl");
	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &temp_udi);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &udi);

	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	if (reply == NULL) {
		return FALSE;
	}

	dbus_message_unref (reply);
	return TRUE;
}

/**
 * libhal_remove_device:
 * @ctx: the context for the connection to hald
 * @udi: the Unique device id.
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * This method can be invoked when a device is removed. The HAL daemon
 * will shut down the device. Note that the device may still be in the
 * device list if the Persistent property is set to true.
 *
 * Note that the program invoking this method needs to run with super
 * user privileges.
 *
 * Returns: TRUE if the device was removed, FALSE otherwise
 */
dbus_bool_t
libhal_remove_device (LibHalContext *ctx, const char *udi, DBusError *error)
{
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal",
						"/org/freedesktop/Hal/Manager",
						"org.freedesktop.Hal.Manager",
						"Remove");
	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &udi);

	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	if (reply == NULL) {
		return FALSE;
	}

	dbus_message_unref (reply);
	return TRUE;
}

/**
 * libhal_device_exists:
 * @ctx: the context for the connection to hald
 * @udi: the Unique device id.
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Determine if a device exists.
 *
 * Returns: TRUE if the device exists
 */
dbus_bool_t
libhal_device_exists (LibHalContext *ctx, const char *udi, DBusError *error)
{
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter, reply_iter;
	dbus_bool_t value;
	DBusError _error;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal",
						"/org/freedesktop/Hal/Manager",
						"org.freedesktop.Hal.Manager",
						"DeviceExists");
	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &udi);

	dbus_error_init (&_error);
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   &_error);

	dbus_message_unref (message);

	dbus_move_error (&_error, error);
	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	if (reply == NULL) {
		return FALSE;
	}

	dbus_message_iter_init (reply, &reply_iter);

	/* now analyze reply */
	if (dbus_message_iter_get_arg_type (&reply_iter) != DBUS_TYPE_BOOLEAN) {
		fprintf (stderr,
			 "%s %d : expected a bool in reply to DeviceExists\n",
			 __FILE__, __LINE__);
		dbus_message_unref (reply);
		return FALSE;
	}

	dbus_message_iter_get_basic (&reply_iter, &value);

	dbus_message_unref (reply);
	return value;
}

/**
 * libhal_device_property_exists:
 * @ctx: the context for the connection to hald
 * @udi: the Unique device id.
 * @key: name of the property
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Determine if a property on a device exists.
 *
 * Returns: TRUE if the device exists, FALSE otherwise
 */
dbus_bool_t
libhal_device_property_exists (LibHalContext *ctx, 
			       const char *udi, const char *key, DBusError *error)
{
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter, reply_iter;
	dbus_bool_t value;
	DBusError _error;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", FALSE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
						"org.freedesktop.Hal.Device",
						"PropertyExists");
	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &key);

	dbus_error_init (&_error);
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   &_error);

	dbus_message_unref (message);

	dbus_move_error (&_error, error);
	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	if (reply == NULL) {
		return FALSE;
	}

	dbus_message_iter_init (reply, &reply_iter);

	/* now analyse reply */
	if (dbus_message_iter_get_arg_type (&reply_iter) != DBUS_TYPE_BOOLEAN) {
		fprintf (stderr, "%s %d : expected a bool in reply to "
			 "PropertyExists\n", __FILE__, __LINE__);
		dbus_message_unref (reply);
		return FALSE;
	}

	dbus_message_iter_get_basic (&reply_iter, &value);

	dbus_message_unref (reply);
	return value;
}

/**
 * libhal_merge_properties:
 * @ctx: the context for the connection to hald
 * @target_udi: the Unique device id of target device to merge to
 * @source_udi: the Unique device id of device to merge from
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Merge properties from one device to another.
 *
 * Returns: TRUE if the properties were merged, FALSE otherwise
 */
dbus_bool_t
libhal_merge_properties (LibHalContext *ctx, 
			 const char *target_udi, const char *source_udi, DBusError *error)
{
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(target_udi, FALSE);
	LIBHAL_CHECK_UDI_VALID(source_udi, FALSE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal",
						"/org/freedesktop/Hal/Manager",
						"org.freedesktop.Hal.Manager",
						"MergeProperties");
	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &target_udi);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &source_udi);

	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	if (reply == NULL) {
		return FALSE;
	}

	dbus_message_unref (reply);
	return TRUE;
}

/**
 * libhal_device_matches:
 * @ctx: the context for the connection to hald
 * @udi1: the Unique Device Id for device 1
 * @udi2: the Unique Device Id for device 2
 * @property_namespace: the namespace for set of devices, e.g. "usb"
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Check a set of properties for two devices matches. 
 *
 * Checks that all properties where keys, starting with a given value
 * (namespace), of the first device is in the second device and that
 * they got the same value and type.
 *
 * Note that the other inclusion isn't tested, so there could be
 * properties (from the given namespace) in the second device not
 * present in the first device.
 *
 * Returns: TRUE if all properties starting with the given namespace
 * parameter from one device is in the other and have the same value.
 */
dbus_bool_t
libhal_device_matches (LibHalContext *ctx, 
		       const char *udi1, const char *udi2,
		       const char *property_namespace, DBusError *error)
{
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter, reply_iter;
	dbus_bool_t value;
	DBusError _error;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi1, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi2, FALSE);
	LIBHAL_CHECK_PARAM_VALID(property_namespace, "*property_namespace", FALSE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal",
						"/org/freedesktop/Hal/Manager",
						"org.freedesktop.Hal.Manager",
						"DeviceMatches");
	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, udi1);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, udi2);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, property_namespace);

	dbus_error_init (&_error);
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   &_error);

	dbus_message_unref (message);

	dbus_move_error (&_error, error);
	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	if (reply == NULL) {
		return FALSE;
	}
	/* now analyse reply */
	dbus_message_iter_init (reply, &reply_iter);

	if (dbus_message_iter_get_arg_type (&reply_iter) != DBUS_TYPE_BOOLEAN) {
		fprintf (stderr,
			 "%s %d : expected a bool in reply to DeviceMatches\n",
			 __FILE__, __LINE__);
		dbus_message_unref (reply);
		return FALSE;
	}

	dbus_message_iter_get_basic (&reply_iter, &value);

	dbus_message_unref (reply);
	return value;
}

/**
 * libhal_device_print:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Print a device to stdout; useful for debugging.
 *
 * Returns: TRUE if device's information could be obtained, FALSE otherwise
 */
dbus_bool_t
libhal_device_print (LibHalContext *ctx, const char *udi, DBusError *error)
{
	int type;
	char *key;
	LibHalPropertySet *pset;
	LibHalPropertySetIterator i;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);

	printf ("device_id = %s\n", udi);

	if ((pset = libhal_device_get_all_properties (ctx, udi, error)) == NULL)
		return FALSE;

	for (libhal_psi_init (&i, pset); libhal_psi_has_more (&i);
	     libhal_psi_next (&i)) {
		type = libhal_psi_get_type (&i);
		key = libhal_psi_get_key (&i);

		switch (type) {
		case LIBHAL_PROPERTY_TYPE_STRING:
			printf ("    %s = '%s' (string)\n", key,
				libhal_psi_get_string (&i));
			break;
		case LIBHAL_PROPERTY_TYPE_INT32:
			printf ("    %s = %d = 0x%x (int)\n", key,
				libhal_psi_get_int (&i),
				libhal_psi_get_int (&i));
			break;
		case LIBHAL_PROPERTY_TYPE_UINT64:
			printf ("    %s = %llu = 0x%llx (uint64)\n", key,
				(long long unsigned int) libhal_psi_get_uint64 (&i),
				(long long unsigned int) libhal_psi_get_uint64 (&i));
			break;
		case LIBHAL_PROPERTY_TYPE_BOOLEAN:
			printf ("    %s = %s (bool)\n", key,
				(libhal_psi_get_bool (&i) ? "true" :
				 "false"));
			break;
		case LIBHAL_PROPERTY_TYPE_DOUBLE:
			printf ("    %s = %g (double)\n", key,
				libhal_psi_get_double (&i));
			break;
		case LIBHAL_PROPERTY_TYPE_STRLIST:
		{
			unsigned int j;
			char **str_list;

			str_list = libhal_psi_get_strlist (&i);
			printf ("    %s = [", key);
			for (j = 0; str_list[j] != NULL; j++) {
				printf ("'%s'", str_list[j]);
				if (str_list[j+1] != NULL)
					printf (", ");
			}
			printf ("] (string list)\n");

			break;
		}
		default:
			printf ("    *** unknown type for key %s\n", key);
			break;
		}
	}

	libhal_free_property_set (pset);

	return TRUE;
}

/**
 * libhal_manager_find_device_string_match:
 * @ctx: the context for the connection to hald
 * @key: name of the property
 * @value: the value to match
 * @num_devices: pointer to store number of devices
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Find a device in the GDL where a single string property matches a
 * given value.
 *
 * Returns: UDI of devices; free with libhal_free_string_array()
 */
char **
libhal_manager_find_device_string_match (LibHalContext *ctx, 
					 const char *key,
					 const char *value, int *num_devices, DBusError *error)
{
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter, iter_array, reply_iter;
	char **hal_device_names;
	DBusError _error;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, NULL);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", NULL);
	LIBHAL_CHECK_PARAM_VALID(value, "*value", NULL);

	message = dbus_message_new_method_call ("org.freedesktop.Hal",
						"/org/freedesktop/Hal/Manager",
						"org.freedesktop.Hal.Manager",
						"FindDeviceStringMatch");
	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return NULL;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &key);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &value);

	dbus_error_init (&_error);
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   &_error);

	dbus_message_unref (message);

	dbus_move_error (&_error, error);
	if (error != NULL && dbus_error_is_set (error)) {
		return NULL;
	}
	if (reply == NULL) {
		return NULL;
	}
	/* now analyse reply */
	dbus_message_iter_init (reply, &reply_iter);

	if (dbus_message_iter_get_arg_type (&reply_iter) != DBUS_TYPE_ARRAY) {
		fprintf (stderr, "%s %d : wrong reply from hald.  Expecting an array.\n", __FILE__, __LINE__);
		return NULL;
	}
	
	dbus_message_iter_recurse (&reply_iter, &iter_array);

	hal_device_names = libhal_get_string_array_from_iter (&iter_array, num_devices);
		      
	dbus_message_unref (reply);
	return hal_device_names;
}


/**
 * libhal_device_add_capability:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @capability: the capability name to add
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Assign a capability to a device.
 *
 * Returns: TRUE if the capability was added, FALSE if the device didn't exist
 */
dbus_bool_t
libhal_device_add_capability (LibHalContext *ctx, 
			      const char *udi, const char *capability, DBusError *error)
{
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);
	LIBHAL_CHECK_PARAM_VALID(capability, "*capability", FALSE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
						"org.freedesktop.Hal.Device",
						"AddCapability");
	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &capability);

	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	if (reply == NULL) {
		return FALSE;
	}

	dbus_message_unref (reply);
	return TRUE;
}

/**
 * libhal_device_query_capability:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @capability: the capability name
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Check if a device has a capability. The result is undefined if the
 * device doesn't exist.
 *
 * Returns: TRUE if the device has the capability, otherwise FALSE
 */
dbus_bool_t
libhal_device_query_capability (LibHalContext *ctx, const char *udi, const char *capability, DBusError *error)
{
	char **caps;
	unsigned int i;
	dbus_bool_t ret;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);
	LIBHAL_CHECK_PARAM_VALID(capability, "*capability", FALSE);

	ret = FALSE;

	caps = libhal_device_get_property_strlist (ctx, udi, "info.capabilities", error);
	if (caps != NULL) {
		for (i = 0; caps[i] != NULL; i++) {
			if (strcmp (caps[i], capability) == 0) {
				ret = TRUE;
				break;
			}
		}
		libhal_free_string_array (caps);
	}

	return ret;
}

/**
 * libhal_find_device_by_capability:
 * @ctx: the context for the connection to hald
 * @capability: the capability name
 * @num_devices: pointer to store number of devices
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Find devices with a given capability.
 *
 * Returns: UDI of devices; free with libhal_free_string_array()
 */
char **
libhal_find_device_by_capability (LibHalContext *ctx, 
				  const char *capability, int *num_devices, DBusError *error)
{
	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter, iter_array, reply_iter;
	char **hal_device_names;
	DBusError _error;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, NULL);
	LIBHAL_CHECK_PARAM_VALID(capability, "*capability", NULL);

	message = dbus_message_new_method_call ("org.freedesktop.Hal",
						"/org/freedesktop/Hal/Manager",
						"org.freedesktop.Hal.Manager",
						"FindDeviceByCapability");
	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return NULL;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &capability);

	dbus_error_init (&_error);
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   &_error);

	dbus_message_unref (message);

	dbus_move_error (&_error, error);
	if (error != NULL && dbus_error_is_set (error)) {
		return NULL;
	}
	if (reply == NULL) {
		return NULL;
	}
	/* now analyse reply */
	dbus_message_iter_init (reply, &reply_iter);

	if (dbus_message_iter_get_arg_type (&reply_iter) != DBUS_TYPE_ARRAY) {
		fprintf (stderr, "%s %d : wrong reply from hald.  Expecting an array.\n", __FILE__, __LINE__);
		return NULL;
	}
	
	dbus_message_iter_recurse (&reply_iter, &iter_array);

	hal_device_names = libhal_get_string_array_from_iter (&iter_array, num_devices);
		      
	dbus_message_unref (reply);
	return hal_device_names;
}

/**
 * libhal_device_property_watch_all:
 * @ctx: the context for the connection to hald
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Watch all devices, ie. the device_property_changed callback is
 * invoked when the properties on any device changes.
 *
 * Returns: TRUE only if the operation succeeded
 */
dbus_bool_t
libhal_device_property_watch_all (LibHalContext *ctx, DBusError *error)
{
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);

	dbus_bus_add_match (ctx->connection,
			    "type='signal',"
			    "interface='org.freedesktop.Hal.Device',"
			    "sender='org.freedesktop.Hal'", error);
	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	return TRUE;
}


/**
 * libhal_device_add_property_watch:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Add a watch on a device, so the device_property_changed callback is
 * invoked when the properties on the given device changes.
 *
 * The application itself is responsible for deleting the watch, using
 * libhal_device_remove_property_watch, if the device is removed.
 *
 * Returns: TRUE only if the operation succeeded
 */
dbus_bool_t
libhal_device_add_property_watch (LibHalContext *ctx, const char *udi, DBusError *error)
{	
	char buf[512];
	
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);

	snprintf (buf, 512,
		  "type='signal',"
		  "interface='org.freedesktop.Hal.Device',"
		  "sender='org.freedesktop.Hal'," "path=%s", udi);

	dbus_bus_add_match (ctx->connection, buf, error);
	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	return TRUE;
}


/**
 * libhal_device_remove_property_watch:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Remove a watch on a device.
 *
 * Returns: TRUE only if the operation succeeded
 */
dbus_bool_t
libhal_device_remove_property_watch (LibHalContext *ctx, const char *udi, DBusError *error)
{	
	char buf[512];
	
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);

	snprintf (buf, 512,
		  "type='signal',"
		  "interface='org.freedesktop.Hal.Device',"
		  "sender='org.freedesktop.Hal'," "path=%s", udi);

	dbus_bus_remove_match (ctx->connection, buf, error);
	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	return TRUE;
}


/**
 * libhal_ctx_new:
 *
 * Create a new LibHalContext
 *
 * Returns: a new uninitialized LibHalContext object
 */
LibHalContext *
libhal_ctx_new (void)
{
	LibHalContext *ctx;

	ctx = calloc (1, sizeof (LibHalContext));
	if (ctx == NULL) {
		fprintf (stderr, 
			 "%s %d : Failed to allocate %lu bytes\n",
			 __FILE__, __LINE__, (unsigned long) sizeof (LibHalContext));
		return NULL;
	}

	ctx->is_initialized = FALSE;
	ctx->is_shutdown = FALSE;
	ctx->connection = NULL;
	ctx->is_direct = FALSE;

	return ctx;
}

/**
 * libhal_ctx_set_cache:
 * @ctx: context to enable/disable cache for
 * @use_cache: whether or not to use cache
 *
 * Enable or disable caching. Note: Caching is not actually
 * implemented yet.
 *
 * Returns: TRUE if cache was successfully enabled/disabled, FALSE otherwise
 */
dbus_bool_t
libhal_ctx_set_cache (LibHalContext *ctx, dbus_bool_t use_cache)
{
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);

	ctx->cache_enabled = use_cache;
	return TRUE;
}

/**
 * libhal_ctx_set_dbus_connection:
 * @ctx: context to set connection for
 * @conn: DBus connection to use
 *
 * Set DBus connection to use to talk to hald.
 *
 * Returns: TRUE if connection was successfully set, FALSE otherwise
 */
dbus_bool_t
libhal_ctx_set_dbus_connection (LibHalContext *ctx, DBusConnection *conn)
{
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);

	if (conn == NULL)
		return FALSE;

	ctx->connection = conn;
	return TRUE;
}

/**
 * libhal_ctx_get_dbus_connection:
 * @ctx: context to get connection for
 *
 * Get DBus connection used for talking to hald.
 *
 * Returns: DBus connection to use or NULL
 */
DBusConnection *
libhal_ctx_get_dbus_connection (LibHalContext *ctx)
{
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, NULL);

	return ctx->connection;
}


/**
 * libhal_ctx_init:
 * @ctx: Context for connection to hald (D-BUS connection should be set with libhal_ctx_set_dbus_connection)
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Initialize the connection to hald.
 *
 * Returns: TRUE if initialization succeeds, FALSE otherwise
 */
dbus_bool_t 
libhal_ctx_init (LibHalContext *ctx, DBusError *error)
{
	DBusError _error;
	dbus_bool_t hald_exists;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);

	if (ctx->connection == NULL)
		return FALSE;

	dbus_error_init (&_error);
	hald_exists = dbus_bus_name_has_owner (ctx->connection, "org.freedesktop.Hal", &_error);
	dbus_move_error (&_error, error);
	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}

	if (!hald_exists) {
		return FALSE;
	}

	
	if (!dbus_connection_add_filter (ctx->connection, filter_func, ctx, NULL)) {
		return FALSE;
	}

	dbus_bus_add_match (ctx->connection, 
			    "type='signal',"
			    "interface='org.freedesktop.Hal.Manager',"
			    "sender='org.freedesktop.Hal',"
			    "path='/org/freedesktop/Hal/Manager'", &_error);
	dbus_move_error (&_error, error);
	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	ctx->is_initialized = TRUE;
	ctx->is_direct = FALSE;

	return TRUE;
}

/**
 * libhal_ctx_init_direct:
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Create an already initialized connection to hald. This function should only be used by HAL helpers.
 *
 * Returns: A pointer to an already initialized LibHalContext
 */
LibHalContext *
libhal_ctx_init_direct (DBusError *error)
{
	char *hald_addr;
	LibHalContext *ctx;
	DBusError _error;

	ctx = libhal_ctx_new ();
	if (ctx == NULL)
		goto out;

	if (((hald_addr = getenv ("HALD_DIRECT_ADDR"))) == NULL) {
		libhal_ctx_free (ctx);
		ctx = NULL;
		goto out;
	}

	dbus_error_init (&_error);
	ctx->connection = dbus_connection_open (hald_addr, &_error);
	dbus_move_error (&_error, error);
	if (error != NULL && dbus_error_is_set (error)) {
		libhal_ctx_free (ctx);
		ctx = NULL;
		goto out;
	}

	if (!dbus_connection_add_filter (ctx->connection, filter_func, ctx, NULL)) {
		libhal_ctx_free (ctx);
		ctx = NULL;
		goto out;
	}


	ctx->is_initialized = TRUE;
	ctx->is_direct = TRUE;

out:
	return ctx;
}

/**
 * libhal_ctx_shutdown:
 * @ctx: the context for the connection to hald
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Shut down a connection to hald.
 *
 * Returns: TRUE if connection successfully shut down, FALSE otherwise
 */
dbus_bool_t    
libhal_ctx_shutdown (LibHalContext *ctx, DBusError *error)
{	
	DBusError myerror;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);

	if (ctx->is_direct) {
		/* for some reason dbus_connection_set_exit_on_disconnect doesn't work yet so don't unref */
		/*dbus_connection_unref (ctx->connection);*/
	} else {
		dbus_error_init (&myerror);
		dbus_bus_remove_match (ctx->connection, 
				       "type='signal',"
				       "interface='org.freedesktop.Hal.Manager',"
				       "sender='org.freedesktop.Hal',"
				       "path='/org/freedesktop/Hal/Manager'", &myerror);
		dbus_move_error (&myerror, error);
		if (error != NULL && dbus_error_is_set (error)) {
			fprintf (stderr, "%s %d : Error unsubscribing to signals, error=%s\n", 
				 __FILE__, __LINE__, error->message);
			/** @todo  clean up */
		}

		/* TODO: remove other matches */

		dbus_connection_remove_filter (ctx->connection, filter_func, ctx);
	}

	ctx->is_initialized = FALSE;

	return TRUE;
}

/**
 * libhal_ctx_free:
 * @ctx: pointer to a LibHalContext
 *
 * Free a LibHalContext resource.
 *
 * Returns: TRUE
 */
dbus_bool_t    
libhal_ctx_free (LibHalContext *ctx)
{
	free (ctx);
	return TRUE;
}

/**
 * libhal_ctx_set_device_added:
 * @ctx: the context for the connection to hald
 * @callback: the function to call when a device is added
 *
 * Set the callback for when a device is added
 *
 * Returns: TRUE if callback was successfully set, FALSE otherwise
 */
dbus_bool_t
libhal_ctx_set_device_added (LibHalContext *ctx, LibHalDeviceAdded callback)
{
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);

	ctx->device_added = callback;
	return TRUE;
}

/**
 * libhal_ctx_set_device_removed:
 * @ctx: the context for the connection to hald
 * @callback: the function to call when a device is removed
 *
 * Set the callback for when a device is removed.
 *
 * Returns: TRUE if callback was successfully set, FALSE otherwise
 */
dbus_bool_t
libhal_ctx_set_device_removed (LibHalContext *ctx, LibHalDeviceRemoved callback)
{
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);

	ctx->device_removed = callback;
	return TRUE;
}

/**
 * libhal_ctx_set_device_new_capability:
 * @ctx: the context for the connection to hald
 * @callback: the function to call when a device gains a new capability
 *
 * Set the callback for when a device gains a new capability.
 *
 * Returns: TRUE if callback was successfully set, FALSE otherwise
 */
dbus_bool_t
libhal_ctx_set_device_new_capability (LibHalContext *ctx, LibHalDeviceNewCapability callback)
{
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);

	ctx->device_new_capability = callback;
	return TRUE;
}

/**
 * libhal_ctx_set_device_lost_capability:
 * @ctx: the context for the connection to hald
 * @callback: the function to call when a device loses a capability
 *
 * Set the callback for when a device loses a capability
 *
 * Returns: TRUE if callback was successfully set, FALSE otherwise
 */
dbus_bool_t
libhal_ctx_set_device_lost_capability (LibHalContext *ctx, LibHalDeviceLostCapability callback)
{
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);

	ctx->device_lost_capability = callback;
	return TRUE;
}

/**
 * libhal_ctx_set_device_property_modified:
 * @ctx: the context for the connection to hald
 * @callback: the function to call when a property is modified on a device
 *
 * Set the callback for when a property is modified on a device.
 *
 * Returns: TRUE if callback was successfully set, FALSE otherwise
 */
dbus_bool_t
libhal_ctx_set_device_property_modified (LibHalContext *ctx, LibHalDevicePropertyModified callback)
{
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);

	ctx->device_property_modified = callback;
	return TRUE;
}

/**
 * libhal_ctx_set_device_condition:
 * @ctx: the context for the connection to hald
 * @callback: the function to call when a device emits a condition
 *
 * Set the callback for when a device emits a condition
 *
 * Returns: TRUE if callback was successfully set, FALSE otherwise
 */
dbus_bool_t
libhal_ctx_set_device_condition (LibHalContext *ctx, LibHalDeviceCondition callback)
{
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);

	ctx->device_condition = callback;
	return TRUE;
}

/**
 * libhal_ctx_set_singleton_device_added:
 * @ctx: the context for the connection to hald
 * @callback: the function to call when a device emits a condition
 *
 * Set the callback for when a singleton should handle a new device
 *
 * Returns: TRUE if callback was successfully set, FALSE otherwise
 */
dbus_bool_t
libhal_ctx_set_singleton_device_added (LibHalContext *ctx, LibHalSingletonDeviceAdded callback)
{
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);

	ctx->singleton_device_added = callback;
	return TRUE;
}

/**
 * libhal_ctx_set_singleton_device_removed:
 * @ctx: the context for the connection to hald
 * @callback: the function to call when a device emits a condition
 *
 * Set the callback for when a singleton should discard a device
 *
 * Returns: TRUE if callback was successfully set, FALSE otherwise
 */
dbus_bool_t
libhal_ctx_set_singleton_device_removed (LibHalContext *ctx, LibHalSingletonDeviceRemoved callback)
{
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);

	ctx->singleton_device_removed = callback;
	return TRUE;
}

/**
 * libhal_string_array_length:
 * @str_array: array of strings to consider
 *
 * Get the length of an array of strings.
 *
 * Returns: Number of strings in array
 */
unsigned int 
libhal_string_array_length (char **str_array)
{
	unsigned int i;

	if (str_array == NULL)
		return 0;

	for (i = 0; str_array[i] != NULL; i++)
		;

	return i;
}


/**
 * libhal_device_rescan:
 * @ctx: the context for the connection to hald
 * @udi: the Unique id of device
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * TODO document me.
 *
 * Returns: Whether the operation succeeded
 */
dbus_bool_t 
libhal_device_rescan (LibHalContext *ctx, const char *udi, DBusError *error)
{
	DBusMessage *message;
	DBusMessageIter reply_iter;
	DBusMessage *reply;
	dbus_bool_t result;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
						"org.freedesktop.Hal.Device",
						"Rescan");

	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}
	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	if (reply == NULL)
		return FALSE;

	dbus_message_iter_init (reply, &reply_iter);
	if (dbus_message_iter_get_arg_type (&reply_iter) !=
		   DBUS_TYPE_BOOLEAN) {
		dbus_message_unref (reply);
		return FALSE;
	}
	dbus_message_iter_get_basic (&reply_iter, &result);

	dbus_message_unref (reply);

	return result;
}

/**
 * libhal_device_reprobe:
 * @ctx: the context for the connection to hald
 * @udi: the Unique id of device
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * TODO document me.
 *
 * Returns: Whether the operation succeeded
 */
dbus_bool_t
libhal_device_reprobe (LibHalContext *ctx, const char *udi, DBusError *error)
{
	DBusMessage *message;
	DBusMessageIter reply_iter;
	DBusMessage *reply;
	dbus_bool_t result;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal",
						udi,
						"org.freedesktop.Hal.Device",
						"Reprobe");

	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}
	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	if (reply == NULL)
		return FALSE;

	dbus_message_iter_init (reply, &reply_iter);
	if (dbus_message_iter_get_arg_type (&reply_iter) !=
		   DBUS_TYPE_BOOLEAN) {
		dbus_message_unref (reply);
		return FALSE;
	}
	dbus_message_iter_get_basic (&reply_iter, &result);

	dbus_message_unref (reply);

	return result;
}

/**
 * libhal_device_emit_condition:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @condition_name: user-readable name of condition
 * @condition_details: user-readable details of condition
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Emit a condition from a device. Can only be used from hald helpers.
 *
 * Returns: TRUE if condition successfully emitted,
 *                              FALSE otherwise
 */
dbus_bool_t libhal_device_emit_condition (LibHalContext *ctx,
					  const char *udi,
					  const char *condition_name,
					  const char *condition_details,
					  DBusError *error)
{
	DBusMessage *message;
	DBusMessageIter iter;
	DBusMessageIter reply_iter;
	DBusMessage *reply;
	dbus_bool_t result;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);
	LIBHAL_CHECK_PARAM_VALID(condition_name, "*condition_name", FALSE);
	LIBHAL_CHECK_PARAM_VALID(condition_details, "*condition_details", FALSE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal",
						udi,
						"org.freedesktop.Hal.Device",
						"EmitCondition");

	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &condition_name);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &condition_details);
	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		fprintf (stderr,
			 "%s %d : Failure sending D-BUS message: %s: %s\n",
			 __FILE__, __LINE__, error->name, error->message);
		return FALSE;
	}

	if (reply == NULL) {
		fprintf (stderr,
			 "%s %d : Got no reply\n",
			 __FILE__, __LINE__);
		return FALSE;
	}

	dbus_message_iter_init (reply, &reply_iter);
	if (dbus_message_iter_get_arg_type (&reply_iter) !=
		   DBUS_TYPE_BOOLEAN) {
		dbus_message_unref (reply);
		fprintf (stderr,
			 "%s %d : Malformed reply\n",
			 __FILE__, __LINE__);
		return FALSE;
	}
	dbus_message_iter_get_basic (&reply_iter, &result);

	dbus_message_unref (reply);

	return result;	
}

static dbus_bool_t
addon_is_ready(LibHalContext *ctx, const char *identifier,
	       dbus_bool_t singleton, DBusError *error)
{
	DBusMessage *message;
	DBusMessageIter iter;
	DBusMessage *reply;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_PARAM_VALID(identifier, "*identifier", FALSE);

        if (singleton) {
        	message = dbus_message_new_method_call ("org.freedesktop.Hal",
							"/org/freedesktop/Hal/Manager",
							"org.freedesktop.Hal.Manager",
							"SingletonAddonIsReady");
        } else {
        	message = dbus_message_new_method_call ("org.freedesktop.Hal",
							identifier,
							"org.freedesktop.Hal.Device",
							"AddonIsReady");
        }

	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}

	dbus_message_iter_init_append (message, &iter);

	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &identifier);

	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	if (reply == NULL)
		return FALSE;

	dbus_message_unref (reply);
	return TRUE;
}


/**
 * libhal_device_addon_is_ready:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id this addon is handling
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * HAL addon's must call this method when they are done initializing the device object. The HAL
 * daemon will wait for all addon's to call this.
 *
 * Can only be used from hald helpers.
 *
 * Returns: TRUE if the HAL daemon received the message, FALSE otherwise
 */
dbus_bool_t
libhal_device_addon_is_ready (LibHalContext *ctx,
			      const char *udi,
			      DBusError *error)
{

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);

	return addon_is_ready (ctx, udi, FALSE, error);
}

/**
 * libhal_device_singleton_addon_is_ready:
 * @ctx: the context for the connection to hald
 * @command_line: commandline singleton was started with
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * HAL singleton addon's must call this method when they are done initializing the device object. The HAL
 * daemon will wait for all addon's to call this.
 *
 * Can only be used from hald helpers.
 *
 * Returns: TRUE if the HAL daemon received the message, FALSE otherwise
 */
dbus_bool_t
libhal_device_singleton_addon_is_ready (LibHalContext *ctx,
					const char *command_line,
					DBusError *error)
{
	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_PARAM_VALID(command_line, "*command_line", FALSE);
		
	return addon_is_ready (ctx, command_line, TRUE, error);
}


/**
 * libhal_device_claim_interface:
 * @ctx: the context for the connection to hald
 * @udi: the Unique Device Id
 * @interface_name: Name of interface to claim, e.g. org.freedesktop.Hal.Device.FoobarKindOfThing
 * @introspection_xml: Introspection XML containing what would be inside the interface XML tag
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 *
 * Claim an interface for a device. All messages to this interface
 * will be forwarded to the helper. Can only be used from hald
 * helpers.
 *
 * Returns: TRUE if interface was claimed, FALSE otherwise
 */
dbus_bool_t
libhal_device_claim_interface (LibHalContext *ctx,
			       const char *udi,
			       const char *interface_name,
			       const char *introspection_xml,
			       DBusError *error)
{
	DBusMessage *message;
	DBusMessageIter iter;
	DBusMessageIter reply_iter;
	DBusMessage *reply;
	dbus_bool_t result;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);
	LIBHAL_CHECK_PARAM_VALID(interface_name, "*interface_name", FALSE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal",
						udi,
						"org.freedesktop.Hal.Device",
						"ClaimInterface");

	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &interface_name);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &introspection_xml);
	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	if (reply == NULL)
		return FALSE;

	dbus_message_iter_init (reply, &reply_iter);
	if (dbus_message_iter_get_arg_type (&reply_iter) !=
		   DBUS_TYPE_BOOLEAN) {
		dbus_message_unref (reply);
		return FALSE;
	}
	dbus_message_iter_get_basic (&reply_iter, &result);

	dbus_message_unref (reply);

	return result;	
}



struct LibHalChangeSetElement_s;

typedef struct LibHalChangeSetElement_s LibHalChangeSetElement;

struct LibHalChangeSetElement_s {
	char *key;
	int change_type;
	union {
		char *val_str;
		dbus_int32_t val_int;
		dbus_uint64_t val_uint64;
		double val_double;
		dbus_bool_t val_bool;
		char **val_strlist;
	} value;
	LibHalChangeSetElement *next;
	LibHalChangeSetElement *prev;
};

struct LibHalChangeSet_s {
	char *udi;
	LibHalChangeSetElement *head;
	LibHalChangeSetElement *tail;
};

/**
 * libhal_device_new_changeset:
 * @udi: unique device identifier
 *
 * Request a new changeset object. Used for changing multiple properties at once. Useful when
 * performance is critical and also for atomically updating several properties.
 * 
 * Returns: A new changeset object or NULL on error
 */
LibHalChangeSet *
libhal_device_new_changeset (const char *udi)
{
	LibHalChangeSet *changeset;

	LIBHAL_CHECK_UDI_VALID(udi, NULL);

	changeset = calloc (1, sizeof (LibHalChangeSet));
	if (changeset == NULL)
		goto out;

	changeset->udi = strdup (udi);
	if (changeset->udi == NULL) {
		free (changeset);
		changeset = NULL;
		goto out;
	}

	changeset->head = NULL;
	changeset->tail = NULL;

out:
	return changeset;
}

static void
libhal_changeset_append (LibHalChangeSet *changeset, LibHalChangeSetElement *elem)
{
	LIBHAL_CHECK_PARAM_VALID(changeset, "*changeset", (void) NULL );
	LIBHAL_CHECK_PARAM_VALID(elem, "*elem", (void) NULL);

	if (changeset->head == NULL) {
		changeset->head = elem;
		changeset->tail = elem;
		elem->next = NULL;
		elem->prev = NULL;
	} else {
		elem->prev = changeset->tail;
		elem->next = NULL;
		elem->prev->next = elem;
		changeset->tail = elem;
	}
}


/**
 * libhal_changeset_set_property_string:
 * @changeset: the changeset
 * @key: key of property 
 * @value: the value to set
 * 
 * Set a property.
 * 
 * Returns: FALSE on OOM
 */
dbus_bool_t
libhal_changeset_set_property_string (LibHalChangeSet *changeset, const char *key, const char *value)
{
	LibHalChangeSetElement *elem;

	LIBHAL_CHECK_PARAM_VALID(changeset, "*changeset", FALSE);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", FALSE);
	LIBHAL_CHECK_PARAM_VALID(value, "*value", FALSE);

	elem = calloc (1, sizeof (LibHalChangeSetElement));
	if (elem == NULL)
		goto out;
	elem->key = strdup (key);
	if (elem->key == NULL) {
		free (elem);
		elem = NULL;
		goto out;
	}

	elem->change_type = LIBHAL_PROPERTY_TYPE_STRING;
	elem->value.val_str = strdup (value);
	if (elem->value.val_str == NULL) {
		free (elem->key);
		free (elem);
		elem = NULL;
		goto out;
	}

	libhal_changeset_append (changeset, elem);
out:
	return elem != NULL;
}

/**
 * libhal_changeset_set_property_int:
 * @changeset: the changeset
 * @key: key of property 
 * @value: the value to set
 * 
 * Set a property.
 * 
 * Returns: FALSE on OOM
 */
dbus_bool_t
libhal_changeset_set_property_int (LibHalChangeSet *changeset, const char *key, dbus_int32_t value)
{
	LibHalChangeSetElement *elem;

	LIBHAL_CHECK_PARAM_VALID(changeset, "*changeset", FALSE);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", FALSE);

	elem = calloc (1, sizeof (LibHalChangeSetElement));
	if (elem == NULL)
		goto out;
	elem->key = strdup (key);
	if (elem->key == NULL) {
		free (elem);
		elem = NULL;
		goto out;
	}

	elem->change_type = LIBHAL_PROPERTY_TYPE_INT32;
	elem->value.val_int = value;

	libhal_changeset_append (changeset, elem);
out:
	return elem != NULL;
}

/**
 * libhal_changeset_set_property_uint64:
 * @changeset: the changeset
 * @key: key of property 
 * @value: the value to set
 * 
 * Set a property.
 * 
 * Returns: FALSE on OOM
 */
dbus_bool_t
libhal_changeset_set_property_uint64 (LibHalChangeSet *changeset, const char *key, dbus_uint64_t value)
{
	LibHalChangeSetElement *elem;

	LIBHAL_CHECK_PARAM_VALID(changeset, "*changeset", FALSE);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", FALSE);

	elem = calloc (1, sizeof (LibHalChangeSetElement));
	if (elem == NULL)
		goto out;
	elem->key = strdup (key);
	if (elem->key == NULL) {
		free (elem);
		elem = NULL;
		goto out;
	}

	elem->change_type = LIBHAL_PROPERTY_TYPE_UINT64;
	elem->value.val_uint64 = value;

	libhal_changeset_append (changeset, elem);
out:
	return elem != NULL;
}

/**
 * libhal_changeset_set_property_double:
 * @changeset: the changeset
 * @key: key of property 
 * @value: the value to set
 * 
 * Set a property.
 * 
 * Returns: FALSE on OOM
 */
dbus_bool_t
libhal_changeset_set_property_double (LibHalChangeSet *changeset, const char *key, double value)
{
	LibHalChangeSetElement *elem;

	LIBHAL_CHECK_PARAM_VALID(changeset, "*changeset", FALSE);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", FALSE);

	elem = calloc (1, sizeof (LibHalChangeSetElement));
	if (elem == NULL)
		goto out;
	elem->key = strdup (key);
	if (elem->key == NULL) {
		free (elem);
		elem = NULL;
		goto out;
	}

	elem->change_type = LIBHAL_PROPERTY_TYPE_DOUBLE;
	elem->value.val_double = value;

	libhal_changeset_append (changeset, elem);
out:
	return elem != NULL;
}

/**
 * libhal_changeset_set_property_bool:
 * @changeset: the changeset
 * @key: key of property 
 * @value: the value to set
 * 
 * Set a property.
 * 
 * Returns: FALSE on OOM
 */
dbus_bool_t
libhal_changeset_set_property_bool (LibHalChangeSet *changeset, const char *key, dbus_bool_t value)
{
	LibHalChangeSetElement *elem;

	LIBHAL_CHECK_PARAM_VALID(changeset, "*changeset", FALSE);
	LIBHAL_CHECK_PARAM_VALID(key, "*key", FALSE);

	elem = calloc (1, sizeof (LibHalChangeSetElement));
	if (elem == NULL)
		goto out;
	elem->key = strdup (key);
	if (elem->key == NULL) {
		free (elem);
		elem = NULL;
		goto out;
	}

	elem->change_type = LIBHAL_PROPERTY_TYPE_BOOLEAN;
	elem->value.val_bool = value;

	libhal_changeset_append (changeset, elem);
out:
	return elem != NULL;
}

/**
 * libhal_changeset_set_property_strlist:
 * @changeset: the changeset
 * @key: key of property 
 * @value: the value to set - NULL terminated array of strings
 * 
 * Set a property.
 * 
 * Returns: FALSE on OOM
 */
dbus_bool_t
libhal_changeset_set_property_strlist (LibHalChangeSet *changeset, const char *key, const char **value)
{
	LibHalChangeSetElement *elem;
	char **value_copy;
	int len;
	int i, j;

        LIBHAL_CHECK_PARAM_VALID(changeset, "*changeset", FALSE);
        LIBHAL_CHECK_PARAM_VALID(key, "*key", FALSE);

	elem = calloc (1, sizeof (LibHalChangeSetElement));
	if (elem == NULL)
		goto out;
	elem->key = strdup (key);
	if (elem->key == NULL) {
		free (elem);
		elem = NULL;
		goto out;
	}

	for (i = 0; value[i] != NULL; i++)
		;
	len = i;

	value_copy = calloc (len + 1, sizeof (char *));
	if (value_copy == NULL) {
		free (elem->key);
		free (elem);
		elem = NULL;
		goto out;
	}

	for (i = 0; i < len; i++) {
		value_copy[i] = strdup (value[i]);
		if (value_copy[i] == NULL) {
			for (j = 0; j < i; j++) {
				free (value_copy[j]);
			}
			free (value_copy);
			free (elem->key);
			free (elem);
			elem = NULL;
			goto out;
		}
	}
	value_copy[i] = NULL;

	elem->change_type = LIBHAL_PROPERTY_TYPE_STRLIST;
	elem->value.val_strlist = value_copy;

	libhal_changeset_append (changeset, elem);
out:
	return elem != NULL;
}

/**
 * libhal_device_commit_changeset:
 * @ctx: the context for the connection to hald
 * @changeset: the changeset to commit
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 * 
 * Commit a changeset to the daemon.
 * 
 * Returns: True if the changeset was committed on the daemon side
 */
dbus_bool_t
libhal_device_commit_changeset (LibHalContext *ctx, LibHalChangeSet *changeset, DBusError *error)
{
	LibHalChangeSetElement *elem;
	DBusMessage *message;
	DBusMessage *reply;
	DBusError _error;
	DBusMessageIter iter;
	DBusMessageIter sub;
	DBusMessageIter sub2;
	DBusMessageIter sub3;
	DBusMessageIter sub4;
	int i;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(changeset->udi, FALSE);

	if (changeset->head == NULL) {
		return TRUE;
	}

	message = dbus_message_new_method_call ("org.freedesktop.Hal", changeset->udi,
						"org.freedesktop.Hal.Device",
						"SetMultipleProperties");

	if (message == NULL) {
		fprintf (stderr, "%s %d : Couldn't allocate D-BUS message\n", __FILE__, __LINE__);
		return FALSE;
	}

	dbus_message_iter_init_append (message, &iter);

	dbus_message_iter_open_container (&iter, 
					  DBUS_TYPE_ARRAY,
					  DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
					  DBUS_TYPE_STRING_AS_STRING
					  DBUS_TYPE_VARIANT_AS_STRING
					  DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
					  &sub);

	for (elem = changeset->head; elem != NULL; elem = elem->next) {
		dbus_message_iter_open_container (&sub,
						  DBUS_TYPE_DICT_ENTRY,
						  NULL,
						  &sub2);
		dbus_message_iter_append_basic (&sub2, DBUS_TYPE_STRING, &(elem->key));

		switch (elem->change_type) {
		case LIBHAL_PROPERTY_TYPE_STRING:
			dbus_message_iter_open_container (&sub2, DBUS_TYPE_VARIANT, DBUS_TYPE_STRING_AS_STRING, &sub3);
			dbus_message_iter_append_basic (&sub3, DBUS_TYPE_STRING, &(elem->value.val_str));
			dbus_message_iter_close_container (&sub2, &sub3);
			break;
		case LIBHAL_PROPERTY_TYPE_STRLIST:
			dbus_message_iter_open_container (&sub2, DBUS_TYPE_VARIANT, 
							  DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING, &sub3);
			dbus_message_iter_open_container (&sub3, DBUS_TYPE_ARRAY, 
							  DBUS_TYPE_STRING_AS_STRING, &sub4);
			for (i = 0; elem->value.val_strlist[i] != NULL; i++) {
				dbus_message_iter_append_basic (&sub4, DBUS_TYPE_STRING, 
								&(elem->value.val_strlist[i]));
			}
			dbus_message_iter_close_container (&sub3, &sub4);
			dbus_message_iter_close_container (&sub2, &sub3);
			break;
		case LIBHAL_PROPERTY_TYPE_INT32:
			dbus_message_iter_open_container (&sub2, DBUS_TYPE_VARIANT, DBUS_TYPE_INT32_AS_STRING, &sub3);
			dbus_message_iter_append_basic (&sub3, DBUS_TYPE_INT32, &(elem->value.val_int));
			dbus_message_iter_close_container (&sub2, &sub3);
			break;
		case LIBHAL_PROPERTY_TYPE_UINT64:
			dbus_message_iter_open_container (&sub2, DBUS_TYPE_VARIANT, DBUS_TYPE_UINT64_AS_STRING, &sub3);
			dbus_message_iter_append_basic (&sub3, DBUS_TYPE_UINT64, &(elem->value.val_uint64));
			dbus_message_iter_close_container (&sub2, &sub3);
			break;
		case LIBHAL_PROPERTY_TYPE_DOUBLE:
			dbus_message_iter_open_container (&sub2, DBUS_TYPE_VARIANT, DBUS_TYPE_DOUBLE_AS_STRING, &sub3);
			dbus_message_iter_append_basic (&sub3, DBUS_TYPE_DOUBLE, &(elem->value.val_double));
			dbus_message_iter_close_container (&sub2, &sub3);
			break;
		case LIBHAL_PROPERTY_TYPE_BOOLEAN:
			dbus_message_iter_open_container (&sub2, DBUS_TYPE_VARIANT, DBUS_TYPE_BOOLEAN_AS_STRING,&sub3);
			dbus_message_iter_append_basic (&sub3, DBUS_TYPE_BOOLEAN, &(elem->value.val_bool));
			dbus_message_iter_close_container (&sub2, &sub3);
			break;
		default:
			fprintf (stderr, "%s %d : unknown change_type %d\n", __FILE__, __LINE__, elem->change_type);
			break;
		}
		dbus_message_iter_close_container (&sub, &sub2);
	}

	dbus_message_iter_close_container (&iter, &sub);

	
	dbus_error_init (&_error);
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   &_error);

	dbus_message_unref (message);

	dbus_move_error (&_error, error);
	if (error != NULL && dbus_error_is_set (error)) {
		fprintf (stderr,
			 "%s %d : %s\n",
			 __FILE__, __LINE__, error->message);

		return FALSE;
	}
	if (reply == NULL) {
		return FALSE;
	}

	dbus_message_unref (reply);
	return TRUE;
}

/**
 * libhal_device_free_changeset:
 * @changeset: the changeset to free
 * 
 * Free a changeset.
 */
void
libhal_device_free_changeset (LibHalChangeSet *changeset)
{
	LibHalChangeSetElement *elem;
	LibHalChangeSetElement *elem2;

	for (elem = changeset->head; elem != NULL; elem = elem2) {
		elem2 = elem->next;

		switch (elem->change_type) {
		case LIBHAL_PROPERTY_TYPE_STRING:
			free (elem->value.val_str);
			break;
		case LIBHAL_PROPERTY_TYPE_STRLIST:
			libhal_free_string_array (elem->value.val_strlist);
			break;
                /* explicit fallthrough */
		case LIBHAL_PROPERTY_TYPE_INT32:
		case LIBHAL_PROPERTY_TYPE_UINT64:
		case LIBHAL_PROPERTY_TYPE_DOUBLE:
		case LIBHAL_PROPERTY_TYPE_BOOLEAN:
			break;
		default:
			fprintf (stderr, "%s %d : unknown change_type %d\n", __FILE__, __LINE__, elem->change_type);
			break;
		}
		free (elem->key);
		free (elem);
	}

	free (changeset->udi);
	free (changeset);
}


/**
 * libhal_device_acquire_interface_lock:
 * @ctx: the context for the connection to hald
 * @udi: the Unique id of device
 * @interface: the intername name to lock
 * @exclusive: whether the lock should be exclusive
 * @error: pointer to an initialized dbus error object for returning errors
 * 
 * Releases a lock on an interface for a specific device. 
 * 
 * Returns: TRUE iff the lock was acquired
 **/
dbus_bool_t 
libhal_device_acquire_interface_lock (LibHalContext *ctx,
                                      const char *udi,
                                      const char *interface,
                                      dbus_bool_t exclusive,
                                      DBusError *error)
{
	DBusMessage *message;
	DBusMessageIter iter;
	DBusMessage *reply;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);
	LIBHAL_CHECK_PARAM_VALID(interface, "*interface", FALSE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal",
						udi,
						"org.freedesktop.Hal.Device",
						"AcquireInterfaceLock");

	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &interface);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &exclusive);
	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	if (reply == NULL)
		return FALSE;

	dbus_message_unref (reply);
	return TRUE;
}

/**
 * libhal_device_release_interface_lock:
 * @ctx: the context for the connection to hald
 * @udi: the Unique id of device
 * @interface: the intername name to unlock
 * @error: pointer to an initialized dbus error object for returning errors
 * 
 * Acquires a lock on an interface for a specific device.
 * 
 * Returns: TRUE iff the lock was released.
 **/
dbus_bool_t libhal_device_release_interface_lock (LibHalContext *ctx,
                                                  const char *udi,
                                                  const char *interface,
                                                  DBusError *error)
{
	DBusMessage *message;
	DBusMessageIter iter;
	DBusMessage *reply;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_UDI_VALID(udi, FALSE);
	LIBHAL_CHECK_PARAM_VALID(interface, "*interface", FALSE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal",
						udi,
						"org.freedesktop.Hal.Device",
						"ReleaseInterfaceLock");

	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &interface);
	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	if (reply == NULL)
		return FALSE;

	dbus_message_unref (reply);
	return TRUE;
}

/**
 * libhal_acquire_global_interface_lock:
 * @ctx: the context for the connection to hald
 * @interface: the intername name to lock
 * @exclusive: whether the lock should be exclusive
 * @error: pointer to an initialized dbus error object for returning errors
 * 
 * Acquires a global lock on an interface.
 * 
 * Returns: TRUE iff the lock was acquired
 **/
dbus_bool_t libhal_acquire_global_interface_lock (LibHalContext *ctx,
                                                  const char *interface,
                                                  dbus_bool_t exclusive,
                                                  DBusError *error)
{
	DBusMessage *message;
	DBusMessageIter iter;
	DBusMessage *reply;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_PARAM_VALID(interface, "*interface", FALSE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal",
						"/org/freedesktop/Hal/Manager",
						"org.freedesktop.Hal.Manager",
						"AcquireGlobalInterfaceLock");

	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &interface);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &exclusive);
	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	if (reply == NULL)
		return FALSE;

	dbus_message_unref (reply);
	return TRUE;
}

/**
 * libhal_release_global_interface_lock:
 * @ctx: the context for the connection to hald
 * @interface: the intername name to unlock
 * @error: pointer to an initialized dbus error object for returning errors
 * 
 * Releases a global lock on an interface.
 * 
 * Returns: TRUE iff the lock was released
 **/
dbus_bool_t libhal_release_global_interface_lock (LibHalContext *ctx,
                                                  const char *interface,
                                                  DBusError *error)
{
	DBusMessage *message;
	DBusMessageIter iter;
	DBusMessage *reply;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, FALSE);
	LIBHAL_CHECK_PARAM_VALID(interface, "*interface", FALSE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal",
						"/org/freedesktop/Hal/Manager",
						"org.freedesktop.Hal.Manager",
						"ReleaseGlobalInterfaceLock");

	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return FALSE;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &interface);
	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		return FALSE;
	}
	if (reply == NULL)
		return FALSE;

	dbus_message_unref (reply);
	return TRUE;
}

/**
 * libhal_device_is_caller_locked_out:
 * @ctx: the context for the connection to hald
 * @udi: the Unique id of device
 * @interface: the intername name to check
 * @caller: the caller to check for
 * @error: pointer to an initialized dbus error object for returning errors
 * 
 * Determines whether a given process on the system message bus is
 * locked out from an interface on a specific device. Only HAL helpers
 * are privileged to use this method.
 * 
 * Returns: Whether the given caller is locked out
 **/
dbus_bool_t
libhal_device_is_caller_locked_out (LibHalContext *ctx,
                                    const char *udi,
                                    const char *interface,
                                    const char *caller,
                                    DBusError *error)
{
	DBusMessage *message;
	DBusMessageIter iter;
	DBusMessage *reply;
	DBusMessageIter reply_iter;
        dbus_bool_t value;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, TRUE);
	LIBHAL_CHECK_UDI_VALID(udi, TRUE);
	LIBHAL_CHECK_PARAM_VALID(interface, "*interface", TRUE);
	LIBHAL_CHECK_PARAM_VALID(caller, "*caller", TRUE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal",
						udi,
						"org.freedesktop.Hal.Device",
						"IsCallerLockedOut");

	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return TRUE;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &interface);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &caller);
	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		return TRUE;
	}
	if (reply == NULL)
		return TRUE;

	/* now analyze reply */
	dbus_message_iter_init (reply, &reply_iter);
	if (dbus_message_iter_get_arg_type (&reply_iter) != DBUS_TYPE_BOOLEAN) {
		dbus_message_unref (reply);
		return TRUE;
	}
	dbus_message_iter_get_basic (&reply_iter, &value);
	dbus_message_unref (reply);
	return value;
}


/**
 * libhal_ctx_set_global_interface_lock_acquired:
 * @ctx: the context for the connection to hald
 * @callback: the callback
 *
 * Set the callback for when a global interface lock is acquired.
 *
 * Returns: TRUE if callback was successfully set, FALSE otherwise
 */
dbus_bool_t
libhal_ctx_set_global_interface_lock_acquired (LibHalContext *ctx, LibHalGlobalInterfaceLockAcquired callback)
{
	LIBHAL_CHECK_LIBHALCONTEXT (ctx, FALSE);

	ctx->global_interface_lock_acquired = callback;
	return TRUE;
}

/**
 * libhal_ctx_set_global_interface_lock_released:
 * @ctx: the context for the connection to hald
 * @callback: the callback
 *
 * Set the callback for when a global interface lock is released.
 *
 * Returns: TRUE if callback was successfully set, FALSE otherwise
 */
dbus_bool_t
libhal_ctx_set_global_interface_lock_released (LibHalContext *ctx, LibHalGlobalInterfaceLockReleased callback)
{
	LIBHAL_CHECK_LIBHALCONTEXT (ctx, FALSE);

	ctx->global_interface_lock_released = callback;
	return TRUE;
}


/**
 * libhal_ctx_set_interface_lock_acquired:
 * @ctx: the context for the connection to hald
 * @callback: the callback
 *
 * Set the callback for when an interface lock is acquired.
 *
 * Returns: TRUE if callback was successfully set, FALSE otherwise
 */
dbus_bool_t
libhal_ctx_set_interface_lock_acquired (LibHalContext *ctx, LibHalInterfaceLockAcquired callback)
{
	LIBHAL_CHECK_LIBHALCONTEXT (ctx, FALSE);

	ctx->interface_lock_acquired = callback;
	return TRUE;
}

/**
 * libhal_ctx_set_interface_lock_released:
 * @ctx: the context for the connection to hald
 * @callback: the callback
 *
 * Set the callback for when an interface lock is released.
 *
 * Returns: TRUE if callback was successfully set, FALSE otherwise
 */
dbus_bool_t
libhal_ctx_set_interface_lock_released (LibHalContext *ctx, LibHalInterfaceLockReleased callback)
{
	LIBHAL_CHECK_LIBHALCONTEXT (ctx, FALSE);

	ctx->interface_lock_released = callback;
	return TRUE;
}



/**
 * libhal_device_is_locked_by_others:
 * @ctx: the context for the connection to hald
 * @udi: the Unique id of device
 * @interface: the intername name to check
 * @error: pointer to an initialized dbus error object for returning errors or NULL
 * 
 * Determines whether a determines other processes than the caller holds a lock on the given device.
 * 
 * Returns: If another process is holding a lock on the device
 **/
dbus_bool_t 
libhal_device_is_locked_by_others (LibHalContext *ctx,
                                   const char *udi,
                                   const char *interface,
                                   DBusError *error)
{
	DBusMessage *message;
	DBusMessageIter iter;
	DBusMessage *reply;
	DBusMessageIter reply_iter;
        dbus_bool_t value;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, TRUE);
	LIBHAL_CHECK_UDI_VALID(udi, TRUE);
	LIBHAL_CHECK_PARAM_VALID(interface, "*interface", TRUE);

	message = dbus_message_new_method_call ("org.freedesktop.Hal",
						udi,
						"org.freedesktop.Hal.Device",
						"IsLockedByOthers");

	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return TRUE;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &interface);
	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		return TRUE;
	}
	if (reply == NULL)
		return TRUE;

	/* now analyze reply */
	dbus_message_iter_init (reply, &reply_iter);
	if (dbus_message_iter_get_arg_type (&reply_iter) != DBUS_TYPE_BOOLEAN) {
		dbus_message_unref (reply);
		return TRUE;
	}
	dbus_message_iter_get_basic (&reply_iter, &value);
	dbus_message_unref (reply);
	return value;
}

/**
 * libhal_device_is_caller_privileged:
 * @ctx: the context for the connection to hald
 * @udi: the Unique id of device
 * @action: the action to check for
 * @caller: the caller to check for
 * @error: pointer to an initialized dbus error object for returning errors
 * 
 * Determines if a given caller have a given privilege on a given
 * device. Will always error out if HAL is not built with PolicyKit
 * support.
 * 
 * Returns: The textual reply from PolicyKit. See the #PolicyKitResult
 * enumeration in the PolicyKit documentation for details. The caller
 * is responsible for freeing this string with the function
 * libhal_free_string().
 **/
char*
libhal_device_is_caller_privileged (LibHalContext *ctx,
                                    const char *udi,
                                    const char *action,
                                    const char *caller,
                                    DBusError *error)
{
	DBusMessage *message;
	DBusMessageIter iter;
	DBusMessage *reply;
	DBusMessageIter reply_iter;
        char *dbus_str;
        char *value;

	LIBHAL_CHECK_LIBHALCONTEXT(ctx, NULL);
	LIBHAL_CHECK_UDI_VALID(udi, NULL);
	LIBHAL_CHECK_PARAM_VALID(action, "*action", NULL);
	LIBHAL_CHECK_PARAM_VALID(caller, "*caller", NULL);

	message = dbus_message_new_method_call ("org.freedesktop.Hal",
						udi,
						"org.freedesktop.Hal.Device",
						"IsCallerPrivileged");

	if (message == NULL) {
		fprintf (stderr,
			 "%s %d : Couldn't allocate D-BUS message\n",
			 __FILE__, __LINE__);
		return NULL;
	}

	dbus_message_iter_init_append (message, &iter);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &action);
	dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &caller);
	
	reply = dbus_connection_send_with_reply_and_block (ctx->connection,
							   message, -1,
							   error);

	dbus_message_unref (message);

	if (error != NULL && dbus_error_is_set (error)) {
		return NULL;
	}
	if (reply == NULL)
		return NULL;

	/* now analyze reply */
	dbus_message_iter_init (reply, &reply_iter);
	if (dbus_message_iter_get_arg_type (&reply_iter) != DBUS_TYPE_STRING) {
		dbus_message_unref (reply);
		return NULL;
	}
	dbus_message_iter_get_basic (&reply_iter, &dbus_str);
	value = (char *) ((dbus_str != NULL) ? strdup (dbus_str) : NULL);
	if (value == NULL) {
		fprintf (stderr, "%s %d : error allocating memory\n", __FILE__, __LINE__);
	}
	dbus_message_unref (reply);
	return value;
}

/**
 * libhal_get_all_devices_with_properties:
 * @out_num_devices: Return location for number of devices
 * @out_udi: Return location for array of of udi's. Caller should free this with libhal_free_string_array() when done with it.
 * @out_properties: Return location for array of #LibHalPropertySet objects. Caller should free each one of them with libhal_free_property_set() when done with it
 * @error: Return location for error
 *
 * Get all devices in the hal database as well as all properties for each device.
 *
 * Return: %TRUE if success; %FALSE and @error will be set.
 **/
dbus_bool_t libhal_get_all_devices_with_properties (LibHalContext       *ctx, 
                                                    int                 *out_num_devices, 
                                                    char              ***out_udi,
                                                    LibHalPropertySet ***out_properties, 
                                                    DBusError           *error)
{

	DBusMessage *message;
	DBusMessage *reply;
	DBusMessageIter iter_array, reply_iter;
	DBusError _error;
        char **udi_array;
        char **_udi_array;
        LibHalPropertySet **prop_array;
        LibHalPropertySet **_prop_array;
        size_t count;
        unsigned int n;

	LIBHAL_CHECK_LIBHALCONTEXT (ctx, FALSE);
	LIBHAL_CHECK_PARAM_VALID (out_num_devices, "*out_num_devices",FALSE);
	LIBHAL_CHECK_PARAM_VALID (out_udi, "***out_udi", FALSE);
	LIBHAL_CHECK_PARAM_VALID (out_properties, "***out_properties", FALSE);

	*out_num_devices = 0;
        *out_udi = NULL;
        *out_properties = NULL;

        count = 0;
        udi_array  = NULL;
        prop_array = NULL;

	message = dbus_message_new_method_call ("org.freedesktop.Hal",
						"/org/freedesktop/Hal/Manager",
						"org.freedesktop.Hal.Manager",
						"GetAllDevicesWithProperties");
	if (message == NULL) {
		fprintf (stderr, "%s %d : Could not allocate D-BUS message\n", __FILE__, __LINE__);
		goto fail;
	}

	dbus_error_init (&_error);
	reply = dbus_connection_send_with_reply_and_block (ctx->connection, message, -1, &_error);

	dbus_move_error (&_error, error);
	if (error != NULL && dbus_error_is_set (error)) {
		dbus_message_unref (message);
		goto fail;
	}
	if (reply == NULL) {
		dbus_message_unref (message);
		goto fail;
	}

	/* now analyze reply */
	dbus_message_iter_init (reply, &reply_iter);

	if (dbus_message_iter_get_arg_type (&reply_iter) != DBUS_TYPE_ARRAY) {
		fprintf (stderr, "%s %d : wrong reply from hald.  Expecting an array.\n", __FILE__, __LINE__);
		goto fail;
	}
	
	dbus_message_iter_recurse (&reply_iter, &iter_array);

        #define _BLOCK_SIZE 32

        udi_array = (char **) malloc (sizeof (char*) * _BLOCK_SIZE);
        if (udi_array == NULL)
                goto fail;

        prop_array = (LibHalPropertySet **) malloc (sizeof (void*) * _BLOCK_SIZE);
        if (prop_array == NULL)
                goto fail;

	while (dbus_message_iter_get_arg_type (&iter_array) == DBUS_TYPE_STRUCT) {
                DBusMessageIter iter_struct;
		const char *value;
                LibHalPropertySet *pset;
		char *udi;

		if ((count % _BLOCK_SIZE) == 0 && count > 0) {
                        _udi_array = (char **) realloc (udi_array, sizeof (char*) * (count + _BLOCK_SIZE));
                        _prop_array = (LibHalPropertySet **) realloc (prop_array, sizeof (void*) * (count+_BLOCK_SIZE));
			if (_udi_array == NULL || _prop_array == NULL)
				goto fail;
                        udi_array = _udi_array;
                        prop_array = _prop_array;
		}

                dbus_message_iter_recurse (&iter_array, &iter_struct);
		
		dbus_message_iter_get_basic (&iter_struct, &value);
		udi = strdup (value);
		if (udi == NULL)
			goto fail;

		dbus_message_iter_next(&iter_struct);

                pset = get_property_set (&iter_struct);

                udi_array[count] = udi;
                prop_array[count] = pset;
                count++;

		dbus_message_iter_next (&iter_array);
	}

        if ((count % _BLOCK_SIZE) == 0 && count > 0) {
                _udi_array = (char **) realloc (udi_array, sizeof (char*) * (count + _BLOCK_SIZE));
                _prop_array = (LibHalPropertySet **) realloc (prop_array, sizeof (void*) * (count + _BLOCK_SIZE));
                if (_udi_array == NULL || _prop_array == NULL)
                        goto fail;
                udi_array = _udi_array;
                prop_array = _prop_array;
        }
        udi_array[count] = NULL;
        prop_array[count] = NULL;

	*out_num_devices = count;
        *out_udi = udi_array;
        *out_properties = prop_array;
		      
	dbus_message_unref (reply);
	dbus_message_unref (message);

	return TRUE;

fail:
        if (udi_array != NULL) {
                for (n = 0; n < count; n++) {
                        free (udi_array[n]);
                }
                free (udi_array);
        }

        if (prop_array != NULL) {
                for (n = 0; n < count; n++) {
                        free (prop_array[n]);
                }
                free (prop_array);
        }

        return FALSE;
}
