diff options
author | mkaay <mkaay@mkaay.de> | 2010-12-19 20:53:54 +0100 |
---|---|---|
committer | mkaay <mkaay@mkaay.de> | 2010-12-19 20:53:54 +0100 |
commit | a8b5b705427b25b3a9a20c6e2c77ca8d9dd8a7d4 (patch) | |
tree | 42b295cfa31f1d40c2fb4fc9793b4e5b87e8d142 | |
parent | some adjusments (diff) | |
download | pyload-a8b5b705427b25b3a9a20c6e2c77ca8d9dd8a7d4.tar.xz |
new network backend: xdcc support
-rw-r--r-- | module/network/Browser.py | 7 | ||||
-rw-r--r-- | module/network/FTPBase.py | 3 | ||||
-rw-r--r-- | module/network/HTTPDownload.py | 2 | ||||
-rw-r--r-- | module/network/XDCCBase.py | 290 |
4 files changed, 300 insertions, 2 deletions
diff --git a/module/network/Browser.py b/module/network/Browser.py index b749a8338..90502b298 100644 --- a/module/network/Browser.py +++ b/module/network/Browser.py @@ -2,6 +2,7 @@ from HTTPBase import HTTPBase from HTTPDownload import HTTPDownload from os.path import exists +from randowm import randint import zlib from cookielib import CookieJar @@ -66,6 +67,12 @@ class Browser(): d = dwnld.download(resume=resume) return d + + def xdccDownload(self, server, port, channel, bot, pack, filename, nick="pyload_%d" % randint(1000,9999), ident="pyload", real="pyloadreal"): + dwnld = XDCCDownload(server, port, channel, bot, pack, nick, ident, real, filename, bucket=self.bucket, interface=self.interface, proxies=self.proxies) + + d = dwnld.download() + return d if __name__ == "__main__": browser = Browser(proxies={"socks5": "localhost:5000"}) diff --git a/module/network/FTPBase.py b/module/network/FTPBase.py index 4d27585a1..da67573a3 100644 --- a/module/network/FTPBase.py +++ b/module/network/FTPBase.py @@ -133,6 +133,7 @@ class FTPDownload(): break self.fh.write(data) + self.fh.close() conn.close() self.endTime = inttime() if not self.abort: @@ -142,7 +143,7 @@ class FTPDownload(): if self.abort: self.deferred.error("abort") elif self.size is None or self.size == self.arrived: - self.deferred.callback(resp) #@TODO resp = unresolved? + self.deferred.callback() else: self.deferred.error("wrong content lenght") diff --git a/module/network/HTTPDownload.py b/module/network/HTTPDownload.py index d0e2eeb1f..1be1a4552 100644 --- a/module/network/HTTPDownload.py +++ b/module/network/HTTPDownload.py @@ -272,7 +272,7 @@ if __name__ == "__main__": from Bucket import Bucket bucket = Bucket() bucket.setRate(200*1024) - bucket = None + #bucket = None url = "http://speedtest.netcologne.de/test_100mb.bin" diff --git a/module/network/XDCCBase.py b/module/network/XDCCBase.py new file mode 100644 index 000000000..b242c8f27 --- /dev/null +++ b/module/network/XDCCBase.py @@ -0,0 +1,290 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, + or (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. + + @author: mkaay +""" + +import socket +import socks +import re + +from os.path import getsize +from urlparse import urlparse +from urllib2 import _parse_proxy + +from helper import * +from time import sleep + +import struct +from select import select + +class XDCCError(Exception): + pass + +class XDCCDownload(): + def __init__(self, server, port, channel, bot, pack, nick, ident, real, filename, timeout=30, bucket=None, interface=None, proxies={}): + self.server = server + self.port = port + self.channel = channel + self.bot = bot + self.pack = pack + self.nick = nick + self.ident = ident + self.real = real + self.filename = filename + self.bucket = bucket + self.interface = interface + self.proxies = proxies + self.timeout = timeout + self.debug = 2 + + self.deferred = Deferred() + + self.finished = False + self.size = None + + self.speed = 0 + + self.abort = False + + self.arrived = 0 + + self.startTime = None + self.endTime = None + + self.speed = 0 #byte/sec + self.speedCalcTime = None + self.speedCalcLen = 0 + + self.fh = None + + def createSocket(self): + proxytype = None + proxy = None + if self.proxies.has_key("socks5"): + proxytype = socks.PROXY_TYPE_SOCKS5 + proxy = self.proxies["socks5"] + elif self.proxies.has_key("socks4"): + proxytype = socks.PROXY_TYPE_SOCKS4 + proxy = self.proxies["socks4"] + if proxytype: + sock = socks.socksocket() + t = _parse_proxy(proxy) + sock.setproxy(proxytype, addr=t[3].split(":")[0], port=int(t[3].split(":")[1]), username=t[1], password=t[2]) + else: + sock = socket.socket() + return sock + + def _download(self, ip, port): + startTime = inttime() + + dccsock = self.createSocket() + + dccsock.settimeout(self.timeout) + dccsock.bind(self.sourceAddress) + dccsock.connect((ip, port)) + + # recv loop for dcc socket + while True: + if self.abort: + break + count = 4096 + if self.bucket: + count = self.bucket.add(count) + if count < 4096: + sleep(0.01) + continue + + try: + data = dccsock.recv(count) + except: + self.deferred.error("timeout") + + if self.speedCalcTime < inttime(): + self.speed = self.speedCalcLen + self.speedCalcTime = inttime() + self.speedCalcLen = 0 + size = len(data) + self.speedCalcLen += size + self.arrived += size + + if not data: + break + + self.fh.write(data) + + # acknowledge data by sending number of recceived bytes + dccsock.send(struct.pack('!I', self.arrived)) + + dccsock.close() + self.fh.close() + + self.endTime = inttime() + if self.abort: + self.deferred.error("abort") + elif self.size is None or self.size == self.arrived: + self.deferred.callback() + else: + self.deferred.error("wrong content lenght") + + def download(self): + self.fh = open(self.filename, "wb") + + def debug(s): + print "XDCC:", s + + sock = self.createSocket() + print self.server, self.port + sock.connect((self.server, int(self.port))) + _realsend = sock.send + def send(s): + if self.debug == 2: + print s + _realsend(s) + sock.send = send + + sock.send("NICK %s\r\n" % self.nick) + sock.send("USER %s %s bla :%s\r\n" % (self.ident, self.server, self.real)) + sleep(3) + sock.send("JOIN #%s\r\n" % self.channel) + sock.send("PRIVMSG %s :xdcc send #%s\r\n" % (self.bot, self.pack)) + + # IRC recv loop + readbuffer = "" + dlTime = inttime() + done = False + retry = None + while True: + # done is set if we got our real link + if done: break + + if retry: + if inttime() > retry: + retry = None + dlTime = inttime() + sock.send("PRIVMSG %s :xdcc send #%s\r\n" % (self.bot, self.pack)) + + else: + if (dlTime + self.timeout) < inttime(): + sock.send("QUIT :byebye\r\n") + sock.close() + raise XDCCError("XDCC Bot did not answer") + + + fdset = select([sock], [], [], 0) + if sock not in fdset[0]: + continue + + readbuffer += sock.recv(1024) + temp = readbuffer.split("\n") + readbuffer = temp.pop() + + for line in temp: + if self.debug is 2: print "*> " + line + line = line.rstrip() + first = line.split() + + if(first[0] == "PING"): + sock.send("PONG %s\r\n" % first[1]) + + if first[0] == "ERROR": + raise XDCCError("IRC-Error: %s" % line) + + msg = line.split(None, 3) + if len(msg) != 4: + continue + + msg = { \ + "origin":msg[0][1:], \ + "action":msg[1], \ + "target":msg[2], \ + "text" :msg[3][1:] \ + } + + + if self.nick == msg["target"][0:len(self.nick)] and "PRIVMSG" == msg["action"]: + if msg["text"] == "\x01VERSION\x01": + debug("XDCC: Sending CTCP VERSION.") + sock.send("NOTICE %s :%s\r\n" % (msg['origin'], "pyLoad! IRC Interface")) + elif msg["text"] == "\x01TIME\x01": + debug("Sending CTCP TIME.") + sock.send("NOTICE %s :%d\r\n" % (msg['origin'], inttime())) + elif msg["text"] == "\x01LAG\x01": + pass # don't know how to answer + + if not (self.bot == msg["origin"][0:len(self.bot)] + and self.nick == msg["target"][0:len(self.nick)] + and msg["action"] in ("PRIVMSG", "NOTICE")): + continue + + if self.debug is 1: + print "%s: %s" % (msg["origin"], msg["text"]) + + if "You already requested that pack" in msg["text"]: + retry = inttime() + 300 + + if "you must be on a known channel to request a pack" in msg["text"]: + raise XDCCError("Wrong channel") + + m = re.match('\x01DCC SEND (.*?) (\d+) (\d+)(?: (\d+))?\x01', msg["text"]) + if m != None: + done = True + + # get connection data + ip = socket.inet_ntoa(struct.pack('L', socket.ntohl(int(m.group(2))))) + port = int(m.group(3)) + self.realfilename = m.group(1) + + if len(m.groups()) > 3: + self.size = int(m.group(4)) + + debug("XDCC: Downloading %s from %s:%d" % (packname, ip, port)) + + self._download(ip, port) + return self.deferred + +if __name__ == "__main__": + import sys + from Bucket import Bucket + bucket = Bucket() + #bucket.setRate(200*1000) + bucket = None + + finished = False + def err(*a, **b): + print a, b + def callb(*a, **b): + global finished + finished = True + print a, b + + print "starting" + dwnld = XDCCDownload("<server>", 6667, "<chan>", "<bot>", "<pack>", "<nick>", "<ident>", "<real>", "<filename>") + d = dwnld.download() + d.addCallback(callb) + d.addErrback(err) + + try: + while True: + if not dwnld.finished: + print dwnld.speed/1024, "kb/s", "size", dwnld.arrived, "/", dwnld.size#, int(float(dwnld.arrived)/dwnld.size*100), "%" + if finished: + print "- finished" + break + sleep(1) + except KeyboardInterrupt: + dwnld.abort = True + sys.exit() |