Removing async calls due to race conditions they create (mainly errors on shutdown...
[theonering] / src / channel / contact_list.py
1 import logging
2
3 import telepathy
4
5 import util.go_utils as gobject_utils
6 import util.coroutines as coroutines
7 import gtk_toolbox
8 import handle
9
10
11 _moduleLogger = logging.getLogger("channel.contact_list")
12
13
14 class AbstractListChannel(
15                 telepathy.server.ChannelTypeContactList,
16                 telepathy.server.ChannelInterfaceGroup,
17         ):
18         "Abstract Contact List channels"
19
20         def __init__(self, connection, manager, props, h):
21                 self._manager = manager
22                 self._props = props
23
24                 try:
25                         # HACK Older python-telepathy way
26                         telepathy.server.ChannelTypeContactList.__init__(self, connection, h)
27                 except TypeError:
28                         # HACK Newer python-telepathy way
29                         telepathy.server.ChannelTypeContactList.__init__(self, connection, manager, props)
30                 telepathy.server.ChannelInterfaceGroup.__init__(self)
31
32                 self._session = connection.session
33
34
35 class AllContactsListChannel(AbstractListChannel):
36         """
37         The group of contacts for whom you receive presence
38         """
39
40         def __init__(self, connection, manager, props, h):
41                 AbstractListChannel.__init__(self, connection, manager, props, h)
42
43                 self._callback = coroutines.func_sink(
44                         coroutines.expand_positional(
45                                 self._on_contacts_refreshed
46                         )
47                 )
48                 self._session.addressbook.updateSignalHandler.register_sink(
49                         self._callback
50                 )
51
52                 self.GroupFlagsChanged(0, 0)
53
54                 addressbook = connection.session.addressbook
55                 contacts = addressbook.get_contacts()
56                 self._process_refresh(addressbook, contacts, [])
57
58         @gtk_toolbox.log_exception(_moduleLogger)
59         def Close(self):
60                 self.close()
61
62         def close(self):
63                 self._session.addressbook.updateSignalHandler.unregister_sink(
64                         self._callback
65                 )
66                 self._callback = None
67
68                 telepathy.server.ChannelTypeContactList.Close(self)
69                 if self._manager.channel_exists(self._props):
70                         # HACK Older python-telepathy requires doing this manually
71                         self._manager.remove_channel(self)
72                 self.remove_from_connection()
73
74         @gtk_toolbox.log_exception(_moduleLogger)
75         def _on_contacts_refreshed(self, addressbook, added, removed, changed):
76                 self._process_refresh(addressbook, added, removed)
77
78         def _process_refresh(self, addressbook, added, removed):
79                 connection = self._conn
80                 handlesAdded = [
81                         handle.create_handle(connection, "contact", contactId, phoneNumber)
82                         for contactId in added
83                         for (phoneType, phoneNumber) in addressbook.get_contact_details(contactId)
84                 ]
85                 handlesRemoved = [
86                         handle.create_handle(connection, "contact", contactId, phoneNumber)
87                         for contactId in removed
88                         for (phoneType, phoneNumber) in addressbook.get_contact_details(contactId)
89                 ]
90                 message = ""
91                 actor = 0
92                 reason = telepathy.CHANNEL_GROUP_CHANGE_REASON_NONE
93                 self.MembersChanged(
94                         message,
95                         handlesAdded, handlesRemoved,
96                         (), (),
97                         actor,
98                         reason,
99                 )
100
101
102 def create_contact_list_channel(connection, manager, props, h):
103         if h.get_name() == 'subscribe':
104                 # The group of contacts for whom you receive presence
105                 ChannelClass = AllContactsListChannel
106         elif h.get_name() == 'publish':
107                 # The group of contacts who may receive your presence
108                 ChannelClass = AllContactsListChannel
109         elif h.get_name() == 'hide':
110                 # A group of contacts who are on the publish list but are temporarily
111                 # disallowed from receiving your presence
112                 # This doesn't make sense to support
113                 _moduleLogger.warn("Unsuported type %s" % h.get_name())
114         elif h.get_name() == 'allow':
115                 # A group of contacts who may send you messages
116                 # @todo Allow-List would be cool to support
117                 _moduleLogger.warn("Unsuported type %s" % h.get_name())
118         elif h.get_name() == 'deny':
119                 # A group of contacts who may not send you messages
120                 # @todo Deny-List would be cool to support
121                 _moduleLogger.warn("Unsuported type %s" % h.get_name())
122         elif h.get_name() == 'stored':
123                 # On protocols where the user's contacts are stored, this contact list
124                 # contains all stored contacts regardless of subscription status.
125                 ChannelClass = AllContactsListChannel
126         else:
127                 raise TypeError("Unknown list type : " + h.get_name())
128         return ChannelClass(connection, manager, props, h)
129
130