diff options
Diffstat (limited to 'module/plugins/hooks')
-rw-r--r-- | module/plugins/hooks/IRCInterface.py | 244 | ||||
-rw-r--r-- | module/plugins/hooks/UnRar.py | 4 | ||||
-rw-r--r-- | module/plugins/hooks/XMPPInterface.py | 229 |
3 files changed, 360 insertions, 117 deletions
diff --git a/module/plugins/hooks/IRCInterface.py b/module/plugins/hooks/IRCInterface.py index a9d9639d4..8d50647e3 100644 --- a/module/plugins/hooks/IRCInterface.py +++ b/module/plugins/hooks/IRCInterface.py @@ -15,6 +15,7 @@ along with this program; if not, see <http://www.gnu.org/licenses/>. @author: RaNaN + @author: jeix @interface-version: 0.2 """ @@ -33,12 +34,12 @@ class IRCInterface(Thread, Hook): __version__ = "0.1" __description__ = """connect to irc and let owner perform different tasks""" __config__ = [("activated", "bool", "Activated", "False"), - ("host", "str", "IRC-Server Address", ""), + ("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"), + ("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") @@ -48,27 +49,19 @@ class IRCInterface(Thread, Hook): 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.host = self.getConfig("host") + ":" + str(self.getConfig("port")) - self.owner = self.getConfig("owner") - self.nick = self.getConfig("nick") - - #self.targets = irc_targets # where replies will go to - #if len(self.targets) < 1: - - self.targets = [self.getConfig("owner")] - self.links_added = 0 self.start() - def packageFinished(self, pypack): + def packageFinished(self, pypack): try: if self.getConfig("info_pack"): self.response(_("Package finished: %s") % pypack.name) @@ -85,15 +78,16 @@ class IRCInterface(Thread, Hook): def run(self): # connect to IRC etc. self.sock = socket.socket() - temp = self.host.split(":", 1) - self.sock.connect((temp[0], int(temp[1]))) - self.sock.send("NICK %s\r\n" % self.nick) - self.sock.send("USER %s %s bla :%s\r\n" % (self.nick, self.host, self.nick)) - for t in self.targets: - if t.startswith("#"): - self.sock.send("JOIN %s\r\n" % t) - self.log.info("pyLoadIRC: Connected to %s!" % self.host) - self.log.info("pyLoadIRC: Switching to listening mode!") + 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() @@ -101,7 +95,6 @@ class IRCInterface(Thread, Hook): self.sock.send("QUIT :byebye\r\n") print_exc() self.sock.close() - sys.exit(1) def main_loop(self): @@ -144,14 +137,27 @@ class IRCInterface(Thread, Hook): def handle_events(self, msg): - if msg["origin"].split("!", 1)[0] != self.owner: + if not msg["origin"].split("!", 1)[0] in self.getConfig("owner").split(): return - if msg["target"].split("!", 1)[0] != self.nick: + 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 @@ -163,30 +169,34 @@ class IRCInterface(Thread, Hook): handler = getattr(self, "event_%s" % trigger, self.event_pass) try: - handler(args) + res = handler(args) + for line in res: + self.response(line, msg["origin"]) except Exception, e: - self.log.error("pyLoadIRC: "+ repr(e)) + self.log.error("pyLoad IRC: "+ repr(e)) - def response(self, msg): - #print _(msg) - for t in self.targets: - self.sock.send("PRIVMSG %s :%s\r\n" % (t, msg)) + 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): - pass + return [] def event_status(self, args): - downloads = self.core.status_downloads() + downloads = self.sm.status_downloads() if len(downloads) < 1: - self.response("INFO: There are no active downloads currently.") - return + return ["INFO: There are no active downloads currently."] - self.response("ID - Name - Status - Speed - ETA - Progress") + lines = [] + lines.append("ID - Name - Status - Speed - ETA - Progress") for data in downloads: - self.response("#%d - %s - %s - %s - %s - %s" % + lines.append("#%d - %s - %s - %s - %s - %s" % ( data['id'], data['name'], @@ -196,103 +206,114 @@ class IRCInterface(Thread, Hook): "%d/%d MB (%d%%)" % ((data['size']-data['kbleft']) / 1024, data['size'] / 1024, data['percent']) ) ) + return lines def event_queue(self, args): # just forward for now - self.event_status(args) + return self.event_status(args) def event_collector(self, args): - ps = self.core.get_collector() + ps = self.sm.get_collector() if len(ps) == 0: - self.response("INFO: No packages in collector!") - return + return ["INFO: No packages in collector!"] + lines = [] for packdata in ps: - self.response('PACKAGE: Package "%s" with id #%d' % (packdata['package_name'], packdata['id'])) - for fileid in self.core.get_package_files(packdata['id']): - fileinfo = self.core.get_file_info(fileid) - self.response('#%d FILE: %s (#%d)' % (packdata['id'], fileinfo["filename"], fileinfo["id"])) + lines.append('PACKAGE: Package "%s" with id #%d' % (packdata['package_name'], packdata['id'])) + for fileid in self.sm.get_package_files(packdata['id']): + fileinfo = self.sm.get_file_info(fileid) + lines.append('#%d FILE: %s (#%d)' % (packdata['id'], fileinfo["filename"], fileinfo["id"])) + + return lines def event_links(self, args): - fids = self.core.get_files() + fids = self.sm.get_files() if len(fids) == 0: - self.response("INFO: No links.") - return + return ["INFO: No links."] + lines = [] for fid in fids: - info = self.core.get_file_info(fid) - self.response('LINK #%d: %s [%s]' % (fid, info["filename"], info["status_type"])) + info = self.sm.get_file_info(fid) + lines.append('LINK #%d: %s [%s]' % (fid, info["filename"], info["status_type"])) + + return lines def event_packages(self, args): - pids = self.core.get_packages() + pids = self.sm.get_packages() if len(pids) == 0: - self.response("INFO: No packages.") - return + return ["INFO: No packages."] + lines = [] for pid in pids: - data = self.core.get_package_data(pid) - self.response('PACKAGE #%d: %s (%d links)' % (pid, data["package_name"], len(self.core.get_package_files(pid)))) + data = self.sm.get_package_data(pid) + lines.append('PACKAGE #%d: %s (%d links)' % (pid, data["package_name"], len(self.sm.get_package_files(pid)))) + + return lines def event_info(self, args): if not args: - self.response('ERROR: Use info like this: info <id>') - return + return ['ERROR: Use info like this: info <id>'] - info = self.core.get_file_info(int(args[0])) - self.response('LINK #%d: %s (%d) [%s bytes]' % (info['id'], info['filename'], info['size'], info['status_type'])) + info = self.sm.get_file_info(int(args[0])) + return ['LINK #%d: %s (%d) [%s bytes]' % (info['id'], info['filename'], info['size'], info['status_type'])] def event_packinfo(self, args): if not args: - self.response('ERROR: Use packinfo like this: packinfo <id>') - return + return ['ERROR: Use packinfo like this: packinfo <id>'] - packdata = self.core.get_package_data(int(args[0])) - self.response('PACKAGE: Package "%s" with id #%d' % (packdata['package_name'], packdata['id'])) - for fileid in self.core.get_package_files(packdata['id']): - fileinfo = self.core.get_file_info(fileid) - self.response('#%d LINK: %s (#%d)' % (packdata['id'], fileinfo["filename"], fileinfo["id"])) + lines = [] + packdata = self.sm.get_package_data(int(args[0])) + lines.append('PACKAGE: Package "%s" with id #%d' % (packdata['package_name'], packdata['id'])) + for fileid in self.sm.get_package_files(packdata['id']): + fileinfo = self.sm.get_file_info(fileid) + lines.append('#%d LINK: %s (#%d)' % (packdata['id'], fileinfo["filename"], fileinfo["id"])) + + return lines def event_start(self, args): if not args: count = 0 - for packdata in self.core.get_collector_packages(): - self.core.push_package_2_queue(packdata['id']) + for packdata in self.sm.get_collector_packages(): + self.sm.push_package_2_queue(packdata['id']) count += 1 - - self.response("INFO: %d downloads started." % count) - return + + return ["INFO: %d downloads started." % count] + lines = [] for val in args: id = int(val.strip()) - self.core.push_package_2_queue(id) - self.response("INFO: Starting download #%d" % id) + self.sm.push_package_2_queue(id) + lines.append("INFO: Starting download #%d" % id) + + return lines def event_stop(self, args): if not args: - self.core.stop_downloads() - self.response("INFO: All downloads stopped.") - return + self.sm.stop_downloads() + return ["INFO: All downloads stopped."] + lines = [] for val in args: id = int(val.strip()) - self.core.stop_download("", id) - self.response("INFO: Download #%d stopped." % id) + self.sm.stop_download("", id) + lines.append("INFO: Download #%d stopped." % id) + + return lines def event_add(self, args): if len(args) != 2: - self.response('ERROR: Add links like this: "add <package|id> <link>". '\ - 'This will add the link <link> to to the package <package> / the package with id <id>!') - return + return ['ERROR: Add links like this: "add <package|id> <link>". '\ + 'This will add the link <link> to to the package <package> / the package with id <id>!'] def get_pack_id(pack): if pack.isdigit(): pack = int(pack) - for packdata in self.core.get_collector_packages(): + for packdata in self.sm.get_collector_packages(): if packdata['id'] == pack: return pack return -1 - for packdata in self.core.get_collector_packages(): + for packdata in self.sm.get_collector_packages(): if packdata['package_name'] == pack: return packdata['id'] return -1 @@ -304,56 +325,49 @@ class IRCInterface(Thread, Hook): count_failed = 0 # verify that we have a valid link - if not self.core.is_valid_link(link): - self.response("ERROR: Your specified link is not supported by pyLoad.") - return + #if not self.sm.is_valid_link(link): + #return ["ERROR: Your specified link is not supported by pyLoad."] # get a valid package id (create new package if it doesn't exist) pack_id = get_pack_id(pack) if pack_id == -1: - pack_id = self.core.new_package(pack) + pack_id = self.sm.new_package(pack) # move link into package - fid = self.core.add_links_to_package(pack_id, [link]) - self.response("INFO: Added %s to Package %s [#%d]" % (link, pack, pack_id)) + fid = self.sm.add_links_to_package(pack_id, [link]) + return ["INFO: Added %s to Package %s [#%d]" % (link, pack, pack_id)] def event_del(self, args): if len(args) < 2: - self.response("ERROR: Use del command like this: del -p|-l <id> [...] (-p indicates that the ids are from packages, -l indicates that the ids are from links)") - return + return ["ERROR: Use del command like this: del -p|-l <id> [...] (-p indicates that the ids are from packages, -l indicates that the ids are from links)"] if args[0] == "-p": - ret = self.core.del_packages(map(int, args[1:])) - self.response("INFO: Deleted %d packages!" % ret) + ret = self.sm.del_packages(map(int, args[1:])) + return ["INFO: Deleted %d packages!" % ret] elif args[0] == "-l": - ret = self.core.del_links(map(int, args[1:])) - self.response("INFO: Deleted %d links!" % ret) + ret = self.sm.del_links(map(int, args[1:])) + return ["INFO: Deleted %d links!" % ret] else: - self.response("ERROR: Use del command like this: del <-p|-l> <id> [...] (-p indicates that the ids are from packages, -l indicates that the ids are from links)") - return + return ["ERROR: Use del command like this: del <-p|-l> <id> [...] (-p indicates that the ids are from packages, -l indicates that the ids are from links)"] def event_help(self, args): - self.response("The following commands are available:") - self.response("add <package|packid> <link> Adds link to package. (creates new package if it does not exist)") - time.sleep(1) - self.response("collector Shows all packages in collector") - self.response("del -p|-l <id> [...] Deletes all packages|links with the ids specified") - time.sleep(1) - self.response("info <id> Shows info of the link with id <id>") - self.response("help Shows this help file") - time.sleep(1) - self.response("links Shows all links in pyload") - self.response("packages Shows all packages in pyload") - time.sleep(1) - self.response("packinfo <id> Shows info of the package with id <id>") - self.response("queue Shows info about the queue") - time.sleep(1) - self.response("start [<id>...] Starts the package with id <id> or all packages if no id is given") - self.response("status Show general download status") - time.sleep(1) - self.response("stop [<id>...] Stops the package with id <id> or all packages if no id is given") + lines = [] + lines.append("The following commands are available:") + lines.append("add <package|packid> <link> Adds link to package. (creates new package if it does not exist)") + lines.append("collector Shows all packages in collector") + lines.append("del -p|-l <id> [...] Deletes all packages|links with the ids specified") + lines.append("info <id> Shows info of the link with id <id>") + lines.append("help Shows this help file") + lines.append("links Shows all links in pyload") + lines.append("packages Shows all packages in pyload") + lines.append("packinfo <id> Shows info of the package with id <id>") + lines.append("queue Shows info about the queue") + lines.append("start [<id>...] Starts the package with id <id> or all packages if no id is given") + lines.append("status Show general download status") + lines.append("stop [<id>...] Stops the package with id <id> or all packages if no id is given") + return lines class IRCError(Exception): diff --git a/module/plugins/hooks/UnRar.py b/module/plugins/hooks/UnRar.py index 7cc73f265..38a5a55e8 100644 --- a/module/plugins/hooks/UnRar.py +++ b/module/plugins/hooks/UnRar.py @@ -30,7 +30,7 @@ class UnRar(Hook): __name__ = "UnRar" __version__ = "0.1" __description__ = """unrar""" - __config__ = [ ("activated", "bool", "Activated", True), + __config__ = [ ("activated", "bool", "Activated", False), ("fullpath", "bool", "extract full path", True), ("overwrite", "bool", "overwrite files", True), ("passwordfile", "str", "unrar passoword file", "unrar_passwords.txt"), @@ -109,7 +109,7 @@ class UnRar(Hook): u.crackPassword(passwords=self.passwords, statusFunction=s, overwrite=True, destination=folder) except WrongPasswordError: continue - except CommandError as e: + except CommandError , e: if re.search("Cannot find volume", e.stderr): continue try: 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 <http://www.gnu.org/licenses/>. + + @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 <iq type='get'/> stanzas""" + return [ + ("query", "jabber:iq:version", self.get_version), + ] + + def get_iq_set_handlers(self): + """Return empty list, as this class provides no <iq type='set'/> 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 |