summaryrefslogtreecommitdiff
path: root/sunshine/channel
diff options
context:
space:
mode:
Diffstat (limited to 'sunshine/channel')
-rw-r--r--sunshine/channel/Makefile.am5
-rw-r--r--sunshine/channel/__init__.py18
-rw-r--r--sunshine/channel/contact_list.py371
-rw-r--r--sunshine/channel/group.py223
-rw-r--r--sunshine/channel/text.py198
5 files changed, 815 insertions, 0 deletions
diff --git a/sunshine/channel/Makefile.am b/sunshine/channel/Makefile.am
new file mode 100644
index 0000000..875550b
--- /dev/null
+++ b/sunshine/channel/Makefile.am
@@ -0,0 +1,5 @@
+channeldir = $(pythondir)/sunshine/channel
+channel_PYTHON = contact_list.py \
+ group.py \
+ __init__.py \
+ text.py
diff --git a/sunshine/channel/__init__.py b/sunshine/channel/__init__.py
new file mode 100644
index 0000000..7a6b8a1
--- /dev/null
+++ b/sunshine/channel/__init__.py
@@ -0,0 +1,18 @@
+# telepathy-sunshine is the GaduGadu connection manager for Telepathy
+#
+# Copyright (C) 2006-2007 Ali Sabil <ali.sabil@gmail.com>
+# Copyright (C) 2010 Krzysztof Klinikowski <kkszysiu@gmail.com>
+#
+# 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
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
diff --git a/sunshine/channel/contact_list.py b/sunshine/channel/contact_list.py
new file mode 100644
index 0000000..31b4a02
--- /dev/null
+++ b/sunshine/channel/contact_list.py
@@ -0,0 +1,371 @@
+# telepathy-sunshine is the GaduGadu connection manager for Telepathy
+#
+# Copyright (C) 2006-2007 Ali Sabil <ali.sabil@gmail.com>
+# Copyright (C) 2010 Krzysztof Klinikowski <kkszysiu@gmail.com>
+#
+# 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
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import logging
+import weakref
+
+import telepathy
+
+import xml.etree.ElementTree as ET
+
+from sunshine.util.decorator import async
+from sunshine.handle import SunshineHandleFactory
+
+from sunshine.lqsoft.pygadu.twisted_protocol import GaduClient
+from sunshine.lqsoft.pygadu.models import GaduProfile, GaduContact
+
+__all__ = ['SunshineContactListChannelFactory']
+
+logger = logging.getLogger('Sunshine.ContactListChannel')
+
+class HandleMutex(object):
+ def __init__(self):
+ self._handles = set()
+ self._keys = {}
+ self._callbacks = {}
+
+ def is_locked(self, handle):
+ return (handle in self._handles)
+
+ def is_owned(self, key, handle):
+ return (handle in self._handles and self._keys[handle] == key)
+
+ def lock(self, key, handle):
+ if self.is_locked(handle):
+ return False
+ self._handles.add(handle)
+ self._keys[handle] = key
+ return True
+
+ def unlock(self, key, handle):
+ if not self.is_owned(key, handle):
+ return
+ self._handles.remove(handle)
+ del self._keys[handle]
+ callbacks = self._callbacks.get(handle, [])[:]
+ self._callbacks[handle] = []
+ for callback in callbacks:
+ callback[0](*callback[1:])
+
+ def add_callback(self, key, handle, callback):
+ if self.is_owned(key, handle):
+ return
+ if not self.is_locked(handle):
+ callback[0](*callback[1:])
+ else:
+ self._callbacks.setdefault(handle, []).append(callback)
+
+class Lockable(object):
+ def __init__(self, mutex, key, cb_name):
+ self._mutex = mutex
+ self._key = key
+ self._cb_name = cb_name
+
+ def __call__(self, func):
+ def method(object, handle, *args, **kwargs):
+ def finished_cb(*user_data):
+ self._mutex.unlock(self._key, handle)
+
+ def unlocked_cb():
+ self._mutex.lock(self._key, handle)
+ kwargs[self._cb_name] = finished_cb
+ if func(object, handle, *args, **kwargs):
+ finished_cb()
+
+ self._mutex.add_callback(self._key, handle, (unlocked_cb,))
+
+ return method
+
+mutex = HandleMutex()
+
+
+def SunshineContactListChannelFactory(connection, manager, handle, props):
+ handle = connection.handle(
+ props[telepathy.CHANNEL_INTERFACE + '.TargetHandleType'],
+ props[telepathy.CHANNEL_INTERFACE + '.TargetHandle'])
+
+ if handle.get_name() == 'subscribe':
+ channel_class = SunshineSubscribeListChannel
+ #hacky & tricky
+# elif handle.get_name() == 'publish':
+# channel_class = SunshineSubscribeListChannel
+
+# elif handle.get_name() == 'publish':
+# channel_class = ButterflyPublishListChannel
+# elif handle.get_name() == 'hide':
+# channel_class = ButterflyHideListChannel
+# elif handle.get_name() == 'allow':
+# channel_class = ButterflyAllowListChannel
+# elif handle.get_name() == 'deny':
+# channel_class = ButterflyDenyListChannel
+ else:
+ raise TypeError("Unknown list type : " + handle.get_name())
+ return channel_class(connection, manager, props)
+
+
+class SunshineListChannel(
+ telepathy.server.ChannelTypeContactList,
+ telepathy.server.ChannelInterfaceGroup):
+ "Abstract Contact List channels"
+
+ def __init__(self, connection, manager, props):
+ self._conn_ref = weakref.ref(connection)
+ telepathy.server.ChannelTypeContactList.__init__(self, connection, manager, props)
+ telepathy.server.ChannelInterfaceGroup.__init__(self)
+ self._populate(connection)
+
+ def GetLocalPendingMembersWithInfo(self):
+ return []
+
+ # papyon.event.AddressBookEventInterface
+ def on_addressbook_contact_added(self, contact):
+ added = set()
+ local_pending = set()
+ remote_pending = set()
+
+ ad, lp, rp = self._filter_contact(contact)
+ if ad or lp or rp:
+ handle = ButterflyHandleFactory(self._conn_ref(), 'contact',
+ contact.account, contact.network_id)
+ if ad: added.add(handle)
+ if lp: local_pending.add(handle)
+ if rp: remote_pending.add(handle)
+ msg = contact.attributes.get('invite_message', '')
+ self.MembersChanged(msg, added, (), local_pending, remote_pending, 0,
+ telepathy.CHANNEL_GROUP_CHANGE_REASON_NONE)
+
+ # papyon.event.AddressBookEventInterface
+ def on_addressbook_contact_deleted(self, contact):
+ handle = ButterflyHandleFactory(self._conn_ref(), 'contact',
+ contact.account, contact.network_id)
+ ad, lp, rp = self._filter_contact(contact)
+ if self._contains_handle(handle) and not ad:
+ self.MembersChanged('', (), [handle], (), (), 0,
+ telepathy.CHANNEL_GROUP_CHANGE_REASON_NONE)
+
+ # papyon.event.AddressBookEventInterface
+ def on_addressbook_contact_blocked(self, contact):
+ pass
+
+ # papyon.event.AddressBookEventInterface
+ def on_addressbook_contact_unblocked(self, contact):
+ pass
+
+ @async
+ def _populate(self, connection):
+ added = set()
+ local_pending = set()
+ remote_pending = set()
+
+ for contact in connection.gadu_client.contacts:
+ #logger.info("New contact %s, name: %s added." % (contact.uin, contact.ShowName))
+ ad, lp, rp = self._filter_contact(contact)
+ if ad or lp or rp:
+ handle = SunshineHandleFactory(self._conn_ref(), 'contact',
+ contact.uin, None)
+ if ad: added.add(handle)
+ if lp: local_pending.add(handle)
+ if rp: remote_pending.add(handle)
+ self.MembersChanged('', added, (), local_pending, remote_pending, 0,
+ telepathy.CHANNEL_GROUP_CHANGE_REASON_NONE)
+
+ def _filter_contact(self, contact):
+ return (False, False, False)
+
+ def _contains_handle(self, handle):
+ members, local_pending, remote_pending = self.GetAllMembers()
+ return (handle in members) or (handle in local_pending) or \
+ (handle in remote_pending)
+
+
+class SunshineSubscribeListChannel(SunshineListChannel):
+ """Subscribe List channel.
+
+ This channel contains the list of contact to whom the current used is
+ 'subscribed', basically this list contains the contact for whom you are
+ supposed to receive presence notification."""
+
+ def __init__(self, connection, manager, props):
+ SunshineListChannel.__init__(self, connection, manager, props)
+ self.GroupFlagsChanged(telepathy.CHANNEL_GROUP_FLAG_CAN_ADD |
+ telepathy.CHANNEL_GROUP_FLAG_CAN_REMOVE, 0)
+
+ def AddMembers(self, contacts, message):
+ logger.info("Subscribe - AddMembers called")
+ for h in contacts:
+ handle = self._conn.handle(telepathy.constants.HANDLE_TYPE_CONTACT, h)
+ contact_xml = ET.Element("Contact")
+ ET.SubElement(contact_xml, "Guid").text = str(handle.name)
+ ET.SubElement(contact_xml, "GGNumber").text = str(handle.name)
+ ET.SubElement(contact_xml, "ShowName").text = str(handle.name)
+ ET.SubElement(contact_xml, "Groups")
+ c = GaduContact.from_xml(contact_xml)
+ self._conn_ref().gadu_client.addContact( c )
+ #config.addNewContact( c )
+ self._conn_ref().gadu_client.notifyAboutContact( c )
+ logger.info("Adding contact: %s" % (handle.name))
+ self.MembersChanged('', [handle], (), (), (), 0,
+ telepathy.CHANNEL_GROUP_CHANGE_REASON_INVITED)
+
+ #alias and group settings for new contacts are bit tricky
+ #try to set alias
+ handle.contact.ShowName = self._conn_ref().get_contact_alias(handle.id)
+ #and group
+ if self._conn_ref().pending_contacts_to_group.has_key(handle.name):
+ logger.info("Trying to add temporary group.")
+ print str(self._conn_ref().pending_contacts_to_group)
+ print str(self._conn_ref().pending_contacts_to_group[handle.name])
+ handle.contact.updateGroups(self._conn_ref().pending_contacts_to_group[handle.name])
+ logger.info("Contact added.")
+
+ def RemoveMembers(self, contacts, message):
+ for h in contacts:
+ self._remove(h)
+
+ def _filter_contact(self, contact):
+ return (True, False, False)
+ #return (contact.is_member(papyon.Membership.FORWARD) and not
+ # contact.is_member(papyon.Membership.PENDING), False, False)
+
+ #@Lockable(mutex, 'add_subscribe', 'finished_cb')
+# def _add(self, handle_id, message, finished_cb):
+# logger.info("Subscribe - Add Members called.")
+# handle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, handle_id)
+# if handle.contact is not None and \
+# handle.contact.is_member(papyon.Membership.FORWARD):
+# return True
+#
+# account = handle.account
+# network = handle.network
+# groups = list(handle.pending_groups)
+# handle.pending_groups = set()
+# ab = self._conn.msn_client.address_book
+# ab.add_messenger_contact(account,
+# network_id=network,
+# auto_allow=False,
+# invite_message=message.encode('utf-8'),
+# groups=groups,
+# done_cb=(finished_cb,),
+# failed_cb=(finished_cb,))
+
+ @Lockable(mutex, 'rem_subscribe', 'finished_cb')
+ def _remove(self, handle_id, finished_cb):
+ handle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, handle_id)
+ contact = handle.contact
+ if contact is None or not contact.is_member(papyon.Membership.FORWARD):
+ return True
+ ab = self._conn.msn_client.address_book
+ ab.delete_contact(contact, done_cb=(finished_cb,),
+ failed_cb=(finished_cb,))
+
+ # papyon.event.ContactEventInterface
+ def on_contact_memberships_changed(self, contact):
+ handle = ButterflyHandleFactory(self._conn_ref(), 'contact',
+ contact.account, contact.network_id)
+ if contact.is_member(papyon.Membership.FORWARD):
+ self.MembersChanged('', [handle], (), (), (), 0,
+ telepathy.CHANNEL_GROUP_CHANGE_REASON_INVITED)
+ if len(handle.pending_groups) > 0:
+ ab = self._conn.msn_client.address_book
+ for group in handle.pending_groups:
+ ab.add_contact_to_group(group, contact)
+ handle.pending_groups = set()
+
+#
+#class ButterflyPublishListChannel(ButterflyListChannel,
+# papyon.event.ContactEventInterface):
+#
+# def __init__(self, connection, manager, props):
+# ButterflyListChannel.__init__(self, connection, manager, props)
+# papyon.event.ContactEventInterface.__init__(self, connection.msn_client)
+# self.GroupFlagsChanged(0, 0)
+#
+# def AddMembers(self, contacts, message):
+# for handle_id in contacts:
+# self._add(handle_id, message)
+#
+# def RemoveMembers(self, contacts, message):
+# for handle_id in contacts:
+# self._remove(handle_id)
+#
+# def GetLocalPendingMembersWithInfo(self):
+# result = []
+# for contact in self._conn.msn_client.address_book.contacts:
+# if not contact.is_member(papyon.Membership.PENDING):
+# continue
+# handle = ButterflyHandleFactory(self._conn_ref(), 'contact',
+# contact.account, contact.network_id)
+# result.append((handle, handle,
+# telepathy.CHANNEL_GROUP_CHANGE_REASON_INVITED,
+# contact.attributes.get('invite_message', '')))
+# return result
+#
+# def _filter_contact(self, contact):
+# return (contact.is_member(papyon.Membership.ALLOW),
+# contact.is_member(papyon.Membership.PENDING),
+# False)
+#
+# @Lockable(mutex, 'add_publish', 'finished_cb')
+# def _add(self, handle_id, message, finished_cb):
+# handle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, handle_id)
+# contact = handle.contact
+# if contact is not None and contact.is_member(papyon.Membership.ALLOW):
+# return True
+#
+# account = handle.account
+# network = handle.network
+# ab = self._conn.msn_client.address_book
+# if contact is not None and contact.is_member(papyon.Membership.PENDING):
+# ab.accept_contact_invitation(contact, False,
+# done_cb=(finished_cb,), failed_cb=(finished_cb,))
+# else:
+# ab.allow_contact(account, network,
+# done_cb=(finished_cb,), failed_cb=(finished_cb,))
+#
+# @Lockable(mutex, 'rem_publish', 'finished_cb')
+# def _remove(self, handle_id, finished_cb):
+# handle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, handle_id)
+# contact = handle.contact
+# ab = self._conn.msn_client.address_book
+# if contact.is_member(papyon.Membership.PENDING):
+# ab.decline_contact_invitation(contact, False, done_cb=finished_cb,
+# failed_cb=finished_cb)
+# elif contact.is_member(papyon.Membership.ALLOW):
+# ab.disallow_contact(contact, done_cb=(finished_cb,),
+# failed_cb=(finished_cb,))
+# else:
+# return True
+#
+# # papyon.event.ContactEventInterface
+# def on_contact_memberships_changed(self, contact):
+# handle = ButterflyHandleFactory(self._conn_ref(), 'contact',
+# contact.account, contact.network_id)
+# if self._contains_handle(handle):
+# if contact.is_member(papyon.Membership.PENDING):
+# # Nothing worth our attention
+# return
+#
+# if contact.is_member(papyon.Membership.ALLOW):
+# # Contact accepted
+# self.MembersChanged('', [handle], (), (), (), 0,
+# telepathy.CHANNEL_GROUP_CHANGE_REASON_INVITED)
+# else:
+# # Contact rejected
+# self.MembersChanged('', (), [handle], (), (), 0,
+# telepathy.CHANNEL_GROUP_CHANGE_REASON_NONE)
diff --git a/sunshine/channel/group.py b/sunshine/channel/group.py
new file mode 100644
index 0000000..6094092
--- /dev/null
+++ b/sunshine/channel/group.py
@@ -0,0 +1,223 @@
+# telepathy-sunshine is the GaduGadu connection manager for Telepathy
+#
+# Copyright (C) 2006-2007 Ali Sabil <ali.sabil@gmail.com>
+# Copyright (C) 2007 Johann Prieur <johann.prieur@gmail.com>
+# Copyright (C) 2010 Krzysztof Klinikowski <kkszysiu@gmail.com>
+#
+# 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
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import logging, hashlib
+
+import telepathy
+
+import xml.etree.ElementTree as ET
+
+from sunshine.lqsoft.pygadu.models import GaduProfile, GaduContact, GaduContactGroup
+
+from sunshine.util.decorator import async
+from sunshine.handle import SunshineHandleFactory
+from sunshine.channel.contact_list import SunshineListChannel
+
+__all__ = ['SunshineGroupChannel']
+
+logger = logging.getLogger('Sunshine.GroupChannel')
+
+
+class SunshineGroupChannel(SunshineListChannel):
+
+ def __init__(self, connection, manager, props):
+ self.__pending_add = []
+ self.__pending_remove = []
+ self.conn = connection
+ self.groups = {}
+ SunshineListChannel.__init__(self, connection, manager, props)
+ self.GroupFlagsChanged(telepathy.CHANNEL_GROUP_FLAG_CAN_ADD |
+ telepathy.CHANNEL_GROUP_FLAG_CAN_REMOVE, 0)
+ @async
+ def create_group():
+ if self._handle.group is None:
+ name = self._handle.name
+ for group in self.conn.profile.groups:
+ if group.Name != name:
+ h = hashlib.md5()
+ h.update(name)
+
+ group_xml = ET.Element("Group")
+ ET.SubElement(group_xml, "Id").text = h.hexdigest()
+ ET.SubElement(group_xml, "Name").text = name
+ ET.SubElement(group_xml, "IsExpanded").text = str('True')
+ ET.SubElement(group_xml, "IsRemovable").text = str('True')
+
+ g = GaduContactGroup.from_xml(group_xml)
+ self.conn.profile.addGroup(g)
+
+ for group in self.conn.profile.groups:
+ self.groups[group.Id] = group.Name
+
+ for contact in self.conn.profile.contacts:
+ contact_groups = ET.fromstring(contact.Groups)
+ if contact.Groups:
+ for group in contact_groups.getchildren():
+ if self.groups.has_key(group.text):
+ if self.groups[group.text] == self._handle.group.Name:
+ self.add_contact_to_group(self._handle.group, contact, None)
+ create_group()
+
+
+ def AddMembers(self, contacts, message):
+ for contact_handle_id in contacts:
+ contact_handle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT,
+ contact_handle_id)
+ logger.info("Adding contact %s to group %s" %
+ (unicode(contact_handle), unicode(self._handle)))
+
+ contact = contact_handle.contact
+ group = self._handle.group
+
+ self.add_contact_to_group(group, contact, contact_handle)
+
+
+ def RemoveMembers(self, contacts, message):
+ for contact_handle_id in contacts:
+ contact_handle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT,
+ contact_handle_id)
+ logger.info("Removing contact %s from pending group %s" %
+ (unicode(contact_handle), unicode(self._handle)))
+
+ contact = contact_handle.contact
+ group = self._handle.group
+
+ self.delete_contact_from_group(group, contact, contact_handle)
+
+ def Close(self):
+ logger.debug("Deleting group %s" % self._handle.name)
+ del self.conn.profile.groups[self._handle.name]
+# ab = self._conn.msn_client.address_book
+# group = self._handle.group
+# ab.delete_group(group)
+
+# def _filter_contact(self, contact):
+# if contact.is_member(papyon.Membership.FORWARD):
+# for group in contact.groups:
+# if group.name.decode("utf-8") == self._handle.name:
+# return (True, False, False)
+# return (False, False, False)
+#
+# def on_addressbook_group_added(self, group):
+# if group.name.decode("utf-8") == self._handle.name:
+# self.AddMembers(self.__pending_add, None)
+# self.__pending_add = []
+# self.RemoveMembers(self.__pending_remove, None)
+# self.__pending_remove = []
+#
+# def on_addressbook_group_deleted(self, group):
+# if group.name.decode("utf-8") == self._handle.name:
+# self.Closed()
+# self._conn.remove_channel(self)
+#
+# def on_addressbook_group_contact_added(self, group, contact):
+# group_name = group.name.decode("utf-8")
+# if group_name == self._handle.name:
+# handle = ButterflyHandleFactory(self._conn_ref(), 'contact',
+# contact.account, contact.network_id)
+#
+# added = set()
+# added.add(handle)
+#
+# self.MembersChanged('', added, (), (), (), 0,
+# telepathy.CHANNEL_GROUP_CHANGE_REASON_NONE)
+#
+# logger.debug("Contact %s added to group %s" %
+# (handle.name, group_name))
+#
+# def on_addressbook_group_contact_deleted(self, group, contact):
+# group_name = group.name.decode("utf-8")
+# if group_name == self._handle.name:
+# handle = ButterflyHandleFactory(self._conn_ref(), 'contact',
+# contact.account, contact.network_id)
+#
+# removed = set()
+# removed.add(handle)
+#
+# self.MembersChanged('', (), removed, (), (), 0,
+# telepathy.CHANNEL_GROUP_CHANGE_REASON_NONE)
+#
+# logger.debug("Contact %s removed from group %s" %
+# (handle.name, group_name))
+#
+
+ @async
+ def add_contact_to_group(self, group, contact, contact_handle):
+ group_name = group.Name
+ if group_name == self._handle.name:
+ if hasattr(contact, 'uin'):
+ contact_uin = contact.uin
+ else:
+ contact_uin = contact_handle.name
+
+ handle = SunshineHandleFactory(self.conn, 'contact',
+ contact_uin, None)
+ added = set()
+ added.add(handle)
+
+ if group.Name and group.Id:
+ is_group = False
+
+ contact_groups_xml = ET.Element("Groups")
+ if hasattr(contact, 'Groups'):
+ contact_groups = ET.fromstring(contact.Groups)
+ for c_group in contact_groups.getchildren():
+ if c_group.text == group.Id:
+ is_group = True
+ ET.SubElement(contact_groups_xml, "GroupId").text = c_group.text
+ if is_group != True:
+ ET.SubElement(contact_groups_xml, "GroupId").text = group.Id
+ c_groups = ET.tostring(contact_groups_xml)
+
+ if hasattr(contact, 'updateGroups'):
+ contact.updateGroups(c_groups)
+ else:
+ self.conn.pending_contacts_to_group[contact_uin] = c_groups
+
+ self.MembersChanged('', added, (), (), (), 0,
+ telepathy.CHANNEL_GROUP_CHANGE_REASON_NONE)
+
+ logger.debug("Contact %s added to group %s" %
+ (handle.name, group_name))
+
+ @async
+ def delete_contact_from_group(self, group, contact, contact_handle):
+ group_name = group.Name
+ if group_name == self._handle.name:
+ handle = SunshineHandleFactory(self.conn, 'contact',
+ contact.uin, None)
+ removed = set()
+ removed.add(handle)
+
+ contact_groups_xml = ET.Element("Groups")
+ contact_groups = ET.fromstring(contact.Groups)
+ if contact.Groups:
+ for c_group in contact_groups.getchildren():
+ if c_group.text != group.Id:
+ ET.SubElement(contact_groups_xml, "GroupId").text = c_group.text
+ c_groups = ET.tostring(contact_groups_xml)
+
+ contact.updateGroups(c_groups)
+
+ self.MembersChanged('', (), removed, (), (), 0,
+ telepathy.CHANNEL_GROUP_CHANGE_REASON_NONE)
+
+ logger.debug("Contact %s removed from group %s" %
+ (handle.name, group_name))
diff --git a/sunshine/channel/text.py b/sunshine/channel/text.py
new file mode 100644
index 0000000..03ccc9e
--- /dev/null
+++ b/sunshine/channel/text.py
@@ -0,0 +1,198 @@
+# telepathy-sunshine is the GaduGadu connection manager for Telepathy
+#
+# Copyright (C) 2006-2007 Ali Sabil <ali.sabil@gmail.com>
+# Copyright (C) 2007 Johann Prieur <johann.prieur@gmail.com>
+# Copyright (C) 2010 Krzysztof Klinikowski <kkszysiu@gmail.com>
+#
+# 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
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import logging
+import weakref
+import time
+
+import telepathy
+
+from sunshine.util.decorator import async
+from sunshine.handle import SunshineHandleFactory
+
+__all__ = ['SunshineTextChannel']
+
+logger = logging.getLogger('Sunshine.TextChannel')
+
+
+class SunshineTextChannel(telepathy.server.ChannelTypeText):
+
+ def __init__(self, conn, manager, conversation, props):
+ _, surpress_handler, handle = manager._get_type_requested_handle(props)
+ self._recv_id = 0
+ self._conn_ref = weakref.ref(conn)
+ self.conn = conn
+
+ self.contact = handle.contact
+
+ telepathy.server.ChannelTypeText.__init__(self, conn, manager, props)
+
+ def Send(self, message_type, text):
+ if message_type == telepathy.CHANNEL_TEXT_MESSAGE_TYPE_NORMAL:
+ logger.info("Sending message : %s" % unicode(text))
+ msg = text.encode('windows-1250')
+ self.conn.gadu_client.sendTo(int(self.contact.uin), str(text), str(msg))
+ else:
+ raise telepathy.NotImplemented("Unhandled message type")
+ self.Sent(int(time.time()), message_type, text)
+
+ def Close(self):
+ telepathy.server.ChannelTypeText.Close(self)
+ self.remove_from_connection()
+
+ # Redefine GetSelfHandle since we use our own handle
+ # as Butterfly doesn't have channel specific handles
+ def GetSelfHandle(self):
+ return self._conn.GetSelfHandle()
+
+ # Rededefine AcknowledgePendingMessages to remove offline messages
+ # from the oim box.
+ def AcknowledgePendingMessages(self, ids):
+ telepathy.server.ChannelTypeText.AcknowledgePendingMessages(self, ids)
+# messages = []
+# for id in ids:
+# if id in self._pending_offline_messages.keys():
+# messages.append(self._pending_offline_messages[id])
+# del self._pending_offline_messages[id]
+# self._oim_box_ref().delete_messages(messages)
+
+ # Rededefine ListPendingMessages to remove offline messages
+ # from the oim box.
+ def ListPendingMessages(self, clear):
+ return telepathy.server.ChannelTypeText.ListPendingMessages(self, clear)
+
+
+# if clear:
+# messages = self._pending_offline_messages.values()
+# self._oim_box_ref().delete_messages(messages)
+# return telepathy.server.ChannelTypeText.ListPendingMessages(self, clear)
+#
+# # papyon.event.ConversationEventInterface
+# def on_conversation_user_joined(self, contact):
+# handle = ButterflyHandleFactory(self._conn_ref(), 'contact',
+# contact.account, contact.network_id)
+# logger.info("User %s joined" % unicode(handle))
+# if handle not in self._members:
+# self.MembersChanged('', [handle], [], [], [],
+# handle, telepathy.CHANNEL_GROUP_CHANGE_REASON_INVITED)
+#
+# # papyon.event.ConversationEventInterface
+# def on_conversation_user_left(self, contact):
+# handle = ButterflyHandleFactory(self._conn_ref(), 'contact',
+# contact.account, contact.network_id)
+# logger.info("User %s left" % unicode(handle))
+# # There was only us and we are leaving, is it necessary?
+# if len(self._members) == 1:
+# self.ChatStateChanged(handle, telepathy.CHANNEL_CHAT_STATE_GONE)
+# elif len(self._members) == 2:
+# # Add the last user who left as the offline contact so we may still send
+# # him offlines messages and destroy the conversation
+# self._conversation.leave()
+# self._conversation = None
+# self._offline_handle = handle
+# self._offline_contact = contact
+# else:
+# #If there is only us and a offline contact don't remove him from
+# #the members since we still send him messages
+# self.MembersChanged('', [], [handle], [], [],
+# handle, telepathy.CHANNEL_GROUP_CHANGE_REASON_NONE)
+#
+# # papyon.event.ConversationEventInterface
+# def on_conversation_user_typing(self, contact):
+# handle = ButterflyHandleFactory(self._conn_ref(), 'contact',
+# contact.account, contact.network_id)
+# logger.info("User %s is typing" % unicode(handle))
+# self.ChatStateChanged(handle, telepathy.CHANNEL_CHAT_STATE_COMPOSING)
+#
+# # papyon.event.ConversationEventInterface
+# def on_conversation_message_received(self, sender, message):
+# id = self._recv_id
+# timestamp = int(time.time())
+# handle = ButterflyHandleFactory(self._conn_ref(), 'contact',
+# sender.account, sender.network_id)
+# type = telepathy.CHANNEL_TEXT_MESSAGE_TYPE_NORMAL
+# message = message.content
+# logger.info("User %s sent a message" % unicode(handle))
+# self.Received(id, timestamp, handle, type, 0, message)
+# self._recv_id += 1
+#
+# # papyon.event.ConversationEventInterface
+# def on_conversation_nudge_received(self, sender):
+# id = self._recv_id
+# timestamp = int(time.time())
+# handle = ButterflyHandleFactory(self._conn_ref(), 'contact',
+# sender.account, sender.network_id)
+# type = telepathy.CHANNEL_TEXT_MESSAGE_TYPE_ACTION
+# text = unicode("sends you a nudge", "utf-8")
+# logger.info("User %s sent a nudge" % unicode(handle))
+# self.Received(id, timestamp, handle, type, 0, text)
+# self._recv_id += 1
+#
+# # papyon.event.ContactEventInterface
+# def on_contact_presence_changed(self, contact):
+# handle = ButterflyHandleFactory(self._conn_ref(), 'contact',
+# contact.account, contact.network_id)
+# # Recreate a conversation if our contact join
+# if self._offline_contact == contact and contact.presence != papyon.Presence.OFFLINE:
+# logger.info('Contact %s connected, inviting him to the text channel' % unicode(contact))
+# client = self._conn_ref().msn_client
+# self._conversation = papyon.Conversation(client, [contact])
+# papyon.event.ConversationEventInterface.__init__(self, self._conversation)
+# self._offline_contact = None
+# self._offline_handle = None
+# #FIXME : I really hope there is no race condition between the time
+# # the contact accept the invitation and the time we send him a message
+# # Can a user refuse an invitation? what happens then?
+#
+#
+# # Public API
+# def offline_message_received(self, message):
+# # @message a papyon.OfflineIM.OfflineMessage
+# id = self._recv_id
+# sender = message.sender
+# timestamp = time.mktime(message.date.timetuple())
+# text = message.text
+#
+# # Map the id to the offline message so we can remove it
+# # when acked by the client
+# self._pending_offline_messages[id] = message
+#
+# handle = ButterflyHandleFactory(self._conn_ref(), 'contact',
+# sender.account, sender.network_id)
+# type = telepathy.CHANNEL_TEXT_MESSAGE_TYPE_NORMAL
+# logger.info("User %r sent a offline message" % handle)
+# self.Received(id, timestamp, handle, type, 0, text)
+#
+# self._recv_id += 1
+#
+# @async
+# def __add_initial_participants(self):
+# handles = []
+# handles.append(self._conn.GetSelfHandle())
+# if self._conversation:
+# for participant in self._conversation.participants:
+# handle = ButterflyHandleFactory(self._conn_ref(), 'contact',
+# participant.account, participant.network_id)
+# handles.append(handle)
+# else:
+# handles.append(self._offline_handle)
+#
+# self.MembersChanged('', handles, [], [], [],
+# 0, telepathy.CHANNEL_GROUP_CHANGE_REASON_NONE)