--- /dev/null
+import logging
+
+import gobject
+import telepathy
+
+try:
+ import conic as _conic
+ conic = _conic
+except (ImportError, OSError):
+ conic = None
+
+import constants
+import util.coroutines as coroutines
+import util.go_utils as gobject_utils
+import util.tp_utils as telepathy_utils
+import gtk_toolbox
+
+
+_moduleLogger = logging.getLogger("autogv")
+
+
+class NewGVConversations(object):
+
+ def __init__(self, connRef):
+ self._connRef = connRef
+ self.__callback = None
+
+ def start(self):
+ self.__callback = coroutines.func_sink(
+ coroutines.expand_positional(
+ self._on_conversations_updated
+ )
+ )
+ self._connRef().session.voicemails.updateSignalHandler.register_sink(
+ self.__callback
+ )
+ self._connRef().session.texts.updateSignalHandler.register_sink(
+ self.__callback
+ )
+
+ def stop(self):
+ self._connRef().session.voicemails.updateSignalHandler.unregister_sink(
+ self.__callback
+ )
+ self._connRef().session.texts.updateSignalHandler.unregister_sink(
+ self.__callback
+ )
+ self.__callback = None
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def _on_conversations_updated(self, conv, conversationIds):
+ _moduleLogger.debug("Incoming messages from: %r" % (conversationIds, ))
+ for phoneNumber in conversationIds:
+ h = self._connRef().get_handle_by_name(telepathy.HANDLE_TYPE_CONTACT, phoneNumber)
+ # Just let the TextChannel decide whether it should be reported to the user or not
+ props = self._connRef().generate_props(telepathy.CHANNEL_TYPE_TEXT, h, False)
+ if self._channel_manager.channel_exists(props):
+ continue
+
+ # Maemo 4.1's RTComm opens a window for a chat regardless if a
+ # message is received or not, so we need to do some filtering here
+ mergedConv = conv.get_conversation(phoneNumber)
+ unreadConvs = [
+ conversation
+ for conversation in mergedConv.conversations
+ if not conversation.isRead and not conversation.isArchived
+ ]
+ if not unreadConvs:
+ continue
+
+ chan = self._channel_manager.channel_for_props(props, signal=True)
+
+
+class RefreshVoicemail(object):
+
+ def __init__(self, connRef):
+ self._connRef = connRef
+ self._newChannelSignaller = telepathy_utils.NewChannelSignaller(self._on_new_channel)
+ self._outstandingRequests = []
+
+ def start(self):
+ self._newChannelSignaller.start()
+
+ def stop(self):
+ _moduleLogger.info("Stopping voicemail refresh")
+ self._newChannelSignaller.stop()
+
+ # I don't want to trust whether the cancel happens within the current
+ # callback or not which could be the deciding factor between invalid
+ # iterators or infinite loops
+ localRequests = [r for r in self._outstandingRequests]
+ for request in localRequests:
+ localRequests.cancel()
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def _on_new_channel(self, bus, serviceName, connObjectPath, channelObjectPath, channelType):
+ if channelType != telepathy.interfaces.CHANNEL_TYPE_STREAMED_MEDIA:
+ return
+
+ cmName = telepathy_utils.cm_from_path(connObjectPath)
+ if cmName == constants._telepathy_implementation_name_:
+ _moduleLogger.debug("Ignoring channels from self to prevent deadlock")
+ return
+
+ conn = telepathy.client.Connection(serviceName, connObjectPath)
+ chan = telepathy.client.Channel(serviceName, channelObjectPath)
+ missDetection = telepathy_utils.WasMissedCall(
+ bus, conn, chan, self._on_missed_call, self._on_error_for_missed
+ )
+ self._outstandingRequests.append(missDetection)
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def _on_missed_call(self, missDetection):
+ _moduleLogger.info("Missed a call")
+ self._connRef().session.voicemailsStateMachine.reset_timers()
+ self._outstandingRequests.remove(missDetection)
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def _on_error_for_missed(self, missDetection, reason):
+ _moduleLogger.debug("Error: %r claims %r" % (missDetection, reason))
+ self._outstandingRequests.remove(missDetection)
+
+
+class AutoDisconnect(object):
+
+ def __init__(self, connRef):
+ self._connRef = connRef
+ if conic is not None:
+ self.__connection = conic.Connection()
+ else:
+ self.__connection = None
+
+ self.__connectionEventId = None
+ self.__delayedDisconnectEventId = None
+
+ def start(self):
+ if self.__connection is not None:
+ self.__connectionEventId = self.__connection.connect("connection-event", self._on_connection_change)
+
+ def stop(self):
+ self._cancel_delayed_disconnect()
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def _on_connection_change(self, connection, event):
+ """
+ @note Maemo specific
+ """
+ status = event.get_status()
+ error = event.get_error()
+ iap_id = event.get_iap_id()
+ bearer = event.get_bearer_type()
+
+ if status == conic.STATUS_DISCONNECTED:
+ _moduleLogger.info("Disconnected from network, starting countdown to logoff")
+ self.__delayedDisconnectEventId = gobject_utils.timeout_add_seconds(
+ 5, self._on_delayed_disconnect
+ )
+ elif status == conic.STATUS_CONNECTED:
+ _moduleLogger.info("Connected to network")
+ self._cancel_delayed_disconnect()
+ else:
+ _moduleLogger.info("Other status: %r" % (status, ))
+
+ def _cancel_delayed_disconnect(self):
+ if self.__delayedDisconnectEventId is None:
+ return
+ _moduleLogger.info("Cancelling auto-log off")
+ gobject.source_reove(self.__delayedDisconnectEventId)
+ self.__delayedDisconnectEventId = None
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def _on_delayed_disconnect(self):
+ if not self.session.is_logged_in():
+ _moduleLogger.info("Received connection change event when not logged in")
+ return
+ try:
+ self._connRef().disconnect()
+ except Exception:
+ _moduleLogger.exception("Error durring disconnect")
+ self._connRef().StatusChanged(
+ telepathy.CONNECTION_STATUS_DISCONNECTED,
+ telepathy.CONNECTION_STATUS_REASON_NETWORK_ERROR
+ )
+ self.__delayedDisconnectEventId = None
+ return False
+
return
try:
- publishProps = self._conn._generate_props(telepathy.CHANNEL_TYPE_FILE_TRANSFER, self.__otherHandle, False)
+ publishProps = self._conn.generate_props(telepathy.CHANNEL_TYPE_FILE_TRANSFER, self.__otherHandle, False)
self._conn._channel_manager.channel_for_props(publishProps, signal=True)
except Exception, e:
self._report_new_message(str(e))
import weakref
import logging
-import gobject
import telepathy
-try:
- import conic as _conic
- conic = _conic
-except (ImportError, OSError):
- conic = None
-
import constants
import tp
-import util.coroutines as coroutines
-import util.go_utils as gobject_utils
import util.misc as util_misc
import gtk_toolbox
import presence
import capabilities
+import autogv
import channel_manager
self.__callbackNumberParameter = encodedCallback
self.__channelManager = channel_manager.ChannelManager(self)
- if conic is not None:
- self.__connection = conic.Connection()
- else:
- self.__connection = None
self.__cachePath = os.sep.join((constants._data_path_, "cache", self.username))
try:
os.makedirs(self.__cachePath)
raise
self.set_self_handle(handle.create_handle(self, 'connection'))
+ self._plumbing = [
+ autogv.NewGVConversations(weakref.ref(self)),
+ autogv.RefreshVoicemail(weakref.ref(self)),
+ autogv.AutoDisconnect(weakref.ref(self)),
+ ]
- self.__callback = None
- self.__connectionEventId = None
- self.__delayedDisconnectEventId = None
_moduleLogger.info("Connection to the account %s created" % account)
@property
try:
self.__session.load(self.__cachePath)
- self.__callback = coroutines.func_sink(
- coroutines.expand_positional(
- self._on_conversations_updated
- )
- )
- self.session.voicemails.updateSignalHandler.register_sink(
- self.__callback
- )
- self.session.texts.updateSignalHandler.register_sink(
- self.__callback
- )
+ for plumber in self._plumbing:
+ plumber.start()
self.session.login(*self.__credentials)
if not self.__callbackNumberParameter:
callback = gvoice.backend.get_sane_callback(
self.session.backend.set_callback_number(self.__callbackNumberParameter)
subscribeHandle = self.get_handle_by_name(telepathy.HANDLE_TYPE_LIST, "subscribe")
- subscribeProps = self._generate_props(telepathy.CHANNEL_TYPE_CONTACT_LIST, subscribeHandle, False)
+ subscribeProps = self.generate_props(telepathy.CHANNEL_TYPE_CONTACT_LIST, subscribeHandle, False)
self.__channelManager.channel_for_props(subscribeProps, signal=True)
publishHandle = self.get_handle_by_name(telepathy.HANDLE_TYPE_LIST, "publish")
- publishProps = self._generate_props(telepathy.CHANNEL_TYPE_CONTACT_LIST, publishHandle, False)
+ publishProps = self.generate_props(telepathy.CHANNEL_TYPE_CONTACT_LIST, publishHandle, False)
self.__channelManager.channel_for_props(publishProps, signal=True)
except gvoice.backend.NetworkError, e:
_moduleLogger.exception("Connection Failed")
telepathy.CONNECTION_STATUS_CONNECTED,
telepathy.CONNECTION_STATUS_REASON_REQUESTED
)
- if self.__connection is not None:
- self.__connectionEventId = self.__connection.connect("connection-event", self._on_connection_change)
@gtk_toolbox.log_exception(_moduleLogger)
def Disconnect(self):
For org.freedesktop.telepathy.Connection
"""
try:
- self._disconnect()
+ self.disconnect()
except Exception:
_moduleLogger.exception("Error durring disconnect")
self.StatusChanged(
self.check_handle(handleType, handleId)
h = self.get_handle_by_id(handleType, handleId) if handleId != 0 else None
- props = self._generate_props(type, h, suppressHandler)
+ props = self.generate_props(type, h, suppressHandler)
self._validate_handle(props)
chan = self.__channelManager.channel_for_props(props, signal=True)
_moduleLogger.info("RequestChannel Object Path (%s): %s" % (type.rsplit(".", 1)[-1], path))
return path
- def _generate_props(self, channelType, handle, suppressHandler, initiatorHandle=None):
+ def generate_props(self, channelType, handle, suppressHandler, initiatorHandle=None):
targetHandle = 0 if handle is None else handle.get_id()
targetHandleType = telepathy.HANDLE_TYPE_NONE if handle is None else handle.get_type()
props = {
return props
- def _disconnect(self):
+ def disconnect(self):
_moduleLogger.info("Disconnecting")
- self.session.voicemails.updateSignalHandler.unregister_sink(
- self.__callback
- )
- self.session.texts.updateSignalHandler.unregister_sink(
- self.__callback
- )
- self.__callback = None
+ for plumber in self._plumbing:
+ plumber.stop()
self.__channelManager.close()
self.session.save(self.__cachePath)
self.session.close()
self.manager.disconnected(self)
-
- self._cancel_delayed_disconnect()
- self.__connection = None
_moduleLogger.info("Disconnected")
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def _on_conversations_updated(self, conv, conversationIds):
- _moduleLogger.debug("Incoming messages from: %r" % (conversationIds, ))
- for phoneNumber in conversationIds:
- h = self.get_handle_by_name(telepathy.HANDLE_TYPE_CONTACT, phoneNumber)
- # Just let the TextChannel decide whether it should be reported to the user or not
- props = self._generate_props(telepathy.CHANNEL_TYPE_TEXT, h, False)
- if self.__channelManager.channel_exists(props):
- continue
-
- # Maemo 4.1's RTComm opens a window for a chat regardless if a
- # message is received or not, so we need to do some filtering here
- mergedConv = conv.get_conversation(phoneNumber)
- unreadConvs = [
- conversation
- for conversation in mergedConv.conversations
- if not conversation.isRead and not conversation.isArchived
- ]
- if not unreadConvs:
- continue
-
- chan = self.__channelManager.channel_for_props(props, signal=True)
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def _on_connection_change(self, connection, event):
- """
- @note Maemo specific
- """
- status = event.get_status()
- error = event.get_error()
- iap_id = event.get_iap_id()
- bearer = event.get_bearer_type()
-
- if status == conic.STATUS_DISCONNECTED:
- _moduleLogger.info("Disconnected from network, starting countdown to logoff")
- self.__delayedDisconnectEventId = gobject_utils.timeout_add_seconds(
- 5, self._on_delayed_disconnect
- )
- elif status == conic.STATUS_CONNECTED:
- _moduleLogger.info("Connected to network")
- self._cancel_delayed_disconnect()
- else:
- _moduleLogger.info("Other status: %r" % (status, ))
-
- def _cancel_delayed_disconnect(self):
- if self.__delayedDisconnectEventId is None:
- return
- _moduleLogger.info("Cancelling auto-log off")
- gobject.source_reove(self.__delayedDisconnectEventId)
- self.__delayedDisconnectEventId = None
-
- @gtk_toolbox.log_exception(_moduleLogger)
- def _on_delayed_disconnect(self):
- if not self.session.is_logged_in():
- _moduleLogger.info("Received connection change event when not logged in")
- return
- try:
- self._disconnect()
- except Exception:
- _moduleLogger.exception("Error durring disconnect")
- self.StatusChanged(
- telepathy.CONNECTION_STATUS_DISCONNECTED,
- telepathy.CONNECTION_STATUS_REASON_NETWORK_ERROR
- )
- self.__delayedDisconnectEventId = None
- return False
#!/usr/bin/env python
import logging
-import pprint
import gobject
import dbus
@gtk_toolbox.log_exception(_moduleLogger)
def _on_members_changed(self, message, added, removed, lp, rp, actor, reason):
- pprint.pprint((message, added, removed, lp, rp, actor, reason))
if added:
self._didMembersChange = True
self._report_missed_if_ready()
def _on_new_channel(
self, channelObjectPath, channelType, handleType, handle, supressHandler
):
- connObjectPath = channelObjectPath.rsplit("/", 1)[0]
- serviceName = connObjectPath[1:].replace("/", ".")
- conn = telepathy.client.Connection(serviceName, connObjectPath)
- chan = telepathy.client.Channel(serviceName, channelObjectPath)
- self._on_user_new_channel(self._sessionBus, conn, chan, channelType)
+ connObjectPath = channel_path_to_conn_path(channelObjectPath)
+ serviceName = path_to_service_name(channelObjectPath)
+ self._on_user_new_channel(
+ self._sessionBus, serviceName, connObjectPath, channelObjectPath, channelType
+ )
+
+
+def channel_path_to_conn_path(channelObjectPath):
+ """
+ >>> channel_path_to_conn_path("/org/freedesktop/Telepathy/ConnectionManager/theonering/sip/USERNAME/Channel1")
+ '/org/freedesktop/Telepathy/ConnectionManager/theonering/sip/USERNAME'
+ """
+ return channelObjectPath.rsplit("/", 1)[0]
+
+
+def path_to_service_name(path):
+ """
+ >>> path_to_service_name("/org/freedesktop/Telepathy/ConnectionManager/theonering/sip/USERNAME/Channel1")
+ 'org.freedesktop.Telepathy.ConnectionManager.theonering.sip.USERNAME'
+ """
+ return ".".join(path[1:].split("/")[0:7])
+
+
+def cm_from_path(path):
+ """
+ >>> cm_from_path("/org/freedesktop/Telepathy/ConnectionManager/theonering/sip/USERNAME/Channel1")
+ 'theonering'
+ """
+ return path[1:].split("/")[4]