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
26 NS_ACTIVITY ='http://jabber.org/protocol/activity'
27 NS_ADDRESS ='http://jabber.org/protocol/address'
28 NS_AGENTS ='jabber:iq:agents'
29 NS_AMP ='http://jabber.org/protocol/amp'
30 NS_AMP_ERRORS =NS_AMP+'#errors'
31 NS_ATOM ='http://www.w3.org/2005/Atom'
32 NS_AUTH ='jabber:iq:auth'
33 NS_AVATAR ='http://www.xmpp.org/extensions/xep-0084.html#ns-metadata'
34 NS_BIND ='urn:ietf:params:xml:ns:xmpp-bind'
35 NS_BOB ='urn:xmpp:bob'
36 NS_BROWSE ='jabber:iq:browse'
37 NS_BROWSING ='http://jabber.org/protocol/browsing'
38 NS_BYTESTREAM ='http://jabber.org/protocol/bytestreams'
39 NS_CAPS ='http://jabber.org/protocol/caps'
40 NS_CAPTCHA ='urn:xmpp:captcha'
41 NS_CHATSTATES ='http://jabber.org/protocol/chatstates'
42 NS_CHATTING ='http://jabber.org/protocol/chatting'
43 NS_CLIENT ='jabber:client'
44 NS_COMMANDS ='http://jabber.org/protocol/commands'
45 NS_COMPONENT_ACCEPT='jabber:component:accept'
46 NS_COMPONENT_1 ='http://jabberd.jabberstudio.org/ns/component/1.0'
47 NS_COMPRESS ='http://jabber.org/protocol/compress'
48 NS_CONFERENCE ='jabber:x:conference'
49 NS_DATA ='jabber:x:data'
50 NS_DATA_MEDIA ='urn:xmpp:media-element'
51 NS_DELAY ='jabber:x:delay'
52 NS_DELAY2 ='urn:xmpp:delay'
53 NS_DIALBACK ='jabber:server:dialback'
54 NS_DISCO ='http://jabber.org/protocol/disco'
55 NS_DISCO_INFO =NS_DISCO+'#info'
56 NS_DISCO_ITEMS =NS_DISCO+'#items'
57 NS_ENCRYPTED ='jabber:x:encrypted'
58 NS_ESESSION ='http://www.xmpp.org/extensions/xep-0116.html#ns'
59 NS_ESESSION_INIT='http://www.xmpp.org/extensions/xep-0116.html#ns-init'
60 NS_EVENT ='jabber:x:event'
61 NS_FEATURE ='http://jabber.org/protocol/feature-neg'
62 NS_FILE ='http://jabber.org/protocol/si/profile/file-transfer'
63 NS_GAMING ='http://jabber.org/protocol/gaming'
64 NS_GEOLOC ='http://jabber.org/protocol/geoloc'
65 NS_GROUPCHAT ='gc-1.0'
66 NS_HTTP_AUTH ='http://jabber.org/protocol/http-auth'
67 NS_HTTP_BIND ='http://jabber.org/protocol/httpbind'
68 NS_IBB ='http://jabber.org/protocol/ibb'
69 NS_INVISIBLE ='presence-invisible'
70 NS_IQ ='iq'
71 NS_JINGLE ='urn:xmpp:jingle:1'
72 NS_JINGLE_ERRORS='urn:xmpp:jingle:errors:1'
73 NS_JINGLE_RTP ='urn:xmpp:jingle:apps:rtp:1'
74 NS_JINGLE_RTP_AUDIO='urn:xmpp:jingle:apps:rtp:audio'
75 NS_JINGLE_RTP_VIDEO='urn:xmpp:jingle:apps:rtp:video'
76 NS_JINGLE_RAW_UDP='urn:xmpp:jingle:transports:raw-udp:1'
77 NS_JINGLE_ICE_UDP='urn:xmpp:jingle:transports:ice-udp:1'
78 NS_LAST ='jabber:iq:last'
79 NS_LOCATION ='http://jabber.org/protocol/geoloc'
80 NS_MESSAGE ='message'
81 NS_MOOD ='http://jabber.org/protocol/mood'
82 NS_MUC ='http://jabber.org/protocol/muc'
83 NS_MUC_USER =NS_MUC+'#user'
84 NS_MUC_ADMIN =NS_MUC+'#admin'
85 NS_MUC_OWNER =NS_MUC+'#owner'
86 NS_MUC_UNIQUE =NS_MUC+'#unique'
87 NS_MUC_CONFIG =NS_MUC+'#roomconfig'
88 NS_NICK ='http://jabber.org/protocol/nick'
89 NS_OFFLINE ='http://www.jabber.org/jeps/jep-0030.html'
90 NS_PHYSLOC ='http://jabber.org/protocol/physloc'
91 NS_PING ='urn:xmpp:ping'
92 NS_PRESENCE ='presence'
93 NS_PRIVACY ='jabber:iq:privacy'
94 NS_PRIVATE ='jabber:iq:private'
95 NS_PROFILE ='http://jabber.org/protocol/profile'
96 NS_PUBSUB ='http://jabber.org/protocol/pubsub'
97 NS_PUBSUB_EVENT = 'http://jabber.org/protocol/pubsub#event'
98 NS_PUBSUB_PUBLISH_OPTIONS = NS_PUBSUB + '#publish-options'
99 NS_PUBSUB_OWNER ='http://jabber.org/protocol/pubsub#owner'
100 NS_REGISTER ='jabber:iq:register'
101 NS_ROSTER ='jabber:iq:roster'
102 NS_ROSTERX ='http://jabber.org/protocol/rosterx'
103 NS_ROSTER_VER ='urn:xmpp:features:rosterver'
104 NS_RPC ='jabber:iq:rpc'
105 NS_SASL ='urn:ietf:params:xml:ns:xmpp-sasl'
106 NS_SECLABEL ='urn:xmpp:sec-label:0'
107 NS_SECLABEL_CATALOG ='urn:xmpp:sec-label:catalog:0'
108 NS_SEARCH ='jabber:iq:search'
109 NS_SERVER ='jabber:server'
110 NS_SESSION ='urn:ietf:params:xml:ns:xmpp-session'
111 NS_SI ='http://jabber.org/protocol/si'
112 NS_SI_PUB ='http://jabber.org/protocol/sipub'
113 NS_SIGNED ='jabber:x:signed'
114 NS_SSN ='urn:xmpp:ssn'
115 NS_STANZA_CRYPTO='http://www.xmpp.org/extensions/xep-0200.html#ns'
116 NS_STANZAS ='urn:ietf:params:xml:ns:xmpp-stanzas'
117 NS_STREAM ='http://affinix.com/jabber/stream'
118 NS_STREAMS ='http://etherx.jabber.org/streams'
119 NS_TIME ='jabber:iq:time'
120 NS_TIME_REVISED ='urn:xmpp:time'
121 NS_TLS ='urn:ietf:params:xml:ns:xmpp-tls'
122 NS_TUNE ='http://jabber.org/protocol/tune'
123 NS_VACATION ='http://jabber.org/protocol/vacation'
124 NS_VCARD ='vcard-temp'
125 NS_GMAILNOTIFY ='google:mail:notify'
126 NS_GTALKSETTING ='google:setting'
127 NS_VCARD_UPDATE =NS_VCARD+':x:update'
128 NS_VERSION ='jabber:iq:version'
129 NS_VIEWING ='http://jabber.org/protocol/viewing'
130 NS_WAITINGLIST ='http://jabber.org/protocol/waitinglist'
131 NS_XHTML_IM ='http://jabber.org/protocol/xhtml-im'
132 NS_XHTML = 'http://www.w3.org/1999/xhtml'
133 NS_DATA_LAYOUT ='http://jabber.org/protocol/xdata-layout'
134 NS_DATA_VALIDATE='http://jabber.org/protocol/xdata-validate'
135 NS_XMPP_STREAMS ='urn:ietf:params:xml:ns:xmpp-streams'
136 NS_RECEIPTS ='urn:xmpp:receipts'
137
138 xmpp_stream_error_conditions = '''
139 bad-format -- -- -- The entity has sent XML that cannot be processed.
140 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.
141 conflict -- -- -- The server is closing the active stream for this entity because a new stream has been initiated that conflicts with the existing stream.
142 connection-timeout -- -- -- The entity has not generated any traffic over the stream for some period of time.
143 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.
144 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.
145 improper-addressing -- -- -- A stanza sent between two servers lacks a 'to' or 'from' attribute (or the attribute has no value).
146 internal-server-error -- -- -- The server has experienced a misconfiguration or an otherwise-undefined internal error that prevents it from servicing the stream.
147 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.
148 invalid-id -- -- -- The stream ID or dialback ID is invalid or does not match an ID previously provided.
149 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".
150 invalid-xml -- -- -- The entity has sent invalid XML over the stream to a server that performs validation.
151 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.
152 policy-violation -- -- -- The entity has violated some local service policy.
153 remote-connection-failed -- -- -- The server is unable to properly connect to a remote resource that is required for authentication or authorization.
154 resource-constraint -- -- -- The server lacks the system resources necessary to service the stream.
155 restricted-xml -- -- -- The entity has attempted to send restricted XML features such as a comment, processing instruction, DTD, entity reference, or unescaped character.
156 see-other-host -- -- -- The server will not provide service to the initiating entity but is redirecting traffic to another host.
157 system-shutdown -- -- -- The server is being shut down and all active streams are being closed.
158 undefined-condition -- -- -- The error condition is not one of those defined by the other conditions in this list.
159 unsupported-encoding -- -- -- The initiating entity has encoded the stream in an encoding that is not supported by the server.
160 unsupported-stanza-type -- -- -- The initiating entity has sent a first-level child of the stream that is not supported by the server.
161 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.
162 xml-not-well-formed -- -- -- The initiating entity has sent XML that is not well-formed.'''
163
164 xmpp_stanza_error_conditions = '''
165 bad-request -- 400 -- modify -- The sender has sent XML that is malformed or that cannot be processed.
166 conflict -- 409 -- cancel -- Access cannot be granted because an existing resource or session exists with the same name or address.
167 feature-not-implemented -- 501 -- cancel -- The feature requested is not implemented by the recipient or server and therefore cannot be processed.
168 forbidden -- 403 -- auth -- The requesting entity does not possess the required permissions to perform the action.
169 gone -- 302 -- modify -- The recipient or server can no longer be contacted at this address.
170 internal-server-error -- 500 -- wait -- The server could not process the stanza because of a misconfiguration or an otherwise-undefined internal server error.
171 item-not-found -- 404 -- cancel -- The addressed JID or item requested cannot be found.
172 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.
173 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.
174 not-allowed -- 405 -- cancel -- The recipient or server does not allow any entity to perform the action.
175 not-authorized -- 401 -- auth -- The sender must provide proper credentials before being allowed to perform the action, or has provided improper credentials.
176 payment-required -- 402 -- auth -- The requesting entity is not authorized to access the requested service because payment is required.
177 recipient-unavailable -- 404 -- wait -- The intended recipient is temporarily unavailable.
178 redirect -- 302 -- modify -- The recipient or server is redirecting requests for this information to another entity.
179 registration-required -- 407 -- auth -- The requesting entity is not authorized to access the requested service because registration is required.
180 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.
181 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.
182 resource-constraint -- 500 -- wait -- The server or recipient lacks the system resources necessary to service the request.
183 service-unavailable -- 503 -- cancel -- The server or recipient does not currently provide the requested service.
184 subscription-required -- 407 -- auth -- The requesting entity is not authorized to access the requested service because a subscription is required.
185 undefined-condition -- 500 -- -- Undefined Condition
186 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).'''
187
188 sasl_error_conditions = '''
189 aborted -- -- -- The receiving entity acknowledges an <abort/> element sent by the initiating entity; sent in reply to the <abort/> element.
190 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.
191 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.
192 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.
193 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.
194 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.
195 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.'''
196
197 ERRORS, _errorcodes = {}, {}
198 for ns, errname, errpool in ((NS_XMPP_STREAMS, 'STREAM', xmpp_stream_error_conditions),
199 (NS_STANZAS, 'ERR', xmpp_stanza_error_conditions),
200 (NS_SASL, 'SASL', sasl_error_conditions)):
201 for err in errpool.split('\n')[1:]:
202 cond, code, typ, text = err.split(' -- ')
203 name = errname + '_' + cond.upper().replace('-', '_')
204 locals()[name] = ns + ' ' + cond
205 ERRORS[ns + ' ' + cond] = [code, typ, text]
206 if code:
207 _errorcodes[code] = cond
208 del ns, errname, errpool, err, cond, code, typ, text
209
211 """
212 Return true if the node is a positive reply
213 """
214 return node and node.getType() == 'result'
215
217 """
218 Return true if the node is a negative reply
219 """
220 return node and node.getType() == 'error'
221
223 """
224 Exception that should be raised by handler when the handling should be
225 stopped
226 """
227 pass
228
230 """
231 Base exception class for stream errors
232 """
233 pass
234
237
240
243
246
249
252
255
258
261
264
267
270
273
276
279
282
285
288
291
294
297
300
303
306
307 stream_exceptions = {'bad-format': BadFormat,
308 'bad-namespace-prefix': BadNamespacePrefix,
309 'conflict': Conflict,
310 'connection-timeout': ConnectionTimeout,
311 'host-gone': HostGone,
312 'host-unknown': HostUnknown,
313 'improper-addressing': ImproperAddressing,
314 'internal-server-error': InternalServerError,
315 'invalid-from': InvalidFrom,
316 'invalid-id': InvalidID,
317 'invalid-namespace': InvalidNamespace,
318 'invalid-xml': InvalidXML,
319 'not-authorized': NotAuthorized,
320 'policy-violation': PolicyViolation,
321 'remote-connection-failed': RemoteConnectionFailed,
322 'resource-constraint': ResourceConstraint,
323 'restricted-xml': RestrictedXML,
324 'see-other-host': SeeOtherHost,
325 'system-shutdown': SystemShutdown,
326 'undefined-condition': UndefinedCondition,
327 'unsupported-encoding': UnsupportedEncoding,
328 'unsupported-stanza-type': UnsupportedStanzaType,
329 'unsupported-version': UnsupportedVersion,
330 'xml-not-well-formed': XMLNotWellFormed}
331
333 """
334 JID can be built from string, modified, compared, serialised into string
335 """
336
337 - def __init__(self, jid=None, node='', domain='', resource=''):
338 """
339 JID can be specified as string (jid argument) or as separate parts
340
341 Examples:
342 JID('node@domain/resource')
343 JID(node='node',domain='domain.org')
344 """
345 if not jid and not domain:
346 raise ValueError('JID must contain at least domain name')
347 elif type(jid) == type(self):
348 self.node, self.domain, self.resource = jid.node, jid.domain, jid.resource
349 elif domain:
350 self.node, self.domain, self.resource = node, domain, resource
351 else:
352 if jid.find('@') + 1:
353 self.node, jid = jid.split('@', 1)
354 else:
355 self.node = ''
356 if jid.find('/')+1:
357 self.domain, self.resource = jid.split('/', 1)
358 else:
359 self.domain, self.resource = jid, ''
360
362 """
363 Return the node part of the JID
364 """
365 return self.node
366
368 """
369 Set the node part of the JID to new value. Specify None to remove the node part
370 """
371 self.node = node.lower()
372
373 - def getDomain(self):
374 """
375 Return the domain part of the JID
376 """
377 return self.domain
378
379 - def setDomain(self, domain):
380 """
381 Set the domain part of the JID to new value
382 """
383 self.domain = domain.lower()
384
386 """
387 Return the resource part of the JID
388 """
389 return self.resource
390
392 """
393 Set the resource part of the JID to new value. Specify None to remove the
394 resource part
395 """
396 self.resource = resource
397
399 """
400 Return the bare representation of JID. I.e. string value w/o resource
401 """
402 return self.__str__(0)
403
405 """
406 Compare the JID to another instance or to string for equality
407 """
408 try:
409 other = JID(other)
410 except ValueError:
411 return 0
412 return self.resource == other.resource and self.__str__(0) == other.__str__(0)
413
415 """
416 Compare the JID to another instance or to string for non-equality
417 """
418 return not self.__eq__(other)
419
421 """
422 Compare the node and domain parts of the JID's for equality
423 """
424 return self.__str__(0) == JID(other).__str__(0)
425
427 """
428 Serialise JID into string
429 """
430 if self.node:
431 jid = self.node + '@' + self.domain
432 else:
433 jid = self.domain
434 if wresource and self.resource:
435 return jid + '/' + self.resource
436 return jid
437
439 """
440 Produce hash of the JID, Allows to use JID objects as keys of the dictionary
441 """
442 return hash(str(self))
443
444 -class BOSHBody(Node):
445 """
446 <body> tag that wraps usual XMPP stanzas in XMPP over BOSH
447 """
448
449 - def __init__(self, attrs={}, payload=[], node=None):
450 Node.__init__(self, tag='body', attrs=attrs, payload=payload, node=node)
451 self.setNamespace(NS_HTTP_BIND)
452
453
455 """
456 A "stanza" object class. Contains methods that are common for presences, iqs
457 and messages
458 """
459
460 - def __init__(self, name=None, to=None, typ=None, frm=None, attrs={},
461 payload=[], timestamp=None, xmlns=None, node=None):
462 """
463 Constructor, name is the name of the stanza i.e. 'message' or 'presence' or 'iq'
464
465 to is the value of 'to' attribure, 'typ' - 'type' attribute
466 frn - from attribure, attrs - other attributes mapping,
467 payload - same meaning as for simplexml payload definition
468 timestamp - the time value that needs to be stamped over stanza
469 xmlns - namespace of top stanza node
470 node - parsed or unparsed stana to be taken as prototype.
471 """
472 if not attrs:
473 attrs = {}
474 if to:
475 attrs['to'] = to
476 if frm:
477 attrs['from'] = frm
478 if typ:
479 attrs['type'] = typ
480 Node.__init__(self, tag=name, attrs=attrs, payload=payload, node=node)
481 if not node and xmlns:
482 self.setNamespace(xmlns)
483 if self['to']:
484 self.setTo(self['to'])
485 if self['from']:
486 self.setFrom(self['from'])
487 if node and type(self) == type(node) and self.__class__ == node.__class__ and self.attrs.has_key('id'):
488 del self.attrs['id']
489 self.timestamp = None
490 for d in self.getTags('delay', namespace=NS_DELAY2):
491 try:
492 if d.getAttr('stamp') < self.getTimestamp2():
493 self.setTimestamp(d.getAttr('stamp'))
494 except Exception:
495 pass
496 if not self.timestamp:
497 for x in self.getTags('x', namespace=NS_DELAY):
498 try:
499 if x.getAttr('stamp') < self.getTimestamp():
500 self.setTimestamp(x.getAttr('stamp'))
501 except Exception:
502 pass
503 if timestamp is not None:
504 self.setTimestamp(timestamp)
505
507 """
508 Return value of the 'to' attribute
509 """
510 try:
511 return self['to']
512 except:
513 return None
514
516 """
517 Return value of the 'from' attribute
518 """
519 try:
520 return self['from']
521 except:
522 return None
523
525 """
526 Return the timestamp in the 'yyyymmddThhmmss' format
527 """
528 if self.timestamp:
529 return self.timestamp
530 return time.strftime('%Y%m%dT%H:%M:%S', time.gmtime())
531
533 """
534 Return the timestamp in the 'yyyymmddThhmmss' format
535 """
536 if self.timestamp:
537 return self.timestamp
538 return time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
539
541 """
542 Return the value of the 'id' attribute
543 """
544 return self.getAttr('id')
545
547 """
548 Set the value of the 'to' attribute
549 """
550 self.setAttr('to', JID(val))
551
553 """
554 Return the value of the 'type' attribute
555 """
556 return self.getAttr('type')
557
559 """
560 Set the value of the 'from' attribute
561 """
562 self.setAttr('from', JID(val))
563
565 """
566 Set the value of the 'type' attribute
567 """
568 self.setAttr('type', val)
569
571 """
572 Set the value of the 'id' attribute
573 """
574 self.setAttr('id', val)
575
577 """
578 Return the error-condition (if present) or the textual description of the
579 error (otherwise)
580 """
581 errtag = self.getTag('error')
582 if errtag:
583 for tag in errtag.getChildren():
584 if tag.getName() <> 'text':
585 return tag.getName()
586 return errtag.getData()
587
589 """
590 Return the textual description of the error (if present) or the error condition
591 """
592 errtag = self.getTag('error')
593 if errtag:
594 for tag in errtag.getChildren():
595 if tag.getName() == 'text':
596 return tag.getData()
597 return self.getError()
598
600 """
601 Return the error code. Obsolete.
602 """
603 return self.getTagAttr('error', 'code')
604
618
620 """
621 Set the timestamp. timestamp should be the yyyymmddThhmmss string
622 """
623 if not val:
624 val = time.strftime('%Y%m%dT%H:%M:%S', time.gmtime())
625 self.timestamp=val
626 self.setTag('x', {'stamp': self.timestamp}, namespace=NS_DELAY)
627
629 """
630 Return the list of namespaces to which belongs the direct childs of element
631 """
632 props = []
633 for child in self.getChildren():
634 prop = child.getNamespace()
635 if prop not in props:
636 props.append(prop)
637 return props
638
640 """
641 Set the item 'item' to the value 'val'
642 """
643 if item in ['to', 'from']:
644 val = JID(val)
645 return self.setAttr(item, val)
646
647
649 """
650 XMPP Message stanza - "push" mechanism
651 """
652
653 - def __init__(self, to=None, body=None, xhtml=None, typ=None, subject=None,
654 attrs={}, frm=None, payload=[], timestamp=None, xmlns=NS_CLIENT,
655 node=None):
656 """
657 You can specify recipient, text of message, type of message any
658 additional attributes, sender of the message, any additional payload
659 (f.e. jabber:x:delay element) and namespace in one go.
660
661 Alternatively you can pass in the other XML object as the 'node'
662 parameted to replicate it as message
663 """
664 Protocol.__init__(self, 'message', to=to, typ=typ, attrs=attrs, frm=frm,
665 payload=payload, timestamp=timestamp, xmlns=xmlns, node=node)
666 if body:
667 self.setBody(body)
668 if xhtml:
669 self.setXHTML(xhtml)
670 if subject is not None:
671 self.setSubject(subject)
672
674 """
675 Return text of the message
676 """
677 return self.getTagData('body')
678
680 """
681 Return serialized xhtml-im element text of the message
682
683 TODO: Returning a DOM could make rendering faster.
684 """
685 xhtml = self.getTag('html')
686 if xhtml:
687 if xmllang:
688 body = xhtml.getTag('body', attrs={'xml:lang': xmllang})
689 else:
690 body = xhtml.getTag('body')
691 return str(body)
692 return None
693
695 """
696 Return subject of the message
697 """
698 return self.getTagData('subject')
699
701 """
702 Return thread of the message
703 """
704 return self.getTagData('thread')
705
706 - def setBody(self, val):
707 """
708 Set the text of the message"""
709 self.setTagData('body', val)
710
727
728
730 """
731 Set the subject of the message
732 """
733 self.setTagData('subject', val)
734
736 """
737 Set the thread of the message
738 """
739 self.setTagData('thread', val)
740
742 """
743 Builds and returns another message object with specified text. The to,
744 from and thread properties of new message are pre-set as reply to this
745 message
746 """
747 m = Message(to=self.getFrom(), frm=self.getTo(), body=text)
748 th = self.getThread()
749 if th:
750 m.setThread(th)
751 return m
752
754 """
755 Return the status code of the message (for groupchat config change)
756 """
757 attrs = []
758 for xtag in self.getTags('x'):
759 for child in xtag.getTags('status'):
760 attrs.append(child.getAttr('code'))
761 return attrs
762
764
765 - def __init__(self, to=None, typ=None, priority=None, show=None, status=None,
766 attrs={}, frm=None, timestamp=None, payload=[], xmlns=NS_CLIENT,
767 node=None):
768 """
769 You can specify recipient, type of message, priority, show and status
770 values any additional attributes, sender of the presence, timestamp, any
771 additional payload (f.e. jabber:x:delay element) and namespace in one go.
772 Alternatively you can pass in the other XML object as the 'node'
773 parameted to replicate it as presence
774 """
775 Protocol.__init__(self, 'presence', to=to, typ=typ, attrs=attrs, frm=frm,
776 payload=payload, timestamp=timestamp, xmlns=xmlns, node=node)
777 if priority:
778 self.setPriority(priority)
779 if show:
780 self.setShow(show)
781 if status:
782 self.setStatus(status)
783
785 """
786 Return the priority of the message
787 """
788 return self.getTagData('priority')
789
791 """
792 Return the show value of the message
793 """
794 return self.getTagData('show')
795
797 """
798 Return the status string of the message
799 """
800 return self.getTagData('status')
801
803 """
804 Set the priority of the message
805 """
806 self.setTagData('priority', val)
807
809 """
810 Set the show value of the message
811 """
812 self.setTagData('show', val)
813
815 """
816 Set the status string of the message
817 """
818 self.setTagData('status', val)
819
826
835
837 """
838 Return the presence role (for groupchat)
839 """
840 return self._muc_getItemAttr('item', 'role')
842 """
843 Return the presence affiliation (for groupchat)
844 """
845 return self._muc_getItemAttr('item', 'affiliation')
846
848 """
849 Return the status code of the presence (for groupchat)
850 """
851 return self._muc_getItemAttr('item', 'nick')
852
854 """
855 Return the presence jid (for groupchat)
856 """
857 return self._muc_getItemAttr('item', 'jid')
858
860 """
861 Returns the reason of the presence (for groupchat)
862 """
863 return self._muc_getSubTagDataAttr('reason', '')[0]
864
866 """
867 Return the reason of the presence (for groupchat)
868 """
869 return self._muc_getSubTagDataAttr('actor', 'jid')[1]
870
872 """
873 Return the status code of the presence (for groupchat)
874 """
875 attrs = []
876 for xtag in self.getTags('x'):
877 for child in xtag.getTags('status'):
878 attrs.append(child.getAttr('code'))
879 return attrs
880
882 """
883 XMPP Iq object - get/set dialog mechanism
884 """
885
886 - def __init__(self, typ=None, queryNS=None, attrs={}, to=None, frm=None,
887 payload=[], xmlns=NS_CLIENT, node=None):
888 """
889 You can specify type, query namespace any additional attributes,
890 recipient of the iq, sender of the iq, any additional payload (f.e.
891 jabber:x:data node) and namespace in one go.
892
893 Alternatively you can pass in the other XML object as the 'node'
894 parameted to replicate it as an iq
895 """
896 Protocol.__init__(self, 'iq', to=to, typ=typ, attrs=attrs, frm=frm, xmlns=xmlns, node=node)
897 if payload:
898 self.setQueryPayload(payload)
899 if queryNS:
900 self.setQueryNS(queryNS)
901
903 """
904 Return the namespace of the 'query' child element
905 """
906 tag = self.getTag('query')
907 if tag:
908 return tag.getNamespace()
909
911 """
912 Return the 'node' attribute value of the 'query' child element
913 """
914 return self.getTagAttr('query', 'node')
915
917 """
918 Return the 'query' child element payload
919 """
920 tag = self.getTag('query')
921 if tag:
922 return tag.getPayload()
923
925 """
926 Return the 'query' child element child nodes
927 """
928 tag = self.getTag('query')
929 if tag:
930 return tag.getChildren()
931
937
939 """
940 Set the 'query' child element payload
941 """
942 self.setTag('query').setPayload(payload)
943
945 """
946 Set the 'node' attribute value of the 'query' child element
947 """
948 self.setTagAttr('query', 'node', node)
949
951 """
952 Build and return another Iq object of specified type. The to, from and
953 query child node of new Iq are pre-set as reply to this Iq.
954 """
955 iq = Iq(typ, to=self.getFrom(), frm=self.getTo(), attrs={'id': self.getID()})
956 if self.getTag('query'):
957 iq.setQueryNS(self.getQueryNS())
958 return iq
959
961 """
962 XMPP-style error element
963
964 In the case of stanza error should be attached to XMPP stanza.
965 In the case of stream-level errors should be used separately.
966 """
967
968 - def __init__(self, name, code=None, typ=None, text=None):
969 """
970 Mandatory parameter: name - name of error condition.
971 Optional parameters: code, typ, text. Used for backwards compartibility with older jabber protocol.
972 """
973 if name in ERRORS:
974 cod, type_, txt = ERRORS[name]
975 ns = name.split()[0]
976 else:
977 cod, ns, type_, txt = '500', NS_STANZAS, 'cancel', ''
978 if typ:
979 type_ = typ
980 if code:
981 cod = code
982 if text:
983 txt = text
984 Node.__init__(self, 'error', {}, [Node(name)])
985 if type_:
986 self.setAttr('type', type_)
987 if not cod:
988 self.setName('stream:error')
989 if txt:
990 self.addChild(node=Node(ns + ' text', {}, [txt]))
991 if cod:
992 self.setAttr('code', cod)
993
995 """
996 Used to quickly transform received stanza into error reply
997 """
998
999 - def __init__(self, node, error, reply=1):
1000 """
1001 Create error reply basing on the received 'node' stanza and the 'error'
1002 error condition
1003
1004 If the 'node' is not the received stanza but locally created ('to' and
1005 'from' fields needs not swapping) specify the 'reply' argument as false.
1006 """
1007 if reply:
1008 Protocol.__init__(self, to=node.getFrom(), frm=node.getTo(), node=node)
1009 else:
1010 Protocol.__init__(self, node=node)
1011 self.setError(error)
1012 if node.getType() == 'error':
1013 self.__str__ = self.__dupstr__
1014
1016 """
1017 Dummy function used as preventor of creating error node in reply to error
1018 node. I.e. you will not be able to serialise "double" error into string.
1019 """
1020 return ''
1021
1023 """
1024 This class is used in the DataForm class to describe the single data item
1025
1026 If you are working with jabber:x:data (XEP-0004, XEP-0068, XEP-0122) then
1027 you will need to work with instances of this class.
1028 """
1029
1030 - def __init__(self, name=None, value=None, typ=None, required=0, desc=None,
1031 options=[], node=None):
1032 """
1033 Create new data field of specified name,value and type
1034
1035 Also 'required','desc' and 'options' fields can be set. Alternatively
1036 other XML object can be passed in as the 'node' parameted to replicate it
1037 as a new datafiled.
1038 """
1039 Node.__init__(self, 'field', node=node)
1040 if name:
1041 self.setVar(name)
1042 if isinstance(value, (list, tuple)):
1043 self.setValues(value)
1044 elif value:
1045 self.setValue(value)
1046 if typ:
1047 self.setType(typ)
1048 elif not typ and not node:
1049 self.setType('text-single')
1050 if required:
1051 self.setRequired(required)
1052 if desc:
1053 self.setDesc(desc)
1054 if options:
1055 self.setOptions(options)
1056
1058 """
1059 Change the state of the 'required' flag
1060 """
1061 if req:
1062 self.setTag('required')
1063 else:
1064 try:
1065 self.delChild('required')
1066 except ValueError:
1067 return
1068
1070 """
1071 Return in this field a required one
1072 """
1073 return self.getTag('required')
1074
1076 """
1077 Set the description of this field
1078 """
1079 self.setTagData('desc', desc)
1080
1082 """
1083 Return the description of this field
1084 """
1085 return self.getTagData('desc')
1086
1088 """
1089 Set the value of this field
1090 """
1091 self.setTagData('value', val)
1092
1095
1097 """
1098 Set the values of this field as values-list. Replaces all previous filed
1099 values! If you need to just add a value - use addValue method
1100 """
1101 while self.getTag('value'):
1102 self.delChild('value')
1103 for val in lst:
1104 self.addValue(val)
1105
1107 """
1108 Add one more value to this field. Used in 'get' iq's or such
1109 """
1110 self.addChild('value', {}, [val])
1111
1113 """
1114 Return the list of values associated with this field
1115 """
1116 ret = []
1117 for tag in self.getTags('value'):
1118 ret.append(tag.getData())
1119 return ret
1120
1122 """
1123 Return label-option pairs list associated with this field
1124 """
1125 ret = []
1126 for tag in self.getTags('option'):
1127 ret.append([tag.getAttr('label'), tag.getTagData('value')])
1128 return ret
1129
1131 """
1132 Set label-option pairs list associated with this field
1133 """
1134 while self.getTag('option'):
1135 self.delChild('option')
1136 for opt in lst:
1137 self.addOption(opt)
1138
1140 """
1141 Add one more label-option pair to this field
1142 """
1143 if isinstance(opt, basestring):
1144 self.addChild('option').setTagData('value', opt)
1145 else:
1146 self.addChild('option', {'label': opt[0]}).setTagData('value', opt[1])
1147
1149 """
1150 Get type of this field
1151 """
1152 return self.getAttr('type')
1153
1155 """
1156 Set type of this field
1157 """
1158 return self.setAttr('type', val)
1159
1161 """
1162 Get 'var' attribute value of this field
1163 """
1164 return self.getAttr('var')
1165
1167 """
1168 Set 'var' attribute value of this field
1169 """
1170 return self.setAttr('var', val)
1171
1312