glib_telepathy_properties/example.c

Source File

#include <unistd.h>

#include <glib.h>

#include <telepathy-glib/connection-manager.h>
#include <telepathy-glib/connection.h>
#include <telepathy-glib/channel.h>
#include <telepathy-glib/interfaces.h>
#include <telepathy-glib/gtypes.h>
#include <telepathy-glib/util.h>
#include <telepathy-glib/enums.h>
#include <telepathy-glib/debug.h>

static GMainLoop *loop = NULL;
static TpDBusDaemon *bus_daemon = NULL;
static TpConnection *conn = NULL;

/* Begin Example 2-18 */
typedef struct _TpProperty TpProperty;
struct _TpProperty
{
	guint id;
	char *name;
	guint flags;
};

static GHashTable *
tp_property_get_map (TpProxy *proxy)
{
	g_return_val_if_fail (TP_IS_PROXY (proxy), NULL);

	GHashTable *map = g_object_get_data (G_OBJECT (proxy),
			"tpproperties-map");

	return map;
}

static guint
tp_property_get_id (TpProxy *proxy, const char *name)
{
	GHashTable *map = tp_property_get_map (proxy);

	g_return_val_if_fail (map != NULL, 0);

	return GPOINTER_TO_UINT (g_hash_table_lookup (map, name));
}

static GArray *
tp_property_get_array (TpProxy *proxy)
{
	g_return_val_if_fail (TP_IS_PROXY (proxy), NULL);

	GArray *array = g_object_get_data (G_OBJECT (proxy),
			"tpproperties-array");

	return array;
}

static TpProperty *
tp_property_from_id (TpProxy *proxy, guint id)
{
	GArray *array = tp_property_get_array (proxy);

	g_return_val_if_fail (array != NULL, NULL);

	return &g_array_index (array, TpProperty, id);
}

static void
tp_property_insert (TpProxy *proxy, TpProperty *property)
{
	GArray *array = tp_property_get_array (proxy);
	GHashTable *map = tp_property_get_map (proxy);

	g_array_append_val (array, *property);
	g_hash_table_insert (map, property->name, GUINT_TO_POINTER (property->id));
}

static void
tp_property_init (TpProxy *proxy)
{
	g_return_if_fail (TP_IS_PROXY (proxy));

	GArray *array = g_array_new (FALSE, FALSE,
			sizeof (TpProperty));
	GHashTable *map = g_hash_table_new (g_str_hash, g_str_equal);

	/* FIXME: use g_object_set_data_full() to cleanup on object finalize */
	g_object_set_data (G_OBJECT (proxy), "tpproperties-array", array);
	g_object_set_data (G_OBJECT (proxy), "tpproperties-map", map);
}
/* End Example 2-18 */

static void
handle_error (const GError *error)
{
	if (error)
	{
		g_print ("ERROR: %s\n", error->message);
		tp_cli_connection_call_disconnect (conn, -1, NULL,
				NULL, NULL, NULL);
	}
}

/* Begin Example 2-23 */
static void
tp_properties_changed_cb (TpProxy	  *channel,
			  const GPtrArray *properties,
			  gpointer	   user_data,
			  GObject	  *weak_obj)
{
	g_print (" > tp_properties_changed_cb\n");

	int i;
	for (i = 0; i < properties->len; i++)
	{
		GValueArray *property = g_ptr_array_index (properties, i);
		/* the id is a GValue<UINT>
		 * the variant is a GValue<GValue<??> */
		guint id;
		GValue *value;

		tp_value_array_unpack (property, 2,
				&id,
				&value);

		TpProperty *tpproperty = tp_property_from_id (TP_PROXY (channel), id);

		/* get a string representation of value */
		char *str = g_strdup_value_contents (value);
		g_print ("Property %s (%i): %s\n", tpproperty->name, id, str);
		g_free (str);
	}
}
/* End Example 2-23 */

/* Begin Example 2-24 */
static void
tp_property_flags_changed_cb (TpProxy		*channel,
			      const GPtrArray	*properties,
			      gpointer		 user_data,
			      GObject		*weak_obj)
{
	g_print (" > tp_property_flags_changed_cb\n");

	int i;
	for (i = 0; i < properties->len; i++)
	{
		GValueArray *property = g_ptr_array_index (properties, i);
		guint id, flags;

		tp_value_array_unpack (property, 2,
				&id,
				&flags);

		TpProperty *tpproperty = tp_property_from_id (TP_PROXY (channel), id);

		g_print ("Property %s (%i): %x\n",
			tpproperty->name, id, flags);
	}
}
/* End Example 2-24 */

/* Begin Example 2-21 */
static void
tp_properties_get_cb (TpProxy		*channel,
		      const GPtrArray	*properties,
		      const GError	*in_error,
		      gpointer		 user_data,
		      GObject		*weak_obj)
{
	handle_error (in_error);

	g_print (" > tp_properties_get_cb\n");

	int i;
	for (i = 0; i < properties->len; i++)
	{
		GValueArray *property = g_ptr_array_index (properties, i);
		/* the id is a GValue<UINT>
		 * the variant is a GValue<GValue<??> */
		guint id;
		GValue *value;

		tp_value_array_unpack (property, 2,
				&id,
				&value);

		TpProperty *tpproperty = tp_property_from_id (TP_PROXY (channel), id);

		/* get a string representation of value */
		char *str = g_strdup_value_contents (value);
		g_print ("Property %s (%i): %s\n", tpproperty->name, id, str);
		g_free (str);
	}
}
/* End Example 2-21 */

/* Begin Example 2-19 */
static void
list_properties_cb (TpProxy		*channel,
		    const GPtrArray	*available_properties,
		    const GError	*in_error,
		    gpointer		 user_data,
		    GObject		*weak_obj)
{
	handle_error (in_error);

	g_print (" > list_properties_cb\n");

	/* @available_properties is a GPtrArray of GValueArray structs
	 * of signature (ussu) */
	int i;
	for (i = 0; i < available_properties->len; i++)
	{
		GValueArray *prop = g_ptr_array_index (available_properties, i);

		guint id, flags;
		const char *name, *sig;

		tp_value_array_unpack (prop, 4,
				&id,
				&name,
				&sig,
				&flags);

		g_print ("%u %s (%s) %x\n", id, name, sig, flags);

		TpProperty property = { 0, };
		property.id = id;
		property.name = g_strdup (name);
		property.flags = flags;
		tp_property_insert (TP_PROXY (channel), &property);
	}

	/* call the chained callback if set */
	if (user_data)
	{
		void (* func) (TpProxy *);

		func = user_data;

		func (channel);
	}
}
/* End Example 2-19 */

static void
tpproperties_ready (TpChannel	*channel)
{
	{ /* pack the readable properties into a GArray */
		/* Begin Example 2-20 */
		GArray *array = g_array_new (FALSE, FALSE, sizeof (guint));
		int i;

		g_print ("Read property: ");
		for (i = 0; i < tp_property_get_array (TP_PROXY (channel))->len; i++)
		{
			TpProperty *property = tp_property_from_id (TP_PROXY (channel), i);

			if (!(property->flags & TP_PROPERTY_FLAG_READ)) continue;

			g_print ("%i ", i);
			g_array_append_val (array, i);
		}
		g_print ("\n");

		tp_cli_properties_interface_call_get_properties (channel, -1,
				array, tp_properties_get_cb,
				NULL, NULL, NULL);

		g_array_free (array, TRUE);
		/* End Example 2-20 */
	}

	{ /* set some properties */
		/* Begin Example 2-22 */
		GPtrArray *array = g_ptr_array_new_with_free_func (
				(GDestroyNotify) g_value_array_free);
		GValue value = { 0, };

		/* FIXME we're assuming this property exists, we should check */
		guint id = tp_property_get_id (TP_PROXY (channel), "subject");

		g_value_init (&value, G_TYPE_STRING);
		g_value_set_static_string (&value, "Test Subject");

		g_ptr_array_add (array, tp_value_array_build (2,
				G_TYPE_UINT, id,
				G_TYPE_VALUE, &value,
				G_TYPE_INVALID));

		g_value_unset (&value);

		g_print ("Setting properties...\n");
		tp_cli_properties_interface_call_set_properties (channel, -1,
				array, NULL, NULL, NULL, NULL);

		g_ptr_array_free (array, TRUE);
		/* End Example 2-22 */
	}
}

static void
muc_channel_ready (TpChannel	*channel,
		   const GError	*in_error,
		   gpointer	 user_data)
{
	g_print ("MUC channel (%s) ready\n",
			tp_channel_get_identifier (channel));

	/* exciting things about MUC channels are stored as Telepathy
	 * Properties (not D-Bus properties). This interface is a little
	 * awkward.
	 * First we need to get a list of available properties */
	tp_property_init (TP_PROXY (channel));
	tp_cli_properties_interface_call_list_properties (channel, -1,
			list_properties_cb, tpproperties_ready, NULL, NULL);

	GError *error = NULL;

	tp_cli_properties_interface_connect_to_properties_changed (
			channel, tp_properties_changed_cb,
			NULL, NULL, NULL, &error);
	handle_error (error);
	tp_cli_properties_interface_connect_to_property_flags_changed (
			channel, tp_property_flags_changed_cb,
			NULL, NULL, NULL, &error);
	handle_error (error);
}

static void
create_muc_cb (TpConnection	*conn,
	       gboolean		 yours,
	       const char	*object_path,
	       GHashTable	*props,
	       const GError	*in_error,
	       gpointer		 user_data,
	       GObject		*weak_obj)
{
	handle_error (in_error);
	GError *error = NULL;

	g_print (" > create_muc_cb (%s)\n", object_path);

	TpChannel *channel = tp_channel_new_from_properties (conn,
			object_path, props, &error);
	handle_error (error);

	tp_channel_call_when_ready (channel, muc_channel_ready, NULL);
}

static void
muc_request_channel_cb (TpConnection	*conn,
			const char	*object_path,
			const GError	*in_error,
			gpointer	 user_data,
			GObject		*weak_obj)
{
	handle_error (in_error);
	GError *error = NULL;

	g_print (" > muc_request_channel_cb (%s)\n", object_path);

	TpHandle handle = GPOINTER_TO_INT (user_data);
	TpChannel *channel = tp_channel_new (conn,
			object_path,
			TP_IFACE_CHANNEL_TYPE_TEXT,
			TP_HANDLE_TYPE_ROOM,
			handle,
			&error);
	handle_error (error);

	tp_channel_call_when_ready (channel, muc_channel_ready, NULL);
}

static void
request_handles_cb (TpConnection	*conn,
		    TpHandleType	 handle_type,
		    guint		 n_handles,
		    const TpHandle	*handles,
		    const char * const	*ids,
		    const GError	*in_error,
		    gpointer		 user_data,
		    GObject		*weak_object)
{
	g_print (" > request_handles_cb\n");

	handle_error (in_error);
	g_return_if_fail (n_handles == 1);

	/* since there is no error, and only 1 handle, let us assume that it
	 * is the handle for the room #test */

	tp_cli_connection_call_request_channel (conn, -1,
			TP_IFACE_CHANNEL_TYPE_TEXT,
			TP_HANDLE_TYPE_ROOM, handles[0],
			TRUE,
			muc_request_channel_cb,
			GINT_TO_POINTER (handles[0]), NULL, NULL);
}

static void
conn_ready (TpConnection	*conn,
            const GError	*in_error,
	    gpointer		 user_data)
{
	GError *error = NULL;

	g_print (" > conn_ready\n");

	handle_error (in_error);

	/* check if the Requests interface is available */
	if (tp_proxy_has_interface_by_id (conn,
		TP_IFACE_QUARK_CONNECTION_INTERFACE_REQUESTS))
	{
		/* make a connection to a MUC channel */
		GHashTable *map = tp_asv_new (
			TP_PROP_CHANNEL_CHANNEL_TYPE,
			G_TYPE_STRING,
			TP_IFACE_CHANNEL_TYPE_TEXT,

			TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
			G_TYPE_UINT,
			TP_HANDLE_TYPE_ROOM,

			TP_PROP_CHANNEL_TARGET_ID,
			G_TYPE_STRING,
			"#test",

			NULL);

		tp_cli_connection_interface_requests_call_ensure_channel (
				conn, -1, map,
				create_muc_cb,
				NULL, NULL, NULL);

		g_hash_table_destroy (map);
	}
	else
	{
		g_print ("Requests interface is not available\n");

		/* we need a handle for the channel we are going to join */
		const char *handles[] = { "#test", NULL };
		tp_connection_request_handles (conn, -1, TP_HANDLE_TYPE_ROOM,
				handles, request_handles_cb,
				NULL, NULL, NULL);
	}
}

static void
status_changed_cb (TpConnection	*conn,
                   guint	 status,
		   guint	 reason,
		   gpointer	 user_data,
		   GObject	*weak_object)
{
	if (status == TP_CONNECTION_STATUS_DISCONNECTED)
	{
		g_print ("Disconnected\n");
		g_main_loop_quit (loop);
	}
	else if (status == TP_CONNECTION_STATUS_CONNECTED)
	{
		g_print ("Connected\n");
	}
}

static void
request_connection_cb (TpConnectionManager	*cm,
                       const char		*bus_name,
		       const char		*object_path,
		       const GError		*in_error,
		       gpointer			 user_data,
		       GObject			*weak_object)
{
	GError *error = NULL;

	if (in_error) g_error ("%s", in_error->message);

	conn = tp_connection_new (bus_daemon, bus_name, object_path, &error);
	if (error) g_error ("%s", error->message);

	tp_connection_call_when_ready (conn, conn_ready, NULL);

	tp_cli_connection_connect_to_status_changed (conn, status_changed_cb,
			NULL, NULL, NULL, &error);
	handle_error (error);

	/* initiate the connection */
	tp_cli_connection_call_connect (conn, -1, NULL, NULL, NULL, NULL);
}

static void
cm_ready (TpConnectionManager	*cm,
	  const GError		*in_error,
	  gpointer		 user_data,
	  GObject		*weak_obj)
{
	char **argv = (char **) user_data;

	g_print (" > cm_ready\n");

	if (in_error) g_error ("%s", in_error->message);

	/* request a new connection */
	GHashTable *parameters = tp_asv_new (
			"account", G_TYPE_STRING, argv[1],
			"server", G_TYPE_STRING, argv[2],
			NULL);

	tp_cli_connection_manager_call_request_connection (cm, -1,
			"irc",
			parameters,
			request_connection_cb,
			NULL, NULL, NULL);

	g_hash_table_destroy (parameters);
}

static void
interrupt_cb (int signal)
{
	g_print ("Interrupt\n");
	/* disconnect */
	tp_cli_connection_call_disconnect (conn, -1, NULL, NULL, NULL, NULL);
}

int
main (int argc, char **argv)
{
	GError *error = NULL;

	g_type_init ();

	if (argc != 3)
	{
		g_error ("Must provide username and server!");
	}

	/* create a main loop */
	loop = g_main_loop_new (NULL, FALSE);

	/* acquire a connection to the D-Bus daemon */
	bus_daemon = tp_dbus_daemon_dup (&error);
	if (bus_daemon == NULL)
	{
		g_error ("%s", error->message);
	}

	/* we want to request the gabble CM */
	TpConnectionManager *cm = tp_connection_manager_new (bus_daemon,
			"idle", NULL, &error);
	if (error) g_error ("%s", error->message);

	tp_connection_manager_call_when_ready (cm, cm_ready,
			argv, NULL, NULL);

	/* set up a signal handler */
	struct sigaction sa = { 0 };
	sa.sa_handler = interrupt_cb;
	sigaction (SIGINT, &sa, NULL);

	g_main_loop_run (loop);

	g_object_unref (bus_daemon);

	return 0;
}