From: jamesbunton Date: Wed, 9 Aug 2006 04:25:10 +0000 (+0000) Subject: Merged branches/msnfix 204:217 to trunk X-Git-Url: https://code.delx.au/pymsnt/commitdiff_plain/fca228030295117f69ade6644859c10f1b10b388 Merged branches/msnfix 204:217 to trunk Changes: * Use twistfix library for twisted compatibility. * Fixed licensing issues for the Debian packager. * Fixed an XDB hash problem. * Added python-imaging notes to docs git-svn-id: http://delx.cjb.net/svn/pymsnt/trunk@218 55fbd22a-6204-0410-b2f0-b6c764c7e90a committer: jamesbunton --- diff --git a/PyMSNt.py b/PyMSNt.py index 8736387..b9378f5 100755 --- a/PyMSNt.py +++ b/PyMSNt.py @@ -1,4 +1,6 @@ #!/usr/bin/env python +# Copyright 2006 James Bunton +# Licensed for distribution under the GPL version 2, check COPYING for details # Make 'cwd'/src in the PYTHONPATH import sys, os, os.path diff --git a/TODO b/TODO index 20a1509..930ca1d 100644 --- a/TODO +++ b/TODO @@ -1,15 +1,46 @@ -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) diff --git a/docs/developer.html b/docs/developer.html index fc3c014..21731bb 100644 --- a/docs/developer.html +++ b/docs/developer.html @@ -306,6 +306,9 @@ wish to accept all of these roster pushes.

Bug reports and comments go to James Bunton

I'll gladly help you with any problems, but please look through this whole page first

+
+

Copyright James Bunton <james at delx.cjb.net>. You may freely redistribute this file.

+ diff --git a/docs/server.html b/docs/server.html index 6c7534f..c500805 100644 --- a/docs/server.html +++ b/docs/server.html @@ -34,7 +34,7 @@ Make sure have installed Python 2.3 or newer (I have reports that some older ver

  • Mandrake:
    urpmi python
  • -
  • Debian:
    apt-get install python2.3
  • +
  • Debian:
    apt-get install python
  • Fedora:
    yum install python
  • Centos 4:
    yum install python
  • Suse:
    yast -i python
  • @@ -55,8 +55,8 @@ or make sure you download at least these modules: core, web, words

    • 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.
    • -
    • Debian sarge:
      apt-get install python2.3-twisted python2.3-crypto python2.3-pyopenssl
    • -
    • Debian:
      apt-get install python2.3-twisted python2.3-twisted-words python2.3-crypto python2.3-pyopenssl
    • +
    • Debian sarge:
      apt-get install python-twisted python-crypto python-pyopenssl python-imaging
    • +
    • Debian:
      apt-get install python-twisted python-twisted-words python-crypto python-pyopenssl python-imaging
    • Fedora:
      yum install python-twisted pyOpenSSL pycrypto
    • Centos 4:
      yum install python-twisted python-crypto pyOpenSSL
    • Suse:
      yast -i python-twisted python-openssl python-pycrypto
    • @@ -270,6 +270,8 @@ msn.host.com/registered in them. These users should delete the transport from th

      Bug reports and comments go to James Bunton

      I'll gladly help you with any problems, but please look through this whole page, the README & config.xml files first.

      +
      +

      Copyright James Bunton <james at delx.cjb.net>. You may freely redistribute this file.

      diff --git a/docs/user.html b/docs/user.html index 68aef1f..8eee7ab 100644 --- a/docs/user.html +++ b/docs/user.html @@ -117,6 +117,9 @@ Many clients will do this for you automatically if you tell them to:
    • 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.
    +
    +

    Copyright James Bunton <james at delx.cjb.net>. You may freely redistribute this file.

    + diff --git a/src/avatar.py b/src/avatar.py index 697400a..1ba454c 100644 --- a/src/avatar.py +++ b/src/avatar.py @@ -1,10 +1,10 @@ -# Copyright 2005 James Bunton +# Copyright 2005-2006 James Bunton # 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 diff --git a/src/baseproto/__init__.py b/src/baseproto/__init__.py index b7713dd..21c4e51 100644 --- a/src/baseproto/__init__.py +++ b/src/baseproto/__init__.py @@ -1,3 +1,6 @@ +# Copyright 2006 James Bunton +# 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 diff --git a/src/baseproto/glue.py b/src/baseproto/glue.py index 017ec1c..bf987d7 100644 --- a/src/baseproto/glue.py +++ b/src/baseproto/glue.py @@ -1,8 +1,8 @@ -# Copyright 2005 James Bunton +# Copyright 2004-2006 James Bunton # 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 diff --git a/src/contact.py b/src/contact.py index 088f1c0..7545797 100644 --- a/src/contact.py +++ b/src/contact.py @@ -1,10 +1,10 @@ -# Copyright 2005 James Bunton +# Copyright 2005-2006 James Bunton # 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 diff --git a/src/disco.py b/src/disco.py index 1c49386..4fd2221 100644 --- a/src/disco.py +++ b/src/disco.py @@ -1,11 +1,12 @@ -# Copyright 2004-2005 James Bunton +# Copyright 2004-2006 James Bunton # 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 @@ -113,8 +114,8 @@ class ServerDiscovery: 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") diff --git a/src/ft.py b/src/ft.py index 218441a..78e5b05 100644 --- a/src/ft.py +++ b/src/ft.py @@ -1,9 +1,9 @@ -# Copyright 2005 James Bunton +# Copyright 2005-2006 James Bunton # 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 @@ -279,11 +279,11 @@ class FTReceive: # 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 diff --git a/src/groupchat.py b/src/groupchat.py index c190579..b190da3 100644 --- a/src/groupchat.py +++ b/src/groupchat.py @@ -3,7 +3,8 @@ 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 diff --git a/src/housekeep.py b/src/housekeep.py index 82a459b..057bb34 100644 --- a/src/housekeep.py +++ b/src/housekeep.py @@ -1,7 +1,7 @@ # Copyright 2005 James Bunton # 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 @@ -72,7 +72,7 @@ def doSpoolPrepCheck(): 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) diff --git a/src/jabw.py b/src/jabw.py index e6f2897..aa9ddff 100644 --- a/src/jabw.py +++ b/src/jabw.py @@ -2,7 +2,8 @@ # 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 @@ -34,8 +35,8 @@ def sendPresence(pytrans, to, fro, show=None, status=None, priority=None, ptype= # 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 @@ -183,8 +184,8 @@ class JabberConnection: 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 @@ -245,9 +246,9 @@ class JabberConnection: """ 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 packet ptype = el.getAttribute("type") diff --git a/src/lang.py b/src/lang.py index 4674cbe..025baa3 100644 --- a/src/lang.py +++ b/src/lang.py @@ -1,4 +1,7 @@ # -*- coding: UTF-8 -*- +# Copyright 2004-2006 James Bunton +# Licensed for distribution under the GPL version 2, check COPYING for details + import config diff --git a/src/legacy/glue.py b/src/legacy/glue.py index c278079..c1826ed 100644 --- a/src/legacy/glue.py +++ b/src/legacy/glue.py @@ -2,11 +2,11 @@ # 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 @@ -18,7 +18,7 @@ import lang 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 diff --git a/src/tlib/msn/__init__.py b/src/legacy/msn/__init__.py similarity index 75% rename from src/tlib/msn/__init__.py rename to src/legacy/msn/__init__.py index 3a80ccc..b54e987 100644 --- a/src/tlib/msn/__init__.py +++ b/src/legacy/msn/__init__.py @@ -1,3 +1,6 @@ +# Copyright 2006 James Bunton +# 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 diff --git a/src/tlib/msn/msn.py b/src/legacy/msn/msn.py similarity index 99% rename from src/tlib/msn/msn.py rename to src/legacy/msn/msn.py index 2bda133..98d2893 100644 --- a/src/tlib/msn/msn.py +++ b/src/legacy/msn/msn.py @@ -106,9 +106,8 @@ except ImportError: 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 @@ -525,7 +524,7 @@ class MSNObject: self.text = '' % (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") @@ -1019,7 +1018,7 @@ class NotificationClient(MSNEventBase): 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") @@ -1058,7 +1057,7 @@ class NotificationClient(MSNEventBase): p1 = lm.find("") + 5 p2 = lm.find("") 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: diff --git a/src/tlib/msn/msnft.py b/src/legacy/msn/msnft.py similarity index 100% rename from src/tlib/msn/msnft.py rename to src/legacy/msn/msnft.py diff --git a/src/tlib/msn/msnp11chl.py b/src/legacy/msn/msnp11chl.py similarity index 100% rename from src/tlib/msn/msnp11chl.py rename to src/legacy/msn/msnp11chl.py diff --git a/src/tlib/msn/msnw.py b/src/legacy/msn/msnw.py similarity index 99% rename from src/tlib/msn/msnw.py rename to src/legacy/msn/msnw.py index 87ed044..da41fc5 100644 --- a/src/tlib/msn/msnw.py +++ b/src/legacy/msn/msnw.py @@ -1,4 +1,4 @@ -# Copyright 2004-2005 James Bunton +# Copyright 2004-2006 James Bunton # Licensed for distribution under the GPL version 2, check COPYING for details # Twisted imports @@ -11,7 +11,7 @@ import math, base64, binascii # Local imports from debug import LogEvent, INFO, WARN, ERROR -from tlib.msn import msn +import msn diff --git a/src/tlib/msn/test_msn.py b/src/legacy/msn/test_msn.py similarity index 99% rename from src/tlib/msn/test_msn.py rename to src/legacy/msn/test_msn.py index bec12e6..85b9067 100644 --- a/src/tlib/msn/test_msn.py +++ b/src/legacy/msn/test_msn.py @@ -1,6 +1,7 @@ # Copyright (c) 2001-2005 Twisted Matrix Laboratories. -# Copyright (c) 2005-2006 James Bunton -# See LICENSE for details. +# Copyright (c) 2005-2006 James Bunton +# Licensed for distribution under the GPL version 2, check COPYING for details + """ Test cases for msn. diff --git a/src/tlib/msn/test_msnw.py b/src/legacy/msn/test_msnw.py similarity index 100% rename from src/tlib/msn/test_msnw.py rename to src/legacy/msn/test_msnw.py diff --git a/src/main.py b/src/main.py index eb90dd4..305d549 100644 --- a/src/main.py +++ b/src/main.py @@ -6,6 +6,7 @@ reload(sys) 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: @@ -29,6 +30,8 @@ except: sys.exit(1) bestreactor.install() +import twistfix +twistfix.main() # Must load config before everything else @@ -92,7 +95,10 @@ if config.reactor: 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 @@ -240,7 +246,7 @@ class PyTransport(component.Service): def onMessage(self, el): fro = el.getAttribute("from") try: - froj = jid.intern(fro) + froj = internJID(fro) except Exception, e: LogEvent(WARN, "", "Failed stringprep.") return @@ -266,8 +272,8 @@ class PyTransport(component.Service): 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 diff --git a/src/misciq.py b/src/misciq.py index dc6baad..eb27bd7 100644 --- a/src/misciq.py +++ b/src/misciq.py @@ -3,7 +3,8 @@ 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 @@ -30,7 +31,7 @@ class ConnectUsers: 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 @@ -131,7 +132,7 @@ class AdHocCommands: 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") @@ -219,9 +220,9 @@ class VCardFactory: 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") @@ -280,9 +281,9 @@ class IqAvatarFactory: 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"): @@ -489,8 +490,8 @@ class FileTransferOOBSend: 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() @@ -548,8 +549,8 @@ class Socks5FileTransfer: 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() @@ -620,8 +621,8 @@ class Socks5FileTransfer: 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"): diff --git a/src/register.py b/src/register.py index cdac868..603aa5c 100644 --- a/src/register.py +++ b/src/register.py @@ -1,8 +1,9 @@ -# Copyright 2004-2005 James Bunton +# Copyright 2004-2006 James Bunton # 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 @@ -38,11 +39,11 @@ class RegisterManager: (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 @@ -98,7 +99,7 @@ class RegisterManager: 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 @@ -133,7 +134,7 @@ class RegisterManager: 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) diff --git a/src/session.py b/src/session.py index f4d11bd..be29550 100644 --- a/src/session.py +++ b/src/session.py @@ -1,7 +1,7 @@ -# Copyright 2004-2005 James Bunton +# Copyright 2004-2006 James Bunton # 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 @@ -150,7 +150,7 @@ class Session(jabw.JabberConnection): 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) diff --git a/src/tlib/socks5.py b/src/socks5.py similarity index 100% rename from src/tlib/socks5.py rename to src/socks5.py diff --git a/src/svninfo.py b/src/svninfo.py index e1871eb..8773ac4 100644 --- a/src/svninfo.py +++ b/src/svninfo.py @@ -1,6 +1,8 @@ #!/usr/bin/env python +# Copyright 2006 James Bunton +# 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 diff --git a/src/tlib/throttle.py b/src/throttle.py similarity index 86% rename from src/tlib/throttle.py rename to src/throttle.py index 88b194e..c1d8c52 100644 --- a/src/tlib/throttle.py +++ b/src/throttle.py @@ -1,6 +1,8 @@ -from twisted.internet import task +# Copyright 2006 James Bunton +# Licensed for distribution under the GPL version 2, check COPYING for details +from twisted.internet import task class Throttler: def __init__(self, consumer, speed): diff --git a/src/tlib/__init__.py b/src/tlib/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/tlib/domish.py b/src/tlib/domish.py deleted file mode 100644 index e5ee203..0000000 --- a/src/tlib/domish.py +++ /dev/null @@ -1,723 +0,0 @@ -# -*- 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("" % (name)) - else: - write("" % (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("" % (name)) - else: - write("" % (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) - - diff --git a/src/tlib/jabber/__init__.py b/src/tlib/jabber/__init__.py deleted file mode 100644 index d22eb42..0000000 --- a/src/tlib/jabber/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ - -# 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 - -""" diff --git a/src/tlib/jabber/client.py b/src/tlib/jabber/client.py deleted file mode 100644 index fb48782..0000000 --- a/src/tlib/jabber/client.py +++ /dev/null @@ -1,176 +0,0 @@ -# -*- 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}. Additionally, this - authenticator provides the ability to perform inline registration, per - U{JEP 77}. - - 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) - diff --git a/src/tlib/jabber/component.py b/src/tlib/jabber/component.py deleted file mode 100644 index 6c7863b..0000000 --- a/src/tlib/jabber/component.py +++ /dev/null @@ -1,175 +0,0 @@ -# -*- 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 diff --git a/src/tlib/jabber/jid.py b/src/tlib/jabber/jid.py deleted file mode 100644 index dccc7b6..0000000 --- a/src/tlib/jabber/jid.py +++ /dev/null @@ -1,152 +0,0 @@ -# -*- 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) diff --git a/src/tlib/jabber/jstrports.py b/src/tlib/jabber/jstrports.py deleted file mode 100644 index 9fbcfc5..0000000 --- a/src/tlib/jabber/jstrports.py +++ /dev/null @@ -1,42 +0,0 @@ -# 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) diff --git a/src/tlib/jabber/xmpp_stringprep.py b/src/tlib/jabber/xmpp_stringprep.py deleted file mode 100644 index 626263e..0000000 --- a/src/tlib/jabber/xmpp_stringprep.py +++ /dev/null @@ -1,231 +0,0 @@ -# -*- 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() diff --git a/src/tlib/xmlstream.py b/src/tlib/xmlstream.py deleted file mode 100644 index bb7f1da..0000000 --- a/src/tlib/xmlstream.py +++ /dev/null @@ -1,223 +0,0 @@ -# -*- 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 = "" % \ - (self.namespace) - else: - sh = "" % \ - (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)) - - - - - diff --git a/src/tlib/xmlw.py b/src/tlib/xmlw.py deleted file mode 100644 index 7341636..0000000 --- a/src/tlib/xmlw.py +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright 2004-2005 James Bunton -# 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) - - diff --git a/src/utils.py b/src/utils.py index 8107ddc..cb73ea3 100644 --- a/src/utils.py +++ b/src/utils.py @@ -69,51 +69,3 @@ errorCodeMap = { -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 - - - diff --git a/src/xdb.py b/src/xdb.py index 23da7ad..a3e3b5d 100644 --- a/src/xdb.py +++ b/src/xdb.py @@ -1,7 +1,7 @@ -# Copyright 2004-2005 James Bunton +# Copyright 2004-2006 James Bunton # 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 @@ -45,7 +45,7 @@ class XDB: 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 @@ -104,7 +104,7 @@ class XDB: 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(): diff --git a/src/xmlconfig.py b/src/xmlconfig.py index 11eab8f..7b0e1dd 100644 --- a/src/xmlconfig.py +++ b/src/xmlconfig.py @@ -1,7 +1,7 @@ # Copyright 2004-2005 James Bunton # 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 @@ -23,7 +23,7 @@ def importFile(configFile): # Get ourself a DOM try: - root = xmlw.parseFile(configFile) + root = parseFile(configFile) except Exception, e: invalidError("Error parsing configuration file: " + str(e))