summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar mkaay <mkaay@mkaay.de> 2010-12-19 20:53:54 +0100
committerGravatar mkaay <mkaay@mkaay.de> 2010-12-19 20:53:54 +0100
commita8b5b705427b25b3a9a20c6e2c77ca8d9dd8a7d4 (patch)
tree42b295cfa31f1d40c2fb4fc9793b4e5b87e8d142
parentsome adjusments (diff)
downloadpyload-a8b5b705427b25b3a9a20c6e2c77ca8d9dd8a7d4.tar.xz
new network backend: xdcc support
-rw-r--r--module/network/Browser.py7
-rw-r--r--module/network/FTPBase.py3
-rw-r--r--module/network/HTTPDownload.py2
-rw-r--r--module/network/XDCCBase.py290
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()