#!/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()