core: style improvments (90 chars limit)
This commit is contained in:
parent
0e23f244f5
commit
ff79fa3ce0
5 changed files with 219 additions and 196 deletions
|
|
@ -55,7 +55,8 @@ class SAT(service.Service):
|
||||||
self._menus = (
|
self._menus = (
|
||||||
OrderedDict()
|
OrderedDict()
|
||||||
) # dynamic menus. key: callback_id, value: menu data (dictionnary)
|
) # dynamic menus. key: callback_id, value: menu data (dictionnary)
|
||||||
self._menus_paths = {} # path to id. key: (menu_type, lower case tuple of path), value: menu id
|
self._menus_paths = {} # path to id. key: (menu_type, lower case tuple of path),
|
||||||
|
# value: menu id
|
||||||
self.initialised = defer.Deferred()
|
self.initialised = defer.Deferred()
|
||||||
self.profiles = {}
|
self.profiles = {}
|
||||||
self.plugins = {}
|
self.plugins = {}
|
||||||
|
|
@ -153,7 +154,10 @@ class SAT(service.Service):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def full_version(self):
|
def full_version(self):
|
||||||
"""Return the full version of SàT (with release name and extra data when in development mode)"""
|
"""Return the full version of SàT
|
||||||
|
|
||||||
|
In developement mode, release name and extra data are returned too
|
||||||
|
"""
|
||||||
version = self.version
|
version = self.version
|
||||||
if version[-1] == "D":
|
if version[-1] == "D":
|
||||||
# we are in debug version, we add extra data
|
# we are in debug version, we add extra data
|
||||||
|
|
@ -201,8 +205,9 @@ class SAT(service.Service):
|
||||||
# FIXME: module imported but cancelled should be deleted
|
# FIXME: module imported but cancelled should be deleted
|
||||||
# TODO: make this more generic and reusable in tools.common
|
# TODO: make this more generic and reusable in tools.common
|
||||||
# FIXME: should use imp
|
# FIXME: should use imp
|
||||||
# TODO: do not import all plugins if no needed: component plugins are not needed if we
|
# TODO: do not import all plugins if no needed: component plugins are not needed
|
||||||
# just use a client, and plugin blacklisting should be possible in sat.conf
|
# if we just use a client, and plugin blacklisting should be possible in
|
||||||
|
# sat.conf
|
||||||
plugins_path = os.path.dirname(sat.plugins.__file__)
|
plugins_path = os.path.dirname(sat.plugins.__file__)
|
||||||
plugin_glob = "plugin*." + C.PLUGIN_EXT
|
plugin_glob = "plugin*." + C.PLUGIN_EXT
|
||||||
plug_lst = [
|
plug_lst = [
|
||||||
|
|
@ -219,7 +224,8 @@ class SAT(service.Service):
|
||||||
except exceptions.MissingModule as e:
|
except exceptions.MissingModule as e:
|
||||||
self._unimport_plugin(plugin_path)
|
self._unimport_plugin(plugin_path)
|
||||||
log.warning(
|
log.warning(
|
||||||
u"Can't import plugin [{path}] because of an unavailale third party module:\n{msg}".format(
|
u"Can't import plugin [{path}] because of an unavailale third party "
|
||||||
|
u"module:\n{msg}".format(
|
||||||
path=plugin_path, msg=e
|
path=plugin_path, msg=e
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -265,7 +271,8 @@ class SAT(service.Service):
|
||||||
if import_name in plugins_to_import:
|
if import_name in plugins_to_import:
|
||||||
log.error(
|
log.error(
|
||||||
_(
|
_(
|
||||||
u"Name conflict for import name [{import_name}], can't import plugin [{name}]"
|
u"Name conflict for import name [{import_name}], can't import "
|
||||||
|
u"plugin [{name}]"
|
||||||
).format(**plugin_info)
|
).format(**plugin_info)
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
|
@ -283,9 +290,12 @@ class SAT(service.Service):
|
||||||
):
|
):
|
||||||
"""Recursively import and their dependencies in the right order
|
"""Recursively import and their dependencies in the right order
|
||||||
|
|
||||||
@param plugins_to_import(dict): key=import_name and values=(plugin_path, module, plugin_info)
|
@param plugins_to_import(dict): key=import_name and values=(plugin_path, module,
|
||||||
@param import_name(unicode, None): name of the plugin to import as found in PLUGIN_INFO['import_name']
|
plugin_info)
|
||||||
@param optional(bool): if False and plugin is not found, an ImportError exception is raised
|
@param import_name(unicode, None): name of the plugin to import as found in
|
||||||
|
PLUGIN_INFO['import_name']
|
||||||
|
@param optional(bool): if False and plugin is not found, an ImportError exception
|
||||||
|
is raised
|
||||||
"""
|
"""
|
||||||
if import_name in self.plugins:
|
if import_name in self.plugins:
|
||||||
log.debug(u"Plugin {} already imported, passing".format(import_name))
|
log.debug(u"Plugin {} already imported, passing".format(import_name))
|
||||||
|
|
@ -417,7 +427,8 @@ class SAT(service.Service):
|
||||||
|
|
||||||
Return list of activated plugins and plugin specific data
|
Return list of activated plugins and plugin specific data
|
||||||
@param profile_key: %(doc_profile_key)s
|
@param profile_key: %(doc_profile_key)s
|
||||||
C.PROF_KEY_NONE can be used to have general plugins data (i.e. not profile dependent)
|
C.PROF_KEY_NONE can be used to have general plugins data (i.e. not profile
|
||||||
|
dependent)
|
||||||
@return (dict)[Deferred]: features data where:
|
@return (dict)[Deferred]: features data where:
|
||||||
- key is plugin import name, present only for activated plugins
|
- key is plugin import name, present only for activated plugins
|
||||||
- value is a an other dict, when meaning is specific to each plugin.
|
- value is a an other dict, when meaning is specific to each plugin.
|
||||||
|
|
@ -534,8 +545,9 @@ class SAT(service.Service):
|
||||||
raise exceptions.NotFound(profile_key)
|
raise exceptions.NotFound(profile_key)
|
||||||
|
|
||||||
def getClients(self, profile_key):
|
def getClients(self, profile_key):
|
||||||
"""Convenient method to get list of clients from profile key (manage list through profile_key like C.PROF_KEY_ALL)
|
"""Convenient method to get list of clients from profile key
|
||||||
|
|
||||||
|
Manage list through profile_key like C.PROF_KEY_ALL
|
||||||
@param profile_key: %(doc_profile_key)s
|
@param profile_key: %(doc_profile_key)s
|
||||||
@return: list of clients
|
@return: list of clients
|
||||||
"""
|
"""
|
||||||
|
|
@ -599,8 +611,8 @@ class SAT(service.Service):
|
||||||
@param *extra_path: extra path element(s) to use
|
@param *extra_path: extra path element(s) to use
|
||||||
@return (unicode): path
|
@return (unicode): path
|
||||||
"""
|
"""
|
||||||
# FIXME: component and profile are parsed with **kwargs because of python 2 limitations
|
# FIXME: component and profile are parsed with **kwargs because of python 2
|
||||||
# once moved to python 3, this can be fixed
|
# limitations. Once moved to python 3, this can be fixed
|
||||||
component = kwargs.pop("component", False)
|
component = kwargs.pop("component", False)
|
||||||
profile = kwargs.pop("profile", True)
|
profile = kwargs.pop("profile", True)
|
||||||
assert not kwargs
|
assert not kwargs
|
||||||
|
|
@ -639,18 +651,12 @@ class SAT(service.Service):
|
||||||
|
|
||||||
## XMPP methods ##
|
## XMPP methods ##
|
||||||
|
|
||||||
def _messageSend(
|
def _messageSend(self, to_jid_s, message, subject=None, mess_type="auto", extra=None,
|
||||||
self,
|
profile_key=C.PROF_KEY_NONE,):
|
||||||
to_jid_s,
|
|
||||||
message,
|
|
||||||
subject=None,
|
|
||||||
mess_type="auto",
|
|
||||||
extra=None,
|
|
||||||
profile_key=C.PROF_KEY_NONE,
|
|
||||||
):
|
|
||||||
client = self.getClient(profile_key)
|
client = self.getClient(profile_key)
|
||||||
to_jid = jid.JID(to_jid_s)
|
to_jid = jid.JID(to_jid_s)
|
||||||
# XXX: we need to use the dictionary comprehension because D-Bus return its own types, and pickle can't manage them. TODO: Need to find a better way
|
# XXX: we need to use the dictionary comprehension because D-Bus return its own
|
||||||
|
# types, and pickle can't manage them. TODO: Need to find a better way
|
||||||
return client.sendMessage(
|
return client.sendMessage(
|
||||||
to_jid,
|
to_jid,
|
||||||
message,
|
message,
|
||||||
|
|
@ -674,7 +680,8 @@ class SAT(service.Service):
|
||||||
self.memory.getParamA("Priority", "Connection", profile_key=profile)
|
self.memory.getParamA("Priority", "Connection", profile_key=profile)
|
||||||
)
|
)
|
||||||
self.profiles[profile].presence.available(to_jid, show, statuses, priority)
|
self.profiles[profile].presence.available(to_jid, show, statuses, priority)
|
||||||
# XXX: FIXME: temporary fix to work around openfire 3.7.0 bug (presence is not broadcasted to generating resource)
|
# XXX: FIXME: temporary fix to work around openfire 3.7.0 bug (presence is not
|
||||||
|
# broadcasted to generating resource)
|
||||||
if "" in statuses:
|
if "" in statuses:
|
||||||
statuses[C.PRESENCE_STATUSES_DEFAULT] = statuses.pop("")
|
statuses[C.PRESENCE_STATUSES_DEFAULT] = statuses.pop("")
|
||||||
self.bridge.presenceUpdate(
|
self.bridge.presenceUpdate(
|
||||||
|
|
@ -709,7 +716,8 @@ class SAT(service.Service):
|
||||||
"""Add a contact in roster list"""
|
"""Add a contact in roster list"""
|
||||||
profile = self.memory.getProfileName(profile_key)
|
profile = self.memory.getProfileName(profile_key)
|
||||||
assert profile
|
assert profile
|
||||||
# presence is sufficient, as a roster push will be sent according to RFC 6121 §3.1.2
|
# presence is sufficient, as a roster push will be sent according to
|
||||||
|
# RFC 6121 §3.1.2
|
||||||
self.profiles[profile].presence.subscribe(to_jid)
|
self.profiles[profile].presence.subscribe(to_jid)
|
||||||
|
|
||||||
def _updateContact(self, to_jid_s, name, groups, profile_key):
|
def _updateContact(self, to_jid_s, name, groups, profile_key):
|
||||||
|
|
@ -801,16 +809,18 @@ class SAT(service.Service):
|
||||||
"""retrieve all services or contacts managing a set a features
|
"""retrieve all services or contacts managing a set a features
|
||||||
|
|
||||||
@param namespaces(list[unicode]): features which must be handled
|
@param namespaces(list[unicode]): features which must be handled
|
||||||
@param identities(list[tuple[unicode,unicode]], None): if not None or empty, only keep those identities
|
@param identities(list[tuple[unicode,unicode]], None): if not None or empty,
|
||||||
tuple must by (category, type)
|
only keep those identities tuple must by (category, type)
|
||||||
@param bare_jids(bool): retrieve only bare_jids if True
|
@param bare_jids(bool): retrieve only bare_jids if True
|
||||||
if False, retrieve full jid of connected devices
|
if False, retrieve full jid of connected devices
|
||||||
@param service(bool): if True return service from our roster
|
@param service(bool): if True return service from our roster
|
||||||
@param roster(bool): if True, return entities in roster
|
@param roster(bool): if True, return entities in roster
|
||||||
full jid of all matching resources available will be returned
|
full jid of all matching resources available will be returned
|
||||||
@param own_jid(bool): if True, return profile's jid resources
|
@param own_jid(bool): if True, return profile's jid resources
|
||||||
@param local_device(bool): if True, return profile's jid local resource (i.e. client.jid)
|
@param local_device(bool): if True, return profile's jid local resource
|
||||||
@return (tuple(dict[jid.JID(), tuple[unicode, unicode, unicode]]*3)): found entities in a tuple with:
|
(i.e. client.jid)
|
||||||
|
@return (tuple(dict[jid.JID(), tuple[unicode, unicode, unicode]]*3)): found
|
||||||
|
entities in a tuple with:
|
||||||
- service entities
|
- service entities
|
||||||
- own entities
|
- own entities
|
||||||
- roster entities
|
- roster entities
|
||||||
|
|
@ -896,9 +906,9 @@ class SAT(service.Service):
|
||||||
|
|
||||||
@param action_data(dict): action data (see bridge documentation)
|
@param action_data(dict): action data (see bridge documentation)
|
||||||
@param security_limit: %(doc_security_limit)s
|
@param security_limit: %(doc_security_limit)s
|
||||||
@param keep_id(None, unicode): if not None, used to keep action for differed retrieval
|
@param keep_id(None, unicode): if not None, used to keep action for differed
|
||||||
must be set to the callback_id
|
retrieval. Must be set to the callback_id.
|
||||||
action will be deleted after 30 min.
|
Action will be deleted after 30 min.
|
||||||
@param profile: %(doc_profile)s
|
@param profile: %(doc_profile)s
|
||||||
"""
|
"""
|
||||||
id_ = unicode(uuid.uuid4())
|
id_ = unicode(uuid.uuid4())
|
||||||
|
|
@ -948,7 +958,8 @@ class SAT(service.Service):
|
||||||
@return (dict): data with the following keys:
|
@return (dict): data with the following keys:
|
||||||
'position' (int): current possition
|
'position' (int): current possition
|
||||||
'size' (int): end_position
|
'size' (int): end_position
|
||||||
if id doesn't exists (may be a finished progression), and empty dict is returned
|
if id doesn't exists (may be a finished progression), and empty dict is
|
||||||
|
returned
|
||||||
"""
|
"""
|
||||||
client = self.getClient(profile)
|
client = self.getClient(profile)
|
||||||
try:
|
try:
|
||||||
|
|
@ -969,7 +980,8 @@ class SAT(service.Service):
|
||||||
"""Return all progress metadata at once
|
"""Return all progress metadata at once
|
||||||
|
|
||||||
@param profile_key: %(doc_profile)s
|
@param profile_key: %(doc_profile)s
|
||||||
if C.PROF_KEY_ALL is used, all progress metadata from all profiles are returned
|
if C.PROF_KEY_ALL is used, all progress metadata from all profiles are
|
||||||
|
returned
|
||||||
@return (dict[dict[dict]]): a dict which map profile to progress_dict
|
@return (dict[dict[dict]]): a dict which map profile to progress_dict
|
||||||
progress_dict map progress_id to progress_data
|
progress_dict map progress_id to progress_data
|
||||||
progress_metadata is the same dict as sent by [progressStarted]
|
progress_metadata is the same dict as sent by [progressStarted]
|
||||||
|
|
@ -1012,7 +1024,8 @@ class SAT(service.Service):
|
||||||
@param callback(callable): method to call
|
@param callback(callable): method to call
|
||||||
@param kwargs: can contain:
|
@param kwargs: can contain:
|
||||||
with_data(bool): True if the callback use the optional data dict
|
with_data(bool): True if the callback use the optional data dict
|
||||||
force_id(unicode): id to avoid generated id. Can lead to name conflict, avoid if possible
|
force_id(unicode): id to avoid generated id. Can lead to name conflict, avoid
|
||||||
|
if possible
|
||||||
one_shot(bool): True to delete callback once it have been called
|
one_shot(bool): True to delete callback once it have been called
|
||||||
@return: id of the registered callback
|
@return: id of the registered callback
|
||||||
"""
|
"""
|
||||||
|
|
@ -1050,7 +1063,8 @@ class SAT(service.Service):
|
||||||
@profile_key: %(doc_profile_key)s
|
@profile_key: %(doc_profile_key)s
|
||||||
@return: a deferred which fire a dict where key can be:
|
@return: a deferred which fire a dict where key can be:
|
||||||
- xmlui: a XMLUI need to be displayed
|
- xmlui: a XMLUI need to be displayed
|
||||||
- validated: if present, can be used to launch a callback, it can have the values
|
- validated: if present, can be used to launch a callback, it can have the
|
||||||
|
values
|
||||||
- C.BOOL_TRUE
|
- C.BOOL_TRUE
|
||||||
- C.BOOL_FALSE
|
- C.BOOL_FALSE
|
||||||
"""
|
"""
|
||||||
|
|
@ -1117,25 +1131,33 @@ class SAT(service.Service):
|
||||||
):
|
):
|
||||||
"""register a new menu for frontends
|
"""register a new menu for frontends
|
||||||
|
|
||||||
@param path(iterable[unicode]): path to go to the menu (category/subcategory/.../item) (e.g.: ("File", "Open"))
|
@param path(iterable[unicode]): path to go to the menu
|
||||||
|
(category/subcategory/.../item) (e.g.: ("File", "Open"))
|
||||||
/!\ use D_() instead of _() for translations (e.g. (D_("File"), D_("Open")))
|
/!\ use D_() instead of _() for translations (e.g. (D_("File"), D_("Open")))
|
||||||
untranslated/lower case path can be used to identity a menu, for this reason it must be unique independently of case.
|
untranslated/lower case path can be used to identity a menu, for this reason
|
||||||
@param callback(callable): method to be called when menuitem is selected, callable or a callback id (string) as returned by [registerCallback]
|
it must be unique independently of case.
|
||||||
|
@param callback(callable): method to be called when menuitem is selected, callable
|
||||||
|
or a callback id (string) as returned by [registerCallback]
|
||||||
@param security_limit(int): %(doc_security_limit)s
|
@param security_limit(int): %(doc_security_limit)s
|
||||||
/!\ security_limit MUST be added to data in launchCallback if used #TODO
|
/!\ security_limit MUST be added to data in launchCallback if used #TODO
|
||||||
@param help_string(unicode): string used to indicate what the menu do (can be show as a tooltip).
|
@param help_string(unicode): string used to indicate what the menu do (can be
|
||||||
|
show as a tooltip).
|
||||||
/!\ use D_() instead of _() for translations
|
/!\ use D_() instead of _() for translations
|
||||||
@param type(unicode): one of:
|
@param type(unicode): one of:
|
||||||
- C.MENU_GLOBAL: classical menu, can be shown in a menubar on top (e.g. something like File/Open)
|
- C.MENU_GLOBAL: classical menu, can be shown in a menubar on top (e.g.
|
||||||
|
something like File/Open)
|
||||||
- C.MENU_ROOM: like a global menu, but only shown in multi-user chat
|
- C.MENU_ROOM: like a global menu, but only shown in multi-user chat
|
||||||
menu_data must contain a "room_jid" data
|
menu_data must contain a "room_jid" data
|
||||||
- C.MENU_SINGLE: like a global menu, but only shown in one2one chat
|
- C.MENU_SINGLE: like a global menu, but only shown in one2one chat
|
||||||
menu_data must contain a "jid" data
|
menu_data must contain a "jid" data
|
||||||
- C.MENU_JID_CONTEXT: contextual menu, used with any jid (e.g.: ad hoc commands, jid is already filled)
|
- C.MENU_JID_CONTEXT: contextual menu, used with any jid (e.g.: ad hoc
|
||||||
|
commands, jid is already filled)
|
||||||
menu_data must contain a "jid" data
|
menu_data must contain a "jid" data
|
||||||
- C.MENU_ROSTER_JID_CONTEXT: like JID_CONTEXT, but restricted to jids in roster.
|
- C.MENU_ROSTER_JID_CONTEXT: like JID_CONTEXT, but restricted to jids in
|
||||||
|
roster.
|
||||||
menu_data must contain a "room_jid" data
|
menu_data must contain a "room_jid" data
|
||||||
- C.MENU_ROSTER_GROUP_CONTEXT: contextual menu, used with group (e.g.: publish microblog, group is already filled)
|
- C.MENU_ROSTER_GROUP_CONTEXT: contextual menu, used with group (e.g.: publish
|
||||||
|
microblog, group is already filled)
|
||||||
menu_data must contain a "group" data
|
menu_data must contain a "group" data
|
||||||
@return (unicode): menu_id (same as callback_id)
|
@return (unicode): menu_id (same as callback_id)
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
143
sat/core/xmpp.py
143
sat/core/xmpp.py
|
|
@ -54,9 +54,11 @@ class SatXMPPEntity(object):
|
||||||
self.profile = profile
|
self.profile = profile
|
||||||
self.host_app = host_app
|
self.host_app = host_app
|
||||||
self.cache = cache.Cache(host_app, profile)
|
self.cache = cache.Cache(host_app, profile)
|
||||||
self._mess_id_uid = {} # map from message id to uid used in history. Key: (full_jid,message_id) Value: uid
|
self._mess_id_uid = {} # map from message id to uid used in history.
|
||||||
|
# Key: (full_jid,message_id) Value: uid
|
||||||
self.conn_deferred = defer.Deferred()
|
self.conn_deferred = defer.Deferred()
|
||||||
self._progress_cb = {} # callback called when a progress is requested (key = progress id)
|
self._progress_cb = {} # callback called when a progress is requested
|
||||||
|
# (key = progress id)
|
||||||
self.actions = {} # used to keep track of actions for retrieval (key = action_id)
|
self.actions = {} # used to keep track of actions for retrieval (key = action_id)
|
||||||
|
|
||||||
## initialisation ##
|
## initialisation ##
|
||||||
|
|
@ -113,10 +115,12 @@ class SatXMPPEntity(object):
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def startConnection(cls, host, profile, max_retries):
|
def startConnection(cls, host, profile, max_retries):
|
||||||
"""instantiate the entity and start the connection"""
|
"""instantiate the entity and start the connection"""
|
||||||
# FIXME: reconnection doesn't seems to be handled correclty (client is deleted then recreated from scrash
|
# FIXME: reconnection doesn't seems to be handled correclty
|
||||||
# most of methods called here should be called once on first connection (e.g. adding subprotocols)
|
# (client is deleted then recreated from scratch)
|
||||||
# but client should not be deleted except if session is finished (independently of connection/deconnection
|
# most of methods called here should be called once on first connection
|
||||||
#
|
# (e.g. adding subprotocols)
|
||||||
|
# but client should not be deleted except if session is finished
|
||||||
|
# (independently of connection/deconnection)
|
||||||
try:
|
try:
|
||||||
port = int(
|
port = int(
|
||||||
host.memory.getParamA(
|
host.memory.getParamA(
|
||||||
|
|
@ -164,9 +168,10 @@ class SatXMPPEntity(object):
|
||||||
|
|
||||||
yield defer.maybeDeferred(entity.entityConnected)
|
yield defer.maybeDeferred(entity.entityConnected)
|
||||||
|
|
||||||
# Call profileConnected callback for all plugins, and print error message if any of them fails
|
# Call profileConnected callback for all plugins,
|
||||||
|
# and print error message if any of them fails
|
||||||
conn_cb_list = []
|
conn_cb_list = []
|
||||||
for dummy, callback in plugin_conn_cb:
|
for __, callback in plugin_conn_cb:
|
||||||
conn_cb_list.append(defer.maybeDeferred(callback, entity))
|
conn_cb_list.append(defer.maybeDeferred(callback, entity))
|
||||||
list_d = defer.DeferredList(conn_cb_list)
|
list_d = defer.DeferredList(conn_cb_list)
|
||||||
|
|
||||||
|
|
@ -193,7 +198,7 @@ class SatXMPPEntity(object):
|
||||||
"""Return a deferred which fire when the client is connected"""
|
"""Return a deferred which fire when the client is connected"""
|
||||||
return self.conn_deferred
|
return self.conn_deferred
|
||||||
|
|
||||||
def _disconnectionCb(self, dummy):
|
def _disconnectionCb(self, __):
|
||||||
self._connected = None
|
self._connected = None
|
||||||
|
|
||||||
def _disconnectionEb(self, failure_):
|
def _disconnectionEb(self, failure_):
|
||||||
|
|
@ -219,7 +224,7 @@ class SatXMPPEntity(object):
|
||||||
self.profile, unicode(self.jid)
|
self.profile, unicode(self.jid)
|
||||||
) # we send the signal to the clients
|
) # we send the signal to the clients
|
||||||
|
|
||||||
def _finish_connection(self, dummy):
|
def _finish_connection(self, __):
|
||||||
self.conn_deferred.callback(None)
|
self.conn_deferred.callback(None)
|
||||||
|
|
||||||
def streamInitialized(self):
|
def streamInitialized(self):
|
||||||
|
|
@ -283,7 +288,7 @@ class SatXMPPEntity(object):
|
||||||
)
|
)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def _cleanConnection(self, dummy):
|
def _cleanConnection(self, __):
|
||||||
"""method called on disconnection
|
"""method called on disconnection
|
||||||
|
|
||||||
used to call profileDisconnected* triggers
|
used to call profileDisconnected* triggers
|
||||||
|
|
@ -380,17 +385,9 @@ class SatXMPPEntity(object):
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def sendMessage(
|
def sendMessage(self, to_jid, message, subject=None, mess_type="auto", extra=None,
|
||||||
self,
|
uid=None, no_trigger=False,):
|
||||||
to_jid,
|
r"""Send a message to an entity
|
||||||
message,
|
|
||||||
subject=None,
|
|
||||||
mess_type="auto",
|
|
||||||
extra=None,
|
|
||||||
uid=None,
|
|
||||||
no_trigger=False,
|
|
||||||
):
|
|
||||||
"""Send a message to an entity
|
|
||||||
|
|
||||||
@param to_jid(jid.JID): destinee of the message
|
@param to_jid(jid.JID): destinee of the message
|
||||||
@param message(dict): message body, key is the language (use '' when unknown)
|
@param message(dict): message body, key is the language (use '' when unknown)
|
||||||
|
|
@ -470,21 +467,15 @@ class SatXMPPEntity(object):
|
||||||
):
|
):
|
||||||
return defer.succeed(None)
|
return defer.succeed(None)
|
||||||
|
|
||||||
log.debug(
|
log.debug(_(u"Sending message (type {type}, to {to})")
|
||||||
_(u"Sending message (type {type}, to {to})").format(
|
.format(type=data["type"], to=to_jid.full()))
|
||||||
type=data["type"], to=to_jid.full()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
pre_xml_treatments.addCallback(lambda dummy: self.generateMessageXML(data))
|
pre_xml_treatments.addCallback(lambda __: self.generateMessageXML(data))
|
||||||
pre_xml_treatments.chainDeferred(post_xml_treatments)
|
pre_xml_treatments.chainDeferred(post_xml_treatments)
|
||||||
post_xml_treatments.addCallback(self.sendMessageData)
|
post_xml_treatments.addCallback(self.sendMessageData)
|
||||||
if send_only:
|
if send_only:
|
||||||
log.debug(
|
log.debug(_(u"Triggers, storage and echo have been inhibited by the "
|
||||||
_(
|
u"'send_only' parameter"))
|
||||||
"Triggers, storage and echo have been inhibited by the 'send_only' parameter"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
self.addPostXmlCallbacks(post_xml_treatments)
|
self.addPostXmlCallbacks(post_xml_treatments)
|
||||||
post_xml_treatments.addErrback(self._cancelErrorTrap)
|
post_xml_treatments.addErrback(self._cancelErrorTrap)
|
||||||
|
|
@ -562,8 +553,10 @@ class SatXMPPClient(SatXMPPEntity, wokkel_client.XMPPClient):
|
||||||
self.started = time.time()
|
self.started = time.time()
|
||||||
|
|
||||||
# Currently, we use "client/pc/Salut à Toi", but as
|
# Currently, we use "client/pc/Salut à Toi", but as
|
||||||
# SàT is multi-frontends and can be used on mobile devices, as a bot, with a web frontend,
|
# SàT is multi-frontends and can be used on mobile devices, as a bot,
|
||||||
# etc., we should implement a way to dynamically update identities through the bridge
|
# with a web frontend,
|
||||||
|
# etc., we should implement a way to dynamically update identities through the
|
||||||
|
# bridge
|
||||||
self.identities = [disco.DiscoIdentity(u"client", u"pc", C.APP_NAME)]
|
self.identities = [disco.DiscoIdentity(u"client", u"pc", C.APP_NAME)]
|
||||||
if sys.platform == "android":
|
if sys.platform == "android":
|
||||||
# FIXME: temporary hack as SRV is not working on android
|
# FIXME: temporary hack as SRV is not working on android
|
||||||
|
|
@ -591,9 +584,8 @@ class SatXMPPClient(SatXMPPEntity, wokkel_client.XMPPClient):
|
||||||
host_data = None
|
host_data = None
|
||||||
if host_data is not None:
|
if host_data is not None:
|
||||||
log.info(
|
log.info(
|
||||||
u"using {host}:{port} for host {host_ori} as requested in config".format(
|
u"using {host}:{port} for host {host_ori} as requested in config"
|
||||||
host_ori=user_jid.host, host=host, port=port
|
.format(host_ori=user_jid.host, host=host, port=port)
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
wokkel_client.XMPPClient.__init__(
|
wokkel_client.XMPPClient.__init__(
|
||||||
|
|
@ -634,7 +626,8 @@ class SatXMPPClient(SatXMPPEntity, wokkel_client.XMPPClient):
|
||||||
# (out of band transmission for instance).
|
# (out of band transmission for instance).
|
||||||
# e2e should have a priority of 0 here, and out of band transmission
|
# e2e should have a priority of 0 here, and out of band transmission
|
||||||
# a lower priority
|
# a lower priority
|
||||||
# FIXME: trigger not used yet, can be uncommented when e2e full stanza encryption is implemented
|
# FIXME: trigger not used yet, can be uncommented when e2e full stanza
|
||||||
|
# encryption is implemented
|
||||||
# if not self.host_app.trigger.point("send", self, obj):
|
# if not self.host_app.trigger.point("send", self, obj):
|
||||||
# return
|
# return
|
||||||
super(SatXMPPClient, self).send(obj)
|
super(SatXMPPClient, self).send(obj)
|
||||||
|
|
@ -648,8 +641,10 @@ class SatXMPPClient(SatXMPPEntity, wokkel_client.XMPPClient):
|
||||||
@param mess_data(dict): message data as constructed by onMessage workflow
|
@param mess_data(dict): message data as constructed by onMessage workflow
|
||||||
@return (dict): mess_data (so it can be used in a deferred chain)
|
@return (dict): mess_data (so it can be used in a deferred chain)
|
||||||
"""
|
"""
|
||||||
# XXX: This is the last trigger before u"send" (last but one globally) for sending message.
|
# XXX: This is the last trigger before u"send" (last but one globally)
|
||||||
# This is intented for e2e encryption which doesn't do full stanza encryption (e.g. OTR)
|
# for sending message.
|
||||||
|
# This is intented for e2e encryption which doesn't do full stanza
|
||||||
|
# encryption (e.g. OTR)
|
||||||
# This trigger point can't cancel the method
|
# This trigger point can't cancel the method
|
||||||
self.host_app.trigger.point("sendMessageData", self, mess_data)
|
self.host_app.trigger.point("sendMessageData", self, mess_data)
|
||||||
self.send(mess_data[u"xml"])
|
self.send(mess_data[u"xml"])
|
||||||
|
|
@ -675,10 +670,10 @@ class SatXMPPClient(SatXMPPEntity, wokkel_client.XMPPClient):
|
||||||
profile=self.profile,
|
profile=self.profile,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _finish_connection(self, dummy):
|
def _finish_connection(self, __):
|
||||||
self.roster.requestRoster()
|
self.roster.requestRoster()
|
||||||
self.presence.available()
|
self.presence.available()
|
||||||
super(SatXMPPClient, self)._finish_connection(dummy)
|
super(SatXMPPClient, self)._finish_connection(__)
|
||||||
|
|
||||||
|
|
||||||
class SatXMPPComponent(SatXMPPEntity, component.Component):
|
class SatXMPPComponent(SatXMPPEntity, component.Component):
|
||||||
|
|
@ -696,7 +691,8 @@ class SatXMPPComponent(SatXMPPEntity, component.Component):
|
||||||
is_component = True
|
is_component = True
|
||||||
sendHistory = (
|
sendHistory = (
|
||||||
False
|
False
|
||||||
) # XXX: set to True from entry plugin to keep messages in history for received messages
|
) # XXX: set to True from entry plugin to keep messages in history for received
|
||||||
|
# messages
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
|
@ -748,7 +744,8 @@ class SatXMPPComponent(SatXMPPEntity, component.Component):
|
||||||
@param required(bool): True if plugin is mandatory
|
@param required(bool): True if plugin is mandatory
|
||||||
for recursive calls only, should not be modified by inital caller
|
for recursive calls only, should not be modified by inital caller
|
||||||
@raise InternalError: one of the plugin is not handling components
|
@raise InternalError: one of the plugin is not handling components
|
||||||
@raise KeyError: one plugin should be present in self.host_app.plugins but it is not
|
@raise KeyError: one plugin should be present in self.host_app.plugins but it
|
||||||
|
is not
|
||||||
"""
|
"""
|
||||||
if C.PLUG_MODE_COMPONENT not in current._info[u"modes"]:
|
if C.PLUG_MODE_COMPONENT not in current._info[u"modes"]:
|
||||||
if not required:
|
if not required:
|
||||||
|
|
@ -756,7 +753,8 @@ class SatXMPPComponent(SatXMPPEntity, component.Component):
|
||||||
else:
|
else:
|
||||||
log.error(
|
log.error(
|
||||||
_(
|
_(
|
||||||
u"Plugin {current_name} is needed for {entry_name}, but it doesn't handle component mode"
|
u"Plugin {current_name} is needed for {entry_name}, "
|
||||||
|
u"but it doesn't handle component mode"
|
||||||
).format(
|
).format(
|
||||||
current_name=current._info[u"import_name"],
|
current_name=current._info[u"import_name"],
|
||||||
entry_name=self.entry_plugin._info[u"import_name"],
|
entry_name=self.entry_plugin._info[u"import_name"],
|
||||||
|
|
@ -898,7 +896,7 @@ class SatMessageProtocol(xmppim.MessageProtocol):
|
||||||
else:
|
else:
|
||||||
return self.host.memory.addToHistory(client, data)
|
return self.host.memory.addToHistory(client, data)
|
||||||
|
|
||||||
def bridgeSignal(self, dummy, client, data):
|
def bridgeSignal(self, __, client, data):
|
||||||
try:
|
try:
|
||||||
data["extra"]["received_timestamp"] = data["received_timestamp"]
|
data["extra"]["received_timestamp"] = data["received_timestamp"]
|
||||||
data["extra"]["delay_sender"] = data["delay_sender"]
|
data["extra"]["delay_sender"] = data["delay_sender"]
|
||||||
|
|
@ -942,7 +940,8 @@ class SatRosterProtocol(xmppim.RosterClientProtocol):
|
||||||
# if there is no presence subscription
|
# if there is no presence subscription
|
||||||
# may change in the future
|
# may change in the future
|
||||||
log.info(
|
log.info(
|
||||||
u"Removing contact {} from roster because there is no presence subscription".format(
|
u"Removing contact {} from roster because there is no presence "
|
||||||
|
u"subscription".format(
|
||||||
item.jid
|
item.jid
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -959,7 +958,8 @@ class SatRosterProtocol(xmppim.RosterClientProtocol):
|
||||||
log.debug(u"registering item: {}".format(item.entity.full()))
|
log.debug(u"registering item: {}".format(item.entity.full()))
|
||||||
if item.entity.resource:
|
if item.entity.resource:
|
||||||
log.warning(
|
log.warning(
|
||||||
u"Received a roster item with a resource, this is not common but not restricted by RFC 6121, this case may be not well tested."
|
u"Received a roster item with a resource, this is not common but not "
|
||||||
|
u"restricted by RFC 6121, this case may be not well tested."
|
||||||
)
|
)
|
||||||
if not item.subscriptionTo:
|
if not item.subscriptionTo:
|
||||||
if not item.subscriptionFrom:
|
if not item.subscriptionFrom:
|
||||||
|
|
@ -1044,8 +1044,8 @@ class SatRosterProtocol(xmppim.RosterClientProtocol):
|
||||||
del self._groups[group]
|
del self._groups[group]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
log.warning(
|
log.warning(
|
||||||
u"there is no cache for the group [%(group)s] of the removed roster item [%(jid)s]"
|
u"there is no cache for the group [{group}] of the removed roster "
|
||||||
% {"group": group, "jid": entity}
|
u"item [{jid_}]".format(group=group, jid=entity)
|
||||||
)
|
)
|
||||||
|
|
||||||
# then we send the bridge signal
|
# then we send the bridge signal
|
||||||
|
|
@ -1139,14 +1139,15 @@ class SatPresenceProtocol(xmppim.PresenceClientProtocol):
|
||||||
def availableReceived(self, entity, show=None, statuses=None, priority=0):
|
def availableReceived(self, entity, show=None, statuses=None, priority=0):
|
||||||
log.debug(
|
log.debug(
|
||||||
_(
|
_(
|
||||||
u"presence update for [%(entity)s] (available, show=%(show)s statuses=%(statuses)s priority=%(priority)d)"
|
u"presence update for [{entity}] (available, show={show} "
|
||||||
|
u"statuses={statuses} priority={priority})"
|
||||||
|
)
|
||||||
|
.format(
|
||||||
|
entity=entity,
|
||||||
|
show=show,
|
||||||
|
statuses=statuses,
|
||||||
|
priority=priority,
|
||||||
)
|
)
|
||||||
% {
|
|
||||||
"entity": entity,
|
|
||||||
C.PRESENCE_SHOW: show,
|
|
||||||
C.PRESENCE_STATUSES: statuses,
|
|
||||||
C.PRESENCE_PRIORITY: priority,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if not statuses:
|
if not statuses:
|
||||||
|
|
@ -1187,7 +1188,8 @@ class SatPresenceProtocol(xmppim.PresenceClientProtocol):
|
||||||
return
|
return
|
||||||
|
|
||||||
# now it's time to notify frontends
|
# now it's time to notify frontends
|
||||||
# if the entity is not known yet in this session or is already unavailable, there is no need to send an unavailable signal
|
# if the entity is not known yet in this session or is already unavailable,
|
||||||
|
# there is no need to send an unavailable signal
|
||||||
try:
|
try:
|
||||||
presence = self.host.memory.getEntityDatum(
|
presence = self.host.memory.getEntityDatum(
|
||||||
entity, "presence", self.parent.profile
|
entity, "presence", self.parent.profile
|
||||||
|
|
@ -1275,7 +1277,8 @@ class SatPresenceProtocol(xmppim.PresenceClientProtocol):
|
||||||
yield self.parent.roster.got_roster
|
yield self.parent.roster.got_roster
|
||||||
item = self.parent.roster.getItem(entity)
|
item = self.parent.roster.getItem(entity)
|
||||||
if item and item.subscriptionTo:
|
if item and item.subscriptionTo:
|
||||||
# We automatically accept subscription if we are already subscribed to contact presence
|
# We automatically accept subscription if we are already subscribed to
|
||||||
|
# contact presence
|
||||||
log.debug(_("sending automatic subscription acceptance"))
|
log.debug(_("sending automatic subscription acceptance"))
|
||||||
self.subscribed(entity)
|
self.subscribed(entity)
|
||||||
else:
|
else:
|
||||||
|
|
@ -1314,22 +1317,22 @@ class SatFallbackHandler(generic.FallbackHandler):
|
||||||
|
|
||||||
|
|
||||||
class SatVersionHandler(generic.VersionHandler):
|
class SatVersionHandler(generic.VersionHandler):
|
||||||
|
|
||||||
def getDiscoInfo(self, requestor, target, node):
|
def getDiscoInfo(self, requestor, target, node):
|
||||||
# XXX: We need to work around wokkel's behaviour (namespace not added if there is a
|
# XXX: We need to work around wokkel's behaviour (namespace not added if there
|
||||||
# node) as it cause issues with XEP-0115 & PEP (XEP-0163): there is a node when server
|
# is a node) as it cause issues with XEP-0115 & PEP (XEP-0163): there is a
|
||||||
# ask for disco info, and not when we generate the key, so the hash is used with different
|
# node when server ask for disco info, and not when we generate the key, so
|
||||||
# disco features, and when the server (seen on ejabberd) generate its own hash for security check
|
# the hash is used with different disco features, and when the server (seen
|
||||||
# it reject our features (resulting in e.g. no notification on PEP)
|
# on ejabberd) generate its own hash for security check it reject our
|
||||||
|
# features (resulting in e.g. no notification on PEP)
|
||||||
return generic.VersionHandler.getDiscoInfo(self, requestor, target, None)
|
return generic.VersionHandler.getDiscoInfo(self, requestor, target, None)
|
||||||
|
|
||||||
|
|
||||||
class SatIdentityHandler(XMPPHandler):
|
class SatIdentityHandler(XMPPHandler):
|
||||||
""" Manage disco Identity of SàT.
|
"""Manage disco Identity of SàT."""
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# TODO: dynamic identity update (see docstring). Note that a XMPP entity can have several identities
|
|
||||||
implements(iwokkel.IDisco)
|
implements(iwokkel.IDisco)
|
||||||
|
# TODO: dynamic identity update (see docstring). Note that a XMPP entity can have
|
||||||
|
# several identities
|
||||||
|
|
||||||
def getDiscoInfo(self, requestor, target, nodeIdentifier=""):
|
def getDiscoInfo(self, requestor, target, nodeIdentifier=""):
|
||||||
return self.parent.identities
|
return self.parent.identities
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,8 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
# XXX: thanks to Darrik L Mazey for his documentation (https://blog.darmasoft.net/2013/06/30/using-pure-python-otr.html)
|
# XXX: thanks to Darrik L Mazey for his documentation
|
||||||
|
# (https://blog.darmasoft.net/2013/06/30/using-pure-python-otr.html)
|
||||||
# this implentation is based on it
|
# this implentation is based on it
|
||||||
|
|
||||||
from sat.core.i18n import _, D_
|
from sat.core.i18n import _, D_
|
||||||
|
|
@ -52,10 +53,14 @@ NS_OTR = "otr_plugin"
|
||||||
PRIVATE_KEY = "PRIVATE KEY"
|
PRIVATE_KEY = "PRIVATE KEY"
|
||||||
OTR_MENU = D_(u"OTR")
|
OTR_MENU = D_(u"OTR")
|
||||||
AUTH_TXT = D_(
|
AUTH_TXT = D_(
|
||||||
u"To authenticate your correspondent, you need to give your below fingerprint *BY AN EXTERNAL CANAL* (i.e. not in this chat), and check that the one he gives you is the same as below. If there is a mismatch, there can be a spy between you!"
|
u"To authenticate your correspondent, you need to give your below fingerprint "
|
||||||
|
u"*BY AN EXTERNAL CANAL* (i.e. not in this chat), and check that the one he gives "
|
||||||
|
u"you is the same as below. If there is a mismatch, there can be a spy between you!"
|
||||||
)
|
)
|
||||||
DROP_TXT = D_(
|
DROP_TXT = D_(
|
||||||
u"You private key is used to encrypt messages for your correspondent, nobody except you must know it, if you are in doubt, you should drop it!\n\nAre you sure you want to drop your private key?"
|
u"You private key is used to encrypt messages for your correspondent, nobody except "
|
||||||
|
u"you must know it, if you are in doubt, you should drop it!\n\nAre you sure you "
|
||||||
|
u"want to drop your private key?"
|
||||||
)
|
)
|
||||||
# NO_LOG_AND = D_(u"/!\\Your history is not logged anymore, and") # FIXME: not used at the moment
|
# NO_LOG_AND = D_(u"/!\\Your history is not logged anymore, and") # FIXME: not used at the moment
|
||||||
NO_ADV_FEATURES = D_(u"Some of advanced features are disabled !")
|
NO_ADV_FEATURES = D_(u"Some of advanced features are disabled !")
|
||||||
|
|
@ -83,8 +88,8 @@ class Context(potr.context.Context):
|
||||||
"""Inject encrypted data in the stream
|
"""Inject encrypted data in the stream
|
||||||
|
|
||||||
if appdata is not None, we are sending a message in sendMessageDataTrigger
|
if appdata is not None, we are sending a message in sendMessageDataTrigger
|
||||||
stanza will be injected directly if appdata is None, else we just update the element
|
stanza will be injected directly if appdata is None,
|
||||||
and follow normal workflow
|
else we just update the element and follow normal workflow
|
||||||
@param msg_str(str): encrypted message body
|
@param msg_str(str): encrypted message body
|
||||||
@param appdata(None, dict): None for signal message,
|
@param appdata(None, dict): None for signal message,
|
||||||
message data when an encrypted message is going to be sent
|
message data when an encrypted message is going to be sent
|
||||||
|
|
@ -137,7 +142,8 @@ class Context(potr.context.Context):
|
||||||
).format(trusted=trusted_str, other_jid=self.peer.full())
|
).format(trusted=trusted_str, other_jid=self.peer.full())
|
||||||
else:
|
else:
|
||||||
feedback = D_(
|
feedback = D_(
|
||||||
u"{trusted} encrypted OTR conversation started with {other_jid}\n{extra_info}"
|
u"{trusted} encrypted OTR conversation started with {other_jid}\n"
|
||||||
|
u"{extra_info}"
|
||||||
).format(
|
).format(
|
||||||
trusted=trusted_str,
|
trusted=trusted_str,
|
||||||
other_jid=self.peer.full(),
|
other_jid=self.peer.full(),
|
||||||
|
|
@ -165,13 +171,18 @@ class Context(potr.context.Context):
|
||||||
super(Context, self).disconnect()
|
super(Context, self).disconnect()
|
||||||
|
|
||||||
def finish(self):
|
def finish(self):
|
||||||
"""Finish the session - avoid to send any message but the user still has to end the session himself."""
|
"""Finish the session
|
||||||
|
|
||||||
|
avoid to send any message but the user still has to end the session himself.
|
||||||
|
"""
|
||||||
if self.state == potr.context.STATE_ENCRYPTED:
|
if self.state == potr.context.STATE_ENCRYPTED:
|
||||||
self.processTLVs([potr.proto.DisconnectTLV()])
|
self.processTLVs([potr.proto.DisconnectTLV()])
|
||||||
|
|
||||||
|
|
||||||
class Account(potr.context.Account):
|
class Account(potr.context.Account):
|
||||||
# TODO: manage trusted keys: if a fingerprint is not used anymore, we have no way to remove it from database yet (same thing for a correspondent jid)
|
# TODO: manage trusted keys: if a fingerprint is not used anymore,
|
||||||
|
# we have no way to remove it from database yet (same thing for a
|
||||||
|
# correspondent jid)
|
||||||
# TODO: manage explicit message encryption
|
# TODO: manage explicit message encryption
|
||||||
|
|
||||||
def __init__(self, host, client):
|
def __init__(self, host, client):
|
||||||
|
|
@ -353,7 +364,8 @@ class OTR(object):
|
||||||
if not to_jid.resource:
|
if not to_jid.resource:
|
||||||
to_jid.resource = self.host.memory.getMainResource(
|
to_jid.resource = self.host.memory.getMainResource(
|
||||||
client, to_jid
|
client, to_jid
|
||||||
) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
) # FIXME: temporary and unsecure, must be changed when frontends
|
||||||
|
# are refactored
|
||||||
otrctx = client._otr_context_manager.getContextForUser(to_jid)
|
otrctx = client._otr_context_manager.getContextForUser(to_jid)
|
||||||
query = otrctx.sendMessage(0, "?OTRv?")
|
query = otrctx.sendMessage(0, "?OTRv?")
|
||||||
otrctx.inject(query)
|
otrctx.inject(query)
|
||||||
|
|
@ -378,7 +390,8 @@ class OTR(object):
|
||||||
if not to_jid.resource:
|
if not to_jid.resource:
|
||||||
to_jid.resource = self.host.memory.getMainResource(
|
to_jid.resource = self.host.memory.getMainResource(
|
||||||
client, to_jid
|
client, to_jid
|
||||||
) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
) # FIXME: temporary and unsecure, must be changed when frontends
|
||||||
|
# are refactored
|
||||||
otrctx = client._otr_context_manager.getContextForUser(to_jid)
|
otrctx = client._otr_context_manager.getContextForUser(to_jid)
|
||||||
otrctx.disconnect()
|
otrctx.disconnect()
|
||||||
return {}
|
return {}
|
||||||
|
|
@ -402,7 +415,8 @@ class OTR(object):
|
||||||
if not to_jid.resource:
|
if not to_jid.resource:
|
||||||
to_jid.resource = self.host.memory.getMainResource(
|
to_jid.resource = self.host.memory.getMainResource(
|
||||||
client, to_jid
|
client, to_jid
|
||||||
) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
) # FIXME: temporary and unsecure, must be changed when frontends
|
||||||
|
# are refactored
|
||||||
ctxMng = client._otr_context_manager
|
ctxMng = client._otr_context_manager
|
||||||
otrctx = ctxMng.getContextForUser(to_jid)
|
otrctx = ctxMng.getContextForUser(to_jid)
|
||||||
priv_key = ctxMng.account.privkey
|
priv_key = ctxMng.account.privkey
|
||||||
|
|
@ -414,7 +428,8 @@ class OTR(object):
|
||||||
dialog_opt={
|
dialog_opt={
|
||||||
C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_MESSAGE,
|
C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_MESSAGE,
|
||||||
C.XMLUI_DATA_MESS: _(
|
C.XMLUI_DATA_MESS: _(
|
||||||
u"You have no private key yet, start an OTR conversation to have one"
|
u"You have no private key yet, start an OTR conversation to "
|
||||||
|
u"have one"
|
||||||
),
|
),
|
||||||
C.XMLUI_DATA_LVL: C.XMLUI_DATA_LVL_WARNING,
|
C.XMLUI_DATA_LVL: C.XMLUI_DATA_LVL_WARNING,
|
||||||
},
|
},
|
||||||
|
|
@ -431,7 +446,8 @@ class OTR(object):
|
||||||
dialog_opt={
|
dialog_opt={
|
||||||
C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_MESSAGE,
|
C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_MESSAGE,
|
||||||
C.XMLUI_DATA_MESS: _(
|
C.XMLUI_DATA_MESS: _(
|
||||||
u"Your fingerprint is:\n{fingerprint}\n\nStart an OTR conversation to have your correspondent one."
|
u"Your fingerprint is:\n{fingerprint}\n\n"
|
||||||
|
u"Start an OTR conversation to have your correspondent one."
|
||||||
).format(fingerprint=priv_key),
|
).format(fingerprint=priv_key),
|
||||||
C.XMLUI_DATA_LVL: C.XMLUI_DATA_LVL_INFO,
|
C.XMLUI_DATA_LVL: C.XMLUI_DATA_LVL_INFO,
|
||||||
},
|
},
|
||||||
|
|
@ -501,7 +517,8 @@ class OTR(object):
|
||||||
if not to_jid.resource:
|
if not to_jid.resource:
|
||||||
to_jid.resource = self.host.memory.getMainResource(
|
to_jid.resource = self.host.memory.getMainResource(
|
||||||
client, to_jid
|
client, to_jid
|
||||||
) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
) # FIXME: temporary and unsecure, must be changed when frontends
|
||||||
|
# are refactored
|
||||||
except KeyError:
|
except KeyError:
|
||||||
log.error(_(u"jid key is not present !"))
|
log.error(_(u"jid key is not present !"))
|
||||||
return defer.fail(exceptions.DataError)
|
return defer.fail(exceptions.DataError)
|
||||||
|
|
@ -518,7 +535,8 @@ class OTR(object):
|
||||||
for context in ctxMng.contexts.values():
|
for context in ctxMng.contexts.values():
|
||||||
context.disconnect()
|
context.disconnect()
|
||||||
ctxMng.account.privkey = None
|
ctxMng.account.privkey = None
|
||||||
ctxMng.account.getPrivkey() # as account.privkey is None, getPrivkey will generate a new key, and save it
|
ctxMng.account.getPrivkey() # as account.privkey is None, getPrivkey
|
||||||
|
# will generate a new key, and save it
|
||||||
return {
|
return {
|
||||||
"xmlui": xml_tools.note(
|
"xmlui": xml_tools.note(
|
||||||
D_(u"Your private key has been dropped")
|
D_(u"Your private key has been dropped")
|
||||||
|
|
@ -550,14 +568,14 @@ class OTR(object):
|
||||||
encrypted = False
|
encrypted = False
|
||||||
if otrctx.state == potr.context.STATE_ENCRYPTED:
|
if otrctx.state == potr.context.STATE_ENCRYPTED:
|
||||||
log.warning(
|
log.warning(
|
||||||
u"Received unencrypted message in an encrypted context (from {jid})".format(
|
u"Received unencrypted message in an encrypted context (from {jid})"
|
||||||
jid=from_jid.full()
|
.format(jid=from_jid.full())
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
feedback = (
|
feedback = (
|
||||||
D_(
|
D_(
|
||||||
u"WARNING: received unencrypted data in a supposedly encrypted context"
|
u"WARNING: received unencrypted data in a supposedly encrypted "
|
||||||
|
u"context"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
client.feedback(from_jid, feedback)
|
client.feedback(from_jid, feedback)
|
||||||
|
|
@ -569,17 +587,21 @@ class OTR(object):
|
||||||
if encrypted:
|
if encrypted:
|
||||||
if res[0] != None:
|
if res[0] != None:
|
||||||
# decrypted messages handling.
|
# decrypted messages handling.
|
||||||
# receiveMessage() will return a tuple, the first part of which will be the decrypted message
|
# receiveMessage() will return a tuple,
|
||||||
|
# the first part of which will be the decrypted message
|
||||||
data["message"] = {
|
data["message"] = {
|
||||||
"": res[0].decode("utf-8")
|
"": res[0].decode("utf-8")
|
||||||
} # FIXME: Q&D fix for message refactoring, message is now a dict
|
} # FIXME: Q&D fix for message refactoring, message is now a dict
|
||||||
try:
|
try:
|
||||||
# we want to keep message in history, even if no store is requested in message hints
|
# we want to keep message in history, even if no store is
|
||||||
|
# requested in message hints
|
||||||
del data[u"history"]
|
del data[u"history"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
# TODO: add skip history as an option, but by default we don't skip it
|
# TODO: add skip history as an option, but by default we don't skip it
|
||||||
# data[u'history'] = C.HISTORY_SKIP # we send the decrypted message to frontends, but we don't want it in history
|
# data[u'history'] = C.HISTORY_SKIP # we send the decrypted message to
|
||||||
|
# frontends, but we don't want it in
|
||||||
|
# history
|
||||||
else:
|
else:
|
||||||
log.warning(
|
log.warning(
|
||||||
u"An encrypted message was expected, but got {}".format(
|
u"An encrypted message was expected, but got {}".format(
|
||||||
|
|
@ -604,8 +626,10 @@ class OTR(object):
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
return data
|
return data
|
||||||
if message.startswith(potr.proto.OTRTAG):
|
if message.startswith(potr.proto.OTRTAG):
|
||||||
# FIXME: it may be better to cancel the message and send it direclty to bridge
|
# FIXME: it may be better to cancel the message and send it direclty to
|
||||||
# this is used by Libervia, but this may send garbage message to other frontends
|
# bridge
|
||||||
|
# this is used by Libervia, but this may send garbage message to
|
||||||
|
# other frontends
|
||||||
# if they are used at the same time as Libervia.
|
# if they are used at the same time as Libervia.
|
||||||
# Hard to avoid with decryption on Libervia though.
|
# Hard to avoid with decryption on Libervia though.
|
||||||
data[u"history"] = C.HISTORY_SKIP
|
data[u"history"] = C.HISTORY_SKIP
|
||||||
|
|
@ -647,39 +671,38 @@ class OTR(object):
|
||||||
otrctx.sendMessage(0, unicode(body).encode("utf-8"), appdata=mess_data)
|
otrctx.sendMessage(0, unicode(body).encode("utf-8"), appdata=mess_data)
|
||||||
else:
|
else:
|
||||||
feedback = D_(
|
feedback = D_(
|
||||||
u"Your message was not sent because your correspondent closed the encrypted conversation on his/her side. "
|
u"Your message was not sent because your correspondent closed the "
|
||||||
|
u"encrypted conversation on his/her side. "
|
||||||
u"Either close your own side, or refresh the session."
|
u"Either close your own side, or refresh the session."
|
||||||
)
|
)
|
||||||
log.warning(_(u"Message discarded because closed encryption channel"))
|
log.warning(_(u"Message discarded because closed encryption channel"))
|
||||||
client.feedback(to_jid, feedback)
|
client.feedback(to_jid, feedback)
|
||||||
raise failure.Failure(exceptions.CancelError(u"Cancelled by OTR plugin"))
|
raise failure.Failure(exceptions.CancelError(u"Cancelled by OTR plugin"))
|
||||||
|
|
||||||
def sendMessageTrigger(
|
def sendMessageTrigger(self, client, mess_data, pre_xml_treatments,
|
||||||
self, client, mess_data, pre_xml_treatments, post_xml_treatments
|
post_xml_treatments):
|
||||||
):
|
|
||||||
if mess_data["type"] == "groupchat":
|
if mess_data["type"] == "groupchat":
|
||||||
return True
|
return True
|
||||||
if (
|
|
||||||
client.profile in self.skipped_profiles
|
if client.profile in self.skipped_profiles:
|
||||||
): # FIXME: should not be done on a per-profile basis
|
# FIXME: should not be done on a per-profile basis
|
||||||
return True
|
return True
|
||||||
|
|
||||||
to_jid = copy.copy(mess_data["to"])
|
to_jid = copy.copy(mess_data["to"])
|
||||||
if not to_jid.resource:
|
if not to_jid.resource:
|
||||||
to_jid.resource = self.host.memory.getMainResource(
|
to_jid.resource = self.host.memory.getMainResource(
|
||||||
client, to_jid
|
client, to_jid
|
||||||
) # FIXME: full jid may not be known
|
) # FIXME: full jid may not be known
|
||||||
|
|
||||||
otrctx = client._otr_context_manager.getContextForUser(to_jid)
|
otrctx = client._otr_context_manager.getContextForUser(to_jid)
|
||||||
|
|
||||||
if otrctx.state != potr.context.STATE_PLAINTEXT:
|
if otrctx.state != potr.context.STATE_PLAINTEXT:
|
||||||
self._p_hints.addHint(mess_data, self._p_hints.HINT_NO_COPY)
|
self._p_hints.addHint(mess_data, self._p_hints.HINT_NO_COPY)
|
||||||
self._p_hints.addHint(mess_data, self._p_hints.HINT_NO_PERMANENT_STORE)
|
self._p_hints.addHint(mess_data, self._p_hints.HINT_NO_PERMANENT_STORE)
|
||||||
mess_data[
|
mess_data["OTR"] = (otrctx) # this indicate that encryption is needed in
|
||||||
"OTR"
|
# sendMessageData trigger
|
||||||
] = (
|
if not mess_data["to"].resource:
|
||||||
otrctx
|
# if not resource was given, we force it here
|
||||||
) # this indicate that encryption is needed in sendMessageData trigger
|
|
||||||
if not mess_data[
|
|
||||||
"to"
|
|
||||||
].resource: # if not resource was given, we force it here
|
|
||||||
mess_data["to"] = to_jid
|
mess_data["to"] = to_jid
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
@ -691,7 +714,8 @@ class OTR(object):
|
||||||
try:
|
try:
|
||||||
entity.resource = self.host.memory.getMainResource(
|
entity.resource = self.host.memory.getMainResource(
|
||||||
client, entity
|
client, entity
|
||||||
) # FIXME: temporary and unsecure, must be changed when frontends are refactored
|
) # FIXME: temporary and unsecure, must be changed when frontends
|
||||||
|
# are refactored
|
||||||
except exceptions.UnknownEntityError:
|
except exceptions.UnknownEntityError:
|
||||||
return True # entity was not connected
|
return True # entity was not connected
|
||||||
if entity in client._otr_context_manager.contexts:
|
if entity in client._otr_context_manager.contexts:
|
||||||
|
|
|
||||||
|
|
@ -520,16 +520,8 @@ class XEP_0060(object):
|
||||||
raise exceptions.DataError(u"Can't find Item in MAM message element")
|
raise exceptions.DataError(u"Can't find Item in MAM message element")
|
||||||
return item_elt
|
return item_elt
|
||||||
|
|
||||||
def _getItems(
|
def _getItems(self, service="", node="", max_items=10, item_ids=None, sub_id=None,
|
||||||
self,
|
extra_dict=None, profile_key=C.PROF_KEY_NONE):
|
||||||
service="",
|
|
||||||
node="",
|
|
||||||
max_items=10,
|
|
||||||
item_ids=None,
|
|
||||||
sub_id=None,
|
|
||||||
extra_dict=None,
|
|
||||||
profile_key=C.PROF_KEY_NONE,
|
|
||||||
):
|
|
||||||
"""Get items from pubsub node
|
"""Get items from pubsub node
|
||||||
|
|
||||||
@param max_items(int): maximum number of item to get, C.NO_LIMIT for no limit
|
@param max_items(int): maximum number of item to get, C.NO_LIMIT for no limit
|
||||||
|
|
@ -551,17 +543,8 @@ class XEP_0060(object):
|
||||||
d.addCallback(self.serItemsData)
|
d.addCallback(self.serItemsData)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def getItems(
|
def getItems(self, client, service, node, max_items=None, item_ids=None, sub_id=None,
|
||||||
self,
|
rsm_request=None, extra=None):
|
||||||
client,
|
|
||||||
service,
|
|
||||||
node,
|
|
||||||
max_items=None,
|
|
||||||
item_ids=None,
|
|
||||||
sub_id=None,
|
|
||||||
rsm_request=None,
|
|
||||||
extra=None,
|
|
||||||
):
|
|
||||||
"""Retrieve pubsub items from a node.
|
"""Retrieve pubsub items from a node.
|
||||||
|
|
||||||
@param service (JID, None): pubsub service.
|
@param service (JID, None): pubsub service.
|
||||||
|
|
@ -678,28 +661,15 @@ class XEP_0060(object):
|
||||||
# d_dict[publisher] = self.getItems(service, node, max_items, None, sub_id, rsm, client.profile)
|
# d_dict[publisher] = self.getItems(service, node, max_items, None, sub_id, rsm, client.profile)
|
||||||
# defer.returnValue(d_dict)
|
# defer.returnValue(d_dict)
|
||||||
|
|
||||||
def getOptions(
|
def getOptions(self, service, nodeIdentifier, subscriber, subscriptionIdentifier=None,
|
||||||
self,
|
profile_key=C.PROF_KEY_NONE):
|
||||||
service,
|
|
||||||
nodeIdentifier,
|
|
||||||
subscriber,
|
|
||||||
subscriptionIdentifier=None,
|
|
||||||
profile_key=C.PROF_KEY_NONE,
|
|
||||||
):
|
|
||||||
client = self.host.getClient(profile_key)
|
client = self.host.getClient(profile_key)
|
||||||
return client.pubsub_client.getOptions(
|
return client.pubsub_client.getOptions(
|
||||||
service, nodeIdentifier, subscriber, subscriptionIdentifier
|
service, nodeIdentifier, subscriber, subscriptionIdentifier
|
||||||
)
|
)
|
||||||
|
|
||||||
def setOptions(
|
def setOptions(self, service, nodeIdentifier, subscriber, options,
|
||||||
self,
|
subscriptionIdentifier=None, profile_key=C.PROF_KEY_NONE):
|
||||||
service,
|
|
||||||
nodeIdentifier,
|
|
||||||
subscriber,
|
|
||||||
options,
|
|
||||||
subscriptionIdentifier=None,
|
|
||||||
profile_key=C.PROF_KEY_NONE,
|
|
||||||
):
|
|
||||||
client = self.host.getClient(profile_key)
|
client = self.host.getClient(profile_key)
|
||||||
return client.pubsub_client.setOptions(
|
return client.pubsub_client.setOptions(
|
||||||
service, nodeIdentifier, subscriber, options, subscriptionIdentifier
|
service, nodeIdentifier, subscriber, options, subscriptionIdentifier
|
||||||
|
|
|
||||||
|
|
@ -72,11 +72,14 @@ class XEP_0163(object):
|
||||||
def addPEPEvent(self, event_type, node, in_callback, out_callback=None, notify=True):
|
def addPEPEvent(self, event_type, node, in_callback, out_callback=None, notify=True):
|
||||||
"""Add a Personal Eventing Protocol event manager
|
"""Add a Personal Eventing Protocol event manager
|
||||||
|
|
||||||
@param event_type(unicode): type of the event (always uppercase), can be MOOD, TUNE, etc
|
@param event_type(unicode): type of the event (always uppercase),
|
||||||
@param node(unicode): namespace of the node (e.g. http://jabber.org/protocol/mood for User Mood)
|
can be MOOD, TUNE, etc
|
||||||
|
@param node(unicode): namespace of the node (e.g. http://jabber.org/protocol/mood
|
||||||
|
for User Mood)
|
||||||
@param in_callback(callable): method to call when this event occur
|
@param in_callback(callable): method to call when this event occur
|
||||||
the callable will be called with (itemsEvent, profile) as arguments
|
the callable will be called with (itemsEvent, profile) as arguments
|
||||||
@param out_callback(callable,None): method to call when we want to publish this event (must return a deferred)
|
@param out_callback(callable,None): method to call when we want to publish this
|
||||||
|
event (must return a deferred)
|
||||||
the callable will be called when sendPEPEvent is called
|
the callable will be called when sendPEPEvent is called
|
||||||
@param notify(bool): add autosubscribe (+notify) if True
|
@param notify(bool): add autosubscribe (+notify) if True
|
||||||
"""
|
"""
|
||||||
|
|
@ -116,7 +119,8 @@ class XEP_0163(object):
|
||||||
def PEPSend(self, event_type, data, profile_key=C.PROF_KEY_NONE):
|
def PEPSend(self, event_type, data, profile_key=C.PROF_KEY_NONE):
|
||||||
"""Send personal event after checking the data is alright
|
"""Send personal event after checking the data is alright
|
||||||
|
|
||||||
@param event_type: type of event (eg: MOOD, TUNE), must be in self.pep_out_cb.keys()
|
@param event_type: type of event (eg: MOOD, TUNE),
|
||||||
|
must be in self.pep_out_cb.keys()
|
||||||
@param data: dict of {string:string} of event_type dependant data
|
@param data: dict of {string:string} of event_type dependant data
|
||||||
@param profile_key: profile who send the event
|
@param profile_key: profile who send the event
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue