summaryrefslogtreecommitdiff
path: root/src/xmpppy-0.5.0rc1/xmpp/protocol.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/xmpppy-0.5.0rc1/xmpp/protocol.py')
-rw-r--r--src/xmpppy-0.5.0rc1/xmpp/protocol.py860
1 files changed, 0 insertions, 860 deletions
diff --git a/src/xmpppy-0.5.0rc1/xmpp/protocol.py b/src/xmpppy-0.5.0rc1/xmpp/protocol.py
deleted file mode 100644
index 3e49b8d2..00000000
--- a/src/xmpppy-0.5.0rc1/xmpp/protocol.py
+++ /dev/null
@@ -1,860 +0,0 @@
-## protocol.py
-##
-## Copyright (C) 2003-2005 Alexey "Snake" Nezhdanov
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2, or (at your option)
-## any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-## GNU General Public License for more details.
-
-# $Id: protocol.py,v 1.60 2009/04/07 11:14:28 snakeru Exp $
-
-"""
-Protocol module contains tools that is needed for processing of
-xmpp-related data structures.
-"""
-
-from simplexml import Node,ustr
-import time
-NS_ACTIVITY ='http://jabber.org/protocol/activity' # XEP-0108
-NS_ADDRESS ='http://jabber.org/protocol/address' # XEP-0033
-NS_ADMIN ='http://jabber.org/protocol/admin' # XEP-0133
-NS_ADMIN_ADD_USER =NS_ADMIN+'#add-user' # XEP-0133
-NS_ADMIN_DELETE_USER =NS_ADMIN+'#delete-user' # XEP-0133
-NS_ADMIN_DISABLE_USER =NS_ADMIN+'#disable-user' # XEP-0133
-NS_ADMIN_REENABLE_USER =NS_ADMIN+'#reenable-user' # XEP-0133
-NS_ADMIN_END_USER_SESSION =NS_ADMIN+'#end-user-session' # XEP-0133
-NS_ADMIN_GET_USER_PASSWORD =NS_ADMIN+'#get-user-password' # XEP-0133
-NS_ADMIN_CHANGE_USER_PASSWORD =NS_ADMIN+'#change-user-password' # XEP-0133
-NS_ADMIN_GET_USER_ROSTER =NS_ADMIN+'#get-user-roster' # XEP-0133
-NS_ADMIN_GET_USER_LASTLOGIN =NS_ADMIN+'#get-user-lastlogin' # XEP-0133
-NS_ADMIN_USER_STATS =NS_ADMIN+'#user-stats' # XEP-0133
-NS_ADMIN_EDIT_BLACKLIST =NS_ADMIN+'#edit-blacklist' # XEP-0133
-NS_ADMIN_EDIT_WHITELIST =NS_ADMIN+'#edit-whitelist' # XEP-0133
-NS_ADMIN_REGISTERED_USERS_NUM =NS_ADMIN+'#get-registered-users-num' # XEP-0133
-NS_ADMIN_DISABLED_USERS_NUM =NS_ADMIN+'#get-disabled-users-num' # XEP-0133
-NS_ADMIN_ONLINE_USERS_NUM =NS_ADMIN+'#get-online-users-num' # XEP-0133
-NS_ADMIN_ACTIVE_USERS_NUM =NS_ADMIN+'#get-active-users-num' # XEP-0133
-NS_ADMIN_IDLE_USERS_NUM =NS_ADMIN+'#get-idle-users-num' # XEP-0133
-NS_ADMIN_REGISTERED_USERS_LIST =NS_ADMIN+'#get-registered-users-list' # XEP-0133
-NS_ADMIN_DISABLED_USERS_LIST =NS_ADMIN+'#get-disabled-users-list' # XEP-0133
-NS_ADMIN_ONLINE_USERS_LIST =NS_ADMIN+'#get-online-users-list' # XEP-0133
-NS_ADMIN_ACTIVE_USERS_LIST =NS_ADMIN+'#get-active-users-list' # XEP-0133
-NS_ADMIN_IDLE_USERS_LIST =NS_ADMIN+'#get-idle-users-list' # XEP-0133
-NS_ADMIN_ANNOUNCE =NS_ADMIN+'#announce' # XEP-0133
-NS_ADMIN_SET_MOTD =NS_ADMIN+'#set-motd' # XEP-0133
-NS_ADMIN_EDIT_MOTD =NS_ADMIN+'#edit-motd' # XEP-0133
-NS_ADMIN_DELETE_MOTD =NS_ADMIN+'#delete-motd' # XEP-0133
-NS_ADMIN_SET_WELCOME =NS_ADMIN+'#set-welcome' # XEP-0133
-NS_ADMIN_DELETE_WELCOME =NS_ADMIN+'#delete-welcome' # XEP-0133
-NS_ADMIN_EDIT_ADMIN =NS_ADMIN+'#edit-admin' # XEP-0133
-NS_ADMIN_RESTART =NS_ADMIN+'#restart' # XEP-0133
-NS_ADMIN_SHUTDOWN =NS_ADMIN+'#shutdown' # XEP-0133
-NS_AGENTS ='jabber:iq:agents' # XEP-0094 (historical)
-NS_AMP ='http://jabber.org/protocol/amp' # XEP-0079
-NS_AMP_ERRORS =NS_AMP+'#errors' # XEP-0079
-NS_AUTH ='jabber:iq:auth' # XEP-0078
-NS_AVATAR ='jabber:iq:avatar' # XEP-0008 (historical)
-NS_BIND ='urn:ietf:params:xml:ns:xmpp-bind' # RFC 3920
-NS_BROWSE ='jabber:iq:browse' # XEP-0011 (historical)
-NS_BYTESTREAM ='http://jabber.org/protocol/bytestreams' # XEP-0065
-NS_CAPS ='http://jabber.org/protocol/caps' # XEP-0115
-NS_CHATSTATES ='http://jabber.org/protocol/chatstates' # XEP-0085
-NS_CLIENT ='jabber:client' # RFC 3921
-NS_COMMANDS ='http://jabber.org/protocol/commands' # XEP-0050
-NS_COMPONENT_ACCEPT ='jabber:component:accept' # XEP-0114
-NS_COMPONENT_1 ='http://jabberd.jabberstudio.org/ns/component/1.0' # Jabberd2
-NS_COMPRESS ='http://jabber.org/protocol/compress' # XEP-0138
-NS_DATA ='jabber:x:data' # XEP-0004
-NS_DATA_LAYOUT ='http://jabber.org/protocol/xdata-layout' # XEP-0141
-NS_DATA_VALIDATE ='http://jabber.org/protocol/xdata-validate' # XEP-0122
-NS_DELAY ='jabber:x:delay' # XEP-0091 (deprecated)
-NS_DIALBACK ='jabber:server:dialback' # RFC 3921
-NS_DISCO ='http://jabber.org/protocol/disco' # XEP-0030
-NS_DISCO_INFO =NS_DISCO+'#info' # XEP-0030
-NS_DISCO_ITEMS =NS_DISCO+'#items' # XEP-0030
-NS_ENCRYPTED ='jabber:x:encrypted' # XEP-0027
-NS_EVENT ='jabber:x:event' # XEP-0022 (deprecated)
-NS_FEATURE ='http://jabber.org/protocol/feature-neg' # XEP-0020
-NS_FILE ='http://jabber.org/protocol/si/profile/file-transfer' # XEP-0096
-NS_GATEWAY ='jabber:iq:gateway' # XEP-0100
-NS_GEOLOC ='http://jabber.org/protocol/geoloc' # XEP-0080
-NS_GROUPCHAT ='gc-1.0' # XEP-0045
-NS_HTTP_BIND ='http://jabber.org/protocol/httpbind' # XEP-0124
-NS_IBB ='http://jabber.org/protocol/ibb' # XEP-0047
-NS_INVISIBLE ='presence-invisible' # Jabberd2
-NS_IQ ='iq' # Jabberd2
-NS_LAST ='jabber:iq:last' # XEP-0012
-NS_MESSAGE ='message' # Jabberd2
-NS_MOOD ='http://jabber.org/protocol/mood' # XEP-0107
-NS_MUC ='http://jabber.org/protocol/muc' # XEP-0045
-NS_MUC_ADMIN =NS_MUC+'#admin' # XEP-0045
-NS_MUC_OWNER =NS_MUC+'#owner' # XEP-0045
-NS_MUC_UNIQUE =NS_MUC+'#unique' # XEP-0045
-NS_MUC_USER =NS_MUC+'#user' # XEP-0045
-NS_MUC_REGISTER =NS_MUC+'#register' # XEP-0045
-NS_MUC_REQUEST =NS_MUC+'#request' # XEP-0045
-NS_MUC_ROOMCONFIG =NS_MUC+'#roomconfig' # XEP-0045
-NS_MUC_ROOMINFO =NS_MUC+'#roominfo' # XEP-0045
-NS_MUC_ROOMS =NS_MUC+'#rooms' # XEP-0045
-NS_MUC_TRAFIC =NS_MUC+'#traffic' # XEP-0045
-NS_NICK ='http://jabber.org/protocol/nick' # XEP-0172
-NS_OFFLINE ='http://jabber.org/protocol/offline' # XEP-0013
-NS_PHYSLOC ='http://jabber.org/protocol/physloc' # XEP-0112
-NS_PRESENCE ='presence' # Jabberd2
-NS_PRIVACY ='jabber:iq:privacy' # RFC 3921
-NS_PRIVATE ='jabber:iq:private' # XEP-0049
-NS_PUBSUB ='http://jabber.org/protocol/pubsub' # XEP-0060
-NS_REGISTER ='jabber:iq:register' # XEP-0077
-NS_RC ='http://jabber.org/protocol/rc' # XEP-0146
-NS_ROSTER ='jabber:iq:roster' # RFC 3921
-NS_ROSTERX ='http://jabber.org/protocol/rosterx' # XEP-0144
-NS_RPC ='jabber:iq:rpc' # XEP-0009
-NS_SASL ='urn:ietf:params:xml:ns:xmpp-sasl' # RFC 3920
-NS_SEARCH ='jabber:iq:search' # XEP-0055
-NS_SERVER ='jabber:server' # RFC 3921
-NS_SESSION ='urn:ietf:params:xml:ns:xmpp-session' # RFC 3921
-NS_SI ='http://jabber.org/protocol/si' # XEP-0096
-NS_SI_PUB ='http://jabber.org/protocol/sipub' # XEP-0137
-NS_SIGNED ='jabber:x:signed' # XEP-0027
-NS_STANZAS ='urn:ietf:params:xml:ns:xmpp-stanzas' # RFC 3920
-NS_STREAMS ='http://etherx.jabber.org/streams' # RFC 3920
-NS_TIME ='jabber:iq:time' # XEP-0090 (deprecated)
-NS_TLS ='urn:ietf:params:xml:ns:xmpp-tls' # RFC 3920
-NS_VACATION ='http://jabber.org/protocol/vacation' # XEP-0109
-NS_VCARD ='vcard-temp' # XEP-0054
-NS_VCARD_UPDATE ='vcard-temp:x:update' # XEP-0153
-NS_VERSION ='jabber:iq:version' # XEP-0092
-NS_WAITINGLIST ='http://jabber.org/protocol/waitinglist' # XEP-0130
-NS_XHTML_IM ='http://jabber.org/protocol/xhtml-im' # XEP-0071
-NS_XMPP_STREAMS ='urn:ietf:params:xml:ns:xmpp-streams' # RFC 3920
-
-xmpp_stream_error_conditions="""
-bad-format -- -- -- The entity has sent XML that cannot be processed.
-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.
-conflict -- -- -- The server is closing the active stream for this entity because a new stream has been initiated that conflicts with the existing stream.
-connection-timeout -- -- -- The entity has not generated any traffic over the stream for some period of time.
-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.
-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.
-improper-addressing -- -- -- A stanza sent between two servers lacks a 'to' or 'from' attribute (or the attribute has no value).
-internal-server-error -- -- -- The server has experienced a misconfiguration or an otherwise-undefined internal error that prevents it from servicing the stream.
-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.
-invalid-id -- -- -- The stream ID or dialback ID is invalid or does not match an ID previously provided.
-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".
-invalid-xml -- -- -- The entity has sent invalid XML over the stream to a server that performs validation.
-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.
-policy-violation -- -- -- The entity has violated some local service policy.
-remote-connection-failed -- -- -- The server is unable to properly connect to a remote resource that is required for authentication or authorization.
-resource-constraint -- -- -- The server lacks the system resources necessary to service the stream.
-restricted-xml -- -- -- The entity has attempted to send restricted XML features such as a comment, processing instruction, DTD, entity reference, or unescaped character.
-see-other-host -- -- -- The server will not provide service to the initiating entity but is redirecting traffic to another host.
-system-shutdown -- -- -- The server is being shut down and all active streams are being closed.
-undefined-condition -- -- -- The error condition is not one of those defined by the other conditions in this list.
-unsupported-encoding -- -- -- The initiating entity has encoded the stream in an encoding that is not supported by the server.
-unsupported-stanza-type -- -- -- The initiating entity has sent a first-level child of the stream that is not supported by the server.
-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.
-xml-not-well-formed -- -- -- The initiating entity has sent XML that is not well-formed."""
-xmpp_stanza_error_conditions="""
-bad-request -- 400 -- modify -- The sender has sent XML that is malformed or that cannot be processed.
-conflict -- 409 -- cancel -- Access cannot be granted because an existing resource or session exists with the same name or address.
-feature-not-implemented -- 501 -- cancel -- The feature requested is not implemented by the recipient or server and therefore cannot be processed.
-forbidden -- 403 -- auth -- The requesting entity does not possess the required permissions to perform the action.
-gone -- 302 -- modify -- The recipient or server can no longer be contacted at this address.
-internal-server-error -- 500 -- wait -- The server could not process the stanza because of a misconfiguration or an otherwise-undefined internal server error.
-item-not-found -- 404 -- cancel -- The addressed JID or item requested cannot be found.
-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.
-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.
-not-allowed -- 405 -- cancel -- The recipient or server does not allow any entity to perform the action.
-not-authorized -- 401 -- auth -- The sender must provide proper credentials before being allowed to perform the action, or has provided improper credentials.
-payment-required -- 402 -- auth -- The requesting entity is not authorized to access the requested service because payment is required.
-recipient-unavailable -- 404 -- wait -- The intended recipient is temporarily unavailable.
-redirect -- 302 -- modify -- The recipient or server is redirecting requests for this information to another entity.
-registration-required -- 407 -- auth -- The requesting entity is not authorized to access the requested service because registration is required.
-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.
-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.
-resource-constraint -- 500 -- wait -- The server or recipient lacks the system resources necessary to service the request.
-service-unavailable -- 503 -- cancel -- The server or recipient does not currently provide the requested service.
-subscription-required -- 407 -- auth -- The requesting entity is not authorized to access the requested service because a subscription is required.
-undefined-condition -- 500 -- --
-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)."""
-sasl_error_conditions="""
-aborted -- -- -- The receiving entity acknowledges an <abort/> element sent by the initiating entity; sent in reply to the <abort/> element.
-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.
-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.
-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.
-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.
-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.
-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."""
-
-ERRORS,_errorcodes={},{}
-for ns,errname,errpool in [(NS_XMPP_STREAMS,'STREAM',xmpp_stream_error_conditions),
- (NS_STANZAS ,'ERR' ,xmpp_stanza_error_conditions),
- (NS_SASL ,'SASL' ,sasl_error_conditions)]:
- for err in errpool.split('\n')[1:]:
- cond,code,typ,text=err.split(' -- ')
- name=errname+'_'+cond.upper().replace('-','_')
- locals()[name]=ns+' '+cond
- ERRORS[ns+' '+cond]=[code,typ,text]
- if code: _errorcodes[code]=cond
-del ns,errname,errpool,err,cond,code,typ,text
-
-def isResultNode(node):
- """ Returns true if the node is a positive reply. """
- return node and node.getType()=='result'
-def isErrorNode(node):
- """ Returns true if the node is a negative reply. """
- return node and node.getType()=='error'
-
-class NodeProcessed(Exception):
- """ Exception that should be raised by handler when the handling should be stopped. """
-class StreamError(Exception):
- """ Base exception class for stream errors."""
-class BadFormat(StreamError): pass
-class BadNamespacePrefix(StreamError): pass
-class Conflict(StreamError): pass
-class ConnectionTimeout(StreamError): pass
-class HostGone(StreamError): pass
-class HostUnknown(StreamError): pass
-class ImproperAddressing(StreamError): pass
-class InternalServerError(StreamError): pass
-class InvalidFrom(StreamError): pass
-class InvalidID(StreamError): pass
-class InvalidNamespace(StreamError): pass
-class InvalidXML(StreamError): pass
-class NotAuthorized(StreamError): pass
-class PolicyViolation(StreamError): pass
-class RemoteConnectionFailed(StreamError): pass
-class ResourceConstraint(StreamError): pass
-class RestrictedXML(StreamError): pass
-class SeeOtherHost(StreamError): pass
-class SystemShutdown(StreamError): pass
-class UndefinedCondition(StreamError): pass
-class UnsupportedEncoding(StreamError): pass
-class UnsupportedStanzaType(StreamError): pass
-class UnsupportedVersion(StreamError): pass
-class XMLNotWellFormed(StreamError): pass
-
-stream_exceptions = {'bad-format': BadFormat,
- 'bad-namespace-prefix': BadNamespacePrefix,
- 'conflict': Conflict,
- 'connection-timeout': ConnectionTimeout,
- 'host-gone': HostGone,
- 'host-unknown': HostUnknown,
- 'improper-addressing': ImproperAddressing,
- 'internal-server-error': InternalServerError,
- 'invalid-from': InvalidFrom,
- 'invalid-id': InvalidID,
- 'invalid-namespace': InvalidNamespace,
- 'invalid-xml': InvalidXML,
- 'not-authorized': NotAuthorized,
- 'policy-violation': PolicyViolation,
- 'remote-connection-failed': RemoteConnectionFailed,
- 'resource-constraint': ResourceConstraint,
- 'restricted-xml': RestrictedXML,
- 'see-other-host': SeeOtherHost,
- 'system-shutdown': SystemShutdown,
- 'undefined-condition': UndefinedCondition,
- 'unsupported-encoding': UnsupportedEncoding,
- 'unsupported-stanza-type': UnsupportedStanzaType,
- 'unsupported-version': UnsupportedVersion,
- 'xml-not-well-formed': XMLNotWellFormed}
-
-class JID:
- """ JID object. JID can be built from string, modified, compared, serialised into string. """
- def __init__(self, jid=None, node='', domain='', resource=''):
- """ Constructor. JID can be specified as string (jid argument) or as separate parts.
- Examples:
- JID('node@domain/resource')
- JID(node='node',domain='domain.org')
- """
- if not jid and not domain: raise ValueError('JID must contain at least domain name')
- elif type(jid)==type(self): self.node,self.domain,self.resource=jid.node,jid.domain,jid.resource
- elif domain: self.node,self.domain,self.resource=node,domain,resource
- else:
- if jid.find('@')+1: self.node,jid=jid.split('@',1)
- else: self.node=''
- if jid.find('/')+1: self.domain,self.resource=jid.split('/',1)
- else: self.domain,self.resource=jid,''
- def getNode(self):
- """ Return the node part of the JID """
- return self.node
- def setNode(self,node):
- """ Set the node part of the JID to new value. Specify None to remove the node part."""
- self.node=node.lower()
- def getDomain(self):
- """ Return the domain part of the JID """
- return self.domain
- def setDomain(self,domain):
- """ Set the domain part of the JID to new value."""
- self.domain=domain.lower()
- def getResource(self):
- """ Return the resource part of the JID """
- return self.resource
- def setResource(self,resource):
- """ Set the resource part of the JID to new value. Specify None to remove the resource part."""
- self.resource=resource
- def getStripped(self):
- """ Return the bare representation of JID. I.e. string value w/o resource. """
- return self.__str__(0)
- def __eq__(self, other):
- """ Compare the JID to another instance or to string for equality. """
- try: other=JID(other)
- except ValueError: return 0
- return self.resource==other.resource and self.__str__(0) == other.__str__(0)
- def __ne__(self, other):
- """ Compare the JID to another instance or to string for non-equality. """
- return not self.__eq__(other)
- def bareMatch(self, other):
- """ Compare the node and domain parts of the JID's for equality. """
- return self.__str__(0) == JID(other).__str__(0)
- def __str__(self,wresource=1):
- """ Serialise JID into string. """
- if self.node: jid=self.node+'@'+self.domain
- else: jid=self.domain
- if wresource and self.resource: return jid+'/'+self.resource
- return jid
- def __hash__(self):
- """ Produce hash of the JID, Allows to use JID objects as keys of the dictionary. """
- return hash(self.__str__())
-
-class Protocol(Node):
- """ A "stanza" object class. Contains methods that are common for presences, iqs and messages. """
- def __init__(self, name=None, to=None, typ=None, frm=None, attrs={}, payload=[], timestamp=None, xmlns=None, node=None):
- """ Constructor, name is the name of the stanza i.e. 'message' or 'presence' or 'iq'.
- to is the value of 'to' attribure, 'typ' - 'type' attribute
- frn - from attribure, attrs - other attributes mapping, payload - same meaning as for simplexml payload definition
- timestamp - the time value that needs to be stamped over stanza
- xmlns - namespace of top stanza node
- node - parsed or unparsed stana to be taken as prototype.
- """
- if not attrs: attrs={}
- if to: attrs['to']=to
- if frm: attrs['from']=frm
- if typ: attrs['type']=typ
- Node.__init__(self, tag=name, attrs=attrs, payload=payload, node=node)
- if not node and xmlns: self.setNamespace(xmlns)
- if self['to']: self.setTo(self['to'])
- if self['from']: self.setFrom(self['from'])
- if node and type(self)==type(node) and self.__class__==node.__class__ and self.attrs.has_key('id'): del self.attrs['id']
- self.timestamp=None
- for x in self.getTags('x',namespace=NS_DELAY):
- try:
- if not self.getTimestamp() or x.getAttr('stamp')<self.getTimestamp(): self.setTimestamp(x.getAttr('stamp'))
- except: pass
- if timestamp is not None: self.setTimestamp(timestamp) # To auto-timestamp stanza just pass timestamp=''
- def getTo(self):
- """ Return value of the 'to' attribute. """
- try: return self['to']
- except: return None
- def getFrom(self):
- """ Return value of the 'from' attribute. """
- try: return self['from']
- except: return None
- def getTimestamp(self):
- """ Return the timestamp in the 'yyyymmddThhmmss' format. """
- return self.timestamp
- def getID(self):
- """ Return the value of the 'id' attribute. """
- return self.getAttr('id')
- def setTo(self,val):
- """ Set the value of the 'to' attribute. """
- self.setAttr('to', JID(val))
- def getType(self):
- """ Return the value of the 'type' attribute. """
- return self.getAttr('type')
- def setFrom(self,val):
- """ Set the value of the 'from' attribute. """
- self.setAttr('from', JID(val))
- def setType(self,val):
- """ Set the value of the 'type' attribute. """
- self.setAttr('type', val)
- def setID(self,val):
- """ Set the value of the 'id' attribute. """
- self.setAttr('id', val)
- def getError(self):
- """ Return the error-condition (if present) or the textual description of the error (otherwise). """
- errtag=self.getTag('error')
- if errtag:
- for tag in errtag.getChildren():
- if tag.getName()<>'text': return tag.getName()
- return errtag.getData()
- def getErrorCode(self):
- """ Return the error code. Obsolette. """
- return self.getTagAttr('error','code')
- def setError(self,error,code=None):
- """ Set the error code. Obsolette. Use error-conditions instead. """
- if code:
- if str(code) in _errorcodes.keys(): error=ErrorNode(_errorcodes[str(code)],text=error)
- else: error=ErrorNode(ERR_UNDEFINED_CONDITION,code=code,typ='cancel',text=error)
- elif type(error) in [type(''),type(u'')]: error=ErrorNode(error)
- self.setType('error')
- self.addChild(node=error)
- def setTimestamp(self,val=None):
- """Set the timestamp. timestamp should be the yyyymmddThhmmss string."""
- if not val: val=time.strftime('%Y%m%dT%H:%M:%S', time.gmtime())
- self.timestamp=val
- self.setTag('x',{'stamp':self.timestamp},namespace=NS_DELAY)
- def getProperties(self):
- """ Return the list of namespaces to which belongs the direct childs of element"""
- props=[]
- for child in self.getChildren():
- prop=child.getNamespace()
- if prop not in props: props.append(prop)
- return props
- def __setitem__(self,item,val):
- """ Set the item 'item' to the value 'val'."""
- if item in ['to','from']: val=JID(val)
- return self.setAttr(item,val)
-
-class Message(Protocol):
- """ XMPP Message stanza - "push" mechanism."""
- def __init__(self, to=None, body=None, typ=None, subject=None, attrs={}, frm=None, payload=[], timestamp=None, xmlns=NS_CLIENT, node=None):
- """ Create message object. You can specify recipient, text of message, type of message
- any additional attributes, sender of the message, any additional payload (f.e. jabber:x:delay element) and namespace in one go.
- Alternatively you can pass in the other XML object as the 'node' parameted to replicate it as message. """
- Protocol.__init__(self, 'message', to=to, typ=typ, attrs=attrs, frm=frm, payload=payload, timestamp=timestamp, xmlns=xmlns, node=node)
- if body: self.setBody(body)
- if subject: self.setSubject(subject)
- def getBody(self):
- """ Returns text of the message. """
- return self.getTagData('body')
- def getSubject(self):
- """ Returns subject of the message. """
- return self.getTagData('subject')
- def getThread(self):
- """ Returns thread of the message. """
- return self.getTagData('thread')
- def setBody(self,val):
- """ Sets the text of the message. """
- self.setTagData('body',val)
- def setSubject(self,val):
- """ Sets the subject of the message. """
- self.setTagData('subject',val)
- def setThread(self,val):
- """ Sets the thread of the message. """
- self.setTagData('thread',val)
- def buildReply(self,text=None):
- """ Builds and returns another message object with specified text.
- The to, from and thread properties of new message are pre-set as reply to this message. """
- m=Message(to=self.getFrom(),frm=self.getTo(),body=text)
- th=self.getThread()
- if th: m.setThread(th)
- return m
-
-class Presence(Protocol):
- """ XMPP Presence object."""
- def __init__(self, to=None, typ=None, priority=None, show=None, status=None, attrs={}, frm=None, timestamp=None, payload=[], xmlns=NS_CLIENT, node=None):
- """ Create presence object. You can specify recipient, type of message, priority, show and status values
- any additional attributes, sender of the presence, timestamp, any additional payload (f.e. jabber:x:delay element) and namespace in one go.
- Alternatively you can pass in the other XML object as the 'node' parameted to replicate it as presence. """
- Protocol.__init__(self, 'presence', to=to, typ=typ, attrs=attrs, frm=frm, payload=payload, timestamp=timestamp, xmlns=xmlns, node=node)
- if priority: self.setPriority(priority)
- if show: self.setShow(show)
- if status: self.setStatus(status)
- def getPriority(self):
- """ Returns the priority of the message. """
- return self.getTagData('priority')
- def getShow(self):
- """ Returns the show value of the message. """
- return self.getTagData('show')
- def getStatus(self):
- """ Returns the status string of the message. """
- return self.getTagData('status')
- def setPriority(self,val):
- """ Sets the priority of the message. """
- self.setTagData('priority',val)
- def setShow(self,val):
- """ Sets the show value of the message. """
- self.setTagData('show',val)
- def setStatus(self,val):
- """ Sets the status string of the message. """
- self.setTagData('status',val)
-
- def _muc_getItemAttr(self,tag,attr):
- for xtag in self.getTags('x'):
- for child in xtag.getTags(tag):
- return child.getAttr(attr)
- def _muc_getSubTagDataAttr(self,tag,attr):
- for xtag in self.getTags('x'):
- for child in xtag.getTags('item'):
- for cchild in child.getTags(tag):
- return cchild.getData(),cchild.getAttr(attr)
- return None,None
- def getRole(self):
- """Returns the presence role (for groupchat)"""
- return self._muc_getItemAttr('item','role')
- def getAffiliation(self):
- """Returns the presence affiliation (for groupchat)"""
- return self._muc_getItemAttr('item','affiliation')
- def getNick(self):
- """Returns the nick value (for nick change in groupchat)"""
- return self._muc_getItemAttr('item','nick')
- def getJid(self):
- """Returns the presence jid (for groupchat)"""
- return self._muc_getItemAttr('item','jid')
- def getReason(self):
- """Returns the reason of the presence (for groupchat)"""
- return self._muc_getSubTagDataAttr('reason','')[0]
- def getActor(self):
- """Returns the reason of the presence (for groupchat)"""
- return self._muc_getSubTagDataAttr('actor','jid')[1]
- def getStatusCode(self):
- """Returns the status code of the presence (for groupchat)"""
- return self._muc_getItemAttr('status','code')
-
-class Iq(Protocol):
- """ XMPP Iq object - get/set dialog mechanism. """
- def __init__(self, typ=None, queryNS=None, attrs={}, to=None, frm=None, payload=[], xmlns=NS_CLIENT, node=None):
- """ Create Iq object. You can specify type, query namespace
- 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.
- Alternatively you can pass in the other XML object as the 'node' parameted to replicate it as an iq. """
- Protocol.__init__(self, 'iq', to=to, typ=typ, attrs=attrs, frm=frm, xmlns=xmlns, node=node)
- if payload: self.setQueryPayload(payload)
- if queryNS: self.setQueryNS(queryNS)
- def getQueryNS(self):
- """ Return the namespace of the 'query' child element."""
- tag=self.getTag('query')
- if tag: return tag.getNamespace()
- def getQuerynode(self):
- """ Return the 'node' attribute value of the 'query' child element."""
- return self.getTagAttr('query','node')
- def getQueryPayload(self):
- """ Return the 'query' child element payload."""
- tag=self.getTag('query')
- if tag: return tag.getPayload()
- def getQueryChildren(self):
- """ Return the 'query' child element child nodes."""
- tag=self.getTag('query')
- if tag: return tag.getChildren()
- def setQueryNS(self,namespace):
- """ Set the namespace of the 'query' child element."""
- self.setTag('query').setNamespace(namespace)
- def setQueryPayload(self,payload):
- """ Set the 'query' child element payload."""
- self.setTag('query').setPayload(payload)
- def setQuerynode(self,node):
- """ Set the 'node' attribute value of the 'query' child element."""
- self.setTagAttr('query','node',node)
- def buildReply(self,typ):
- """ Builds and returns another Iq object of specified type.
- The to, from and query child node of new Iq are pre-set as reply to this Iq. """
- iq=Iq(typ,to=self.getFrom(),frm=self.getTo(),attrs={'id':self.getID()})
- if self.getTag('query'): iq.setQueryNS(self.getQueryNS())
- return iq
-
-class ErrorNode(Node):
- """ XMPP-style error element.
- In the case of stanza error should be attached to XMPP stanza.
- In the case of stream-level errors should be used separately. """
- def __init__(self,name,code=None,typ=None,text=None):
- """ Create new error node object.
- Mandatory parameter: name - name of error condition.
- Optional parameters: code, typ, text. Used for backwards compartibility with older jabber protocol."""
- if ERRORS.has_key(name):
- cod,type,txt=ERRORS[name]
- ns=name.split()[0]
- else: cod,ns,type,txt='500',NS_STANZAS,'cancel',''
- if typ: type=typ
- if code: cod=code
- if text: txt=text
- Node.__init__(self,'error',{},[Node(name)])
- if type: self.setAttr('type',type)
- if not cod: self.setName('stream:error')
- if txt: self.addChild(node=Node(ns+' text',{},[txt]))
- if cod: self.setAttr('code',cod)
-
-class Error(Protocol):
- """ Used to quickly transform received stanza into error reply."""
- def __init__(self,node,error,reply=1):
- """ Create error reply basing on the received 'node' stanza and the 'error' error condition.
- If the 'node' is not the received stanza but locally created ('to' and 'from' fields needs not swapping)
- specify the 'reply' argument as false."""
- if reply: Protocol.__init__(self,to=node.getFrom(),frm=node.getTo(),node=node)
- else: Protocol.__init__(self,node=node)
- self.setError(error)
- if node.getType()=='error': self.__str__=self.__dupstr__
- def __dupstr__(self,dup1=None,dup2=None):
- """ Dummy function used as preventor of creating error node in reply to error node.
- I.e. you will not be able to serialise "double" error into string.
- """
- return ''
-
-class DataField(Node):
- """ This class is used in the DataForm class to describe the single data item.
- If you are working with jabber:x:data (XEP-0004, XEP-0068, XEP-0122)
- then you will need to work with instances of this class. """
- def __init__(self,name=None,value=None,typ=None,required=0,label=None,desc=None,options=[],node=None):
- """ Create new data field of specified name,value and type.
- Also 'required','desc' and 'options' fields can be set.
- Alternatively other XML object can be passed in as the 'node' parameted to replicate it as a new datafiled.
- """
- Node.__init__(self,'field',node=node)
- if name: self.setVar(name)
- if type(value) in [list,tuple]: self.setValues(value)
- elif value: self.setValue(value)
- if typ: self.setType(typ)
- elif not typ and not node: self.setType('text-single')
- if required: self.setRequired(required)
- if label: self.setLabel(label)
- if desc: self.setDesc(desc)
- if options: self.setOptions(options)
- def setRequired(self,req=1):
- """ Change the state of the 'required' flag. """
- if req: self.setTag('required')
- else:
- try: self.delChild('required')
- except ValueError: return
- def isRequired(self):
- """ Returns in this field a required one. """
- return self.getTag('required')
- def setLabel(self,label):
- """ Set the label of this field. """
- self.setAttr('label',label)
- def getLabel(self):
- """ Return the label of this field. """
- return self.getAttr('label')
- def setDesc(self,desc):
- """ Set the description of this field. """
- self.setTagData('desc',desc)
- def getDesc(self):
- """ Return the description of this field. """
- return self.getTagData('desc')
- def setValue(self,val):
- """ Set the value of this field. """
- self.setTagData('value',val)
- def getValue(self):
- return self.getTagData('value')
- def setValues(self,lst):
- """ Set the values of this field as values-list.
- Replaces all previous filed values! If you need to just add a value - use addValue method."""
- while self.getTag('value'): self.delChild('value')
- for val in lst: self.addValue(val)
- def addValue(self,val):
- """ Add one more value to this field. Used in 'get' iq's or such."""
- self.addChild('value',{},[val])
- def getValues(self):
- """ Return the list of values associated with this field."""
- ret=[]
- for tag in self.getTags('value'): ret.append(tag.getData())
- return ret
- def getOptions(self):
- """ Return label-option pairs list associated with this field."""
- ret=[]
- for tag in self.getTags('option'): ret.append([tag.getAttr('label'),tag.getTagData('value')])
- return ret
- def setOptions(self,lst):
- """ Set label-option pairs list associated with this field."""
- while self.getTag('option'): self.delChild('option')
- for opt in lst: self.addOption(opt)
- def addOption(self,opt):
- """ Add one more label-option pair to this field."""
- if type(opt) in [str,unicode]: self.addChild('option').setTagData('value',opt)
- else: self.addChild('option',{'label':opt[0]}).setTagData('value',opt[1])
- def getType(self):
- """ Get type of this field. """
- return self.getAttr('type')
- def setType(self,val):
- """ Set type of this field. """
- return self.setAttr('type',val)
- def getVar(self):
- """ Get 'var' attribute value of this field. """
- return self.getAttr('var')
- def setVar(self,val):
- """ Set 'var' attribute value of this field. """
- return self.setAttr('var',val)
-
-class DataReported(Node):
- """ This class is used in the DataForm class to describe the 'reported data field' data items which are used in
- 'multiple item form results' (as described in XEP-0004).
- Represents the fields that will be returned from a search. This information is useful when
- you try to use the jabber:iq:search namespace to return dynamic form information.
- """
- def __init__(self,node=None):
- """ Create new empty 'reported data' field. However, note that, according XEP-0004:
- * It MUST contain one or more DataFields.
- * Contained DataFields SHOULD possess a 'type' and 'label' attribute in addition to 'var' attribute
- * Contained DataFields SHOULD NOT contain a <value/> element.
- Alternatively other XML object can be passed in as the 'node' parameted to replicate it as a new
- dataitem.
- """
- Node.__init__(self,'reported',node=node)
- if node:
- newkids=[]
- for n in self.getChildren():
- if n.getName()=='field': newkids.append(DataField(node=n))
- else: newkids.append(n)
- self.kids=newkids
- def getField(self,name):
- """ Return the datafield object with name 'name' (if exists). """
- return self.getTag('field',attrs={'var':name})
- def setField(self,name,typ=None,label=None):
- """ Create if nessessary or get the existing datafield object with name 'name' and return it.
- If created, attributes 'type' and 'label' are applied to new datafield."""
- f=self.getField(name)
- if f: return f
- return self.addChild(node=DataField(name,None,typ,0,label))
- def asDict(self):
- """ Represent dataitem as simple dictionary mapping of datafield names to their values."""
- ret={}
- for field in self.getTags('field'):
- name=field.getAttr('var')
- typ=field.getType()
- if isinstance(typ,(str,unicode)) and typ[-6:]=='-multi':
- val=[]
- for i in field.getTags('value'): val.append(i.getData())
- else: val=field.getTagData('value')
- ret[name]=val
- if self.getTag('instructions'): ret['instructions']=self.getInstructions()
- return ret
- def __getitem__(self,name):
- """ Simple dictionary interface for getting datafields values by their names."""
- item=self.getField(name)
- if item: return item.getValue()
- raise IndexError('No such field')
- def __setitem__(self,name,val):
- """ Simple dictionary interface for setting datafields values by their names."""
- return self.setField(name).setValue(val)
-
-class DataItem(Node):
- """ This class is used in the DataForm class to describe data items which are used in 'multiple
- item form results' (as described in XEP-0004).
- """
- def __init__(self,node=None):
- """ Create new empty data item. However, note that, according XEP-0004, DataItem MUST contain ALL
- DataFields described in DataReported.
- Alternatively other XML object can be passed in as the 'node' parameted to replicate it as a new
- dataitem.
- """
- Node.__init__(self,'item',node=node)
- if node:
- newkids=[]
- for n in self.getChildren():
- if n.getName()=='field': newkids.append(DataField(node=n))
- else: newkids.append(n)
- self.kids=newkids
- def getField(self,name):
- """ Return the datafield object with name 'name' (if exists). """
- return self.getTag('field',attrs={'var':name})
- def setField(self,name):
- """ Create if nessessary or get the existing datafield object with name 'name' and return it. """
- f=self.getField(name)
- if f: return f
- return self.addChild(node=DataField(name))
- def asDict(self):
- """ Represent dataitem as simple dictionary mapping of datafield names to their values."""
- ret={}
- for field in self.getTags('field'):
- name=field.getAttr('var')
- typ=field.getType()
- if isinstance(typ,(str,unicode)) and typ[-6:]=='-multi':
- val=[]
- for i in field.getTags('value'): val.append(i.getData())
- else: val=field.getTagData('value')
- ret[name]=val
- if self.getTag('instructions'): ret['instructions']=self.getInstructions()
- return ret
- def __getitem__(self,name):
- """ Simple dictionary interface for getting datafields values by their names."""
- item=self.getField(name)
- if item: return item.getValue()
- raise IndexError('No such field')
- def __setitem__(self,name,val):
- """ Simple dictionary interface for setting datafields values by their names."""
- return self.setField(name).setValue(val)
-
-class DataForm(Node):
- """ DataForm class. Used for manipulating dataforms in XMPP.
- Relevant XEPs: 0004, 0068, 0122.
- Can be used in disco, pub-sub and many other applications."""
- def __init__(self, typ=None, data=[], title=None, node=None):
- """
- Create new dataform of type 'typ'; 'data' is the list of DataReported,
- DataItem and DataField instances that this dataform contains; 'title'
- is the title string.
- You can specify the 'node' argument as the other node to be used as
- base for constructing this dataform.
-
- title and instructions is optional and SHOULD NOT contain newlines.
- Several instructions MAY be present.
- 'typ' can be one of ('form' | 'submit' | 'cancel' | 'result' )
- 'typ' of reply iq can be ( 'result' | 'set' | 'set' | 'result' ) respectively.
- 'cancel' form can not contain any fields. All other forms contains AT LEAST one field.
- 'title' MAY be included in forms of type "form" and "result"
- """
- Node.__init__(self,'x',node=node)
- if node:
- newkids=[]
- for n in self.getChildren():
- if n.getName()=='field': newkids.append(DataField(node=n))
- elif n.getName()=='item': newkids.append(DataItem(node=n))
- elif n.getName()=='reported': newkids.append(DataReported(node=n))
- else: newkids.append(n)
- self.kids=newkids
- if typ: self.setType(typ)
- self.setNamespace(NS_DATA)
- if title: self.setTitle(title)
- if type(data)==type({}):
- newdata=[]
- for name in data.keys(): newdata.append(DataField(name,data[name]))
- data=newdata
- for child in data:
- if type(child) in [type(''),type(u'')]: self.addInstructions(child)
- elif child.__class__.__name__=='DataField': self.kids.append(child)
- elif child.__class__.__name__=='DataItem': self.kids.append(child)
- elif child.__class__.__name__=='DataReported': self.kids.append(child)
- else: self.kids.append(DataField(node=child))
- def getType(self):
- """ Return the type of dataform. """
- return self.getAttr('type')
- def setType(self,typ):
- """ Set the type of dataform. """
- self.setAttr('type',typ)
- def getTitle(self):
- """ Return the title of dataform. """
- return self.getTagData('title')
- def setTitle(self,text):
- """ Set the title of dataform. """
- self.setTagData('title',text)
- def getInstructions(self):
- """ Return the instructions of dataform. """
- return self.getTagData('instructions')
- def setInstructions(self,text):
- """ Set the instructions of dataform. """
- self.setTagData('instructions',text)
- def addInstructions(self,text):
- """ Add one more instruction to the dataform. """
- self.addChild('instructions',{},[text])
- def getField(self,name):
- """ Return the datafield object with name 'name' (if exists). """
- return self.getTag('field',attrs={'var':name})
- def setField(self,name):
- """ Create if nessessary or get the existing datafield object with name 'name' and return it. """
- f=self.getField(name)
- if f: return f
- return self.addChild(node=DataField(name))
- def asDict(self):
- """ Represent dataform as simple dictionary mapping of datafield names to their values."""
- ret={}
- for field in self.getTags('field'):
- name=field.getAttr('var')
- typ=field.getType()
- if isinstance(typ,(str,unicode)) and typ[-6:]=='-multi':
- val=[]
- for i in field.getTags('value'): val.append(i.getData())
- else: val=field.getTagData('value')
- ret[name]=val
- if self.getTag('instructions'): ret['instructions']=self.getInstructions()
- return ret
- def __getitem__(self,name):
- """ Simple dictionary interface for getting datafields values by their names."""
- item=self.getField(name)
- if item: return item.getValue()
- raise IndexError('No such field')
- def __setitem__(self,name,val):
- """ Simple dictionary interface for setting datafields values by their names."""
- return self.setField(name).setValue(val)