9 import util.go_utils as gobject_utils
10 import util.misc as misc_utils
21 import simple_presence
24 import channel_manager
27 _moduleLogger = logging.getLogger(__name__)
30 class TheOneRingOptions(object):
34 assert gvoice.session.Session._DEFAULTS["contacts"][1] == "hours"
35 contactsPollPeriodInHours = gvoice.session.Session._DEFAULTS["contacts"][0]
37 assert gvoice.session.Session._DEFAULTS["voicemail"][1] == "minutes"
38 voicemailPollPeriodInMinutes = gvoice.session.Session._DEFAULTS["voicemail"][0]
40 assert gvoice.session.Session._DEFAULTS["texts"][1] == "minutes"
41 textsPollPeriodInMinutes = gvoice.session.Session._DEFAULTS["texts"][0]
43 def __init__(self, parameters = None):
44 if parameters is None:
46 self.useGVContacts = parameters["use-gv-contacts"]
47 self.contactsPollPeriodInHours = parameters['contacts-poll-period-in-hours']
48 self.voicemailPollPeriodInMinutes = parameters['voicemail-poll-period-in-minutes']
49 self.textsPollPeriodInMinutes = parameters['texts-poll-period-in-minutes']
52 class TheOneRingConnection(
54 aliasing.AliasingMixin,
56 capabilities.CapabilitiesMixin,
57 contacts.ContactsMixin,
58 presence.PresenceMixin,
59 requests.RequestsMixin,
60 simple_presence.SimplePresenceMixin,
63 # overiding base class variable
64 _mandatory_parameters = {
68 # overiding base class variable
69 _optional_parameters = {
71 'use-gv-contacts': 'b',
72 'contacts-poll-period-in-hours': 'i',
73 'voicemail-poll-period-in-minutes': 'i',
74 'texts-poll-period-in-minutes': 'i',
76 _parameter_defaults = {
78 'use-gv-contacts': TheOneRingOptions.useGVContacts,
79 'contacts-poll-period-in-hours': TheOneRingOptions.contactsPollPeriodInHours,
80 'voicemail-poll-period-in-minutes': TheOneRingOptions.voicemailPollPeriodInMinutes,
81 'texts-poll-period-in-minutes': TheOneRingOptions.textsPollPeriodInMinutes,
83 _secret_parameters = set((
87 @misc_utils.log_exception(_moduleLogger)
88 def __init__(self, manager, parameters):
89 self.check_parameters(parameters)
90 account = unicode(parameters['account'])
91 encodedAccount = parameters['account'].encode('utf-8')
92 encodedPassword = parameters['password'].encode('utf-8')
93 encodedCallback = misc_utils.normalize_number(parameters['forward'].encode('utf-8'))
94 if encodedCallback and not misc_utils.is_valid_number(encodedCallback):
95 raise telepathy.errors.InvalidArgument("Invalid forwarding number")
97 # Connection init must come first
98 self.__options = TheOneRingOptions(parameters)
99 self.__session = gvoice.session.Session(
102 "contacts": (self.__options.contactsPollPeriodInHours, "hours"),
103 "voicemail": (self.__options.voicemailPollPeriodInMinutes, "minutes"),
104 "texts": (self.__options.textsPollPeriodInMinutes, "minutes"),
107 tp.Connection.__init__(
109 constants._telepathy_protocol_name_,
111 constants._telepathy_implementation_name_
113 aliasing.AliasingMixin.__init__(self)
114 avatars.AvatarsMixin.__init__(self)
115 capabilities.CapabilitiesMixin.__init__(self)
116 contacts.ContactsMixin.__init__(self)
117 presence.PresenceMixin.__init__(self)
118 requests.RequestsMixin.__init__(self)
119 simple_presence.SimplePresenceMixin.__init__(self)
121 self.__manager = weakref.proxy(manager)
122 self.__credentials = (
126 self.__callbackNumberParameter = encodedCallback
127 self.__channelManager = channel_manager.ChannelManager(self)
129 self.__cachePath = os.sep.join((constants._data_path_, "cache", self.username))
131 os.makedirs(self.__cachePath)
136 self.set_self_handle(handle.create_handle(self, 'connection'))
138 autogv.NewGVConversations(weakref.ref(self)),
139 autogv.RefreshVoicemail(weakref.ref(self)),
140 autogv.AutoDisconnect(weakref.ref(self)),
141 autogv.DelayEnableContactIntegration(constants._telepathy_implementation_name_),
143 self._delayedConnect = gobject_utils.Async(self._delayed_connect)
145 _moduleLogger.info("Connection to the account %s created" % account)
146 self._timedDisconnect = autogv.TimedDisconnect(weakref.ref(self))
147 self._timedDisconnect.start()
151 return self.__manager
155 return self.__session
159 return self.__options
163 return self.__credentials[0]
166 def callbackNumberParameter(self):
167 return self.__callbackNumberParameter
169 def get_handle_by_name(self, handleType, handleName):
170 requestedHandleName = handleName.encode('utf-8')
171 if handleType == telepathy.HANDLE_TYPE_CONTACT:
172 h = handle.create_handle(self, 'contact', requestedHandleName)
173 elif handleType == telepathy.HANDLE_TYPE_LIST:
174 # Support only server side (immutable) lists
175 h = handle.create_handle(self, 'list', requestedHandleName)
177 raise telepathy.errors.NotAvailable('Handle type unsupported %d' % handleType)
181 def _channel_manager(self):
182 return self.__channelManager
184 @misc_utils.log_exception(_moduleLogger)
187 For org.freedesktop.telepathy.Connection
189 if self._status != telepathy.CONNECTION_STATUS_DISCONNECTED:
190 _moduleLogger.info("Attempting connect when not disconnected")
192 _moduleLogger.info("Kicking off connect")
193 self._delayedConnect.start()
194 self._timedDisconnect.stop()
196 @misc_utils.log_exception(_moduleLogger)
197 def _delayed_connect(self):
198 _moduleLogger.info("Connecting...")
200 telepathy.CONNECTION_STATUS_CONNECTING,
201 telepathy.CONNECTION_STATUS_REASON_REQUESTED
204 self.__session.load(self.__cachePath)
206 for plumber in self._plumbing:
208 self.session.login(*self.__credentials)
209 if not self.__callbackNumberParameter:
210 callback = gvoice.backend.get_sane_callback(
213 self.__callbackNumberParameter = misc_utils.normalize_number(callback)
214 self.session.backend.set_callback_number(self.__callbackNumberParameter)
216 subscribeHandle = self.get_handle_by_name(telepathy.HANDLE_TYPE_LIST, "subscribe")
217 subscribeProps = self.generate_props(telepathy.CHANNEL_TYPE_CONTACT_LIST, subscribeHandle, False)
218 self.__channelManager.channel_for_props(subscribeProps, signal=True)
219 publishHandle = self.get_handle_by_name(telepathy.HANDLE_TYPE_LIST, "publish")
220 publishProps = self.generate_props(telepathy.CHANNEL_TYPE_CONTACT_LIST, publishHandle, False)
221 self.__channelManager.channel_for_props(publishProps, signal=True)
222 except gvoice.backend.NetworkError:
223 _moduleLogger.exception("Connection Failed")
224 self.disconnect(telepathy.CONNECTION_STATUS_REASON_NETWORK_ERROR)
227 _moduleLogger.exception("Connection Failed")
228 self.disconnect(telepathy.CONNECTION_STATUS_REASON_AUTHENTICATION_FAILED)
231 _moduleLogger.info("Connected")
233 telepathy.CONNECTION_STATUS_CONNECTED,
234 telepathy.CONNECTION_STATUS_REASON_REQUESTED
237 @misc_utils.log_exception(_moduleLogger)
238 def Disconnect(self):
240 For org.freedesktop.telepathy.Connection
242 _moduleLogger.info("Kicking off disconnect")
243 self.disconnect(telepathy.CONNECTION_STATUS_REASON_REQUESTED)
245 @misc_utils.log_exception(_moduleLogger)
246 def RequestChannel(self, type, handleType, handleId, suppressHandler):
248 For org.freedesktop.telepathy.Connection
250 @param type DBus interface name for base channel type
251 @param handleId represents a contact, list, etc according to handleType
253 @returns DBus object path for the channel created or retrieved
255 self.check_connected()
256 self.check_handle(handleType, handleId)
258 h = self.get_handle_by_id(handleType, handleId) if handleId != 0 else None
259 props = self.generate_props(type, h, suppressHandler)
260 self._validate_handle(props)
262 chan = self.__channelManager.channel_for_props(props, signal=True)
263 path = chan._object_path
264 _moduleLogger.info("RequestChannel Object Path (%s): %s" % (type.rsplit(".", 1)[-1], path))
267 def generate_props(self, channelType, handleObj, suppressHandler, initiatorHandle=None):
268 targetHandle = 0 if handleObj is None else handleObj.get_id()
269 targetHandleType = telepathy.HANDLE_TYPE_NONE if handleObj is None else handleObj.get_type()
271 telepathy.CHANNEL_INTERFACE + '.ChannelType': channelType,
272 telepathy.CHANNEL_INTERFACE + '.TargetHandle': targetHandle,
273 telepathy.CHANNEL_INTERFACE + '.TargetHandleType': targetHandleType,
274 telepathy.CHANNEL_INTERFACE + '.Requested': suppressHandler
277 if initiatorHandle is not None:
278 props[telepathy.CHANNEL_INTERFACE + '.InitiatorHandle'] = initiatorHandle.id
282 def disconnect(self, reason):
283 _moduleLogger.info("Disconnecting")
285 self._delayedConnect.cancel()
286 self._timedDisconnect.stop()
288 # Not having the disconnect first can cause weird behavior with clients
289 # including not being able to reconnect or even crashing
291 telepathy.CONNECTION_STATUS_DISCONNECTED,
295 for plumber in self._plumbing:
298 self.__channelManager.close()
299 self.manager.disconnected(self)
301 self.session.save(self.__cachePath)
302 self.session.logout()
305 # In case one of the above items takes too long (which it should never
306 # do), we leave the starting of the shutdown-on-idle counter to the
308 self.manager.disconnect_completed()
310 _moduleLogger.info("Disconnected")