#!/usr/bin/env python
+# Copyright 2006 James Bunton <james@delx.cjb.net>
+# Licensed for distribution under the GPL version 2, check COPYING for details
# Make 'cwd'/src in the PYTHONPATH
import sys, os, os.path
-For 0.9.4:
-* Fix any outstanding bugs.
-* Hopefully get all the translations up to scratch
+I'm hoping that 0.12 will be a quick release, so if I don't have time
+to do some of these things, they may get dropped. The first 5 are
+definites though. The last ones shouldn't take too long, so they will
+most likely get done also.
+I'll be merging changes into trunk as they become stable.
+
+
+For 0.12:
+* Clean up MSNConnection
+* Connect Session and MSNConnection with sigslot, so I can worry less
+ about managing references
+* Make ReconnectingClientFactory behave properly
+* Separate the all the removeMe methods into two parts. def removeMe(self):
+ will set about the destruction of the object, but will not free any
+ resources. def removed(self): will be called when it is safe to free
+ resources. This should clean the code up a lot, and make it work better.
+* Avatar sending with JEP-0008
+* On login failure, respond with presence error, not a message error
+* Steal infodump.py from Daniel
+* Handle adding bad contacts with a presence error reply
+* Password obscuring in xdb
+* Make sure everything runs fine under Psyco
+
+
+For 0.13:
+* epoll installer (point & shoot)
+* Twisted installer, eg run ./getTwisted.py and you suddenly have
+ a known-good version of Twisted and epoll installed in your PyMSNt
+ directory.
+* Port to new (not yet existing) Jabber library, based on Twisted
+* PEP nicks
+* PEP avatars
+* Chatstates
+* Steal XDB and language code from Daniel
+* More adhoc commands
+ - Update to latest version
+ - Restart/Shutdown transport
+ - Set a JID as a destination for log entries with filtering support,
+ eg only tracebacks, or errors or warnings
+ - Force a resync of a users roster
+ - Send announcement messages to all logged in users
+ - Set a MOTD
-For 0.10:
-* Load testing.
-* Check avatar compatibility with various MSN plugins.
-* Handle timeouts connecting to the MSN dispatch server as errors.
-* Get all translations to remove references to nicknames from registerText
-* Decide on default for legacy.msnw.GETALLAVATARS
-For 0.11:
-* File transfer (new & old)
<p>Bug reports and comments go to <a href="mailto:james@delx.cjb.net">James Bunton</a></p>
<p>I'll gladly help you with any problems, but please look through this whole page first</p>
+<hr/>
+<p>Copyright James Bunton <james at delx.cjb.net>. You may freely redistribute this file.</p>
+
</body>
</p>
<ul>
<li>Mandrake: <pre>urpmi python</pre></li>
-<li>Debian: <pre>apt-get install python2.3</pre></li>
+<li>Debian: <pre>apt-get install python</pre></li>
<li>Fedora: <pre>yum install python</pre></li>
<li>Centos 4: <pre>yum install python</pre></li>
<li>Suse: <pre>yast -i python</pre></li>
</p>
<ul>
<li>Mandrake: You will need to download the pycrypto and python openssl RPMs and install them as well as twisted using urpmi. Unfortunately I cannot provide links. I know you can get them from Mandrake Club though.</li>
-<li>Debian sarge: <pre>apt-get install python2.3-twisted python2.3-crypto python2.3-pyopenssl</pre></li>
-<li>Debian: <pre>apt-get install python2.3-twisted python2.3-twisted-words python2.3-crypto python2.3-pyopenssl</pre></li>
+<li>Debian sarge: <pre>apt-get install python-twisted python-crypto python-pyopenssl python-imaging</pre></li>
+<li>Debian: <pre>apt-get install python-twisted python-twisted-words python-crypto python-pyopenssl python-imaging</pre></li>
<li>Fedora: <pre>yum install python-twisted pyOpenSSL pycrypto</pre></li>
<li>Centos 4: <pre>yum install python-twisted python-crypto pyOpenSSL</pre></li>
<li>Suse: <pre>yast -i python-twisted python-openssl python-pycrypto</pre></li>
<p>Bug reports and comments go to <a href="mailto:james@delx.cjb.net">James Bunton</a></p>
<p>I'll gladly help you with any problems, but please look through this whole page, the README & config.xml files first.</p>
+<hr/>
+<p>Copyright James Bunton <james at delx.cjb.net>. You may freely redistribute this file.</p>
</body>
<li>If your MSN transport session seems frozen, and logging in & out doesn't fix it. Try double-clicking the MSN Transport icon in your list, and sending a "end" as a message. That should log you out of MSN and you can try using the transport again.</li>
</ul>
+<hr/>
+<p>Copyright James Bunton <james at delx.cjb.net>. You may freely redistribute this file.</p>
+
</body>
</html>
-# Copyright 2005 James Bunton <james@delx.cjb.net>
+# Copyright 2005-2006 James Bunton <james@delx.cjb.net>
# Licensed for distribution under the GPL version 2, check COPYING for details
from debug import LogEvent, INFO, WARN, ERROR
from twisted.internet import reactor
-from tlib.xmlw import Element
+from twisted.words.xish.domish import Element
import sha, base64, os, os.path
+# Copyright 2006 James Bunton <james@delx.cjb.net>
+# Licensed for distribution under the GPL version 2, check COPYING for details
+
from glue import LegacyConnection, LegacyGroupchat, translateAccount, startStats, updateStats, reloadConfig
from glue import name, url, version, mangle, id, namespace
from glue import formRegEntry, getAttributes, isGroupJID
-# Copyright 2005 James Bunton <james@delx.cjb.net>
+# Copyright 2004-2006 James Bunton <james@delx.cjb.net>
# Licensed for distribution under the GPL version 2, check COPYING for details
import utils
-from tlib import xmlw # Provides Element, parseText, parseFile
+from twisted.words.xish.domish import Element, parseText, parseFile
import avatar
import groupchat
import config
-# Copyright 2005 James Bunton <james@delx.cjb.net>
+# Copyright 2005-2006 James Bunton <james@delx.cjb.net>
# Licensed for distribution under the GPL version 2, check COPYING for details
from debug import LogEvent, INFO, WARN, ERROR
from twisted.internet import reactor
-from tlib.xmlw import Element
+from twisted.words.xish.domish import Element
import disco
import legacy
-# Copyright 2004-2005 James Bunton <james@delx.cjb.net>
+# Copyright 2004-2006 James Bunton <james@delx.cjb.net>
# Licensed for distribution under the GPL version 2, check COPYING for details
from debug import LogEvent, INFO, WARN, ERROR
from twisted.internet.defer import Deferred
from twisted.internet import reactor
-from tlib.xmlw import Element, jid
+from twisted.words.xish.domish import Element
+from twisted.words.protocols.jabber.jid import internJID
import sys
iqType = el.getAttribute("type")
ulang = utils.getLang(el)
try: # Stringprep
- froj = jid.intern(fro)
- to = jid.intern(to).full()
+ froj = internJID(fro)
+ to = internJID(to).full()
except Exception:
LogEvent(WARN, "", "Dropping IQ because of stringprep error")
-# Copyright 2005 James Bunton <james@delx.cjb.net>
+# Copyright 2005-2006 James Bunton <james@delx.cjb.net>
# Licensed for distribution under the GPL version 2, check COPYING for details
-from tlib.throttle import Throttler
-from tlib.xmlw import Element
+from throttle import Throttler
from twisted.internet import protocol
+from twisted.words.xish.domish import Element
import disco
import lang
# SOCKS5
-from tlib import socks5
+import socks5
import struct
class JEP65ConnectionSend(protocol.Protocol):
-# TODO, clean up and move this to tlib.socks5
+# TODO, clean up and move this to socks5
STATE_INITIAL = 1
STATE_WAIT_AUTHOK = 2
STATE_WAIT_CONNECTOK = 3
import utils
from twisted.internet import reactor
-from tlib.xmlw import Element
+from twisted.words.xish.domish import Element
+
from debug import LogEvent, INFO, WARN, ERROR
import disco
import jabw
# Copyright 2005 James Bunton <james@delx.cjb.net>
# Licensed for distribution under the GPL version 2, check COPYING for details
-from tlib import xmlw
+from twisted.words.protocols.jabber.jid import internJID
import utils
import config
if not os.path.isfile(file):
continue
file = xdb.unmangle(file).decode("utf-8")
- filej = xmlw.jid.intern(file).full()
+ filej = internJID(file).full()
if(file != filej):
file = xdb.mangle(file)
filej = xdb.mangle(filej)
# Licensed for distribution under the GPL version 2, check COPYING for details
import utils
-from tlib.xmlw import Element, jid
+from twisted.words.xish.domish import Element
+from twisted.words.protocols.jabber.jid import internJID
from debug import LogEvent, INFO, WARN, ERROR
import disco
# Strip the resource off any presence subscribes (as per XMPP RFC 3921 Section 5.1.6)
# Makes eJabberd behave :)
if ptype in ("subscribe", "subscribed", "unsubscribe", "unsubscribed"):
- to = jid.intern(to).userhost()
- fro = jid.intern(fro).userhost()
+ to = internJID(to).userhost()
+ fro = internJID(fro).userhost()
el = Element((None, "presence"))
el.attributes["to"] = to
fro = el.getAttribute("from")
to = el.getAttribute("to")
try:
- froj = jid.intern(fro)
- toj = jid.intern(to)
+ froj = internJID(fro)
+ toj = internJID(to)
except Exception, e:
LogEvent(WARN, self.jabberID)
return
""" Handles incoming presence packets """
#LogEvent(INFO, self.jabberID)
fro = el.getAttribute("from")
- froj = jid.intern(fro)
+ froj = internJID(fro)
to = el.getAttribute("to")
- toj = jid.intern(to)
+ toj = internJID(to)
# Grab the contents of the <presence/> packet
ptype = el.getAttribute("type")
# -*- coding: UTF-8 -*-
+# Copyright 2004-2006 James Bunton <james@delx.cjb.net>
+# Licensed for distribution under the GPL version 2, check COPYING for details
+
import config
# Licensed for distribution under the GPL version 2, check COPYING for details
import os.path
-import utils
from twisted.internet import task, error
-from tlib.xmlw import Element
-from tlib import msn
+from twisted.words.xish.domish import Element
+
from debug import LogEvent, INFO, WARN, ERROR
+from legacy import msn
import disco
import groupchat
import ft
url = "http://msn-transport.jabberstudio.org"
-version = "0.11.2-dev" # The transport version
+version = "0.11.2-dev" # The transport version
mangle = True # XDB '@' -> '%' mangling
id = "msn" # The transport identifier
+# Copyright 2006 James Bunton <james@delx.cjb.net>
+# Licensed for distribution under the GPL version 2, check COPYING for details
+
from msnw import MSNConnection, MultiSwitchboardSession
from msn import FORWARD_LIST, ALLOW_LIST, BLOCK_LIST, REVERSE_LIST, PENDING_LIST
from msn import STATUS_ONLINE, STATUS_OFFLINE, STATUS_HIDDEN, STATUS_IDLE, STATUS_AWAY, STATUS_BUSY, STATUS_BRB, STATUS_PHONE, STATUS_LUNCH
print "You must install pycrypto and pyopenssl."
raise
from twisted.python import failure, log
+from twisted.words.xish.domish import parseText, unescapeFromXml
-# Compat stuff
-from tlib import xmlw
# System imports
import types, operator, os, sys, base64, random, struct, random, sha, base64, StringIO, array, codecs, binascii
self.text = '<msnobj Creator="%s" Size="%s" Type="%s" Location="%s" Friendly="%s" SHA1D="%s" SHA1C="%s"/>' % (self.creator, str(self.size), str(self.type), self.location, self.friendly, self.sha1d, sha1c)
def parse(self, s):
- e = xmlw.parseText(s, True)
+ e = parseText(s, True)
if not e:
return # Parse failed
self.creator = e.getAttribute("Creator")
self.gotRealtimeEmailNotification(mailfrom, fromaddr, subject)
def _gotMSNAlert(self, message):
- notification = xmlw.parseText(message.message, beExtremelyLenient=True)
+ notification = parseText(message.message, beExtremelyLenient=True)
siteurl = notification.getAttribute("siteurl")
notid = notification.getAttribute("id")
p1 = lm.find("<psm>") + 5
p2 = lm.find("</psm>")
if p1 >= 0 and p2 >= 0:
- personal = xmlw.unescapeFromXml(message.message[p1:p2])
+ personal = unescapeFromXml(message.message[p1:p2])
msnContact.personal = personal
self.contactPersonalChanged(message.userHandle, personal)
else:
-# Copyright 2004-2005 James Bunton <james@delx.cjb.net>
+# Copyright 2004-2006 James Bunton <james@delx.cjb.net>
# Licensed for distribution under the GPL version 2, check COPYING for details
# Twisted imports
# Local imports
from debug import LogEvent, INFO, WARN, ERROR
-from tlib.msn import msn
+import msn
# Copyright (c) 2001-2005 Twisted Matrix Laboratories.
-# Copyright (c) 2005-2006 James Bunton
-# See LICENSE for details.
+# Copyright (c) 2005-2006 James Bunton <james@delx.cjb.net>
+# Licensed for distribution under the GPL version 2, check COPYING for details
+
"""
Test cases for msn.
sys.setdefaultencoding("utf-8")
sys.stdout = codecs.lookup('utf-8')[-1](sys.stdout)
+
# Find the best reactor
selectWarning = "Unable to install any good reactors (kqueue, epoll, poll).\nWe fell back to using select. You may have scalability problems.\nThis reactor will not support more than 1024 connections at a time."
try:
sys.exit(1)
bestreactor.install()
+import twistfix
+twistfix.main()
# Must load config before everything else
from twisted.internet import reactor, task
from twisted.internet.defer import Deferred
-from tlib.xmlw import Element, jid, component
+from twisted.words.xish.domish import Element
+from twisted.words.protocols.jabber import component
+from twisted.words.protocols.jabber.jid import internJID
+
from debug import LogEvent, INFO, WARN, ERROR
import debug
def onMessage(self, el):
fro = el.getAttribute("from")
try:
- froj = jid.intern(fro)
+ froj = internJID(fro)
except Exception, e:
LogEvent(WARN, "", "Failed stringprep.")
return
fro = el.getAttribute("from")
to = el.getAttribute("to")
try:
- froj = jid.intern(fro)
- toj = jid.intern(to)
+ froj = internJID(fro)
+ toj = internJID(to)
except Exception, e:
LogEvent(WARN, "", "Failed stringprep.")
return
import utils
from twisted.internet import reactor, task, protocol, error
-from tlib.xmlw import Element, jid
+from twisted.words.xish.domish import Element
+from twisted.words.protocols.jabber.jid import internJID
from debug import LogEvent, INFO, WARN, ERROR
import svninfo
import jabw
ID = el.getAttribute("id")
ulang = utils.getLang(el)
- if config.admins.count(jid.intern(to).userhost()) == 0:
+ if config.admins.count(internJID(to).userhost()) == 0:
self.pytrans.discovery.sendIqError(to=to, fro=config.jid, ID=ID, xmlns=disco.COMMANDS, etype="cancel", condition="not-authorized")
return
def incomingIq(self, el):
itype = el.getAttribute("type")
fro = el.getAttribute("from")
- froj = jid.intern(fro)
+ froj = internJID(fro)
to = el.getAttribute("to")
ID = el.getAttribute("id")
def incomingIq(self, el):
itype = el.getAttribute("type")
fro = el.getAttribute("from")
- froj = jid.intern(fro)
+ froj = internJID(fro)
to = el.getAttribute("to")
- toj = jid.intern(to)
+ toj = internJID(to)
ID = el.getAttribute("id")
if itype != "get" and itype != "error":
self.pytrans.discovery.sendIqError(to=fro, fro=config.jid, ID=ID, xmlns="vcard-temp", etype="cancel", condition="feature-not-implemented")
def incomingIq(self, el):
itype = el.getAttribute("type")
fro = el.getAttribute("from")
- froj = jid.intern(fro)
+ froj = internJID(fro)
to = el.getAttribute("to")
- toj = jid.intern(to)
+ toj = internJID(to)
ID = el.getAttribute("id")
if(itype != "get" and itype != "error"):
else:
return errOut()
- froj = jid.intern(el.getAttribute("from"))
- toj = jid.intern(el.getAttribute("to"))
+ froj = internJID(el.getAttribute("from"))
+ toj = internJID(el.getAttribute("to"))
session = self.pytrans.sessions.get(froj.userhost(), None)
if not session:
return errOut()
def errOut():
self.pytrans.discovery.sendIqError(to=el.getAttribute("from"), fro=el.getAttribute("to"), ID=ID, xmlns=disco.SI, etype="cancel", condition="bad-request")
- toj = jid.intern(el.getAttribute("to"))
- froj = jid.intern(el.getAttribute("from"))
+ toj = internJID(el.getAttribute("to"))
+ froj = internJID(el.getAttribute("from"))
session = self.pytrans.sessions.get(froj.userhost(), None)
if not session:
return errOut()
if el.getAttribute("type") != "set":
return errOut()
- toj = jid.intern(el.getAttribute("to"))
- froj = jid.intern(el.getAttribute("from"))
+ toj = internJID(el.getAttribute("to"))
+ froj = internJID(el.getAttribute("from"))
query = el.query
if not (query and query.getAttribute("mode", "tcp") == "tcp"):
-# Copyright 2004-2005 James Bunton <james@delx.cjb.net>
+# Copyright 2004-2006 James Bunton <james@delx.cjb.net>
# Licensed for distribution under the GPL version 2, check COPYING for details
import utils
-from tlib.xmlw import Element, jid
+from twisted.words.xish.domish import Element
+from twisted.words.protocols.jabber.jid import internJID
from debug import LogEvent, INFO, WARN, ERROR
import disco
import session
(blah1, password, blah3) = self.getRegInfo(jabberID)
reginfo = legacy.formRegEntry(username, password)
- self.pytrans.xdb.set(jid.intern(jabberID).userhost(), legacy.namespace, reginfo)
+ self.pytrans.xdb.set(internJID(jabberID).userhost(), legacy.namespace, reginfo)
def getRegInfo(self, jabberID):
LogEvent(INFO)
- result = self.pytrans.xdb.request(jid.intern(jabberID).userhost(), legacy.namespace)
+ result = self.pytrans.xdb.request(internJID(jabberID).userhost(), legacy.namespace)
if(result == None):
LogEvent(INFO, "", "Not registered!")
return None
ID = incoming.getAttribute("id")
fro = incoming.getAttribute("from")
LogEvent(INFO)
- source = jid.intern(fro).userhost()
+ source = internJID(fro).userhost()
ulang = utils.getLang(incoming)
username = None
password = None
LogEvent(INFO, "", "Updated XDB")
self.successReply(incoming)
LogEvent(INFO, "", "Sent a result Iq")
- (user, host, res) = jid.parse(incoming.getAttribute("from"))
+ (user, host, res) = jid.internJID(incoming.getAttribute("from"))
jabw.sendPresence(self.pytrans, to=user + "@" + host, fro=config.jid, ptype="subscribe")
if(config.registerMessage):
jabw.sendMessage(self.pytrans, to=incoming.getAttribute("from"), fro=config.jid, body=config.registerMessage)
-# Copyright 2004-2005 James Bunton <james@delx.cjb.net>
+# Copyright 2004-2006 James Bunton <james@delx.cjb.net>
# Licensed for distribution under the GPL version 2, check COPYING for details
-from tlib.xmlw import jid
+from twisted.words.protocols.jabber.jid import internJID
import utils
import legacy
def updateNickname(self, nickname):
self.nickname = nickname
if not self.nickname:
- j = jid.intern(self.jabberID)
+ j = internJID(self.jabberID)
self.nickname = j.user
self.setStatus(self.show, self.status)
#!/usr/bin/env python
+# Copyright 2006 James Bunton <james@delx.cjb.net>
+# Licensed for distribution under the GPL version 2, check COPYING for details
-from tlib.xmlw import parseFile
+from twisted.words.xish.domish import parseFile
import os, os.path
-from twisted.internet import task
+# Copyright 2006 James Bunton <james@delx.cjb.net>
+# Licensed for distribution under the GPL version 2, check COPYING for details
+from twisted.internet import task
class Throttler:
def __init__(self, consumer, speed):
+++ /dev/null
-# -*- test-case-name: twisted.test.test_domish -*-
-#
-# Twisted, the Framework of Your Internet
-# Copyright (C) 2001 Matthew W. Lefkowitz
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of version 2.1 of the GNU Lesser General Public
-# License as published by the Free Software Foundation.
-#
-# This library 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
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-from __future__ import generators
-
-import types
-
-try:
- import cStringIO as StringIO
-except ImportError:
- import StringIO
-
-def _splitPrefix(name):
- """Internal method for splitting a prefixed Element name into its respective parts """
- ntok = name.split(":", 1)
- if len(ntok) == 2:
- return ntok
- else:
- return (None, ntok[0])
-
-class _Serializer:
- """ Internal class which serializes an Element tree into a buffer """
- def __init__(self, prefixes = None):
- self.cio = StringIO.StringIO()
- self.prefixes = prefixes or {}
- self.prefixCounter = 0
-
- def getValue(self):
- return self.cio.getvalue()
-
- def getPrefix(self, uri):
- if not self.prefixes.has_key(uri):
- self.prefixes[uri] = "xn%d" % (self.prefixCounter)
- self.prefixCounter = self.prefixCounter + 1
- return self.prefixes[uri]
-
- def serialize(self, elem, closeElement = 1):
- # Optimization shortcuts
- write = self.cio.write
-
- # Shortcut, check to see if elem is actually a chunk o' serialized XML
- if isinstance(elem, SerializedXML):
- write(elem.encode("utf-8"))
- return
-
- # Shortcut, check to see if elem is actually a string (aka Cdata)
- if isinstance(elem, types.StringTypes):
- write(escapeToXml(elem).encode("utf-8"))
- return
-
- # Further optimizations
- parent = elem.parent
- name = elem.name
- uri = elem.uri
- defaultUri = elem.defaultUri
-
-
- # Seralize element name
- if defaultUri == uri:
- if parent == None or defaultUri == parent.defaultUri:
- write("<%s" % (name))
- else:
- write("<%s xmlns='%s' " % (name, defaultUri))
- else:
- prefix = self.getPrefix(uri)
- if parent == None or elem.defaultUri == parent.defaultUri:
- write("<%s:%s xmlns:%s='%s'" % (prefix, name, prefix, uri))
- else:
- write("<%s:%s xmlns:%s='%s' xmlns='%s'" % (prefix, name, prefix, uri, defaultUri))
-
- # Serialize attributes
- for k,v in elem.attributes.items():
- # If the attribute name is a list, it's a qualified attribute
- if isinstance(k, types.TupleType):
- write((" %s:%s='%s'" % (self.getPrefix(k[0]), k[1], escapeToXml(v, 1))).encode("utf-8"))
- else:
- write((" %s='%s'" % ( k, escapeToXml(v, 1))).encode("utf-8"))
-
- # Shortcut out if this is only going to return
- # the element (i.e. no children)
- if closeElement == 0:
- write(">")
- return
-
- # Serialize children
- if len(elem.children) > 0:
- write(">")
- for c in elem.children:
- self.serialize(c)
- # Add closing tag
- if defaultUri == uri:
- write("</%s>" % (name))
- else:
- write("</%s:%s>" % (self.getPrefix(uri), name))
- else:
- write("/>")
-
-class _ListSerializer:
- """ Internal class which serializes an Element tree into a buffer """
- def __init__(self, prefixes = None):
- self.writelist = []
- self.prefixes = prefixes or {}
- self.prefixCounter = 0
-
- def getValue(self):
- d = "".join(self.writelist)
- return d.encode("utf-8")
-
- def getPrefix(self, uri):
- if not self.prefixes.has_key(uri):
- self.prefixes[uri] = "xn%d" % (self.prefixCounter)
- self.prefixCounter = self.prefixCounter + 1
- return self.prefixes[uri]
-
- def serialize(self, elem, closeElement = 1):
- # Optimization shortcuts
- write = self.writelist.append
-
- # Shortcut, check to see if elem is actually a chunk o' serialized XML
- if isinstance(elem, SerializedXML):
- write(elem)
- return
-
- # Shortcut, check to see if elem is actually a string (aka Cdata)
- if isinstance(elem, types.StringTypes):
- write(escapeToXml(elem))
- return
-
- # Further optimizations
- parent = elem.parent
- name = elem.name
- uri = elem.uri
- defaultUri = elem.defaultUri
-
- # Seralize element name
- if defaultUri == uri:
- if parent == None or defaultUri == parent.defaultUri:
- write("<%s" % (name))
- else:
- write("<%s xmlns='%s' " % (name, defaultUri))
- else:
- prefix = self.getPrefix(uri)
- if parent == None or elem.defaultUri == parent.defaultUri:
- write("<%s:%s xmlns:%s='%s'" % (prefix, name, prefix, uri))
- else:
- write("<%s:%s xmlns:%s='%s' xmlns='%s'" % (prefix, name, prefix, uri, defaultUri))
-
- # Serialize attributes
- for k,v in elem.attributes.items():
- # If the attribute name is a list, it's a qualified attribute
- if isinstance(k, types.TupleType):
- write(" %s:%s='%s'" % (self.getPrefix(k[0]), k[1], escapeToXml(v, 1)))
- else:
- write((" %s='%s'" % ( k, escapeToXml(v, 1))))
-
- # Shortcut out if this is only going to return
- # the element (i.e. no children)
- if closeElement == 0:
- write(">")
- return
-
- # Serialize children
- if len(elem.children) > 0:
- write(">")
- for c in elem.children:
- self.serialize(c)
- # Add closing tag
- if defaultUri == uri:
- write("</%s>" % (name))
- else:
- write("</%s:%s>" % (self.getPrefix(uri), name))
- else:
- write("/>")
-
-
-SerializerClass = _Serializer
-
-def escapeToXml(text, isattrib = 0):
- """Escape text to proper XML form, per section 2.3 in the XML specification.
-
- @type text: L{str}
- @param text: Text to escape
-
- @type isattrib: L{Boolean}
- @param isattrib: Triggers escaping of characters necessary for use as attribute values
- """
- text = text.replace("&", "&")
- text = text.replace("<", "<")
- text = text.replace(">", ">")
- if isattrib == 1:
- text = text.replace("'", "'")
- text = text.replace("\"", """)
- return text
-
-def unescapeFromXml(text):
- text = text.replace("<", "<")
- text = text.replace(">", ">")
- text = text.replace("'", "'")
- text = text.replace(""", "\"")
- text = text.replace("&", "&")
- return text
-
-def generateOnlyKlass(list, klass):
- """ Filters items in a list by class
- """
- for n in list:
- if n.__class__ == klass:
- yield n
-
-def generateElementsQNamed(list, name, uri):
- """ Filters Element items in a list with matching name and URI
- """
- for n in list:
- if n.__class__ == Element and n.name == name and n.uri == uri:
- yield n
-
-def generateElementsNamed(list, name):
- """ Filters Element items in a list with matching name, regardless of URI
- """
- for n in list:
- if n.__class__ == Element and n.name == name:
- yield n
-
-
-class SerializedXML(str):
- """ Marker class for pre-serialized XML in the DOM """
- pass
-
-
-class Namespace:
- """ Convenience object for tracking namespace declarations
- """
- def __init__(self, uri):
- self._uri = uri
- def __getattr__(self, n):
- return (self._uri, n)
- def __getitem__(self, n):
- return (self._uri, n)
-
-
-class Element(object):
- """Object representing a container (a.k.a. tag or element) in an HTML or XML document.
-
- An Element contains a series of attributes (name/value pairs),
- content (character data), and other child Element objects. When building a document
- with markup (such as HTML or XML), use this object as the starting point.
-
- @type uri: C{str}
- @ivar uri: URI of this Element's name
-
- @type defaultUri: C{str}
- @ivar defaultUri: URI this Element exists within
-
- @type name: C{str}
- @ivar name: Name of this Element
-
- @type children: C{list}
- @ivar children: List of child Elements and content
-
- @type parent: C{Element}
- @ivar parent: Reference to the parent Element, if any.
-
- @type attributes: C{dict}
- @ivar attributes: Dictionary of attributes associated with this Element.
-
- """
- _idCounter = 0
- def __init__(self, qname, defaultUri = None, attribs = None):
- """
- @param qname: Tuple of (uri, name)
- @param defaultUri: The default URI of the element; defaults to the URI specified in L{qname}
- @param attribs: Dictionary of attributes
- """
- self.uri, self.name = qname
- self.defaultUri = defaultUri or self.uri
- self.attributes = attribs or {}
- self.children = []
- self.parent = None
-
- def __getattr__(self, key):
- # Check child list for first Element with a name matching the key
- for n in self.children:
- if n.__class__ == Element and n.name == key:
- return n
-
- # Tweak the behaviour so that it's more friendly about not
- # finding elements -- we need to document this somewhere :)
- return None
-
- def __getitem__(self, key):
- return self.attributes[self._dqa(key)]
-
- def __delitem__(self, key):
- del self.attributes[self._dqa(key)];
-
- def __setitem__(self, key, value):
- self.attributes[self._dqa(key)] = value
-
- def __str__(self):
- """ Retrieve the first CData (content) node
- """
- for n in self.children:
- if isinstance(n, types.StringTypes): return n
- return ""
-
- def _dqa(self, attr):
- """Dequalify an attribute key as needed"""
- if isinstance(attr, types.TupleType) and attr[0] == self.uri:
- return attr[1]
- else:
- return attr
-
- def getAttribute(self, attribname, default = None):
- """Retrieve the value of attribname, if it exists """
- return self.attributes.get(attribname, default)
-
- def hasAttribute(self, attrib):
- """Determine if the specified attribute exists """
- return self.attributes.has_key(self._dqa(attrib))
-
- def compareAttribute(self, attrib, value):
- """Safely compare the value of an attribute against a provided value; None-safe. """
- return self.attributes.get(self._dqa(attrib), None) == value
-
- def swapAttributeValues(self, left, right):
- """Swap the values of two attribute"""
- d = self.attributes
- l = d[left]
- d[left] = d[right]
- d[right] = l
-
- def addChild(self, node):
- """Add a child to this Element"""
- if node.__class__ == Element:
- node.parent = self
- self.children.append(node)
- return self.children[-1]
-
- def addContent(self, text):
- """Add some text data to this element"""
- c = self.children
- if len(c) > 0 and isinstance(c[-1], types.StringTypes):
- c[-1] = c[-1] + text
- else:
- c.append(text)
- return c[-1]
-
- def addElement(self, name, defaultUri = None, content = None):
- """Add a new child Element to this Element; preferred method
- """
- result = None
- if isinstance(name, type(())):
- defaultUri = defaultUri or name[0]
- self.children.append(Element(name, defaultUri))
- else:
- defaultUri = defaultUri or self.defaultUri
- self.children.append(Element((self.uri, name), defaultUri))
-
- result = self.children[-1]
- result.parent = self
-
- if content:
- result.children.append(content)
-
- return result
-
- def addRawXml(self, rawxmlstring):
- """Add a pre-serialized chunk o' XML as a child of this Element.
- """
- self.children.append(SerializedXML(rawxmlstring))
-
- def addUniqueId(self):
- """Add a unique (across a given Python session) id attribute to this Element"""
- self.attributes["id"] = "H_%d" % Element._idCounter
- Element._idCounter = Element._idCounter + 1
-
- def elements(self):
- """Iterate across all children of this Element that are Elements"""
- return generateOnlyKlass(self.children, Element)
-
- def toXml(self, prefixes = None, closeElement = 1):
- """Serialize this Element and all children to a string """
- s = SerializerClass(prefixes)
- s.serialize(self, closeElement)
- return s.getValue()
-
- def firstChildElement(self):
- for c in self.children:
- if c.__class__ == Element:
- return c
- return None
-
- def getElement(self, tagName):
- for child in self.elements():
- if(child.name == tagName):
- return child
-
-
-class ParserError(Exception):
- """ Exception thrown when a parsing error occurs """
- pass
-
-def elementStream():
- """ Preferred method to construct an ElementStream
-
- Uses Expat-based stream if available, and falls back to Sux if necessary.
- """
- try:
- es = ExpatElementStream()
- return es
- except ImportError:
- es = SuxElementStream()
- return es
-
-from twisted.protocols import sux
-class SuxElementStream(sux.XMLParser):
- def __init__(self):
- self.connectionMade()
- self.DocumentStartEvent = None
- self.ElementEvent = None
- self.DocumentEndEvent = None
- self.currElem = None
- self.rootElem = None
- self.documentStarted = False
- self.defaultNsStack = []
- self.prefixStack = []
- self.parse = self.dataReceived
-
- def findUri(self, prefix):
- # Walk prefix stack backwards, looking for the uri
- # matching the specified prefix
- stack = self.prefixStack
- for i in range(-1, (len(self.prefixStack)+1) * -1, -1):
- if prefix in stack[i]:
- return stack[i][prefix]
- return None
-
- def gotTagStart(self, name, attributes):
- defaultUri = None
- localPrefixes = {}
- attribs = {}
- uri = None
-
- # Pass 1 - Identify namespace decls
- for k, v in attributes.items():
- if k.startswith("xmlns"):
- x, p = _splitPrefix(k)
- if (x == None): # I.e. default declaration
- defaultUri = v
- else:
- localPrefixes[p] = v
- del attributes[k]
-
- # Push namespace decls onto prefix stack
- self.prefixStack.append(localPrefixes)
-
- # Determine default namespace for this element; if there
- # is one
- if defaultUri == None and len(self.defaultNsStack) > 0:
- defaultUri = self.defaultNsStack[-1]
-
- # Fix up name
- prefix, name = _splitPrefix(name)
- if prefix == None: # This element is in the default namespace
- uri = defaultUri
- else:
- # Find the URI for the prefix
- uri = self.findUri(prefix)
-
- # Pass 2 - Fix up and escape attributes
- for k, v in attributes.items():
- p, n = _splitPrefix(k)
- if p == None:
- attribs[n] = v
- else:
- attribs[(self.findUri(p)), n] = unescapeFromXml(v)
-
- # Construct the actual Element object
- e = Element((uri, name), defaultUri, attribs)
-
- # Save current default namespace
- self.defaultNsStack.append(defaultUri)
-
- # Document already started
- if self.documentStarted:
- # Starting a new packet
- if self.currElem == None:
- self.currElem = e
- # Adding to existing element
- else:
- self.currElem = self.currElem.addChild(e)
- # New document
- else:
- self.rootElem = e
- self.documentStarted = True
- self.DocumentStartEvent(e)
-
- def gotText(self, data):
- if self.currElem != None:
- self.currElem.addContent(data)
-
- def gotCData(self, data):
- if self.currElem != None:
- self.currElem.addContent(data)
-
- def gotComment(self, data):
- # Ignore comments for the moment
- pass
-
- entities = { "amp" : "&",
- "lt" : "<",
- "gt" : ">",
- "apos": "'",
- "quot": "\"" }
-
- def gotEntityReference(self, entityRef):
- # If this is an entity we know about, add it as content
- # to the current element
- if entityRef in SuxElementStream.entities:
- self.currElem.addContent(SuxElementStream.entities[entityRef])
-
- def gotTagEnd(self, name):
- # Ensure the document hasn't already ended
- if self.rootElem == None:
- # XXX: Write more legible explanation
- raise ParserError, "Element closed after end of document."
-
- # Fix up name
- prefix, name = _splitPrefix(name)
- if prefix == None:
- uri = self.defaultNsStack[-1]
- else:
- uri = self.findUri(prefix)
-
- # End of document
- if self.currElem == None:
- # Ensure element name and uri matches
- if self.rootElem.name != name or self.rootElem.uri != uri:
- raise ParserError, "Mismatched root elements"
- self.DocumentEndEvent()
- self.rootElem = None
-
- # Other elements
- else:
- # Ensure the tag being closed matches the name of the current
- # element
- if self.currElem.name != name or self.currElem.uri != uri:
- # XXX: Write more legible explanation
- raise ParserError, "Malformed element close"
-
- # Pop prefix and default NS stack
- self.prefixStack.pop()
- self.defaultNsStack.pop()
-
- # Check for parent null parent of current elem;
- # that's the top of the stack
- if self.currElem.parent == None:
- self.ElementEvent(self.currElem)
- self.currElem = None
-
- # Anything else is just some element wrapping up
- else:
- self.currElem = self.currElem.parent
-
-
-class ExpatElementStream:
- def __init__(self):
- import pyexpat
- self.DocumentStartEvent = None
- self.ElementEvent = None
- self.DocumentEndEvent = None
- self.parser = pyexpat.ParserCreate("UTF-8", " ")
- self.parser.StartElementHandler = self._onStartElement
- self.parser.EndElementHandler = self._onEndElement
- self.parser.CharacterDataHandler = self._onCdata
- self.parser.StartNamespaceDeclHandler = self._onStartNamespace
- self.parser.EndNamespaceDeclHandler = self._onEndNamespace
- self.currElem = None
- self.defaultNsStack = []
- self.documentStarted = 0
-
- def parse(self, buffer):
- self.parser.Parse(buffer)
-
- def _onStartElement(self, name, attrs):
- # Generate a qname tuple from the provided name
- qname = name.split(" ")
-
- # Process attributes
- for k, v in attrs.items():
- if k.find(" ") != -1:
-# attrs[k.split(" ")] = v
- aqname = k.split(" ")
- attrs[(aqname[0], aqname[1])] = v
- del attrs[k]
-
- # Construct the new element
- e = Element(qname, self.defaultNsStack[-1], attrs)
-
- # Document already started
- if self.documentStarted == 1:
- if self.currElem != None:
- self.currElem.children.append(e)
- e.parent = self.currElem
- self.currElem = e
-
- # New document
- else:
- self.documentStarted = 1
- self.DocumentStartEvent(e)
-
- def _onEndElement(self, _):
- # Check for null current elem; end of doc
- if self.currElem == None:
- self.DocumentEndEvent()
-
- # Check for parent that is None; that's
- # the top of the stack
- elif self.currElem.parent == None:
- self.ElementEvent(self.currElem)
- self.currElem = None
-
- # Anything else is just some element in the current
- # packet wrapping up
- else:
- self.currElem = self.currElem.parent
-
- def _onCdata(self, data):
- if self.currElem != None:
- self.currElem.addContent(data)
-
- def _onStartNamespace(self, prefix, uri):
- # If this is the default namespace, put
- # it on the stack
- if prefix == None:
- self.defaultNsStack.append(uri)
-
- def _onEndNamespace(self, prefix):
- # Remove last element on the stack
- if prefix == None:
- self.defaultNsStack.pop()
-
-
-
-def parseText(text):
- t = TextParser()
- t.parseString(text)
- return t.root
-
-def parseFile(filename):
- t = TextParser()
- t.parseFile(filename)
- return t.root
-
-class TextParser:
- """ Taken from http://xoomer.virgilio.it/dialtone/rsschannel.py """
-
- def __init__(self):
- self.root = None
-
- def parseFile(self, filename):
- return self.parseString(file(filename).read())
-
- def parseString(self, data):
- es = SuxElementStream()
- es.DocumentStartEvent = self.docStart
- es.DocumentEndEvent = self.docEnd
- es.ElementEvent = self.element
- es.parse(data)
- return self.root
-
- def docStart(self, e):
- self.root = e
-
- def docEnd(self):
- pass
-
- def element(self, e):
- self.root.addChild(e)
-
-
-## class FileParser(ElementStream):
-## def __init__(self):
-## ElementStream.__init__(self)
-## self.DocumentStartEvent = self.docStart
-## self.ElementEvent = self.elem
-## self.DocumentEndEvent = self.docEnd
-## self.done = 0
-
-## def docStart(self, elem):
-## self.document = elem
-
-## def elem(self, elem):
-## self.document.addChild(elem)
-
-## def docEnd(self):
-## self.done = 1
-
-## def parse(self, filename):
-## for l in open(filename).readlines():
-## self.parser.Parse(l)
-## assert self.done == 1
-## return self.document
-
-## def parseFile(filename):
-## return FileParser().parse(filename)
-
-
+++ /dev/null
-
-# Twisted, the Framework of Your Internet
-# Copyright (C) 2001 Matthew W. Lefkowitz
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of version 2.1 of the GNU Lesser General Public
-# License as published by the Free Software Foundation.
-#
-# This library 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
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-"""
-
-Twisted Jabber: Jabber Protocol Helpers
-
-"""
+++ /dev/null
-# -*- test-case-name: twisted.test.test_jabbercomponent -*-
-#
-# Twisted, the Framework of Your Internet
-# Copyright (C) 2001 Matthew W. Lefkowitz
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of version 2.1 of the GNU Lesser General Public
-# License as published by the Free Software Foundation.
-#
-# This library 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
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-from tlib import domish
-from twisted.xish import xpath, utility
-from tlib import xmlstream
-
-DigestAuthQry = xpath.intern("/iq/query/digest")
-PlaintextAuthQry = xpath.intern("/iq/query/password")
-
-def basicClientFactory(jid, secret):
- a = BasicAuthenticator(jid, secret)
- return xmlstream.XmlStreamFactory(a)
-
-class IQ(domish.Element):
- """ Wrapper for a Info/Query packet
-
- This provides the necessary functionality to send IQs and get notified
- when a result comes back. It's a subclass from domish.Element, so you can
- use the standard DOM manipulation calls to add data to the outbound
- request.
-
- @type callbacks: C{hemp.utility.CallbackList}
- @cvar callbacks: Callback list to be notified when response comes back
-
- """
- def __init__(self, xmlstream, type = "set"):
- """
- @type xmlstream: C{XmlStream}
- @param xmlstream: XmlStream to use for transmission of this IQ
-
- @type type: C{str}
- @param type: IQ type identifier ('get' or 'set')
-
- """
- domish.Element.__init__(self, ("jabber:client", "iq"))
- self.addUniqueId()
- self["type"] = type
- self._xmlstream = xmlstream
- self.callbacks = utility.CallbackList()
-
- def addCallback(self, fn, *args, **kwargs):
- """
- Register a callback for notification when the IQ result
- is available.
-
- """
- self.callbacks.addCallback(True, fn, *args, **kwargs)
-
- def send(self, to = None):
- """
- Call this method to send this IQ request via the associated XmlStream
-
- @type to: C{str}
- @type to: Jabber ID of the entity to send the request to
-
- @returns: Callback list for this IQ. Any callbacks added to this list will
- be fired when the result comes back.
- """
- if to != None:
- self["to"] = to
- self._xmlstream.addOnetimeObserver("/iq[@id='%s']" % self["id"], \
- self._resultEvent)
- self._xmlstream.send(self.toXml())
-
- def _resultEvent(self, iq):
- self.callbacks.callback(iq)
- self.callbacks = None
-
-class BasicAuthenticator(xmlstream.ConnectAuthenticator):
- """ Authenticates an XmlStream against a Jabber server as a Client
-
- This only implements non-SASL authentication, per
- U{JEP 78<http://www.jabber.org/jeps/jep-0078.html>}. Additionally, this
- authenticator provides the ability to perform inline registration, per
- U{JEP 77<http://www.jabber.org/jeps/jep-0077.html>}.
-
- Under normal circumstances, the BasicAuthenticator generates the L{STREAM_AUTHD_EVENT}
- once the stream has authenticated. However, it can also generate other events, such
- as:
- - L{INVALID_USER_EVENT} : Authentication failed, due to invalid username
- - L{AUTH_FAILED_EVENT} : Authentication failed, due to invalid password
- - L{REGISTER_FAILED_EVENT} : Registration failed
-
- If authentication fails for any reason, you can attempt to register by calling
- the L{registerAccount} method. If the registration succeeds, a L{STREAM_AUTHD_EVENT}
- will be fired. Otherwise, one of the above errors will be generated (again).
-
- """
- namespace = "jabber:client"
-
- INVALID_USER_EVENT = "//event/client/basicauth/invaliduser"
- AUTH_FAILED_EVENT = "//event/client/basicauth/authfailed"
- REGISTER_FAILED_EVENT = "//event/client/basicauth/registerfailed"
-
- def __init__(self, jid, password):
- xmlstream.ConnectAuthenticator.__init__(self, jid.host)
- self.jid = jid
- self.password = password
-
- def streamStarted(self, rootelem):
- # Send request for auth fields
- iq = IQ(self.xmlstream, "get")
- iq.addElement(("jabber:iq:auth", "query"))
- iq.query.addElement("username", content = self.jid.user)
- iq.addCallback(self._authQueryResultEvent)
- iq.send()
-
- def _authQueryResultEvent(self, iq):
- if iq["type"] == "result":
- # Construct auth request
- iq = IQ(self.xmlstream, "set")
- iq.addElement(("jabber:iq:auth", "query"))
- iq.query.addElement("username", content = self.jid.user)
- iq.query.addElement("resource", content = self.jid.resource)
-
- # Prefer digest over plaintext
- if DigestAuthQry.matches(iq):
- digest = xmlstream.hashPassword(self.xmlstream.sid, self.password)
- iq.query.addElement("digest", content = digest)
- else:
- iq.query.addElement("password", content = self.password)
-
- iq.addCallback(self._authResultEvent)
- iq.send()
- else:
- # Check for 401 -- Invalid user
- if iq.error["code"] == "401":
- self.xmlstream.dispatch(iq, self.INVALID_USER_EVENT)
- else:
- self.xmlstream.dispatch(iq, self.AUTH_FAILED_EVENT)
-
- def _authResultEvent(self, iq):
- if iq["type"] == "result":
- self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT)
- else:
- self.xmlstream.dispatch(iq, self.AUTH_FAILED_EVENT)
-
- def registerAccount(self, username = None, password = None):
- if username:
- self.jid.user = username
- if password:
- self.password = password
-
- iq = IQ(self.xmlstream, "set")
- iq.addElement(("jabber:iq:register", "query"))
- iq.query.addElement("username", content = self.jid.user)
- iq.query.addElement("password", content = self.password)
-
- iq.addCallback(self._registerResultEvent)
-
- iq.send()
-
- def _registerResultEvent(self, iq):
- if iq["type"] == "result":
- # Registration succeeded -- go ahead and auth
- self.streamStarted(None)
- else:
- # Registration failed
- self.xmlstream.dispatch(iq, self.REGISTER_FAILED_EVENT)
-
+++ /dev/null
-# -*- test-case-name: twisted.test.test_jabbercomponent -*-
-#
-# Twisted, the Framework of Your Internet
-# Copyright (C) 2001 Matthew W. Lefkowitz
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of version 2.1 of the GNU Lesser General Public
-# License as published by the Free Software Foundation.
-#
-# This library 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
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-from tlib import domish
-from twisted.xish import xpath, utility
-from tlib import xmlstream
-from twisted.protocols.jabber import jstrports
-
-def componentFactory(componentid, password):
- a = ConnectComponentAuthenticator(componentid, password)
- return xmlstream.XmlStreamFactory(a)
-
-class ConnectComponentAuthenticator(xmlstream.ConnectAuthenticator):
- """ Authenticator to permit an XmlStream to authenticate against a Jabber
- Server as a Component (where the Authenticator is initiating the stream).
-
- This implements the basic component authentication. Unfortunately this
- protocol is not formally described anywhere. Fortunately, all the Jabber
- servers I know of use this mechanism in exactly the same way.
-
- """
- namespace = 'jabber:component:accept'
-
- def __init__(self, componentjid, password):
- """
- @type componentjid: C{str}
- @param componentjid: Jabber ID that this component wishes to bind to.
-
- @type password: C{str}
- @param password: Password/secret this component uses to authenticate.
- """
- xmlstream.ConnectAuthenticator.__init__(self, componentjid)
- self.password = password
-
- def streamStarted(self, rootelem):
- # Create handshake
- hs = domish.Element(("jabber:component:accept", "handshake"))
- hs.addContent(xmlstream.hashPassword(self.xmlstream.sid, self.password))
-
- # Setup observer to watch for handshake result
- self.xmlstream.addOnetimeObserver("/handshake", self._handshakeEvent)
- self.xmlstream.send(hs)
-
- def _handshakeEvent(self, elem):
- self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT)
-
-class ListenComponentAuthenticator(xmlstream.Authenticator):
- """ Placeholder for listening components """
- pass
-
-
-from twisted.application import service
-from twisted.python import components
-
-class IService(components.Interface):
- def componentConnected(self, xmlstream):
- """ Parent component has established a connection
- """
-
- def componentDisconnected(self):
- """ Parent component has lost a connection to the Jabber system
- """
-
- def transportConnected(self, xmlstream):
- """ Parent component has established a connection over the underlying transport
- """
-
-class Service(service.Service):
- __implements__ = (IService, )
-
- def componentConnected(self, xmlstream):
- pass
-
- def componentDisconnected(self):
- pass
-
- def transportConnected(self, xmlstream):
- pass
-
- def send(self, obj):
- self.parent.send(obj)
-
-class ServiceManager(service.MultiService):
- """ Business logic representing a managed component connection to a Jabber router
-
- This Service maintains a single connection to a Jabber router and
- provides facilities for packet routing and transmission. Business
- logic modules can
- subclasses, and added as sub-service.
- """
- def __init__(self, jid, password):
- service.MultiService.__init__(self)
-
- # Setup defaults
- self.jabberId = jid
- self.xmlstream = None
-
- # Internal buffer of packets
- self._packetQueue = []
-
- # Setup the xmlstream factory
- self._xsFactory = componentFactory(self.jabberId, password)
-
- # Register some lambda functions to keep the self.xmlstream var up to date
- self._xsFactory.addBootstrap(xmlstream.STREAM_CONNECTED_EVENT, self._connected)
- self._xsFactory.addBootstrap(xmlstream.STREAM_AUTHD_EVENT, self._authd)
- self._xsFactory.addBootstrap(xmlstream.STREAM_END_EVENT, self._disconnected)
-
- # Map addBootstrap and removeBootstrap to the underlying factory -- is this
- # right? I have no clue...but it'll work for now, until i can think about it
- # more.
- self.addBootstrap = self._xsFactory.addBootstrap
- self.removeBootstrap = self._xsFactory.removeBootstrap
-
- def getFactory(self):
- return self._xsFactory
-
- def _connected(self, xs):
- self.xmlstream = xs
- for c in self:
- if components.implements(c, IService):
- c.transportConnected(xs)
-
- def _authd(self, xs):
- # Flush all pending packets
- for p in self._packetQueue:
- self.xmlstream.send(p)
- self._packetQueue = []
-
- # Notify all child services which implement
- # the IService interface
- for c in self:
- if components.implements(c, IService):
- c.componentConnected(xs)
-
- def _disconnected(self, _):
- self.xmlstream = None
-
- # Notify all child services which implement
- # the IService interface
- for c in self:
- if components.implements(c, IService):
- c.componentDisconnected()
-
- def send(self, obj):
- if self.xmlstream != None:
- self.xmlstream.send(obj)
- else:
- self._packetQueue.append(obj)
-
-
-
-
-def buildServiceManager(jid, password, strport):
- """ Constructs a pre-built C{component.ServiceManager}, using the specified strport string.
- """
- svc = ServiceManager(jid, password)
- client_svc = jstrports.client(strport, svc.getFactory())
- client_svc.setServiceParent(svc)
- return svc
+++ /dev/null
-# -*- test-case-name: twisted.test.test_jabberjid -*-
-#
-# Twisted, the Framework of Your Internet
-# Copyright (C) 2001 Matthew W. Lefkowitz
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of version 2.1 of the GNU Lesser General Public
-# License as published by the Free Software Foundation.
-#
-# This library 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
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-from twisted.internet import reactor, protocol, defer
-from tlib import domish
-from tlib.jabber.xmpp_stringprep import nodeprep, resourceprep, nameprep
-
-class InvalidFormat(Exception):
- pass
-
-def parse(jidstring):
- user = None
- server = None
- resource = None
-
- # Search for delimiters
- user_sep = jidstring.find("@")
- res_sep = jidstring.find("/")
-
- if user_sep == -1:
- if res_sep == -1:
- # host
- server = jidstring
- else:
- # host/resource
- server = jidstring[0:res_sep]
- resource = jidstring[res_sep + 1:] or None
- else:
- if res_sep == -1:
- # user@host
- user = jidstring[0:user_sep] or None
- server = jidstring[user_sep + 1:]
- else:
- if user_sep < res_sep:
- # user@host/resource
- user = jidstring[0:user_sep] or None
- server = jidstring[user_sep + 1:user_sep + (res_sep - user_sep)]
- resource = jidstring[res_sep + 1:] or None
- else:
- # server/resource (with an @ in resource)
- server = jidstring[0:res_sep]
- resource = jidstring[res_sep + 1:] or None
-
- # Return the tuple
- return prep(user, server, resource)
-
-
-def prep(user, server, resource):
- """ Stringprep, backported from Twisted 2.0 """
-
- if user:
- try:
- user = nodeprep.prepare(unicode(user))
- except UnicodeError:
- raise InvalidFormat, "Invalid character in username"
- else:
- user = None
-
- if not server:
- raise InvalidFormat, "Server address required."
- else:
- try:
- server = nameprep.prepare(unicode(server))
- except UnicodeError:
- raise InvalidFormat, "Invalid character in resource"
-
- if resource:
- try:
- resource = resourceprep.prepare(unicode(resource))
- except UnicodeError:
- raise InvalidFormat, "Invalid character in resource"
- else:
- resource = None
-
- return (user, server, resource)
-
-
-
-__internJIDs = {}
-
-def intern(str):
- # XXX: Ensure that stringprep'd jids map to same JID
- if str in __internJIDs:
- return __internJIDs[str]
- else:
- j = JID(str)
- __internJIDs[str] = j
- return j
-
-class JID:
- def __init__(self, str = None, tuple = None):
- assert (str or tuple)
-
- if str:
- user, host, res = parse(str)
- else:
- user, host, res = tuple
-
- self.host = host
- self.user = user
- self.resource = res
-
- def userhost(self):
- if self.user:
- return "%s@%s" % (self.user, self.host)
- else:
- return self.host
-
- def userhostJID(self):
- if self.resource:
- if "_uhjid" not in self.__dict__:
- self._uhjid = jid.intern(self.userhost())
- return self._uhjid
- else:
- return self
-
- def full(self):
- if self.user:
- if self.resource:
- return "%s@%s/%s" % (self.user, self.host, self.resource)
- else:
- return "%s@%s" % (self.user, self.host)
- else:
- if self.resource:
- return "%s/%s" % (self.host, self.resource)
- else:
- return self.host
-
- def __eq__(self, other):
- return (self.user == other.user and
- self.host == other.host and
- self.resource == other.resource)
-
- def __ne__(self, other):
- return not (self.user == other.user and
- self.host == other.host and
- self.resource == other.resource)
+++ /dev/null
-# Twisted, the Framework of Your Internet
-# Copyright (C) 2001-2003 Matthew W. Lefkowitz
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of version 2.1 of the GNU Lesser General Public
-# License as published by the Free Software Foundation.
-#
-# This library 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
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-""" A temporary placeholder for client-capable strports, until we
-sufficient use cases get identified """
-
-from twisted.application import strports
-
-def _parseTCPSSL(factory, domain, port):
- """ For the moment, parse TCP or SSL connections the same """
- return (domain, int(port), factory), {}
-
-def _parseUNIX(factory, address):
- return (address, factory), {}
-
-
-_funcs = { "tcp" : _parseTCPSSL,
- "unix" : _parseUNIX,
- "ssl" : _parseTCPSSL }
-
-
-def parse(description, factory):
- args, kw = strports._parse(description)
- return (args[0].upper(),) + _funcs[args[0]](factory, *args[1:], **kw)
-
-def client(description, factory):
- from twisted.application import internet
- name, args, kw = parse(description, factory)
- return getattr(internet, name + 'Client')(*args, **kw)
+++ /dev/null
-# -*- test-case-name: twisted.words.test.test_jabberxmppstringprep -*-
-#
-# Copyright (c) 2001-2005 Twisted Matrix Laboratories.
-# See LICENSE for details.
-
-import sys, warnings
-
-if sys.version_info < (2,3,2):
- import re
-
- class IDNA:
- dots = re.compile(u"[\u002E\u3002\uFF0E\uFF61]")
- def nameprep(self, label):
- return label.lower()
-
- idna = IDNA()
-
- crippled = True
-
- warnings.warn("Accented and non-Western Jabber IDs will not be properly "
- "case-folded with this version of Python, resulting in "
- "incorrect protocol-level behavior. It is strongly "
- "recommended you upgrade to Python 2.3.2 or newer if you "
- "intend to use Twisted's Jabber support.")
-
-else:
- import stringprep
- import unicodedata
- from encodings import idna
-
- crippled = False
-
-del sys, warnings
-
-class ILookupTable:
- """ Interface for character lookup classes. """
-
- def lookup(self, c):
- """ Return whether character is in this table. """
-
-class IMappingTable:
- """ Interface for character mapping classes. """
-
- def map(self, c):
- """ Return mapping for character. """
-
-class LookupTableFromFunction:
-
- __implements__ = ILookupTable
-
- def __init__(self, in_table_function):
- self.lookup = in_table_function
-
-class LookupTable:
-
- __implements__ = ILookupTable
-
- def __init__(self, table):
- self._table = table
-
- def lookup(self, c):
- return c in self._table
-
-class MappingTableFromFunction:
-
- __implements__ = IMappingTable
-
- def __init__(self, map_table_function):
- self.map = map_table_function
-
-class EmptyMappingTable:
-
- __implements__ = IMappingTable
-
- def __init__(self, in_table_function):
- self._in_table_function = in_table_function
-
- def map(self, c):
- if self._in_table_function(c):
- return None
- else:
- return c
-
-class Profile:
- def __init__(self, mappings=[], normalize=True, prohibiteds=[],
- check_unassigneds=True, check_bidi=True):
- self.mappings = mappings
- self.normalize = normalize
- self.prohibiteds = prohibiteds
- self.do_check_unassigneds = check_unassigneds
- self.do_check_bidi = check_bidi
-
- def prepare(self, string):
- result = self.map(string)
- if self.normalize:
- result = unicodedata.normalize("NFKC", result)
- self.check_prohibiteds(result)
- if self.do_check_unassigneds:
- self.check_unassigneds(result)
- if self.do_check_bidi:
- self.check_bidirectionals(result)
- return result
-
- def map(self, string):
- result = []
-
- for c in string:
- result_c = c
-
- for mapping in self.mappings:
- result_c = mapping.map(c)
- if result_c != c:
- break
-
- if result_c is not None:
- result.append(result_c)
-
- return u"".join(result)
-
- def check_prohibiteds(self, string):
- for c in string:
- for table in self.prohibiteds:
- if table.lookup(c):
- raise UnicodeError, "Invalid character %s" % repr(c)
-
- def check_unassigneds(self, string):
- for c in string:
- if stringprep.in_table_a1(c):
- raise UnicodeError, "Unassigned code point %s" % repr(c)
-
- def check_bidirectionals(self, string):
- found_LCat = False
- found_RandALCat = False
-
- for c in string:
- if stringprep.in_table_d1(c):
- found_RandALCat = True
- if stringprep.in_table_d2(c):
- found_LCat = True
-
- if found_LCat and found_RandALCat:
- raise UnicodeError, "Violation of BIDI Requirement 2"
-
- if found_RandALCat and not (stringprep.in_table_d1(string[0]) and
- stringprep.in_table_d1(string[-1])):
- raise UnicodeError, "Violation of BIDI Requirement 3"
-
-
-class NamePrep:
- """ Implements nameprep on international domain names.
-
- STD3ASCIIRules is assumed true in this implementation.
- """
-
- # Prohibited characters.
- prohibiteds = [unichr(n) for n in range(0x00, 0x2c + 1) +
- range(0x2e, 0x2f + 1) +
- range(0x3a, 0x40 + 1) +
- range(0x5b, 0x60 + 1) +
- range(0x7b, 0x7f + 1) ]
-
- def prepare(self, string):
- result = []
-
- labels = idna.dots.split(string)
-
- if labels and len(labels[-1]) == 0:
- trailing_dot = '.'
- del labels[-1]
- else:
- trailing_dot = ''
-
- for label in labels:
- result.append(self.nameprep(label))
-
- return ".".join(result)+trailing_dot
-
- def check_prohibiteds(self, string):
- for c in string:
- if c in self.prohibiteds:
- raise UnicodeError, "Invalid character %s" % repr(c)
-
- def nameprep(self, label):
- label = idna.nameprep(label)
- self.check_prohibiteds(label)
- if label[0] == '-':
- raise UnicodeError, "Invalid leading hyphen-minus"
- if label[-1] == '-':
- raise UnicodeError, "Invalid trailing hyphen-minus"
- return label
-
-if crippled:
- case_map = MappingTableFromFunction(lambda c: c.lower())
- nodeprep = Profile(mappings=[case_map],
- normalize=False,
- prohibiteds=[LookupTable([u' ', u'"', u'&', u"'", u'/',
- u':', u'<', u'>', u'@'])],
- check_unassigneds=False,
- check_bidi=False)
-
- resourceprep = Profile(normalize=False,
- check_unassigneds=False,
- check_bidi=False)
-
-else:
- C_11 = LookupTableFromFunction(stringprep.in_table_c11)
- C_12 = LookupTableFromFunction(stringprep.in_table_c12)
- C_21 = LookupTableFromFunction(stringprep.in_table_c21)
- C_22 = LookupTableFromFunction(stringprep.in_table_c22)
- C_3 = LookupTableFromFunction(stringprep.in_table_c3)
- C_4 = LookupTableFromFunction(stringprep.in_table_c4)
- C_5 = LookupTableFromFunction(stringprep.in_table_c5)
- C_6 = LookupTableFromFunction(stringprep.in_table_c6)
- C_7 = LookupTableFromFunction(stringprep.in_table_c7)
- C_8 = LookupTableFromFunction(stringprep.in_table_c8)
- C_9 = LookupTableFromFunction(stringprep.in_table_c9)
-
- B_1 = EmptyMappingTable(stringprep.in_table_b1)
- B_2 = MappingTableFromFunction(stringprep.map_table_b2)
-
- nodeprep = Profile(mappings=[B_1, B_2],
- prohibiteds=[C_11, C_12, C_21, C_22,
- C_3, C_4, C_5, C_6, C_7, C_8, C_9,
- LookupTable([u'"', u'&', u"'", u'/',
- u':', u'<', u'>', u'@'])])
-
- resourceprep = Profile(mappings=[B_1,],
- prohibiteds=[C_12, C_21, C_22,
- C_3, C_4, C_5, C_6, C_7, C_8, C_9])
-
-nameprep = NamePrep()
+++ /dev/null
-# -*- test-case-name: twisted.test.test_xmlstream -*-
-#
-# Twisted, the Framework of Your Internet
-# Copyright (C) 2001 Matthew W. Lefkowitz
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of version 2.1 of the GNU Lesser General Public
-# License as published by the Free Software Foundation.
-#
-# This library 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
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-from twisted.internet import reactor, protocol, defer
-from twisted.xish import utility
-import domish
-
-STREAM_CONNECTED_EVENT = intern("//event/stream/connected")
-STREAM_START_EVENT = intern("//event/stream/start")
-STREAM_END_EVENT = intern("//event/stream/end")
-STREAM_ERROR_EVENT = intern("//event/stream/error")
-STREAM_AUTHD_EVENT = intern("//event/stream/authd")
-RAWDATA_IN_EVENT = intern("//event/rawdata/in")
-RAWDATA_OUT_EVENT = intern("//event/rawdata/out")
-
-def hashPassword(sid, password):
- """Create a SHA1-digest string of a session identifier and password """
- import sha
- return sha.new("%s%s" % (sid, password)).hexdigest()
-
-class Authenticator:
- """ Base class for business logic of authenticating an XmlStream
-
- Subclass this object to enable an XmlStream to authenticate to different
- types of stream hosts (such as clients, components, etc.).
-
- Rules:
- 1. The Authenticator MUST dispatch a L{STREAM_AUTHD_EVENT} when the stream
- has been completely authenticated.
- 2. The Authenticator SHOULD reset all state information when
- L{associateWithStream} is called.
- 3. The Authenticator SHOULD override L{streamStarted}, and start
- authentication there.
-
-
- @type namespace: C{str}
- @cvar namespace: Default namespace for the XmlStream
-
- @type version: C{int}
- @cvar version: Version attribute for XmlStream. 0.0 will cause the
- XmlStream to not include a C{version} attribute in the
- header.
-
- @type streamHost: C{str}
- @ivar streamHost: Target host for this stream (used as the 'to' attribute)
-
- @type xmlstream: C{XmlStream}
- @ivar xmlstream: The XmlStream that needs authentication
- """
-
- namespace = 'invalid' # Default namespace for stream
- version = 0.0 # Stream version
-
- def __init__(self, streamHost):
- self.streamHost = streamHost
- self.xmlstream = None
-
- def connectionMade(self):
- """
- Called by the XmlStream when the underlying socket connection is
- in place. This allows the Authenticator to send an initial root
- element, if it's connecting, or wait for an inbound root from
- the peer if it's accepting the connection
-
- Subclasses can use self.xmlstream.send() with the provided xmlstream
- parameter to send any initial data to the peer
- """
-
- def streamStarted(self, rootelem):
- """
- Called by the XmlStream when it has received a root element from
- the connected peer.
-
- @type rootelem: C{Element}
- @param rootelem: The root element of the XmlStream received from
- the streamHost
- """
-
- def associateWithStream(self, xmlstream):
- """
- Called by the XmlStreamFactory when a connection has been made
- to the requested peer, and an XmlStream object has been
- instantiated.
-
- The default implementation just saves a handle to the new
- XmlStream.
-
- @type xmlstream: C{XmlStream}
- @param xmlstream: The XmlStream that will be passing events to this
- Authenticator.
-
- """
- self.xmlstream = xmlstream
-
-class ConnectAuthenticator(Authenticator):
- def connectionMade(self):
- # Generate stream header
- if self.version == 1.0:
- sh = "<stream:stream xmlns='%s' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>" % \
- (self.namespace)
- else:
- sh = "<stream:stream xmlns='%s' xmlns:stream='http://etherx.jabber.org/streams' to='%s'>" % \
- (self.namespace, self.streamHost)
- self.xmlstream.send(sh)
-
-class XmlStream(protocol.Protocol, utility.EventDispatcher):
- def __init__(self, authenticator):
- utility.EventDispatcher.__init__(self)
- self.stream = None
- self.authenticator = authenticator
- self.sid = None
- self.rawDataOutFn = None
- self.rawDataInFn = None
-
- # Reset the authenticator
- authenticator.associateWithStream(self)
-
- # Setup watcher for stream errors
- self.addObserver("/error[@xmlns='http://etherx.jabber.org/streams']", self.streamError)
-
- def streamError(self, errelem):
- self.dispatch(errelem, STREAM_ERROR_EVENT)
- self.transport.loseConnection()
-
- ### --------------------------------------------------------------
- ###
- ### Protocol events
- ###
- ### --------------------------------------------------------------
- def connectionMade(self):
- # Setup the parser
- self.stream = domish.elementStream()
- self.stream.DocumentStartEvent = self.onDocumentStart
- self.stream.ElementEvent = self.onElement
- self.stream.DocumentEndEvent = self.onDocumentEnd
-
- self.dispatch(self, STREAM_CONNECTED_EVENT)
-
- self.authenticator.connectionMade()
-
- def dataReceived(self, buf):
- try:
- if self.rawDataInFn: self.rawDataInFn(buf)
- self.stream.parse(buf)
- except domish.ParserError:
- self.dispatch(self, STREAM_ERROR_EVENT)
- self.transport.loseConnection()
-
- def connectionLost(self, _):
- self.dispatch(self, STREAM_END_EVENT)
- self.stream = None
-
- ### --------------------------------------------------------------
- ###
- ### DOM events
- ###
- ### --------------------------------------------------------------
- def onDocumentStart(self, rootelem):
- if rootelem.hasAttribute("id"):
- self.sid = rootelem["id"] # Extract stream identifier
- self.authenticator.streamStarted(rootelem) # Notify authenticator
- self.dispatch(self, STREAM_START_EVENT)
-
- def onElement(self, element):
- self.dispatch(element)
-
- def onDocumentEnd(self):
- self.transport.loseConnection()
-
- def setDispatchFn(self, fn):
- self.stream.ElementEvent = fn
-
- def resetDispatchFn(self):
- self.stream.ElementEvent = self.onElement
-
- def send(self, obj):
- if isinstance(obj, domish.Element):
- obj = obj.toXml()
-
- if self.rawDataOutFn:
- self.rawDataOutFn(obj)
-
- self.transport.write(obj)
-
-
-class XmlStreamFactory(protocol.ReconnectingClientFactory):
- def __init__(self, authenticator):
- self.authenticator = authenticator
- self.bootstraps = []
-
- def buildProtocol(self, _):
- self.resetDelay()
- # Create the stream and register all the bootstrap observers
- xs = XmlStream(self.authenticator)
- xs.factory = self
- for event, fn in self.bootstraps: xs.addObserver(event, fn)
- return xs
-
- def addBootstrap(self, event, fn):
- self.bootstraps.append((event, fn))
-
- def removeBootstrap(self, event, fn):
- self.bootstraps.remove((event, fn))
-
-
-
-
-
+++ /dev/null
-# Copyright 2004-2005 James Bunton <james@delx.cjb.net>
-# Licensed for distribution under the GPL version 2, check COPYING for details
-
-from twisted.python import log
-
-checkTwistedCached = None
-def checkTwisted():
- """ Returns False if we're using an old version that needs tlib, otherwise returns True """
- global checkTwistedCached
- if checkTwistedCached == None:
- import twisted.copyright
- checkTwistedCached = (VersionNumber(twisted.copyright.version) >= VersionNumber("2.0.0"))
- return checkTwistedCached
-
-class VersionNumber:
- def __init__(self, vstring):
- self.varray = [0]
- index = 0
- flag = True
- for c in vstring:
- if c == '.':
- self.varray.append(0)
- index += 1
- flag = True
- elif c.isdigit() and flag:
- self.varray[index] *= 10
- self.varray[index] += int(c)
- else:
- flag = False
-
- def __cmp__(self, other):
- i = 0
- while(True):
- if i == len(other.varray):
- if i < len(self.varray):
- return 1
- else:
- return 0
- if i == len(self.varray):
- if i < len(other.varray):
- return -1
- else:
- return 0
-
- if self.varray[i] > other.varray[i]:
- return 1
- elif self.varray[i] < other.varray[i]:
- return -1
-
- i += 1
-
-
-
-def parseText(text, beExtremelyLenient=False):
- return TextParser(beExtremelyLenient).parseString(text)
-
-def parseFile(filename, beExtremelyLenient=False):
- t = TextParser(beExtremelyLenient)
- t.parseFile(filename)
- return t.root
-
-
-import warnings, re
-# Suppress the annoying warning we get with Twisted 1.3 words being deprecated
-warnings.filters.append(("ignore", None, UserWarning, re.compile("twisted.words"), 21))
-# Suppress the OpenSSL UserWarning
-warnings.filters.append(("ignore", re.compile("SSL connection shutdown possibly unreliable, please upgrade to ver 0.XX"), UserWarning, re.compile("twisted.internet.tcp"), 216))
-
-
-try:
- log.msg("Trying to import XML DOM")
- from twisted.words.xish.domish import SuxElementStream, Element, unescapeFromXml
- from twisted.words.protocols.jabber import jid, component
- jid.intern = jid.internJID # This got renamed for some reason
- log.msg("Using Twisted >= 2.0, Words >= 0.3, Words DOM")
-except ImportError:
- try:
- log.msg("Checking Twisted version...")
- if checkTwisted():
- from twisted.xish.domish import SuxElementStream, Element, unescapeFromXml
- from twisted.words.protocols.jabber import jid, component
- jid.intern = jid.internJID # This got renamed for some reason
- log.msg("Using Twisted >= 2.0, Words < 0.3, Twisted DOM")
- else:
- from tlib.domish import SuxElementStream, Element, unescapeFromXml
- from tlib.jabber import jid, component
- log.msg("Using Twisted < 2.0, Internal patched DOM")
- except ImportError:
- print "Could not find the XML DOM. If you're using Twisted 2.x make sure you have twisted.words installed."
- raise
-
-
-class TextParser:
- """ Taken from http://xoomer.virgilio.it/dialtone/rsschannel.py """
-
- def __init__(self, beExtremelyLenient=False):
- self.root = None
- self.beExtremelyLenient = beExtremelyLenient
-
- def parseFile(self, filename):
- return self.parseString(file(filename).read())
-
- def parseString(self, data):
- es = SuxElementStream()
- es.beExtremelyLenient = self.beExtremelyLenient
- es.DocumentStartEvent = self.docStart
- es.DocumentEndEvent = self.docEnd
- es.ElementEvent = self.element
- es.parse(data)
- return self.root
-
- def docStart(self, e):
- self.root = e
-
- def docEnd(self):
- pass
-
- def element(self, e):
- self.root.addChild(e)
-
-
-checkTwistedCached = None
-def checkTwisted():
- """ Returns False if we're using an old version that needs tlib, otherwise returns True """
- global checkTwistedCached
- if checkTwistedCached == None:
- import twisted.copyright
- checkTwistedCached = (VersionNumber(twisted.copyright.version) >= VersionNumber("2.0.0"))
- return checkTwistedCached
-
-class VersionNumber:
- def __init__(self, vstring):
- self.varray = [0]
- index = 0
- flag = True
- for c in vstring:
- if c == '.':
- self.varray.append(0)
- index += 1
- flag = True
- elif c.isdigit() and flag:
- self.varray[index] *= 10
- self.varray[index] += int(c)
- else:
- flag = False
-
- def __cmp__(self, other):
- i = 0
- while(True):
- if i == len(other.varray):
- if i < len(self.varray):
- return 1
- else:
- return 0
- if i == len(self.varray):
- if i < len(other.varray):
- return -1
- else:
- return 0
-
- if self.varray[i] > other.varray[i]:
- return 1
- elif self.varray[i] < other.varray[i]:
- return -1
-
- i += 1
-
-
-
-# Copyright 2004-2005 James Bunton <james@delx.cjb.net>
+# Copyright 2004-2006 James Bunton <james@delx.cjb.net>
# Licensed for distribution under the GPL version 2, check COPYING for details
-from tlib import xmlw
+from twisted.words.xish.domish import parseFile, Element
from debug import LogEvent, INFO, WARN
import os
import os.path
file = mangle(file)
hash = makeHash(file)
- document = xmlw.parseFile(self.name + X + hash + X + file + ".xml")
+ document = parseFile(self.name + X + hash + X + file + ".xml")
return document
except IOError:
pass
if(not document):
- document = xmlw.Element((None, "xdb"))
+ document = Element((None, "xdb"))
# Remove the existing node (if any)
for child in document.elements():
# Copyright 2004-2005 James Bunton <james@delx.cjb.net>
# Licensed for distribution under the GPL version 2, check COPYING for details
-from tlib import xmlw
+from twisted.words.xish.domish import parseFile
import sys, os
# Get ourself a DOM
try:
- root = xmlw.parseFile(configFile)
+ root = parseFile(configFile)
except Exception, e:
invalidError("Error parsing configuration file: " + str(e))