# -*- 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 . """ 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