This method is used to retrieve last messages and their timestamps. It's useful on frontend startup to display data on last discussions.
1357 lines
45 KiB
Python
1357 lines
45 KiB
Python
#!/usr/bin/env python3
|
|
|
|
# Libervia D-Bus communication bridge
|
|
# Copyright (C) 2009-2026 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/>.
|
|
|
|
import asyncio
|
|
from functools import partial
|
|
|
|
from twisted.internet import reactor
|
|
from txdbus import client
|
|
|
|
from .bridge_frontend import BridgeException
|
|
from libervia.backend.core.exceptions import BridgeExceptionNoService, BridgeInitError
|
|
from libervia.backend.core.i18n import _
|
|
from libervia.backend.core.log import getLogger
|
|
from libervia.backend.tools import config
|
|
|
|
log = getLogger(__name__)
|
|
|
|
|
|
# Interface prefix
|
|
conf = config.parse_main_conf()
|
|
bridge_conf = conf.get_bridge_conf(config.DBusBridgeSettings)
|
|
const_INT_PREFIX = bridge_conf.int_prefix
|
|
const_ERROR_PREFIX = const_INT_PREFIX + ".error"
|
|
const_OBJ_PATH = "/org/libervia/Libervia/bridge"
|
|
const_CORE_SUFFIX = ".core"
|
|
const_PLUGIN_SUFFIX = ".plugin"
|
|
const_TIMEOUT = 120
|
|
|
|
|
|
def dbus_to_bridge_exception(failure):
|
|
"""Convert a txdbus Failure to a BridgeException.
|
|
|
|
@param failure (Failure or RemoteError)
|
|
@return: BridgeException
|
|
"""
|
|
import ast
|
|
|
|
from twisted.python.failure import Failure
|
|
|
|
if isinstance(failure, Failure):
|
|
err = failure.value
|
|
message = failure.getErrorMessage()
|
|
else:
|
|
err = failure
|
|
message = str(failure)
|
|
|
|
full_name = getattr(err, "dbusErrorName", None) or getattr(err, "errName", None)
|
|
if full_name is None:
|
|
full_name = ".".join(
|
|
(const_ERROR_PREFIX, err.__class__.__module__, err.__class__.__name__)
|
|
)
|
|
|
|
if full_name and full_name.startswith(const_ERROR_PREFIX):
|
|
name = full_name[len(const_ERROR_PREFIX) + 1 :]
|
|
else:
|
|
name = full_name
|
|
|
|
# The backend GenericException serializes (message, condition) as a tuple string.
|
|
# Try to unpack it or fall back to using the raw message with no condition.
|
|
condition = ""
|
|
try:
|
|
data = ast.literal_eval(message)
|
|
if isinstance(data, tuple) and len(data) >= 2:
|
|
message = data[0]
|
|
condition = data[1]
|
|
except (ValueError, SyntaxError):
|
|
pass
|
|
|
|
return BridgeException(name, message, condition)
|
|
|
|
|
|
class SignalsHandler:
|
|
|
|
def __init__(self, db_object, iface_name):
|
|
self.db_object = db_object
|
|
self._iface_names = {
|
|
"core": const_INT_PREFIX + const_CORE_SUFFIX,
|
|
"plugin": iface_name,
|
|
}
|
|
|
|
def __getattr__(self, name):
|
|
if name.startswith("remote_"):
|
|
log.debug("calling an unregistered signal: {name}".format(name=name[7:]))
|
|
return lambda *args, **kwargs: None
|
|
else:
|
|
raise AttributeError(name)
|
|
|
|
def register_signal(self, name, handler, iface="core"):
|
|
log.debug("registering signal {name}".format(name=name))
|
|
method_name = "remote_" + name
|
|
try:
|
|
self.__getattribute__(method_name)
|
|
except AttributeError:
|
|
pass
|
|
else:
|
|
from libervia.backend.core import exceptions
|
|
|
|
raise exceptions.InternalError(
|
|
"{name} signal handler has been registered twice".format(name=method_name)
|
|
)
|
|
|
|
def on_signal(*args):
|
|
handler(*args)
|
|
|
|
d = self.db_object.notifyOnSignal(
|
|
name, on_signal, interface=self._iface_names[iface]
|
|
)
|
|
d.addErrback(
|
|
lambda failure_: log.error(
|
|
f"Failed to register signal {name}: {failure_.getErrorMessage()}"
|
|
)
|
|
)
|
|
|
|
setattr(self, method_name, on_signal)
|
|
|
|
|
|
class bridge:
|
|
|
|
def __init__(self):
|
|
self.conn = None
|
|
self.db_object = None
|
|
self.db_core_iface = None
|
|
self.db_plugin_iface = None
|
|
self.signals_handler = None
|
|
|
|
def __getattr__(self, name):
|
|
return partial(self.call, name)
|
|
|
|
@staticmethod
|
|
def _unwrap_txdbus_result(d, return_sig):
|
|
"""Unwrap txdbus result."""
|
|
# txdbus seems to wrongly wrap result in a list, this is a workaround.
|
|
# FIXME: check txdbus behaviour, and report upstream if it's confirmed to be a
|
|
# bug.
|
|
if return_sig and return_sig.startswith("("):
|
|
d.addCallback(
|
|
lambda result: (
|
|
result[0] if isinstance(result, list) and len(result) == 1 else result
|
|
)
|
|
)
|
|
return d
|
|
|
|
def call(self, name: str, *args, **kwargs):
|
|
"""Call a remote method.
|
|
|
|
@param name: name of the bridge method
|
|
@param args: method arguments
|
|
@param kwargs: keyword arguments (callback, errback, and methods arguments)
|
|
"""
|
|
# Determine which interface to use by checking where the method is defined
|
|
for iface in self.db_object.interfaces:
|
|
if name in iface.methods:
|
|
interface_name = iface.name
|
|
return_sig = iface.methods[name].sigOut
|
|
break
|
|
else:
|
|
# Default to core interface if method not found (will raise AttributeError)
|
|
interface_name = const_INT_PREFIX + const_CORE_SUFFIX
|
|
return_sig = None
|
|
|
|
# Call the remote method
|
|
d = self.db_object.callRemote(
|
|
name,
|
|
*args,
|
|
interface=interface_name,
|
|
timeout=const_TIMEOUT,
|
|
)
|
|
|
|
self._unwrap_txdbus_result(d, return_sig)
|
|
|
|
return d
|
|
|
|
async def bridge_connect(self) -> None:
|
|
"""Connect to the D-Bus bridge."""
|
|
try:
|
|
self.conn = await client.connect(reactor)
|
|
self.db_object = await self.conn.getRemoteObject(
|
|
const_INT_PREFIX, const_OBJ_PATH
|
|
)
|
|
# Find the core and plugin interfaces
|
|
for iface in self.db_object.interfaces:
|
|
if iface.name == const_INT_PREFIX + const_CORE_SUFFIX:
|
|
self.db_core_iface = iface
|
|
elif iface.name == const_INT_PREFIX + const_PLUGIN_SUFFIX:
|
|
self.db_plugin_iface = iface
|
|
|
|
if self.db_core_iface is None:
|
|
raise BridgeInitError(
|
|
f"Core interface {const_INT_PREFIX + const_CORE_SUFFIX} not found"
|
|
)
|
|
if self.db_plugin_iface is None:
|
|
raise BridgeInitError(
|
|
f"Plugin interface {const_INT_PREFIX + const_PLUGIN_SUFFIX} not found"
|
|
)
|
|
|
|
# Initialize signals handler
|
|
self.signals_handler = SignalsHandler(
|
|
self.db_object, const_INT_PREFIX + const_PLUGIN_SUFFIX
|
|
)
|
|
except BridgeInitError as e:
|
|
raise e
|
|
except Exception as e:
|
|
msg = str(e)
|
|
if (
|
|
"org.freedesktop.DBus.Error.ServiceUnknown" in msg
|
|
or "org.freedesktop.DBus.Error.Spawn.ExecFailed" in msg
|
|
):
|
|
raise BridgeExceptionNoService from e
|
|
elif "org.freedesktop.DBus.Error.NotSupported" in msg:
|
|
log.error(
|
|
_(
|
|
"D-Bus is not launched, please see README to see instructions on"
|
|
" how to launch it"
|
|
)
|
|
)
|
|
raise BridgeInitError from e
|
|
|
|
def register_signal(self, functionName, handler, iface="core"):
|
|
if self.signals_handler is None:
|
|
log.error("Bridge not initialized")
|
|
return
|
|
self.signals_handler.register_signal(functionName, handler, iface)
|
|
|
|
def action_launch(self, callback_id, data, profile_key="@DEFAULT@"):
|
|
return self.call("action_launch", callback_id, data, profile_key)
|
|
|
|
def actions_get(self, profile_key="@DEFAULT@"):
|
|
return self.call("actions_get", profile_key)
|
|
|
|
def config_get(self, section, name):
|
|
return self.call("config_get", section, name)
|
|
|
|
def connect(self, profile_key="@DEFAULT@", password="", options={}):
|
|
return self.call("connect", profile_key, password, options)
|
|
|
|
def contact_add(self, entity_jid, profile_key="@DEFAULT@"):
|
|
return self.call("contact_add", entity_jid, profile_key)
|
|
|
|
def contact_del(self, entity_jid, profile_key="@DEFAULT@"):
|
|
return self.call("contact_del", entity_jid, profile_key)
|
|
|
|
def contact_get(self, arg_0, profile_key="@DEFAULT@"):
|
|
return self.call("contact_get", arg_0, profile_key)
|
|
|
|
def contact_update(self, entity_jid, name, groups, profile_key="@DEFAULT@"):
|
|
return self.call("contact_update", entity_jid, name, groups, profile_key)
|
|
|
|
def contacts_get(self, profile_key="@DEFAULT@"):
|
|
return self.call("contacts_get", profile_key)
|
|
|
|
def contacts_get_from_group(self, group, profile_key="@DEFAULT@"):
|
|
return self.call("contacts_get_from_group", group, profile_key)
|
|
|
|
def devices_infos_get(self, bare_jid, profile_key):
|
|
return self.call("devices_infos_get", bare_jid, profile_key)
|
|
|
|
def disco_find_by_features(
|
|
self,
|
|
namespaces,
|
|
identities,
|
|
bare_jid=False,
|
|
service=True,
|
|
roster=True,
|
|
own_jid=True,
|
|
local_device=False,
|
|
profile_key="@DEFAULT@",
|
|
):
|
|
return self.call(
|
|
"disco_find_by_features",
|
|
namespaces,
|
|
identities,
|
|
bare_jid,
|
|
service,
|
|
roster,
|
|
own_jid,
|
|
local_device,
|
|
profile_key,
|
|
)
|
|
|
|
def disco_infos(self, entity_jid, node="", use_cache=True, profile_key="@DEFAULT@"):
|
|
return self.call("disco_infos", entity_jid, node, use_cache, profile_key)
|
|
|
|
def disco_items(self, entity_jid, node="", use_cache=True, profile_key="@DEFAULT@"):
|
|
return self.call("disco_items", entity_jid, node, use_cache, profile_key)
|
|
|
|
def disconnect(self, profile_key="@DEFAULT@"):
|
|
return self.call("disconnect", profile_key)
|
|
|
|
def encryption_namespace_get(self, arg_0):
|
|
return self.call("encryption_namespace_get", arg_0)
|
|
|
|
def encryption_plugins_get(
|
|
self,
|
|
):
|
|
return self.call(
|
|
"encryption_plugins_get",
|
|
)
|
|
|
|
def encryption_trust_ui_get(self, to_jid, namespace, profile_key):
|
|
return self.call("encryption_trust_ui_get", to_jid, namespace, profile_key)
|
|
|
|
def entities_data_get(self, jids, keys, profile):
|
|
return self.call("entities_data_get", jids, keys, profile)
|
|
|
|
def entity_data_get(self, jid, keys, profile):
|
|
return self.call("entity_data_get", jid, keys, profile)
|
|
|
|
def features_get(self, profile_key):
|
|
return self.call("features_get", profile_key)
|
|
|
|
def history_get(
|
|
self, from_jid, to_jid, limit, between=True, filters="", profile="@NONE@"
|
|
):
|
|
return self.call(
|
|
"history_get", from_jid, to_jid, limit, between, filters, profile
|
|
)
|
|
|
|
def history_last_messages_metadata_get(self, options, profile_key="@NONE@"):
|
|
return self.call("history_last_messages_metadata_get", options, profile_key)
|
|
|
|
def image_check(self, arg_0):
|
|
return self.call("image_check", arg_0)
|
|
|
|
def image_convert(self, source, dest, arg_2, extra):
|
|
return self.call("image_convert", source, dest, arg_2, extra)
|
|
|
|
def image_generate_preview(self, image_path, profile_key):
|
|
return self.call("image_generate_preview", image_path, profile_key)
|
|
|
|
def image_resize(self, image_path, width, height):
|
|
return self.call("image_resize", image_path, width, height)
|
|
|
|
def init_pre_script(
|
|
self,
|
|
):
|
|
return self.call(
|
|
"init_pre_script",
|
|
)
|
|
|
|
def is_connected(self, profile_key="@DEFAULT@"):
|
|
return self.call("is_connected", profile_key)
|
|
|
|
def main_resource_get(self, contact_jid, profile_key="@DEFAULT@"):
|
|
return self.call("main_resource_get", contact_jid, profile_key)
|
|
|
|
def menu_help_get(self, menu_id, language):
|
|
return self.call("menu_help_get", menu_id, language)
|
|
|
|
def menu_launch(self, menu_type, path, data, security_limit, profile_key):
|
|
return self.call(
|
|
"menu_launch", menu_type, path, data, security_limit, profile_key
|
|
)
|
|
|
|
def menus_get(self, language, security_limit):
|
|
return self.call("menus_get", language, security_limit)
|
|
|
|
def message_encryption_get(self, to_jid, profile_key):
|
|
return self.call("message_encryption_get", to_jid, profile_key)
|
|
|
|
def message_encryption_start(
|
|
self, to_jid, namespace="", replace=False, profile_key="@NONE@"
|
|
):
|
|
return self.call(
|
|
"message_encryption_start", to_jid, namespace, replace, profile_key
|
|
)
|
|
|
|
def message_encryption_stop(self, to_jid, profile_key):
|
|
return self.call("message_encryption_stop", to_jid, profile_key)
|
|
|
|
def message_send(
|
|
self,
|
|
to_jid,
|
|
message,
|
|
subject={},
|
|
mess_type="auto",
|
|
extra={},
|
|
profile_key="@NONE@",
|
|
):
|
|
return self.call(
|
|
"message_send", to_jid, message, subject, mess_type, extra, profile_key
|
|
)
|
|
|
|
def namespaces_get(
|
|
self,
|
|
):
|
|
return self.call(
|
|
"namespaces_get",
|
|
)
|
|
|
|
def notification_add(
|
|
self,
|
|
type_,
|
|
body_plain,
|
|
body_rich,
|
|
title,
|
|
is_global,
|
|
requires_action,
|
|
arg_6,
|
|
priority,
|
|
expire_at,
|
|
extra,
|
|
):
|
|
return self.call(
|
|
"notification_add",
|
|
type_,
|
|
body_plain,
|
|
body_rich,
|
|
title,
|
|
is_global,
|
|
requires_action,
|
|
arg_6,
|
|
priority,
|
|
expire_at,
|
|
extra,
|
|
)
|
|
|
|
def notification_delete(self, id_, is_global, profile_key):
|
|
return self.call("notification_delete", id_, is_global, profile_key)
|
|
|
|
def notifications_expired_clean(self, limit_timestamp, profile_key):
|
|
return self.call("notifications_expired_clean", limit_timestamp, profile_key)
|
|
|
|
def notifications_get(self, filters, profile_key):
|
|
return self.call("notifications_get", filters, profile_key)
|
|
|
|
def param_get_a(self, name, category, attribute="value", profile_key="@DEFAULT@"):
|
|
return self.call("param_get_a", name, category, attribute, profile_key)
|
|
|
|
def param_get_a_async(
|
|
self,
|
|
name,
|
|
category,
|
|
attribute="value",
|
|
security_limit=-1,
|
|
profile_key="@DEFAULT@",
|
|
):
|
|
return self.call(
|
|
"param_get_a_async", name, category, attribute, security_limit, profile_key
|
|
)
|
|
|
|
def param_set(
|
|
self, name, value, category, security_limit=-1, profile_key="@DEFAULT@"
|
|
):
|
|
return self.call("param_set", name, value, category, security_limit, profile_key)
|
|
|
|
def param_ui_get(self, security_limit=-1, app="", extra="", profile_key="@DEFAULT@"):
|
|
return self.call("param_ui_get", security_limit, app, extra, profile_key)
|
|
|
|
def params_categories_get(
|
|
self,
|
|
):
|
|
return self.call(
|
|
"params_categories_get",
|
|
)
|
|
|
|
def params_register_app(self, xml, security_limit=-1, app=""):
|
|
return self.call("params_register_app", xml, security_limit, app)
|
|
|
|
def params_template_load(self, filename):
|
|
return self.call("params_template_load", filename)
|
|
|
|
def params_template_save(self, filename):
|
|
return self.call("params_template_save", filename)
|
|
|
|
def params_values_from_category_get_async(
|
|
self, category, security_limit=-1, app="", extra="", profile_key="@DEFAULT@"
|
|
):
|
|
return self.call(
|
|
"params_values_from_category_get_async",
|
|
category,
|
|
security_limit,
|
|
app,
|
|
extra,
|
|
profile_key,
|
|
)
|
|
|
|
def presence_set(self, to_jid="", show="", statuses={}, profile_key="@DEFAULT@"):
|
|
return self.call("presence_set", to_jid, show, statuses, profile_key)
|
|
|
|
def presence_statuses_get(self, profile_key="@DEFAULT@"):
|
|
return self.call("presence_statuses_get", profile_key)
|
|
|
|
def private_data_delete(self, namespace, key, arg_2):
|
|
return self.call("private_data_delete", namespace, key, arg_2)
|
|
|
|
def private_data_get(self, namespace, key, profile_key):
|
|
return self.call("private_data_get", namespace, key, profile_key)
|
|
|
|
def private_data_set(self, namespace, key, data, profile_key):
|
|
return self.call("private_data_set", namespace, key, data, profile_key)
|
|
|
|
def profile_create(self, profile, password="", component=""):
|
|
return self.call("profile_create", profile, password, component)
|
|
|
|
def profile_delete_async(self, profile):
|
|
return self.call("profile_delete_async", profile)
|
|
|
|
def profile_is_session_started(self, profile_key="@DEFAULT@"):
|
|
return self.call("profile_is_session_started", profile_key)
|
|
|
|
def profile_name_get(self, profile_key="@DEFAULT@"):
|
|
return self.call("profile_name_get", profile_key)
|
|
|
|
def profile_set_default(self, profile):
|
|
return self.call("profile_set_default", profile)
|
|
|
|
def profile_start_session(self, password="", profile_key="@DEFAULT@"):
|
|
return self.call("profile_start_session", password, profile_key)
|
|
|
|
def profiles_list_get(self, clients=True, components=False):
|
|
return self.call("profiles_list_get", clients, components)
|
|
|
|
def progress_get(self, id, profile):
|
|
return self.call("progress_get", id, profile)
|
|
|
|
def progress_get_all(self, profile):
|
|
return self.call("progress_get_all", profile)
|
|
|
|
def progress_get_all_metadata(self, profile):
|
|
return self.call("progress_get_all_metadata", profile)
|
|
|
|
def ready_get(
|
|
self,
|
|
):
|
|
return self.call(
|
|
"ready_get",
|
|
)
|
|
|
|
def roster_resync(self, profile_key="@DEFAULT@"):
|
|
return self.call("roster_resync", profile_key)
|
|
|
|
def session_infos_get(self, profile_key):
|
|
return self.call("session_infos_get", profile_key)
|
|
|
|
def sub_waiting_get(self, profile_key="@DEFAULT@"):
|
|
return self.call("sub_waiting_get", profile_key)
|
|
|
|
def subscription(self, sub_type, entity, profile_key="@DEFAULT@"):
|
|
return self.call("subscription", sub_type, entity, profile_key)
|
|
|
|
def tor_infos_get(self, extra, profile_key):
|
|
return self.call("tor_infos_get", extra, profile_key)
|
|
|
|
def tor_mode_get(self, profile_key):
|
|
return self.call("tor_mode_get", profile_key)
|
|
|
|
def version_get(
|
|
self,
|
|
):
|
|
return self.call(
|
|
"version_get",
|
|
)
|
|
|
|
|
|
class AIOSignalsHandler(SignalsHandler):
|
|
|
|
def register_signal(self, name, handler, iface="core"):
|
|
"""Register an async signal handler."""
|
|
async_handler = lambda *args: asyncio.ensure_future(handler(*args))
|
|
return super().register_signal(name, async_handler, iface)
|
|
|
|
async def action_launch(self, callback_id, data, profile_key="@DEFAULT@"):
|
|
return await self.call("action_launch", callback_id, data, profile_key)
|
|
|
|
async def actions_get(self, profile_key="@DEFAULT@"):
|
|
return await self.call("actions_get", profile_key)
|
|
|
|
async def config_get(self, section, name):
|
|
return await self.call("config_get", section, name)
|
|
|
|
async def connect(self, profile_key="@DEFAULT@", password="", options={}):
|
|
return await self.call("connect", profile_key, password, options)
|
|
|
|
async def contact_add(self, entity_jid, profile_key="@DEFAULT@"):
|
|
return await self.call("contact_add", entity_jid, profile_key)
|
|
|
|
async def contact_del(self, entity_jid, profile_key="@DEFAULT@"):
|
|
return await self.call("contact_del", entity_jid, profile_key)
|
|
|
|
async def contact_get(self, arg_0, profile_key="@DEFAULT@"):
|
|
return await self.call("contact_get", arg_0, profile_key)
|
|
|
|
async def contact_update(self, entity_jid, name, groups, profile_key="@DEFAULT@"):
|
|
return await self.call("contact_update", entity_jid, name, groups, profile_key)
|
|
|
|
async def contacts_get(self, profile_key="@DEFAULT@"):
|
|
return await self.call("contacts_get", profile_key)
|
|
|
|
async def contacts_get_from_group(self, group, profile_key="@DEFAULT@"):
|
|
return await self.call("contacts_get_from_group", group, profile_key)
|
|
|
|
async def devices_infos_get(self, bare_jid, profile_key):
|
|
return await self.call("devices_infos_get", bare_jid, profile_key)
|
|
|
|
async def disco_find_by_features(
|
|
self,
|
|
namespaces,
|
|
identities,
|
|
bare_jid=False,
|
|
service=True,
|
|
roster=True,
|
|
own_jid=True,
|
|
local_device=False,
|
|
profile_key="@DEFAULT@",
|
|
):
|
|
return await self.call(
|
|
"disco_find_by_features",
|
|
namespaces,
|
|
identities,
|
|
bare_jid,
|
|
service,
|
|
roster,
|
|
own_jid,
|
|
local_device,
|
|
profile_key,
|
|
)
|
|
|
|
async def disco_infos(
|
|
self, entity_jid, node="", use_cache=True, profile_key="@DEFAULT@"
|
|
):
|
|
return await self.call("disco_infos", entity_jid, node, use_cache, profile_key)
|
|
|
|
async def disco_items(
|
|
self, entity_jid, node="", use_cache=True, profile_key="@DEFAULT@"
|
|
):
|
|
return await self.call("disco_items", entity_jid, node, use_cache, profile_key)
|
|
|
|
async def disconnect(self, profile_key="@DEFAULT@"):
|
|
return await self.call("disconnect", profile_key)
|
|
|
|
async def encryption_namespace_get(self, arg_0):
|
|
return await self.call("encryption_namespace_get", arg_0)
|
|
|
|
async def encryption_plugins_get(
|
|
self,
|
|
):
|
|
return await self.call(
|
|
"encryption_plugins_get",
|
|
)
|
|
|
|
async def encryption_trust_ui_get(self, to_jid, namespace, profile_key):
|
|
return await self.call("encryption_trust_ui_get", to_jid, namespace, profile_key)
|
|
|
|
async def entities_data_get(self, jids, keys, profile):
|
|
return await self.call("entities_data_get", jids, keys, profile)
|
|
|
|
async def entity_data_get(self, jid, keys, profile):
|
|
return await self.call("entity_data_get", jid, keys, profile)
|
|
|
|
async def features_get(self, profile_key):
|
|
return await self.call("features_get", profile_key)
|
|
|
|
async def history_get(
|
|
self, from_jid, to_jid, limit, between=True, filters="", profile="@NONE@"
|
|
):
|
|
return await self.call(
|
|
"history_get", from_jid, to_jid, limit, between, filters, profile
|
|
)
|
|
|
|
async def history_last_messages_metadata_get(self, options, profile_key="@NONE@"):
|
|
return await self.call("history_last_messages_metadata_get", options, profile_key)
|
|
|
|
async def image_check(self, arg_0):
|
|
return await self.call("image_check", arg_0)
|
|
|
|
async def image_convert(self, source, dest, arg_2, extra):
|
|
return await self.call("image_convert", source, dest, arg_2, extra)
|
|
|
|
async def image_generate_preview(self, image_path, profile_key):
|
|
return await self.call("image_generate_preview", image_path, profile_key)
|
|
|
|
async def image_resize(self, image_path, width, height):
|
|
return await self.call("image_resize", image_path, width, height)
|
|
|
|
async def init_pre_script(
|
|
self,
|
|
):
|
|
return await self.call(
|
|
"init_pre_script",
|
|
)
|
|
|
|
async def is_connected(self, profile_key="@DEFAULT@"):
|
|
return await self.call("is_connected", profile_key)
|
|
|
|
async def main_resource_get(self, contact_jid, profile_key="@DEFAULT@"):
|
|
return await self.call("main_resource_get", contact_jid, profile_key)
|
|
|
|
async def menu_help_get(self, menu_id, language):
|
|
return await self.call("menu_help_get", menu_id, language)
|
|
|
|
async def menu_launch(self, menu_type, path, data, security_limit, profile_key):
|
|
return await self.call(
|
|
"menu_launch", menu_type, path, data, security_limit, profile_key
|
|
)
|
|
|
|
async def menus_get(self, language, security_limit):
|
|
return await self.call("menus_get", language, security_limit)
|
|
|
|
async def message_encryption_get(self, to_jid, profile_key):
|
|
return await self.call("message_encryption_get", to_jid, profile_key)
|
|
|
|
async def message_encryption_start(
|
|
self, to_jid, namespace="", replace=False, profile_key="@NONE@"
|
|
):
|
|
return await self.call(
|
|
"message_encryption_start", to_jid, namespace, replace, profile_key
|
|
)
|
|
|
|
async def message_encryption_stop(self, to_jid, profile_key):
|
|
return await self.call("message_encryption_stop", to_jid, profile_key)
|
|
|
|
async def message_send(
|
|
self,
|
|
to_jid,
|
|
message,
|
|
subject={},
|
|
mess_type="auto",
|
|
extra={},
|
|
profile_key="@NONE@",
|
|
):
|
|
return await self.call(
|
|
"message_send", to_jid, message, subject, mess_type, extra, profile_key
|
|
)
|
|
|
|
async def namespaces_get(
|
|
self,
|
|
):
|
|
return await self.call(
|
|
"namespaces_get",
|
|
)
|
|
|
|
async def notification_add(
|
|
self,
|
|
type_,
|
|
body_plain,
|
|
body_rich,
|
|
title,
|
|
is_global,
|
|
requires_action,
|
|
arg_6,
|
|
priority,
|
|
expire_at,
|
|
extra,
|
|
):
|
|
return await self.call(
|
|
"notification_add",
|
|
type_,
|
|
body_plain,
|
|
body_rich,
|
|
title,
|
|
is_global,
|
|
requires_action,
|
|
arg_6,
|
|
priority,
|
|
expire_at,
|
|
extra,
|
|
)
|
|
|
|
async def notification_delete(self, id_, is_global, profile_key):
|
|
return await self.call("notification_delete", id_, is_global, profile_key)
|
|
|
|
async def notifications_expired_clean(self, limit_timestamp, profile_key):
|
|
return await self.call(
|
|
"notifications_expired_clean", limit_timestamp, profile_key
|
|
)
|
|
|
|
async def notifications_get(self, filters, profile_key):
|
|
return await self.call("notifications_get", filters, profile_key)
|
|
|
|
async def param_get_a(
|
|
self, name, category, attribute="value", profile_key="@DEFAULT@"
|
|
):
|
|
return await self.call("param_get_a", name, category, attribute, profile_key)
|
|
|
|
async def param_get_a_async(
|
|
self,
|
|
name,
|
|
category,
|
|
attribute="value",
|
|
security_limit=-1,
|
|
profile_key="@DEFAULT@",
|
|
):
|
|
return await self.call(
|
|
"param_get_a_async", name, category, attribute, security_limit, profile_key
|
|
)
|
|
|
|
async def param_set(
|
|
self, name, value, category, security_limit=-1, profile_key="@DEFAULT@"
|
|
):
|
|
return await self.call(
|
|
"param_set", name, value, category, security_limit, profile_key
|
|
)
|
|
|
|
async def param_ui_get(
|
|
self, security_limit=-1, app="", extra="", profile_key="@DEFAULT@"
|
|
):
|
|
return await self.call("param_ui_get", security_limit, app, extra, profile_key)
|
|
|
|
async def params_categories_get(
|
|
self,
|
|
):
|
|
return await self.call(
|
|
"params_categories_get",
|
|
)
|
|
|
|
async def params_register_app(self, xml, security_limit=-1, app=""):
|
|
return await self.call("params_register_app", xml, security_limit, app)
|
|
|
|
async def params_template_load(self, filename):
|
|
return await self.call("params_template_load", filename)
|
|
|
|
async def params_template_save(self, filename):
|
|
return await self.call("params_template_save", filename)
|
|
|
|
async def params_values_from_category_get_async(
|
|
self, category, security_limit=-1, app="", extra="", profile_key="@DEFAULT@"
|
|
):
|
|
return await self.call(
|
|
"params_values_from_category_get_async",
|
|
category,
|
|
security_limit,
|
|
app,
|
|
extra,
|
|
profile_key,
|
|
)
|
|
|
|
async def presence_set(
|
|
self, to_jid="", show="", statuses={}, profile_key="@DEFAULT@"
|
|
):
|
|
return await self.call("presence_set", to_jid, show, statuses, profile_key)
|
|
|
|
async def presence_statuses_get(self, profile_key="@DEFAULT@"):
|
|
return await self.call("presence_statuses_get", profile_key)
|
|
|
|
async def private_data_delete(self, namespace, key, arg_2):
|
|
return await self.call("private_data_delete", namespace, key, arg_2)
|
|
|
|
async def private_data_get(self, namespace, key, profile_key):
|
|
return await self.call("private_data_get", namespace, key, profile_key)
|
|
|
|
async def private_data_set(self, namespace, key, data, profile_key):
|
|
return await self.call("private_data_set", namespace, key, data, profile_key)
|
|
|
|
async def profile_create(self, profile, password="", component=""):
|
|
return await self.call("profile_create", profile, password, component)
|
|
|
|
async def profile_delete_async(self, profile):
|
|
return await self.call("profile_delete_async", profile)
|
|
|
|
async def profile_is_session_started(self, profile_key="@DEFAULT@"):
|
|
return await self.call("profile_is_session_started", profile_key)
|
|
|
|
async def profile_name_get(self, profile_key="@DEFAULT@"):
|
|
return await self.call("profile_name_get", profile_key)
|
|
|
|
async def profile_set_default(self, profile):
|
|
return await self.call("profile_set_default", profile)
|
|
|
|
async def profile_start_session(self, password="", profile_key="@DEFAULT@"):
|
|
return await self.call("profile_start_session", password, profile_key)
|
|
|
|
async def profiles_list_get(self, clients=True, components=False):
|
|
return await self.call("profiles_list_get", clients, components)
|
|
|
|
async def progress_get(self, id, profile):
|
|
return await self.call("progress_get", id, profile)
|
|
|
|
async def progress_get_all(self, profile):
|
|
return await self.call("progress_get_all", profile)
|
|
|
|
async def progress_get_all_metadata(self, profile):
|
|
return await self.call("progress_get_all_metadata", profile)
|
|
|
|
async def ready_get(
|
|
self,
|
|
):
|
|
return await self.call(
|
|
"ready_get",
|
|
)
|
|
|
|
async def roster_resync(self, profile_key="@DEFAULT@"):
|
|
return await self.call("roster_resync", profile_key)
|
|
|
|
async def session_infos_get(self, profile_key):
|
|
return await self.call("session_infos_get", profile_key)
|
|
|
|
async def sub_waiting_get(self, profile_key="@DEFAULT@"):
|
|
return await self.call("sub_waiting_get", profile_key)
|
|
|
|
async def subscription(self, sub_type, entity, profile_key="@DEFAULT@"):
|
|
return await self.call("subscription", sub_type, entity, profile_key)
|
|
|
|
async def tor_infos_get(self, extra, profile_key):
|
|
return await self.call("tor_infos_get", extra, profile_key)
|
|
|
|
async def tor_mode_get(self, profile_key):
|
|
return await self.call("tor_mode_get", profile_key)
|
|
|
|
async def version_get(
|
|
self,
|
|
):
|
|
return await self.call(
|
|
"version_get",
|
|
)
|
|
|
|
|
|
class AIOBridge(bridge):
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.signals_handler = None
|
|
|
|
async def post_init(self):
|
|
"""Post-initialization hook.
|
|
|
|
Actually no post init is needed, but the "__getattr__" return "partial" for
|
|
unknown methods, and this causes "dynamic_import.load_and_init_bridge" to fail.
|
|
"""
|
|
pass
|
|
|
|
def call(self, name: str, *args, **kwargs):
|
|
"""Call a remote method and return an awaitable.
|
|
|
|
@param name: name of the bridge method
|
|
"""
|
|
d = super().call(name, *args, **kwargs)
|
|
return d.asFuture(asyncio.get_event_loop())
|
|
|
|
async def bridge_connect(self):
|
|
"""Connect to the D-Bus bridge (async version)."""
|
|
try:
|
|
self.conn = await client.connect(reactor).asFuture(asyncio.get_event_loop())
|
|
self.db_object = await self.conn.getRemoteObject(
|
|
const_INT_PREFIX, const_OBJ_PATH
|
|
).asFuture(asyncio.get_event_loop())
|
|
|
|
# Find the core and plugin interfaces
|
|
for iface in self.db_object.interfaces:
|
|
if iface.name == const_INT_PREFIX + const_CORE_SUFFIX:
|
|
self.db_core_iface = iface
|
|
elif iface.name == const_INT_PREFIX + const_PLUGIN_SUFFIX:
|
|
self.db_plugin_iface = iface
|
|
|
|
if self.db_core_iface is None:
|
|
raise BridgeInitError(
|
|
f"Core interface {const_INT_PREFIX + const_CORE_SUFFIX} not found"
|
|
)
|
|
if self.db_plugin_iface is None:
|
|
raise BridgeInitError(
|
|
f"Plugin interface {const_INT_PREFIX + const_PLUGIN_SUFFIX} not found"
|
|
)
|
|
|
|
# Initialize signals handler
|
|
self.signals_handler = AIOSignalsHandler(
|
|
self.db_object, const_INT_PREFIX + const_PLUGIN_SUFFIX
|
|
)
|
|
except BridgeInitError as e:
|
|
raise e
|
|
except Exception as e:
|
|
msg = str(e)
|
|
if (
|
|
"org.freedesktop.DBus.Error.ServiceUnknown" in msg
|
|
or "org.freedesktop.DBus.Error.Spawn.ExecFailed" in msg
|
|
):
|
|
raise BridgeExceptionNoService from e
|
|
elif "org.freedesktop.DBus.Error.NotSupported" in msg:
|
|
log.error(
|
|
_(
|
|
"D-Bus is not launched, please see README to see instructions on"
|
|
" how to launch it"
|
|
)
|
|
)
|
|
raise BridgeInitError from e
|
|
|
|
def register_signal(self, functionName, handler, iface="core"):
|
|
if self.signals_handler is None:
|
|
log.error("Bridge not initialized")
|
|
return
|
|
self.signals_handler.register_signal(functionName, handler, iface)
|
|
|
|
|
|
class TwistedBridge(bridge):
|
|
|
|
async def post_init(self):
|
|
"""Post-initialization hook.
|
|
|
|
Actually no post init is needed, but the "__getattr__" return "partial" for
|
|
unknown methods, and this causes "dynamic_import.load_and_init_bridge" to fail.
|
|
"""
|
|
pass
|
|
|
|
def _errback(self, failure_):
|
|
"""Convert Failure to BridgeException"""
|
|
raise dbus_to_bridge_exception(failure_)
|
|
|
|
def call(self, name, *args, **kwargs):
|
|
"""Call a remote method and return a Deferred.
|
|
|
|
@param name(str): name of the bridge method
|
|
@param args(list): arguments
|
|
@param kwargs(dict): keyword arguments (profile, etc.)
|
|
"""
|
|
d = super().call(name, *args, **kwargs)
|
|
d.addErrback(self._errback)
|
|
return d
|
|
|
|
async def bridge_connect(self) -> None:
|
|
"""Connect to the D-Bus bridge."""
|
|
return await super().bridge_connect()
|
|
|
|
def register_signal(self, functionName, handler, iface="core"):
|
|
super().register_signal(functionName, handler, iface)
|
|
|
|
def action_launch(self, callback_id, data, profile_key="@DEFAULT@"):
|
|
return self.call("action_launch", callback_id, data, profile_key)
|
|
|
|
def actions_get(self, profile_key="@DEFAULT@"):
|
|
return self.call("actions_get", profile_key)
|
|
|
|
def config_get(self, section, name):
|
|
return self.call("config_get", section, name)
|
|
|
|
def connect(self, profile_key="@DEFAULT@", password="", options={}):
|
|
return self.call("connect", profile_key, password, options)
|
|
|
|
def contact_add(self, entity_jid, profile_key="@DEFAULT@"):
|
|
return self.call("contact_add", entity_jid, profile_key)
|
|
|
|
def contact_del(self, entity_jid, profile_key="@DEFAULT@"):
|
|
return self.call("contact_del", entity_jid, profile_key)
|
|
|
|
def contact_get(self, arg_0, profile_key="@DEFAULT@"):
|
|
return self.call("contact_get", arg_0, profile_key)
|
|
|
|
def contact_update(self, entity_jid, name, groups, profile_key="@DEFAULT@"):
|
|
return self.call("contact_update", entity_jid, name, groups, profile_key)
|
|
|
|
def contacts_get(self, profile_key="@DEFAULT@"):
|
|
return self.call("contacts_get", profile_key)
|
|
|
|
def contacts_get_from_group(self, group, profile_key="@DEFAULT@"):
|
|
return self.call("contacts_get_from_group", group, profile_key)
|
|
|
|
def devices_infos_get(self, bare_jid, profile_key):
|
|
return self.call("devices_infos_get", bare_jid, profile_key)
|
|
|
|
def disco_find_by_features(
|
|
self,
|
|
namespaces,
|
|
identities,
|
|
bare_jid=False,
|
|
service=True,
|
|
roster=True,
|
|
own_jid=True,
|
|
local_device=False,
|
|
profile_key="@DEFAULT@",
|
|
):
|
|
return self.call(
|
|
"disco_find_by_features",
|
|
namespaces,
|
|
identities,
|
|
bare_jid,
|
|
service,
|
|
roster,
|
|
own_jid,
|
|
local_device,
|
|
profile_key,
|
|
)
|
|
|
|
def disco_infos(self, entity_jid, node="", use_cache=True, profile_key="@DEFAULT@"):
|
|
return self.call("disco_infos", entity_jid, node, use_cache, profile_key)
|
|
|
|
def disco_items(self, entity_jid, node="", use_cache=True, profile_key="@DEFAULT@"):
|
|
return self.call("disco_items", entity_jid, node, use_cache, profile_key)
|
|
|
|
def disconnect(self, profile_key="@DEFAULT@"):
|
|
return self.call("disconnect", profile_key)
|
|
|
|
def encryption_namespace_get(self, arg_0):
|
|
return self.call("encryption_namespace_get", arg_0)
|
|
|
|
def encryption_plugins_get(
|
|
self,
|
|
):
|
|
return self.call(
|
|
"encryption_plugins_get",
|
|
)
|
|
|
|
def encryption_trust_ui_get(self, to_jid, namespace, profile_key):
|
|
return self.call("encryption_trust_ui_get", to_jid, namespace, profile_key)
|
|
|
|
def entities_data_get(self, jids, keys, profile):
|
|
return self.call("entities_data_get", jids, keys, profile)
|
|
|
|
def entity_data_get(self, jid, keys, profile):
|
|
return self.call("entity_data_get", jid, keys, profile)
|
|
|
|
def features_get(self, profile_key):
|
|
return self.call("features_get", profile_key)
|
|
|
|
def history_get(
|
|
self, from_jid, to_jid, limit, between=True, filters="", profile="@NONE@"
|
|
):
|
|
return self.call(
|
|
"history_get", from_jid, to_jid, limit, between, filters, profile
|
|
)
|
|
|
|
def history_last_messages_metadata_get(self, options, profile_key="@NONE@"):
|
|
return self.call("history_last_messages_metadata_get", options, profile_key)
|
|
|
|
def image_check(self, arg_0):
|
|
return self.call("image_check", arg_0)
|
|
|
|
def image_convert(self, source, dest, arg_2, extra):
|
|
return self.call("image_convert", source, dest, arg_2, extra)
|
|
|
|
def image_generate_preview(self, image_path, profile_key):
|
|
return self.call("image_generate_preview", image_path, profile_key)
|
|
|
|
def image_resize(self, image_path, width, height):
|
|
return self.call("image_resize", image_path, width, height)
|
|
|
|
def init_pre_script(
|
|
self,
|
|
):
|
|
return self.call(
|
|
"init_pre_script",
|
|
)
|
|
|
|
def is_connected(self, profile_key="@DEFAULT@"):
|
|
return self.call("is_connected", profile_key)
|
|
|
|
def main_resource_get(self, contact_jid, profile_key="@DEFAULT@"):
|
|
return self.call("main_resource_get", contact_jid, profile_key)
|
|
|
|
def menu_help_get(self, menu_id, language):
|
|
return self.call("menu_help_get", menu_id, language)
|
|
|
|
def menu_launch(self, menu_type, path, data, security_limit, profile_key):
|
|
return self.call(
|
|
"menu_launch", menu_type, path, data, security_limit, profile_key
|
|
)
|
|
|
|
def menus_get(self, language, security_limit):
|
|
return self.call("menus_get", language, security_limit)
|
|
|
|
def message_encryption_get(self, to_jid, profile_key):
|
|
return self.call("message_encryption_get", to_jid, profile_key)
|
|
|
|
def message_encryption_start(
|
|
self, to_jid, namespace="", replace=False, profile_key="@NONE@"
|
|
):
|
|
return self.call(
|
|
"message_encryption_start", to_jid, namespace, replace, profile_key
|
|
)
|
|
|
|
def message_encryption_stop(self, to_jid, profile_key):
|
|
return self.call("message_encryption_stop", to_jid, profile_key)
|
|
|
|
def message_send(
|
|
self,
|
|
to_jid,
|
|
message,
|
|
subject={},
|
|
mess_type="auto",
|
|
extra={},
|
|
profile_key="@NONE@",
|
|
):
|
|
return self.call(
|
|
"message_send", to_jid, message, subject, mess_type, extra, profile_key
|
|
)
|
|
|
|
def namespaces_get(
|
|
self,
|
|
):
|
|
return self.call(
|
|
"namespaces_get",
|
|
)
|
|
|
|
def notification_add(
|
|
self,
|
|
type_,
|
|
body_plain,
|
|
body_rich,
|
|
title,
|
|
is_global,
|
|
requires_action,
|
|
arg_6,
|
|
priority,
|
|
expire_at,
|
|
extra,
|
|
):
|
|
return self.call(
|
|
"notification_add",
|
|
type_,
|
|
body_plain,
|
|
body_rich,
|
|
title,
|
|
is_global,
|
|
requires_action,
|
|
arg_6,
|
|
priority,
|
|
expire_at,
|
|
extra,
|
|
)
|
|
|
|
def notification_delete(self, id_, is_global, profile_key):
|
|
return self.call("notification_delete", id_, is_global, profile_key)
|
|
|
|
def notifications_expired_clean(self, limit_timestamp, profile_key):
|
|
return self.call("notifications_expired_clean", limit_timestamp, profile_key)
|
|
|
|
def notifications_get(self, filters, profile_key):
|
|
return self.call("notifications_get", filters, profile_key)
|
|
|
|
def param_get_a(self, name, category, attribute="value", profile_key="@DEFAULT@"):
|
|
return self.call("param_get_a", name, category, attribute, profile_key)
|
|
|
|
def param_get_a_async(
|
|
self,
|
|
name,
|
|
category,
|
|
attribute="value",
|
|
security_limit=-1,
|
|
profile_key="@DEFAULT@",
|
|
):
|
|
return self.call(
|
|
"param_get_a_async", name, category, attribute, security_limit, profile_key
|
|
)
|
|
|
|
def param_set(
|
|
self, name, value, category, security_limit=-1, profile_key="@DEFAULT@"
|
|
):
|
|
return self.call("param_set", name, value, category, security_limit, profile_key)
|
|
|
|
def param_ui_get(self, security_limit=-1, app="", extra="", profile_key="@DEFAULT@"):
|
|
return self.call("param_ui_get", security_limit, app, extra, profile_key)
|
|
|
|
def params_categories_get(
|
|
self,
|
|
):
|
|
return self.call(
|
|
"params_categories_get",
|
|
)
|
|
|
|
def params_register_app(self, xml, security_limit=-1, app=""):
|
|
return self.call("params_register_app", xml, security_limit, app)
|
|
|
|
def params_template_load(self, filename):
|
|
return self.call("params_template_load", filename)
|
|
|
|
def params_template_save(self, filename):
|
|
return self.call("params_template_save", filename)
|
|
|
|
def params_values_from_category_get_async(
|
|
self, category, security_limit=-1, app="", extra="", profile_key="@DEFAULT@"
|
|
):
|
|
return self.call(
|
|
"params_values_from_category_get_async",
|
|
category,
|
|
security_limit,
|
|
app,
|
|
extra,
|
|
profile_key,
|
|
)
|
|
|
|
def presence_set(self, to_jid="", show="", statuses={}, profile_key="@DEFAULT@"):
|
|
return self.call("presence_set", to_jid, show, statuses, profile_key)
|
|
|
|
def presence_statuses_get(self, profile_key="@DEFAULT@"):
|
|
return self.call("presence_statuses_get", profile_key)
|
|
|
|
def private_data_delete(self, namespace, key, arg_2):
|
|
return self.call("private_data_delete", namespace, key, arg_2)
|
|
|
|
def private_data_get(self, namespace, key, profile_key):
|
|
return self.call("private_data_get", namespace, key, profile_key)
|
|
|
|
def private_data_set(self, namespace, key, data, profile_key):
|
|
return self.call("private_data_set", namespace, key, data, profile_key)
|
|
|
|
def profile_create(self, profile, password="", component=""):
|
|
return self.call("profile_create", profile, password, component)
|
|
|
|
def profile_delete_async(self, profile):
|
|
return self.call("profile_delete_async", profile)
|
|
|
|
def profile_is_session_started(self, profile_key="@DEFAULT@"):
|
|
return self.call("profile_is_session_started", profile_key)
|
|
|
|
def profile_name_get(self, profile_key="@DEFAULT@"):
|
|
return self.call("profile_name_get", profile_key)
|
|
|
|
def profile_set_default(self, profile):
|
|
return self.call("profile_set_default", profile)
|
|
|
|
def profile_start_session(self, password="", profile_key="@DEFAULT@"):
|
|
return self.call("profile_start_session", password, profile_key)
|
|
|
|
def profiles_list_get(self, clients=True, components=False):
|
|
return self.call("profiles_list_get", clients, components)
|
|
|
|
def progress_get(self, id, profile):
|
|
return self.call("progress_get", id, profile)
|
|
|
|
def progress_get_all(self, profile):
|
|
return self.call("progress_get_all", profile)
|
|
|
|
def progress_get_all_metadata(self, profile):
|
|
return self.call("progress_get_all_metadata", profile)
|
|
|
|
def ready_get(
|
|
self,
|
|
):
|
|
return self.call(
|
|
"ready_get",
|
|
)
|
|
|
|
def roster_resync(self, profile_key="@DEFAULT@"):
|
|
return self.call("roster_resync", profile_key)
|
|
|
|
def session_infos_get(self, profile_key):
|
|
return self.call("session_infos_get", profile_key)
|
|
|
|
def sub_waiting_get(self, profile_key="@DEFAULT@"):
|
|
return self.call("sub_waiting_get", profile_key)
|
|
|
|
def subscription(self, sub_type, entity, profile_key="@DEFAULT@"):
|
|
return self.call("subscription", sub_type, entity, profile_key)
|
|
|
|
def tor_infos_get(self, extra, profile_key):
|
|
return self.call("tor_infos_get", extra, profile_key)
|
|
|
|
def tor_mode_get(self, profile_key):
|
|
return self.call("tor_mode_get", profile_key)
|
|
|
|
def version_get(
|
|
self,
|
|
):
|
|
return self.call(
|
|
"version_get",
|
|
)
|