super(DisplayParams, self)._on_done()
-class Connect(Action):
+class RequestConnection(Action):
def __init__(self, cm, username, password, forward):
- super(Connect, self).__init__()
+ super(RequestConnection, self).__init__()
self._cm = cm
self._conn = None
'password': self._password,
'forward': self._forward,
},
- reply_handler = self._on_connection_requested,
+ reply_handler = self._on_done,
error_handler = self._on_error,
)
- def _on_connection_requested(self, busName, objectPath):
+ def _on_done(self, busName, objectPath):
self._serviceName = busName
self._conn = telepathy.client.Connection(busName, objectPath)
- self._conn[telepathy.server.CONNECTION].connect_to_signal(
+ super(RequestConnection, self)._on_done()
+
+
+class Connect(Action):
+
+ def __init__(self, connAction):
+ super(Connect, self).__init__()
+ self._connAction = connAction
+
+ def queue_action(self):
+ self._connAction.conn[telepathy.server.CONNECTION].connect_to_signal(
'StatusChanged',
self._on_change,
)
- self._conn[telepathy.server.CONNECTION].Connect(
+ self._connAction.conn[telepathy.server.CONNECTION].Connect(
reply_handler = self._on_generic_message,
error_handler = self._on_error,
)
super(RequestChannel, self)._on_done()
+class CloseChannel(Action):
+
+ def __init__(self, connAction, chanAction):
+ super(CloseChannel, self).__init__()
+ self._connAction = connAction
+ self._chanAction = chanAction
+ self._handles = []
+
+ def queue_action(self):
+ self._chanAction.channel[telepathy.server.CHANNEL].Close(
+ reply_handler = self._on_done,
+ error_handler = self._on_error,
+ )
+
+ def _on_done(self):
+ super(CloseChannel, self)._on_done()
+
+
class ContactHandles(Action):
def __init__(self, connAction, chanAction):
username = sys.argv[1]
password = sys.argv[2]
forward = sys.argv[3]
- con = Connect(cm, username, password, forward)
+ reqcon = RequestConnection(cm, username, password, forward)
+ lastAction.append_action(reqcon)
+ lastAction = reqcon
+
+ if False:
+ reqcon = RequestConnection(cm, username, password, forward)
+ lastAction.append_action(reqcon)
+ lastAction = reqcon
+
+ con = Connect(reqcon)
lastAction.append_action(con)
lastAction = con
if True:
- spo = SimplePresenceOptions(con)
+ spo = SimplePresenceOptions(reqcon)
lastAction.append_action(spo)
lastAction = spo
if True:
- uh = UserHandle(con)
+ uh = UserHandle(reqcon)
lastAction.append_action(uh)
lastAction = uh
- ua = Aliases(con, uh)
+ ua = Aliases(reqcon, uh)
lastAction.append_action(ua)
lastAction = ua
- sps = SimplePresenceStatus(con, uh)
+ sps = SimplePresenceStatus(reqcon, uh)
lastAction.append_action(sps)
lastAction = sps
if False:
- setdnd = SetSimplePresence(con, "dnd", "")
+ setdnd = SetSimplePresence(reqcon, "dnd", "")
lastAction.append_action(setdnd)
lastAction = setdnd
- sps = SimplePresenceStatus(con, uh)
+ sps = SimplePresenceStatus(reqcon, uh)
lastAction.append_action(sps)
lastAction = sps
- setdnd = SetSimplePresence(con, "available", "")
+ setdnd = SetSimplePresence(reqcon, "available", "")
lastAction.append_action(setdnd)
lastAction = setdnd
- sps = SimplePresenceStatus(con, uh)
+ sps = SimplePresenceStatus(reqcon, uh)
lastAction.append_action(sps)
lastAction = sps
lastAction = sl
if False:
- rclh = RequestHandle(con, telepathy.HANDLE_TYPE_LIST, ["subscribe"])
+ rclh = RequestHandle(reqcon, telepathy.HANDLE_TYPE_LIST, ["subscribe"])
lastAction.append_action(rclh)
lastAction = rclh
rclc = RequestChannel(
- con,
+ reqcon,
rclh,
telepathy.CHANNEL_TYPE_CONTACT_LIST,
telepathy.HANDLE_TYPE_LIST,
lastAction.append_action(rclc)
lastAction = rclc
- ch = ContactHandles(con, rclc)
+ ch = ContactHandles(reqcon, rclc)
lastAction.append_action(ch)
lastAction = ch
- ca = Aliases(con, ch)
+ ca = Aliases(reqcon, ch)
lastAction.append_action(ca)
lastAction = ca
- if False:
- rch = RequestHandle(con, telepathy.HANDLE_TYPE_CONTACT, ["18005558355"]) #(1-800-555-TELL)
+ if True:
+ rch = RequestHandle(reqcon, telepathy.HANDLE_TYPE_CONTACT, ["18005558355"]) #(1-800-555-TELL)
lastAction.append_action(rch)
lastAction = rch
smHandle = nullHandle
smHandleType = telepathy.HANDLE_TYPE_NONE
rsmc = RequestChannel(
- con,
+ reqcon,
smHandle,
telepathy.CHANNEL_TYPE_STREAMED_MEDIA,
smHandleType,
lastAction = rsmc
if False:
- call = Call(con, rsmc, rch)
+ call = Call(reqcon, rsmc, rch)
lastAction.append_action(call)
lastAction = call
# sending a text
rtc = RequestChannel(
- con,
+ reqcon,
rch,
telepathy.CHANNEL_TYPE_TEXT,
smHandleType,
lastAction.append_action(rtc)
lastAction = rtc
+ if True:
+ closechan = CloseChannel(reqcon, rtc)
+ lastAction.append_action(closechan)
+ lastAction = closechan
+
+ rtc = RequestChannel(
+ reqcon,
+ rch,
+ telepathy.CHANNEL_TYPE_TEXT,
+ smHandleType,
+ )
+ lastAction.append_action(rtc)
+ lastAction = rtc
+
if False:
- sendtext = SendText(con, rtc, rch, telepathy.CHANNEL_TEXT_MESSAGE_TYPE_NORMAL, "Boo!")
+ sendtext = SendText(reqcon, rtc, rch, telepathy.CHANNEL_TEXT_MESSAGE_TYPE_NORMAL, "Boo!")
lastAction.append_action(sendtext)
lastAction = sendtext
lastAction.append_action(bl)
lastAction = bl
- if True:
+ if False:
sl = Sleep(30 * 1000)
lastAction.append_action(sl)
lastAction = sl
- dis = Disconnect(con)
+ dis = Disconnect(reqcon)
lastAction.append_action(dis)
lastAction = dis
--- /dev/null
+import logging
+
+import telepathy
+
+import gtk_toolbox
+
+
+_moduleLogger = logging.getLogger('capabilities')
+
+
+class CapabilitiesMixin(telepathy.server.ConnectionInterfaceCapabilities):
+
+ def __init__(self):
+ telepathy.server.ConnectionInterfaceCapabilities.__init__(self)
+ self._implement_property_get(
+ telepathy.interfaces.CONN_INTERFACE_CAPABILITIES,
+ {"caps": self.GetCapabilities},
+ )
+
+ @property
+ def session(self):
+ """
+ @abstract
+ """
+ raise NotImplementedError("Abstract property called")
+
+ @property
+ def handle(self):
+ """
+ @abstract
+ """
+ raise NotImplementedError("Abstract property called")
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def GetCapabilities(self, handles):
+ """
+ @todo HACK Remove this once we are building against a fixed version of python-telepathy
+ """
+ ret = []
+ for handle in handles:
+ if handle != 0 and (telepathy.HANDLE_TYPE_CONTACT, handle) not in self._handles:
+ raise telepathy.errors.InvalidHandle
+ elif handle in self._caps:
+ theirs = self._caps[handle]
+ for type in theirs:
+ ret.append([handle, type, theirs[0], theirs[1]])
+ _moduleLogger.info("GetCaps %r" % ret)
+ return ret
+
+ @gtk_toolbox.log_exception(_moduleLogger)
+ def AdvertiseCapabilities(self, add, remove):
+ """
+ @todo HACK Remove this once we are building against a fixed version of python-telepathy
+ """
+ my_caps = self._caps.setdefault(self._self_handle, {})
+
+ changed = {}
+ for ctype, spec_caps in add:
+ changed[ctype] = spec_caps
+ for ctype in remove:
+ changed[ctype] = None
+
+ caps = []
+ for ctype, spec_caps in changed.iteritems():
+ gen_old, spec_old = my_caps.get(ctype, (0, 0))
+ if spec_caps is None:
+ # channel type no longer supported (provider has gone away)
+ gen_new, spec_new = 0, 0
+ else:
+ # channel type supports new capabilities
+ gen_new, spec_new = gen_old, spec_old | spec_caps
+ if spec_old != spec_new or gen_old != gen_new:
+ caps.append((self._self_handle, ctype, gen_old, gen_new,
+ spec_old, spec_new))
+
+ _moduleLogger.info("CapsChanged %r" % caps)
+ self.CapabilitiesChanged(caps)
+
+ # return all my capabilities
+ ret = [(ctype, caps[1]) for ctype, caps in my_caps.iteritems()]
+ _moduleLogger.info("Adv %r" % ret)
+ return ret
self._contactHandle = contactHandle
@gtk_toolbox.log_exception(_moduleLogger)
+ def Close(self):
+ self.close()
+
+ def close(self):
+ telepathy.server.ChannelTypeStreamedMedia.Close(self)
+ self.remove_from_connection()
+ self._prop_getters = None # HACK to get around python-telepathy memory leaks
+
+ @gtk_toolbox.log_exception(_moduleLogger)
def ListStreams(self):
"""
For org.freedesktop.Telepathy.Channel.Type.StreamedMedia
def __init__(self, connection, h):
AbstractListChannel.__init__(self, connection, h)
+
self._callback = coroutines.func_sink(
coroutines.expand_positional(
self._on_contacts_refreshed
self._session.addressbook.updateSignalHandler.register_sink(
self._callback
)
+
self.GroupFlagsChanged(0, 0)
addressbook = connection.session.addressbook
@gtk_toolbox.log_exception(_moduleLogger)
def Close(self):
- telepathy.server.ChannelTypeContactList.Close(self)
- self.remove_from_connection()
+ self.close()
+
+ def close(self):
self._session.addressbook.updateSignalHandler.unregister_sink(
self._callback
)
+ self._callback = None
+
+ telepathy.server.ChannelTypeContactList.Close(self)
+ self.remove_from_connection()
+ self._prop_getters = None # HACK to get around python-telepathy memory leaks
@gobject_utils.async
@gtk_toolbox.log_exception(_moduleLogger)
@gtk_toolbox.log_exception(_moduleLogger)
def Close(self):
- try:
- # Clear since the user has seen it all and it should start a new conversation
- self._conn.session.conversations.clear_conversation(self._contactKey)
+ self.close()
- self._conn.session.conversations.updateSignalHandler.unregister_sink(
- self._callback
- )
- finally:
- telepathy.server.ChannelTypeText.Close(self)
- self.remove_from_connection()
+ def close(self):
+ _moduleLogger.info("Closing text channel for %r" % (self._otherHandle, ))
+ self._conn.session.conversations.updateSignalHandler.unregister_sink(
+ self._callback
+ )
+ self._callback = None
+
+ telepathy.server.ChannelTypeText.Close(self)
+ self.remove_from_connection()
+ self._prop_getters = None # HACK to get around python-telepathy memory leaks
@property
def _contactKey(self):
import weakref
+import itertools
import logging
import telepathy
self._callChannels = weakref.WeakValueDictionary()
def close(self):
- for chan in self._listChannels.values():
- chan.Close()
- for chan in self._textChannels.values():
- chan.Close()
- for chan in self._callChannels.values():
- chan.Close()
+ for chan in itertools.chain(
+ self._listChannels.values(), self._textChannels.values(), self._callChannels.values()
+ ):
+ try:
+ chan.close()
+ except Exception:
+ _moduleLogger.exception("Shutting down %r" % (chan, ))
def channel_for_list(self, handle, suppress_handler=False):
try:
import handle
import aliasing
import simple_presence
+import capabilities
import channel_manager
telepathy.server.Connection,
aliasing.AliasingMixin,
simple_presence.SimplePresenceMixin,
+ capabilities.CapabilitiesMixin,
):
# Overriding a base class variable
)
aliasing.AliasingMixin.__init__(self)
simple_presence.SimplePresenceMixin.__init__(self)
+ capabilities.CapabilitiesMixin.__init__(self)
self._manager = weakref.proxy(manager)
self._credentials = (
self._callbackNumber = parameters['forward'].encode('utf-8')
self._channelManager = channel_manager.ChannelManager(self)
- cookieFilePath = None
- self._session = gvoice.session.Session(cookieFilePath)
+ self._session = gvoice.session.Session(None)
self.set_self_handle(handle.create_handle(self, 'connection'))
- self._callback = coroutines.func_sink(
- coroutines.expand_positional(
- self._on_conversations_updated
- )
- )
+ self._callback = None
_moduleLogger.info("Connection to the account %s created" % account)
except Exception, e:
_moduleLogger.exception("Failed to create Connection")
telepathy.CONNECTION_STATUS_REASON_REQUESTED
)
try:
+ cookieFilePath = None
+ self._session = gvoice.session.Session(cookieFilePath)
+
+ self._callback = coroutines.func_sink(
+ coroutines.expand_positional(
+ self._on_conversations_updated
+ )
+ )
self.session.conversations.updateSignalHandler.register_sink(
self._callback
)
self.session.conversations.updateSignalHandler.unregister_sink(
self._callback
)
+ self._callback = None
self._channelManager.close()
self.session.logout()
+ self.session.close()
+ self._session = None
_moduleLogger.info("Disconnected")
except Exception:
_moduleLogger.exception("Disconnecting Failed")
telepathy.CONNECTION_STATUS_DISCONNECTED,
telepathy.CONNECTION_STATUS_REASON_REQUESTED
)
+ self.manager.disconnected(self)
@gtk_toolbox.log_exception(_moduleLogger)
def RequestChannel(self, type, handleType, handleId, suppressHandler):
"""
Empathy Experience:
- .profile file needs to be updated with proper presence
- Can't reopen a conversation for someone when I've already closed it
Can't call
When first started, reports all read conversations when some might have been read
When first started, reports all of an SMS conversation even though some has been reported previously
+ Still leaking one of two contact lists
"""
import logging
Overrides telepathy.server.ConnectionManager
"""
result = telepathy.server.ConnectionManager.disconnected(self, conn)
- gobject.timeout_add(5000, self.shutdown)
+ gobject.timeout_add(5000, self._shutdown)
def quit(self):
"""
connection.Disconnect()
_moduleLogger.info("Connection manager quitting")
+ @gtk_toolbox.log_exception(_moduleLogger)
def _shutdown(self):
if (
self._on_shutdown is not None and
return _safecall
+def log_call(logger):
+
+ def log_call_decorator(func):
+
+ @functools.wraps(func)
+ def wrapper(*args, **kwds):
+ _moduleLogger.info("-> %s" % (func.__name__, ))
+ try:
+ return func(*args, **kwds)
+ finally:
+ _moduleLogger.info("<- %s" % (func.__name__, ))
+
+ return wrapper
+
+ return log_call_decorator
+
+
def log_exception(logger):
def log_exception_decorator(func):
self._stateMachine.request_reset_timers
)
+ def close(self):
+ self._conversations.updateSignalHandler.unregister_sink(
+ self._stateMachine.request_reset_timers
+ )
+ self._stateMachine.close()
+
def login(self, username, password):
self._username = username
self._password = password
class StateMachine(object):
- STATE_ACTIVE = "active"
- STATE_IDLE = "idle"
- STATE_DND = "dnd"
+ STATE_ACTIVE = 0, "active"
+ STATE_IDLE = 1, "idle"
+ STATE_DND = 2, "dnd"
_ACTION_UPDATE = "update"
_ACTION_RESET = "reset"
)
)
+ def close(self):
+ self._callback = None
+
@gobject_utils.async
@gtk_toolbox.log_exception(_moduleLogger)
def start(self):
_moduleLogger.info("Stopping an already stopped state machine")
self._stop_update()
- def set_state(self, state):
- self._state = state
+ def set_state(self, newState):
+ oldState = self._state
+ _moduleLogger.info("Transitioning from %s to %s" % (oldState, newState))
+
+ self._state = newState
self.reset_timers()
def get_state(self):
import gtk_toolbox
import handle
+import gvoice.state_machine as state_machine
_moduleLogger = logging.getLogger("simple_presence")
class TheOneRingPresence(object):
+
+ # Note: these strings are also in the theonering.profile file
ONLINE = 'available'
- IDLE = 'idle'
- BUSY = 'dnd'
+ AWAY = 'away'
+ HIDDEN = 'hidden'
+ OFFLINE = 'offline'
TO_PRESENCE_TYPE = {
ONLINE: telepathy.constants.CONNECTION_PRESENCE_TYPE_AVAILABLE,
- IDLE: telepathy.constants.CONNECTION_PRESENCE_TYPE_AWAY,
- BUSY: telepathy.constants.CONNECTION_PRESENCE_TYPE_HIDDEN,
+ AWAY: telepathy.constants.CONNECTION_PRESENCE_TYPE_AWAY,
+ HIDDEN: telepathy.constants.CONNECTION_PRESENCE_TYPE_HIDDEN,
+ OFFLINE: telepathy.constants.CONNECTION_PRESENCE_TYPE_OFFLINE,
}
def __init__(self):
telepathy.server.ConnectionInterfaceSimplePresence.__init__(self)
- dbus_interface = 'org.freedesktop.Telepathy.Connection.Interface.SimplePresence'
-
- self._implement_property_get(dbus_interface, {'Statuses' : self._get_statuses})
+ self._implement_property_get(
+ telepathy.server.CONNECTION_INTERFACE_SIMPLE_PRESENCE,
+ {'Statuses' : self._get_statuses}
+ )
@property
def session(self):
"""
raise NotImplementedError("Abstract property called")
+ def Disconnect(self):
+ """
+ @abstract
+ """
+ raise NotImplementedError("Abstract function called")
+
@gtk_toolbox.log_exception(_moduleLogger)
def GetPresences(self, contacts):
"""
if isinstance(h, handle.ConnectionHandle):
isDnd = self.session.backend.is_dnd()
if isDnd:
- presence = TheOneRingPresence.BUSY
+ presence = TheOneRingPresence.HIDDEN
else:
- # @todo switch this over to also supporting idle
- presence = TheOneRingPresence.ONLINE
+ state = self.session.stateMachine.get_state()
+ if state == state_machine.StateMachine.STATE_ACTIVE:
+ presence = TheOneRingPresence.ONLINE
+ elif state == state_machine.StateMachine.STATE_IDLE:
+ presence = TheOneRingPresence.AWAY
+ else:
+ raise telepathy.errors.InvalidArgument("Unsupported state on the state machine: %s" % state)
personalMessage = u""
presenceType = TheOneRingPresence.TO_PRESENCE_TYPE[presence]
else:
if status == TheOneRingPresence.ONLINE:
self.session.backend.set_dnd(False)
- elif status == TheOneRingPresence.IDLE:
- # @todo Add idle support
- raise telepathy.errors.InvalidArgument("Not Supported Yet")
- elif status == TheOneRingPresence.BUSY:
+ self.session.stateMachine.set_state(state_machine.StateMachine.STATE_ACTIVE)
+ elif status == TheOneRingPresence.AWAY:
+ self.session.stateMachine.set_state(state_machine.StateMachine.STATE_IDLE)
+ elif status == TheOneRingPresence.HIDDEN:
self.session.backend.set_dnd(True)
+ elif status == TheOneRingPresence.OFFLINE:
+ self.Disconnect()
else:
raise telepathy.errors.InvalidArgument("Unsupported status: %r" % status)
_moduleLogger.info("Setting Presence to '%s'" % status)
Capabilities = chat-p2p, voice-p2p, supports-alias
VCardDefault = 1
VCardField = X-GV
-SupportedPresences = offline,available,away,extended-away,hidden,do-not-disturb
+SupportedPresences = offline,available,away,hidden