X-Git-Url: https://code.delx.au/pymsnt/blobdiff_plain/85e6c0f55bd0d46cad7f94d15b52c7b64dc94c08..cbe38882237ba2dff9191c266ddc04a5e78ebba4:/src/misciq.py diff --git a/src/misciq.py b/src/misciq.py index 56b5be6..64cbe90 100644 --- a/src/misciq.py +++ b/src/misciq.py @@ -2,21 +2,18 @@ # Licensed for distribution under the GPL version 2, check COPYING for details import utils -if(utils.checkTwisted()): - from twisted.xish.domish import Element - from twisted.words.protocols.jabber import jid -else: - from tlib.domish import Element - from tlib.jabber import jid -from twisted.internet import reactor, task +from twisted.internet import reactor, task, protocol, error +from tlib.xmlw import Element, jid from debug import LogEvent, INFO, WARN, ERROR +import svninfo import jabw import legacy import disco import config import lang +import ft import base64 -import sys +import sys, urllib class ConnectUsers: @@ -33,7 +30,7 @@ class ConnectUsers: ID = el.getAttribute("id") ulang = utils.getLang(el) - if config.admins.count(jid.JID(to).userhost()) == 0: + if config.admins.count(jid.intern(to).userhost()) == 0: self.pytrans.discovery.sendIqError(to=to, fro=config.jid, ID=ID, xmlns=disco.COMMANDS, etype="cancel", condition="not-authorized") return @@ -53,7 +50,7 @@ class ConnectUsers: command.attributes["status"] = "completed" x = command.addElement("x") - x.attributes["xmlns"] = "jabber:x:data" + x.attributes["xmlns"] = disco.XDATA x.attributes["type"] = "result" title = x.addElement("title") @@ -97,7 +94,7 @@ class Statistics: command.attributes["status"] = "completed" x = command.addElement("x") - x.attributes["xmlns"] = "jabber:x:data" + x.attributes["xmlns"] = disco.XDATA x.attributes["type"] = "result" title = x.addElement("title") @@ -134,7 +131,7 @@ class AdHocCommands: def incomingIq(self, el): itype = el.getAttribute("type") fro = el.getAttribute("from") - froj = jid.JID(fro) + froj = jid.intern(fro) to = el.getAttribute("to") ID = el.getAttribute("id") @@ -142,7 +139,7 @@ class AdHocCommands: node = None for child in el.elements(): - xmlns = child.defaultUri + xmlns = child.uri node = child.getAttribute("node") handled = False @@ -222,10 +219,11 @@ class VCardFactory: def incomingIq(self, el): itype = el.getAttribute("type") fro = el.getAttribute("from") - froj = jid.JID(fro) + froj = jid.intern(fro) to = el.getAttribute("to") + toj = jid.intern(to) ID = el.getAttribute("id") - if(itype != "get" and itype != "error"): + 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") return @@ -233,17 +231,17 @@ class VCardFactory: toGateway = not (to.find('@') > 0) - if(not toGateway): - if(not self.pytrans.sessions.has_key(froj.userhost())): + if not toGateway: + if not self.pytrans.sessions.has_key(froj.userhost()): self.pytrans.discovery.sendIqError(to=fro, fro=config.jid, ID=ID, xmlns="vcard-temp", etype="auth", condition="not-authorized") return s = self.pytrans.sessions[froj.userhost()] - if(not s.ready): + if not s.ready: self.pytrans.discovery.sendIqError(to=fro, fro=config.jid, ID=ID, xmlns="vcard-temp", etype="auth", condition="not-authorized") return - c = s.contactList.findContact(to) - if(not c): + c = s.contactList.findContact(toj.userhost()) + if not c: self.pytrans.discovery.sendIqError(to=fro, fro=config.jid, ID=ID, xmlns="vcard-temp", etype="cancel", condition="recipient-unavailable") return @@ -256,18 +254,18 @@ class VCardFactory: iq.attributes["type"] = "result" vCard = iq.addElement("vCard") vCard.attributes["xmlns"] = "vcard-temp" - if(toGateway): + if toGateway: FN = vCard.addElement("FN") - FN.addContent(legacy.name) + FN.addContent(config.discoName) DESC = vCard.addElement("DESC") - DESC.addContent(legacy.name) + DESC.addContent(config.discoName) URL = vCard.addElement("URL") URL.addContent(legacy.url) else: - if(c.nickname): + if c.nickname: NICKNAME = vCard.addElement("NICKNAME") NICKNAME.addContent(c.nickname) - if(c.avatar): + if c.avatar: PHOTO = c.avatar.makePhotoElement() vCard.addChild(PHOTO) @@ -282,8 +280,9 @@ class IqAvatarFactory: def incomingIq(self, el): itype = el.getAttribute("type") fro = el.getAttribute("from") - froj = jid.JID(fro) + froj = jid.intern(fro) to = el.getAttribute("to") + toj = jid.intern(to) ID = el.getAttribute("id") if(itype != "get" and itype != "error"): @@ -300,7 +299,7 @@ class IqAvatarFactory: self.pytrans.discovery.sendIqError(to=fro, fro=config.jid, ID=ID, xmlns=disco.IQAVATAR, etype="auth", condition="not-authorized") return - c = s.contactList.findContact(to) + c = s.contactList.findContact(toj.userhost()) if(not c): self.pytrans.discovery.sendIqError(to=fro, fro=config.jid, ID=ID, xmlns=disco.IQAVATAR, etype="cancel", condition="recipient-unavailable") return @@ -433,6 +432,11 @@ class VersionTeller: self.pytrans = pytrans self.pytrans.discovery.addFeature(disco.IQVERSION, self.incomingIq, config.jid) self.pytrans.discovery.addFeature(disco.IQVERSION, self.incomingIq, "USER") + try: + self.version = "%s - SVN r%s" % (legacy.version, svninfo.getSVNVersion()) + except: + self.version = legacy.version + self.os = "Python" + ".".join([str(x) for x in sys.version_info[0:3]]) + " - " + sys.platform def incomingIq(self, el): eltype = el.getAttribute("type") @@ -451,13 +455,231 @@ class VersionTeller: query = iq.addElement("query") query.attributes["xmlns"] = disco.IQVERSION name = query.addElement("name") - name.addContent(legacy.name) + name.addContent(config.discoName) version = query.addElement("version") - version.addContent(legacy.version) + version.addContent(self.version) os = query.addElement("os") - os.addContent("Python" + ".".join([str(x) for x in sys.version_info[0:3]]) + " - " + sys.platform) + os.addContent(self.os) self.pytrans.send(iq) +class FileTransferOOBSend: + def __init__(self, pytrans): + self.pytrans = pytrans + self.pytrans.discovery.addFeature(disco.IQOOB, self.incomingOOB, "USER") + + def incomingOOB(self, el): + ID = el.getAttribute("id") + def errOut(): + self.pytrans.discovery.sendIqError(to=el.getAttribute("from"), fro=el.getAttribute("to"), ID=ID, xmlns=disco.IQOOB, etype="cancel", condition="feature-not-implemented") + + if el.attributes["type"] != "set": + return errOut() + for child in el.elements(): + if child.name == "query": + query = child + break + else: + return errOut() + for child in query.elements(): + if child.name == "url": + url = child.__str__() + break + else: + return errOut() + + froj = jid.intern(el.getAttribute("from")) + toj = jid.intern(el.getAttribute("to")) + session = self.pytrans.sessions.get(froj.userhost(), None) + if not session: + return errOut() + + res = utils.getURLBits(url, "http") + if not res: + return errOut() + host, port, path, filename = res + + + def sendResult(): + iq = Element((None, "iq")) + iq.attributes["to"] = froj.full() + iq.attributes["from"] = toj.full() + iq.attributes["type"] = "result" + if ID: + iq.attributes["id"] = ID + iq.addElement("query").attributes["xmlns"] = "jabber:iq:oob" + self.pytrans.send(iq) + + def startTransfer(consumer): + factory = protocol.ClientFactory() + factory.protocol = ft.OOBSendConnector + factory.path = path + factory.host = host + factory.port = port + factory.consumer = consumer + factory.finished = sendResult + reactor.connectTCP(host, port, factory) + + def doSendFile(length): + ft.FTSend(session, toj.userhost(), startTransfer, errOut, filename, length) + + # Make a HEAD request to grab the length of data first + factory = protocol.ClientFactory() + factory.protocol = ft.OOBHeaderHelper + factory.path = path + factory.host = host + factory.port = port + factory.gotLength = doSendFile + reactor.connectTCP(host, port, factory) + + + +class Socks5FileTransfer: + def __init__(self, pytrans): + self.pytrans = pytrans + self.pytrans.discovery.addFeature(disco.SI, self.incomingSI, "USER") + self.pytrans.discovery.addFeature(disco.FT, lambda: None, "USER") + self.pytrans.discovery.addFeature(disco.S5B, self.incomingS5B, "USER") + self.sessions = {} + + def incomingSI(self, el): + ID = el.getAttribute("id") + 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")) + session = self.pytrans.sessions.get(froj.userhost(), None) + if not session: + return errOut() + + si = el.si + if not (si and si.getAttribute("profile") == disco.FT): + return errOut() + file = si.file + if not (file and file.uri == disco.FT): + return errOut() + try: + sid = si["id"] + filename = file["name"] + filesize = int(file["size"]) + except KeyError: + return errOut() + except ValueError: + return errOut() + + # Check that we can use socks5 bytestreams + feature = si.feature + if not (feature and feature.uri == disco.FEATURE_NEG): + return errOut() + x = feature.x + if not (x and x.uri == disco.XDATA): + return errOut() + field = x.field + if not (field and field.getAttribute("var") == "stream-method"): + return errOut() + for option in field.elements(): + value = option.value + if not value: + continue + value = value.__str__() + if value == disco.S5B: + break + else: + return errOut() # Socks5 bytestreams not supported :( + + + def startTransfer(consumer): + iq = Element((None, "iq")) + iq["type"] = "result" + iq["to"] = froj.full() + iq["from"] = toj.full() + iq["id"] = ID + si = iq.addElement("si") + si["xmlns"] = disco.SI + feature = si.addElement("feature") + feature["xmlns"] = disco.FEATURE_NEG + x = feature.addElement("x") + x["xmlns"] = disco.XDATA + x["type"] = "submit" + field = x.addElement("field") + field["var"] = "stream-method" + value = field.addElement("value") + value.addContent(disco.S5B) + self.pytrans.send(iq) + self.sessions[(froj.full(), sid)] = consumer + + ft.FTSend(session, toj.userhost(), startTransfer, errOut, filename, filesize) + + def incomingS5B(self, el): + ID = el.getAttribute("id") + def errOut(): + self.pytrans.discovery.sendIqError(to=el.getAttribute("from"), fro=el.getAttribute("to"), ID=ID, xmlns=disco.S5B, etype="cancel", condition="item-not-found") + + if el.getAttribute("type") != "set": + return errOut() + + toj = jid.intern(el.getAttribute("to")) + froj = jid.intern(el.getAttribute("from")) + + query = el.query + if not (query and query.getAttribute("mode", "tcp") == "tcp"): + return errOut() + sid = query.getAttribute("sid") + consumer = self.sessions.pop((froj.full(), sid), None) + if not consumer: + return errOut() + streamhosts = [] + for streamhost in query.elements(): + if streamhost.name == "streamhost": + try: + JID = streamhost["jid"] + host = streamhost["host"] + port = int(streamhost["port"]) + except ValueError: + return errOut() + except KeyError: + continue + streamhosts.append((JID, host, port)) + + + def gotStreamhost(host): + for streamhost in streamhosts: + if streamhost[1] == host: + jid = streamhost[0] + break + else: + LogEvent(WARN) + return errOut() + + for connector in factory.connectors: + # Stop any other connections + try: + connector.stopConnecting() + except error.NotConnectingError: + pass + + iq = Element((None, "iq")) + iq["type"] = "result" + iq["from"] = toj.full() + iq["to"] = froj.full() + iq["id"] = ID + query = iq.addElement("query") + query["xmlns"] = disco.S5B + streamhost = query.addElement("streamhost-used") + streamhost["jid"] = jid + self.pytrans.send(iq) + + + # Try the streamhosts + factory = protocol.ClientFactory() + factory.protocol = ft.JEP65ConnectionSend + factory.consumer = consumer + factory.hash = utils.socks5Hash(sid, froj.full(), toj.full()) + factory.madeConnection = gotStreamhost + factory.connectors = [] + for streamhost in streamhosts: + factory.connectors.append(reactor.connectTCP(streamhost[1], streamhost[2], factory)) +