## ## Copyright (C) 2006 Gajim Team ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published ## by the Free Software Foundation; version 2 only. ## ## 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 General Public License for more details. ## import xmpp import helpers import dataforms import gajim class AdHocCommand: commandnode = 'command' commandname = 'The Command' commandfeatures = (xmpp.NS_DATA,) @staticmethod def isVisibleFor(jid): return True def __init__(self, conn, jid, sessionid): self.connection = conn self.jid = jid self.sessionid = sessionid def buildResponse(self, request, status='executing', defaultaction=None, actions=None): assert status in ('executing', 'completed', 'canceled') response = request.buildReply('result') cmd = response.addChild('command', { 'xmlns': xmpp.NS_COMMANDS, 'sessionid': self.sessionid, 'node': self.commandnode, 'status': status}) if defaultaction is not None or actions is not None: if defaultaction is not None: assert defaultaction in ('cancel', 'execute', 'prev', 'next', 'complete') attrs = {'action': defaultaction} else: attrs = {} cmd.addChild('actions', attrs, actions) return response, cmd def cancel(self, request): response, cmd = self.buildResponse(request, status='canceled') self.connection.send(response) return False # finish the session class ChangeStatusCommand(AdHocCommand): commandnode = 'change-status' commandname = 'Change status information' def execute(self, request): # first query... response, cmd = self.buildResponse(request, defaultaction='execute', actions=['execute']) cmd.addChild(node=dataforms.DataForm( title='Change status', instructions='Set the presence type and description', fields=[ dataforms.DataField('list-single', 'presence-type', label='Type of presence:', options=[ (u'free-for-chat', u'Free for chat'), (u'online', u'Online'), (u'away', u'Away'), (u'xa', u'Extended away'), (u'dnd', u'Do not disturb'), (u'offline', u'Offline - disconnect')], value='online', required=True), dataforms.DataField('text-multi', 'presence-desc', label='Presence description:')])) self.connection.send(response) # for next invocation self.execute = self.changestatus return True # keep the session def changestatus(self, request): # check if the data is correct try: form=dataforms.DataForm(node=request.getTag('command').getTag('x')) except TypeError: return False try: presencetype = form['presence-type'] if not presencetype in ('free-for-chat', 'online', 'away', 'xa', 'dnd', 'offline'): #raise BadSomething return except KeyError: # raise BadSomething return try: presencedesc = form['presence-desc'] except KeyError: presencedesc = u'' response, cmd = self.buildResponse(request, status='completed') cmd.addChild('note', {}, 'The status has been changed.') self.connection.send(response) # looking for account name... accname = None for acc in gajim.connections.iterkeys(): if self.connection is gajim.connections[acc]: accname=acc assert accname is not None gajim.interface.roster.send_status(accname, presencetype, presencedesc) return False # finish the session class ConnectionCommands: ''' This class depends on that it is a part of Connection() class. ''' def __init__(self): # a list of all commands exposed: node -> command class self.__commands = {} for cmdobj in (ChangeStatusCommand,): self.__commands[cmdobj.commandnode] = cmdobj # a list of sessions; keys are tuples (jid, sessionid, node) self.__sessions = {} def commandListQuery(self, con, iq_obj): iq = iq_obj.buildReply('result') jid = helpers.get_full_jid_from_iq(iq_obj) q = iq.getTag('query') for node, cmd in self.__commands.iteritems(): if cmd.isVisibleFor(jid): q.addChild('item', { # TODO: find the jid 'jid': 'our-jid', 'node': node, 'name': cmd.commandname}) self.connection.send(iq) def commandQuery(self, con, iq_obj): ''' Send disco result for query for command (JEP-0050, example 6.). Return True if the result was sent, False if not. ''' jid = helpers.get_full_jid_from_iq(iq_obj) node = iq_obj.getTagAttr('query', 'node') if node not in self.__commands: return False cmd = self.__commands[node] if cmd.isVisibleFor(jid): iq = iq_obj.buildReply('result') q = iq.getTag('query') q.addChild('identity', attrs = {'type': 'command-node', 'category': 'automation', 'name': cmd.commandname}) q.addChild('feature', attrs = {'var': xmpp.NS_COMMANDS}) for feature in cmd.commandfeatures: q.addChild('feature', attrs = {'var': feature}) self.connection.send(iq) return True return False def _CommandExecuteCB(self, con, iq_obj): jid = helpers.get_full_jid_from_iq(iq_obj) cmd = iq_obj.getTag('command') if cmd is None: return node = cmd.getAttr('node') if node is None: return sessionid = cmd.getAttr('sessionid') if sessionid is None: # we start a new command session... only if we are visible for the jid newcmd = self.__commands[node] if not newcmd.isVisibleFor(jid): return # generate new sessionid sessionid = self.connection.getAnID() # create new instance and run it obj = newcmd(conn=self.connection, jid=jid, sessionid=sessionid) rc = obj.execute(iq_obj) if rc: self.__sessions[(jid, sessionid, node)] = obj raise NodeProcessed else: # the command is already running, check for it magictuple = (jid, sessionid, node) if magictuple not in self.__sessions: # we don't have this session... ha! return action = cmd.getAttr('action') obj = self.__sessions[magictuple] try: if action == 'cancel': rc = obj.cancel(iq_obj) elif action == 'prev': rc = obj.prev(iq_obj) elif action == 'next': rc = obj.next(iq_obj) elif action == 'execute': rc = obj.execute(iq_obj) elif action == 'complete': rc = obj.complete(iq_obj) else: # action is wrong. stop the session, send error del self.__sessions[magictuple] return except AttributeError: # the command probably doesn't handle invoked action... # stop the session, return error del self.__sessions[magictuple] return # delete the session if rc is False if not rc: del self.__sessions[magictuple] raise xmpp.NodeProcessed