From 8e432b48684eb6146154b3f6ebce80fec71a5335 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Apr 21 2009 09:25:28 +0000 Subject: Ver. 0.5.0-rc1 --- diff --git a/.cvsignore b/.cvsignore index 1acc267..8aefa5d 100644 --- a/.cvsignore +++ b/.cvsignore @@ -1 +1 @@ -xmpppy-0.4.1.tar.gz +xmpppy-0.5.0rc1.tar.gz diff --git a/import.log b/import.log index 745c030..1aeb32f 100644 --- a/import.log +++ b/import.log @@ -1 +1,2 @@ python-xmpp-0_4_1-6_fc10:HEAD:python-xmpp-0.4.1-6.fc10.src.rpm:1237110721 +python-xmpp-0_5_0-0_1_rc1_fc10:HEAD:python-xmpp-0.5.0-0.1.rc1.fc10.src.rpm:1240305850 diff --git a/python-xmpp.spec b/python-xmpp.spec index 2c5459c..5850e01 100644 --- a/python-xmpp.spec +++ b/python-xmpp.spec @@ -1,24 +1,14 @@ %{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} Name: python-xmpp -Version: 0.4.1 -Release: 6%{?dist} +Version: 0.5.0 +Release: 0.1.rc1%{?dist} Summary: Python library for easy scripting with Jabber Group: Development/Languages License: GPLv2+ URL: http://xmpppy.sourceforge.net/ -Source0: http://downloads.sourceforge.net/xmpppy/xmpppy-%{version}.tar.gz -# HTTPPROXYsocket will send data before we have a Dispatcher (from upstream CVS) -Patch0: xmpppy-dispatcher.diff -# Handle spaces in SASL DIGEST-MD5 responses correctly (from upstream CVS) -# Fix for incorrect-encoding during SASL PLAIN auth (from upstream CVS) -# Allow anonymous auth if username is None (from upstream CVS) -Patch1: xmpppy-auth-fixes.diff -# Add extra XEP refs (from upstream CVS) -Patch2: xmpppy-extra-xep-refs.diff -# simplexml updates from gajim (from upstream CVS) -Patch3: xmpppy-gajim-simplexml-updates.diff +Source0: http://downloads.sourceforge.net/xmpppy/xmpppy-%{version}rc1.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) Provides: xmpppy = %{version}-%{release} @@ -43,11 +33,7 @@ architecture and primarily aims to work with jabberd2 - the new Open Source Jabber Server. %prep -%setup -q -n xmpppy-%{version} -%patch0 -p0 -b .dispatcher -%patch1 -p0 -b .auth_orig -%patch2 -p0 -b .extra_refs -%patch3 -p0 -b .gajim +%setup -q -n xmpppy-%{version}rc1 # strip executable permissions so that dependencies aren't picked up # from documentation files. @@ -70,6 +56,10 @@ rm -rf %{buildroot} %{python_sitelib}/* %changelog +* Sat Apr 11 2009 Peter Lemenkov - 0.5.0-0.1.rc1 +- Ver. 0.5.0rc1 +- Dropped patches + * Sun Mar 15 2009 Peter Lemenkov - 0.4.1-6 - Added Provides: xmpppy - Added patches from upstream CVS diff --git a/sources b/sources index 6b0ac0d..edc8281 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -ca36d685643f2c56ab07323a09ece9e4 xmpppy-0.4.1.tar.gz +e0d2b3f9e4a278c163431e64adb0c861 xmpppy-0.5.0rc1.tar.gz diff --git a/xmpppy-auth-fixes.diff b/xmpppy-auth-fixes.diff deleted file mode 100644 index e27ba52..0000000 --- a/xmpppy-auth-fixes.diff +++ /dev/null @@ -1,42 +0,0 @@ ---- xmpp/auth.py~ 2007-08-28 14:03:33.000000000 +0400 -+++ xmpp/auth.py 2009-03-15 12:45:17.000000000 +0300 -@@ -12,7 +12,7 @@ - ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - ## GNU General Public License for more details. - --# $Id: auth.py,v 1.38 2007/08/28 10:03:33 normanr Exp $ -+# $Id: auth.py,v 1.41 2008/09/13 21:45:21 normanr Exp $ - - """ - Provides library with all Non-SASL and SASL authentication mechanisms. -@@ -137,11 +137,13 @@ - self._owner.RegisterHandler('challenge',self.SASLHandler,xmlns=NS_SASL) - self._owner.RegisterHandler('failure',self.SASLHandler,xmlns=NS_SASL) - self._owner.RegisterHandler('success',self.SASLHandler,xmlns=NS_SASL) -- if "DIGEST-MD5" in mecs: -+ if "ANONYMOUS" in mecs and self.username == None: -+ node=Node('auth',attrs={'xmlns':NS_SASL,'mechanism':'ANONYMOUS'}) -+ elif "DIGEST-MD5" in mecs: - node=Node('auth',attrs={'xmlns':NS_SASL,'mechanism':'DIGEST-MD5'}) - elif "PLAIN" in mecs: - sasl_data='%s\x00%s\x00%s'%(self.username+'@'+self._owner.Server,self.username,self.password) -- node=Node('auth',attrs={'xmlns':NS_SASL,'mechanism':'PLAIN'},payload=[base64.encodestring(sasl_data)]) -+ node=Node('auth',attrs={'xmlns':NS_SASL,'mechanism':'PLAIN'},payload=[base64.encodestring(sasl_data).replace('\r','').replace('\n','')]) - else: - self.startsasl='failure' - self.DEBUG('I can only use DIGEST-MD5 and PLAIN mecanisms.','error') -@@ -173,11 +175,11 @@ - chal={} - data=base64.decodestring(incoming_data) - self.DEBUG('Got challenge:'+data,'ok') -- for pair in re.findall('(\w+=(?:"[^"]+")|(?:[^,]+))',data): -- key,value=pair.split('=', 1) -+ for pair in re.findall('(\w+\s*=\s*(?:(?:"[^"]+")|(?:[^,]+)))',data): -+ key,value=[x.strip() for x in pair.split('=', 1)] - if value[:1]=='"' and value[-1:]=='"': value=value[1:-1] - chal[key]=value -- if chal.has_key('qop') and 'auth' in chal['qop'].split(','): -+ if chal.has_key('qop') and 'auth' in [x.strip() for x in chal['qop'].split(',')]: - resp={} - resp['username']=self.username - resp['realm']=self._owner.Server diff --git a/xmpppy-dispatcher.diff b/xmpppy-dispatcher.diff deleted file mode 100644 index 63d7565..0000000 --- a/xmpppy-dispatcher.diff +++ /dev/null @@ -1,21 +0,0 @@ ---- xmpp/transports.py~ 2007-09-15 15:34:28.000000000 +0400 -+++ xmpp/transports.py 2009-03-15 11:49:08.812623324 +0300 -@@ -12,7 +12,7 @@ - ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - ## GNU General Public License for more details. - --# $Id: transports.py,v 1.31 2007/09/15 11:34:28 normanr Exp $ -+# $Id: transports.py,v 1.32 2008/01/04 11:32:53 normanr Exp $ - - """ - This module contains the low-level implementations of xmpppy connect methods or -@@ -181,7 +181,8 @@ - # Avoid printing messages that are empty keepalive packets. - if raw_data.strip(): - self.DEBUG(raw_data,'sent') -- self._owner.Dispatcher.Event('', DATA_SENT, raw_data) -+ if hasattr(self._owner, 'Dispatcher'): # HTTPPROXYsocket will send data before we have a Dispatcher -+ self._owner.Dispatcher.Event('', DATA_SENT, raw_data) - except: - self.DEBUG("Socket error while sending data",'error') - self._owner.disconnected() diff --git a/xmpppy-extra-xep-refs.diff b/xmpppy-extra-xep-refs.diff deleted file mode 100644 index dd81109..0000000 --- a/xmpppy-extra-xep-refs.diff +++ /dev/null @@ -1,119 +0,0 @@ ---- xmpp/protocol.py~ 2007-05-14 17:03:48.000000000 +0400 -+++ xmpp/protocol.py 2009-03-15 11:50:32.377621887 +0300 -@@ -12,7 +12,7 @@ - ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - ## GNU General Public License for more details. - --# $Id: protocol.py,v 1.58 2007/05/13 17:55:46 normanr Exp $ -+# $Id: protocol.py,v 1.59 2009/02/13 10:26:11 normanr Exp $ - - """ - Protocol module contains tools that is needed for processing of -@@ -58,36 +58,38 @@ - 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' -+NS_AUTH ='jabber:iq:auth' # XEP-0078 - NS_AVATAR ='jabber:iq:avatar' # XEP-0008 (historical) --NS_BIND ='urn:ietf:params:xml:ns:xmpp-bind' -+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' --NS_COMMANDS ='http://jabber.org/protocol/commands' --NS_COMPONENT_ACCEPT ='jabber:component:accept' --NS_COMPONENT_1 ='http://jabberd.jabberstudio.org/ns/component/1.0' -+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_DELAY ='jabber:x:delay' --NS_DIALBACK ='jabber:server:dialback' -+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 --NS_FEATURE ='http://jabber.org/protocol/feature-neg' -+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' -+NS_GATEWAY ='jabber:iq:gateway' # XEP-0100 - NS_GEOLOC ='http://jabber.org/protocol/geoloc' # XEP-0080 --NS_GROUPCHAT ='gc-1.0' -+NS_GROUPCHAT ='gc-1.0' # XEP-0045 - NS_HTTP_BIND ='http://jabber.org/protocol/httpbind' # XEP-0124 --NS_IBB ='http://jabber.org/protocol/ibb' -+NS_IBB ='http://jabber.org/protocol/ibb' # XEP-0047 - NS_INVISIBLE ='presence-invisible' # Jabberd2 - NS_IQ ='iq' # Jabberd2 --NS_LAST ='jabber:iq:last' -+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 -@@ -101,35 +103,36 @@ - 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' --NS_PRIVATE ='jabber:iq:private' -+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' --NS_ROSTER ='jabber:iq:roster' -+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' --NS_SEARCH ='jabber:iq:search' --NS_SERVER ='jabber:server' --NS_SESSION ='urn:ietf:params:xml:ns:xmpp-session' -+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' --NS_STREAMS ='http://etherx.jabber.org/streams' --NS_TIME ='jabber:iq:time' --NS_TLS ='urn:ietf:params:xml:ns:xmpp-tls' --NS_VACATION ='http://jabber.org/protocol/vacation' --NS_VCARD ='vcard-temp' --NS_VERSION ='jabber:iq:version' -+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_DATA_LAYOUT ='http://jabber.org/protocol/xdata-layout' # XEP-0141 --NS_DATA_VALIDATE ='http://jabber.org/protocol/xdata-validate' # XEP-0122 --NS_XMPP_STREAMS ='urn:ietf:params:xml:ns:xmpp-streams' -+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. diff --git a/xmpppy-gajim-simplexml-updates.diff b/xmpppy-gajim-simplexml-updates.diff deleted file mode 100644 index 880f634..0000000 --- a/xmpppy-gajim-simplexml-updates.diff +++ /dev/null @@ -1,400 +0,0 @@ ---- xmpp/simplexml.py~ 2007-09-11 16:46:16.000000000 +0400 -+++ xmpp/simplexml.py 2009-03-15 11:52:26.657623220 +0300 -@@ -12,7 +12,7 @@ - ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - ## GNU General Public License for more details. - --# $Id: simplexml.py,v 1.33 2007/09/11 12:46:16 normanr Exp $ -+# $Id: simplexml.py,v 1.34 2009/03/03 10:24:02 normanr Exp $ - - """Simplexml module provides xmpppy library with all needed tools to handle XML nodes and XML streams. - I'm personally using it in many other separate projects. It is designed to be as standalone as possible.""" -@@ -21,18 +21,19 @@ - - def XMLescape(txt): - """Returns provided string with symbols & < > " replaced by their respective XML entities.""" -- return txt.replace("&", "&").replace("<", "<").replace(">", ">").replace('"', """) -+ # replace also FORM FEED and ESC, because they are not valid XML chars -+ return txt.replace("&", "&").replace("<", "<").replace(">", ">").replace('"', """).replace(u'\x0C', "").replace(u'\x1B', "") - - ENCODING='utf-8' - def ustr(what): - """Converts object "what" to unicode string using it's own __str__ method if accessible or unicode method otherwise.""" -- if type(what) == type(u''): return what -+ if isinstance(what, unicode): return what - try: r=what.__str__() - except AttributeError: r=str(what) -- if type(r)<>type(u''): return unicode(r,ENCODING) -+ if not isinstance(r, unicode): return unicode(r,ENCODING) - return r - --class Node: -+class Node(object): - """ Node class describes syntax of separate XML Node. It have a constructor that permits node creation - from set of "namespace name", attributes and payload of text strings and other nodes. - It does not natively support building node from text string and uses NodeBuilder class for that purpose. -@@ -48,33 +49,63 @@ - replication (and using replication only to move upwards on the classes tree). - """ - FORCE_NODE_RECREATION=0 -- def __init__(self, tag=None, attrs={}, payload=[], parent=None, node=None): -+ def __init__(self, tag=None, attrs={}, payload=[], parent=None, nsp=None, node_built=False, node=None): - """ Takes "tag" argument as the name of node (prepended by namespace, if needed and separated from it - by a space), attrs dictionary as the set of arguments, payload list as the set of textual strings - and child nodes that this node carries within itself and "parent" argument that is another node -- that this one will be the child of. Also the __init__ can be provided with "node" argument that is -+ that this one will be the child of. Also the __init__ can be provided with "node" argument that is - either a text string containing exactly one node or another Node instance to begin with. If both - "node" and other arguments is provided then the node initially created as replica of "node" - provided and then modified to be compliant with other arguments.""" - if node: -- if self.FORCE_NODE_RECREATION and type(node)==type(self): node=str(node) -- if type(node)<>type(self): node=NodeBuilder(node,self) -+ if self.FORCE_NODE_RECREATION and isinstance(node, Node): -+ node=str(node) -+ if not isinstance(node, Node): -+ node=NodeBuilder(node,self) -+ node_built = True - else: -- self.name,self.namespace,self.attrs,self.data,self.kids,self.parent = node.name,node.namespace,{},[],[],node.parent -+ self.name,self.namespace,self.attrs,self.data,self.kids,self.parent,self.nsd = node.name,node.namespace,{},[],[],node.parent,{} - for key in node.attrs.keys(): self.attrs[key]=node.attrs[key] - for data in node.data: self.data.append(data) - for kid in node.kids: self.kids.append(kid) -- else: self.name,self.namespace,self.attrs,self.data,self.kids,self.parent = 'tag','',{},[],[],None -- -- if tag: self.namespace, self.name = ([self.namespace]+tag.split())[-2:] -- if parent: self.parent = parent -- if self.parent and not self.namespace: self.namespace=self.parent.namespace -- for attr in attrs.keys(): -+ for k,v in node.nsd.items(): self.nsd[k] = v -+ else: self.name,self.namespace,self.attrs,self.data,self.kids,self.parent,self.nsd = 'tag','',{},[],[],None,{} -+ if parent: -+ self.parent = parent -+ self.nsp_cache = {} -+ if nsp: -+ for k,v in nsp.items(): self.nsp_cache[k] = v -+ for attr,val in attrs.items(): -+ if attr == 'xmlns': -+ self.nsd[u''] = val -+ elif attr.startswith('xmlns:'): -+ self.nsd[attr[6:]] = val - self.attrs[attr]=attrs[attr] -- if type(payload) in (type(''),type(u'')): payload=[payload] -+ if tag: -+ if node_built: -+ pfx,self.name = (['']+tag.split(':'))[-2:] -+ self.namespace = self.lookup_nsp(pfx) -+ else: -+ if ' ' in tag: -+ self.namespace,self.name = tag.split() -+ else: -+ self.name = tag -+ if isinstance(payload, basestring): payload=[payload] - for i in payload: -- if type(i)==type(self): self.addChild(node=i) -- else: self.addData(i) -+ if isinstance(i, Node): self.addChild(node=i) -+ else: self.data.append(ustr(i)) -+ -+ def lookup_nsp(self,pfx=''): -+ ns = self.nsd.get(pfx,None) -+ if ns is None: -+ ns = self.nsp_cache.get(pfx,None) -+ if ns is None: -+ if self.parent: -+ ns = self.parent.lookup_nsp(pfx) -+ self.nsp_cache[pfx] = ns -+ else: -+ return 'http://www.gajim.org/xmlns/undeclared' -+ return ns - - def __str__(self,fancy=0): - """ Method used to dump node into textual representation. -@@ -82,22 +113,26 @@ - s = (fancy-1) * 2 * ' ' + "<" + self.name - if self.namespace: - if not self.parent or self.parent.namespace!=self.namespace: -- s = s + ' xmlns="%s"'%self.namespace -+ if 'xmlns' not in self.attrs: -+ s = s + ' xmlns="%s"'%self.namespace - for key in self.attrs.keys(): - val = ustr(self.attrs[key]) - s = s + ' %s="%s"' % ( key, XMLescape(val) ) - s = s + ">" -- cnt = 0 -+ cnt = 0 - if self.kids: - if fancy: s = s + "\n" - for a in self.kids: - if not fancy and (len(self.data)-1)>=cnt: s=s+XMLescape(self.data[cnt]) - elif (len(self.data)-1)>=cnt: s=s+XMLescape(self.data[cnt].strip()) -- if a: s = s + a.__str__(fancy and fancy+1) -+ if isinstance(a, Node): -+ s = s + a.__str__(fancy and fancy+1) -+ elif a: -+ s = s + a.__str__() - cnt=cnt+1 - if not fancy and (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt]) - elif (len(self.data)-1) >= cnt: s = s + XMLescape(self.data[cnt].strip()) -- if not self.kids and s[-1:]=='>': -+ if not self.kids and s.endswith('>'): - s=s[:-1]+' />' - if fancy: s = s + "\n" - else: -@@ -121,13 +156,14 @@ - def addChild(self, name=None, attrs={}, payload=[], namespace=None, node=None): - """ If "node" argument is provided, adds it as child node. Else creates new node from - the other arguments' values and adds it as well.""" -- if attrs.has_key('xmlns'): -+ if 'xmlns' in attrs: - raise AttributeError("Use namespace=x instead of attrs={'xmlns':x}") -- if namespace: name=namespace+' '+name - if node: - newnode=node - node.parent = self - else: newnode=Node(tag=name, parent=self, attrs=attrs, payload=payload) -+ if namespace: -+ newnode.setNamespace(namespace) - self.kids.append(newnode) - self.data.append(u'') - return newnode -@@ -144,7 +180,7 @@ - def delChild(self, node, attrs={}): - """ Deletes the "node" from the node's childs list, if "node" is an instance. - Else deletes the first node that have specified name and (optionally) attributes. """ -- if type(node)<>type(self): node=self.getTag(node,attrs) -+ if not isinstance(node, Node): node=self.getTag(node,attrs) - self.kids[self.kids.index(node)]=None - return node - def getAttrs(self): -@@ -196,13 +232,26 @@ - nodes=[] - for node in self.kids: - if not node: continue -- if namespace and namespace<>node.getNamespace(): continue -+ if namespace and namespace!=node.getNamespace(): continue - if node.getName() == name: - for key in attrs.keys(): -- if not node.attrs.has_key(key) or node.attrs[key]<>attrs[key]: break -+ if key not in node.attrs or node.attrs[key]!=attrs[key]: break - else: nodes.append(node) - if one and nodes: return nodes[0] - if not one: return nodes -+ -+ def iterTags(self, name, attrs={}, namespace=None): -+ """ Iterate over all children using specified arguments as filter. """ -+ for node in self.kids: -+ if not node: continue -+ if namespace is not None and namespace!=node.getNamespace(): continue -+ if node.getName() == name: -+ for key in attrs.keys(): -+ if key not in node.attrs or \ -+ node.attrs[key]!=attrs[key]: break -+ else: -+ yield node -+ - def setAttr(self, key, val): - """ Sets attribute "key" with the value "val". """ - self.attrs[key]=val -@@ -215,14 +264,14 @@ - def setNamespace(self, namespace): - """ Changes the node namespace. """ - self.namespace=namespace -- def setParent(self, node): -- """ Sets node's parent to "node". WARNING: do not checks if the parent already present -+ def setParent(self, node): -+ """ Sets node's parent to "node". WARNING: do not checks if the parent already present - and not removes the node from the list of childs of previous parent. """ - self.parent = node - def setPayload(self,payload,add=0): - """ Sets node payload according to the list specified. WARNING: completely replaces all node's - previous content. If you wish just to add child or CDATA - use addData or addChild methods. """ -- if type(payload) in (type(''),type(u'')): payload=[payload] -+ if isinstance(payload, basestring): payload=[payload] - if add: self.kids+=payload - else: self.kids=payload - def setTag(self, name, attrs={}, namespace=None): -@@ -243,7 +292,7 @@ - except: self.addChild(tag,attrs,payload=[ustr(val)]) - def has_attr(self,key): - """ Checks if node have attribute "key".""" -- return self.attrs.has_key(key) -+ return key in self.attrs - def __getitem__(self,item): - """ Returns node's attribute "item" value. """ - return self.getAttr(item) -@@ -283,7 +332,7 @@ - class NodeBuilder: - """ Builds a Node class minidom from data parsed to it. This class used for two purposes: - 1. Creation an XML Node from a textual representation. F.e. reading a config file. See an XML2Node method. -- 2. Handling an incoming XML stream. This is done by mangling -+ 2. Handling an incoming XML stream. This is done by mangling - the __dispatch_depth parameter and redefining the dispatch method. - You do not need to use this class directly if you do not designing your own XML handler.""" - def __init__(self,data=None,initial_node=None): -@@ -294,26 +343,37 @@ - "data" (if provided) feeded to parser immidiatedly after instance init. - """ - self.DEBUG(DBG_NODEBUILDER, "Preparing to handle incoming XML stream.", 'start') -- self._parser = xml.parsers.expat.ParserCreate(namespace_separator=' ') -+ self._parser = xml.parsers.expat.ParserCreate() - self._parser.StartElementHandler = self.starttag - self._parser.EndElementHandler = self.endtag -- self._parser.CharacterDataHandler = self.handle_data -+ self._parser.CharacterDataHandler = self.handle_cdata - self._parser.StartNamespaceDeclHandler = self.handle_namespace_start -+ self._parser.buffer_text = True - self.Parse = self._parser.Parse - - self.__depth = 0 -+ self.__last_depth = 0 -+ self.__max_depth = 0 - self._dispatch_depth = 1 - self._document_attrs = None -+ self._document_nsp = None - self._mini_dom=initial_node - self.last_is_data = 1 - self._ptr=None -- self.namespaces={"http://www.w3.org/XML/1998/namespace":'xml:'} -- self.xmlns="http://www.w3.org/XML/1998/namespace" -- -- if data: self._parser.Parse(data,1) -+ self.data_buffer = None -+ self.streamError = '' -+ if data: -+ self._parser.Parse(data,1) -+ -+ def check_data_buffer(self): -+ if self.data_buffer: -+ self._ptr.data.append(''.join(self.data_buffer)) -+ del self.data_buffer[:] -+ self.data_buffer = None - - def destroy(self): - """ Method used to allow class instance to be garbage-collected. """ -+ self.check_data_buffer() - self._parser.StartElementHandler = None - self._parser.EndElementHandler = None - self._parser.CharacterDataHandler = None -@@ -321,68 +381,97 @@ - - def starttag(self, tag, attrs): - """XML Parser callback. Used internally""" -- attlist=attrs.keys() # -- for attr in attlist: # FIXME: Crude hack. And it also slows down the whole library considerably. -- sp=attr.rfind(" ") # -- if sp==-1: continue # -- ns=attr[:sp] # -- attrs[self.namespaces[ns]+attr[sp+1:]]=attrs[attr] -- del attrs[attr] # -- self.__depth += 1 -+ self.check_data_buffer() -+ self._inc_depth() - self.DEBUG(DBG_NODEBUILDER, "DEPTH -> %i , tag -> %s, attrs -> %s" % (self.__depth, tag, `attrs`), 'down') - if self.__depth == self._dispatch_depth: -- if not self._mini_dom : self._mini_dom = Node(tag=tag, attrs=attrs) -- else: Node.__init__(self._mini_dom,tag=tag, attrs=attrs) -+ if not self._mini_dom : -+ self._mini_dom = Node(tag=tag, attrs=attrs, nsp = self._document_nsp, node_built=True) -+ else: -+ Node.__init__(self._mini_dom,tag=tag, attrs=attrs, nsp = self._document_nsp, node_built=True) - self._ptr = self._mini_dom - elif self.__depth > self._dispatch_depth: -- self._ptr.kids.append(Node(tag=tag,parent=self._ptr,attrs=attrs)) -+ self._ptr.kids.append(Node(tag=tag,parent=self._ptr,attrs=attrs, node_built=True)) - self._ptr = self._ptr.kids[-1] - if self.__depth == 1: -- self._document_attrs = attrs -- ns, name = (['']+tag.split())[-2:] -- self.stream_header_received(ns, name, attrs) -- if not self.last_is_data and self._ptr.parent: self._ptr.parent.data.append('') -+ self._document_attrs = {} -+ self._document_nsp = {} -+ nsp, name = (['']+tag.split(':'))[-2:] -+ for attr,val in attrs.items(): -+ if attr == 'xmlns': -+ self._document_nsp[u''] = val -+ elif attr.startswith('xmlns:'): -+ self._document_nsp[attr[6:]] = val -+ else: -+ self._document_attrs[attr] = val -+ ns = self._document_nsp.get(nsp, 'http://www.gajim.org/xmlns/undeclared-root') -+ try: -+ self.stream_header_received(ns, name, attrs) -+ except ValueError, e: -+ self._document_attrs = None -+ raise ValueError(str(e)) -+ if not self.last_is_data and self._ptr.parent: -+ self._ptr.parent.data.append('') - self.last_is_data = 0 - - def endtag(self, tag ): - """XML Parser callback. Used internally""" - self.DEBUG(DBG_NODEBUILDER, "DEPTH -> %i , tag -> %s" % (self.__depth, tag), 'up') -+ self.check_data_buffer() - if self.__depth == self._dispatch_depth: -+ if self._mini_dom.getName() == 'error': -+ self.streamError = self._mini_dom.getChildren()[0].getName() - self.dispatch(self._mini_dom) - elif self.__depth > self._dispatch_depth: - self._ptr = self._ptr.parent - else: - self.DEBUG(DBG_NODEBUILDER, "Got higher than dispatch level. Stream terminated?", 'stop') -- self.__depth -= 1 -+ self._dec_depth() - self.last_is_data = 0 - if self.__depth == 0: self.stream_footer_received() - -- def handle_data(self, data): -+ def handle_cdata(self, data): - """XML Parser callback. Used internally""" - self.DEBUG(DBG_NODEBUILDER, data, 'data') -- if not self._ptr: return - if self.last_is_data: -- self._ptr.data[-1] += data -- else: -- self._ptr.data.append(data) -+ if self.data_buffer: -+ self.data_buffer.append(data) -+ elif self._ptr: -+ self.data_buffer = [data] - self.last_is_data = 1 - - def handle_namespace_start(self, prefix, uri): - """XML Parser callback. Used internally""" -- if prefix: self.namespaces[uri]=prefix+':' -- else: self.xmlns=uri -+ self.check_data_buffer() -+ - def DEBUG(self, level, text, comment=None): - """ Gets all NodeBuilder walking events. Can be used for debugging if redefined.""" - def getDom(self): - """ Returns just built Node. """ -+ self.check_data_buffer() - return self._mini_dom - def dispatch(self,stanza): - """ Gets called when the NodeBuilder reaches some level of depth on it's way up with the built - node as argument. Can be redefined to convert incoming XML stanzas to program events. """ - def stream_header_received(self,ns,tag,attrs): - """ Method called when stream just opened. """ -+ self.check_data_buffer() - def stream_footer_received(self): - """ Method called when stream just closed. """ -+ self.check_data_buffer() -+ -+ def has_received_endtag(self, level=0): -+ """ Return True if at least one end tag was seen (at level) """ -+ return self.__depth <= level and self.__max_depth > level -+ -+ def _inc_depth(self): -+ self.__last_depth = self.__depth -+ self.__depth += 1 -+ self.__max_depth = max(self.__depth, self.__max_depth) -+ -+ def _dec_depth(self): -+ self.__last_depth = self.__depth -+ self.__depth -= 1 - - def XML2Node(xml): - """ Converts supplied textual string into XML node. Handy f.e. for reading configuration file.