1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17  """ 
 18  Protocol module contains tools that is needed for processing of  
 19  xmpp-related data structures. 
 20  """ 
 21   
 22  from simplexml import Node,ustr 
 23  import time 
 24  NS_ACTIVITY         ='http://jabber.org/protocol/activity'                   
 25  NS_ADDRESS          ='http://jabber.org/protocol/address'                    
 26  NS_ADMIN            ='http://jabber.org/protocol/admin'                      
 27  NS_ADMIN_ADD_USER               =NS_ADMIN+'#add-user'                        
 28  NS_ADMIN_DELETE_USER            =NS_ADMIN+'#delete-user'                     
 29  NS_ADMIN_DISABLE_USER           =NS_ADMIN+'#disable-user'                    
 30  NS_ADMIN_REENABLE_USER          =NS_ADMIN+'#reenable-user'                   
 31  NS_ADMIN_END_USER_SESSION       =NS_ADMIN+'#end-user-session'                
 32  NS_ADMIN_GET_USER_PASSWORD      =NS_ADMIN+'#get-user-password'               
 33  NS_ADMIN_CHANGE_USER_PASSWORD   =NS_ADMIN+'#change-user-password'            
 34  NS_ADMIN_GET_USER_ROSTER        =NS_ADMIN+'#get-user-roster'                 
 35  NS_ADMIN_GET_USER_LASTLOGIN     =NS_ADMIN+'#get-user-lastlogin'              
 36  NS_ADMIN_USER_STATS             =NS_ADMIN+'#user-stats'                      
 37  NS_ADMIN_EDIT_BLACKLIST         =NS_ADMIN+'#edit-blacklist'                  
 38  NS_ADMIN_EDIT_WHITELIST         =NS_ADMIN+'#edit-whitelist'                  
 39  NS_ADMIN_REGISTERED_USERS_NUM   =NS_ADMIN+'#get-registered-users-num'        
 40  NS_ADMIN_DISABLED_USERS_NUM     =NS_ADMIN+'#get-disabled-users-num'          
 41  NS_ADMIN_ONLINE_USERS_NUM       =NS_ADMIN+'#get-online-users-num'            
 42  NS_ADMIN_ACTIVE_USERS_NUM       =NS_ADMIN+'#get-active-users-num'            
 43  NS_ADMIN_IDLE_USERS_NUM         =NS_ADMIN+'#get-idle-users-num'              
 44  NS_ADMIN_REGISTERED_USERS_LIST  =NS_ADMIN+'#get-registered-users-list'       
 45  NS_ADMIN_DISABLED_USERS_LIST    =NS_ADMIN+'#get-disabled-users-list'         
 46  NS_ADMIN_ONLINE_USERS_LIST      =NS_ADMIN+'#get-online-users-list'           
 47  NS_ADMIN_ACTIVE_USERS_LIST      =NS_ADMIN+'#get-active-users-list'           
 48  NS_ADMIN_IDLE_USERS_LIST        =NS_ADMIN+'#get-idle-users-list'             
 49  NS_ADMIN_ANNOUNCE               =NS_ADMIN+'#announce'                        
 50  NS_ADMIN_SET_MOTD               =NS_ADMIN+'#set-motd'                        
 51  NS_ADMIN_EDIT_MOTD              =NS_ADMIN+'#edit-motd'                       
 52  NS_ADMIN_DELETE_MOTD            =NS_ADMIN+'#delete-motd'                     
 53  NS_ADMIN_SET_WELCOME            =NS_ADMIN+'#set-welcome'                     
 54  NS_ADMIN_DELETE_WELCOME         =NS_ADMIN+'#delete-welcome'                  
 55  NS_ADMIN_EDIT_ADMIN             =NS_ADMIN+'#edit-admin'                      
 56  NS_ADMIN_RESTART                =NS_ADMIN+'#restart'                         
 57  NS_ADMIN_SHUTDOWN               =NS_ADMIN+'#shutdown'                        
 58  NS_AGENTS           ='jabber:iq:agents'                                      
 59  NS_AMP              ='http://jabber.org/protocol/amp'                        
 60  NS_AMP_ERRORS                   =NS_AMP+'#errors'                            
 61  NS_AUTH             ='jabber:iq:auth' 
 62  NS_AVATAR           ='jabber:iq:avatar'                                      
 63  NS_BIND             ='urn:ietf:params:xml:ns:xmpp-bind' 
 64  NS_BROWSE           ='jabber:iq:browse'                                      
 65  NS_BYTESTREAM       ='http://jabber.org/protocol/bytestreams'                
 66  NS_CAPS             ='http://jabber.org/protocol/caps'                       
 67  NS_CHATSTATES       ='http://jabber.org/protocol/chatstates'                 
 68  NS_CLIENT           ='jabber:client' 
 69  NS_COMMANDS         ='http://jabber.org/protocol/commands' 
 70  NS_COMPONENT_ACCEPT ='jabber:component:accept' 
 71  NS_COMPONENT_1      ='http://jabberd.jabberstudio.org/ns/component/1.0' 
 72  NS_COMPRESS         ='http://jabber.org/protocol/compress'                   
 73  NS_DATA             ='jabber:x:data'                                         
 74  NS_DELAY            ='jabber:x:delay' 
 75  NS_DIALBACK         ='jabber:server:dialback' 
 76  NS_DISCO            ='http://jabber.org/protocol/disco'                      
 77  NS_DISCO_INFO                   =NS_DISCO+'#info'                            
 78  NS_DISCO_ITEMS                  =NS_DISCO+'#items'                           
 79  NS_ENCRYPTED        ='jabber:x:encrypted'                                    
 80  NS_EVENT            ='jabber:x:event'                                        
 81  NS_FEATURE          ='http://jabber.org/protocol/feature-neg'   
 82  NS_FILE             ='http://jabber.org/protocol/si/profile/file-transfer'   
 83  NS_GATEWAY          ='jabber:iq:gateway' 
 84  NS_GEOLOC           ='http://jabber.org/protocol/geoloc'                     
 85  NS_GROUPCHAT        ='gc-1.0' 
 86  NS_HTTP_BIND        ='http://jabber.org/protocol/httpbind'                   
 87  NS_IBB              ='http://jabber.org/protocol/ibb' 
 88  NS_INVISIBLE        ='presence-invisible'                                    
 89  NS_IQ               ='iq'                                                    
 90  NS_LAST             ='jabber:iq:last' 
 91  NS_MESSAGE          ='message'                                               
 92  NS_MOOD             ='http://jabber.org/protocol/mood'                       
 93  NS_MUC              ='http://jabber.org/protocol/muc'                        
 94  NS_MUC_ADMIN                    =NS_MUC+'#admin'                             
 95  NS_MUC_OWNER                    =NS_MUC+'#owner'                             
 96  NS_MUC_UNIQUE                   =NS_MUC+'#unique'                            
 97  NS_MUC_USER                     =NS_MUC+'#user'                              
 98  NS_MUC_REGISTER                 =NS_MUC+'#register'                          
 99  NS_MUC_REQUEST                  =NS_MUC+'#request'                           
100  NS_MUC_ROOMCONFIG               =NS_MUC+'#roomconfig'                        
101  NS_MUC_ROOMINFO                 =NS_MUC+'#roominfo'                          
102  NS_MUC_ROOMS                    =NS_MUC+'#rooms'                             
103  NS_MUC_TRAFIC                   =NS_MUC+'#traffic'                           
104  NS_OFFLINE          ='http://jabber.org/protocol/offline'                    
105  NS_PHYSLOC          ='http://jabber.org/protocol/physloc'                    
106  NS_PRESENCE         ='presence'                                              
107  NS_PRIVACY          ='jabber:iq:privacy' 
108  NS_PRIVATE          ='jabber:iq:private' 
109  NS_PUBSUB           ='http://jabber.org/protocol/pubsub'                     
110  NS_REGISTER         ='jabber:iq:register' 
111  NS_ROSTER           ='jabber:iq:roster' 
112  NS_ROSTERX          ='http://jabber.org/protocol/rosterx'                    
113  NS_RPC              ='jabber:iq:rpc'                                         
114  NS_SASL             ='urn:ietf:params:xml:ns:xmpp-sasl' 
115  NS_SEARCH           ='jabber:iq:search' 
116  NS_SERVER           ='jabber:server' 
117  NS_SESSION          ='urn:ietf:params:xml:ns:xmpp-session' 
118  NS_SI               ='http://jabber.org/protocol/si'                         
119  NS_SI_PUB           ='http://jabber.org/protocol/sipub'                      
120  NS_SIGNED           ='jabber:x:signed'                                       
121  NS_STANZAS          ='urn:ietf:params:xml:ns:xmpp-stanzas' 
122  NS_STREAMS          ='http://etherx.jabber.org/streams' 
123  NS_TIME             ='jabber:iq:time' 
124  NS_TLS              ='urn:ietf:params:xml:ns:xmpp-tls' 
125  NS_VACATION         ='http://jabber.org/protocol/vacation' 
126  NS_VCARD            ='vcard-temp' 
127  NS_VERSION          ='jabber:iq:version' 
128  NS_WAITINGLIST      ='http://jabber.org/protocol/waitinglist'                
129  NS_XHTML_IM         ='http://jabber.org/protocol/xhtml-im'                   
130  NS_DATA_LAYOUT      ='http://jabber.org/protocol/xdata-layout'               
131  NS_DATA_VALIDATE    ='http://jabber.org/protocol/xdata-validate'             
132  NS_XMPP_STREAMS     ='urn:ietf:params:xml:ns:xmpp-streams' 
133   
134  xmpp_stream_error_conditions=""" 
135  bad-format --  --  -- The entity has sent XML that cannot be processed. 
136  bad-namespace-prefix --  --  -- The entity has sent a namespace prefix that is unsupported, or has sent no namespace prefix on an element that requires such a prefix. 
137  conflict --  --  -- The server is closing the active stream for this entity because a new stream has been initiated that conflicts with the existing stream. 
138  connection-timeout --  --  -- The entity has not generated any traffic over the stream for some period of time. 
139  host-gone --  --  -- The value of the 'to' attribute provided by the initiating entity in the stream header corresponds to a hostname that is no longer hosted by the server. 
140  host-unknown --  --  -- The value of the 'to' attribute provided by the initiating entity in the stream header does not correspond to a hostname that is hosted by the server. 
141  improper-addressing --  --  -- A stanza sent between two servers lacks a 'to' or 'from' attribute (or the attribute has no value). 
142  internal-server-error --  --  -- The server has experienced a misconfiguration or an otherwise-undefined internal error that prevents it from servicing the stream. 
143  invalid-from -- cancel --  -- The JID or hostname provided in a 'from' address does not match an authorized JID or validated domain negotiated between servers via SASL or dialback, or between a client and a server via authentication and resource authorization. 
144  invalid-id --  --  -- The stream ID or dialback ID is invalid or does not match an ID previously provided. 
145  invalid-namespace --  --  -- The streams namespace name is something other than "http://etherx.jabber.org/streams" or the dialback namespace name is something other than "jabber:server:dialback". 
146  invalid-xml --  --  -- The entity has sent invalid XML over the stream to a server that performs validation. 
147  not-authorized --  --  -- The entity has attempted to send data before the stream has been authenticated, or otherwise is not authorized to perform an action related to stream negotiation. 
148  policy-violation --  --  -- The entity has violated some local service policy. 
149  remote-connection-failed --  --  -- The server is unable to properly connect to a remote resource that is required for authentication or authorization. 
150  resource-constraint --  --  -- The server lacks the system resources necessary to service the stream. 
151  restricted-xml --  --  -- The entity has attempted to send restricted XML features such as a comment, processing instruction, DTD, entity reference, or unescaped character. 
152  see-other-host --  --  -- The server will not provide service to the initiating entity but is redirecting traffic to another host. 
153  system-shutdown --  --  -- The server is being shut down and all active streams are being closed. 
154  undefined-condition --  --  -- The error condition is not one of those defined by the other conditions in this list. 
155  unsupported-encoding --  --  -- The initiating entity has encoded the stream in an encoding that is not supported by the server. 
156  unsupported-stanza-type --  --  -- The initiating entity has sent a first-level child of the stream that is not supported by the server. 
157  unsupported-version --  --  -- The value of the 'version' attribute provided by the initiating entity in the stream header specifies a version of XMPP that is not supported by the server. 
158  xml-not-well-formed --  --  -- The initiating entity has sent XML that is not well-formed.""" 
159  xmpp_stanza_error_conditions=""" 
160  bad-request -- 400 -- modify -- The sender has sent XML that is malformed or that cannot be processed. 
161  conflict -- 409 -- cancel -- Access cannot be granted because an existing resource or session exists with the same name or address. 
162  feature-not-implemented -- 501 -- cancel -- The feature requested is not implemented by the recipient or server and therefore cannot be processed. 
163  forbidden -- 403 -- auth -- The requesting entity does not possess the required permissions to perform the action. 
164  gone -- 302 -- modify -- The recipient or server can no longer be contacted at this address. 
165  internal-server-error -- 500 -- wait -- The server could not process the stanza because of a misconfiguration or an otherwise-undefined internal server error. 
166  item-not-found -- 404 -- cancel -- The addressed JID or item requested cannot be found. 
167  jid-malformed -- 400 -- modify -- The value of the 'to' attribute in the sender's stanza does not adhere to the syntax defined in Addressing Scheme. 
168  not-acceptable -- 406 -- cancel -- The recipient or server understands the request but is refusing to process it because it does not meet criteria defined by the recipient or server. 
169  not-allowed -- 405 -- cancel -- The recipient or server does not allow any entity to perform the action. 
170  not-authorized -- 401 -- auth -- The sender must provide proper credentials before being allowed to perform the action, or has provided improper credentials. 
171  payment-required -- 402 -- auth -- The requesting entity is not authorized to access the requested service because payment is required. 
172  recipient-unavailable -- 404 -- wait -- The intended recipient is temporarily unavailable. 
173  redirect -- 302 -- modify -- The recipient or server is redirecting requests for this information to another entity. 
174  registration-required -- 407 -- auth -- The requesting entity is not authorized to access the requested service because registration is required. 
175  remote-server-not-found -- 404 -- cancel -- A remote server or service specified as part or all of the JID of the intended recipient does not exist. 
176  remote-server-timeout -- 504 -- wait -- A remote server or service specified as part or all of the JID of the intended recipient could not be contacted within a reasonable amount of time. 
177  resource-constraint -- 500 -- wait -- The server or recipient lacks the system resources necessary to service the request. 
178  service-unavailable -- 503 -- cancel -- The server or recipient does not currently provide the requested service. 
179  subscription-required -- 407 -- auth -- The requesting entity is not authorized to access the requested service because a subscription is required. 
180  undefined-condition -- 500 --  --  
181  unexpected-request -- 400 -- wait -- The recipient or server understood the request but was not expecting it at this time (e.g., the request was out of order).""" 
182  sasl_error_conditions=""" 
183  aborted --  --  -- The receiving entity acknowledges an <abort/> element sent by the initiating entity; sent in reply to the <abort/> element. 
184  incorrect-encoding --  --  -- The data provided by the initiating entity could not be processed because the [BASE64]Josefsson, S., The Base16, Base32, and Base64 Data Encodings, July 2003. encoding is incorrect (e.g., because the encoding does not adhere to the definition in Section 3 of [BASE64]Josefsson, S., The Base16, Base32, and Base64 Data Encodings, July 2003.); sent in reply to a <response/> element or an <auth/> element with initial response data. 
185  invalid-authzid --  --  -- The authzid provided by the initiating entity is invalid, either because it is incorrectly formatted or because the initiating entity does not have permissions to authorize that ID; sent in reply to a <response/> element or an <auth/> element with initial response data. 
186  invalid-mechanism --  --  -- The initiating entity did not provide a mechanism or requested a mechanism that is not supported by the receiving entity; sent in reply to an <auth/> element. 
187  mechanism-too-weak --  --  -- The mechanism requested by the initiating entity is weaker than server policy permits for that initiating entity; sent in reply to a <response/> element or an <auth/> element with initial response data. 
188  not-authorized --  --  -- The authentication failed because the initiating entity did not provide valid credentials (this includes but is not limited to the case of an unknown username); sent in reply to a <response/> element or an <auth/> element with initial response data. 
189  temporary-auth-failure --  --  -- The authentication failed because of a temporary error condition within the receiving entity; sent in reply to an <auth/> element or <response/> element.""" 
190   
191  ERRORS,_errorcodes={},{} 
192  for ns,errname,errpool in [(NS_XMPP_STREAMS,'STREAM',xmpp_stream_error_conditions), 
193                             (NS_STANZAS     ,'ERR'   ,xmpp_stanza_error_conditions), 
194                             (NS_SASL        ,'SASL'  ,sasl_error_conditions)]: 
195      for err in errpool.split('\n')[1:]: 
196          cond,code,typ,text=err.split(' -- ') 
197          name=errname+'_'+cond.upper().replace('-','_') 
198          locals()[name]=ns+' '+cond 
199          ERRORS[ns+' '+cond]=[code,typ,text] 
200          if code: _errorcodes[code]=cond 
201  del ns,errname,errpool,err,cond,code,typ,text 
202   
204      """ Returns true if the node is a positive reply. """ 
205      return node and node.getType()=='result' 
 207      """ Returns true if the node is a negative reply. """ 
208      return node and node.getType()=='error' 
 209   
211      """ Exception that should be raised by handler when the handling should be stopped. """ 
 213      """ Base exception class for stream errors.""" 
 238   
239  stream_exceptions = {'bad-format': BadFormat, 
240                       'bad-namespace-prefix': BadNamespacePrefix, 
241                       'conflict': Conflict, 
242                       'connection-timeout': ConnectionTimeout, 
243                       'host-gone': HostGone, 
244                       'host-unknown': HostUnknown, 
245                       'improper-addressing': ImproperAddressing, 
246                       'internal-server-error': InternalServerError, 
247                       'invalid-from': InvalidFrom, 
248                       'invalid-id': InvalidID, 
249                       'invalid-namespace': InvalidNamespace, 
250                       'invalid-xml': InvalidXML, 
251                       'not-authorized': NotAuthorized, 
252                       'policy-violation': PolicyViolation, 
253                       'remote-connection-failed': RemoteConnectionFailed, 
254                       'resource-constraint': ResourceConstraint, 
255                       'restricted-xml': RestrictedXML, 
256                       'see-other-host': SeeOtherHost, 
257                       'system-shutdown': SystemShutdown, 
258                       'undefined-condition': UndefinedCondition, 
259                       'unsupported-encoding': UnsupportedEncoding, 
260                       'unsupported-stanza-type': UnsupportedStanzaType, 
261                       'unsupported-version': UnsupportedVersion, 
262                       'xml-not-well-formed': XMLNotWellFormed} 
263   
265      """ JID object. JID can be built from string, modified, compared, serialised into string. """ 
266 -    def __init__(self, jid=None, node='', domain='', resource=''): 
 267          """ Constructor. JID can be specified as string (jid argument) or as separate parts. 
268              Examples: 
269              JID('node@domain/resource') 
270              JID(node='node',domain='domain.org') 
271          """ 
272          if not jid and not domain: raise ValueError('JID must contain at least domain name') 
273          elif type(jid)==type(self): self.node,self.domain,self.resource=jid.node,jid.domain,jid.resource 
274          elif domain: self.node,self.domain,self.resource=node,domain,resource 
275          else: 
276              if jid.find('@')+1: self.node,jid=jid.split('@',1) 
277              else: self.node='' 
278              if jid.find('/')+1: self.domain,self.resource=jid.split('/',1) 
279              else: self.domain,self.resource=jid,'' 
 281          """ Return the node part of the JID """ 
282          return self.node 
 284          """ Set the node part of the JID to new value. Specify None to remove the node part.""" 
285          self.node=node.lower() 
 286 -    def getDomain(self): 
 287          """ Return the domain part of the JID """ 
288          return self.domain 
 289 -    def setDomain(self,domain): 
 290          """ Set the domain part of the JID to new value.""" 
291          self.domain=domain.lower() 
 293          """ Return the resource part of the JID """ 
294          return self.resource 
 296          """ Set the resource part of the JID to new value. Specify None to remove the resource part.""" 
297          self.resource=resource 
 299          """ Return the bare representation of JID. I.e. string value w/o resource. """ 
300          return self.__str__(0) 
 302          """ Compare the JID to another instance or to string for equality. """ 
303          try: other=JID(other) 
304          except ValueError: return 0 
305          return self.resource==other.resource and self.__str__(0) == other.__str__(0) 
 307          """ Compare the JID to another instance or to string for non-equality. """ 
308          return not self.__eq__(other) 
 310          """ Compare the node and domain parts of the JID's for equality. """ 
311          return self.__str__(0) == JID(other).__str__(0) 
 313          """ Serialise JID into string. """ 
314          if self.node: jid=self.node+'@'+self.domain 
315          else: jid=self.domain 
316          if wresource and self.resource: return jid+'/'+self.resource 
317          return jid 
 319          """ Produce hash of the JID, Allows to use JID objects as keys of the dictionary. """ 
320          return hash(self.__str__()) 
  321   
323      """ A "stanza" object class. Contains methods that are common for presences, iqs and messages. """ 
324 -    def __init__(self, name=None, to=None, typ=None, frm=None, attrs={}, payload=[], timestamp=None, xmlns=None, node=None): 
 325          """ Constructor, name is the name of the stanza i.e. 'message' or 'presence' or 'iq'. 
326              to is the value of 'to' attribure, 'typ' - 'type' attribute 
327              frn - from attribure, attrs - other attributes mapping, payload - same meaning as for simplexml payload definition 
328              timestamp - the time value that needs to be stamped over stanza 
329              xmlns - namespace of top stanza node 
330              node - parsed or unparsed stana to be taken as prototype. 
331          """ 
332          if not attrs: attrs={} 
333          if to: attrs['to']=to 
334          if frm: attrs['from']=frm 
335          if typ: attrs['type']=typ 
336          Node.__init__(self, tag=name, attrs=attrs, payload=payload, node=node) 
337          if not node and xmlns: self.setNamespace(xmlns) 
338          if self['to']: self.setTo(self['to']) 
339          if self['from']: self.setFrom(self['from']) 
340          if node and type(self)==type(node) and self.__class__==node.__class__ and self.attrs.has_key('id'): del self.attrs['id'] 
341          self.timestamp=None 
342          for x in self.getTags('x',namespace=NS_DELAY): 
343              try: 
344                  if not self.getTimestamp() or x.getAttr('stamp')<self.getTimestamp(): self.setTimestamp(x.getAttr('stamp')) 
345              except: pass 
346          if timestamp is not None: self.setTimestamp(timestamp)   
 348          """ Return value of the 'to' attribute. """ 
349          try: return self['to'] 
350          except: return None 
 352          """ Return value of the 'from' attribute. """ 
353          try: return self['from'] 
354          except: return None 
 356          """ Return the timestamp in the 'yyyymmddThhmmss' format. """ 
357          return self.timestamp 
 359          """ Return the value of the 'id' attribute. """ 
360          return self.getAttr('id') 
 362          """ Set the value of the 'to' attribute. """ 
363          self.setAttr('to', JID(val)) 
 365          """ Return the value of the 'type' attribute. """ 
366          return self.getAttr('type') 
 368          """ Set the value of the 'from' attribute. """ 
369          self.setAttr('from', JID(val)) 
 371          """ Set the value of the 'type' attribute. """ 
372          self.setAttr('type', val) 
 374          """ Set the value of the 'id' attribute. """ 
375          self.setAttr('id', val) 
 377          """ Return the error-condition (if present) or the textual description of the error (otherwise). """ 
378          errtag=self.getTag('error') 
379          if errtag: 
380              for tag in errtag.getChildren(): 
381                  if tag.getName()<>'text': return tag.getName() 
382              return errtag.getData() 
 384          """ Return the error code. Obsolette. """ 
385          return self.getTagAttr('error','code') 
 395          """Set the timestamp. timestamp should be the yyyymmddThhmmss string.""" 
396          if not val: val=time.strftime('%Y%m%dT%H:%M:%S', time.gmtime()) 
397          self.timestamp=val 
398          self.setTag('x',{'stamp':self.timestamp},namespace=NS_DELAY) 
 400          """ Return the list of namespaces to which belongs the direct childs of element""" 
401          props=[] 
402          for child in self.getChildren(): 
403              prop=child.getNamespace() 
404              if prop not in props: props.append(prop) 
405          return props 
 407          """ Set the item 'item' to the value 'val'.""" 
408          if item in ['to','from']: val=JID(val) 
409          return self.setAttr(item,val) 
  410   
412      """ XMPP Message stanza - "push" mechanism.""" 
413 -    def __init__(self, to=None, body=None, typ=None, subject=None, attrs={}, frm=None, payload=[], timestamp=None, xmlns=NS_CLIENT, node=None): 
 414          """ Create message object. You can specify recipient, text of message, type of message 
415              any additional attributes, sender of the message, any additional payload (f.e. jabber:x:delay element) and namespace in one go. 
416              Alternatively you can pass in the other XML object as the 'node' parameted to replicate it as message. """ 
417          Protocol.__init__(self, 'message', to=to, typ=typ, attrs=attrs, frm=frm, payload=payload, timestamp=timestamp, xmlns=xmlns, node=node) 
418          if body: self.setBody(body) 
419          if subject: self.setSubject(subject) 
 421          """ Returns text of the message. """ 
422          return self.getTagData('body') 
 424          """ Returns subject of the message. """ 
425          return self.getTagData('subject') 
 427          """ Returns thread of the message. """ 
428          return self.getTagData('thread') 
 429 -    def setBody(self,val): 
 430          """ Sets the text of the message. """ 
431          self.setTagData('body',val) 
 433          """ Sets the subject of the message. """ 
434          self.setTagData('subject',val) 
 436          """ Sets the thread of the message. """ 
437          self.setTagData('thread',val) 
 439          """ Builds and returns another message object with specified text. 
440              The to, from and thread properties of new message are pre-set as reply to this message. """ 
441          m=Message(to=self.getFrom(),frm=self.getTo(),body=text) 
442          th=self.getThread() 
443          if th: m.setThread(th) 
444          return m 
  445   
447      """ XMPP Presence object.""" 
448 -    def __init__(self, to=None, typ=None, priority=None, show=None, status=None, attrs={}, frm=None, timestamp=None, payload=[], xmlns=NS_CLIENT, node=None): 
 449          """ Create presence object. You can specify recipient, type of message, priority, show and status values 
450              any additional attributes, sender of the presence, timestamp, any additional payload (f.e. jabber:x:delay element) and namespace in one go. 
451              Alternatively you can pass in the other XML object as the 'node' parameted to replicate it as presence. """ 
452          Protocol.__init__(self, 'presence', to=to, typ=typ, attrs=attrs, frm=frm, payload=payload, timestamp=timestamp, xmlns=xmlns, node=node) 
453          if priority: self.setPriority(priority) 
454          if show: self.setShow(show) 
455          if status: self.setStatus(status) 
 457          """ Returns the priority of the message. """ 
458          return self.getTagData('priority') 
 460          """ Returns the show value of the message. """ 
461          return self.getTagData('show') 
 463          """ Returns the status string of the message. """ 
464          return self.getTagData('status') 
 466          """ Sets the priority of the message. """ 
467          self.setTagData('priority',val) 
 469          """ Sets the show value of the message. """ 
470          self.setTagData('show',val) 
 472          """ Sets the status string of the message. """ 
473          self.setTagData('status',val) 
 474   
480          for xtag in self.getTags('x'): 
481              for child in xtag.getTags('item'): 
482                  for cchild in child.getTags(tag): 
483                      return cchild.getData(),cchild.getAttr(attr) 
484          return None,None 
 486          """Returns the presence role (for groupchat)""" 
487          return self._muc_getItemAttr('item','role') 
 489          """Returns the presence affiliation (for groupchat)""" 
490          return self._muc_getItemAttr('item','affiliation') 
 492          """Returns the nick value (for nick change in groupchat)""" 
493          return self._muc_getItemAttr('item','nick') 
 495          """Returns the presence jid (for groupchat)""" 
496          return self._muc_getItemAttr('item','jid') 
 498          """Returns the reason of the presence (for groupchat)""" 
499          return self._muc_getSubTagDataAttr('reason','')[0] 
 501          """Returns the reason of the presence (for groupchat)""" 
502          return self._muc_getSubTagDataAttr('actor','jid')[1] 
 504          """Returns the status code of the presence (for groupchat)""" 
505          return self._muc_getItemAttr('status','code') 
  506   
508      """ XMPP Iq object - get/set dialog mechanism. """ 
509 -    def __init__(self, typ=None, queryNS=None, attrs={}, to=None, frm=None, payload=[], xmlns=NS_CLIENT, node=None): 
 510          """ Create Iq object. You can specify type, query namespace 
511              any additional attributes, recipient of the iq, sender of the iq, any additional payload (f.e. jabber:x:data node) and namespace in one go. 
512              Alternatively you can pass in the other XML object as the 'node' parameted to replicate it as an iq. """ 
513          Protocol.__init__(self, 'iq', to=to, typ=typ, attrs=attrs, frm=frm, xmlns=xmlns, node=node) 
514          if payload: self.setQueryPayload(payload) 
515          if queryNS: self.setQueryNS(queryNS) 
 517          """ Return the namespace of the 'query' child element.""" 
518          tag=self.getTag('query') 
519          if tag: return tag.getNamespace() 
 521          """ Return the 'node' attribute value of the 'query' child element.""" 
522          return self.getTagAttr('query','node') 
 524          """ Return the 'query' child element payload.""" 
525          tag=self.getTag('query') 
526          if tag: return tag.getPayload() 
 528          """ Return the 'query' child element child nodes.""" 
529          tag=self.getTag('query') 
530          if tag: return tag.getChildren() 
 532          """ Set the namespace of the 'query' child element.""" 
533          self.setTag('query').setNamespace(namespace) 
 535          """ Set the 'query' child element payload.""" 
536          self.setTag('query').setPayload(payload) 
 538          """ Set the 'node' attribute value of the 'query' child element.""" 
539          self.setTagAttr('query','node',node) 
 541          """ Builds and returns another Iq object of specified type. 
542              The to, from and query child node of new Iq are pre-set as reply to this Iq. """ 
543          iq=Iq(typ,to=self.getFrom(),frm=self.getTo(),attrs={'id':self.getID()}) 
544          if self.getTag('query'): iq.setQueryNS(self.getQueryNS()) 
545          return iq 
 546   
548      """ XMPP-style error element. 
549          In the case of stanza error should be attached to XMPP stanza. 
550          In the case of stream-level errors should be used separately. """ 
551 -    def __init__(self,name,code=None,typ=None,text=None): 
 552          """ Create new error node object. 
553              Mandatory parameter: name - name of error condition. 
554              Optional parameters: code, typ, text. Used for backwards compartibility with older jabber protocol.""" 
555          if ERRORS.has_key(name): 
556              cod,type,txt=ERRORS[name] 
557              ns=name.split()[0] 
558          else: cod,ns,type,txt='500',NS_STANZAS,'cancel','' 
559          if typ: type=typ 
560          if code: cod=code 
561          if text: txt=text 
562          Node.__init__(self,'error',{},[Node(name)]) 
563          if type: self.setAttr('type',type) 
564          if not cod: self.setName('stream:error') 
565          if txt: self.addChild(node=Node(ns+' text',{},[txt])) 
566          if cod: self.setAttr('code',cod) 
  567   
569      """ Used to quickly transform received stanza into error reply.""" 
571          """ Create error reply basing on the received 'node' stanza and the 'error' error condition. 
572              If the 'node' is not the received stanza but locally created ('to' and 'from' fields needs not swapping) 
573              specify the 'reply' argument as false.""" 
574          if reply: Protocol.__init__(self,to=node.getFrom(),frm=node.getTo(),node=node) 
575          else: Protocol.__init__(self,node=node) 
576          self.setError(error) 
577          if node.getType()=='error': self.__str__=self.__dupstr__ 
 579          """ Dummy function used as preventor of creating error node in reply to error node. 
580              I.e. you will not be able to serialise "double" error into string. 
581          """ 
582          return '' 
  583   
585      """ This class is used in the DataForm class to describe the single data item. 
586          If you are working with jabber:x:data (XEP-0004, XEP-0068, XEP-0122)  
587          then you will need to work with instances of this class. """ 
588 -    def __init__(self,name=None,value=None,typ=None,required=0,label=None,desc=None,options=[],node=None): 
 589          """ Create new data field of specified name,value and type. 
590              Also 'required','desc' and 'options' fields can be set. 
591              Alternatively other XML object can be passed in as the 'node' parameted to replicate it as a new datafiled. 
592              """ 
593          Node.__init__(self,'field',node=node) 
594          if name: self.setVar(name) 
595          if type(value) in [list,tuple]: self.setValues(value) 
596          elif value: self.setValue(value) 
597          if typ: self.setType(typ) 
598          elif not typ and not node: self.setType('text-single') 
599          if required: self.setRequired(required) 
600          if label: self.setLabel(label) 
601          if desc: self.setDesc(desc) 
602          if options: self.setOptions(options) 
 604          """ Change the state of the 'required' flag. """ 
605          if req: self.setTag('required') 
606          else: 
607              try: self.delChild('required') 
608              except ValueError: return 
 610          """ Returns in this field a required one. """ 
611          return self.getTag('required') 
 613          """ Set the label of this field. """ 
614          self.setAttr('label',label) 
 616          """ Return the label of this field. """ 
617          return self.getAttr('label') 
 619          """ Set the description of this field. """ 
620          self.setTagData('desc',desc) 
 622          """ Return the description of this field. """ 
623          return self.getTagData('desc') 
 625          """ Set the value of this field. """ 
626          self.setTagData('value',val) 
 630          """ Set the values of this field as values-list. 
631              Replaces all previous filed values! If you need to just add a value - use addValue method.""" 
632          while self.getTag('value'): self.delChild('value') 
633          for val in lst: self.addValue(val) 
 635          """ Add one more value to this field. Used in 'get' iq's or such.""" 
636          self.addChild('value',{},[val]) 
 638          """ Return the list of values associated with this field.""" 
639          ret=[] 
640          for tag in self.getTags('value'): ret.append(tag.getData()) 
641          return ret 
 643          """ Return label-option pairs list associated with this field.""" 
644          ret=[] 
645          for tag in self.getTags('option'): ret.append([tag.getAttr('label'),tag.getTagData('value')]) 
646          return ret 
 648          """ Set label-option pairs list associated with this field.""" 
649          while self.getTag('option'): self.delChild('option') 
650          for opt in lst: self.addOption(opt) 
 652          """ Add one more label-option pair to this field.""" 
653          if type(opt) in [str,unicode]: self.addChild('option').setTagData('value',opt) 
654          else: self.addChild('option',{'label':opt[0]}).setTagData('value',opt[1]) 
 656          """ Get type of this field. """ 
657          return self.getAttr('type') 
 659          """ Set type of this field. """ 
660          return self.setAttr('type',val) 
 662          """ Get 'var' attribute value of this field. """ 
663          return self.getAttr('var') 
 665          """ Set 'var' attribute value of this field. """ 
666          return self.setAttr('var',val) 
  667   
754