# Licensed for distribution under the GPL version 2, check COPYING for details
import utils
-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:
ID = el.getAttribute("id")
ulang = utils.getLang(el)
- if config.admins.count(utils.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
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")
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")
def incomingIq(self, el):
itype = el.getAttribute("type")
fro = el.getAttribute("from")
- froj = utils.jid(fro)
+ froj = jid.intern(fro)
to = el.getAttribute("to")
ID = el.getAttribute("id")
node = None
for child in el.elements():
- xmlns = child.defaultUri
+ xmlns = child.uri
node = child.getAttribute("node")
handled = False
def incomingIq(self, el):
itype = el.getAttribute("type")
fro = el.getAttribute("from")
- froj = utils.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
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
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)
def incomingIq(self, el):
itype = el.getAttribute("type")
fro = el.getAttribute("from")
- froj = utils.jid(fro)
+ froj = jid.intern(fro)
to = el.getAttribute("to")
+ toj = jid.intern(to)
ID = el.getAttribute("id")
if(itype != "get" and itype != "error"):
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
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")
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
+
+ if factory.streamHostTimeout:
+ factory.streamHostTimeout.cancel()
+ factory.streamHostTimeout = None
+
+ 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
+ LogEvent(INFO)
+ factory = protocol.ClientFactory()
+ factory.protocol = ft.JEP65ConnectionSend
+ factory.consumer = consumer
+ factory.hash = utils.socks5Hash(sid, froj.full(), toj.full())
+ factory.madeConnection = gotStreamhost
+ factory.connectors = []
+ factory.streamHostTimeout = reactor.callLater(120, consumer.error)
+
+ for streamhost in streamhosts:
+ factory.connectors.append(reactor.connectTCP(streamhost[1], streamhost[2], factory))
+