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