Requesting Channels

Channels may be requested from the Channel Dispatcher or directly from a Connection object using the Requests interface.

Requesting a channel from the dispatcher will cause it to be handled like any other dispatched channel. This is the preferred way of requesting new channels.

Requesting a channel directly from the Connection will cause it to be ignored by the Channel Dispatcher, meaning that the requesting application must handle the channel itself.

Request Channels from the Channel Dispatcher

Channels requested from the Channel Dispatcher will be dispatched via the same mechanism as an unrequested, incoming channel. This means that the best available application to handle the channel will be used.

Requesting channels through the Channel Dispatcher also allows the Dispatcher to try a different client if the first client fails to handle the channel.

If you wish to handle the request yourself, you can pass the D-Bus well-known name of your client as the preferred handler.

These interfaces provides two methods: CreateChannel and EnsureChannel. Both methods take the same parameter, a dictionary containing the desired properties for the channel.

CreateChannel will attempt to create a new channel with the requested properties. Depending on the protocol, some types of channels are exclusive, and only one such channel can exist at a time (e.g. a XMPP chatroom). If a second channel is requested, the error NotAvailable is returned.

Conversely, EnsureChannel will attempt to reuse an existing channel with the same properties wherever possible, else it will create a new channel. It's possible that another client is also utilising this channel.

When to Create and when to Ensure

Choosing when to always create a new channel, or when to use an existing channel can usually be deduced based on the function of the channel. If it would make sense to reuse an existing channel then use EnsureChannel, otherwise use CreateChannel.

EnsureChannel is usually used for Text, StreamedMedia and ContactList channels.

CreateChannel is usually used for FileTransfer, Tubes, RoomList and ContactSearch channels.

The properties argument for CreateChannel and EnsureChannel is a map of property names on the desired channel, and their values. In general every channel requires at least three channel properties: the type of channel we wish to create (ChannelType), the handle/id of the contact/room/list we wish to create a channel for (TargetHandle or TargetID) and the type of that handle (TargetHandleType). Specific channel types may require additional properties in order to be created, this is noted in the specification.

For example, to create a ContactList channel (this is the type of channel that is used to get a list of subscribed contacts from a service), we might provide a map like so:

org.freedesktop.Telepathy.Channel.ChannelType org.freedesktop.Telepathy.Channel.Type.ContactList
org.freedesktop.Telepathy.Channel.TargetHandleType Handle_Type_List
org.freedesktop.Telepathy.Channel.TargetID "subscribe"
Only one of TargetHandle or TargetID can be supplied

Channel requests should only include one of the properties TargetHandle or TargetID. Providing both properties (even if they match) is an error and your request will fail.

More channel request examples are given in Example 6-1.

Anonymous Channels

Anonymous channels are channels that do not connect to a remote (single) contact, room, list or group. For example, RoomList channels, or anonymous MUC chats (e.g. in MSN).

They are requested by giving a TargetHandleType of Handle_Type_None (you should not specify a TargetHandle for an anonymous channel).

When using the Requests interface to request a channel directly from a Connection, both EnsureChannel and CreateChannel return the object path of a channel that can be access on that Connection.

Using the Channel Dispatcher, requests might take some time (e.g. if the account has to be brought online), so you are instead returned a Channel Request Object, which is discussed further in Section 6.1.1 ― Channel Request Objects.

Example 6-1More Channel Request Examples

Requesting a one-to-one text channel to the contact escher@tuxedo.cat:

Channel.ChannelType Channel.Type.Text
Channel.TargetHandleType Handle_Type_Contact
Channel.TargetID escher@tuxedo.cat

Requesting a one-to-one text channel to the contact with handle id 12 (as requested using RequestHandles), or from a TpContact, or a method-return/signal that gives handles:

Channel.ChannelType Channel.Type.Text
Channel.TargetHandleType Handle_Type_Contact
Channel.TargetHandle 12

Requesting a text channel to the multi-user chat room kitties@conf.litter.cat:

Channel.ChannelType Channel.Type.Text
Channel.TargetHandleType Handle_Type_Room
Channel.TargetID kitties@conf.litter.cat

Requesting a D-Bus tube to cami@egg.cat for the service cat.litter.ExampleService (see more of these in Section 11.1 ― Creating a Tube):

Channel.ChannelType Channel.Type.DBusTube
Channel.TargetHandleType Handle_Type_Contact
Channel.TargetID cami@egg.cat
Channel.Type.DBusTube.ServiceName cat.litter.ExampleService

Opening a file transfer channel to harvey@nom.cat; initial information about the file is included in the channel request so that the recepient can make a decision whether to accept the channel (more information in Section 9.1 ― Sending Files):

Channel.ChannelType Channel.Type.FileTransfer
Channel.TargetHandleType Handle_Type_Contact
Channel.TargetID harvey@nom.cat
Channel.Type.FileTransfer.Filename cat.jpg
Channel.Type.FileTransfer.ContentType image/jpeg
Channel.Type.FileTransfer.Size 115121

6.1.1. Channel Request Objects

Channels requested via the Channel Dispatcher don't immediately return the newly created channel from calls to CreateChannel and EnsureChannel. This is because the specified Account may not be immediately ready to request a channel, and the D-Bus method call may time out before the channel is requested.

In the worst case, the specified Account for a request may be offline and have to be brought online by the Account Manager. The network connection may only connect on demand (common on mobile devices) and may have to be brought up before the account can be connected.

To solve this problem, the Channel Dispatcher returns Channel Request Objects, which implement the ChannelRequest interface. These objects are accessible via the Channel Dispatcher well-known bus name (org.freedesktop.Telepathy.ChannelDispatcher).

Channel Requests provide two signals: Succeeded and Failed. You should connect these two signals and then call the Proceed method to begin the request. If you do not call Proceed, the request will not begin.

There is also a Cancel method that will allow you to cancel a method up until the time it has been dispatched to a Channel Handler. The precise semantics of this method are provided in the Telepathy specification.

6.1.2. telepathy-python

telepathy-python provides the telepathy.client.Channel class as a D-Bus proxy object. To construct this proxy you need to pass the connection's D-Bus service name and the path to the channel object.

telepathy.client.Channel can be inherited like any other Python class, so that you can pass around all of the methods and state relating to that channel as one object. This is shown in Example 6-2.

Example 6-2Inheriting telepathy.client.Channel
class TextChannel (telepathy.client.Channel):
    def __init__ (self, parent, channel_path):
        self.parent = parent
        conn = parent.conn

        super (TextChannel, self).__init__ (conn.service_name, channel_path)

Complete Source Code

6.1.3. telepathy-glib

6.1.3.1. From the Channel Dispatcher

Requesting channels from the Channel Dispatcher is the generally preferred way to create or ensure channels. telepathy-glib provides a class TpAccountChannelRequest that you can use to make channel requests that either you or another client can handle.

There are four main methods available via the TpAccountChannelRequest class:

  • ensure;
  • create;
  • ensure and handle; and
  • create and handle.

The first two methods request a channel that can be dispatched to any channel handler (see Section 4.1 ― Telepathy Clients). You may optionally provide a name for the preferred Handler for this channel request. If you want to request a channel, but you don't care who handles it, use these methods. Example 6-3 is an example of ensuring a channel this way, the channel is most likely going to be handled by Empathy or some other chat client.

The second two methods request a channel that you wish to handle yourself. These methods work by creating a temporary Handler and giving its name as the preferred Handler for the channel request. If you want to request a channel to use yourself (and you want to get a TpChannel in your callback), use these methods.

Example 6-3Ensuring a Channel via the Channel Dispatcher
props = tp_asv_new (
    TP_PROP_CHANNEL_CHANNEL_TYPE, G_TYPE_STRING, TP_IFACE_CHANNEL_TYPE_TEXT,
    TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, TP_TYPE_HANDLE, TP_HANDLE_TYPE_ROOM,
    TP_PROP_CHANNEL_TARGET_ID, G_TYPE_STRING, targetid,
    NULL);

request = tp_account_channel_request_new (TP_ACCOUNT (account),
    props, TP_USER_ACTION_TIME_CURRENT_TIME);

/* ensure this channel, but let the default handler handle it */
tp_account_channel_request_ensure_channel_async (request,
    NULL, NULL, _muc_channel_ready, NULL);

g_hash_table_destroy (props);
g_object_unref (request);

Complete Source Code

These methods follow the GIO async pattern, so you must call the appropriate "finish" method in the callback. Checking the return value and error let's you know the channel was successfully handled. TpChannels have the core feature prepared.

Table 6-1Channel Request Methods
Operation Launch Method/Finish Method Return Value
Create tp_account_channel_request_create_channel_async tp_account_channel_request_create_channel_finish True if channel successfully handled
Ensure tp_account_channel_request_ensure_channel_async tp_account_channel_request_ensure_channel_finish True if channel successfully handled
Create and Handle tp_account_channel_request_create_and_handle_channel_async tp_account_channel_request_create_and_handle_channel_finish TpChannel if successful
Ensure and Handle tp_account_channel_request_ensure_and_handle_channel_async tp_account_channel_request_ensure_and_handle_channel_finish TpChannel if successful

If you are the channel handler, and the channel you've requested can be ensured by other clients (e.g. a text-channel), you should keep the TpAccountChannelRequest and listen to the re-handled signal. This signal will be emitted whenever someone else ensures the channel, and can be used to bring attention to the channel.

User Action Times

When handling a channel, the user action time can be used for things like focus stealing prevention.

User action times in Telepathy are subtley different to event times in X11. To help convert between them, telepathy-glib provides to convenience functions: tp_user_action_time_from_x11 and tp_user_action_time_should_present.

6.1.3.2. From a Connection

It is possible to request a channel directly from a Connection. telepathy-glib provides no high-level API to do this, because it's only intended to be used by the Channel Dispatcher, and not by Telepathy clients. Channels requested directly will be unknown to the Channel Dispatcher.

Example 6-4 shows ensuring a channel directly from a Connection.

Example 6-4Ensuring a Channel from a Connection with telepathy-glib
/* explicitly ask for the publish and subscribe contact lists
 * these will be announced by NewChannels, so we don't need
 * to handle their callbacks (this does mean we also can't
 * handle their errors) */
GHashTable *request = tp_asv_new (
        TP_PROP_CHANNEL_CHANNEL_TYPE,
        G_TYPE_STRING,
        TP_IFACE_CHANNEL_TYPE_CONTACT_LIST,

        TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
        G_TYPE_UINT,
        TP_HANDLE_TYPE_LIST,

        NULL);

/* the 'publish' list */
tp_asv_set_string (request,
        TP_PROP_CHANNEL_TARGET_ID, "publish");
tp_cli_connection_interface_requests_call_ensure_channel (
                conn, -1, request, NULL, NULL, NULL, NULL);

/* the 'subscribe' list */
tp_asv_set_string (request,
        TP_PROP_CHANNEL_TARGET_ID, "subscribe");
tp_cli_connection_interface_requests_call_ensure_channel (
                conn, -1, request, NULL, NULL, NULL, NULL);

g_hash_table_destroy (request);

Complete Source Code

6.1.3.3. TpChannel

The TpChannel is used to work with channels in telepathy-glib. It also provides API to help use the Groups interface, that is present on many channels.

A TpChannel can either be created using tp_channel_new or tp_channel_new_from_properties. The latter is generally preferred as you can just pass in the returned property map, as shown in Example 6-5.

Example 6-5Using tp_channel_new_from_properties
const char *type = tp_asv_get_string (map,
                TP_PROP_CHANNEL_CHANNEL_TYPE);

/* if this channel is a contact list, we want to know
 * about it */
if (!strcmp (type, TP_IFACE_CHANNEL_TYPE_CONTACT_LIST))
{
        TpChannel *channel = tp_channel_new_from_properties (
                        conn, object_path, map,
                        &error);
        handle_error (error);

        tp_channel_call_when_ready (channel,
                        channel_ready, NULL);
}

Complete Source Code

6.1.4. The RequestChannel Method

Deprecated

The RequestChannel method has been deprecated and should only be used if compatibility is required with older Connection Managers that do not implement the Requests interface.

Some Connection Managers might not yet implement the Requests interface on their Connections. In these cases you can use the RequestChannel method on the Connection interface, but you do so at your own risk.

CreateChannel and EnsureChannel fix several flaws that were present in RequestChannel:

  • RequestChannel doesn't provide a way to insist on a new channel or prefer an existing channel;
  • If RequestChannel returns an existing channel, the caller is not told whether the channel can be safely handled or if something else is handling it already;
  • RequestChannel can request a channel type, a handle type and a handle, but nothing more elaborate (e.g. you can't request a RoomList with a particular server); and
  • RequestChannel doesn't return the channel's immutable properties (which is a useful optimization).

If you must call RequestChannel, you call it with the same three arguments required for all channels (channel type, handle type and handle). The Suppress_Handler argument should always be True. Example 6-6 shows how to do this.

Example 6-6Requesting a channel using RequestChannel
        self.conn[CONNECTION].RequestChannel(CHANNEL_TYPE_ROOM_LIST,
                                             HANDLE_TYPE_NONE, 0,
                                             True,
                                             reply_handler = self.got_roomlist,
                                             error_handler = self.error)

# begin example.channel.roomlist.listrooms
def got_roomlist(self, channel_path):
    print 'Got Roomlist Channel'
    channel = telepathy.client.Channel(self.conn.service_name, channel_path)

Complete Source Code

When requesting an anonymous channel with RequestChannel, a channel with handle type Handle_Type_None (as is the case in Example 6-6), pass the handle id 0.