3 @todo Add params for different state machines update times
4 @todo Add option to use screen name as callback
5 @todo Get a callback for missed calls to force an update of the voicemail state machine
6 @todo Get a callback on an incoming call and if its from GV, auto-pickup
7 @todo Observe when connected/disconnected to disconnect CM
17 import util.go_utils as gobject_utils
18 import util.coroutines as coroutines
26 import simple_presence
30 import channel_manager
33 _moduleLogger = logging.getLogger("connection")
36 class TheOneRingConnection(
37 telepathy.server.Connection,
38 telepathy.server.ConnectionInterfaceRequests, # already a mixin
39 contacts.ContactsMixin,
40 aliasing.AliasingMixin,
41 simple_presence.SimplePresenceMixin,
42 presence.PresenceMixin,
43 capabilities.CapabilitiesMixin,
46 # Overriding a base class variable
47 # Should the forwarding number be handled by the alias or by an option?
48 _mandatory_parameters = {
53 # Overriding a base class variable
54 _optional_parameters = {
56 _parameter_defaults = {
59 @gtk_toolbox.log_exception(_moduleLogger)
60 def __init__(self, manager, parameters):
61 self.check_parameters(parameters)
62 account = unicode(parameters['account'])
63 encodedAccount = parameters['account'].encode('utf-8')
64 encodedPassword = parameters['password'].encode('utf-8')
65 encodedCallback = parameters['forward'].encode('utf-8')
66 if not encodedCallback:
67 raise telepathy.errors.InvalidArgument("User must specify what number GV forwards calls to")
69 # Connection init must come first
70 telepathy.server.Connection.__init__(
72 constants._telepathy_protocol_name_,
74 constants._telepathy_implementation_name_
76 telepathy.server.ConnectionInterfaceRequests.__init__(self)
77 contacts.ContactsMixin.__init__(self)
78 aliasing.AliasingMixin.__init__(self)
79 simple_presence.SimplePresenceMixin.__init__(self)
80 presence.PresenceMixin.__init__(self)
81 capabilities.CapabilitiesMixin.__init__(self)
83 self._manager = weakref.proxy(manager)
88 self._callbackNumber = encodedCallback
89 self._channelManager = channel_manager.ChannelManager(self)
91 self._session = gvoice.session.Session(None)
93 self.set_self_handle(handle.create_handle(self, 'connection'))
96 _moduleLogger.info("Connection to the account %s created" % account)
108 return self._credentials[0]
111 def userAliasType(self):
112 return self.USER_ALIAS_ACCOUNT
114 def handle(self, handleType, handleId):
115 self.check_handle(handleType, handleId)
116 return self._handles[handleType, handleId]
118 @gtk_toolbox.log_exception(_moduleLogger)
121 For org.freedesktop.telepathy.Connection
123 _moduleLogger.info("Connecting...")
125 telepathy.CONNECTION_STATUS_CONNECTING,
126 telepathy.CONNECTION_STATUS_REASON_REQUESTED
129 cookieFilePath = None
130 self._session = gvoice.session.Session(cookieFilePath)
132 self._callback = coroutines.func_sink(
133 coroutines.expand_positional(
134 self._on_conversations_updated
137 self.session.voicemails.updateSignalHandler.register_sink(
140 self.session.texts.updateSignalHandler.register_sink(
143 self.session.login(*self._credentials)
144 self.session.backend.set_callback_number(self._callbackNumber)
145 except gvoice.backend.NetworkError, e:
146 _moduleLogger.exception("Connection Failed")
148 telepathy.CONNECTION_STATUS_DISCONNECTED,
149 telepathy.CONNECTION_STATUS_REASON_NETWORK_ERROR
152 _moduleLogger.exception("Connection Failed")
154 telepathy.CONNECTION_STATUS_DISCONNECTED,
155 telepathy.CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED
158 _moduleLogger.info("Connected")
160 telepathy.CONNECTION_STATUS_CONNECTED,
161 telepathy.CONNECTION_STATUS_REASON_REQUESTED
164 @gtk_toolbox.log_exception(_moduleLogger)
165 def Disconnect(self):
167 For org.freedesktop.telepathy.Connection
168 @bug Not properly logging out. Cookie files need to be per connection and removed
170 _moduleLogger.info("Disconnecting")
172 self.session.voicemails.updateSignalHandler.unregister_sink(
175 self.session.texts.updateSignalHandler.unregister_sink(
178 self._callback = None
179 self._channelManager.close()
180 self.session.logout()
183 _moduleLogger.info("Disconnected")
185 _moduleLogger.exception("Disconnecting Failed")
187 telepathy.CONNECTION_STATUS_DISCONNECTED,
188 telepathy.CONNECTION_STATUS_REASON_REQUESTED
190 self.manager.disconnected(self)
192 @gtk_toolbox.log_exception(_moduleLogger)
193 def RequestChannel(self, type, handleType, handleId, suppressHandler):
195 For org.freedesktop.telepathy.Connection
197 @param type DBus interface name for base channel type
198 @param handleId represents a contact, list, etc according to handleType
200 @returns DBus object path for the channel created or retrieved
202 self.check_connected()
203 self.check_handle(handleType, handleId)
205 h = self.handle(handleType, handleId) if handleId != 0 else None
206 props = self._generate_props(type, h, suppressHandler)
207 if hasattr(self, "_validate_handle"):
208 # HACK Newer python-telepathy
209 self._validate_handle(props)
211 chan = self._channelManager.channel_for_props(props, signal=True)
212 path = chan._object_path
213 _moduleLogger.info("RequestChannel Object Path: %s" % path)
216 @gtk_toolbox.log_exception(_moduleLogger)
217 def RequestHandles(self, handleType, names, sender):
219 For org.freedesktop.telepathy.Connection
220 Overiding telepathy.server.Connecton to allow custom handles
222 self.check_connected()
223 self.check_handle_type(handleType)
227 requestedHandleName = name.encode('utf-8')
228 if handleType == telepathy.HANDLE_TYPE_CONTACT:
229 _moduleLogger.info("RequestHandles Contact: %s" % requestedHandleName)
230 requestedContactId, requestedContactNumber = handle.ContactHandle.from_handle_name(
233 h = handle.create_handle(self, 'contact', requestedContactId, requestedContactNumber)
234 elif handleType == telepathy.HANDLE_TYPE_LIST:
235 # Support only server side (immutable) lists
236 _moduleLogger.info("RequestHandles List: %s" % requestedHandleName)
237 h = handle.create_handle(self, 'list', requestedHandleName)
239 raise telepathy.errors.NotAvailable('Handle type unsupported %d' % handleType)
241 self.add_client_handle(h, sender)
244 def _generate_props(self, channelType, handle, suppressHandler, initiatorHandle=None):
245 targetHandle = 0 if handle is None else handle.get_id()
246 targetHandleType = telepathy.HANDLE_TYPE_NONE if handle is None else handle.get_type()
248 telepathy.CHANNEL_INTERFACE + '.ChannelType': channelType,
249 telepathy.CHANNEL_INTERFACE + '.TargetHandle': targetHandle,
250 telepathy.CHANNEL_INTERFACE + '.TargetHandleType': targetHandleType,
251 telepathy.CHANNEL_INTERFACE + '.Requested': suppressHandler
254 if initiatorHandle is not None:
255 props[telepathy.CHANNEL_INTERFACE + '.InitiatorHandle'] = initiatorHandle.id
260 @gtk_toolbox.log_exception(_moduleLogger)
261 def _on_conversations_updated(self, conv, conversationIds):
262 _moduleLogger.debug("Incoming messages from: %r" % (conversationIds, ))
263 for contactId, phoneNumber in conversationIds:
264 h = handle.create_handle(self, 'contact', contactId, phoneNumber)
265 # Just let the TextChannel decide whether it should be reported to the user or not
266 props = self._generate_props(telepathy.CHANNEL_TYPE_TEXT, h, False)
267 channel = self._channelManager.channel_for_props(props, signal=True)