core, jp: component handling + client handling refactoring:

- SàT can now handle components
    - plugin have now a "modes" key in PLUGIN_INFO where they declare if they can be used with clients and or components. They default to be client only.
    - components are really similar to clients, but with some changes in behaviour:
        * component has "entry point", which is a special plugin with a componentStart method, which is called just after component is connected
        * trigger end with a different suffixes (e.g. profileConnected vs profileConnectedComponent), so a plugin which manage both clients and components can have different workflow
        * for clients, only triggers of plugins handling client mode are launched
        * for components, only triggers of plugins needed in dependencies are launched. They all must handle component mode.
        * component have a sendHistory attribute (False by default) which can be set to True to allow saving sent messages into history
        * for convenience, "client" is still used in method even if it can now be a component
        * a new "component" boolean attribute tells if we have a component or a client
        * components have to add themselve Message protocol
        * roster and presence protocols are not added for components
        * component default port is 5347 (which is Prosody's default port)
    - asyncCreateProfile has been renamed for profileCreate, both to follow new naming convention and to prepare the transition to fully asynchronous bridge
    - createProfile has a new "component" attribute. When used to create a component, it must be set to a component entry point
    - jp: added --component argument to profile/create
    - disconnect bridge method is now asynchronous, this way frontends can know when disconnection is finished
    - new PI_* constants for PLUGIN_INFO values (not used everywhere yet)
    - client/component connection workflow has been moved to their classes instead of being a host methods
    - host.messageSend is now client.sendMessage, and former client.sendMessage is now client.sendMessageData.
    - identities are now handled in client.identities list, so it can be updated dynamically by plugins (in the future, frontends should be able to update them too through bridge)
    - profileConnecting* profileConnected* profileDisconnected* and getHandler now all use client instead of profile
This commit is contained in:
Goffi 2017-02-12 17:55:43 +01:00
commit 97337202cb
43 changed files with 839 additions and 523 deletions

View file

@ -41,11 +41,11 @@ PLUGIN_INFO = {
class ExportCommandProtocol(protocol.ProcessProtocol):
""" Try to register an account with prosody """
def __init__(self, parent, target, options, profile):
def __init__(self, parent, client, target, options):
self.parent = parent
self.target = target
self.options = options
self.profile = profile
self.client = client
def _clean(self, data):
if not data:
@ -58,10 +58,10 @@ class ExportCommandProtocol(protocol.ProcessProtocol):
log.info("connectionMade :)")
def outReceived(self, data):
self.parent.host.messageSend(self.target, {'': self._clean(data)}, no_trigger=True, profile_key=self.profile)
self.client.sendMessage(self.target, {'': self._clean(data)}, no_trigger=True)
def errReceived(self, data):
self.parent.host.messageSend(self.target, {'': self._clean(data)}, no_trigger=True, profile_key=self.profile)
self.client.sendMessage(self.target, {'': self._clean(data)}, no_trigger=True)
def processEnded(self, reason):
log.info (u"process finished: %d" % (reason.value.exitCode,))
@ -81,8 +81,9 @@ class ExportCommandProtocol(protocol.ProcessProtocol):
class CommandExport(object):
"""Command export plugin: export a command to an entity"""
#XXX: This plugin can be potentially dangerous if we don't trust entities linked
# this is specially true if we have other triggers.
# XXX: This plugin can be potentially dangerous if we don't trust entities linked
# this is specially true if we have other triggers.
# FIXME: spawned should be a client attribute, not a class one
def __init__(self, host):
log.info(_("Plugin command export initialization"))
@ -96,10 +97,10 @@ class CommandExport(object):
@param entity: jid.JID attached to the process
@param process: process to remove"""
try:
processes_set = self.spawned[(entity, process.profile)]
processes_set = self.spawned[(entity, process.client.profile)]
processes_set.discard(process)
if not processes_set:
del(self.spawned[(entity, process.profile)])
del(self.spawned[(entity, process.client.profile)])
except ValueError:
pass
@ -140,11 +141,7 @@ class CommandExport(object):
- pty: if set, launch in a pseudo terminal
- continue: continue normal MessageReceived handling
"""
profile = self.host.memory.getProfileName(profile_key)
if not profile:
log.warning(u"Unknown profile [%s]" % (profile,))
return
client = self.host.getClient(profile_key)
for target in targets:
try:
_jid = jid.JID(target)
@ -154,7 +151,7 @@ class CommandExport(object):
except (RuntimeError, jid.InvalidFormat, AttributeError):
log.info(u"invalid target ignored: %s" % (target,))
continue
process_prot = ExportCommandProtocol(self, _jid, options, profile)
self.spawned.setdefault((_jid, profile),set()).add(process_prot)
process_prot = ExportCommandProtocol(self, client, _jid, options)
self.spawned.setdefault((_jid, client.profile),set()).add(process_prot)
reactor.spawnProcess(process_prot, command, args, usePTY = process_prot.boolOption('pty'))

View file

@ -65,7 +65,7 @@ class LangDetect(object):
self.host = host
host.memory.updateParams(PARAMS)
host.trigger.add("MessageReceived", self.MessageReceivedTrigger)
host.trigger.add("messageSend", self.MessageSendTrigger)
host.trigger.add("sendMessage", self.MessageSendTrigger)
def addLanguage(self, mess_data):
message = mess_data['message']

View file

@ -43,7 +43,7 @@ class Exp_Parrot(object):
"""Parrot mode plugin: repeat messages from one entity or MUC room to another one"""
# XXX: This plugin can be potentially dangerous if we don't trust entities linked
# this is specially true if we have other triggers.
# messageSendTrigger avoid other triggers execution, it's deactivated to allow
# sendMessageTrigger avoid other triggers execution, it's deactivated to allow
# /unparrot command in text commands plugin.
# FIXME: potentially unsecure, specially with e2e encryption
@ -51,13 +51,13 @@ class Exp_Parrot(object):
log.info(_("Plugin Parrot initialization"))
self.host = host
host.trigger.add("MessageReceived", self.MessageReceivedTrigger, priority=100)
#host.trigger.add("messageSend", self.messageSendTrigger, priority=100)
#host.trigger.add("sendMessage", self.sendMessageTrigger, priority=100)
try:
self.host.plugins[C.TEXT_CMDS].registerTextCommands(self)
except KeyError:
log.info(_(u"Text commands not available"))
#def messageSendTrigger(self, client, mess_data, treatments):
#def sendMessageTrigger(self, client, mess_data, treatments):
# """ Deactivate other triggers if recipient is in parrot links """
# try:
# _links = client.parrot_links
@ -103,7 +103,7 @@ class Exp_Parrot(object):
linked = _links[from_jid.userhostJID()]
self.host.messageSend(jid.JID(unicode(linked)), message, None, "auto", no_trigger=True, profile_key=profile)
client.sendMessage(jid.JID(unicode(linked)), message, None, "auto", no_trigger=True)
return True

View file

@ -242,7 +242,7 @@ class MiscAccount(object):
if profile.lower() in self.getConfig('reserved_list'):
return defer.fail(Failure(exceptions.ConflictError))
d = self.host.memory.asyncCreateProfile(profile, password)
d = self.host.memory.createProfile(profile, password)
d.addCallback(lambda dummy: self.profileCreated(password, jid_s, profile))
return d

View file

@ -219,7 +219,7 @@ class FilePlugin(object):
has_feature = yield self.host.hasFeature(namespace, peer_jid, profile)
if has_feature:
log.info(u"{name} method will be used to send the file".format(name=method_name))
progress_id = yield defer.maybeDeferred(callback, peer_jid, filepath, filename, file_desc, profile)
progress_id = yield callback(peer_jid, filepath, filename, file_desc, profile)
defer.returnValue({'progress': progress_id})
msg = u"Can't find any method to send file to {jid}".format(jid=peer_jid.full())
log.warning(msg)

View file

@ -65,14 +65,13 @@ class GroupBlog(object):
## plugin management methods ##
def getHandler(self, profile):
def getHandler(self, client):
return GroupBlog_handler()
@defer.inlineCallbacks
def profileConnected(self, profile):
client = self.host.getClient(profile)
def profileConnected(self, client):
try:
yield self.host.checkFeatures((NS_PUBSUB_GROUPBLOG,), profile=profile)
yield self.host.checkFeatures((NS_PUBSUB_GROUPBLOG,), profile=client.profile)
except exceptions.FeatureNotFound:
client.server_groupblog_available = False
log.warning(_(u"Server is not able to manage item-access pubsub, we can't use group blog"))

View file

@ -103,7 +103,7 @@ class IPPlugin(object):
self._external_ip_cache = None
self._local_ip_cache = None
def getHandler(self, profile):
def getHandler(self, client):
return IPPlugin_handler()
def refreshIP(self):

View file

@ -45,7 +45,7 @@ PLUGIN_INFO = {
MAILDIR_PATH = "Maildir"
CATEGORY = D_("Mail Server")
NAME = D_('Block "normal" messages propagation')
# FIXME: (very) old and (very) experimental code, need a big cleaning/review
# FIXME: (very) old and (very) experimental code, need a big cleaning/review or to be deprecated
class MaildirError(Exception):
@ -82,8 +82,9 @@ class MaildirBox(object):
#the triggers
host.trigger.add("MessageReceived", self.messageReceivedTrigger)
def profileConnected(self, profile):
"""Called on profile connection, create profile data"""
def profileConnected(self, client):
"""Called on client connection, create profile data"""
profile = client.profile
self.data[profile] = PersistentBinaryDict("plugin_maildir", profile)
self.__mailboxes[profile] = {}
@ -93,8 +94,9 @@ class MaildirBox(object):
self.data[profile]["INBOX"] = {"cur_idx": 0}
self.data[profile].load().addCallback(dataLoaded)
def profileDisconnected(self, profile):
def profileDisconnected(self, client):
"""Called on profile disconnection, free profile's resources"""
profile = client.profile
del self.__mailboxes[profile]
del self.data[profile]

View file

@ -683,7 +683,7 @@ class RoomGame(object):
"""
return self._sendElements(to_jid, [(elem, attrs, content)], profile)
def getHandler(self, profile):
def getHandler(self, client):
return RoomGameHandler(self)

View file

@ -87,7 +87,7 @@ class SatSmtpMessage(object):
"""handle end of message"""
mail = Parser().parsestr("\n".join(self.message))
try:
self.host._messageSend(parseaddr(mail['to'].decode('utf-8', 'replace'))[1], mail.get_payload().decode('utf-8', 'replace'), # TODO: manage other charsets
self.host._sendMessage(parseaddr(mail['to'].decode('utf-8', 'replace'))[1], mail.get_payload().decode('utf-8', 'replace'), # TODO: manage other charsets
subject=mail['subject'].decode('utf-8', 'replace'), mess_type='normal', profile_key=self.profile)
except:
exc_type, exc_value, exc_traceback = sys.exc_info()

View file

@ -60,7 +60,7 @@ class TextCommands(object):
log.info(_("Text commands initialization"))
self.host = host
# this is internal command, so we set high priority
host.trigger.add("messageSend", self.messageSendTrigger, priority=1000000)
host.trigger.add("sendMessage", self.sendMessageTrigger, priority=1000000)
self._commands = {}
self._whois = []
self.registerTextCommands(self)
@ -168,12 +168,12 @@ class TextCommands(object):
self._whois.append((priority, callback))
self._whois.sort(key=lambda item: item[0], reverse=True)
def messageSendTrigger(self, client, mess_data, pre_xml_treatments, post_xml_treatments):
def sendMessageTrigger(self, client, mess_data, pre_xml_treatments, post_xml_treatments):
"""Install SendMessage command hook """
pre_xml_treatments.addCallback(self._messageSendCmdHook, client)
pre_xml_treatments.addCallback(self._sendMessageCmdHook, client)
return True
def _messageSendCmdHook(self, mess_data, client):
def _sendMessageCmdHook(self, mess_data, client):
""" Check text commands in message, and react consequently
msg starting with / are potential command. If a command is found, it is executed, else and help message is sent
@ -181,7 +181,7 @@ class TextCommands(object):
commands can abord message sending (if they return anything evaluating to False), or continue it (if they return True), eventually after modifying the message
an "unparsed" key is added to message, containing part of the message not yet parsed
commands can be deferred or not
@param mess_data(dict): data comming from messageSend trigger
@param mess_data(dict): data comming from sendMessage trigger
@param profile: %(doc_profile)s
"""
try:
@ -254,7 +254,7 @@ class TextCommands(object):
def _contextValid(self, mess_data, cmd_data):
"""Tell if a command can be used in the given context
@param mess_data(dict): message data as given in messageSend trigger
@param mess_data(dict): message data as given in sendMessage trigger
@param cmd_data(dict): command data as returned by self._parseDocString
@return (bool): True if command can be used in this context
"""
@ -290,7 +290,7 @@ class TextCommands(object):
mess_data["type"] = C.MESS_TYPE_INFO
mess_data["message"] = {'': message}
mess_data["extra"]["info_type"] = info_type
self.host.messageSendToBridge(mess_data, client)
self.host.sendMessageToBridge(mess_data, client)
def cmd_whois(self, client, mess_data):
"""show informations on entity

View file

@ -114,7 +114,7 @@ class UploadPlugin(object):
continue # no entity managing this extension found
log.info(u"{name} method will be used to upload the file".format(name=method_name))
progress_id_d, download_d = yield defer.maybeDeferred(upload_cb, filepath, filename, upload_jid, options, client.profile)
progress_id_d, download_d = yield upload_cb(filepath, filename, upload_jid, options, client.profile)
progress_id = yield progress_id_d
defer.returnValue((progress_id, download_d))

View file

@ -70,10 +70,10 @@ class Welcome(object):
self.host = host
host.memory.updateParams(PARAMS)
def profileConnected(self, profile):
def profileConnected(self, client):
# XXX: if you wan to try first_start again, you'll have to remove manually
# the welcome value from your profile params in sat.db
welcome = self.host.memory.params.getParamA(WELCOME_PARAM_NAME, WELCOME_PARAM_CATEGORY, use_default=False, profile_key=profile)
welcome = self.host.memory.params.getParamA(WELCOME_PARAM_NAME, WELCOME_PARAM_CATEGORY, use_default=False, profile_key=client.profile)
if welcome is None:
first_start = True
welcome = True
@ -82,10 +82,10 @@ class Welcome(object):
if welcome:
xmlui = xml_tools.note(WELCOME_MSG, WELCOME_MSG_TITLE)
self.host.actionNew({'xmlui': xmlui.toXml()}, profile=profile)
self.host.memory.setParam(WELCOME_PARAM_NAME, C.BOOL_FALSE, WELCOME_PARAM_CATEGORY, profile_key=profile)
self.host.actionNew({'xmlui': xmlui.toXml()}, profile=client.profile)
self.host.memory.setParam(WELCOME_PARAM_NAME, C.BOOL_FALSE, WELCOME_PARAM_CATEGORY, profile_key=client.profile)
self.host.trigger.point("WELCOME", first_start, welcome, profile)
self.host.trigger.point("WELCOME", first_start, welcome, client.profile)

View file

@ -81,7 +81,7 @@ class Context(potr.context.Context):
def inject(self, msg_str, appdata=None):
"""Inject encrypted data in the stream
if appdata is not None, we are sending a message in sendMessageFinishTrigger
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
and follow normal workflow
@param msg_str(str): encrypted message body
@ -103,7 +103,7 @@ class Context(potr.context.Context):
'extra': {},
'timestamp': time.time(),
}
self.host.generateMessageXML(mess_data)
client.generateMessageXML(mess_data)
client.send(mess_data['xml'])
else:
message_elt = appdata[u'xml']
@ -234,8 +234,8 @@ class OTR(object):
self._p_hints = host.plugins[u'XEP-0334']
self._p_carbons = host.plugins[u'XEP-0280']
host.trigger.add("MessageReceived", self.MessageReceivedTrigger, priority=100000)
host.trigger.add("messageSend", self.messageSendTrigger, priority=100000)
host.trigger.add("sendMessageFinish", self._sendMessageFinishTrigger)
host.trigger.add("sendMessage", self.sendMessageTrigger, priority=100000)
host.trigger.add("sendMessageData", self._sendMessageDataTrigger)
host.bridge.addMethod("skipOTR", ".plugin", in_sign='s', out_sign='', method=self._skipOTR) # FIXME: must be removed, must be done on per-message basis
host.bridge.addSignal("otrState", ".plugin", signature='sss') # args: state, destinee_jid, profile
host.importMenu((OTR_MENU, D_(u"Start/Refresh")), self._otrStartRefresh, security_limit=0, help_string=D_(u"Start or refresh an OTR session"), type_=C.MENU_SINGLE)
@ -255,26 +255,24 @@ class OTR(object):
self.skipped_profiles.add(profile)
@defer.inlineCallbacks
def profileConnected(self, profile):
if profile in self.skipped_profiles:
def profileConnected(self, client):
if client.profile in self.skipped_profiles:
return
client = self.host.getClient(profile)
ctxMng = client._otr_context_manager = ContextManager(self.host, client)
client._otr_data = persistent.PersistentBinaryDict(NS_OTR, profile)
client._otr_data = persistent.PersistentBinaryDict(NS_OTR, client.profile)
yield client._otr_data.load()
encrypted_priv_key = client._otr_data.get(PRIVATE_KEY, None)
if encrypted_priv_key is not None:
priv_key = yield self.host.memory.decryptValue(encrypted_priv_key, profile)
priv_key = yield self.host.memory.decryptValue(encrypted_priv_key, client.profile)
ctxMng.account.privkey = potr.crypt.PK.parsePrivateKey(priv_key.decode('hex'))[0]
else:
ctxMng.account.privkey = None
ctxMng.account.loadTrusts()
def profileDisconnected(self, profile):
if profile in self.skipped_profiles:
self.skipped_profiles.remove(profile)
def profileDisconnected(self, client):
if client.profile in self.skipped_profiles:
self.skipped_profiles.remove(client.profile)
return
client = self.host.getClient(profile)
for context in client._otr_context_manager.contexts.values():
context.disconnect()
del client._otr_context_manager
@ -454,7 +452,7 @@ class OTR(object):
jid = from_jid.full()))
feedback=D_(u"WARNING: received unencrypted data in a supposedly encrypted context"),
client.feedback(from_jid.full(), feedback)
client.feedback(from_jid, feedback)
except StopIteration:
return data
else:
@ -505,7 +503,7 @@ class OTR(object):
post_treat.addCallback(self._receivedTreatment, client)
return True
def _sendMessageFinishTrigger(self, client, mess_data):
def _sendMessageDataTrigger(self, client, mess_data):
if not 'OTR' in mess_data:
return
otrctx = mess_data['OTR']
@ -532,9 +530,11 @@ class OTR(object):
else:
feedback = D_(u"Your message was not sent because your correspondent closed the encrypted conversation on his/her side. "
u"Either close your own side, or refresh the session.")
client.feedback(to_jid.full(), feedback)
log.warning(_(u"Message discarded because closed encryption channel"))
client.feedback(to_jid, feedback)
raise failure.Failure(exceptions.CancelError(u'Cancelled by OTR plugin'))
def messageSendTrigger(self, client, mess_data, pre_xml_treatments, post_xml_treatments):
def sendMessageTrigger(self, client, mess_data, pre_xml_treatments, post_xml_treatments):
if mess_data['type'] == 'groupchat':
return True
if client.profile in self.skipped_profiles: # FIXME: should not be done on a per-profile basis
@ -546,7 +546,7 @@ class OTR(object):
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_PERMANENT_STORE)
mess_data['OTR'] = otrctx # this indicate that encryption is needed in sendMessageFinish trigger
mess_data['OTR'] = otrctx # 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
return True

View file

@ -50,7 +50,7 @@ class XEP_0020(object):
def __init__(self, host):
log.info(_("Plugin XEP_0020 initialization"))
def getHandler(self, profile):
def getHandler(self, client):
return XEP_0020_handler()
def getFeatureElt(self, elt):

View file

@ -72,10 +72,10 @@ class XEP_0033(object):
log.info(_("Extended Stanza Addressing plugin initialization"))
self.host = host
self.internal_data = {}
host.trigger.add("messageSend", self.messageSendTrigger, trigger.TriggerManager.MIN_PRIORITY)
host.trigger.add("sendMessage", self.sendMessageTrigger, trigger.TriggerManager.MIN_PRIORITY)
host.trigger.add("MessageReceived", self.messageReceivedTrigger)
def messageSendTrigger(self, client, mess_data, pre_xml_treatments, post_xml_treatments):
def sendMessageTrigger(self, client, mess_data, pre_xml_treatments, post_xml_treatments):
"""Process the XEP-0033 related data to be sent"""
profile = client.profile
@ -124,9 +124,9 @@ class XEP_0033(object):
client = self.host.profiles[profile]
d = defer.Deferred()
if not skip_send:
d.addCallback(client.sendMessage)
d.addCallback(client.sendMessageData)
d.addCallback(self.host.messageAddToHistory, client)
d.addCallback(self.host.messageSendToBridge, client)
d.addCallback(self.host.sendMessageToBridge, client)
d.addErrback(lambda failure: failure.trap(exceptions.CancelError))
return d.callback(mess_data)
@ -181,8 +181,8 @@ class XEP_0033(object):
post_treat.addCallback(post_treat_addr, addresses.children)
return True
def getHandler(self, profile):
return XEP_0033_handler(self, profile)
def getHandler(self, client):
return XEP_0033_handler(self, client.profile)
class XEP_0033_handler(XMPPHandler):

View file

@ -104,11 +104,10 @@ class XEP_0045(object):
host.trigger.add("presence_available", self.presenceTrigger)
host.trigger.add("MessageReceived", self.MessageReceivedTrigger, priority=1000000)
def profileConnected(self, profile):
def profileConnected(self, client):
def assign_service(service):
client = self.host.getClient(profile)
client.muc_service = service
return self.getMUCService(profile=profile).addCallback(assign_service)
return self.getMUCService(profile=client.profile).addCallback(assign_service)
def MessageReceivedTrigger(self, client, message_elt, post_treat):
if message_elt.getAttribute("type") == C.MESS_TYPE_GROUPCHAT:
@ -477,9 +476,8 @@ class XEP_0045(object):
self.checkRoomJoined(client, room_jid)
return client._muc_client.subject(room_jid, subject)
def getHandler(self, profile):
def getHandler(self, client):
# create a MUC client and associate it with profile' session
client = self.host.getClient(profile)
muc_client = client._muc_client = SatMUCClient(self)
return muc_client
@ -886,7 +884,7 @@ class SatMUCClient(muc.MUCClient):
"timestamp": time.time(),
}
self.host.messageAddToHistory(mess_data, self.parent)
self.host.messageSendToBridge(mess_data, self.parent)
self.host.sendMessageToBridge(mess_data, self.parent)
def userLeftRoom(self, room, user):
@ -925,7 +923,7 @@ class SatMUCClient(muc.MUCClient):
"timestamp": time.time(),
}
self.host.messageAddToHistory(mess_data, self.parent)
self.host.messageSendToBridge(mess_data, self.parent)
self.host.sendMessageToBridge(mess_data, self.parent)
def userChangedNick(self, room, user, new_nick):
self.host.bridge.mucRoomUserChangedNick(room.roomJID.userhost(), user.nick, new_nick, self.parent.profile)

View file

@ -69,11 +69,10 @@ class XEP_0047(object):
log.info(_("In-Band Bytestreams plugin initialization"))
self.host = host
def getHandler(self, profile):
def getHandler(self, client):
return XEP_0047_handler(self)
def profileConnected(self, profile):
client = self.host.getClient(profile)
def profileConnected(self, client):
client.xep_0047_current_stream = {} # key: stream_id, value: data(dict)
def _timeOut(self, sid, client):

View file

@ -73,14 +73,13 @@ class XEP_0048(object):
log.info(_("Text commands not available"))
@defer.inlineCallbacks
def profileConnected(self, profile):
client = self.host.getClient(profile)
local = client.bookmarks_local = PersistentBinaryDict(NS_BOOKMARKS, profile)
def profileConnected(self, client):
local = client.bookmarks_local = PersistentBinaryDict(NS_BOOKMARKS, client.profile)
yield local.load()
if not local:
local[XEP_0048.MUC_TYPE] = dict()
local[XEP_0048.URL_TYPE] = dict()
private = yield self._getServerBookmarks('private', profile)
private = yield self._getServerBookmarks('private', client.profile)
pubsub = client.bookmarks_pubsub = None
for bookmarks in (local, private, pubsub):

View file

@ -221,15 +221,15 @@ class XEP_0050(object):
self.__requesting_id = host.registerCallback(self._requestingEntity, with_data=True)
host.importMenu((D_("Service"), D_("Commands")), self._commandsMenu, security_limit=2, help_string=D_("Execute ad-hoc commands"))
def getHandler(self, profile):
def getHandler(self, client):
return XEP_0050_handler(self)
def profileConnected(self, profile):
self.addAdHocCommand(self._statusCallback, _("Status"), profile_key=profile)
def profileConnected(self, client):
self.addAdHocCommand(self._statusCallback, _("Status"), profile_key=client.profile)
def profileDisconnected(self, profile):
def profileDisconnected(self, client):
try:
del self.answering[profile]
del self.answering[client.profile]
except KeyError:
pass

Some files were not shown because too many files have changed in this diff Show more