From 29f9dc8fb3396b03d732ebcbeb1cc8f00fe13897 Mon Sep 17 00:00:00 2001 From: RaNaN Date: Wed, 25 Aug 2010 18:22:27 +0200 Subject: new dirs --- module/plugins/hooks/ClickAndLoad.py | 79 +++++++ module/plugins/hooks/ExternalScripts.py | 108 ++++++++++ module/plugins/hooks/HotFolder.py | 81 +++++++ module/plugins/hooks/IRCInterface.py | 364 ++++++++++++++++++++++++++++++++ module/plugins/hooks/MultiHome.py | 82 +++++++ module/plugins/hooks/UnRar.py | 137 ++++++++++++ module/plugins/hooks/UpdateManager.py | 59 ++++++ module/plugins/hooks/XMPPInterface.py | 229 ++++++++++++++++++++ module/plugins/hooks/__init__.py | 0 9 files changed, 1139 insertions(+) create mode 100644 module/plugins/hooks/ClickAndLoad.py create mode 100644 module/plugins/hooks/ExternalScripts.py create mode 100644 module/plugins/hooks/HotFolder.py create mode 100644 module/plugins/hooks/IRCInterface.py create mode 100644 module/plugins/hooks/MultiHome.py create mode 100644 module/plugins/hooks/UnRar.py create mode 100644 module/plugins/hooks/UpdateManager.py create mode 100644 module/plugins/hooks/XMPPInterface.py create mode 100644 module/plugins/hooks/__init__.py (limited to 'module/plugins/hooks') diff --git a/module/plugins/hooks/ClickAndLoad.py b/module/plugins/hooks/ClickAndLoad.py new file mode 100644 index 000000000..b9824b863 --- /dev/null +++ b/module/plugins/hooks/ClickAndLoad.py @@ -0,0 +1,79 @@ +# -*- 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: RaNaN + @interface-version: 0.2 +""" + +import socket +import thread + +from module.plugins.Hook import Hook + +class ClickAndLoad(Hook): + __name__ = "ClickAndLoad" + __version__ = "0.2" + __description__ = """Gives abillity to use jd's click and load. depends on webinterface""" + __config__ = [ ("activated", "bool", "Activated" , "True"), + ("extern", "bool", "Allow external link adding", "False") ] + __author_name__ = ("RaNaN", "mkaay") + __author_mail__ = ("RaNaN@pyload.de", "mkaay@mkaay.de") + + def coreReady(self): + self.port = int(self.core.config['webinterface']['port']) + if self.core.config['webinterface']['activated']: + try: + if self.getConfig("extern"): + ip = "0.0.0.0" + else: + ip = "127.0.0.1" + + thread.start_new_thread(proxy, (ip, self.port, 9666)) + except: + self.logger.error("ClickAndLoad port already in use.") + + +def proxy(*settings): + thread.start_new_thread(server, settings) + lock = thread.allocate_lock() + lock.acquire() + lock.acquire() + +def server(*settings): + try: + dock_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + dock_socket.bind((settings[0], settings[2])) + dock_socket.listen(5) + while True: + client_socket = dock_socket.accept()[0] + server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server_socket.connect(("127.0.0.1", settings[1])) + thread.start_new_thread(forward, (client_socket, server_socket)) + thread.start_new_thread(forward, (server_socket, client_socket)) + except: + pass + finally: + thread.start_new_thread(server, settings) + +def forward(source, destination): + string = ' ' + while string: + string = source.recv(1024) + if string: + destination.sendall(string) + else: + #source.shutdown(socket.SHUT_RD) + destination.shutdown(socket.SHUT_WR) diff --git a/module/plugins/hooks/ExternalScripts.py b/module/plugins/hooks/ExternalScripts.py new file mode 100644 index 000000000..a3ed2f168 --- /dev/null +++ b/module/plugins/hooks/ExternalScripts.py @@ -0,0 +1,108 @@ +# -*- 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 + @interface-version: 0.1 +""" + +from module.plugins.Hook import Hook +import subprocess +from os import listdir, sep +from os.path import join +import sys + +class ExternalScripts(Hook): + __name__ = "ExternalScripts" + __version__ = "0.1" + __description__ = """run external scripts""" + __config__ = [ ("activated", "bool", "Activated" , "True") ] + __author_name__ = ("mkaay", "RaNaN", "spoob") + __author_mail__ = ("mkaay@mkaay.de", "ranan@pyload.org", "spoob@pyload.org") + + def __init__(self, core): + Hook.__init__(self, core) + self.scripts = {} + + script_folders = [join(pypath, 'scripts','download_preparing'), + join(pypath,'scripts','download_finished'), + join(pypath,'scripts','package_finished'), + join(pypath,'scripts','before_reconnect'), + join(pypath,'scripts','after_reconnect')] + + folder = core.path("scripts") + + self.core.check_file(folder, _("folders for scripts"), True) + self.core.check_file(script_folders, _("folders for scripts"), True) + + f = lambda x: False if x.startswith("#") or x.endswith("~") else True + self.scripts = {} + + + self.scripts['download_preparing'] = filter(f, listdir(join(folder, 'download_preparing'))) + self.scripts['download_finished'] = filter(f, listdir(join(folder, 'download_finished'))) + self.scripts['package_finished'] = filter(f, listdir(join(folder, 'package_finished'))) + self.scripts['before_reconnect'] = filter(f, listdir(join(folder, 'before_reconnect'))) + self.scripts['after_reconnect'] = filter(f, listdir(join(folder, 'after_reconnect'))) + + for script_type, script_name in self.scripts.iteritems(): + if script_name != []: + self.log.info("Installed %s Scripts: %s" % (script_type, ", ".join(script_name))) + + #~ self.core.logger.info("Installed Scripts: %s" % str(self.scripts)) + + self.folder = folder + + def downloadStarts(self, pyfile): + for script in self.scripts['download_preparing']: + try: + cmd = [join(self.folder, 'download_preparing', script), pyfile.pluginname, pyfile.url] + out = subprocess.Popen(cmd, stdout=subprocess.PIPE) + out.wait() + except: + pass + + def downloadFinished(self, pyfile): + for script in self.scripts['download_finished']: + try: + out = subprocess.Popen([join(self.folder, 'download_finished', script), pyfile.pluginname, pyfile.url, pyfile.name, join(self.core.config['general']['download_folder'], pyfile.package().folder, pyfile.name)], stdout=subprocess.PIPE) + except: + pass + + def packageFinished(self, pypack): + for script in self.scripts['package_finished']: + folder = self.core.config['general']['download_folder'] + if self.core.config.get("general", "folder_per_package"): + folder = join(folder.decode(sys.getfilesystemencoding()), pypack.folder.decode(sys.getfilesystemencoding())) + + try: + out = subprocess.Popen([join(self.folder, 'package_finished', script), pypack.name, folder], stdout=subprocess.PIPE) + except: + pass + + def beforeReconnecting(self, ip): + for script in self.scripts['before_reconnect']: + try: + out = subprocess.Popen([join(self.folder, 'before_reconnect', script), ip], stdout=subprocess.PIPE) + out.wait() + except: + pass + + def afterReconnecting(self, ip): + for script in self.scripts['after_reconnect']: + try: + out = subprocess.Popen([join(self.folder, 'download_preparing', script), ip], stdout=subprocess.PIPE) + except: + pass diff --git a/module/plugins/hooks/HotFolder.py b/module/plugins/hooks/HotFolder.py new file mode 100644 index 000000000..6c6825954 --- /dev/null +++ b/module/plugins/hooks/HotFolder.py @@ -0,0 +1,81 @@ +# -*- 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: RaNaN + @interface-version: 0.2 +""" + +from os import makedirs +from os import listdir +from os.path import exists +from os.path import join +from os.path import isfile +from shutil import move +import time + +from module.plugins.Hook import Hook + +class HotFolder(Hook): + __name__ = "HotFolder" + __version__ = "0.1" + __description__ = """observe folder and file for changes and add container and links""" + __config__ = [ ("activated", "bool", "Activated" , "False"), + ("folder", "str", "Folder to observe", "container"), + ("watch_file", "bool", "Observe link file", "False"), + ("keep", "bool", "Keep added containers", "True"), + ("file", "str", "Link file", "links.txt")] + __threaded__ = [] + __author_name__ = ("RaNaN") + __author_mail__ = ("RaNaN@pyload.de") + + def setup(self): + self.interval = 10 + + def periodical(self): + + if not exists(join(self.getConfig("folder"), "finished")): + makedirs(join(self.getConfig("folder"), "finished")) + + if self.getConfig("watch_file"): + + if not exists(self.getConfig("file")): + f = open(self.getConfig("file"), "wb") + f.close() + + + f = open(self.getConfig("file"), "rb") + urls = [x.strip() for x in f.readlines() if x.strip()] + f.close() + if urls: + name = "%s @ %s" % (self.getConfig("file"), time.strftime("%H:%M:%S %d%b%Y") ) + f = open(self.getConfig("file"), "wb") + f.close() + + self.core.server_methods.add_package(f.name, urls, 1) + + for f in listdir(self.getConfig("folder")): + path = join(self.getConfig("folder"), f) + + if not isfile(path) or f.endswith("~") or f.startswith("#"): + continue + + newpath = join(self.getConfig("folder"), "finished", f if self.getConfig("keep") else "tmp_"+f) + move(path, newpath) + + self.log.info(_("Added %s from HotFolder") % f) + self.core.server_methods.add_package(f, [newpath], 1) + + \ No newline at end of file diff --git a/module/plugins/hooks/IRCInterface.py b/module/plugins/hooks/IRCInterface.py new file mode 100644 index 000000000..e7e1e6797 --- /dev/null +++ b/module/plugins/hooks/IRCInterface.py @@ -0,0 +1,364 @@ +# -*- 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: RaNaN + @author: jeix + @interface-version: 0.2 +""" + +from select import select +import socket +import sys +from threading import Thread +import time +from time import sleep +from traceback import print_exc + +from module.plugins.Hook import Hook + +class IRCInterface(Thread, Hook): + __name__ = "IRCInterface" + __version__ = "0.1" + __description__ = """connect to irc and let owner perform different tasks""" + __config__ = [("activated", "bool", "Activated", "False"), + ("host", "str", "IRC-Server Address", "Enter your server here!"), + ("port", "int", "IRC-Server Port", "6667"), + ("ident", "str", "Clients ident", "pyload-irc"), + ("realname", "str", "Realname", "pyload-irc"), + ("nick", "str", "Nickname the Client will take", "pyLoad-IRC"), + ("owner", "str", "Nickname the Client will accept commands from", "Enter your nick here!"), + ("info_file", "bool", "Inform about every file finished", "False"), + ("info_pack", "bool", "Inform about every package finished", "True")] + __author_name__ = ("Jeix") + __author_mail__ = ("Jeix@hasnomail.com") + + def __init__(self, core): + Thread.__init__(self) + Hook.__init__(self, core) + self.setDaemon(True) + self.sm = core.server_methods + + def coreReady(self): + self.new_package = {} + + self.abort = False + + self.links_added = 0 + self.more = [] + + self.start() + + + def packageFinished(self, pypack): + try: + if self.getConfig("info_pack"): + self.response(_("Package finished: %s") % pypack.name) + except: + pass + + def downloadFinished(self, pyfile): + try: + if self.getConfig("info_file"): + self.response(_("Download finished: %s @ %s") % (pyfile.name, pyfile.pluginname) ) + except: + pass + + def run(self): + # connect to IRC etc. + self.sock = socket.socket() + host = self.getConfig("host") + self.sock.connect((host, self.getConfig("port"))) + nick = self.getConfig("nick") + self.sock.send("NICK %s\r\n" % nick) + self.sock.send("USER %s %s bla :%s\r\n" % (nick, host, nick)) + for t in self.getConfig("owner").split(): + if t.strip().startswith("#"): + self.sock.send("JOIN %s\r\n" % t.strip()) + self.log.info("pyLoad IRC: Connected to %s!" % host) + self.log.info("pyLoad IRC: Switching to listening mode!") + try: + self.main_loop() + + except IRCError, ex: + self.sock.send("QUIT :byebye\r\n") + print_exc() + self.sock.close() + + + def main_loop(self): + readbuffer = "" + while True: + sleep(1) + fdset = select([self.sock], [], [], 0) + if self.sock not in fdset[0]: + continue + + if self.abort: + raise IRCError("quit") + + readbuffer += self.sock.recv(1024) + temp = readbuffer.split("\n") + readbuffer = temp.pop() + + for line in temp: + line = line.rstrip() + first = line.split() + + if(first[0] == "PING"): + self.sock.send("PING %s\r\n" % first[1]) + + if first[0] == "ERROR": + raise IRCError(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:] + } + + self.handle_events(msg) + + + def handle_events(self, msg): + if not msg["origin"].split("!", 1)[0] in self.getConfig("owner").split(): + return + + if msg["target"].split("!", 1)[0] != self.getConfig("nick"): + return + + if msg["action"] != "PRIVMSG": + return + + # HANDLE CTCP ANTI FLOOD/BOT PROTECTION + if msg["text"] == "\x01VERSION\x01": + self.log.debug("Sending CTCP VERSION.") + self.sock.send("NOTICE %s :%s\r\n" % (msg['origin'], "pyLoad! IRC Interface")) + return + elif msg["text"] == "\x01TIME\x01": + self.log.debug("Sending CTCP TIME.") + self.sock.send("NOTICE %s :%d\r\n" % (msg['origin'], time.time())) + return + elif msg["text"] == "\x01LAG\x01": + self.log.debug("Received CTCP LAG.") # don't know how to answer + return + + trigger = "pass" + args = None + + temp = msg["text"].split() + trigger = temp[0] + if len(temp) > 1: + args = temp[1:] + + handler = getattr(self, "event_%s" % trigger, self.event_pass) + try: + res = handler(args) + for line in res: + self.response(line, msg["origin"]) + except Exception, e: + self.log.error("pyLoad IRC: "+ repr(e)) + + + def response(self, msg, origin=""): + if origin == "": + for t in self.getConfig("owner").split(): + self.sock.send("PRIVMSG %s :%s\r\n" % (t.strip(), msg)) + else: + self.sock.send("PRIVMSG %s :%s\r\n" % (origin.split("!", 1)[0], msg)) + + +#### Events + def event_pass(self, args): + return [] + + def event_status(self, args): + downloads = self.sm.status_downloads() + if not downloads: + return ["INFO: There are no active downloads currently."] + + lines = [] + lines.append("ID - Name - Status - Speed - ETA - Progress") + for data in downloads: + lines.append("#%d - %s - %s - %s - %s - %s" % + ( + data['id'], + data['name'], + data['statusmsg'], + "%.2f kb/s" % data['speed'], + "%s" % data['format_eta'], + "%d%% (%s)" % (data['percent'], data['format_size']) + ) + ) + return lines + + def event_queue(self, args): + ps = self.sm.get_queue() + + if not ps: + return ["INFO: There are no packages in queue."] + + lines = [] + for id, pack in ps.iteritems(): + lines.append('PACKAGE #%s: "%s" with %d links.' % (id, pack['name'], len(pack['links']) )) + + return lines + + def event_collector(self, args): + ps = self.sm.get_collector() + if not ps: + return ["INFO: No packages in collector!"] + + lines = [] + for id, pack in ps.iteritems(): + lines.append('PACKAGE #%s: "%s" with %d links.' % (id, pack['name'], len(pack['links']) )) + + return lines + + def event_info(self, args): + if not args: + return ['ERROR: Use info like this: info '] + + info = self.sm.get_file_data(int(args[0])) + + if not info: + return ["ERROR: Link doesn't exists."] + + id = info.keys()[0] + data = info[id] + + return ['LINK #%s: %s (%s) [%s][%s]' % (id, data['name'], data['format_size'], data['statusmsg'], data['plugin'])] + + def event_packinfo(self, args): + if not args: + return ['ERROR: Use packinfo like this: packinfo '] + + lines = [] + pack = self.sm.get_package_data(int(args[0])) + + if not pack: + return ["ERROR: Package doesn't exists."] + + id = args[0] + + self.more = [] + + lines.append('PACKAGE #%s: "%s" with %d links' % (id, pack['name'], len(pack["links"])) ) + for id, pyfile in pack["links"].iteritems(): + self.more.append('LINK #%s: %s (%s) [%s][%s]' % (id, pyfile["name"], pyfile["format_size"], pyfile["statusmsg"], pyfile["plugin"])) + + if len(self.more) < 6: + lines.extend(self.more) + self.more = [] + else: + lines.extend(self.more[:6]) + self.more = self.more[6:] + lines.append("%d more links do display." % len(self.more)) + + + return lines + + def event_more(self, args): + if not self.more: + return ["No more information to display."] + + lines = self.more[:6] + self.more = self.more[6:] + lines.append("%d more links do display." % len(self.more)) + + return lines + + def event_start(self, args): + + self.sm.unpause_server() + return ["INFO: Starting downloads."] + + def event_stop(self, args): + + self.sm.pause_server() + return ["INFO: No new downloads will be started."] + + + def event_add(self, args): + if len(args) < 2: + return ['ERROR: Add links like this: "add links". '\ + 'This will add the link to to the package / the package with id !'] + + + + pack = args[0].strip() + links = [x.strip() for x in args[1:]] + + count_added = 0 + count_failed = 0 + try: + id = int(pack) + pack = self.sm.get_package_data(id) + if not pack: + return ["ERROR: Package doesn't exists."] + + #add links + + + return ["INFO: Added %d links to Package %s [#%d]" % (len(links), pack["name"], id)] + + except: + # create new package + id = self.sm.add_package(pack, links, 1) + return ["INFO: Created new Package %s [#%d] with %d links." % (pack, id, len(links))] + + + def event_del(self, args): + if len(args) < 2: + return ["ERROR: Use del command like this: del -p|-l [...] (-p indicates that the ids are from packages, -l indicates that the ids are from links)"] + + if args[0] == "-p": + ret = self.sm.del_packages(map(int, args[1:])) + return ["INFO: Deleted %d packages!" % len(args[1:])] + + elif args[0] == "-l": + ret = self.sm.del_links(map(int, args[1:])) + return ["INFO: Deleted %d links!" % len(args[1:])] + + else: + return ["ERROR: Use del command like this: del <-p|-l> [...] (-p indicates that the ids are from packages, -l indicates that the ids are from links)"] + + def event_help(self, args): + lines = [] + lines.append("The following commands are available:") + lines.append("add [...] Adds link to package. (creates new package if it does not exist)") + lines.append("queue Shows all packages in the queue") + lines.append("collector Shows all packages in collector") + lines.append("del -p|-l [...] Deletes all packages|links with the ids specified") + lines.append("info Shows info of the link with id ") + lines.append("packinfo Shows info of the package with id ") + lines.append("more Shows more info when the result was truncated") + lines.append("start Starts all downloads") + lines.append("stop Stops the download (but not abort active downloads)") + lines.append("status Show general download status") + lines.append("help Shows this help message") + return lines + + +class IRCError(Exception): + def __init__(self, value): + self.value = value + def __str__(self): + return repr(self.value) \ No newline at end of file diff --git a/module/plugins/hooks/MultiHome.py b/module/plugins/hooks/MultiHome.py new file mode 100644 index 000000000..3f41938dc --- /dev/null +++ b/module/plugins/hooks/MultiHome.py @@ -0,0 +1,82 @@ +# -*- 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 +""" + +from module.plugins.Hook import Hook +from time import time + +class MultiHome(Hook): + __name__ = "MultiHome" + __version__ = "0.1" + __description__ = """ip address changer""" + __config__ = [ ("activated", "bool", "Activated" , "False"), + ("interfaces", "str", "Interfaces" , "None") ] + __author_name__ = ("mkaay") + __author_mail__ = ("mkaay@mkaay.de") + + def setup(self): + self.register = {} + self.interfaces = [] + self.parseInterfaces(self.getConfig("interfaces").split(";")) + if not self.interfaces: + self.parseInterfaces([self.config["general"]["download_interface"]]) + self.setConfig("interfaces", self.toConfig()) + + def toConfig(self): + return ";".join([i.adress for i in self.interfaces]) + + def parseInterfaces(self, interfaces): + for interface in interfaces: + if not interface or str(interface).lower() == "none": + continue + self.interfaces.append(Interface(interface)) + + def coreReady(self): + requestFactory = self.core.requestFactory + oldGetRequest = requestFactory.getRequest + def getRequest(pluginName, account=None, type="HTTP"): + iface = self.bestInterface(pluginName, account) + if iface: + iface.useFor(pluginName, account) + requestFactory.iface = iface.adress + self.log.debug("Multihome: using address: "+iface.adress) + return oldGetRequest(pluginName, account, type) + requestFactory.getRequest = getRequest + + def bestInterface(self, pluginName, account): + best = None + for interface in self.interfaces: + if not best or interface.lastPluginAccess(pluginName, account) < best.lastPluginAccess(pluginName, account): + best = interface + return best + +class Interface(object): + def __init__(self, adress): + self.adress = adress + self.history = {} + + def lastPluginAccess(self, pluginName, account): + if self.history.has_key((pluginName, account)): + return self.history[(pluginName, account)] + return 0 + + def useFor(self, pluginName, account): + self.history[(pluginName, account)] = time() + + def __repr__(self): + return "" % self.adress diff --git a/module/plugins/hooks/UnRar.py b/module/plugins/hooks/UnRar.py new file mode 100644 index 000000000..faa06d179 --- /dev/null +++ b/module/plugins/hooks/UnRar.py @@ -0,0 +1,137 @@ +# -*- 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 +""" +from __future__ import with_statement + +import sys + +from module.plugins.Hook import Hook +from module.pyunrar import Unrar, WrongPasswordError, CommandError, UnknownError + +from os.path import exists, join +from os import remove +import re + +class UnRar(Hook): + __name__ = "UnRar" + __version__ = "0.1" + __description__ = """unrar""" + __config__ = [ ("activated", "bool", "Activated", False), + ("fullpath", "bool", "extract full path", True), + ("overwrite", "bool", "overwrite files", True), + ("passwordfile", "str", "unrar passoword file", "unrar_passwords.txt"), + ("deletearchive", "bool", "delete archives when done", False) ] + __threaded__ = ["packageFinished"] + __author_name__ = ("mkaay") + __author_mail__ = ("mkaay@mkaay.de") + + def setup(self): + self.comments = ["# one password each line"] + self.passwords = [] + if exists(self.getConfig("passwordfile")): + with open(self.getConfig("passwordfile"), "r") as f: + for l in f.readlines(): + l = l.strip("\n\r") + if l and not l.startswith("#"): + self.passwords.append(l) + else: + with open(self.getConfig("passwordfile"), "w") as f: + f.writelines(self.comments) + self.re_splitfile = re.compile("(.*)\.part(\d+)\.rar$") + + def addPassword(self, pw): + if not pw in self.passwords: + self.passwords.insert(0, pw) + with open(self.getConfig("passwordfile"), "w") as f: + f.writelines(self.comments) + f.writelines(self.passwords) + + def removeFiles(self, pack, fname): + if not self.getConfig("deletearchive"): + return + m = self.re_splitfile.search(fname) + + download_folder = self.core.config['general']['download_folder'] + if self.core.config['general']['folder_per_package']: + folder = join(download_folder, pack.folder.decode(sys.getfilesystemencoding())) + else: + folder = download_folder + if m: + nre = re.compile("%s\.part\d+\.rar" % m.group(1)) + for fid, data in pack.getChildren().iteritems(): + if nre.match(data["name"]): + remove(join(folder, data["name"])) + elif not m and fname.endswith(".rar"): + nre = re.compile("^%s\.r..$" % fname.replace(".rar","")) + for fid, data in pack.getChildren().iteritems(): + if nre.match(data["name"]): + remove(join(folder, data["name"])) + + def packageFinished(self, pack): + if pack.password: + self.addPassword(pack.password) + files = [] + for fid, data in pack.getChildren().iteritems(): + m = self.re_splitfile.search(data["name"]) + if m and int(m.group(2)) == 1: + files.append((fid,m.group(0))) + elif not m and data["name"].endswith(".rar"): + files.append((fid,data["name"])) + + for fid, fname in files: + pyfile = self.core.files.getFile(fid) + pyfile.setStatus("custom") + def s(p): + pyfile.alternativePercent = p + + download_folder = self.core.config['general']['download_folder'] + if self.core.config['general']['folder_per_package']: + folder = join(download_folder, pack.folder.decode(sys.getfilesystemencoding())) + else: + folder = download_folder + + u = Unrar(join(folder, fname)) + try: + success = u.crackPassword(passwords=self.passwords, statusFunction=s, overwrite=True, destination=folder, fullPath=self.getConfig("fullpath")) + except WrongPasswordError: + self.core.log.info("Unrar of %s failed (wrong password)" % fname) + continue + except CommandError, e: + if re.search("Cannot find volume", e.stderr): + self.core.log.info("Unrar of %s failed (missing volume)" % fname) + continue + try: + if e.getExitCode() == 1 and len(u.listContent(u.getPassword())) == 1: + self.core.log.debug("Unrar of %s ok" % fname) + self.removeFiles(pack, fname) + except: + self.core.log.info("Unrar of %s failed" % fname) + continue + except UnknownError: + self.core.log.info("Unrar of %s failed" % fname) + continue + else: + if success: + self.core.log.debug("Unrar of %s ok" % fname) + self.removeFiles(pack, fname) + else: + self.core.log.info("Unrar of %s failed (wrong password)" % fname) + finally: + pyfile.alternativePercent = None + pyfile.setStatus("finished") + diff --git a/module/plugins/hooks/UpdateManager.py b/module/plugins/hooks/UpdateManager.py new file mode 100644 index 000000000..2981df9a0 --- /dev/null +++ b/module/plugins/hooks/UpdateManager.py @@ -0,0 +1,59 @@ +# -*- 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 + @interface-version: 0.1 +""" + +from module.network.Request import getURL +from module.plugins.Hook import Hook + +class UpdateManager(Hook): + __name__ = "UpdateManager" + __version__ = "0.1" + __description__ = """checks for updates""" + __config__ = [ ("activated", "bool", "Activated" , "True"), + ("interval", "int", "Check interval in minutes" , "180")] + __author_name__ = ("RaNaN") + __author_mail__ = ("ranan@pyload.org") + + def setup(self): + self.interval = self.getConfig("interval") * 60 + + def coreReady(self): + #@TODO check plugins, restart, and other stuff + pass + + def periodical(self): + self.checkForUpdate() + + + def checkForUpdate(self): + """ checks if an update is available""" + + try: + version_check = getURL("http://get.pyload.org/check/%s/" % self.core.server_methods.get_server_version() ) + if version_check == "": + self.log.info(_("No Updates for pyLoad")) + return False + else: + self.log.info(_("*** New pyLoad Version %s available ***") % version_check) + self.log.info(_("*** Get it here: http://get.pyload.org/get/ ***")) + return True + except: + self.log.error(_("Not able to connect server")) + + \ No newline at end of file diff --git a/module/plugins/hooks/XMPPInterface.py b/module/plugins/hooks/XMPPInterface.py new file mode 100644 index 000000000..67a7f1b77 --- /dev/null +++ b/module/plugins/hooks/XMPPInterface.py @@ -0,0 +1,229 @@ +# -*- 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: RaNaN + @interface-version: 0.2 +""" + +import socket +import sys +from threading import Thread +import time +from time import sleep +from traceback import print_exc + +from pyxmpp.all import JID,Iq,Presence,Message,StreamError +from pyxmpp.jabber.client import JabberClient +from pyxmpp.interface import implements +from pyxmpp.interfaces import * +from pyxmpp.streamtls import TLSSettings + +from module.plugins.Hook import Hook +from module.plugins.hooks.IRCInterface import IRCInterface + +class XMPPInterface(IRCInterface, JabberClient): + __name__ = "XMPPInterface" + __version__ = "0.1" + __description__ = """connect to jabber and let owner perform different tasks""" + __config__ = [("activated", "bool", "Activated", "False"), + ("jid", "str", "Jabber ID", "user@exmaple-jabber-server.org"), + ("pw", "str", "Password", ""), + ("owners", "str", "List of JIDs accepting commands from", "me@icq-gateway.org;some@msn-gateway.org"), + ("info_file", "bool", "Inform about every file finished", "False"), + ("info_pack", "bool", "Inform about every package finished", "True")] + __author_name__ = ("RaNaN") + __author_mail__ = ("RaNaN@pyload.org") + + implements(IMessageHandlersProvider) + + def __init__(self, core): + IRCInterface.__init__(self, core) + + self.jid = JID(self.getConfig("jid")) + password = self.getConfig("pw") + + # if bare JID is provided add a resource -- it is required + if not self.jid.resource: + self.jid=JID(self.jid.node, self.jid.domain, "pyLoad") + + tls_settings = None + + # setup client with provided connection information + # and identity data + JabberClient.__init__(self, self.jid, password, + disco_name="pyLoad XMPP Client", disco_type="bot", + tls_settings = tls_settings) + + self.interface_providers = [ + VersionHandler(self), + self, + ] + + def coreReady(self): + self.new_package = {} + + self.start() + + def packageFinished(self, pypack): + + try: + if self.getConfig("info_pack"): + self.announce(_("Package finished: %s") % pypack.name) + except: + pass + + def downloadFinished(self, pyfile): + try: + if self.getConfig("info_file"): + self.announce(_("Download finished: %s @ %s") % (pyfile.name, pyfile.pluginname) ) + except: + pass + + def run(self): + # connect to IRC etc. + self.connect() + try: + self.loop(1) + except Exception, ex: + self.core.log.error("pyLoad XMPP: %s" % str(ex)) + + def stream_state_changed(self,state,arg): + """This one is called when the state of stream connecting the component + to a server changes. This will usually be used to let the user + know what is going on.""" + self.log.debug("pyLoad XMPP: *** State changed: %s %r ***" % (state,arg) ) + + def get_message_handlers(self): + """Return list of (message_type, message_handler) tuples. + + The handlers returned will be called when matching message is received + in a client session.""" + return [ + ("normal", self.message), + ] + + def message(self,stanza): + """Message handler for the component.""" + subject=stanza.get_subject() + body=stanza.get_body() + t=stanza.get_type() + self.log.debug(_(u'pyLoad XMPP: Message from %s received.') % (unicode(stanza.get_from(),))) + self.log.debug(_(u'pyLoad XMPP: Body: %s') % body) + + if stanza.get_type()=="headline": + # 'headline' messages should never be replied to + return True + if subject: + subject=u"Re: "+subject + + to_jid = stanza.get_from() + from_jid = stanza.get_to() + + #j = JID() + to_name = to_jid.as_utf8() + from_name = from_jid.as_utf8() + + names = self.getConfig("owners").split(";") + + if to_name in names or to_jid.node+"@"+to_jid.domain in names: + + messages = [] + + trigger = "pass" + args = None + + temp = body.split() + trigger = temp[0] + if len(temp) > 1: + args = temp[1:] + + handler = getattr(self, "event_%s" % trigger, self.event_pass) + try: + res = handler(args) + for line in res: + m=Message( + to_jid=to_jid, + from_jid=from_jid, + stanza_type=stanza.get_type(), + subject=subject, + body=line) + + messages.append(m) + except Exception, e: + self.log.error("pyLoad XMPP: "+ repr(e)) + + return messages + + else: + return True + + + def announce(self, message): + """ send message to all owners""" + for user in self.getConfig("owners").split(";"): + + self.log.debug(_("pyLoad XMPP: Send message to %s") % user) + + to_jid = JID(user) + + m = Message(from_jid=self.jid, + to_jid=to_jid, + stanza_type="chat", + body=message) + + self.stream.send(m) + + +class VersionHandler(object): + """Provides handler for a version query. + + This class will answer version query and announce 'jabber:iq:version' namespace + in the client's disco#info results.""" + + implements(IIqHandlersProvider, IFeaturesProvider) + + def __init__(self, client): + """Just remember who created this.""" + self.client = client + + def get_features(self): + """Return namespace which should the client include in its reply to a + disco#info query.""" + return ["jabber:iq:version"] + + def get_iq_get_handlers(self): + """Return list of tuples (element_name, namespace, handler) describing + handlers of stanzas""" + return [ + ("query", "jabber:iq:version", self.get_version), + ] + + def get_iq_set_handlers(self): + """Return empty list, as this class provides no stanza handler.""" + return [] + + def get_version(self,iq): + """Handler for jabber:iq:version queries. + + jabber:iq:version queries are not supported directly by PyXMPP, so the + XML node is accessed directly through the libxml2 API. This should be + used very carefully!""" + iq=iq.make_result_response() + q=iq.new_query("jabber:iq:version") + q.newTextChild(q.ns(),"name","Echo component") + q.newTextChild(q.ns(),"version","1.0") + return iq + \ No newline at end of file diff --git a/module/plugins/hooks/__init__.py b/module/plugins/hooks/__init__.py new file mode 100644 index 000000000..e69de29bb -- cgit v1.2.3