1   
   2   
   3   
   4   
   5   
   6   
   7   
   8   
   9   
  10   
  11   
  12   
  13   
  14   
  15   
  16   
  17  """ 
  18  Protocol module contains tools that are needed for processing of xmpp-related 
  19  data structures, including jabber-objects like JID or different stanzas and 
  20  sub- stanzas) handling routines 
  21  """ 
  22   
  23  from simplexml import Node, NodeBuilder 
  24  import time 
  25  import string 
  26  import hashlib 
  27   
  29      trans_table = string.maketrans(string.ascii_lowercase, 
  30          string.ascii_uppercase) 
  31      return s.translate(trans_table) 
   32   
  33  NS_ACTIVITY       = 'http://jabber.org/protocol/activity'              
  34  NS_ADDRESS        = 'http://jabber.org/protocol/address'               
  35  NS_AGENTS         = 'jabber:iq:agents' 
  36  NS_AMP            = 'http://jabber.org/protocol/amp' 
  37  NS_AMP_ERRORS     = NS_AMP + '#errors' 
  38  NS_ARCHIVE        = 'urn:xmpp:archive'                                 
  39  NS_ARCHIVE_AUTO   = NS_ARCHIVE + ':auto'                               
  40  NS_ARCHIVE_MANAGE = NS_ARCHIVE + ':manage'                             
  41  NS_ARCHIVE_MANUAL = NS_ARCHIVE + ':manual'                             
  42  NS_ARCHIVE_PREF   = NS_ARCHIVE + ':pref' 
  43  NS_ATOM           = 'http://www.w3.org/2005/Atom' 
  44  NS_ATTENTION      = 'urn:xmpp:attention:0'                             
  45  NS_AUTH           = 'jabber:iq:auth' 
  46  NS_AVATAR         = 'http://www.xmpp.org/extensions/xep-0084.html#ns-metadata' 
  47  NS_BIND           = 'urn:ietf:params:xml:ns:xmpp-bind' 
  48  NS_BLOCKING       = 'urn:xmpp:blocking'                                
  49  NS_BOB            = 'urn:xmpp:bob'                                     
  50  NS_BOOKMARKS      = 'storage:bookmarks'                                
  51  NS_BROWSE         = 'jabber:iq:browse' 
  52  NS_BROWSING       = 'http://jabber.org/protocol/browsing'              
  53  NS_BYTESTREAM     = 'http://jabber.org/protocol/bytestreams'           
  54  NS_CAPS           = 'http://jabber.org/protocol/caps'                  
  55  NS_CAPTCHA        = 'urn:xmpp:captcha'                                 
  56  NS_CARBONS        = 'urn:xmpp:carbons:2'                               
  57  NS_CHATSTATES     = 'http://jabber.org/protocol/chatstates'            
  58  NS_CHATTING       = 'http://jabber.org/protocol/chatting'              
  59  NS_CLIENT         = 'jabber:client' 
  60  NS_CONDITIONS     = 'urn:xmpp:muc:conditions:0'                        
  61  NS_COMMANDS       = 'http://jabber.org/protocol/commands' 
  62  NS_COMPONENT_ACCEPT = 'jabber:component:accept' 
  63  NS_COMPONENT_1    = 'http://jabberd.jabberstudio.org/ns/component/1.0' 
  64  NS_COMPRESS       = 'http://jabber.org/protocol/compress'              
  65  NS_CONFERENCE     = 'jabber:x:conference' 
  66  NS_CORRECT        = 'urn:xmpp:message-correct:0'                       
  67  NS_DATA           = 'jabber:x:data'                                    
  68  NS_DATA_MEDIA     = 'urn:xmpp:media-element'                           
  69  NS_DELAY          = 'jabber:x:delay' 
  70  NS_DELAY2         = 'urn:xmpp:delay' 
  71  NS_DIALBACK       = 'jabber:server:dialback' 
  72  NS_DISCO          = 'http://jabber.org/protocol/disco' 
  73  NS_DISCO_INFO     = NS_DISCO + '#info' 
  74  NS_DISCO_ITEMS    = NS_DISCO + '#items' 
  75  NS_ENCRYPTED      = 'jabber:x:encrypted'                               
  76  NS_ESESSION       = 'http://www.xmpp.org/extensions/xep-0116.html#ns' 
  77  NS_ESESSION_INIT  = 'http://www.xmpp.org/extensions/xep-0116.html#ns-init'  
  78  NS_EVENT          = 'jabber:x:event'                                   
  79  NS_FEATURE        = 'http://jabber.org/protocol/feature-neg' 
  80  NS_FILE           = 'http://jabber.org/protocol/si/profile/file-transfer'  
  81  NS_FORWARD        = 'urn:xmpp:forward:0'                               
  82  NS_GAMING         = 'http://jabber.org/protocol/gaming'                
  83  NS_GATEWAY        = 'jabber:iq:gateway'                                
  84  NS_GEOLOC         = 'http://jabber.org/protocol/geoloc'                
  85  NS_GROUPCHAT      = 'gc-1.0' 
  86  NS_HTTP_AUTH      = 'http://jabber.org/protocol/http-auth'             
  87  NS_HTTP_BIND      = 'http://jabber.org/protocol/httpbind'              
  88  NS_IBB            = 'http://jabber.org/protocol/ibb' 
  89  NS_INVISIBLE      = 'presence-invisible'                               
  90  NS_IQ             = 'iq'                                               
  91  NS_JINGLE         ='urn:xmpp:jingle:1'                                 
  92  NS_JINGLE_ERRORS  = 'urn:xmpp:jingle:errors:1'                         
  93  NS_JINGLE_RTP     = 'urn:xmpp:jingle:apps:rtp:1'                       
  94  NS_JINGLE_RTP_AUDIO = 'urn:xmpp:jingle:apps:rtp:audio'                 
  95  NS_JINGLE_RTP_VIDEO = 'urn:xmpp:jingle:apps:rtp:video'                 
  96  NS_JINGLE_FILE_TRANSFER ='urn:xmpp:jingle:apps:file-transfer:3'         
  97  NS_JINGLE_XTLS='urn:xmpp:jingle:security:xtls:0'                       
  98  NS_JINGLE_RAW_UDP = 'urn:xmpp:jingle:transports:raw-udp:1'             
  99  NS_JINGLE_ICE_UDP = 'urn:xmpp:jingle:transports:ice-udp:1'             
 100  NS_JINGLE_BYTESTREAM ='urn:xmpp:jingle:transports:s5b:1'               
 101  NS_JINGLE_IBB     = 'urn:xmpp:jingle:transports:ibb:1'                 
 102  NS_LAST           = 'jabber:iq:last' 
 103  NS_LOCATION       = 'http://jabber.org/protocol/geoloc'                
 104  NS_MAM            = 'urn:xmpp:mam:tmp'                                 
 105  NS_MESSAGE        = 'message'                                          
 106  NS_MOOD           = 'http://jabber.org/protocol/mood'                  
 107  NS_MUC            = 'http://jabber.org/protocol/muc' 
 108  NS_MUC_USER       = NS_MUC + '#user' 
 109  NS_MUC_ADMIN      = NS_MUC + '#admin' 
 110  NS_MUC_OWNER      = NS_MUC + '#owner' 
 111  NS_MUC_UNIQUE     = NS_MUC + '#unique' 
 112  NS_MUC_CONFIG     = NS_MUC + '#roomconfig' 
 113  NS_NICK           = 'http://jabber.org/protocol/nick'                  
 114  NS_OFFLINE        = 'http://www.jabber.org/jeps/jep-0030.html'         
 115  NS_PHYSLOC        = 'http://jabber.org/protocol/physloc'               
 116  NS_PING           = 'urn:xmpp:ping'                                    
 117  NS_PRESENCE       = 'presence'                                         
 118  NS_PRIVACY        = 'jabber:iq:privacy' 
 119  NS_PRIVATE        = 'jabber:iq:private' 
 120  NS_PROFILE        = 'http://jabber.org/protocol/profile'               
 121  NS_PUBSUB         = 'http://jabber.org/protocol/pubsub'                
 122  NS_PUBSUB_EVENT   = 'http://jabber.org/protocol/pubsub#event' 
 123  NS_PUBSUB_PUBLISH_OPTIONS = NS_PUBSUB + '#publish-options'             
 124  NS_PUBSUB_OWNER   = 'http://jabber.org/protocol/pubsub#owner'          
 125  NS_REGISTER       = 'jabber:iq:register' 
 126  NS_ROSTER         = 'jabber:iq:roster' 
 127  NS_ROSTERNOTES    = 'storage:rosternotes' 
 128  NS_ROSTERX        = 'http://jabber.org/protocol/rosterx'               
 129  NS_ROSTER_VER     = 'urn:xmpp:features:rosterver'                      
 130  NS_RPC            = 'jabber:iq:rpc'                                    
 131  NS_RSM            = 'http://jabber.org/protocol/rsm' 
 132  NS_SASL           = 'urn:ietf:params:xml:ns:xmpp-sasl' 
 133  NS_SECLABEL       = 'urn:xmpp:sec-label:0' 
 134  NS_SECLABEL_CATALOG = 'urn:xmpp:sec-label:catalog:2' 
 135  NS_SEARCH         = 'jabber:iq:search' 
 136  NS_SERVER         = 'jabber:server' 
 137  NS_SESSION        = 'urn:ietf:params:xml:ns:xmpp-session' 
 138  NS_SI             = 'http://jabber.org/protocol/si'                    
 139  NS_SI_PUB         = 'http://jabber.org/protocol/sipub'                 
 140  NS_SIGNED         = 'jabber:x:signed'                                  
 141  NS_SSN            = 'urn:xmpp:ssn'                                     
 142  NS_STANZA_CRYPTO  = 'http://www.xmpp.org/extensions/xep-0200.html#ns'  
 143  NS_STANZAS        = 'urn:ietf:params:xml:ns:xmpp-stanzas' 
 144  NS_STREAM         = 'http://affinix.com/jabber/stream' 
 145  NS_STREAMS        = 'http://etherx.jabber.org/streams' 
 146  NS_TIME           = 'jabber:iq:time'                                   
 147  NS_TIME_REVISED   = 'urn:xmpp:time'                                    
 148  NS_TLS            = 'urn:ietf:params:xml:ns:xmpp-tls' 
 149  NS_TUNE           = 'http://jabber.org/protocol/tune'                  
 150  NS_VACATION       = 'http://jabber.org/protocol/vacation' 
 151  NS_VCARD          = 'vcard-temp' 
 152  NS_GMAILNOTIFY    = 'google:mail:notify' 
 153  NS_GTALKSETTING   = 'google:setting' 
 154  NS_VCARD_UPDATE   = NS_VCARD + ':x:update' 
 155  NS_VERSION        = 'jabber:iq:version' 
 156  NS_VIEWING        = 'http://jabber.org/protocol/viewing'               
 157  NS_WAITINGLIST    = 'http://jabber.org/protocol/waitinglist'           
 158  NS_XHTML_IM       = 'http://jabber.org/protocol/xhtml-im'              
 159  NS_XHTML          = 'http://www.w3.org/1999/xhtml'                     
 160  NS_X_OOB          = 'jabber:x:oob'                                     
 161  NS_DATA_LAYOUT    = 'http://jabber.org/protocol/xdata-layout'          
 162  NS_DATA_VALIDATE  = 'http://jabber.org/protocol/xdata-validate'        
 163  NS_XMPP_STREAMS   = 'urn:ietf:params:xml:ns:xmpp-streams' 
 164  NS_RECEIPTS       = 'urn:xmpp:receipts' 
 165  NS_PUBKEY_PUBKEY  = 'urn:xmpp:pubkey:2'                                               
 166  NS_PUBKEY_REVOKE  = 'urn:xmpp:revoke:2' 
 167  NS_PUBKEY_ATTEST  = 'urn:xmpp:attest:2' 
 168  NS_STREAM_MGMT    = 'urn:xmpp:sm:2'                                    
 169  NS_HASHES         = 'urn:xmpp:hashes:1'                                
 170  NS_HASHES_MD5     = 'urn:xmpp:hash-function-textual-names:md5' 
 171  NS_HASHES_SHA1    = 'urn:xmpp:hash-function-textual-names:sha-1' 
 172  NS_HASHES_SHA256  = 'urn:xmpp:hash-function-textual-names:sha-256' 
 173  NS_HASHES_SHA512  = 'urn:xmpp:hash-function-textual-names:sha-512' 
 174   
 175   
 176   
 177   
 178   
 179   
 180   
 181   
 182   
 183   
 184   
 185   
 186   
 187   
 188   
 189   
 190   
 191   
 192   
 193   
 194   
 195   
 196   
 197   
 198   
 199   
 200   
 201   
 202   
 203   
 204   
 205   
 206   
 207   
 208   
 209   
 210   
 211   
 212   
 213   
 214   
 215   
 216   
 217   
 218   
 219   
 220   
 221   
 222   
 223   
 224   
 225   
 226   
 227   
 228   
 229   
 230   
 231   
 232   
 233   
 234   
 235   
 236   
 237   
 238       
 239           
 240           
 241           
 242           
 243           
 244           
 245               
 246   
 247   
 248   
 249   
 250   
 251        
 252   
 253  ERRORS = { 
 254      'urn:ietf:params:xml:ns:xmpp-sasl aborted': ['', 
 255          '', 
 256          'The receiving entity acknowledges an <abort/> element sent by the initiating entity; sent in reply to the <abort/> element.'], 
 257      'urn:ietf:params:xml:ns:xmpp-sasl incorrect-encoding': ['', 
 258          '', 
 259          '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.'], 
 260      'urn:ietf:params:xml:ns:xmpp-sasl invalid-authzid': ['', 
 261          '', 
 262          '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.'], 
 263      'urn:ietf:params:xml:ns:xmpp-sasl invalid-mechanism': ['', 
 264          '', 
 265          '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.'], 
 266      'urn:ietf:params:xml:ns:xmpp-sasl mechanism-too-weak': ['', 
 267          '', 
 268          '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.'], 
 269      'urn:ietf:params:xml:ns:xmpp-sasl not-authorized': ['', 
 270          '', 
 271          '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.'], 
 272      'urn:ietf:params:xml:ns:xmpp-sasl temporary-auth-failure': ['', 
 273          '', 
 274          'The authentication failed because of a temporary error condition within the receiving entity; sent in reply to an <auth/> element or <response/> element.'], 
 275      'urn:ietf:params:xml:ns:xmpp-stanzas bad-request': ['400', 
 276          'modify', 
 277          'The sender has sent XML that is malformed or that cannot be processed.'], 
 278      'urn:ietf:params:xml:ns:xmpp-stanzas conflict': ['409', 
 279          'cancel', 
 280          'Access cannot be granted because an existing resource or session exists with the same name or address.'], 
 281      'urn:ietf:params:xml:ns:xmpp-stanzas feature-not-implemented': ['501', 
 282          'cancel', 
 283          'The feature requested is not implemented by the recipient or server and therefore cannot be processed.'], 
 284      'urn:ietf:params:xml:ns:xmpp-stanzas forbidden': ['403', 
 285          'auth', 
 286          'The requesting entity does not possess the required permissions to perform the action.'], 
 287      'urn:ietf:params:xml:ns:xmpp-stanzas gone': ['302', 
 288          'modify', 
 289          'The recipient or server can no longer be contacted at this address.'], 
 290      'urn:ietf:params:xml:ns:xmpp-stanzas internal-server-error': ['500', 
 291          'wait', 
 292          'The server could not process the stanza because of a misconfiguration or an otherwise-undefined internal server error.'], 
 293      'urn:ietf:params:xml:ns:xmpp-stanzas item-not-found': ['404', 
 294          'cancel', 
 295          'The addressed JID or item requested cannot be found.'], 
 296      'urn:ietf:params:xml:ns:xmpp-stanzas jid-malformed': ['400', 
 297          'modify', 
 298          "The value of the        'to' attribute in the sender's stanza does not adhere to the syntax defined in Addressing Scheme."], 
 299      'urn:ietf:params:xml:ns:xmpp-stanzas not-acceptable': ['406', 
 300          'cancel', 
 301          '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.'], 
 302      'urn:ietf:params:xml:ns:xmpp-stanzas not-allowed': ['405', 
 303          'cancel', 
 304          'The recipient or server does not allow any entity to perform the action.'], 
 305      'urn:ietf:params:xml:ns:xmpp-stanzas not-authorized': ['401', 
 306          'auth', 
 307          'The sender must provide proper credentials before being allowed to perform the action, or has provided improper credentials.'], 
 308      'urn:ietf:params:xml:ns:xmpp-stanzas payment-required': ['402', 
 309          'auth', 
 310          'The requesting entity is not authorized to access the requested service because payment is required.'], 
 311      'urn:ietf:params:xml:ns:xmpp-stanzas recipient-unavailable': ['404', 
 312          'wait', 
 313          'The intended recipient is temporarily unavailable.'], 
 314      'urn:ietf:params:xml:ns:xmpp-stanzas redirect': ['302', 
 315          'modify', 
 316          'The recipient or server is redirecting requests for this information to another entity.'], 
 317      'urn:ietf:params:xml:ns:xmpp-stanzas registration-required': ['407', 
 318          'auth', 
 319          'The requesting entity is not authorized to access the requested service because registration is required.'], 
 320      'urn:ietf:params:xml:ns:xmpp-stanzas remote-server-not-found': ['404', 
 321          'cancel', 
 322          'A remote server or service specified as part or all of the JID of the intended recipient does not exist.'], 
 323      'urn:ietf:params:xml:ns:xmpp-stanzas remote-server-timeout': ['504', 
 324          'wait', 
 325          '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.'], 
 326      'urn:ietf:params:xml:ns:xmpp-stanzas resource-constraint': ['500', 
 327          'wait', 
 328          'The server or recipient lacks the system resources necessary to service the request.'], 
 329      'urn:ietf:params:xml:ns:xmpp-stanzas service-unavailable': ['503', 
 330          'cancel', 
 331          'The server or recipient does not currently provide the requested service.'], 
 332      'urn:ietf:params:xml:ns:xmpp-stanzas subscription-required': ['407', 
 333          'auth', 
 334          'The requesting entity is not authorized to access the requested service because a subscription is required.'], 
 335      'urn:ietf:params:xml:ns:xmpp-stanzas undefined-condition': ['500', 
 336          '', 
 337          'Undefined Condition'], 
 338      'urn:ietf:params:xml:ns:xmpp-stanzas unexpected-request': ['400', 
 339          'wait', 
 340          'The recipient or server understood the request but was not expecting it at this time (e.g., the request was out of order).'], 
 341      'urn:ietf:params:xml:ns:xmpp-streams bad-format': ['', 
 342          '', 
 343          'The entity has sent XML that cannot be processed.'], 
 344      'urn:ietf:params:xml:ns:xmpp-streams bad-namespace-prefix': ['', 
 345          '', 
 346          'The entity has sent a namespace prefix that is unsupported, or has sent no namespace prefix on an element that requires such a prefix.'], 
 347      'urn:ietf:params:xml:ns:xmpp-streams conflict': ['', 
 348          '', 
 349          'The server is closing the active stream for this entity because a new stream has been initiated that conflicts with the existing stream.'], 
 350      'urn:ietf:params:xml:ns:xmpp-streams connection-timeout': ['', 
 351          '', 
 352          'The entity has not generated any traffic over the stream for some period of time.'], 
 353      'urn:ietf:params:xml:ns:xmpp-streams host-gone': ['', 
 354          '', 
 355          "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."], 
 356      'urn:ietf:params:xml:ns:xmpp-streams host-unknown': ['', 
 357          '', 
 358          "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."], 
 359      'urn:ietf:params:xml:ns:xmpp-streams improper-addressing': ['', 
 360          '', 
 361          "A stanza sent between two servers lacks a        'to' or 'from' attribute (or the attribute has no value)."], 
 362      'urn:ietf:params:xml:ns:xmpp-streams internal-server-error': ['', 
 363          '', 
 364          'The server has experienced a misconfiguration or an otherwise-undefined internal error that prevents it from servicing the stream.'], 
 365      'urn:ietf:params:xml:ns:xmpp-streams invalid-from': ['cancel', 
 366          '', 
 367          "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."], 
 368      'urn:ietf:params:xml:ns:xmpp-streams invalid-id': ['', 
 369          '', 
 370          'The stream ID or dialback ID is invalid or does not match an ID previously provided.'], 
 371      'urn:ietf:params:xml:ns:xmpp-streams invalid-namespace': ['', 
 372          '', 
 373          '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".'], 
 374      'urn:ietf:params:xml:ns:xmpp-streams invalid-xml': ['', 
 375          '', 
 376          'The entity has sent invalid XML over the stream to a server that performs validation.'], 
 377      'urn:ietf:params:xml:ns:xmpp-streams not-authorized': ['', 
 378          '', 
 379          '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.'], 
 380      'urn:ietf:params:xml:ns:xmpp-streams policy-violation': ['', 
 381          '', 
 382          'The entity has violated some local service policy.'], 
 383      'urn:ietf:params:xml:ns:xmpp-streams remote-connection-failed': ['', 
 384          '', 
 385          'The server is unable to properly connect to a remote resource that is required for authentication or authorization.'], 
 386      'urn:ietf:params:xml:ns:xmpp-streams resource-constraint': ['', 
 387          '', 
 388          'The server lacks the system resources necessary to service the stream.'], 
 389      'urn:ietf:params:xml:ns:xmpp-streams restricted-xml': ['', 
 390          '', 
 391          'The entity has attempted to send restricted XML features such as a comment, processing instruction, DTD, entity reference, or unescaped character.'], 
 392      'urn:ietf:params:xml:ns:xmpp-streams see-other-host': ['', 
 393          '', 
 394          'The server will not provide service to the initiating entity but is redirecting traffic to another host.'], 
 395      'urn:ietf:params:xml:ns:xmpp-streams system-shutdown': ['', 
 396          '', 
 397          'The server is being shut down and all active streams are being closed.'], 
 398      'urn:ietf:params:xml:ns:xmpp-streams undefined-condition': ['', 
 399          '', 
 400          'The error condition is not one of those defined by the other conditions in this list.'], 
 401      'urn:ietf:params:xml:ns:xmpp-streams unsupported-encoding': ['', 
 402          '', 
 403          'The initiating entity has encoded the stream in an encoding that is not supported by the server.'], 
 404      'urn:ietf:params:xml:ns:xmpp-streams unsupported-stanza-type': ['', 
 405          '', 
 406          'The initiating entity has sent a first-level child of the stream that is not supported by the server.'], 
 407      'urn:ietf:params:xml:ns:xmpp-streams unsupported-version': ['', 
 408          '', 
 409          "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."], 
 410      'urn:ietf:params:xml:ns:xmpp-streams xml-not-well-formed': ['', 
 411          '', 
 412          'The initiating entity has sent XML that is not well-formed.'] 
 413  } 
 414   
 415  _errorcodes = { 
 416      '302': 'redirect', 
 417      '400': 'unexpected-request', 
 418      '401': 'not-authorized', 
 419      '402': 'payment-required', 
 420      '403': 'forbidden', 
 421      '404': 'remote-server-not-found', 
 422      '405': 'not-allowed', 
 423      '406': 'not-acceptable', 
 424      '407': 'subscription-required', 
 425      '409': 'conflict', 
 426      '500': 'undefined-condition', 
 427      '501': 'feature-not-implemented', 
 428      '503': 'service-unavailable', 
 429      '504': 'remote-server-timeout', 
 430      'cancel': 'invalid-from' 
 431  } 
 432   
 433  STREAM_NOT_AUTHORIZED = 'urn:ietf:params:xml:ns:xmpp-streams not-authorized' 
 434  STREAM_REMOTE_CONNECTION_FAILED = 'urn:ietf:params:xml:ns:xmpp-streams remote-connection-failed' 
 435  SASL_MECHANISM_TOO_WEAK = 'urn:ietf:params:xml:ns:xmpp-sasl mechanism-too-weak' 
 436  STREAM_XML_NOT_WELL_FORMED = 'urn:ietf:params:xml:ns:xmpp-streams xml-not-well-formed' 
 437  ERR_JID_MALFORMED = 'urn:ietf:params:xml:ns:xmpp-stanzas jid-malformed' 
 438  STREAM_SEE_OTHER_HOST = 'urn:ietf:params:xml:ns:xmpp-streams see-other-host' 
 439  STREAM_BAD_NAMESPACE_PREFIX = 'urn:ietf:params:xml:ns:xmpp-streams bad-namespace-prefix' 
 440  ERR_SERVICE_UNAVAILABLE = 'urn:ietf:params:xml:ns:xmpp-stanzas service-unavailable' 
 441  STREAM_CONNECTION_TIMEOUT = 'urn:ietf:params:xml:ns:xmpp-streams connection-timeout' 
 442  STREAM_UNSUPPORTED_VERSION = 'urn:ietf:params:xml:ns:xmpp-streams unsupported-version' 
 443  STREAM_IMPROPER_ADDRESSING = 'urn:ietf:params:xml:ns:xmpp-streams improper-addressing' 
 444  STREAM_UNDEFINED_CONDITION = 'urn:ietf:params:xml:ns:xmpp-streams undefined-condition' 
 445  SASL_NOT_AUTHORIZED = 'urn:ietf:params:xml:ns:xmpp-sasl not-authorized' 
 446  ERR_GONE = 'urn:ietf:params:xml:ns:xmpp-stanzas gone' 
 447  SASL_TEMPORARY_AUTH_FAILURE = 'urn:ietf:params:xml:ns:xmpp-sasl temporary-auth-failure' 
 448  ERR_REMOTE_SERVER_NOT_FOUND = 'urn:ietf:params:xml:ns:xmpp-stanzas remote-server-not-found' 
 449  ERR_UNEXPECTED_REQUEST = 'urn:ietf:params:xml:ns:xmpp-stanzas unexpected-request' 
 450  ERR_RECIPIENT_UNAVAILABLE = 'urn:ietf:params:xml:ns:xmpp-stanzas recipient-unavailable' 
 451  ERR_CONFLICT = 'urn:ietf:params:xml:ns:xmpp-stanzas conflict' 
 452  STREAM_SYSTEM_SHUTDOWN = 'urn:ietf:params:xml:ns:xmpp-streams system-shutdown' 
 453  STREAM_BAD_FORMAT = 'urn:ietf:params:xml:ns:xmpp-streams bad-format' 
 454  ERR_SUBSCRIPTION_REQUIRED = 'urn:ietf:params:xml:ns:xmpp-stanzas subscription-required' 
 455  STREAM_INTERNAL_SERVER_ERROR = 'urn:ietf:params:xml:ns:xmpp-streams internal-server-error' 
 456  ERR_NOT_AUTHORIZED = 'urn:ietf:params:xml:ns:xmpp-stanzas not-authorized' 
 457  SASL_ABORTED = 'urn:ietf:params:xml:ns:xmpp-sasl aborted' 
 458  ERR_REGISTRATION_REQUIRED = 'urn:ietf:params:xml:ns:xmpp-stanzas registration-required' 
 459  ERR_INTERNAL_SERVER_ERROR = 'urn:ietf:params:xml:ns:xmpp-stanzas internal-server-error' 
 460  SASL_INCORRECT_ENCODING = 'urn:ietf:params:xml:ns:xmpp-sasl incorrect-encoding' 
 461  STREAM_HOST_GONE = 'urn:ietf:params:xml:ns:xmpp-streams host-gone' 
 462  STREAM_POLICY_VIOLATION = 'urn:ietf:params:xml:ns:xmpp-streams policy-violation' 
 463  STREAM_INVALID_XML = 'urn:ietf:params:xml:ns:xmpp-streams invalid-xml' 
 464  STREAM_CONFLICT = 'urn:ietf:params:xml:ns:xmpp-streams conflict' 
 465  STREAM_RESOURCE_CONSTRAINT = 'urn:ietf:params:xml:ns:xmpp-streams resource-constraint' 
 466  STREAM_UNSUPPORTED_ENCODING = 'urn:ietf:params:xml:ns:xmpp-streams unsupported-encoding' 
 467  ERR_NOT_ALLOWED = 'urn:ietf:params:xml:ns:xmpp-stanzas not-allowed' 
 468  ERR_ITEM_NOT_FOUND = 'urn:ietf:params:xml:ns:xmpp-stanzas item-not-found' 
 469  ERR_NOT_ACCEPTABLE = 'urn:ietf:params:xml:ns:xmpp-stanzas not-acceptable' 
 470  STREAM_INVALID_FROM = 'urn:ietf:params:xml:ns:xmpp-streams invalid-from' 
 471  ERR_FEATURE_NOT_IMPLEMENTED = 'urn:ietf:params:xml:ns:xmpp-stanzas feature-not-implemented' 
 472  ERR_BAD_REQUEST = 'urn:ietf:params:xml:ns:xmpp-stanzas bad-request' 
 473  STREAM_INVALID_ID = 'urn:ietf:params:xml:ns:xmpp-streams invalid-id' 
 474  STREAM_HOST_UNKNOWN = 'urn:ietf:params:xml:ns:xmpp-streams host-unknown' 
 475  ERR_UNDEFINED_CONDITION = 'urn:ietf:params:xml:ns:xmpp-stanzas undefined-condition' 
 476  SASL_INVALID_MECHANISM = 'urn:ietf:params:xml:ns:xmpp-sasl invalid-mechanism' 
 477  STREAM_RESTRICTED_XML = 'urn:ietf:params:xml:ns:xmpp-streams restricted-xml' 
 478  ERR_RESOURCE_CONSTRAINT = 'urn:ietf:params:xml:ns:xmpp-stanzas resource-constraint' 
 479  ERR_REMOTE_SERVER_TIMEOUT = 'urn:ietf:params:xml:ns:xmpp-stanzas remote-server-timeout' 
 480  SASL_INVALID_AUTHZID = 'urn:ietf:params:xml:ns:xmpp-sasl invalid-authzid' 
 481  ERR_PAYMENT_REQUIRED = 'urn:ietf:params:xml:ns:xmpp-stanzas payment-required' 
 482  STREAM_INVALID_NAMESPACE = 'urn:ietf:params:xml:ns:xmpp-streams invalid-namespace' 
 483  ERR_REDIRECT = 'urn:ietf:params:xml:ns:xmpp-stanzas redirect' 
 484  STREAM_UNSUPPORTED_STANZA_TYPE = 'urn:ietf:params:xml:ns:xmpp-streams unsupported-stanza-type' 
 485  ERR_FORBIDDEN = 'urn:ietf:params:xml:ns:xmpp-stanzas forbidden' 
 486   
 488      """ 
 489      Return true if the node is a positive reply 
 490      """ 
 491      return node and node.getType() == 'result' 
  492   
 494      """ 
 495      Return true if the node is a negative reply 
 496      """ 
 497      return node and node.getType() == 'error' 
  498   
 500      """ 
 501      Exception that should be raised by handler when the handling should be 
 502      stopped 
 503      """ 
 504      pass 
  505   
 507      """ 
 508      Base exception class for stream errors 
 509      """ 
 510      pass 
  511   
 514   
 517   
 520   
 523   
 526   
 529   
 532   
 535   
 538   
 541   
 544   
 547   
 550   
 553   
 556   
 559   
 562   
 565   
 568   
 571   
 574   
 577   
 580   
 583   
 584  stream_exceptions = {'bad-format': BadFormat, 
 585                      'bad-namespace-prefix': BadNamespacePrefix, 
 586                      'conflict': Conflict, 
 587                      'connection-timeout': ConnectionTimeout, 
 588                      'host-gone': HostGone, 
 589                      'host-unknown': HostUnknown, 
 590                      'improper-addressing': ImproperAddressing, 
 591                      'internal-server-error': InternalServerError, 
 592                      'invalid-from': InvalidFrom, 
 593                      'invalid-id': InvalidID, 
 594                      'invalid-namespace': InvalidNamespace, 
 595                      'invalid-xml': InvalidXML, 
 596                      'not-authorized': NotAuthorized, 
 597                      'policy-violation': PolicyViolation, 
 598                      'remote-connection-failed': RemoteConnectionFailed, 
 599                      'resource-constraint': ResourceConstraint, 
 600                      'restricted-xml': RestrictedXML, 
 601                      'see-other-host': SeeOtherHost, 
 602                      'system-shutdown': SystemShutdown, 
 603                      'undefined-condition': UndefinedCondition, 
 604                      'unsupported-encoding': UnsupportedEncoding, 
 605                      'unsupported-stanza-type': UnsupportedStanzaType, 
 606                      'unsupported-version': UnsupportedVersion, 
 607                      'xml-not-well-formed': XMLNotWellFormed} 
 608   
 610      """ 
 611      JID can be built from string, modified, compared, serialised into string 
 612      """ 
 613   
 614 -    def __init__(self, jid=None, node='', domain='', resource=''): 
  615          """ 
 616          JID can be specified as string (jid argument) or as separate parts 
 617   
 618          Examples: 
 619          JID('node@domain/resource') 
 620          JID(node='node',domain='domain.org') 
 621          """ 
 622          if not jid and not domain: 
 623              raise ValueError('JID must contain at least domain name') 
 624          elif type(jid) == type(self): 
 625              self.node, self.domain = jid.node, jid.domain 
 626              self.resource = jid.resource 
 627          elif domain: 
 628              self.node, self.domain, self.resource = node, domain, resource 
 629          else: 
 630              if jid.find('@') + 1: 
 631                  self.node, jid = jid.split('@', 1) 
 632              else: 
 633                  self.node = '' 
 634              if jid.find('/')+1: 
 635                  self.domain, self.resource = jid.split('/', 1) 
 636              else: 
 637                  self.domain, self.resource = jid, '' 
  638   
 640          """ 
 641          Return the node part of the JID 
 642          """ 
 643          return self.node 
  644   
 646          """ 
 647          Set the node part of the JID to new value. Specify None to remove 
 648          the node part 
 649          """ 
 650          self.node = node.lower() 
  651   
 652 -    def getDomain(self): 
  653          """ 
 654          Return the domain part of the JID 
 655          """ 
 656          return self.domain 
  657   
 658 -    def setDomain(self, domain): 
  659          """ 
 660          Set the domain part of the JID to new value 
 661          """ 
 662          self.domain = domain.lower() 
  663   
 665          """ 
 666          Return the resource part of the JID 
 667          """ 
 668          return self.resource 
  669   
 671          """ 
 672          Set the resource part of the JID to new value. Specify None to remove the 
 673          resource part 
 674          """ 
 675          self.resource = resource 
  676   
 678          """ 
 679          Return the bare representation of JID. I.e. string value w/o resource 
 680          """ 
 681          return self.__str__(0) 
  682   
 684          """ 
 685          Compare the JID to another instance or to string for equality 
 686          """ 
 687          try: 
 688              other = JID(other) 
 689          except ValueError: 
 690              return 0 
 691          return self.resource == other.resource and \ 
 692              self.__str__(0) == other.__str__(0) 
  693   
 695          """ 
 696          Compare the JID to another instance or to string for non-equality 
 697          """ 
 698          return not self.__eq__(other) 
  699   
 701          """ 
 702          Compare the node and domain parts of the JID's for equality 
 703          """ 
 704          return self.__str__(0) == JID(other).__str__(0) 
  705   
 707          """ 
 708          Serialise JID into string 
 709          """ 
 710          if self.node: 
 711              jid = self.node + '@' + self.domain 
 712          else: 
 713              jid = self.domain 
 714          if wresource and self.resource: 
 715              return jid + '/' + self.resource 
 716          return jid 
  717   
 719          """ 
 720          Produce hash of the JID, Allows to use JID objects as keys of the 
 721          dictionary 
 722          """ 
 723          return hash(str(self)) 
   724   
 725 -class BOSHBody(Node): 
  726      """ 
 727      <body> tag that wraps usual XMPP stanzas in XMPP over BOSH 
 728      """ 
 729   
 730 -    def __init__(self, attrs={}, payload=[], node=None): 
  731          Node.__init__(self, tag='body', attrs=attrs, payload=payload, node=node) 
 732          self.setNamespace(NS_HTTP_BIND) 
   733   
 734   
 736      """ 
 737      A "stanza" object class. Contains methods that are common for presences, iqs 
 738      and messages 
 739      """ 
 740   
 741 -    def __init__(self, name=None, to=None, typ=None, frm=None, attrs={}, 
 742                      payload=[], timestamp=None, xmlns=None, node=None): 
  743          """ 
 744          Constructor, name is the name of the stanza 
 745          i.e. 'message' or 'presence'or 'iq' 
 746   
 747          to is the value of 'to' attribure, 'typ' - 'type' attribute 
 748          frn - from attribure, attrs - other attributes mapping, 
 749          payload - same meaning as for simplexml payload definition 
 750          timestamp - the time value that needs to be stamped over stanza 
 751          xmlns - namespace of top stanza node 
 752          node - parsed or unparsed stana to be taken as prototype. 
 753          """ 
 754          if not attrs: 
 755              attrs = {} 
 756          if to: 
 757              attrs['to'] = to 
 758          if frm: 
 759              attrs['from'] = frm 
 760          if typ: 
 761              attrs['type'] = typ 
 762          Node.__init__(self, tag=name, attrs=attrs, payload=payload, node=node) 
 763          if not node and xmlns: 
 764              self.setNamespace(xmlns) 
 765          if self['to']: 
 766              self.setTo(self['to']) 
 767          if self['from']: 
 768              self.setFrom(self['from']) 
 769          if node and type(self) == type(node) and \ 
 770          self.__class__ == node.__class__ and self.attrs.has_key('id'): 
 771              del self.attrs['id'] 
 772          self.timestamp = None 
 773          for d in self.getTags('delay', namespace=NS_DELAY2): 
 774              try: 
 775                  if d.getAttr('stamp') < self.getTimestamp2(): 
 776                      self.setTimestamp(d.getAttr('stamp')) 
 777              except Exception: 
 778                  pass 
 779          if not self.timestamp: 
 780              for x in self.getTags('x', namespace=NS_DELAY): 
 781                  try: 
 782                      if x.getAttr('stamp') < self.getTimestamp(): 
 783                          self.setTimestamp(x.getAttr('stamp')) 
 784                  except Exception: 
 785                      pass 
 786          if timestamp is not None: 
 787              self.setTimestamp(timestamp)   
  788   
 790          """ 
 791          Return value of the 'to' attribute 
 792          """ 
 793          try: 
 794              return self['to'] 
 795          except: 
 796              return None 
  797   
 799          """ 
 800          Return value of the 'from' attribute 
 801          """ 
 802          try: 
 803              return self['from'] 
 804          except: 
 805              return None 
  806   
 808          """ 
 809          Return the timestamp in the 'yyyymmddThhmmss' format 
 810          """ 
 811          if self.timestamp: 
 812              return self.timestamp 
 813          return time.strftime('%Y%m%dT%H:%M:%S', time.gmtime()) 
  814   
 816          """ 
 817          Return the timestamp in the 'yyyymmddThhmmss' format 
 818          """ 
 819          if self.timestamp: 
 820              return self.timestamp 
 821          return time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) 
  822   
 824          """ 
 825          Return the value of the 'id' attribute 
 826          """ 
 827          return self.getAttr('id') 
  828   
 830          """ 
 831          Set the value of the 'to' attribute 
 832          """ 
 833          self.setAttr('to', JID(val)) 
  834   
 836          """ 
 837          Return the value of the 'type' attribute 
 838          """ 
 839          return self.getAttr('type') 
  840   
 842          """ 
 843          Set the value of the 'from' attribute 
 844          """ 
 845          self.setAttr('from', JID(val)) 
  846   
 848          """ 
 849          Set the value of the 'type' attribute 
 850          """ 
 851          self.setAttr('type', val) 
  852   
 854          """ 
 855          Set the value of the 'id' attribute 
 856          """ 
 857          self.setAttr('id', val) 
  858   
 860          """ 
 861          Return the error-condition (if present) or the textual description 
 862          of the error (otherwise) 
 863          """ 
 864          errtag = self.getTag('error') 
 865          if errtag: 
 866              for tag in errtag.getChildren(): 
 867                  if tag.getName() != 'text': 
 868                      return tag.getName() 
 869              return errtag.getData() 
  870   
 872          """ 
 873          Return the textual description of the error (if present) 
 874          or the error condition 
 875          """ 
 876          errtag = self.getTag('error') 
 877          if errtag: 
 878              for tag in errtag.getChildren(): 
 879                  if tag.getName() == 'text': 
 880                      return tag.getData() 
 881              return self.getError() 
  882   
 884          """ 
 885          Return the error code. Obsolete. 
 886          """ 
 887          return self.getTagAttr('error', 'code') 
  888   
 890          """ 
 891          Return the status conditions list as defined in XEP-0306. 
 892          """ 
 893          conds = [] 
 894          condtag = self.getTag('conditions', namespace=NS_CONDITIONS) 
 895          if condtag: 
 896              for tag in condtag.getChildren(): 
 897                  conds.append(tag.getName()) 
 898          return conds 
  899   
 914   
 916          """ 
 917          Set the timestamp. timestamp should be the yyyymmddThhmmss string 
 918          """ 
 919          if not val: 
 920              val = time.strftime('%Y%m%dT%H:%M:%S', time.gmtime()) 
 921          self.timestamp=val 
 922          self.setTag('x', {'stamp': self.timestamp}, namespace=NS_DELAY) 
  923   
 925          """ 
 926          Return the list of namespaces to which belongs the direct childs of element 
 927          """ 
 928          props = [] 
 929          for child in self.getChildren(): 
 930              prop = child.getNamespace() 
 931              if prop not in props: 
 932                  props.append(prop) 
 933          return props 
  934   
 936          """ 
 937          Set the item 'item' to the value 'val' 
 938          """ 
 939          if item in ['to', 'from']: 
 940              val = JID(val) 
 941          return self.setAttr(item, val) 
   942   
 943   
 945      """ 
 946      XMPP Message stanza - "push" mechanism 
 947      """ 
 948   
 949 -    def __init__(self, to=None, body=None, xhtml=None, typ=None, subject=None, 
 950          attrs={}, frm=None, payload=[], timestamp=None, xmlns=NS_CLIENT, 
 951          node=None): 
  952          """ 
 953          You can specify recipient, text of message, type of message any 
 954          additional attributes, sender of the message, any additional payload 
 955          (f.e. jabber:x:delay element) and namespace in one go. 
 956   
 957          Alternatively you can pass in the other XML object as the 'node' 
 958          parameted to replicate it as message 
 959          """ 
 960          Protocol.__init__(self, 'message', to=to, typ=typ, attrs=attrs, frm=frm, 
 961                  payload=payload, timestamp=timestamp, xmlns=xmlns, node=node) 
 962          if body: 
 963              self.setBody(body) 
 964          if xhtml: 
 965              self.setXHTML(xhtml) 
 966          if subject is not None: 
 967              self.setSubject(subject) 
  968   
 970          """ 
 971          Return text of the message 
 972          """ 
 973          return self.getTagData('body') 
  974   
 976          """ 
 977          Return serialized xhtml-im element text of the message 
 978   
 979          TODO: Returning a DOM could make rendering faster. 
 980          """ 
 981          xhtml = self.getTag('html') 
 982          if xhtml: 
 983              if xmllang: 
 984                  body = xhtml.getTag('body', attrs={'xml:lang': xmllang}) 
 985              else: 
 986                  body = xhtml.getTag('body') 
 987              return str(body) 
 988          return None 
  989   
 991          """ 
 992          Return subject of the message 
 993          """ 
 994          return self.getTagData('subject') 
  995   
 997          """ 
 998          Return thread of the message 
 999          """ 
1000          return self.getTagData('thread') 
 1001   
1002 -    def setBody(self, val): 
 1003          """ 
1004          Set the text of the message""" 
1005          self.setTagData('body', val) 
 1006   
1007 -    def setXHTML(self, val, xmllang=None): 
 1008          """ 
1009          Sets the xhtml text of the message (XEP-0071). The parameter is the 
1010          "inner html" to the body. 
1011          """ 
1012          try: 
1013              if xmllang: 
1014                  dom = NodeBuilder('<body xmlns="%s" xml:lang="%s">%s</body>' \ 
1015                      % (NS_XHTML, xmllang, val)).getDom() 
1016              else: 
1017                  dom = NodeBuilder('<body xmlns="%s">%s</body>' % (NS_XHTML, 
1018                      val), 0).getDom() 
1019              if self.getTag('html'): 
1020                  self.getTag('html').addChild(node=dom) 
1021              else: 
1022                  self.setTag('html', namespace=NS_XHTML_IM).addChild(node=dom) 
1023          except Exception, e: 
1024              print "Error", e 
 1025               
1026   
1028          """ 
1029          Set the subject of the message 
1030          """ 
1031          self.setTagData('subject', val) 
 1032   
1034          """ 
1035          Set the thread of the message 
1036          """ 
1037          self.setTagData('thread', val) 
 1038   
1040          """ 
1041          Builds and returns another message object with specified text. The to, 
1042          from, thread and type properties of new message are pre-set as reply to 
1043          this message 
1044          """ 
1045          m = Message(to=self.getFrom(), frm=self.getTo(), body=text, 
1046              typ=self.getType()) 
1047          th = self.getThread() 
1048          if th: 
1049              m.setThread(th) 
1050          return m 
 1051   
1053          """ 
1054          Return the status code of the message (for groupchat config change) 
1055          """ 
1056          attrs = [] 
1057          for xtag in self.getTags('x'): 
1058              for child in xtag.getTags('status'): 
1059                  attrs.append(child.getAttr('code')) 
1060          return attrs 
  1061   
1063   
1064 -    def __init__(self, to=None, typ=None, priority=None, show=None, status=None, 
1065          attrs={}, frm=None, timestamp=None, payload=[], xmlns=NS_CLIENT, 
1066          node=None): 
 1067          """ 
1068          You can specify recipient, type of message, priority, show and status 
1069          values any additional attributes, sender of the presence, timestamp, any 
1070          additional payload (f.e. jabber:x:delay element) and namespace in one go. 
1071          Alternatively you can pass in the other XML object as the 'node' 
1072          parameted to replicate it as presence 
1073          """ 
1074          Protocol.__init__(self, 'presence', to=to, typ=typ, attrs=attrs, frm=frm, 
1075                  payload=payload, timestamp=timestamp, xmlns=xmlns, node=node) 
1076          if priority: 
1077              self.setPriority(priority) 
1078          if show: 
1079              self.setShow(show) 
1080          if status: 
1081              self.setStatus(status) 
 1082   
1084          """ 
1085          Return the priority of the message 
1086          """ 
1087          return self.getTagData('priority') 
 1088   
1090          """ 
1091          Return the show value of the message 
1092          """ 
1093          return self.getTagData('show') 
 1094   
1096          """ 
1097          Return the status string of the message 
1098          """ 
1099          return self.getTagData('status') 
 1100   
1102          """ 
1103          Set the priority of the message 
1104          """ 
1105          self.setTagData('priority', val) 
 1106   
1108          """ 
1109          Set the show value of the message 
1110          """ 
1111          self.setTagData('show', val) 
 1112   
1114          """ 
1115          Set the status string of the message 
1116          """ 
1117          self.setTagData('status', val) 
 1118   
1125   
1134   
1136          """ 
1137          Return the presence role (for groupchat) 
1138          """ 
1139          return self._muc_getItemAttr('item', 'role') 
 1140   
1142          """ 
1143          Return the presence affiliation (for groupchat) 
1144          """ 
1145          return self._muc_getItemAttr('item', 'affiliation') 
 1146   
1148          """ 
1149          Return the status code of the presence (for groupchat) 
1150          """ 
1151          return self._muc_getItemAttr('item', 'nick') 
 1152   
1154          """ 
1155          Return the presence jid (for groupchat) 
1156          """ 
1157          return self._muc_getItemAttr('item', 'jid') 
 1158   
1160          """ 
1161          Returns the reason of the presence (for groupchat) 
1162          """ 
1163          return self._muc_getSubTagDataAttr('reason', '')[0] 
 1164   
1166          """ 
1167          Return the reason of the presence (for groupchat) 
1168          """ 
1169          return self._muc_getSubTagDataAttr('actor', 'jid')[1] 
 1170   
1172          """ 
1173          Return the status code of the presence (for groupchat) 
1174          """ 
1175          attrs = [] 
1176          for xtag in self.getTags('x'): 
1177              for child in xtag.getTags('status'): 
1178                  attrs.append(child.getAttr('code')) 
1179          return attrs 
  1180   
1181 -class Iq(Protocol): 
 1182      """ 
1183      XMPP Iq object - get/set dialog mechanism 
1184      """ 
1185   
1186 -    def __init__(self, typ=None, queryNS=None, attrs={}, to=None, frm=None, 
1187                      payload=[], xmlns=NS_CLIENT, node=None): 
 1188          """ 
1189          You can specify type, query namespace any additional attributes, 
1190          recipient of the iq, sender of the iq, any additional payload (f.e. 
1191          jabber:x:data node) and namespace in one go. 
1192   
1193          Alternatively you can pass in the other XML object as the 'node' 
1194          parameted to replicate it as an iq 
1195          """ 
1196          Protocol.__init__(self, 'iq', to=to, typ=typ, attrs=attrs, frm=frm, 
1197              xmlns=xmlns, node=node) 
1198          if payload: 
1199              self.setQueryPayload(payload) 
1200          if queryNS: 
1201              self.setQueryNS(queryNS) 
 1202   
1204          """ 
1205          Return the IQ's child element if it exists, None otherwise. 
1206          """ 
1207          children = self.getChildren() 
1208          if children and self.getType() != 'error' and \ 
1209          children[0].getName() != 'error': 
1210              return children[0] 
 1211   
1213          """ 
1214          Return the namespace of the 'query' child element 
1215          """ 
1216          tag = self.getQuery() 
1217          if tag: 
1218              return tag.getNamespace() 
 1219   
1221          """ 
1222          Return the 'node' attribute value of the 'query' child element 
1223          """ 
1224          tag = self.getQuery() 
1225          if tag: 
1226              return tag.getAttr('node') 
 1227   
1229          """ 
1230          Return the 'query' child element payload 
1231          """ 
1232          tag = self.getQuery() 
1233          if tag: 
1234              return tag.getPayload() 
 1235   
1237          """ 
1238          Return the 'query' child element child nodes 
1239          """ 
1240          tag = self.getQuery() 
1241          if tag: 
1242              return tag.getChildren() 
 1243   
1245          """ 
1246          Change the name of the query node, creating it if needed. Keep the 
1247          existing name if none is given (use 'query' if it's a creation). 
1248          Return the query node. 
1249          """ 
1250          query = self.getQuery() 
1251          if query is None: 
1252              query = self.addChild('query') 
1253          if name is not None: 
1254              query.setName(name) 
1255          return query 
 1256   
1258          """ 
1259          Set the namespace of the 'query' child element 
1260          """ 
1261          self.setQuery().setNamespace(namespace) 
 1262   
1264          """ 
1265          Set the 'query' child element payload 
1266          """ 
1267          self.setQuery().setPayload(payload) 
 1268   
1270          """ 
1271          Set the 'node' attribute value of the 'query' child element 
1272          """ 
1273          self.setQuery().setAttr('node', node) 
 1274   
1276          """ 
1277          Build and return another Iq object of specified type. The to, from and 
1278          query child node of new Iq are pre-set as reply to this Iq. 
1279          """ 
1280          iq = Iq(typ, to=self.getFrom(), frm=self.getTo(), 
1281              attrs={'id': self.getID()}) 
1282          iq.setQuery(self.getQuery().getName()).setNamespace(self.getQueryNS()) 
1283          return iq 
  1284   
1286      """ 
1287      Hash elements for various XEPs as defined in XEP-300 
1288      """ 
1289   
1290      """ 
1291      RECOMENDED HASH USE: 
1292      Algorithm     Support 
1293      MD2           MUST NOT 
1294      MD4           MUST NOT 
1295      MD5           MAY 
1296      SHA-1         MUST 
1297      SHA-256       MUST 
1298      SHA-512       SHOULD 
1299      """ 
1300   
1301      supported = ('md5', 'sha-1', 'sha-256', 'sha-512') 
1302   
1307   
1309          """ 
1310          Calculate the hash and add it. It is preferable doing it here 
1311          instead of doing it all over the place in Gajim. 
1312          """ 
1313          hl = None 
1314          hash_ = None 
1315           
1316          if type(file_string) == str:  
1317              if algo == 'sha-1': 
1318                  hl = hashlib.sha1() 
1319              elif algo == 'md5': 
1320                  hl = hashlib.md5() 
1321              elif algo == 'sha-256': 
1322                  hl = hashlib.sha256() 
1323              elif algo == 'sha-512': 
1324                  hl = hashlib.sha512() 
1325              if hl: 
1326                  hl.update(file_string) 
1327                  hash_ = hl.hexdigest() 
1328          else:  
1329              if algo == 'sha-1': 
1330                  hl = hashlib.sha1() 
1331              elif algo == 'md5': 
1332                  hl = hashlib.md5() 
1333              elif algo == 'sha-256': 
1334                  hl = hashlib.sha256() 
1335              elif algo == 'sha-512': 
1336                  hl = hashlib.sha512() 
1337              if hl: 
1338                  for line in file_string: 
1339                      hl.update(line) 
1340                  hash_ = hl.hexdigest() 
1341          return hash_ 
 1342   
 1346   
1348      """ 
1349      Acknowledgement elements for Stream Management 
1350      """ 
1354   
1356          """ 
1357          handled is the number of stanzas handled 
1358          """ 
1359          self.setName('a') 
1360          self.setAttr('h', handled) 
 1361   
1364   
1366          self.setName('enable') 
1367          if resume: 
1368              self.setAttr('resume', 'true') 
 1369   
 1374   
1376      """ 
1377      XMPP-style error element 
1378   
1379      In the case of stanza error should be attached to XMPP stanza. 
1380      In the case of stream-level errors should be used separately. 
1381      """ 
1382   
1383 -    def __init__(self, name, code=None, typ=None, text=None): 
 1384          """ 
1385          Mandatory parameter: name - name of error condition. 
1386          Optional parameters: code, typ, text. 
1387          Used for backwards compartibility with older jabber protocol. 
1388          """ 
1389          if name in ERRORS: 
1390              cod, type_, txt = ERRORS[name] 
1391              ns = name.split()[0] 
1392          else: 
1393              cod, ns, type_, txt = '500', NS_STANZAS, 'cancel', '' 
1394          if typ: 
1395              type_ = typ 
1396          if code: 
1397              cod = code 
1398          if text: 
1399              txt = text 
1400          Node.__init__(self, 'error', {}, [Node(name)]) 
1401          if type_: 
1402              self.setAttr('type', type_) 
1403          if not cod: 
1404              self.setName('stream:error') 
1405          if txt: 
1406              self.addChild(node=Node(ns + ' text', {}, [txt])) 
1407          if cod: 
1408              self.setAttr('code', cod) 
  1409   
1411      """ 
1412      Used to quickly transform received stanza into error reply 
1413      """ 
1414   
1415 -    def __init__(self, node, error, reply=1): 
 1416          """ 
1417          Create error reply basing on the received 'node' stanza and the 'error' 
1418          error condition 
1419   
1420          If the 'node' is not the received stanza but locally created ('to' and 
1421          'from' fields needs not swapping) specify the 'reply' argument as false. 
1422          """ 
1423          if reply: 
1424              Protocol.__init__(self, to=node.getFrom(), frm=node.getTo(), node=node) 
1425          else: 
1426              Protocol.__init__(self, node=node) 
1427          self.setError(error) 
1428          if node.getType() == 'error': 
1429              self.__str__ = self.__dupstr__ 
 1430   
1432          """ 
1433          Dummy function used as preventor of creating error node in reply to error 
1434          node. I.e. you will not be able to serialise "double" error into string. 
1435          """ 
1436          return '' 
  1437   
1439      """ 
1440      This class is used in the DataForm class to describe the single data item 
1441   
1442      If you are working with jabber:x:data (XEP-0004, XEP-0068, XEP-0122) then 
1443      you will need to work with instances of this class. 
1444      """ 
1445   
1446 -    def __init__(self, name=None, value=None, typ=None, required=0, desc=None, 
1447                      options=[], node=None): 
 1448          """ 
1449          Create new data field of specified name,value and type 
1450   
1451          Also 'required','desc' and 'options' fields can be set. Alternatively 
1452          other XML object can be passed in as the 'node' parameted 
1453          to replicate it as a new datafiled. 
1454          """ 
1455          Node.__init__(self, 'field', node=node) 
1456          if name: 
1457              self.setVar(name) 
1458          if isinstance(value, (list, tuple)): 
1459              self.setValues(value) 
1460          elif value: 
1461              self.setValue(value) 
1462          if typ: 
1463              self.setType(typ) 
1464          elif not typ and not node: 
1465              self.setType('text-single') 
1466          if required: 
1467              self.setRequired(required) 
1468          if desc: 
1469              self.setDesc(desc) 
1470          if options: 
1471              self.setOptions(options) 
 1472   
1474          """ 
1475          Change the state of the 'required' flag 
1476          """ 
1477          if req: 
1478              self.setTag('required') 
1479          else: 
1480              try: 
1481                  self.delChild('required') 
1482              except ValueError: 
1483                  return 
 1484   
1486          """ 
1487          Return in this field a required one 
1488          """ 
1489          return self.getTag('required') 
 1490   
1492          """ 
1493          Set the description of this field 
1494          """ 
1495          self.setTagData('desc', desc) 
 1496   
1498          """ 
1499          Return the description of this field 
1500          """ 
1501          return self.getTagData('desc') 
 1502   
1504          """ 
1505          Set the value of this field 
1506          """ 
1507          self.setTagData('value', val) 
 1508   
1511   
1513          """ 
1514          Set the values of this field as values-list. Replaces all previous filed 
1515          values! If you need to just add a value - use addValue method 
1516          """ 
1517          while self.getTag('value'): 
1518              self.delChild('value') 
1519          for val in lst: 
1520              self.addValue(val) 
 1521   
1523          """ 
1524          Add one more value to this field. Used in 'get' iq's or such 
1525          """ 
1526          self.addChild('value', {}, [val]) 
 1527   
1529          """ 
1530          Return the list of values associated with this field 
1531          """ 
1532          ret = [] 
1533          for tag in self.getTags('value'): 
1534              ret.append(tag.getData()) 
1535          return ret 
 1536   
1538          """ 
1539          Return label-option pairs list associated with this field 
1540          """ 
1541          ret = [] 
1542          for tag in self.getTags('option'): 
1543              ret.append([tag.getAttr('label'), tag.getTagData('value')]) 
1544          return ret 
 1545   
1547          """ 
1548          Set label-option pairs list associated with this field 
1549          """ 
1550          while self.getTag('option'): 
1551              self.delChild('option') 
1552          for opt in lst: 
1553              self.addOption(opt) 
 1554   
1556          """ 
1557          Add one more label-option pair to this field 
1558          """ 
1559          if isinstance(opt, basestring): 
1560              self.addChild('option').setTagData('value', opt) 
1561          else: 
1562              self.addChild('option', {'label': opt[0]}).setTagData('value', 
1563                  opt[1]) 
 1564   
1566          """ 
1567          Get type of this field 
1568          """ 
1569          return self.getAttr('type') 
 1570   
1572          """ 
1573          Set type of this field 
1574          """ 
1575          return self.setAttr('type', val) 
 1576   
1578          """ 
1579          Get 'var' attribute value of this field 
1580          """ 
1581          return self.getAttr('var') 
 1582   
1584          """ 
1585          Set 'var' attribute value of this field 
1586          """ 
1587          return self.setAttr('var', val) 
  1588   
1729