Moved some code to ease maintenance.
This commit is contained in:
parent
477f33b6b9
commit
de23ba2a15
|
@ -0,0 +1,232 @@
|
|||
##
|
||||
## 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
|
||||
|
|
@ -32,6 +32,8 @@ import common.xmpp
|
|||
from common import GnuPG
|
||||
from common import helpers
|
||||
from common import gajim
|
||||
from common import dataforms
|
||||
from common.commands import ConnectionCommands
|
||||
|
||||
STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
|
||||
'invisible']
|
||||
|
@ -1054,119 +1056,6 @@ class ConnectionVcard:
|
|||
else:
|
||||
self.dispatch('VCARD', vcard)
|
||||
|
||||
class AdHocCommand:
|
||||
commandnode = 'command'
|
||||
commandname = 'The Command'
|
||||
commandfeatures = (common.xmpp.NS_DATA,)
|
||||
|
||||
@staticmethod
|
||||
def isVisibleFor(jid): return True
|
||||
|
||||
class ChangeStatusCommand(AdHocCommand):
|
||||
commandnode = 'change-status'
|
||||
commandname = 'Change status information'
|
||||
def __init__(self, sessiondata):
|
||||
pass
|
||||
|
||||
class ConnectionCommands:
|
||||
def __init__(self):
|
||||
# a list of all commands exposed
|
||||
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.iter_items():
|
||||
if cmd.isVisibleFor(jid):
|
||||
q.addChild('item', {
|
||||
'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': common.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(jid=jid, sessionid=sessionid)
|
||||
rc = obj.execute()
|
||||
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]
|
||||
|
||||
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
|
||||
|
||||
# delete the session if rc is False
|
||||
if not rc:
|
||||
del self.__sessions[magictuple]
|
||||
|
||||
raise NodeProcessed
|
||||
|
||||
class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, ConnectionCommands):
|
||||
def __init__(self):
|
||||
ConnectionVcard.__init__(self)
|
||||
|
|
|
@ -287,6 +287,13 @@ class DataForm(xmpp.Node, object):
|
|||
return
|
||||
raise KeyError, "This form does not contain %r field." % var
|
||||
|
||||
def __contains__(self, name):
|
||||
for field in self.iter_fields():
|
||||
if field.var==name:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
class DataField(xmpp.Node, object):
|
||||
def __init__(self, typ=None,var=None, value=None, label=None, desc=None,
|
||||
required=None, options=None, node=None):
|
||||
|
|
Loading…
Reference in New Issue