# -*- 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: jeix """ from os.path import join from os.path import exists from os import makedirs import re import sys import time import socket import struct from select import select from module.utils import save_join from module.plugins.Hoster import Hoster class Xdcc(Hoster): __name__ = "Xdcc" __version__ = "0.32" __pattern__ = r'xdcc://([^/]*?)(/#?.*?)?/.*?/#?\d+/?' # xdcc://irc.Abjects.net/#channel/[XDCC]|Shit/#0004/ __type__ = "hoster" __config__ = [("nick", "str", "Nickname", "pyload"), ("ident", "str", "Ident", "pyloadident"), ("realname", "str", "Realname", "pyloadreal")] __description__ = """Download from IRC XDCC bot""" __author_name__ = "jeix" __author_mail__ = "jeix@hasnomail.com" def setup(self): self.debug = 0 # 0,1,2 self.timeout = 30 self.multiDL = False def process(self, pyfile): # change request type self.req = pyfile.m.core.requestFactory.getRequest(self.__name__, type="XDCC") self.pyfile = pyfile for _ in xrange(0, 3): try: nmn = self.doDownload(pyfile.url) self.logDebug("%s: Download of %s finished." % (self.__name__, nmn)) return except socket.error, e: if hasattr(e, "errno"): errno = e.errno else: errno = e.args[0] if errno in (10054,): self.logDebug("XDCC: Server blocked our ip, retry in 5 min") self.setWait(300) self.wait() continue self.fail("Failed due to socket errors. Code: %d" % errno) self.fail("Server blocked our ip, retry again later manually") def doDownload(self, url): self.pyfile.setStatus("waiting") # real link download_folder = self.config['general']['download_folder'] location = join(download_folder, self.pyfile.package().folder.decode(sys.getfilesystemencoding())) if not exists(location): makedirs(location) m = re.match(r'xdcc://(.*?)/#?(.*?)/(.*?)/#?(\d+)/?', url) server = m.group(1) chan = m.group(2) bot = m.group(3) pack = m.group(4) nick = self.getConfig('nick') ident = self.getConfig('ident') real = self.getConfig('realname') temp = server.split(':') ln = len(temp) if ln == 2: host, port = temp elif ln == 1: host, port = temp[0], 6667 else: self.fail("Invalid hostname for IRC Server (%s)" % server) ####################### # CONNECT TO IRC AND IDLE FOR REAL LINK dl_time = time.time() sock = socket.socket() sock.connect((host, int(port))) if nick == "pyload": nick = "pyload-%d" % (time.time() % 1000) # last 3 digits sock.send("NICK %s\r\n" % nick) sock.send("USER %s %s bla :%s\r\n" % (ident, host, real)) time.sleep(3) sock.send("JOIN #%s\r\n" % chan) sock.send("PRIVMSG %s :xdcc send #%s\r\n" % (bot, pack)) # IRC recv loop readbuffer = "" done = False retry = None m = None while True: # done is set if we got our real link if done: break if retry: if time.time() > retry: retry = None dl_time = time.time() sock.send("PRIVMSG %s :xdcc send #%s\r\n" % (bot, pack)) else: if (dl_time + self.timeout) < time.time(): # todo: add in config sock.send("QUIT :byebye\r\n") sock.close() self.fail("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 "*> " + unicode(line, errors='ignore') line = line.rstrip() first = line.split() if first[0] == "PING": sock.send("PONG %s\r\n" % first[1]) if first[0] == "ERROR": self.fail("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 nick == msg["target"][0:len(nick)] and "PRIVMSG" == msg["action"]: if msg["text"] == "\x01VERSION\x01": self.logDebug("XDCC: Sending CTCP VERSION.") sock.send("NOTICE %s :%s\r\n" % (msg['origin'], "pyLoad! IRC Interface")) elif msg["text"] == "\x01TIME\x01": self.logDebug("Sending CTCP TIME.") sock.send("NOTICE %s :%d\r\n" % (msg['origin'], time.time())) elif msg["text"] == "\x01LAG\x01": pass # don't know how to answer if not (bot == msg["origin"][0:len(bot)] and nick == msg["target"][0:len(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 = time.time() + 300 if "you must be on a known channel to request a pack" in msg["text"]: self.fail("Wrong channel") m = re.match('\x01DCC SEND (.*?) (\d+) (\d+)(?: (\d+))?\x01', msg["text"]) if m: done = True # get connection data ip = socket.inet_ntoa(struct.pack('L', socket.ntohl(int(m.group(2))))) port = int(m.group(3)) packname = m.group(1) if len(m.groups()) > 3: self.req.filesize = int(m.group(4)) self.pyfile.name = packname filename = save_join(location, packname) self.logInfo("XDCC: Downloading %s from %s:%d" % (packname, ip, port)) self.pyfile.setStatus("downloading") newname = self.req.download(ip, port, filename, sock, self.pyfile.setProgress) if newname and newname != filename: self.logInfo("%(name)s saved as %(newname)s" % {"name": self.pyfile.name, "newname": newname}) filename = newname # kill IRC socket # sock.send("QUIT :byebye\r\n") sock.close() self.lastDownload = filename return self.lastDownload