Adding some todos
[theonering] / src / connection.py
1
2 """
3 @todo Add params for disable/enable state machines
4 @todo Move addressbook to separate state machine
5 @todo Add option to use screen name as callback
6 """
7
8
9 import weakref
10 import logging
11
12 import telepathy
13
14 import constants
15 import util.go_utils as gobject_utils
16 import util.coroutines as coroutines
17 import gtk_toolbox
18 import gvoice
19 import handle
20 import aliasing
21 import simple_presence
22 import presence
23 import capabilities
24 import channel_manager
25
26
27 _moduleLogger = logging.getLogger("connection")
28
29
30 class TheOneRingConnection(
31         telepathy.server.Connection,
32         aliasing.AliasingMixin,
33         simple_presence.SimplePresenceMixin,
34         presence.PresenceMixin,
35         capabilities.CapabilitiesMixin,
36 ):
37
38         # Overriding a base class variable
39         # Should the forwarding number be handled by the alias or by an option?
40         _mandatory_parameters = {
41                 'account' : 's',
42                 'password' : 's',
43                 'forward' : 's',
44         }
45         # Overriding a base class variable
46         _optional_parameters = {
47         }
48         _parameter_defaults = {
49         }
50
51         def __init__(self, manager, parameters):
52                 self.check_parameters(parameters)
53                 try:
54                         account = unicode(parameters['account'])
55
56                         # Connection init must come first
57                         telepathy.server.Connection.__init__(
58                                 self,
59                                 constants._telepathy_protocol_name_,
60                                 account,
61                                 constants._telepathy_implementation_name_
62                         )
63                         aliasing.AliasingMixin.__init__(self)
64                         simple_presence.SimplePresenceMixin.__init__(self)
65                         presence.PresenceMixin.__init__(self)
66                         capabilities.CapabilitiesMixin.__init__(self)
67
68                         self._manager = weakref.proxy(manager)
69                         self._credentials = (
70                                 parameters['account'].encode('utf-8'),
71                                 parameters['password'].encode('utf-8'),
72                         )
73                         self._callbackNumber = parameters['forward'].encode('utf-8')
74                         self._channelManager = channel_manager.ChannelManager(self)
75
76                         self._session = gvoice.session.Session(None)
77
78                         self.set_self_handle(handle.create_handle(self, 'connection'))
79
80                         self._callback = None
81                         _moduleLogger.info("Connection to the account %s created" % account)
82                 except Exception, e:
83                         _moduleLogger.exception("Failed to create Connection")
84                         raise
85
86         @property
87         def manager(self):
88                 return self._manager
89
90         @property
91         def session(self):
92                 return self._session
93
94         @property
95         def username(self):
96                 return self._credentials[0]
97
98         @property
99         def userAliasType(self):
100                 return self.USER_ALIAS_ACCOUNT
101
102         def handle(self, handleType, handleId):
103                 self.check_handle(handleType, handleId)
104                 return self._handles[handleType, handleId]
105
106         @gtk_toolbox.log_exception(_moduleLogger)
107         def Connect(self):
108                 """
109                 For org.freedesktop.telepathy.Connection
110                 """
111                 _moduleLogger.info("Connecting...")
112                 self.StatusChanged(
113                         telepathy.CONNECTION_STATUS_CONNECTING,
114                         telepathy.CONNECTION_STATUS_REASON_REQUESTED
115                 )
116                 try:
117                         cookieFilePath = None
118                         self._session = gvoice.session.Session(cookieFilePath)
119
120                         self._callback = coroutines.func_sink(
121                                 coroutines.expand_positional(
122                                         self._on_conversations_updated
123                                 )
124                         )
125                         self.session.conversations.updateSignalHandler.register_sink(
126                                 self._callback
127                         )
128                         self.session.login(*self._credentials)
129                         self.session.backend.set_callback_number(self._callbackNumber)
130                 except gvoice.backend.NetworkError, e:
131                         _moduleLogger.exception("Connection Failed")
132                         self.StatusChanged(
133                                 telepathy.CONNECTION_STATUS_DISCONNECTED,
134                                 telepathy.CONNECTION_STATUS_REASON_NETWORK_ERROR
135                         )
136                 except Exception, e:
137                         _moduleLogger.exception("Connection Failed")
138                         self.StatusChanged(
139                                 telepathy.CONNECTION_STATUS_DISCONNECTED,
140                                 telepathy.CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED
141                         )
142                 else:
143                         _moduleLogger.info("Connected")
144                         self.StatusChanged(
145                                 telepathy.CONNECTION_STATUS_CONNECTED,
146                                 telepathy.CONNECTION_STATUS_REASON_REQUESTED
147                         )
148
149         @gtk_toolbox.log_exception(_moduleLogger)
150         def Disconnect(self):
151                 """
152                 For org.freedesktop.telepathy.Connection
153                 @bug Not properly logging out.  Cookie files need to be per connection and removed
154                 """
155                 _moduleLogger.info("Disconnecting")
156                 try:
157                         self.session.conversations.updateSignalHandler.unregister_sink(
158                                 self._callback
159                         )
160                         self._callback = None
161                         self._channelManager.close()
162                         self.session.logout()
163                         self.session.close()
164                         self._session = None
165                         _moduleLogger.info("Disconnected")
166                 except Exception:
167                         _moduleLogger.exception("Disconnecting Failed")
168                 self.StatusChanged(
169                         telepathy.CONNECTION_STATUS_DISCONNECTED,
170                         telepathy.CONNECTION_STATUS_REASON_REQUESTED
171                 )
172                 self.manager.disconnected(self)
173
174         @gtk_toolbox.log_exception(_moduleLogger)
175         def RequestChannel(self, type, handleType, handleId, suppressHandler):
176                 """
177                 For org.freedesktop.telepathy.Connection
178
179                 @param type DBus interface name for base channel type
180                 @param handleId represents a contact, list, etc according to handleType
181
182                 @returns DBus object path for the channel created or retrieved
183                 """
184                 self.check_connected()
185                 self.check_handle(handleType, handleId)
186
187                 h = self.handle(handleType, handleId) if handleId != 0 else None
188                 props = self._generate_props(type, h, suppressHandler)
189                 if hasattr(self, "_validate_handle"):
190                         # On newer python-telepathy
191                         self._validate_handle(props)
192
193                 chan = self._channelManager.channel_for_props(props, signal=True)
194                 path = chan._object_path
195                 _moduleLogger.info("RequestChannel Object Path: %s" % path)
196                 return path
197
198         @gtk_toolbox.log_exception(_moduleLogger)
199         def RequestHandles(self, handleType, names, sender):
200                 """
201                 For org.freedesktop.telepathy.Connection
202                 Overiding telepathy.server.Connecton to allow custom handles
203                 """
204                 self.check_connected()
205                 self.check_handle_type(handleType)
206
207                 handles = []
208                 for name in names:
209                         requestedHandleName = name.encode('utf-8')
210                         if handleType == telepathy.HANDLE_TYPE_CONTACT:
211                                 _moduleLogger.info("RequestHandles Contact: %s" % requestedHandleName)
212                                 requestedContactId, requestedContactNumber = handle.ContactHandle.from_handle_name(
213                                         requestedHandleName
214                                 )
215                                 h = handle.create_handle(self, 'contact', requestedContactId, requestedContactNumber)
216                         elif handleType == telepathy.HANDLE_TYPE_LIST:
217                                 # Support only server side (immutable) lists
218                                 _moduleLogger.info("RequestHandles List: %s" % requestedHandleName)
219                                 h = handle.create_handle(self, 'list', requestedHandleName)
220                         else:
221                                 raise telepathy.errors.NotAvailable('Handle type unsupported %d' % handleType)
222                         handles.append(h.id)
223                         self.add_client_handle(h, sender)
224                 return handles
225
226         def _generate_props(self, channelType, handle, suppressHandler, initiatorHandle=None):
227                 targetHandle = 0 if handle is None else handle.get_id()
228                 targetHandleType = telepathy.HANDLE_TYPE_NONE if handle is None else handle.get_type()
229                 props = {
230                         telepathy.CHANNEL_INTERFACE + '.ChannelType': channelType,
231                         telepathy.CHANNEL_INTERFACE + '.TargetHandle': targetHandle,
232                         telepathy.CHANNEL_INTERFACE + '.TargetHandleType': targetHandleType,
233                         telepathy.CHANNEL_INTERFACE + '.Requested': suppressHandler
234                 }
235
236                 if initiatorHandle is not None:
237                         props[telepathy.CHANNEL_INTERFACE + '.InitiatorHandle'] = initiatorHandle.id
238
239                 return props
240
241         @gobject_utils.async
242         @gtk_toolbox.log_exception(_moduleLogger)
243         def _on_conversations_updated(self, conv, conversationIds):
244                 # @todo get conversations update running
245                 # @todo test conversatiuons
246                 _moduleLogger.info("Incoming messages from: %r" % (conversationIds, ))
247                 for contactId, phoneNumber in conversationIds:
248                         h = handle.create_handle(self, 'contact', contactId, phoneNumber)
249                         # Just let the TextChannel decide whether it should be reported to the user or not
250                         props = self._generate_props(telepathy.CHANNEL_TYPE_TEXT, h, False)
251                         channel = self._channelManager.channel_for_props(props, signal=True)