<commit_before>Use cases for requesting channels
=================================

.. contents::

Outgoing 1-1 text chat
----------------------

_`req1`: Chat from chat UI
~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo has a chat or IM UI open already, and wants to use it to chat to Juliet.
He selects Juliet from a contact list or types in her username on some IM
service.

:New vs. existing:
    New channel required [#]_
:Definition of channel identity:
    ChannelType is Text, TargetHandleType is CONTACT, TargetHandle is juliet

.. [#] If the chat UI is already handling a suitable channel, it can
    reasonably be expected to work this out without the channel dispatcher's
    help

Current implementation::

    if a channel with GetHandle() -> (CONTACT, juliet) exists:
        foreground its window or tab
    else:
        RequestChannel (Text, CONTACT, juliet, suppress_handler=TRUE)

Proposed implementation::

    if a channel with GetHandle() -> (CONTACT, juliet) exists:
        foreground its window or tab (no interaction with the
        ChannelDispatcher)
    else:
        client calls some unspecified method on ChannelDispatcher (FIXME)

        ChannelDispatcher calls RequestChannels (
            SUPPRESS_HANDLER,
            [
                {'...ChannelType': '...Text',
                 '...TargetHandleType': CONTACT,
                 '...TargetHandle': juliet
                }
            ])
        # FIXME: the CD could pass in some sort of "require new" flag?

        if the channel that is returned has the EXISTING flag:
            UI fails with "already talking to Juliet in another application"
        else:
            channel observers run
            ChannelDispatcher returns it to the UI
            UI makes a new window or tab for it

        channel approvers do not run
        channel handler does not run

_`req2`: Chat from elsewhere
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Juliet wants to talk to Romeo. She chooses his entry in an address book
or other list of people (not necessarily Telepathy-centric) and is presented
with a list of possible ways to talk to him. She decides to use text chat.

:New vs. existing:
    Existing channel preferred, new channel acceptable
:Definition of channel identity:
    ChannelType is Text, TargetHandleType is CONTACT, TargetHandle is romeo

Current implementation::

    address book asks Mission Control for a channel with
        (Text, CONTACT, romeo)
    Mission Control calls RequestChannel (Text, CONTACT, romeo,
        suppress_handler=FALSE) on CM
    Mission Control dispatches the channel to the default/only handler

    if the channel is new:
        the channel handler creates a new window or tab
    else:
        the channel handler puts the existing window or tab in the foreground

Problems addressed by proposed implementation:

* It's rather bizarre that Mission Control re-dispatches an existing channel
  as though it was new (proposed solution: a Present() method instead)

* It's very bizarre that the channel handler interprets HandleChannel on a
  channel it's already handling as "put it in the foreground". If we mean
  "put this in the foreground" we should say so (proposed solution: do so)

Proposed implementation::

    client calls some unspecified method on ChannelDispatcher (FIXME)

    ChannelDispatcher calls RequestChannels (REUSE,
        [
            {'...ChannelType': '...Text',
             '...TargetHandleType': CONTACT,
             '...TargetHandle': juliet
            }
        ])

    if the channel that is returned has the EXISTING flag:
        ChannelDispatcher returns it to the address book, which does nothing
            much with it
        ChannelDispatcher calls ChannelHandler.Present() on the existing
            handler (which it already knows about)
        channel observers/approvers do not run
    else:
        channel observers run
        ChannelDispatcher returns it to the address book, which does nothing
            much with it
        channel approvers do not run
        ChannelDispatcher chooses a channel handler in an
            implementation-specific way and calls
            ChannelHandler.HandleChannel() on it

_`req3`: collaborative app
~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo is collaborating on a document with Mercutio, and wants to have a chat
embedded in his AbiWord instance, separate from any other chat with Mercutio
that may be ongoing.

:New vs. existing:
    New channel required [#]_
:Definition of channel identity:
    ChannelType is Text, TargetHandleType is CONTACT, TargetHandle is
    mercutio, Bundle is the same as the AbiWord Tube channel

.. [#] If the collaborative app already has a suitable channel, it is expected
    to work this out without the channel dispatcher's help.
    Stealing a channel from another UI is likely to fail (e.g. in the Text
    interface, they'll both try to acknowledge messages) so we should
    forbid this for sanity's sake

Current implementation: impossible, even in protocols supporting
conversation threads, because the spec can't represent them

Proposed implementation, assuming AbiWord does not have a Text channel yet::

    bundle_id = ...Bundle property of AbiWord Tube channel

    client calls some unspecified method on ChannelDispatcher (FIXME)

    ChannelDispatcher calls RequestChannels (
        SUPPRESS_HANDLER,
        [
            {'...ChannelType': '...Text',
             '...TargetHandleType': CONTACT,
             '...TargetHandle': mercutio,
             '...Bundle': bundle_id,
             }
        ])

    if a new channel that is suitable can be created:
        channel observers run in response to NewChannels
        channel approvers do not run, because the channel is one that
            was requested
        CM returns the channel "channel2" to ChannelDispatcher
        ChannelDispatcher returns the channel "channel2" to AbiWord
        ChannelDispatcher also calls HandleChannel on AbiWord

        if channel2.Bundle == bundle_id:
            that's OK
        else if channel2 has no bundle:
            that's OK too (for legacy CMs with no concept of bundles)
        else:
            (error, the CM is being stupid)
    else:
        AbiWord displays error
        if the error is the EEXIST equivalent, the message might be
            "Already talking to Mercutio in another app, and multiple threads
            are not possible in this protocol"

(Fallback behaviour if the CM is pre-Requests: all requests are made with
suppress_handler = TRUE; the ChannelDispatcher knows which ones it wants
handled.)

_`req26`: Recovering from disconnection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo is talking to Juliet using text chat, but is disconnected due to network
instability. After reconnecting, he wants to keep using the same window to
talk to Juliet.

A solution for this use case should work correctly (and result in
a single channel) if there is a "mid-air collision" with Juliet doing the
same thing, with Juliet sending messages to Romeo while he is still
offline (on store-and-forward protocols like XMPP), or with Juliet
recovering from Romeo's disconnection as per req28_ (on protocols that
do not allow offline messages).

(Recovering from a connection manager crash is equivalent to this.)

:New vs. existing:
    ???
:Definition of channel identity:
    ChannelType is Text, TargetHandleType is CONTACT, TargetHandle is juliet,
    ThreadID is the same as before

Current implementation: same as req1_

Problems addressed by proposed implementation:

* the two conversations are unrelated (Juliet cannot distinguish
  between this case and req1_)

Proposed implementation (with a new Chan.I.Thread)::

    Romeo's chat UI (or incoming message database) automatically saves the
    ...Channel.Interface.Thread.ThreadID property of the old channel

    Disconnect/reconnect occurs

    Client calls some unspecified method on ChannelDispatcher (FIXME)

    ChannelDispatcher calls RequestChannels (SUPPRESS_HANDLER,
        [
            {'...Channel.ChannelType': '...Text',
             '...Channel.TargetHandleType': CONTACT,
             '...Channel.TargetHandle': juliet,
             '...Channel.Interface.Thread.ThreadID': thread_id,
             }
        ])

    from here onwards, same as req1

Problems remaining:

* What should the CM do if the desired thread ID cannot be used for some
  reason?

* There is a potential race, req26b_

Resolution: defer the threads spec til later,
https://bugs.freedesktop.org/show_bug.cgi?id=16544

_`req26b`: potential race
^^^^^^^^^^^^^^^^^^^^^^^^^

The same as req26_, but before Romeo's client can open the replacement
channel, Juliet sends him a message, thus opening a new channel. (This
is really a dispatching problem, but is closely related...)

The desired behaviour is that the same handler receives the channel.

Proposed implementation: FIXME

_`req27`: Resuming a conversation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo chooses a past conversation with Juliet in a log browser and wants
to resume it. (The definition of threading in XMPP expects that this is
possible.)

:New vs. existing:
    Existing channel preferred, new channel acceptable
:Definition of channel identity:
    ChannelType is Text, TargetHandleType is CONTACT, TargetHandle is juliet,
    ThreadID is the same as before

Current implementation: same as req2_

Problems addressed by proposed implementation: Juliet cannot distinguish
between this case and req2_

Proposed implementation: same as req26_, except that it resembles
req2_ instead of req1_ (i.e. no SUPPRESS_HANDLER flag)

Problems remaining: same as req26_ (including a potential race, like req26b_)

Resolution: defer the threads spec til later,
https://bugs.freedesktop.org/show_bug.cgi?id=16544

_`req28`: Recovering from other's disconnection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Juliet is talking to Romeo using text chat when Romeo is disconnected
due to network instability. The protocol is one that does not
allow offline messages to be sent, like IRC. After Romeo reconnects, Juliet
wants to keep using the same window to talk to him.

A solution for this use case should work correctly (and result in
a single channel) if there is a "mid-air collision" with Romeo doing the
same thing, or with Romeo recovering from disconnection as per req26_.

(Recovering from Romeo's connection manager crash is equivalent to this.)

Current implementation: Juliet's text channel does not close, but
she cannot send messages. When Romeo reconnects, because of 1-1 chat
uniqueness, Juliet's client continues to use the same channel and there
is no disconnection.

Proposed implementation: Juliet's client continues to use the same channel

Problems remaining: how do we ensure that?

Outgoing VoIP call
------------------

_`req4`: Call from call UI
~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo has a VoIP UI open already, and wants to use it to chat to Juliet.
He selects Juliet from a contact list or types in her username on some IM
service.

:New vs. existing:
    New channel required (same reasoning as req1_)
:Definition of channel identity:
    ???

Theoretical implementation::

    if a channel containing handle juliet exists:
        foreground its window or tab
    else:
        RequestChannel (StreamedMedia, NONE, 0, suppress_handler=TRUE)
        RequestStreams (juliet, [AUDIO, VIDEO])

Problems:

* This is what the spec says we should do, but it doesn't actually work yet,
  at least in telepathy-gabble
  <https://bugs.freedesktop.org/show_bug.cgi?id=14602>
* Finding out whether a channel containing juliet's handle exists is
  needlessly laborious

Resolved problems:

* Unless the VoIP UI keeps a table of (handle => channel) (which can't
  necessarily be done - some protocols allow "parallel" calls), the following
  race condition::

    choose to call Juliet
    RequestChannel (StreamedMedia, NONE, 0, suppress_handler=TRUE) (request A)
    choose to call Juliet
    RequestChannel (StreamedMedia, NONE, 0, suppress_handler=TRUE) (request B)
    Request A returns /.../ChannelA
    Request B returns /.../ChannelB

  can result in unnecessarily opening two parallel calls to the same contact.

  (For instance: Empathy users sometimes incorrectly double-click on the
  Call button, resulting in two calls.)

  Resolution: UIs are responsible for not doing this. For instance, Empathy
  should disable (make insensitive) the Call button just before requesting
  a streamed media channel, and re-enable it when the request has either
  failed or succeeded.

Practical implementation::

    if a channel containing handle juliet exists:
        foreground its window or tab
    else:
        RequestChannel (StreamedMedia, NONE, 0, suppress_handler=TRUE)
        AddMembers ([juliet])
        RequestStreams (juliet, [AUDIO, VIDEO])

Problems:

* Same needlessly laborious processing as in the theoretical implementation
* Same race condition as in the theoretical implementation
* Juliet appears in the remote-pending set before any attempt has really
  been made to call her, which is misleading

Deprecated implementation::

    if a channel containing handle juliet exists:
        foreground its window or tab
    else:
        RequestChannel (StreamedMedia, CONTACT, juliet, suppress_handler=TRUE)
        RequestStreams (juliet, [AUDIO, VIDEO])

Problems:

* All the problems of the practical implementation
* Implementors might be misled into thinking that the semantics resemble
  text channels more closely than they really do

Proposed implementation: In the medium term, StreamedMedia channels must
retain handle type NONE. However, UIs request handle type CONTACT and
the channel dispatcher does the heavy lifting.

_`req5`: Call from elsewhere
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Juliet wants to talk to Romeo. She chooses his entry in an address book
or other list of people (not necessarily Telepathy-centric) and is presented
with a list of possible ways to talk to him. She decides to use a VoIP call.

:New vs. existing:
    Existing channel preferred? New channel acceptable
:Definition of channel identity:
    ???

Current implementation::

    RequestChannel (StreamedMedia, NONE, 0, suppress_handler=FALSE)
    perhaps AddMembers ([romeo])
    RequestStreams (romeo, [AUDIO, VIDEO])

    The channel handler creates a new window or tab for the new channel

Problems:

* The requester has to keep interacting with the channel, it's not
  "fire and forget"
* Creates a new channel, which is unlikely to be what Juliet wanted

Deprecated (?) implementation::

    ask Mission Control for a channel (StreamedMedia, CONTACT, romeo)
    Mission Control does... something?

Problems:

* Mission Control doesn't know whether to use an existing channel to Romeo,
  or create a new one (using an existing channel is *probably* right)

* Looking for channels to talk to Romeo is hard (have to interact with lots of
  group interfaces)

Proposed implementation: In the medium term, StreamedMedia channels must
retain handle type NONE. However, UIs request handle type CONTACT and
the channel dispatcher does the heavy lifting.

_`req6`: collaborative app
~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo is collaborating on a document with Mercutio, and wants to have a chat
embedded in his AbiWord instance, separate from any other chat with Mercutio
that may be ongoing.

Current implementation: same as req5_

Problems: same as req5_

Proposed implementation: same as req5_, but ask for a new channel in a
bundle

_`req29`: Recovering from disconnection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo is talking to Juliet using VoIP, but is disconnected due to network
instability. After reconnecting, he wants to keep using the same window to
talk to Juliet.

A solution for this use case should ideally work correctly (and result in
a single channel) if there is a "mid-air collision" with Juliet doing the
same thing, or with Juliet recovering from Romeo's disconnection as per
req30_.

(Recovering from a connection manager crash is equivalent to this.)

Current implementation: same as req4_

Problems:

* the two conversations are unrelated (Juliet cannot distinguish
  between this case and req4_)

* if Juliet calls Romeo at the same time that Romeo calls Juliet, a
  collision occurs and both calls probably fail with error BUSY

Proposed implementation: FIXME

_`req30`: Recovering from other's disconnection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Juliet is talking to Romeo using VoIP when Romeo is disconnected
due to network instability. After reconnecting, Juliet
wants to keep using the same window to talk to Romeo.

A solution for this use case should ideally work correctly (and result in
a single channel) if there is a "mid-air collision" with Romeo doing the
same thing, or with Romeo recovering from disconnection as per req29_.

(Recovering from a connection manager crash is equivalent to this.)

Current implementation: same as req4_

Problems:

* the two conversations are unrelated (Romeo cannot distinguish
  between this case and req4_)

* if Juliet calls Romeo at the same time that Romeo calls Juliet, a
  collision occurs and both calls probably fail with error BUSY

Proposed implementation: FIXME

Joining a named chatroom by request
-----------------------------------

_`req7`: joining chatroom from chatroom UI
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Tybalt starts an IRC-style chatroom client and wants to join a chatroom, either
by explicit request or because his client auto-joins his favourite rooms.

:New vs. existing:
    ???
:Definition of channel identity:
    ChannelType is Text, TargetHandleType is ROOM, TargetHandle is #capulet

Current implementation::

    if a channel with GetHandle() -> (ROOM, chatroom_handle) exists:
        foreground its window or tab
    else:
        RequestChannel (Text, ROOM, chatroom_handle, suppress_handler=TRUE)

Problems addressed by proposed implementation:

* Tybalt doesn't get a chance to choose his nickname before joining

Proposed implementation: some new interface for this functionality is
created, like Chan.I.Chatroom. RequestChannels arguments contain::

    {'...Channel.ChannelType': '...Channel.Type.Text',
     '...Channel.TargetHandleType': ROOM,
     '...Channel.TargetHandle': chatroom_handle,
     '...Channel.Interface.Chatroom.Nickname': '<NAME>',
    }

Proplems remaining:

* How do threads work? By analogy with 1-1 messaging, they should be
  separate channels - is this really what we want?

  Resolution: solve later, https://bugs.freedesktop.org/show_bug.cgi?id=16544

_`req8`: joining chatroom from elsewhere
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Benvolio chooses to rejoin a recently-used chatroom from (hypothetical
functionality of) the GNOME Places menu.

:New vs. existing:
    ???
:Definition of channel identity:
    ChannelType is Text, TargetHandleType is ROOM, TargetHandle is #capulet

Current implementation::

    GNOME menu asks Mission Control for a channel with
        (Text, ROOM, chatroom_handle)
    Mission Control calls RequestChannel (Text, ROOM, chatroom_handle,
        suppress_handler=FALSE) on CM
    Mission Control dispatches the channel to the default/only handler

    if the channel is new:
        the channel handler creates a new window or tab
    else:
        the channel handler puts the existing window or tab in the foreground

Problems with proposed solutions:

* As for req7_, Benvolio doesn't get a chance to choose his nickname before
  joining (proposed solution: same as req7_)

* As for req2_, it's rather bizarre that Mission Control re-dispatches an
  existing channel as though it was new (proposed solution: same as req2_)

* As for req2_, it's very bizarre that the channel handler interprets
  HandleChannel on a channel it's already handling as "put it in the
  foreground" (proposed solution: same as req2_)

Proposed implementation: a combination of req2_ and req7_

Listing named chatrooms
-----------------------

_`req9`: listing chatrooms on "home" server
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo wants to list all the chatrooms on the server or service that hosts his
account.

:New vs. existing:
    New (parallel) channel preferred, but might not be possible
:Definition of channel identity:
    None - the returned channel probably looks like one from req10_

Current implementation::

    RequestChannel (RoomList, NONE, 0, suppress_handler=TRUE)

Notes:

* There doesn't seem to be any use case for suppress_handler=FALSE here,
  since a default handler for chatroom lists doesn't really make sense

Implementation problems not affecting design:

* Gabble implements this badly, by treating the room list as a singleton

* Some protocols (IRC!) are terrible, and on these, the room list actually
  *is* a singleton

Proposed implementation: a straightforward port of the current API. In the
returned channel, the Channel.Type.RoomList.Server property will be the
actual server name, rather than empty

_`req10`: listing chatrooms on "foreign" server
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo wants to list all the chatrooms on the server ``capulet.example.com``
that hosts Juliet's account.

:New vs. existing:
    New (parallel) channel preferred, but might not be possible
:Definition of channel identity:
    ChannelType is RoomList, RoomList.Server is capulet.example.com

Current implementation: impossible

Proposed implementation: in the request, set the Channel.Type.RoomList.Server
property to the desired DNS name

Contact lists
-------------

_`req11`: typical contact list UI
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Current best-practice to get contact lists (should not use ListChannels
and NewChannel, cf my conversation with Zdra in #telepathy on 2008-04-25)::

    RequestChannel (ContactList, CONTACT_LIST, handle("subscribe"))
    RequestChannel (ContactList, CONTACT_LIST, handle("publish"))
    RequestChannel (ContactList, CONTACT_LIST, handle("hide"))
    RequestChannel (ContactList, CONTACT_LIST, handle("allow"))
    RequestChannel (ContactList, CONTACT_LIST, handle("deny"))

Current best-practice to get initial user-defined groups::

    ListChannels ()

(finding new groups will be part of the "incoming" use cases list)

Problems:

* Slightly unclear whether suppress_handler should be TRUE or FALSE -
  depends on "incoming" use cases

Proposed implementation _`req11impl1`:

* Everyone who cares about contact lists requests them, with
  suppress_handler=FALSE

* There is never a channel handler for contact lists

* Approvers do not run, because the contact lists are not "incoming"

Problems with req11impl1_:

* If there is no channel handler, we need to make sure the channel
  dispatcher won't treat this as an error, or panic and
  try to close the channel

_`req12`: creating a user-defined contact group
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Current implementation::

    RequestChannel (ContactList, GROUP, handle("Colleagues"),
        suppress_handler=FALSE)

Proposed implementation: a straightforward port to the new API

Ad-hoc chatrooms
----------------

This section refers to protocols like MSN, where what appears to be a 1-1
conversation is actually just an unnamed chatroom into which other users
can be invited.

_`req13`: Chat from chat UI (MSN)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

(The same as req1_, but Romeo and Juliet are using a "fully correct" MSN
implementation like telepathy-butterfly, or some similar protocol.)

Romeo has a chat or IM UI open already, and wants to use it to chat to Juliet.
He selects Juliet from a contact list or types in her username on some IM
service.

Hypothetical implementation::

    if a channel with GetHandle() -> (NONE, 0) exists, and its set of members
    contains Juliet's handle, and no other handles other than Romeo's:
        foreground its window or tab
    else:
        either:
            RequestChannel (Text, CONTACT, juliet, suppress_handler=TRUE)
        or:
            RequestChannel (Text, NONE, 0, suppress_handler=TRUE)
            AddMembers([juliet])

(Capability discovery can be used to decide whether to use the first or
second way to request a channel - the second must be used if we intend to
invite others to the channel later)

Problems:

* Determining whether we already have a channel to Juliet becomes harder
  than in req1_

Proposed implementation:

* Rethink our opinion of how MSN should work, and require butterfly to
  behave as if true 1-1 channels existed; then this reduces to req1_

_`req31`: Recovering from disconnection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo is talking to Juliet using text chat, but is disconnected due to network
instability. After reconnecting, he wants to keep using the same window to
talk to Juliet.

A solution for this use case should work correctly (and result in
a single channel) if there is a "mid-air collision" with Juliet doing the
same thing, with Juliet sending messages to Romeo while he is still
offline (on store-and-forward protocols like XMPP), or with Juliet
recovering from Romeo's disconnection as per req33_ (on protocols that
do not allow offline messages).

(Recovering from a connection manager crash is equivalent to this.)

Current implementation: same as req13_

Problems:

* the two conversations are unrelated (Juliet cannot distinguish
  between this case and req13_)

* A mid-air collision is highly likely to result in two parallel
  conversations with the same members (if this is even allowed by the
  protocol)

* the interaction with offline messages is quite scary

Proposed implementation:

* Rethink our opinion of how MSN should work, and require butterfly to
  behave as if true 1-1 channels existed; then this reduces to req26_

_`req32`: Resuming a conversation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo chooses a past conversation with Juliet in a log browser and wants
to resume it. (The definition of threading in XMPP expects that this is
possible.)

This is basically req31_ but for a different reason; I expect that the
solution can be similar.

Current implementation: same as req13_

Problems: Juliet cannot distinguish between this case and req13_

Proposed implementation:

* Rethink our opinion of how MSN should work, and require butterfly to
  behave as if true 1-1 channels existed; then this reduces to req27_

_`req33`: Recovering from other's disconnection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Juliet is talking to Romeo using text chat when Romeo is disconnected
due to network instability. The protocol is one that does not
allow offline messages to be sent, like IRC. After reconnecting, Juliet
wants to keep using the same window to talk to Romeo.

A solution for this use case should work correctly (and result in
a single channel) if there is a "mid-air collision" with Romeo doing the
same thing, or with Romeo recovering from disconnection as per req31_.

(Recovering from a connection manager crash is equivalent to this.)

Current implementation: unclear

Problems:

* A mid-air collision is highly likely to result in two parallel
  conversations with the same members (if this is even allowed by the
  protocol)

Proposed implementation:

* Rethink our opinion of how MSN should work, and require butterfly to
  behave as if true 1-1 channels existed; then this reduces to req28_

_`req14`: Ad-hoc chatroom from chat UI
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo wants to talk to both Mercutio and Benvolio in an ad-hoc chatroom.
He selects them both from a contact list, or types in both their usernames.

Hypothetical implementation::

    if a channel with GetHandle() -> (NONE, 0) exists, and its set of members
    contains exactly (Mercutio, Benvolio, Romeo):
        foreground its window or tab
    else:
        RequestChannel (Text, NONE, 0, suppress_handler=TRUE)
        AddMembers([mercutio, benvolio])

Problems:

* Determining whether we already have an appropriate channel is race-prone
  and inconvenient

Proposed implementation: FIXME - is this needed? Can we get away with only
supporting req15_?

_`req15`: Ad-hoc chatroom preferred
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo wants to talk to both Mercutio and Benvolio in an ad-hoc chatroom.
He selects Mercutio from a contact list, or types in Mercutio's username,
then invites Benvolio to the chatroom too.

Hypothetical implementation::

    if a channel with GetHandle() -> (NONE, 0) exists, and its set of members
    contains exactly (Mercutio, Romeo):
        foreground its window or tab
    else if Capabilities interface indicates support for inviting contacts
    to ad-hoc chatrooms:
        RequestChannel (Text, NONE, 0, suppress_handler=TRUE)
        AddMembers([mercutio])
        # when Benvolio is invited...
        AddMembers([benvolio])
    else:
        RequestChannel (Text, CONTACT, [mercutio], suppress_handler=TRUE)
        Do not offer UI for adding Benvolio

Problems:

* The UI can't know that the user would prefer an ad-hoc chatroom, so in
  practice it would always have to do this

Proposed implementation:

* Rethink our opinion of how MSN should work, and require butterfly to
  behave as if true 1-1 channels existed

* Create a 1-1 channel for the conversation with Mercutio

* Upgrade it to a chatroom by the same mechanism as req19_, which would
  create an ad-hoc chatroom in the same bundle

_`req16`: Chat from address book (MSN)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

(The same as req2_, but Romeo and Juliet are using a "fully correct" MSN
implementation like telepathy-butterfly, or some similar protocol.)

Juliet wants to talk to Romeo. She chooses his entry in an address book
or other list of people (not necessarily Telepathy-centric) and is presented
with a list of possible ways to talk to him. She decides to use text chat.

Current implementation: unclear

Possible implementation::

    address book asks Mission Control for a channel with
        (Text, CONTACT, romeo)
    Mission Control calls RequestChannel (Text, CONTACT, romeo,
        suppress_handler=FALSE) on CM
    CM does... something?
    Mission Control dispatches the channel to the default/only handler

    if the channel is new:
        the channel handler creates a new window or tab
    else:
        the channel handler puts the existing window or tab in the foreground

Problems:

* The problems of req2_

* Should the CM pick an existing channel containing only Romeo and Juliet
  (if available), or create a new one?

* If both 1-1 and ad-hoc-chatroom channels are available, this would provide
  a true 1-1 channel - what if an ad-hoc chatroom was preferred?

Proposed implementation:

* Rethink our opinion of how MSN should work, and require butterfly to
  behave as if true 1-1 channels existed

* Create a 1-1 channel for the conversation with Romeo, the same as req2_

_`req17`: Ad-hoc chatroom from address book
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The same as req14_ but from an address book.

Hypothetical implementation:

* address book asks MC for a channel with (Text, NONE, 0)

* it's handled by the default channel handler

* address book adds Mercutio and Benvolio with AddMembers

Problems:

* Surely the "address book" (which might instead be e.g. the GNOME menu)
  shouldn't need to know how to interact with Telepathy in this much
  detail?

* The chat UI getting people inserted into "its" channel by another process
  is quite weird

* No way for the chat UI to re-use an existing channel

* Do protocols like MSN allow us to have two channels with the same members?

Proposed implementation:

* Rethink our opinion of how MSN should work, and require butterfly to
  behave as if true 1-1 channels existed

* Create a 1-1 channel for the conversation with Mercutio

* Upgrade it to a chatroom as in req19_, which would
  create an ad-hoc chatroom in the same bundle

_`req18`: Ad-hoc chatroom embedded in collaboraborative app
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo is collaborating on a document with Mercutio, and wants to have a chat
embedded in his AbiWord instance, separate from any other chat with Mercutio
that may be ongoing.

Current implementation: unclear

Possible implementation::

    RequestChannel (Text, NONE, 0, suppress_handler=TRUE)
    AddMembers ([mercutio])

Problems:

* Are we allowed to have two MSN switchboards with the same two members?

Proposed implementation: same as req3_

_`req19`: Upgrading a 1-1 chat to a named or ad-hoc chatroom
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Same as http://www.xmpp.org/extensions/xep-0045.html §7.6. XMPP does this
by using thread IDs.

Current implementation: can't be done

Hypothetical implementation: request a chatroom channel with the same (thread
ID and) bundle ID as the 1-1 chat?

Resolution: defer the threads spec til later,
https://bugs.freedesktop.org/show_bug.cgi?id=16544

File transfers
--------------

_`req20`: Sending a file in the context of a conversation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo is talking to Juliet using a text or VoIP UI, and wishes to
send Juliet a file in the context of that conversation.

_`req20a`: Romeo's Text or VoIP UI also supports file transfers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Proposed implementation::

    Let bundle_id be the value of the Bundle property of the existing
    Text or StreamedMedia channel.

    client calls some unspecified method on ChannelDispatcher (FIXME)

    ChannelDispatcher calls RequestChannels (
        SUPPRESS_HANDLER,
        [
            {'...Channel.ChannelType': '...Channel.Type.FileTransfer',
             '...Channel.TargetHandleType': CONTACT,
             '...Channel.TargetHandle': juliet,
             '...Channel.Bundle': bundle_id,
             # exact attributes of file offers undecided, but might include:
             '...Channel.Type.FileTransfer.ContentType': 'image/png',
             ...
            }
        ])

    channel observers run
    ChannelDispatcher returns it to the text or VoIP UI, which handles it
    channel approvers do not run
    channel handler does not run

_`req20b`: Romeo's Text or VoIP UI does not support file transfers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Proposed implementation::

    Let bundle_id be the value of the Bundle property of the existing
    Text or StreamedMedia channel.

    client calls some unspecified method on ChannelDispatcher (FIXME)

    ChannelDispatcher calls RequestChannels (
        0,
        [
            {'...Channel.ChannelType': '...Channel.Type.FileTransfer',
             '...Channel.TargetHandleType': CONTACT,
             '...Channel.TargetHandle': juliet,
             '...Channel.Bundle': bundle_id,
             # exact attributes of file offers undecided, but might include:
             '...Channel.Type.FileTransfer.ContentType': 'image/png',
             ...
            }
        ])

    channel observers run
    ChannelDispatcher returns it to the text or VoIP UI, which does nothing
        with it
    channel approvers do not run
    a channel handler chosen in an implementation-specific way runs

_`req21`: Sending a file from a file manager
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo right-clicks on a file in his file manager, chooses a "Send to User"
option and chooses to send it to Juliet.

Proposed implementation: file manager is the channel handler

_`req22`: Sending a file automatically in a collaborative application
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

While collaborating on a document with Mercutio, Romeo inserts an embedded
image into the document. The collaborative application could usefully choose
to represent this by a file transfer.

Proposed implementation::

    bundle_id = ...Bundle property of AbiWord Tube channel

    client calls some unspecified method on ChannelDispatcher (FIXME)

    ChannelDispatcher calls RequestChannels (SUPPRESS_HANDLER,
        [
            {'...ChannelType': '...FileTransfer',
             '...TargetHandleType': CONTACT,
             '...TargetHandle': mercutio,
             '...Bundle': bundle_id,
             ...
             }
        ])

    channel observers run if the channel did not already exist
    channel approvers/handler do not run
    ChannelDispatcher returns channel to AbiWord

    if channel has the requested bundle ID:
        AbiWord uses it
    else:
        ??? FIXME

Tubes
-----

_`req23`: One Laptop per Child Activities, as of early 2008
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

An OLPC Activity instance encapsulates an instance of an application,
zero or more D-Bus tubes and zero or more stream tubes to transfer messages
or state between participants, and a text chatroom to discuss the activity.

In the "1.0" protocol used in early 2008, each Activity instance is backed
by an XMPP or Clique_ MUC (chatroom).

Current implementation: we assume that the channels (Tubes, ROOM, foo)
and (Text, ROOM, foo) correspond 1:1. Activity discovery is done out-of-band
using OLPC-specific extensions, although we'd like to make some of it
more standard (mainly invitations).

Problems addressed by proposed implementation:

* we don't want Tubes channels in their current form, since dispatching them
  is likely to be a bit of a nightmare if we can't rely on OLPC assumptions;
  we want one channel per Tube instead

* in a less constrained environment, two different collaborative applications
  could conceivably share a MUC (the OLPC UI can't cause this to happen, but
  would likely get incredibly confused if it did)

.. _Clique: http://telepathy.freedesktop.org/xmpp/clique.html

Proposed implementation:

* Associate each Activity with a ChannelBundle using XMPP threading

_`req24`: Existing UDP/TCP client/server protocol, serving one client
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Tybalt asks Juliet to help him fix a problem with his computer. He offers
her a VNC connection to his computer so she can interact with his desktop.

Proposed implementation:

* The TCP tube is a channel; much like req1_ or req2_

_`req25`: Existing UDP/TCP client/server protocol, serving many clients
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo offers Mercutio and Benvolio access to an OpenArena server running
on his local computer.

Proposed implementation:

* The UDP tube is a channel; much like req1_ or req2_

Failures and other exceptional cases
------------------------------------

_`req34`: failing to send a text message
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo opens a text channel to Juliet to send a message, but Juliet's server
is down and Romeo's server signals failure. (This is mostly applicable to
decentralized protocols like XMPP and SIP.)

Current implementation::

    the message is sent
    SendError (and soon DeliveryReporting) report the failure
    the channel remains open

Proposed implementation: keep the current implementation

_`req35`: failing to make a call
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo makes a VoIP call to Juliet, but Juliet's server crashes and failure
is signalled.

Current implementation::

    Juliet is removed from the Group interface, with an error for the reason
    the StreamedMedia channel closes

Proposed implementation: keep the current implementation?

_`req36`: Cancelling outgoing call
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Juliet starts a VoIP call to Tybalt, but then thinks better of it and
cancels the call before the channel has actually been opened.

Current implementation (NMC 4.x)::

    UI calls mission_control_cancel_channel_request()

    if dispatching of the channel has already begun:
        cancellation succeeds
    else:
        cancellation fails
        the UI is asked to handle the channel

Problems:

* In NMC 4.x the UI cannot distinguish between the channel that it no longer
  cares about and should close/refuse (this use case), and a channel requested
  by another process but handled by it (req5_)

Proposed implementation:

* FIXME

_`req37`: Requesting a channel takes time
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo makes a VoIP call to Juliet from a Maemo device at a time when he has no
connectivity. Mission Control (the ChannelDispatcher implementation) on Maemo
is able to request that the device obtains some sort of connection when
needed, so it does so. However, Romeo is not near a wireless LAN access
point, and it takes a couple of minutes for him to walk towards one.

Naive implementation: the request is a method call, the request being
satisfied is a response

Problems addressed by proposed implementation: the D-Bus method call will
time out after around 25 seconds unless special action is taken

Proposed implementation: the ChannelDispatcher creates a request "cursor"
object for the duration of the request

General issues
--------------

Selecting a channel handler
~~~~~~~~~~~~~~~~~~~~~~~~~~~

We can solve this later. For now, it's implementation-dependent which
handler is used if several are possible - the channel dispatcher can use
any reasonable algorithm to choose one.

Selecting new or existing channels
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

We assume that creating a channel has visible side-effects, and that this
is undesirable in some cases.

When requesting a single channel, there are four possibilities:

* A: Only a new channel is acceptable

* B: Creating a new channel is preferable, but returning an existing channel
  would be OK too

* C: Returning an existing channel is preferable, but creating a new channel
  would be OK too

* D: Only an existing channel is acceptable - creating a new channel is to be
  avoided

When requesting a bundle of channels, everything gets more complicated - if
we're failing a request because the client wanted all new channels but got
one existing channel, we don't want the CM to create *any* new channels.

Proposed implementation:

* invent an error "already exists"

* the default is to require all the channels to be new; the CM should fail
  the request (e.g. with "already exists") if it can't create *all* the
  channels anew, and it should do this without creating *any* channels
  if possible

* case A: is the default

* case B: we've concluded that this is weird. Who'd want this?

* case C: pass the REUSE flag. The CM will return existing channels
  if it can, or new ones if not

* case D: don't use RequestChannels; we could define a QueryChannels if there
  is demand

..
  vim:set sw=4 sts=4 et:
<commit_msg>Mostly dismiss req26b, suggesting a possible implementation (also make the use-case more concrete to make it easier to talk about)<commit_after>Use cases for requesting channels
=================================

.. contents::

Outgoing 1-1 text chat
----------------------

_`req1`: Chat from chat UI
~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo has a chat or IM UI open already, and wants to use it to chat to Juliet.
He selects Juliet from a contact list or types in her username on some IM
service.

:New vs. existing:
    New channel required [#]_
:Definition of channel identity:
    ChannelType is Text, TargetHandleType is CONTACT, TargetHandle is juliet

.. [#] If the chat UI is already handling a suitable channel, it can
    reasonably be expected to work this out without the channel dispatcher's
    help

Current implementation::

    if a channel with GetHandle() -> (CONTACT, juliet) exists:
        foreground its window or tab
    else:
        RequestChannel (Text, CONTACT, juliet, suppress_handler=TRUE)

Proposed implementation::

    if a channel with GetHandle() -> (CONTACT, juliet) exists:
        foreground its window or tab (no interaction with the
        ChannelDispatcher)
    else:
        client calls some unspecified method on ChannelDispatcher (FIXME)

        ChannelDispatcher calls RequestChannels (
            SUPPRESS_HANDLER,
            [
                {'...ChannelType': '...Text',
                 '...TargetHandleType': CONTACT,
                 '...TargetHandle': juliet
                }
            ])
        # FIXME: the CD could pass in some sort of "require new" flag?

        if the channel that is returned has the EXISTING flag:
            UI fails with "already talking to Juliet in another application"
        else:
            channel observers run
            ChannelDispatcher returns it to the UI
            UI makes a new window or tab for it

        channel approvers do not run
        channel handler does not run

_`req2`: Chat from elsewhere
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Juliet wants to talk to Romeo. She chooses his entry in an address book
or other list of people (not necessarily Telepathy-centric) and is presented
with a list of possible ways to talk to him. She decides to use text chat.

:New vs. existing:
    Existing channel preferred, new channel acceptable
:Definition of channel identity:
    ChannelType is Text, TargetHandleType is CONTACT, TargetHandle is romeo

Current implementation::

    address book asks Mission Control for a channel with
        (Text, CONTACT, romeo)
    Mission Control calls RequestChannel (Text, CONTACT, romeo,
        suppress_handler=FALSE) on CM
    Mission Control dispatches the channel to the default/only handler

    if the channel is new:
        the channel handler creates a new window or tab
    else:
        the channel handler puts the existing window or tab in the foreground

Problems addressed by proposed implementation:

* It's rather bizarre that Mission Control re-dispatches an existing channel
  as though it was new (proposed solution: a Present() method instead)

* It's very bizarre that the channel handler interprets HandleChannel on a
  channel it's already handling as "put it in the foreground". If we mean
  "put this in the foreground" we should say so (proposed solution: do so)

Proposed implementation::

    client calls some unspecified method on ChannelDispatcher (FIXME)

    ChannelDispatcher calls RequestChannels (REUSE,
        [
            {'...ChannelType': '...Text',
             '...TargetHandleType': CONTACT,
             '...TargetHandle': juliet
            }
        ])

    if the channel that is returned has the EXISTING flag:
        ChannelDispatcher returns it to the address book, which does nothing
            much with it
        ChannelDispatcher calls ChannelHandler.Present() on the existing
            handler (which it already knows about)
        channel observers/approvers do not run
    else:
        channel observers run
        ChannelDispatcher returns it to the address book, which does nothing
            much with it
        channel approvers do not run
        ChannelDispatcher chooses a channel handler in an
            implementation-specific way and calls
            ChannelHandler.HandleChannel() on it

_`req3`: collaborative app
~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo is collaborating on a document with Mercutio, and wants to have a chat
embedded in his AbiWord instance, separate from any other chat with Mercutio
that may be ongoing.

:New vs. existing:
    New channel required [#]_
:Definition of channel identity:
    ChannelType is Text, TargetHandleType is CONTACT, TargetHandle is
    mercutio, Bundle is the same as the AbiWord Tube channel

.. [#] If the collaborative app already has a suitable channel, it is expected
    to work this out without the channel dispatcher's help.
    Stealing a channel from another UI is likely to fail (e.g. in the Text
    interface, they'll both try to acknowledge messages) so we should
    forbid this for sanity's sake

Current implementation: impossible, even in protocols supporting
conversation threads, because the spec can't represent them

Proposed implementation, assuming AbiWord does not have a Text channel yet::

    bundle_id = ...Bundle property of AbiWord Tube channel

    client calls some unspecified method on ChannelDispatcher (FIXME)

    ChannelDispatcher calls RequestChannels (
        SUPPRESS_HANDLER,
        [
            {'...ChannelType': '...Text',
             '...TargetHandleType': CONTACT,
             '...TargetHandle': mercutio,
             '...Bundle': bundle_id,
             }
        ])

    if a new channel that is suitable can be created:
        channel observers run in response to NewChannels
        channel approvers do not run, because the channel is one that
            was requested
        CM returns the channel "channel2" to ChannelDispatcher
        ChannelDispatcher returns the channel "channel2" to AbiWord
        ChannelDispatcher also calls HandleChannel on AbiWord

        if channel2.Bundle == bundle_id:
            that's OK
        else if channel2 has no bundle:
            that's OK too (for legacy CMs with no concept of bundles)
        else:
            (error, the CM is being stupid)
    else:
        AbiWord displays error
        if the error is the EEXIST equivalent, the message might be
            "Already talking to Mercutio in another app, and multiple threads
            are not possible in this protocol"

(Fallback behaviour if the CM is pre-Requests: all requests are made with
suppress_handler = TRUE; the ChannelDispatcher knows which ones it wants
handled.)

_`req26`: Recovering from disconnection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo is talking to Juliet using text chat, but is disconnected due to network
instability. After reconnecting, he wants to keep using the same window to
talk to Juliet.

A solution for this use case should work correctly (and result in
a single channel) if there is a "mid-air collision" with Juliet doing the
same thing, with Juliet sending messages to Romeo while he is still
offline (on store-and-forward protocols like XMPP), or with Juliet
recovering from Romeo's disconnection as per req28_ (on protocols that
do not allow offline messages).

(Recovering from a connection manager crash is equivalent to this.)

:New vs. existing:
    ???
:Definition of channel identity:
    ChannelType is Text, TargetHandleType is CONTACT, TargetHandle is juliet,
    ThreadID is the same as before

Current implementation: same as req1_

Problems addressed by proposed implementation:

* the two conversations are unrelated (Juliet cannot distinguish
  between this case and req1_)

Proposed implementation (with a new Chan.I.Thread)::

    Romeo's chat UI (or incoming message database) automatically saves the
    ...Channel.Interface.Thread.ThreadID property of the old channel

    Disconnect/reconnect occurs

    Client calls some unspecified method on ChannelDispatcher (FIXME)

    ChannelDispatcher calls RequestChannels (SUPPRESS_HANDLER,
        [
            {'...Channel.ChannelType': '...Text',
             '...Channel.TargetHandleType': CONTACT,
             '...Channel.TargetHandle': juliet,
             '...Channel.Interface.Thread.ThreadID': thread_id,
             }
        ])

    from here onwards, same as req1

Problems remaining:

* What should the CM do if the desired thread ID cannot be used for some
  reason?

* There is a potential race, req26b_

Resolution: defer the threads spec til later,
https://bugs.freedesktop.org/show_bug.cgi?id=16544

_`req26b`: potential race
^^^^^^^^^^^^^^^^^^^^^^^^^

The same as req26_, but before Romeo's client can open the replacement
channel, Juliet sends him a message, thus opening a new channel. (This
is really a dispatching problem, but is closely related...)

The desired behaviour is that the same handler receives the channel.

Imagine that Romeo has both Kopete and Empathy installed, and Empathy
is the default, but Romeo is using Kopete to talk to Juliet.

Naive implementation: either the race is won by the request for a
replacement channel (and Kopete gets it) or it's won by Juliet's message
creating a new channel (and Empathy gets it).

Too-clever implementation: in principle, there's nothing to stop the
channel dispatcher remembering that a channel handler has lost a channel,
and using that as input to its handler-choosing heuristic

_`req27`: Resuming a conversation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo chooses a past conversation with Juliet in a log browser and wants
to resume it. (The definition of threading in XMPP expects that this is
possible.)

:New vs. existing:
    Existing channel preferred, new channel acceptable
:Definition of channel identity:
    ChannelType is Text, TargetHandleType is CONTACT, TargetHandle is juliet,
    ThreadID is the same as before

Current implementation: same as req2_

Problems addressed by proposed implementation: Juliet cannot distinguish
between this case and req2_

Proposed implementation: same as req26_, except that it resembles
req2_ instead of req1_ (i.e. no SUPPRESS_HANDLER flag)

Problems remaining: same as req26_ (including a potential race, like req26b_)

Resolution: defer the threads spec til later,
https://bugs.freedesktop.org/show_bug.cgi?id=16544

_`req28`: Recovering from other's disconnection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Juliet is talking to Romeo using text chat when Romeo is disconnected
due to network instability. The protocol is one that does not
allow offline messages to be sent, like IRC. After Romeo reconnects, Juliet
wants to keep using the same window to talk to him.

A solution for this use case should work correctly (and result in
a single channel) if there is a "mid-air collision" with Romeo doing the
same thing, or with Romeo recovering from disconnection as per req26_.

(Recovering from Romeo's connection manager crash is equivalent to this.)

Current implementation: Juliet's text channel does not close, but
she cannot send messages. When Romeo reconnects, because of 1-1 chat
uniqueness, Juliet's client continues to use the same channel and there
is no disconnection.

Proposed implementation: Juliet's client continues to use the same channel

Problems remaining: how do we ensure that?

Outgoing VoIP call
------------------

_`req4`: Call from call UI
~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo has a VoIP UI open already, and wants to use it to chat to Juliet.
He selects Juliet from a contact list or types in her username on some IM
service.

:New vs. existing:
    New channel required (same reasoning as req1_)
:Definition of channel identity:
    ???

Theoretical implementation::

    if a channel containing handle juliet exists:
        foreground its window or tab
    else:
        RequestChannel (StreamedMedia, NONE, 0, suppress_handler=TRUE)
        RequestStreams (juliet, [AUDIO, VIDEO])

Problems:

* This is what the spec says we should do, but it doesn't actually work yet,
  at least in telepathy-gabble
  <https://bugs.freedesktop.org/show_bug.cgi?id=14602>
* Finding out whether a channel containing juliet's handle exists is
  needlessly laborious

Resolved problems:

* Unless the VoIP UI keeps a table of (handle => channel) (which can't
  necessarily be done - some protocols allow "parallel" calls), the following
  race condition::

    choose to call Juliet
    RequestChannel (StreamedMedia, NONE, 0, suppress_handler=TRUE) (request A)
    choose to call Juliet
    RequestChannel (StreamedMedia, NONE, 0, suppress_handler=TRUE) (request B)
    Request A returns /.../ChannelA
    Request B returns /.../ChannelB

  can result in unnecessarily opening two parallel calls to the same contact.

  (For instance: Empathy users sometimes incorrectly double-click on the
  Call button, resulting in two calls.)

  Resolution: UIs are responsible for not doing this. For instance, Empathy
  should disable (make insensitive) the Call button just before requesting
  a streamed media channel, and re-enable it when the request has either
  failed or succeeded.

Practical implementation::

    if a channel containing handle juliet exists:
        foreground its window or tab
    else:
        RequestChannel (StreamedMedia, NONE, 0, suppress_handler=TRUE)
        AddMembers ([juliet])
        RequestStreams (juliet, [AUDIO, VIDEO])

Problems:

* Same needlessly laborious processing as in the theoretical implementation
* Same race condition as in the theoretical implementation
* Juliet appears in the remote-pending set before any attempt has really
  been made to call her, which is misleading

Deprecated implementation::

    if a channel containing handle juliet exists:
        foreground its window or tab
    else:
        RequestChannel (StreamedMedia, CONTACT, juliet, suppress_handler=TRUE)
        RequestStreams (juliet, [AUDIO, VIDEO])

Problems:

* All the problems of the practical implementation
* Implementors might be misled into thinking that the semantics resemble
  text channels more closely than they really do

Proposed implementation: In the medium term, StreamedMedia channels must
retain handle type NONE. However, UIs request handle type CONTACT and
the channel dispatcher does the heavy lifting.

_`req5`: Call from elsewhere
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Juliet wants to talk to Romeo. She chooses his entry in an address book
or other list of people (not necessarily Telepathy-centric) and is presented
with a list of possible ways to talk to him. She decides to use a VoIP call.

:New vs. existing:
    Existing channel preferred? New channel acceptable
:Definition of channel identity:
    ???

Current implementation::

    RequestChannel (StreamedMedia, NONE, 0, suppress_handler=FALSE)
    perhaps AddMembers ([romeo])
    RequestStreams (romeo, [AUDIO, VIDEO])

    The channel handler creates a new window or tab for the new channel

Problems:

* The requester has to keep interacting with the channel, it's not
  "fire and forget"
* Creates a new channel, which is unlikely to be what Juliet wanted

Deprecated (?) implementation::

    ask Mission Control for a channel (StreamedMedia, CONTACT, romeo)
    Mission Control does... something?

Problems:

* Mission Control doesn't know whether to use an existing channel to Romeo,
  or create a new one (using an existing channel is *probably* right)

* Looking for channels to talk to Romeo is hard (have to interact with lots of
  group interfaces)

Proposed implementation: In the medium term, StreamedMedia channels must
retain handle type NONE. However, UIs request handle type CONTACT and
the channel dispatcher does the heavy lifting.

_`req6`: collaborative app
~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo is collaborating on a document with Mercutio, and wants to have a chat
embedded in his AbiWord instance, separate from any other chat with Mercutio
that may be ongoing.

Current implementation: same as req5_

Problems: same as req5_

Proposed implementation: same as req5_, but ask for a new channel in a
bundle

_`req29`: Recovering from disconnection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo is talking to Juliet using VoIP, but is disconnected due to network
instability. After reconnecting, he wants to keep using the same window to
talk to Juliet.

A solution for this use case should ideally work correctly (and result in
a single channel) if there is a "mid-air collision" with Juliet doing the
same thing, or with Juliet recovering from Romeo's disconnection as per
req30_.

(Recovering from a connection manager crash is equivalent to this.)

Current implementation: same as req4_

Problems:

* the two conversations are unrelated (Juliet cannot distinguish
  between this case and req4_)

* if Juliet calls Romeo at the same time that Romeo calls Juliet, a
  collision occurs and both calls probably fail with error BUSY

Proposed implementation: FIXME

_`req30`: Recovering from other's disconnection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Juliet is talking to Romeo using VoIP when Romeo is disconnected
due to network instability. After reconnecting, Juliet
wants to keep using the same window to talk to Romeo.

A solution for this use case should ideally work correctly (and result in
a single channel) if there is a "mid-air collision" with Romeo doing the
same thing, or with Romeo recovering from disconnection as per req29_.

(Recovering from a connection manager crash is equivalent to this.)

Current implementation: same as req4_

Problems:

* the two conversations are unrelated (Romeo cannot distinguish
  between this case and req4_)

* if Juliet calls Romeo at the same time that Romeo calls Juliet, a
  collision occurs and both calls probably fail with error BUSY

Proposed implementation: FIXME

Joining a named chatroom by request
-----------------------------------

_`req7`: joining chatroom from chatroom UI
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Tybalt starts an IRC-style chatroom client and wants to join a chatroom, either
by explicit request or because his client auto-joins his favourite rooms.

:New vs. existing:
    ???
:Definition of channel identity:
    ChannelType is Text, TargetHandleType is ROOM, TargetHandle is #capulet

Current implementation::

    if a channel with GetHandle() -> (ROOM, chatroom_handle) exists:
        foreground its window or tab
    else:
        RequestChannel (Text, ROOM, chatroom_handle, suppress_handler=TRUE)

Problems addressed by proposed implementation:

* Tybalt doesn't get a chance to choose his nickname before joining

Proposed implementation: some new interface for this functionality is
created, like Chan.I.Chatroom. RequestChannels arguments contain::

    {'...Channel.ChannelType': '...Channel.Type.Text',
     '...Channel.TargetHandleType': ROOM,
     '...Channel.TargetHandle': chatroom_handle,
     '...Channel.Interface.Chatroom.Nickname': '<NAME>',
    }

Proplems remaining:

* How do threads work? By analogy with 1-1 messaging, they should be
  separate channels - is this really what we want?

  Resolution: solve later, https://bugs.freedesktop.org/show_bug.cgi?id=16544

_`req8`: joining chatroom from elsewhere
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Benvolio chooses to rejoin a recently-used chatroom from (hypothetical
functionality of) the GNOME Places menu.

:New vs. existing:
    ???
:Definition of channel identity:
    ChannelType is Text, TargetHandleType is ROOM, TargetHandle is #capulet

Current implementation::

    GNOME menu asks Mission Control for a channel with
        (Text, ROOM, chatroom_handle)
    Mission Control calls RequestChannel (Text, ROOM, chatroom_handle,
        suppress_handler=FALSE) on CM
    Mission Control dispatches the channel to the default/only handler

    if the channel is new:
        the channel handler creates a new window or tab
    else:
        the channel handler puts the existing window or tab in the foreground

Problems with proposed solutions:

* As for req7_, Benvolio doesn't get a chance to choose his nickname before
  joining (proposed solution: same as req7_)

* As for req2_, it's rather bizarre that Mission Control re-dispatches an
  existing channel as though it was new (proposed solution: same as req2_)

* As for req2_, it's very bizarre that the channel handler interprets
  HandleChannel on a channel it's already handling as "put it in the
  foreground" (proposed solution: same as req2_)

Proposed implementation: a combination of req2_ and req7_

Listing named chatrooms
-----------------------

_`req9`: listing chatrooms on "home" server
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo wants to list all the chatrooms on the server or service that hosts his
account.

:New vs. existing:
    New (parallel) channel preferred, but might not be possible
:Definition of channel identity:
    None - the returned channel probably looks like one from req10_

Current implementation::

    RequestChannel (RoomList, NONE, 0, suppress_handler=TRUE)

Notes:

* There doesn't seem to be any use case for suppress_handler=FALSE here,
  since a default handler for chatroom lists doesn't really make sense

Implementation problems not affecting design:

* Gabble implements this badly, by treating the room list as a singleton

* Some protocols (IRC!) are terrible, and on these, the room list actually
  *is* a singleton

Proposed implementation: a straightforward port of the current API. In the
returned channel, the Channel.Type.RoomList.Server property will be the
actual server name, rather than empty

_`req10`: listing chatrooms on "foreign" server
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo wants to list all the chatrooms on the server ``capulet.example.com``
that hosts Juliet's account.

:New vs. existing:
    New (parallel) channel preferred, but might not be possible
:Definition of channel identity:
    ChannelType is RoomList, RoomList.Server is capulet.example.com

Current implementation: impossible

Proposed implementation: in the request, set the Channel.Type.RoomList.Server
property to the desired DNS name

Contact lists
-------------

_`req11`: typical contact list UI
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Current best-practice to get contact lists (should not use ListChannels
and NewChannel, cf my conversation with Zdra in #telepathy on 2008-04-25)::

    RequestChannel (ContactList, CONTACT_LIST, handle("subscribe"))
    RequestChannel (ContactList, CONTACT_LIST, handle("publish"))
    RequestChannel (ContactList, CONTACT_LIST, handle("hide"))
    RequestChannel (ContactList, CONTACT_LIST, handle("allow"))
    RequestChannel (ContactList, CONTACT_LIST, handle("deny"))

Current best-practice to get initial user-defined groups::

    ListChannels ()

(finding new groups will be part of the "incoming" use cases list)

Problems:

* Slightly unclear whether suppress_handler should be TRUE or FALSE -
  depends on "incoming" use cases

Proposed implementation _`req11impl1`:

* Everyone who cares about contact lists requests them, with
  suppress_handler=FALSE

* There is never a channel handler for contact lists

* Approvers do not run, because the contact lists are not "incoming"

Problems with req11impl1_:

* If there is no channel handler, we need to make sure the channel
  dispatcher won't treat this as an error, or panic and
  try to close the channel

_`req12`: creating a user-defined contact group
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Current implementation::

    RequestChannel (ContactList, GROUP, handle("Colleagues"),
        suppress_handler=FALSE)

Proposed implementation: a straightforward port to the new API

Ad-hoc chatrooms
----------------

This section refers to protocols like MSN, where what appears to be a 1-1
conversation is actually just an unnamed chatroom into which other users
can be invited.

_`req13`: Chat from chat UI (MSN)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

(The same as req1_, but Romeo and Juliet are using a "fully correct" MSN
implementation like telepathy-butterfly, or some similar protocol.)

Romeo has a chat or IM UI open already, and wants to use it to chat to Juliet.
He selects Juliet from a contact list or types in her username on some IM
service.

Hypothetical implementation::

    if a channel with GetHandle() -> (NONE, 0) exists, and its set of members
    contains Juliet's handle, and no other handles other than Romeo's:
        foreground its window or tab
    else:
        either:
            RequestChannel (Text, CONTACT, juliet, suppress_handler=TRUE)
        or:
            RequestChannel (Text, NONE, 0, suppress_handler=TRUE)
            AddMembers([juliet])

(Capability discovery can be used to decide whether to use the first or
second way to request a channel - the second must be used if we intend to
invite others to the channel later)

Problems:

* Determining whether we already have a channel to Juliet becomes harder
  than in req1_

Proposed implementation:

* Rethink our opinion of how MSN should work, and require butterfly to
  behave as if true 1-1 channels existed; then this reduces to req1_

_`req31`: Recovering from disconnection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo is talking to Juliet using text chat, but is disconnected due to network
instability. After reconnecting, he wants to keep using the same window to
talk to Juliet.

A solution for this use case should work correctly (and result in
a single channel) if there is a "mid-air collision" with Juliet doing the
same thing, with Juliet sending messages to Romeo while he is still
offline (on store-and-forward protocols like XMPP), or with Juliet
recovering from Romeo's disconnection as per req33_ (on protocols that
do not allow offline messages).

(Recovering from a connection manager crash is equivalent to this.)

Current implementation: same as req13_

Problems:

* the two conversations are unrelated (Juliet cannot distinguish
  between this case and req13_)

* A mid-air collision is highly likely to result in two parallel
  conversations with the same members (if this is even allowed by the
  protocol)

* the interaction with offline messages is quite scary

Proposed implementation:

* Rethink our opinion of how MSN should work, and require butterfly to
  behave as if true 1-1 channels existed; then this reduces to req26_

_`req32`: Resuming a conversation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo chooses a past conversation with Juliet in a log browser and wants
to resume it. (The definition of threading in XMPP expects that this is
possible.)

This is basically req31_ but for a different reason; I expect that the
solution can be similar.

Current implementation: same as req13_

Problems: Juliet cannot distinguish between this case and req13_

Proposed implementation:

* Rethink our opinion of how MSN should work, and require butterfly to
  behave as if true 1-1 channels existed; then this reduces to req27_

_`req33`: Recovering from other's disconnection
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Juliet is talking to Romeo using text chat when Romeo is disconnected
due to network instability. The protocol is one that does not
allow offline messages to be sent, like IRC. After reconnecting, Juliet
wants to keep using the same window to talk to Romeo.

A solution for this use case should work correctly (and result in
a single channel) if there is a "mid-air collision" with Romeo doing the
same thing, or with Romeo recovering from disconnection as per req31_.

(Recovering from a connection manager crash is equivalent to this.)

Current implementation: unclear

Problems:

* A mid-air collision is highly likely to result in two parallel
  conversations with the same members (if this is even allowed by the
  protocol)

Proposed implementation:

* Rethink our opinion of how MSN should work, and require butterfly to
  behave as if true 1-1 channels existed; then this reduces to req28_

_`req14`: Ad-hoc chatroom from chat UI
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo wants to talk to both Mercutio and Benvolio in an ad-hoc chatroom.
He selects them both from a contact list, or types in both their usernames.

Hypothetical implementation::

    if a channel with GetHandle() -> (NONE, 0) exists, and its set of members
    contains exactly (Mercutio, Benvolio, Romeo):
        foreground its window or tab
    else:
        RequestChannel (Text, NONE, 0, suppress_handler=TRUE)
        AddMembers([mercutio, benvolio])

Problems:

* Determining whether we already have an appropriate channel is race-prone
  and inconvenient

Proposed implementation: FIXME - is this needed? Can we get away with only
supporting req15_?

_`req15`: Ad-hoc chatroom preferred
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo wants to talk to both Mercutio and Benvolio in an ad-hoc chatroom.
He selects Mercutio from a contact list, or types in Mercutio's username,
then invites Benvolio to the chatroom too.

Hypothetical implementation::

    if a channel with GetHandle() -> (NONE, 0) exists, and its set of members
    contains exactly (Mercutio, Romeo):
        foreground its window or tab
    else if Capabilities interface indicates support for inviting contacts
    to ad-hoc chatrooms:
        RequestChannel (Text, NONE, 0, suppress_handler=TRUE)
        AddMembers([mercutio])
        # when Benvolio is invited...
        AddMembers([benvolio])
    else:
        RequestChannel (Text, CONTACT, [mercutio], suppress_handler=TRUE)
        Do not offer UI for adding Benvolio

Problems:

* The UI can't know that the user would prefer an ad-hoc chatroom, so in
  practice it would always have to do this

Proposed implementation:

* Rethink our opinion of how MSN should work, and require butterfly to
  behave as if true 1-1 channels existed

* Create a 1-1 channel for the conversation with Mercutio

* Upgrade it to a chatroom by the same mechanism as req19_, which would
  create an ad-hoc chatroom in the same bundle

_`req16`: Chat from address book (MSN)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

(The same as req2_, but Romeo and Juliet are using a "fully correct" MSN
implementation like telepathy-butterfly, or some similar protocol.)

Juliet wants to talk to Romeo. She chooses his entry in an address book
or other list of people (not necessarily Telepathy-centric) and is presented
with a list of possible ways to talk to him. She decides to use text chat.

Current implementation: unclear

Possible implementation::

    address book asks Mission Control for a channel with
        (Text, CONTACT, romeo)
    Mission Control calls RequestChannel (Text, CONTACT, romeo,
        suppress_handler=FALSE) on CM
    CM does... something?
    Mission Control dispatches the channel to the default/only handler

    if the channel is new:
        the channel handler creates a new window or tab
    else:
        the channel handler puts the existing window or tab in the foreground

Problems:

* The problems of req2_

* Should the CM pick an existing channel containing only Romeo and Juliet
  (if available), or create a new one?

* If both 1-1 and ad-hoc-chatroom channels are available, this would provide
  a true 1-1 channel - what if an ad-hoc chatroom was preferred?

Proposed implementation:

* Rethink our opinion of how MSN should work, and require butterfly to
  behave as if true 1-1 channels existed

* Create a 1-1 channel for the conversation with Romeo, the same as req2_

_`req17`: Ad-hoc chatroom from address book
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The same as req14_ but from an address book.

Hypothetical implementation:

* address book asks MC for a channel with (Text, NONE, 0)

* it's handled by the default channel handler

* address book adds Mercutio and Benvolio with AddMembers

Problems:

* Surely the "address book" (which might instead be e.g. the GNOME menu)
  shouldn't need to know how to interact with Telepathy in this much
  detail?

* The chat UI getting people inserted into "its" channel by another process
  is quite weird

* No way for the chat UI to re-use an existing channel

* Do protocols like MSN allow us to have two channels with the same members?

Proposed implementation:

* Rethink our opinion of how MSN should work, and require butterfly to
  behave as if true 1-1 channels existed

* Create a 1-1 channel for the conversation with Mercutio

* Upgrade it to a chatroom as in req19_, which would
  create an ad-hoc chatroom in the same bundle

_`req18`: Ad-hoc chatroom embedded in collaboraborative app
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo is collaborating on a document with Mercutio, and wants to have a chat
embedded in his AbiWord instance, separate from any other chat with Mercutio
that may be ongoing.

Current implementation: unclear

Possible implementation::

    RequestChannel (Text, NONE, 0, suppress_handler=TRUE)
    AddMembers ([mercutio])

Problems:

* Are we allowed to have two MSN switchboards with the same two members?

Proposed implementation: same as req3_

_`req19`: Upgrading a 1-1 chat to a named or ad-hoc chatroom
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Same as http://www.xmpp.org/extensions/xep-0045.html §7.6. XMPP does this
by using thread IDs.

Current implementation: can't be done

Hypothetical implementation: request a chatroom channel with the same (thread
ID and) bundle ID as the 1-1 chat?

Resolution: defer the threads spec til later,
https://bugs.freedesktop.org/show_bug.cgi?id=16544

File transfers
--------------

_`req20`: Sending a file in the context of a conversation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo is talking to Juliet using a text or VoIP UI, and wishes to
send Juliet a file in the context of that conversation.

_`req20a`: Romeo's Text or VoIP UI also supports file transfers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Proposed implementation::

    Let bundle_id be the value of the Bundle property of the existing
    Text or StreamedMedia channel.

    client calls some unspecified method on ChannelDispatcher (FIXME)

    ChannelDispatcher calls RequestChannels (
        SUPPRESS_HANDLER,
        [
            {'...Channel.ChannelType': '...Channel.Type.FileTransfer',
             '...Channel.TargetHandleType': CONTACT,
             '...Channel.TargetHandle': juliet,
             '...Channel.Bundle': bundle_id,
             # exact attributes of file offers undecided, but might include:
             '...Channel.Type.FileTransfer.ContentType': 'image/png',
             ...
            }
        ])

    channel observers run
    ChannelDispatcher returns it to the text or VoIP UI, which handles it
    channel approvers do not run
    channel handler does not run

_`req20b`: Romeo's Text or VoIP UI does not support file transfers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Proposed implementation::

    Let bundle_id be the value of the Bundle property of the existing
    Text or StreamedMedia channel.

    client calls some unspecified method on ChannelDispatcher (FIXME)

    ChannelDispatcher calls RequestChannels (
        0,
        [
            {'...Channel.ChannelType': '...Channel.Type.FileTransfer',
             '...Channel.TargetHandleType': CONTACT,
             '...Channel.TargetHandle': juliet,
             '...Channel.Bundle': bundle_id,
             # exact attributes of file offers undecided, but might include:
             '...Channel.Type.FileTransfer.ContentType': 'image/png',
             ...
            }
        ])

    channel observers run
    ChannelDispatcher returns it to the text or VoIP UI, which does nothing
        with it
    channel approvers do not run
    a channel handler chosen in an implementation-specific way runs

_`req21`: Sending a file from a file manager
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo right-clicks on a file in his file manager, chooses a "Send to User"
option and chooses to send it to Juliet.

Proposed implementation: file manager is the channel handler

_`req22`: Sending a file automatically in a collaborative application
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

While collaborating on a document with Mercutio, Romeo inserts an embedded
image into the document. The collaborative application could usefully choose
to represent this by a file transfer.

Proposed implementation::

    bundle_id = ...Bundle property of AbiWord Tube channel

    client calls some unspecified method on ChannelDispatcher (FIXME)

    ChannelDispatcher calls RequestChannels (SUPPRESS_HANDLER,
        [
            {'...ChannelType': '...FileTransfer',
             '...TargetHandleType': CONTACT,
             '...TargetHandle': mercutio,
             '...Bundle': bundle_id,
             ...
             }
        ])

    channel observers run if the channel did not already exist
    channel approvers/handler do not run
    ChannelDispatcher returns channel to AbiWord

    if channel has the requested bundle ID:
        AbiWord uses it
    else:
        ??? FIXME

Tubes
-----

_`req23`: One Laptop per Child Activities, as of early 2008
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

An OLPC Activity instance encapsulates an instance of an application,
zero or more D-Bus tubes and zero or more stream tubes to transfer messages
or state between participants, and a text chatroom to discuss the activity.

In the "1.0" protocol used in early 2008, each Activity instance is backed
by an XMPP or Clique_ MUC (chatroom).

Current implementation: we assume that the channels (Tubes, ROOM, foo)
and (Text, ROOM, foo) correspond 1:1. Activity discovery is done out-of-band
using OLPC-specific extensions, although we'd like to make some of it
more standard (mainly invitations).

Problems addressed by proposed implementation:

* we don't want Tubes channels in their current form, since dispatching them
  is likely to be a bit of a nightmare if we can't rely on OLPC assumptions;
  we want one channel per Tube instead

* in a less constrained environment, two different collaborative applications
  could conceivably share a MUC (the OLPC UI can't cause this to happen, but
  would likely get incredibly confused if it did)

.. _Clique: http://telepathy.freedesktop.org/xmpp/clique.html

Proposed implementation:

* Associate each Activity with a ChannelBundle using XMPP threading

_`req24`: Existing UDP/TCP client/server protocol, serving one client
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Tybalt asks Juliet to help him fix a problem with his computer. He offers
her a VNC connection to his computer so she can interact with his desktop.

Proposed implementation:

* The TCP tube is a channel; much like req1_ or req2_

_`req25`: Existing UDP/TCP client/server protocol, serving many clients
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo offers Mercutio and Benvolio access to an OpenArena server running
on his local computer.

Proposed implementation:

* The UDP tube is a channel; much like req1_ or req2_

Failures and other exceptional cases
------------------------------------

_`req34`: failing to send a text message
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo opens a text channel to Juliet to send a message, but Juliet's server
is down and Romeo's server signals failure. (This is mostly applicable to
decentralized protocols like XMPP and SIP.)

Current implementation::

    the message is sent
    SendError (and soon DeliveryReporting) report the failure
    the channel remains open

Proposed implementation: keep the current implementation

_`req35`: failing to make a call
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo makes a VoIP call to Juliet, but Juliet's server crashes and failure
is signalled.

Current implementation::

    Juliet is removed from the Group interface, with an error for the reason
    the StreamedMedia channel closes

Proposed implementation: keep the current implementation?

_`req36`: Cancelling outgoing call
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Juliet starts a VoIP call to Tybalt, but then thinks better of it and
cancels the call before the channel has actually been opened.

Current implementation (NMC 4.x)::

    UI calls mission_control_cancel_channel_request()

    if dispatching of the channel has already begun:
        cancellation succeeds
    else:
        cancellation fails
        the UI is asked to handle the channel

Problems:

* In NMC 4.x the UI cannot distinguish between the channel that it no longer
  cares about and should close/refuse (this use case), and a channel requested
  by another process but handled by it (req5_)

Proposed implementation:

* FIXME

_`req37`: Requesting a channel takes time
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Romeo makes a VoIP call to Juliet from a Maemo device at a time when he has no
connectivity. Mission Control (the ChannelDispatcher implementation) on Maemo
is able to request that the device obtains some sort of connection when
needed, so it does so. However, Romeo is not near a wireless LAN access
point, and it takes a couple of minutes for him to walk towards one.

Naive implementation: the request is a method call, the request being
satisfied is a response

Problems addressed by proposed implementation: the D-Bus method call will
time out after around 25 seconds unless special action is taken

Proposed implementation: the ChannelDispatcher creates a request "cursor"
object for the duration of the request

General issues
--------------

Selecting a channel handler
~~~~~~~~~~~~~~~~~~~~~~~~~~~

We can solve this later. For now, it's implementation-dependent which
handler is used if several are possible - the channel dispatcher can use
any reasonable algorithm to choose one.

Selecting new or existing channels
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

We assume that creating a channel has visible side-effects, and that this
is undesirable in some cases.

When requesting a single channel, there are four possibilities:

* A: Only a new channel is acceptable

* B: Creating a new channel is preferable, but returning an existing channel
  would be OK too

* C: Returning an existing channel is preferable, but creating a new channel
  would be OK too

* D: Only an existing channel is acceptable - creating a new channel is to be
  avoided

When requesting a bundle of channels, everything gets more complicated - if
we're failing a request because the client wanted all new channels but got
one existing channel, we don't want the CM to create *any* new channels.

Proposed implementation:

* invent an error "already exists"

* the default is to require all the channels to be new; the CM should fail
  the request (e.g. with "already exists") if it can't create *all* the
  channels anew, and it should do this without creating *any* channels
  if possible

* case A: is the default

* case B: we've concluded that this is weird. Who'd want this?

* case C: pass the REUSE flag. The CM will return existing channels
  if it can, or new ones if not

* case D: don't use RequestChannels; we could define a QueryChannels if there
  is demand

..
  vim:set sw=4 sts=4 et:

{'fraction_non_alphanumeric': 0.09422694094226941, 'fraction_numerical': 0.00635630216882618, 'mean_word_length': 3.7568944512127587, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 21, 'https://': 10, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1016208', 'n_tokens_mistral': 22881, 'n_tokens_neox': 21366, 'n_words': 11090}