# Copyright 2005 James Bunton <james@delx.cjb.net>
# Licensed for distribution under the GPL version 2, check COPYING for details
+from tlib.throttle import Throttler
from tlib.xmlw import Element
from twisted.internet import protocol
import disco
+import lang
from debug import LogEvent, INFO, WARN, ERROR
import config
import utils
import sys
+def doRateLimit(setConsumer, consumer):
+ try:
+ rateLimit = int(config.ftRateLimit)
+ except ValueError:
+ rateLimit = 0
+ if rateLimit > 0:
+ throttler = Throttler(consumer, rateLimit)
+ setConsumer(throttler)
+ else:
+ setConsumer(consumer)
+
+def checkSizeOk(size):
+ try:
+ size = int(size)
+ limit = int(config.ftSizeLimit)
+ except ValueError:
+ return False
+ if limit == 0:
+ return True
+ return limit > size
+
###########
# Sending #
###########
class FTSend:
""" For file transfers going from Jabber to MSN. """
- def __init__(self, startTransfer, filename, filesize):
+ def __init__(self, session, to, startTransfer, cancelTransfer, filename, filesize):
self.startTransfer = startTransfer
+ self.cancelTransfer = cancelTransfer
self.filename = filename
self.filesize = filesize
+ if not checkSizeOk(self.filesize):
+ LogEvent(INFO, session.jabberID, "File too large.")
+ text = lang.get(session.lang).msnFtSizeRejected % (self.filename, config.ftSizeLimit, config.website)
+ session.legacycon.sendMessage(to, "", text, True)
+ session.sendMessage(to=session.jabberID, fro=to, body=text)
+ self.reject()
+ return
+
+ session.legacycon.sendFile(to, self)
def accept(self, legacyFileSend):
- self.startTransfer(legacyFileSend)
+ doRateLimit(self.startTransfer, legacyFileSend)
+ self.cleanup()
def reject(self):
- del self.startTransfer
+ self.cancelTransfer()
+ self.cleanup()
+
+ def cleanup(self):
+ del self.startTransfer, self.cancelTransfer
try:
"""
def __init__(self, session, senderJID, legacyftp):
+ if not checkSizeOk(legacyftp.filesize):
+ LogEvent(INFO, session.jabberID, "File too large.")
+ legacyftp.reject()
+ text = lang.get(session.lang).msnFtSizeRejected % (legacyftp.filename, config.ftSizeLimit, config.website)
+ session.legacycon.sendMessage(senderJID, "", text, False)
+ session.sendMessage(to=session.jabberID, fro=senderJID, body=text)
+ return
self.session = session
self.toJID = self.session.jabberID + "/" + self.session.highestResource()
self.senderJID = senderJID
self.ident = (self.toJID, self.senderJID)
self.legacyftp = legacyftp
- LogEvent(INFO)
+ LogEvent(INFO, session.jabberID)
self.checkSupport()
def checkSupport(self):
def discoDone(features):
LogEvent(INFO, self.ident)
- enabledS5B = hasattr(self.session.pytrans, "ftSOCKS5")
- enabledOOB = hasattr(self.session.pytrans, "ftOOB")
+ enabledS5B = hasattr(self.session.pytrans, "ftSOCKS5Receive")
+ enabledOOB = hasattr(self.session.pytrans, "ftOOBReceive")
hasFT = features.count(disco.FT)
hasS5B = features.count(disco.S5B)
hasOOB = features.count(disco.IQOOB)
def discoFail(err=None):
LogEvent(INFO, self.ident, str(err))
- self.messageOobMode()
+ if hasattr(self.session.pytrans, "ftOOBReceive"):
+ self.messageOobMode()
+ else:
+ # No support
+ self.legacyftp.reject()
+ del self.legacyftp
d = disco.DiscoRequest(self.session.pytrans, self.toJID).doDisco()
d.addCallbacks(discoDone, discoFail)
if el.getAttribute("type") != "result":
ftDeclined()
return
- self.session.pytrans.ftSOCKS5.addConnection(utils.socks5Hash(self.sid, self.senderJID, self.toJID), self.legacyftp)
+ self.session.pytrans.ftSOCKS5Receive.addConnection(utils.socks5Hash(self.sid, self.senderJID, self.toJID), self.legacyftp)
LogEvent(INFO, self.ident)
iq = Element((None, "iq"))
iq.attributes["type"] = "set"
query.attributes["mode"] = "tcp"
streamhost = query.addElement("streamhost")
streamhost.attributes["jid"] = self.senderJID
- streamhost.attributes["host"] = config.ip
+ streamhost.attributes["host"] = config.host
streamhost.attributes["port"] = config.ftJabberPort
d = self.session.pytrans.discovery.sendIq(iq)
d.addErrback(ftDeclined) # Timeout
feature = si.addElement("feature")
feature.attributes["xmlns"] = disco.FEATURE_NEG
x = feature.addElement("x")
- x.attributes["xmlns"] = "jabber:x:data"
+ x.attributes["xmlns"] = disco.XDATA
x.attributes["type"] = "form"
field = x.addElement("field")
field.attributes["type"] = "list-single"
if el.getAttribute("type") != "result":
self.legacyftp.reject()
del self.legacyftp
- self.session.pytrans.ftOOB.remFile(filename)
+ self.session.pytrans.ftOOBReceive.remFile(filename)
def ecb(ignored=None):
self.legacyftp.reject()
del self.legacyftp
LogEvent(INFO, self.ident)
- filename = self.session.pytrans.ftOOB.putFile(self, self.legacyftp.filename)
+ filename = self.session.pytrans.ftOOBReceive.putFile(self, self.legacyftp.filename)
iq = Element((None, "iq"))
iq.attributes["to"] = self.toJID
iq.attributes["from"] = self.senderJID
def messageOobMode(self):
LogEvent(INFO, self.ident)
- filename = self.session.pytrans.ftOOB.putFile(self, self.legacyftp.filename)
+ filename = self.session.pytrans.ftOOBReceive.putFile(self, self.legacyftp.filename)
m = Element((None, "message"))
m.attributes["to"] = self.session.jabberID
m.attributes["from"] = self.senderJID
# SOCKS5
from tlib import socks5
+import struct
+
+class JEP65ConnectionSend(protocol.Protocol):
+# TODO, clean up and move this to tlib.socks5
+ STATE_INITIAL = 1
+ STATE_WAIT_AUTHOK = 2
+ STATE_WAIT_CONNECTOK = 3
+ STATE_READY = 4
+
+ def __init__(self):
+ self.state = self.STATE_INITIAL
+ self.buf = ""
+
+ def connectionMade(self):
+ self.transport.write(struct.pack("!BBB", 5, 1, 0))
+ self.state = self.STATE_WAIT_AUTHOK
+
+ def connectionLost(self, reason):
+ if self.state == self.STATE_READY:
+ self.factory.consumer.close()
+ else:
+ self.factory.consumer.error()
+
+ def _waitAuthOk(self):
+ ver, method = struct.unpack("!BB", self.buf[:2])
+ if ver != 5 or method != 0:
+ self.transport.loseConnection()
+ return
+ self.buf = self.buf[2:] # chop
+
+ # Send CONNECT request
+ length = len(self.factory.hash)
+ self.transport.write(struct.pack("!BBBBB", 5, 1, 0, 3, length))
+ self.transport.write("".join([struct.pack("!B" , ord(x))[0] for x in self.factory.hash]))
+ self.transport.write(struct.pack("!H", 0))
+ self.state = self.STATE_WAIT_CONNECTOK
+
+ def _waitConnectOk(self):
+ ver, rep, rsv, atyp = struct.unpack("!BBBB", self.buf[:4])
+ if not (ver == 5 and rep == 0):
+ self.transport.loseConnection()
+ return
+
+ self.state = self.STATE_READY
+ self.factory.madeConnection(self.transport.addr[0])
+
+ def dataReceived(self, buf):
+ if self.state == self.STATE_READY:
+ self.factory.consumer.write(buf)
+
+ self.buf += buf
+ if self.state == self.STATE_WAIT_AUTHOK:
+ self._waitAuthOk()
+ elif self.state == self.STATE_WAIT_CONNECTOK:
+ self._waitConnectOk()
+
-class JEP65Connection(socks5.SOCKSv5):
+class JEP65ConnectionReceive(socks5.SOCKSv5):
def __init__(self, listener):
socks5.SOCKSv5.__init__(self)
self.listener = listener
self.activeConns = {}
def buildProtocol(self, addr):
- return JEP65Connection(self)
+ return JEP65ConnectionReceive(self)
def isActive(self, address):
return address in self.activeConns
assert address not in self.activeConns
self.activeConns[address] = None
- if not isinstance(olist[0], JEP65Connection):
+ if not isinstance(olist[0], JEP65ConnectionReceive):
legacyftp = olist[0]
connection = olist[1]
- elif not isinstance(olist[1], JEP65Connection):
+ elif not isinstance(olist[1], JEP65ConnectionReceive):
legacyftp = olist[1]
connection = olist[0]
else:
- LogEvent(WARN, '', "No legacyftp")
+ LogEvent(WARN, '', "No JEP65Connection")
return
- legacyftp.accept(connection.transport)
+ doRateLimit(legacyftp.accept, connection.transport)
else:
LogEvent(WARN, '', "No pending connection.")
class OOBReceiveConnector:
def __init__(self, ftReceive, ftHttpPush):
self.ftReceive, self.ftHttpPush = ftReceive, ftHttpPush
- self.ftReceive.legacyftp.accept(self)
+ doRateLimit(self.ftReceive.legacyftp.accept, self)
def write(self, data):
self.ftHttpPush.write(data)