#!/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 .
@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 WrappedXDCCDeferred(WrappedDeferred):
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
try:
self.deferred.progress("percent", 100-int((self.size - self.arrived)/float(self.size)*100))
except:
pass
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 WrappedXDCCDeferred(self, 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("", 6667, "", "", "", "", "", "", "")
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()