I've attached patches for Sugar and Chat that handle incoming XMPP 1-1
connections, from a non-Sugar Jabber client.

It's a bit of a proof of concept, because although it works there are
some issues that need improving - so please review and comment... I'm
looking for input from Marco/Tomeu, Eben, Daf/Guillaume...

* From a Jabber client registered on the Jabber server I initiate a
chat with the Buddy.
* Presence Service sends private-invitation to sugar.presence and then
to the shell.
* The signal has the Telepathy channel for this 1-1 connection which
needs to get to Chat.
* I'm (ab)using activityfactory.create_with_uri to launch Chat with
the Telepathy channel in the metadata.
* That makes Chat launch magically. That violates the requirement that
the user initiate the activity.
* I'm using json (probably badly?) to pass the two strings identifying
the Telepathy channel.
* The first message from the non-Sugar client (which initiates the
connection) gets dropped somewhere, and only subsequent messages get
through to Chat.
* Presence Service sends private-invitation on both CHANNEL_TYPE_TEXT
and CHANNEL_TYPE_STREAMED_MEDIA (which should go to video chat
eventually). When we have agreed on how to handle Chat I'll figure out
how to differentiate them and send the media one to video chat.

It has also been suggested that the incoming chat appear as an
invitation. I had a look but couldn't yet figure out how best to
represent something that is not an activity, does not have colors, and
is not a room on the server. Input appreciated.

Thanks
Morgan
diff --git a/src/model/Owner.py b/src/model/Owner.py
index 1447267..73bd5da 100644
--- a/src/model/Owner.py
+++ b/src/model/Owner.py
@@ -1,4 +1,5 @@
 # Copyright (C) 2006-2007 Red Hat, Inc.
+# Copyright (C) 2008 One Laptop Per Child
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -64,6 +65,8 @@ class ShellOwner(gobject.GObject):
         self._pservice = presenceservice.get_instance()
         self._pservice.connect('activity-invitation',
                                self._activity_invitation_cb)
+        self._pservice.connect('private-invitation',
+                               self._private_invitation_cb)
         self._pservice.connect('activity-disappeared',
                               self._activity_disappeared_cb)
 
@@ -79,5 +82,21 @@ class ShellOwner(gobject.GObject):
         self._invites.add_invite(buddy, activity.props.type,
                                  activity.props.id)
 
+    def _private_invitation_cb(self, pservice, bus_name, connection,
+                               channel):
+        """Handle a private-invitation from Presence Service.
+
+        This is a connection by a non-Sugar XMPP client, so
+        launch Chat with the Telepathy connection and channel.
+        """
+        import json
+        from sugar import activity
+        from sugar.activity import activityfactory
+        tp_channel = json.write([str(bus_name), str(connection),
+                                 str(channel)])
+        registry = activity.get_registry()
+        if registry.get_activity('org.laptop.Chat'):
+            activityfactory.create_with_uri('org.laptop.Chat', tp_channel)
+
     def _activity_disappeared_cb(self, pservice, activity):
         self._invites.remove_activity(activity.props.id)
diff --git a/pippy_app.py b/pippy_app.py
index 2e6e5a0..84b722d 100644
--- a/pippy_app.py
+++ b/pippy_app.py
@@ -20,6 +20,7 @@ import gtk
 import pango
 import logging
 import re
+import json
 from datetime import datetime
 from activity import ViewSourceActivity
 
@@ -34,7 +35,8 @@ from sugar.graphics.menuitem import MenuItem
 
 from telepathy.client import Connection
 from telepathy.interfaces import (
-    CHANNEL_INTERFACE_GROUP, CHANNEL_TYPE_TEXT)
+    CHANNEL_INTERFACE_GROUP, CHANNEL_TYPE_TEXT,
+    CONN_INTERFACE_ALIASING)
 from telepathy.constants import (
     CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES,
     CHANNEL_TEXT_MESSAGE_TYPE_NORMAL)
@@ -66,8 +68,8 @@ class Chat(ViewSourceActivity):
         # Track last message, to combine several messages:
         self._last_msg = None
         self._last_msg_sender = None
-
-        self.connect('shared', self._shared_cb)
+        # Chat is room or one to one:
+        self._chat_is_room = False
         self.text_channel = None
         
         if self._shared_activity:
@@ -76,21 +78,49 @@ class Chat(ViewSourceActivity):
             if self.get_shared():
                 # we have already joined
                 self._joined_cb()
+        elif handle.uri:
+            # XMPP non-Sugar incoming chat, not sharable
+            self._one_to_one_connection(handle.uri)
         else:
             # we are creating the activity
             self._alert(_('Off-line'), _('Share, or invite someone.'))
+            self.connect('shared', self._shared_cb)
 
     def _shared_cb(self, activity):
         logger.debug('Chat was shared')
         self._setup()
 
+    def _one_to_one_connection(self, tp_channel):
+        """Handle a private invite from a non-Sugar XMPP client."""
+        from telepathy.client import Channel
+        if self._shared_activity or self.text_channel:
+            return
+        bus_name, connection, channel = json.read(tp_channel)
+        logger.debug('GOT XMPP: %s %s %s', bus_name, connection,
+                     channel)  # XXX
+        conn = Connection(bus_name, connection)
+        text_channel = Channel(bus_name, channel)
+        self.text_channel = TextChannelWrapper(text_channel, conn)
+        self.text_channel.set_received_callback(self._received_cb)
+        self._chat_is_room = False
+        self._alert(_('On-line'), _('Private Chat'))
+        logger.debug('self.text_channel connected')  # XXX
+        # XXX show buddy
+
+        # XXX How do we cope with the sender leaving? Text channel closes -
+        # handle that.
+        self.entry.set_sensitive(True)
+        self.entry.grab_focus()
+
     def _setup(self):
         self.text_channel = TextChannelWrapper(
-            self._shared_activity.telepathy_text_chan)
+            self._shared_activity.telepathy_text_chan,
+            self._shared_activity.telepathy_conn)
         self.text_channel.set_received_callback(self._received_cb)
         self._alert(_('On-line'), _('Connected'))
         self._shared_activity.connect('buddy-joined', self._buddy_joined_cb)
         self._shared_activity.connect('buddy-left', self._buddy_left_cb)
+        self._chat_is_room = True
         self.entry.set_sensitive(True)
         self.entry.grab_focus()
 
@@ -433,10 +463,11 @@ class Chat(ViewSourceActivity):
 
 class TextChannelWrapper(object):
     """Wrap a telepathy Text Channel to make usage simpler."""
-    def __init__(self, text_chan):
+    def __init__(self, text_chan, conn):
         """Connect to the text channel"""
         self._activity_cb = None
         self._text_chan = text_chan
+        self._conn = conn
         self._logger = logging.getLogger(
             'chat-activity.TextChannelWrapper')
 
@@ -461,8 +492,17 @@ class TextChannelWrapper(object):
         Calls self._activity_cb which is a callback to the activity.
         """
         if self._activity_cb:
-            # XXX: cache these
-            buddy = self._get_buddy(sender)
+            try:
+                self._text_chan[CHANNEL_INTERFACE_GROUP]
+            except:
+                # One to one XMPP chat
+                nick = self._conn[CONN_INTERFACE_ALIASING].RequestAliases(
+                    [sender])[0]
+                buddy = {'nick': nick, 'color': '#000000,#808080'}
+            else:
+                # Normal sugar MUC chat
+                # XXX: cache these
+                buddy = self._get_buddy(sender)
             self._activity_cb(buddy, text)
         else:
             self._logger.debug('Throwing received message on the floor'
_______________________________________________
Sugar mailing list
[email protected]
http://lists.laptop.org/listinfo/sugar

Reply via email to