Moved some code to ease maintenance.
This commit is contained in:
		
							parent
							
								
									477f33b6b9
								
							
						
					
					
						commit
						de23ba2a15
					
				
					 3 changed files with 241 additions and 113 deletions
				
			
		
							
								
								
									
										232
									
								
								src/common/commands.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										232
									
								
								src/common/commands.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -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…
	
	Add table
		
		Reference in a new issue