1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17  """This module is a ad-hoc command processor for xmpppy. It uses the plug-in mechanism like most of the core library. It depends on a DISCO browser manager. 
 18   
 19  There are 3 classes here, a command processor Commands like the Browser, and a command template plugin Command, and an example command. 
 20   
 21  To use this module: 
 22       
 23      Instansiate the module with the parent transport and disco browser manager as parameters. 
 24      'Plug in' commands using the command template. 
 25      The command feature must be added to existing disco replies where neccessary. 
 26       
 27  What it supplies: 
 28       
 29      Automatic command registration with the disco browser manager. 
 30      Automatic listing of commands in the public command list. 
 31      A means of handling requests, by redirection though the command manager. 
 32  """ 
 33   
 34  from protocol import * 
 35  from client import PlugIn 
 36   
 38      """Commands is an ancestor of PlugIn and can be attached to any session. 
 39       
 40      The commands class provides a lookup and browse mechnism. It follows the same priciple of the Browser class, for Service Discovery to provide the list of commands, it adds the 'list' disco type to your existing disco handler function.  
 41       
 42      How it works: 
 43          The commands are added into the existing Browser on the correct nodes. When the command list is built the supplied discovery handler function needs to have a 'list' option in type. This then gets enumerated, all results returned as None are ignored. 
 44          The command executed is then called using it's Execute method. All session management is handled by the command itself. 
 45      """ 
 47          """Initialises class and sets up local variables""" 
 48          PlugIn.__init__(self) 
 49          DBG_LINE='commands' 
 50          self._exported_methods=[] 
 51          self._handlers={'':{}} 
 52          self._browser = browser 
  53   
 61           
 68   
 92   
 94          """The internal method to process service discovery requests""" 
 95           
 96          if typ == 'items': 
 97               
 98               
 99               
100               
101               
102               
103               
104              list = [] 
105              items = [] 
106              jid = str(request.getTo()) 
107               
108              if self._handlers.has_key(jid): 
109                  for each in self._handlers[jid].keys(): 
110                      items.append((jid,each)) 
111              else: 
112                   
113                  for each in self._handlers[''].keys(): 
114                      items.append(('',each)) 
115              if items != []: 
116                  for each in items: 
117                      i = self._handlers[each[0]][each[1]]['disco'](conn,request,'list') 
118                      if i != None: 
119                          list.append(Node(tag='item',attrs={'jid':i[0],'node':i[1],'name':i[2]})) 
120                  iq = request.buildReply('result') 
121                  if request.getQuerynode(): iq.setQuerynode(request.getQuerynode()) 
122                  iq.setQueryPayload(list) 
123                  conn.send(iq) 
124              else: 
125                  conn.send(Error(request,ERR_ITEM_NOT_FOUND)) 
126              raise NodeProcessed 
127          elif typ == 'info': 
128              return {'ids':[{'category':'automation','type':'command-list'}],'features':[]} 
 129   
130 -    def addCommand(self,name,cmddisco,cmdexecute,jid=''): 
 131          """The method to call if adding a new command to the session, the requred parameters of cmddisco and cmdexecute are the methods to enable that command to be executed""" 
132           
133           
134           
135           
136          if not self._handlers.has_key(jid): 
137              self._handlers[jid]={} 
138              self._browser.setDiscoHandler(self._DiscoHandler,node=NS_COMMANDS,jid=jid) 
139          if self._handlers[jid].has_key(name): 
140              raise NameError,'Command Exists' 
141          else: 
142              self._handlers[jid][name]={'disco':cmddisco,'execute':cmdexecute} 
143           
144          self._browser.setDiscoHandler(cmddisco,node=name,jid=jid) 
 145   
147          """Removed command from the session""" 
148           
149           
150           
151           
152          if not self._handlers.has_key(jid): 
153              raise NameError,'Jid not found' 
154          if not self._handlers[jid].has_key(name): 
155              raise NameError, 'Command not found' 
156          else: 
157               
158              command = self.getCommand(name,jid)['disco'] 
159              del self._handlers[jid][name] 
160              self._browser.delDiscoHandler(command,node=name,jid=jid) 
 161   
163          """Returns the command tuple""" 
164           
165           
166           
167          if not self._handlers.has_key(jid): 
168              raise NameError,'Jid not found' 
169          elif not self._handlers[jid].has_key(name): 
170              raise NameError,'Command not found' 
171          else: 
172              return self._handlers[jid][name] 
  173   
175      """This is a prototype command handler, as each command uses a disco method  
176         and execute method you can implement it any way you like, however this is  
177         my first attempt at making a generic handler that you can hang process  
178         stages on too. There is an example command below. 
179   
180      The parameters are as follows: 
181      name : the name of the command within the jabber environment 
182      description : the natural language description 
183      discofeatures : the features supported by the command 
184      initial : the initial command in the from of {'execute':commandname} 
185       
186      All stages set the 'actions' dictionary for each session to represent the possible options available. 
187      """ 
188      name = 'examplecommand' 
189      count = 0 
190      description = 'an example command' 
191      discofeatures = [NS_COMMANDS,NS_DATA] 
192       
194          """Set up the class""" 
195          PlugIn.__init__(self) 
196          DBG_LINE='command' 
197          self.sessioncount = 0 
198          self.sessions = {} 
199           
200          self.discoinfo = {'ids':[{'category':'automation','type':'command-node','name':self.description}],'features': self.discofeatures} 
201          self._jid = jid 
 202   
204          """Plug command into the commands class""" 
205           
206          self._commands = owner 
207          self._owner = owner._owner 
208          self._commands.addCommand(self.name,self._DiscoHandler,self.Execute,jid=self._jid) 
 209   
211          """Remove command from the commands class""" 
212          self._commands.delCommand(self.name,self._jid) 
 213   
215          """Returns an id for the command session""" 
216          self.count = self.count+1 
217          return 'cmd-%s-%d'%(self.name,self.count) 
 218   
253   
255          """The handler for discovery events""" 
256          if type == 'list': 
257              return (request.getTo(),self.name,self.description) 
258          elif type == 'items': 
259              return [] 
260          elif type == 'info': 
261              return self.discoinfo 
  262   
264      """ Example class. You should read source if you wish to understate how it works.  
265          Generally, it presents a "master" that giudes user through to calculate something. 
266      """ 
267      name = 'testcommand' 
268      description = 'a noddy example command' 
273       
275          """ Determine """ 
276           
277          try: 
278              session = request.getTagAttr('command','sessionid') 
279          except: 
280              session = None 
281          if session == None: 
282              session = self.getSessionID() 
283              self.sessions[session]={'jid':request.getFrom(),'actions':{'cancel':self.cmdCancel,'next':self.cmdSecondStage,'execute':self.cmdSecondStage},'data':{'type':None}} 
284           
285          reply = request.buildReply('result') 
286          form = DataForm(title='Select type of operation',data=['Use the combobox to select the type of calculation you would like to do, then click Next',DataField(name='calctype',desc='Calculation Type',value=self.sessions[session]['data']['type'],options=[['circlediameter','Calculate the Diameter of a circle'],['circlearea','Calculate the area of a circle']],typ='list-single',required=1)]) 
287          replypayload = [Node('actions',attrs={'execute':'next'},payload=[Node('next')]),form] 
288          reply.addChild(name='command',namespace=NS_COMMANDS,attrs={'node':request.getTagAttr('command','node'),'sessionid':session,'status':'executing'},payload=replypayload) 
289          self._owner.send(reply) 
290          raise NodeProcessed 
 291   
298   
300          reply = request.buildReply('result') 
301          form = DataForm(title = 'Enter the radius', data=['Enter the radius of the circle (numbers only)',DataField(desc='Radius',name='radius',typ='text-single')]) 
302          replypayload = [Node('actions',attrs={'execute':'complete'},payload=[Node('complete'),Node('prev')]),form] 
303          reply.addChild(name='command',namespace=NS_COMMANDS,attrs={'node':request.getTagAttr('command','node'),'sessionid':request.getTagAttr('command','sessionid'),'status':'executing'},payload=replypayload) 
304          self._owner.send(reply) 
305          raise NodeProcessed 
 306   
308          form = DataForm(node = request.getTag(name='command').getTag(name='x',namespace=NS_DATA)) 
309          try: 
310              num = float(form.getField('radius').getValue()) 
311          except: 
312              self.cmdSecondStageReply(conn,request) 
313          from math import pi 
314          if self.sessions[request.getTagAttr('command','sessionid')]['data']['type'] == 'circlearea': 
315              result = (num**2)*pi 
316          else: 
317              result = num*2*pi 
318          reply = request.buildReply('result') 
319          form = DataForm(typ='result',data=[DataField(desc='result',name='result',value=result)]) 
320          reply.addChild(name='command',namespace=NS_COMMANDS,attrs={'node':request.getTagAttr('command','node'),'sessionid':request.getTagAttr('command','sessionid'),'status':'completed'},payload=[form]) 
321          self._owner.send(reply) 
322          raise NodeProcessed 
 323   
 329