diff options
Diffstat (limited to 'pyload/plugin/addon/XMPPInterface.py')
| -rw-r--r-- | pyload/plugin/addon/XMPPInterface.py | 252 | 
1 files changed, 252 insertions, 0 deletions
| diff --git a/pyload/plugin/addon/XMPPInterface.py b/pyload/plugin/addon/XMPPInterface.py new file mode 100644 index 000000000..2733cfde0 --- /dev/null +++ b/pyload/plugin/addon/XMPPInterface.py @@ -0,0 +1,252 @@ +# -*- coding: utf-8 -*- + +from pyxmpp import streamtls +from pyxmpp.all import JID, Message +from pyxmpp.interface import implements +from pyxmpp.interfaces import * +from pyxmpp.jabber.client import JabberClient + +from pyload.plugin.addon.IRCInterface import IRCInterface + + +class XMPPInterface(IRCInterface, JabberClient): +    __name    = "XMPPInterface" +    __type    = "addon" +    __version = "0.11" + +    __config = [("jid"      , "str" , "Jabber ID"                           , "user@exmaple-jabber-server.org"         ), +                ("pw"       , "str" , "Password"                            , ""                                       ), +                ("tls"      , "bool", "Use TLS"                             , False                                    ), +                ("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                                     ), +                ("captcha"  , "bool", "Send captcha requests"               , True                                     )] + +    __description = """Connect to jabber and let owner perform different tasks""" +    __license     = "GPLv3" +    __authors     = [("RaNaN", "RaNaN@pyload.org")] + + +    implements(IMessageHandlersProvider) + + +    def __init__(self, core, manager): +        IRCInterface.__init__(self, core, manager) + +        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") + +        if self.getConfig('tls'): +            tls_settings = streamtls.TLSSettings(require=True, verify_peer=False) +            auth = ("sasl:PLAIN", "sasl:DIGEST-MD5") +        else: +            tls_settings = None +            auth = ("sasl:DIGEST-MD5", "digest") + +        # 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, auth_methods=auth) + +        self.interface_providers = [ +            VersionHandler(self), +            self, +        ] + + +    def activate(self): +        self.new_package = {} + +        self.start() + + +    def packageFinished(self, pypack): +        try: +            if self.getConfig('info_pack'): +                self.announce(_("Package finished: %s") % pypack.name) +        except Exception: +            pass + + +    def downloadFinished(self, pyfile): +        try: +            if self.getConfig('info_file'): +                self.announce( +                    _("Download finished: %(name)s @ %(plugin)s") % {"name": pyfile.name, "plugin": pyfile.pluginname}) +        except Exception: +            pass + + +    def run(self): +        # connect to IRC etc. +        self.connect() +        try: +            self.loop() +        except Exception, ex: +            self.logError(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.logDebug("*** State changed: %s %r ***" % (state, arg)) + + +    def disconnected(self): +        self.logDebug("Client was disconnected") + + +    def stream_closed(self, stream): +        self.logDebug("Stream was closed", stream) + + +    def stream_error(self, err): +        self.logDebug("Stream Error", err) + + +    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.logDebug("Message from %s received." % unicode(stanza.get_from())) +        self.logDebug("Body: %s Subject: %s Type: %s" % (body, subject, t)) + +        if t == "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 + +            try: +                temp = body.split() +                trigger = temp[0] +                if len(temp) > 1: +                    args = temp[1:] +            except Exception: +                pass + +            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.logError(e) + +            return messages + +        else: +            return True + + +    def response(self, msg, origin=""): +        return self.announce(msg) + + +    def announce(self, message): +        """ send message to all owners""" +        for user in self.getConfig('owners').split(";"): +            self.logDebug("Send message to", user) + +            to_jid = JID(user) + +            m = Message(from_jid=self.jid, +                        to_jid=to_jid, +                        stanza_type="chat", +                        body=message) + +            stream = self.get_stream() +            if not stream: +                self.connect() +                stream = self.get_stream() + +            stream.send(m) + + +    def beforeReconnecting(self, ip): +        self.disconnect() + + +    def afterReconnecting(self, ip): +        self.connect() + + +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 | 
