Telepathy Properties

Telepathy Properties should not be mistaken for D-Bus' own built in properties.

Candidate for Future Deprecation

The Telepathy Properties interface is a little cumbersome to use and is a candidate to be replaced by something easier in the future.

Telepathy Properties differ from built-in D-Bus properties in that their read/write status can change dynamically (unlike D-Bus properties, whose permissions are fixed by the specification). This is to reflect the dynamic nature of some properties based on the changing permissions of a connection or channel.

For example, imagine an IRC room. Depending on the current access level of the user (i.e. is she/he an operator or moderator) and the permissions set on the channel, the user may or may not be permitted to change the channel subject at any given time. The permission flags of the property change to reflect this status.

2.7.1. Listing Properties

Properties have fixed names in the Telepathy specification, but are referred to by ID number in the API. These ID numbers are not assigned to constant values, instead the method call ListProperties should be used. This returns back an array of structs containing the ID number, property name, D-Bus type signature and access flags.

2.7.2. Changing Permissions (Property Flags)

The permissions of a property can change during the lifetime of a channel. For example, a user may not be able to set the channel's subject until she has been granted special privileges on the channel.

A change in property permissions is heralded by the PropertyFlagsChanged signal. This signal provides an array of property IDs and each property's new list of flags.

Write-only Properties

It is possible for a channel to have what appear to be write-only properties. This happens when there is no data to be read yet for a property. For instance, a newly created MUC channel may appear to have a write-only property for its subject property because there is not yet any data to be read for this property. Setting an initial subject will cause the property to change to a read-write property.

2.7.3. Getting Properties

Accessing properties is done using the GetProperties method call. This method takes an array of integer properties (as retrieved by ListProperties) and returns an array of integer property IDs and property values (as variant types).

Requesting invalid property IDs will result in the InvalidArgument error. Requesting properties that do not have the read flag set will result in PermissionDenied being returned.

2.7.4. Setting Properties

Setting properties is done using the SetProperties method. This method takes an array of integer property identifiers (as retrieved by ListProperties) and variant types.

If the property is of the wrong type, the error NotAvailable will be returned. If the property ID is unknown, the error InvalidArgument will be returned. If the write flag is not set on a given property, the error PermissionDenied will be returned. If any error condition is triggered, no properties will be updated, even ones that would otherwise be valid.

When properties have been successfully changed, the PropertiesChanged signal will be emitted with the IDs and new values of the changed properties. Be aware that changing one property may cause several properties to update. For instance, changing subject will cause an update to the properties subject, subject-timestamp and subject-contact.

2.7.5. telepathy-glib

telepathy-glib provides no specific support infrastructure for Telepathy Properties. You can set up some simple infrastructure for handling properties in your project and attach it to the TpProxy.

2.7.5.1. Setup and Listing Properties

Example 2-18telepathy-glib Telepathy Properties Setup
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);
}

Complete Source Code

Example 2-19telepathy-glib Listing Telepathy Properties
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);
        }
}

Complete Source Code

2.7.5.2. Getting Properties

Example 2-20telepathy-glib Getting Telepathy Properties
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);

Complete Source Code

Example 2-21telepathy-glib Get Telepathy Properties Callback
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);
        }
}

Complete Source Code

2.7.5.3. Setting Properties

Example 2-22telepathy-glib Setting Telepathy Properties
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);

Complete Source Code

Example 2-23telepathy-glib Telepathy Properties Changed Callback
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);
        }
}

Complete Source Code

Example 2-24telepathy-glib Telepathy Property Flags Changed Callback
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);
        }
}

Complete Source Code