Mercurial > libervia-backend
view sat/bridge/dbus_bridge.py @ 3732:0fac164ff2d8
tools (xml_tools): fix `widget_args` modification in `_dataFormField2XMLUIData`:
in `textbox` widget, the first arg was modified with all values, lettings other ones
unchanger, resulting in invalid number of arguments. This has been fixed by replacing all
args with the new value.
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 31 Jan 2022 18:35:52 +0100 |
parents | 60d3861e5996 |
children | 524856bd7b19 |
line wrap: on
line source
#!/usr/bin/env python3 # Libervia communication bridge # Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org) # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # 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/>. from types import MethodType from functools import partialmethod from twisted.internet import defer, reactor from sat.core.i18n import _ from sat.core.log import getLogger from sat.core.exceptions import BridgeInitError from sat.tools import config from txdbus import client, objects, error from txdbus.interface import DBusInterface, Method, Signal log = getLogger(__name__) # Interface prefix const_INT_PREFIX = config.getConfig( config.parseMainConf(), "", "bridge_dbus_int_prefix", "org.libervia.Libervia") const_ERROR_PREFIX = const_INT_PREFIX + ".error" const_OBJ_PATH = "/org/libervia/Libervia/bridge" const_CORE_SUFFIX = ".core" const_PLUGIN_SUFFIX = ".plugin" class ParseError(Exception): pass class DBusException(Exception): pass class MethodNotRegistered(DBusException): dbusErrorName = const_ERROR_PREFIX + ".MethodNotRegistered" class GenericException(DBusException): def __init__(self, twisted_error): """ @param twisted_error (Failure): instance of twisted Failure error message is used to store a repr of message and condition in a tuple, so it can be evaluated by the frontend bridge. """ try: # twisted_error.value is a class class_ = twisted_error.value().__class__ except TypeError: # twisted_error.value is an instance class_ = twisted_error.value.__class__ data = twisted_error.getErrorMessage() try: data = (data, twisted_error.value.condition) except AttributeError: data = (data,) else: data = (str(twisted_error),) self.dbusErrorName = ".".join( (const_ERROR_PREFIX, class_.__module__, class_.__name__) ) super(GenericException, self).__init__(repr(data)) @classmethod def create_and_raise(cls, exc): raise cls(exc) class DBusObject(objects.DBusObject): core_iface = DBusInterface( const_INT_PREFIX + const_CORE_SUFFIX, Method('actionsGet', arguments='s', returns='a(a{ss}si)'), Method('addContact', arguments='ss', returns=''), Method('asyncDeleteProfile', arguments='s', returns=''), Method('asyncGetParamA', arguments='sssis', returns='s'), Method('asyncGetParamsValuesFromCategory', arguments='sisss', returns='a{ss}'), Method('connect', arguments='ssa{ss}', returns='b'), Method('contactGet', arguments='ss', returns='(a{ss}as)'), Method('delContact', arguments='ss', returns=''), Method('devicesInfosGet', arguments='ss', returns='s'), Method('discoFindByFeatures', arguments='asa(ss)bbbbbs', returns='(a{sa(sss)}a{sa(sss)}a{sa(sss)})'), Method('discoInfos', arguments='ssbs', returns='(asa(sss)a{sa(a{ss}as)})'), Method('discoItems', arguments='ssbs', returns='a(sss)'), Method('disconnect', arguments='s', returns=''), Method('encryptionNamespaceGet', arguments='s', returns='s'), Method('encryptionPluginsGet', arguments='', returns='s'), Method('encryptionTrustUIGet', arguments='sss', returns='s'), Method('getConfig', arguments='ss', returns='s'), Method('getContacts', arguments='s', returns='a(sa{ss}as)'), Method('getContactsFromGroup', arguments='ss', returns='as'), Method('getEntitiesData', arguments='asass', returns='a{sa{ss}}'), Method('getEntityData', arguments='sass', returns='a{ss}'), Method('getFeatures', arguments='s', returns='a{sa{ss}}'), Method('getMainResource', arguments='ss', returns='s'), Method('getParamA', arguments='ssss', returns='s'), Method('getParamsCategories', arguments='', returns='as'), Method('getParamsUI', arguments='isss', returns='s'), Method('getPresenceStatuses', arguments='s', returns='a{sa{s(sia{ss})}}'), Method('getReady', arguments='', returns=''), Method('getVersion', arguments='', returns='s'), Method('getWaitingSub', arguments='s', returns='a{ss}'), Method('historyGet', arguments='ssiba{ss}s', returns='a(sdssa{ss}a{ss}ss)'), Method('imageCheck', arguments='s', returns='s'), Method('imageConvert', arguments='ssss', returns='s'), Method('imageGeneratePreview', arguments='ss', returns='s'), Method('imageResize', arguments='sii', returns='s'), Method('isConnected', arguments='s', returns='b'), Method('launchAction', arguments='sa{ss}s', returns='a{ss}'), Method('loadParamsTemplate', arguments='s', returns='b'), Method('menuHelpGet', arguments='ss', returns='s'), Method('menuLaunch', arguments='sasa{ss}is', returns='a{ss}'), Method('menusGet', arguments='si', returns='a(ssasasa{ss})'), Method('messageEncryptionGet', arguments='ss', returns='s'), Method('messageEncryptionStart', arguments='ssbs', returns=''), Method('messageEncryptionStop', arguments='ss', returns=''), Method('messageSend', arguments='sa{ss}a{ss}sss', returns=''), Method('namespacesGet', arguments='', returns='a{ss}'), Method('paramsRegisterApp', arguments='sis', returns=''), Method('privateDataDelete', arguments='sss', returns=''), Method('privateDataGet', arguments='sss', returns='s'), Method('privateDataSet', arguments='ssss', returns=''), Method('profileCreate', arguments='sss', returns=''), Method('profileIsSessionStarted', arguments='s', returns='b'), Method('profileNameGet', arguments='s', returns='s'), Method('profileSetDefault', arguments='s', returns=''), Method('profileStartSession', arguments='ss', returns='b'), Method('profilesListGet', arguments='bb', returns='as'), Method('progressGet', arguments='ss', returns='a{ss}'), Method('progressGetAll', arguments='s', returns='a{sa{sa{ss}}}'), Method('progressGetAllMetadata', arguments='s', returns='a{sa{sa{ss}}}'), Method('rosterResync', arguments='s', returns=''), Method('saveParamsTemplate', arguments='s', returns='b'), Method('sessionInfosGet', arguments='s', returns='a{ss}'), Method('setParam', arguments='sssis', returns=''), Method('setPresence', arguments='ssa{ss}s', returns=''), Method('subscription', arguments='sss', returns=''), Method('updateContact', arguments='ssass', returns=''), Signal('_debug', 'sa{ss}s'), Signal('actionNew', 'a{ss}sis'), Signal('connected', 'ss'), Signal('contactDeleted', 'ss'), Signal('disconnected', 's'), Signal('entityDataUpdated', 'ssss'), Signal('messageEncryptionStarted', 'sss'), Signal('messageEncryptionStopped', 'sa{ss}s'), Signal('messageNew', 'sdssa{ss}a{ss}sss'), Signal('newContact', 'sa{ss}ass'), Signal('paramUpdate', 'ssss'), Signal('presenceUpdate', 'ssia{ss}s'), Signal('progressError', 'sss'), Signal('progressFinished', 'sa{ss}s'), Signal('progressStarted', 'sa{ss}s'), Signal('subscribe', 'sss'), ) plugin_iface = DBusInterface( const_INT_PREFIX + const_PLUGIN_SUFFIX ) dbusInterfaces = [core_iface, plugin_iface] def __init__(self, path): super().__init__(path) log.debug("Init DBusObject...") self.cb = {} def register_method(self, name, cb): self.cb[name] = cb def _callback(self, name, *args, **kwargs): """Call the callback if it exists, raise an exception else""" try: cb = self.cb[name] except KeyError: raise MethodNotRegistered else: d = defer.maybeDeferred(cb, *args, **kwargs) d.addErrback(GenericException.create_and_raise) return d def dbus_actionsGet(self, profile_key="@DEFAULT@"): return self._callback("actionsGet", profile_key) def dbus_addContact(self, entity_jid, profile_key="@DEFAULT@"): return self._callback("addContact", entity_jid, profile_key) def dbus_asyncDeleteProfile(self, profile): return self._callback("asyncDeleteProfile", profile) def dbus_asyncGetParamA(self, name, category, attribute="value", security_limit=-1, profile_key="@DEFAULT@"): return self._callback("asyncGetParamA", name, category, attribute, security_limit, profile_key) def dbus_asyncGetParamsValuesFromCategory(self, category, security_limit=-1, app="", extra="", profile_key="@DEFAULT@"): return self._callback("asyncGetParamsValuesFromCategory", category, security_limit, app, extra, profile_key) def dbus_connect(self, profile_key="@DEFAULT@", password='', options={}): return self._callback("connect", profile_key, password, options) def dbus_contactGet(self, arg_0, profile_key="@DEFAULT@"): return self._callback("contactGet", arg_0, profile_key) def dbus_delContact(self, entity_jid, profile_key="@DEFAULT@"): return self._callback("delContact", entity_jid, profile_key) def dbus_devicesInfosGet(self, bare_jid, profile_key): return self._callback("devicesInfosGet", bare_jid, profile_key) def dbus_discoFindByFeatures(self, namespaces, identities, bare_jid=False, service=True, roster=True, own_jid=True, local_device=False, profile_key="@DEFAULT@"): return self._callback("discoFindByFeatures", namespaces, identities, bare_jid, service, roster, own_jid, local_device, profile_key) def dbus_discoInfos(self, entity_jid, node=u'', use_cache=True, profile_key="@DEFAULT@"): return self._callback("discoInfos", entity_jid, node, use_cache, profile_key) def dbus_discoItems(self, entity_jid, node=u'', use_cache=True, profile_key="@DEFAULT@"): return self._callback("discoItems", entity_jid, node, use_cache, profile_key) def dbus_disconnect(self, profile_key="@DEFAULT@"): return self._callback("disconnect", profile_key) def dbus_encryptionNamespaceGet(self, arg_0): return self._callback("encryptionNamespaceGet", arg_0) def dbus_encryptionPluginsGet(self, ): return self._callback("encryptionPluginsGet", ) def dbus_encryptionTrustUIGet(self, to_jid, namespace, profile_key): return self._callback("encryptionTrustUIGet", to_jid, namespace, profile_key) def dbus_getConfig(self, section, name): return self._callback("getConfig", section, name) def dbus_getContacts(self, profile_key="@DEFAULT@"): return self._callback("getContacts", profile_key) def dbus_getContactsFromGroup(self, group, profile_key="@DEFAULT@"): return self._callback("getContactsFromGroup", group, profile_key) def dbus_getEntitiesData(self, jids, keys, profile): return self._callback("getEntitiesData", jids, keys, profile) def dbus_getEntityData(self, jid, keys, profile): return self._callback("getEntityData", jid, keys, profile) def dbus_getFeatures(self, profile_key): return self._callback("getFeatures", profile_key) def dbus_getMainResource(self, contact_jid, profile_key="@DEFAULT@"): return self._callback("getMainResource", contact_jid, profile_key) def dbus_getParamA(self, name, category, attribute="value", profile_key="@DEFAULT@"): return self._callback("getParamA", name, category, attribute, profile_key) def dbus_getParamsCategories(self, ): return self._callback("getParamsCategories", ) def dbus_getParamsUI(self, security_limit=-1, app='', extra='', profile_key="@DEFAULT@"): return self._callback("getParamsUI", security_limit, app, extra, profile_key) def dbus_getPresenceStatuses(self, profile_key="@DEFAULT@"): return self._callback("getPresenceStatuses", profile_key) def dbus_getReady(self, ): return self._callback("getReady", ) def dbus_getVersion(self, ): return self._callback("getVersion", ) def dbus_getWaitingSub(self, profile_key="@DEFAULT@"): return self._callback("getWaitingSub", profile_key) def dbus_historyGet(self, from_jid, to_jid, limit, between=True, filters='', profile="@NONE@"): return self._callback("historyGet", from_jid, to_jid, limit, between, filters, profile) def dbus_imageCheck(self, arg_0): return self._callback("imageCheck", arg_0) def dbus_imageConvert(self, source, dest, arg_2, extra): return self._callback("imageConvert", source, dest, arg_2, extra) def dbus_imageGeneratePreview(self, image_path, profile_key): return self._callback("imageGeneratePreview", image_path, profile_key) def dbus_imageResize(self, image_path, width, height): return self._callback("imageResize", image_path, width, height) def dbus_isConnected(self, profile_key="@DEFAULT@"): return self._callback("isConnected", profile_key) def dbus_launchAction(self, callback_id, data, profile_key="@DEFAULT@"): return self._callback("launchAction", callback_id, data, profile_key) def dbus_loadParamsTemplate(self, filename): return self._callback("loadParamsTemplate", filename) def dbus_menuHelpGet(self, menu_id, language): return self._callback("menuHelpGet", menu_id, language) def dbus_menuLaunch(self, menu_type, path, data, security_limit, profile_key): return self._callback("menuLaunch", menu_type, path, data, security_limit, profile_key) def dbus_menusGet(self, language, security_limit): return self._callback("menusGet", language, security_limit) def dbus_messageEncryptionGet(self, to_jid, profile_key): return self._callback("messageEncryptionGet", to_jid, profile_key) def dbus_messageEncryptionStart(self, to_jid, namespace='', replace=False, profile_key="@NONE@"): return self._callback("messageEncryptionStart", to_jid, namespace, replace, profile_key) def dbus_messageEncryptionStop(self, to_jid, profile_key): return self._callback("messageEncryptionStop", to_jid, profile_key) def dbus_messageSend(self, to_jid, message, subject={}, mess_type="auto", extra={}, profile_key="@NONE@"): return self._callback("messageSend", to_jid, message, subject, mess_type, extra, profile_key) def dbus_namespacesGet(self, ): return self._callback("namespacesGet", ) def dbus_paramsRegisterApp(self, xml, security_limit=-1, app=''): return self._callback("paramsRegisterApp", xml, security_limit, app) def dbus_privateDataDelete(self, namespace, key, arg_2): return self._callback("privateDataDelete", namespace, key, arg_2) def dbus_privateDataGet(self, namespace, key, profile_key): return self._callback("privateDataGet", namespace, key, profile_key) def dbus_privateDataSet(self, namespace, key, data, profile_key): return self._callback("privateDataSet", namespace, key, data, profile_key) def dbus_profileCreate(self, profile, password='', component=''): return self._callback("profileCreate", profile, password, component) def dbus_profileIsSessionStarted(self, profile_key="@DEFAULT@"): return self._callback("profileIsSessionStarted", profile_key) def dbus_profileNameGet(self, profile_key="@DEFAULT@"): return self._callback("profileNameGet", profile_key) def dbus_profileSetDefault(self, profile): return self._callback("profileSetDefault", profile) def dbus_profileStartSession(self, password='', profile_key="@DEFAULT@"): return self._callback("profileStartSession", password, profile_key) def dbus_profilesListGet(self, clients=True, components=False): return self._callback("profilesListGet", clients, components) def dbus_progressGet(self, id, profile): return self._callback("progressGet", id, profile) def dbus_progressGetAll(self, profile): return self._callback("progressGetAll", profile) def dbus_progressGetAllMetadata(self, profile): return self._callback("progressGetAllMetadata", profile) def dbus_rosterResync(self, profile_key="@DEFAULT@"): return self._callback("rosterResync", profile_key) def dbus_saveParamsTemplate(self, filename): return self._callback("saveParamsTemplate", filename) def dbus_sessionInfosGet(self, profile_key): return self._callback("sessionInfosGet", profile_key) def dbus_setParam(self, name, value, category, security_limit=-1, profile_key="@DEFAULT@"): return self._callback("setParam", name, value, category, security_limit, profile_key) def dbus_setPresence(self, to_jid='', show='', statuses={}, profile_key="@DEFAULT@"): return self._callback("setPresence", to_jid, show, statuses, profile_key) def dbus_subscription(self, sub_type, entity, profile_key="@DEFAULT@"): return self._callback("subscription", sub_type, entity, profile_key) def dbus_updateContact(self, entity_jid, name, groups, profile_key="@DEFAULT@"): return self._callback("updateContact", entity_jid, name, groups, profile_key) class Bridge: def __init__(self): log.info("Init DBus...") self._obj = DBusObject(const_OBJ_PATH) async def postInit(self): try: conn = await client.connect(reactor) except error.DBusException as e: if e.errName == "org.freedesktop.DBus.Error.NotSupported": log.error( _( "D-Bus is not launched, please see README to see instructions on " "how to launch it" ) ) raise BridgeInitError(str(e)) conn.exportObject(self._obj) await conn.requestBusName(const_INT_PREFIX) def _debug(self, action, params, profile): self._obj.emitSignal("_debug", action, params, profile) def actionNew(self, action_data, id, security_limit, profile): self._obj.emitSignal("actionNew", action_data, id, security_limit, profile) def connected(self, jid_s, profile): self._obj.emitSignal("connected", jid_s, profile) def contactDeleted(self, entity_jid, profile): self._obj.emitSignal("contactDeleted", entity_jid, profile) def disconnected(self, profile): self._obj.emitSignal("disconnected", profile) def entityDataUpdated(self, jid, name, value, profile): self._obj.emitSignal("entityDataUpdated", jid, name, value, profile) def messageEncryptionStarted(self, to_jid, encryption_data, profile_key): self._obj.emitSignal("messageEncryptionStarted", to_jid, encryption_data, profile_key) def messageEncryptionStopped(self, to_jid, encryption_data, profile_key): self._obj.emitSignal("messageEncryptionStopped", to_jid, encryption_data, profile_key) def messageNew(self, uid, timestamp, from_jid, to_jid, message, subject, mess_type, extra, profile): self._obj.emitSignal("messageNew", uid, timestamp, from_jid, to_jid, message, subject, mess_type, extra, profile) def newContact(self, contact_jid, attributes, groups, profile): self._obj.emitSignal("newContact", contact_jid, attributes, groups, profile) def paramUpdate(self, name, value, category, profile): self._obj.emitSignal("paramUpdate", name, value, category, profile) def presenceUpdate(self, entity_jid, show, priority, statuses, profile): self._obj.emitSignal("presenceUpdate", entity_jid, show, priority, statuses, profile) def progressError(self, id, error, profile): self._obj.emitSignal("progressError", id, error, profile) def progressFinished(self, id, metadata, profile): self._obj.emitSignal("progressFinished", id, metadata, profile) def progressStarted(self, id, metadata, profile): self._obj.emitSignal("progressStarted", id, metadata, profile) def subscribe(self, sub_type, entity_jid, profile): self._obj.emitSignal("subscribe", sub_type, entity_jid, profile) def register_method(self, name, callback): log.debug(f"registering DBus bridge method [{name}]") self._obj.register_method(name, callback) def emitSignal(self, name, *args): self._obj.emitSignal(name, *args) def addMethod( self, name, int_suffix, in_sign, out_sign, method, async_=False, doc={} ): """Dynamically add a method to D-Bus Bridge""" # FIXME: doc parameter is kept only temporary, the time to remove it from calls log.debug(f"Adding method {name!r} to D-Bus bridge") self._obj.plugin_iface.addMethod( Method(name, arguments=in_sign, returns=out_sign) ) # we have to create a method here instead of using partialmethod, because txdbus # uses __func__ which doesn't work with partialmethod def caller(self_, *args, **kwargs): return self_._callback(name, *args, **kwargs) setattr(self._obj, f"dbus_{name}", MethodType(caller, self._obj)) self.register_method(name, method) def addSignal(self, name, int_suffix, signature, doc={}): """Dynamically add a signal to D-Bus Bridge""" log.debug(f"Adding signal {name!r} to D-Bus bridge") self._obj.plugin_iface.addSignal(Signal(name, signature)) setattr(Bridge, name, partialmethod(Bridge.emitSignal, name))