pvl/irker/irk.py
author Tero Marttila <terom@paivola.fi>
Tue, 19 Feb 2013 19:27:51 +0200
changeset 220 e533260bcefd
parent 127 f143171884f9
permissions -rw-r--r--
pvl-verkko: 0.4.1
"""
    Irker protocol implementation.
"""

from twisted.internet import protocol, defer
from twisted.protocols import basic
from twisted.python import log

import json, urlparse

class Irk (basic.LineOnlyReceiver) :
    """
        A connected Irk client.
    """

    delimiter = '\n'
    
    def connectionMade (self) :
        self.transport.logPrefix = self.logPrefix
        
        log.msg("connected", self)
    
    def connectionLost (self, reason) :
        log.err("connection lost", reason)
    
    def error (self, *args) :
        log.err(*args)
        self.transport.loseConnection()

    def lineReceived (self, line) :
        """
            JSON -> in
        """

        try :
            irk = json.loads(line)

        except ValueError as ex :
            # invalid
            return self.error(ex, line)
        
        # dispatch
        self.factory.irkReceived(irk).addErrback(self.error, line)

    def __str__ (self) :
        if not self.transport : return 'Irk'

        host = self.transport.getHost()
        peer = self.transport.getPeer()

        return "{host.host}:{host.port}:{peer.host}".format(host=host, peer=peer)

    logPrefix = __str__

class IrkFactory (protocol.ServerFactory) :
    """
        Manage connected Irk clients.
    """

    MAXATTR = 16

    protocol = Irk

    def __init__ (self, irc) :
        self.irc = irc
    
    @defer.inlineCallbacks
    def irkReceived (self, irk) :
        """
            Deffered to handle lookup of target, and then sending message.

            Errbacks on failures.
        """

        log.msg(str(irk))

        if not 'to' in irk :
            raise ValueError("missing target: to")
        
        # MUST NOT be unicode
        # XXX: ValueError?
        url = urlparse.urlparse(str(irk.pop('to')))
        
        # connect, register, join
        target = yield self.irc.target(url)
        
        # dispatch attrs
        for attr, value in irk.iteritems() :
            if len(attr) >= self.MAXATTR or not attr.islower() :
                raise ValueError("invalid attr: %s" % (attr, ))
    
            method = getattr(self, 'irk_' + attr, None)

            if not method :
                raise ValueError("unknown attr: %s" % (attr, ))

            if value :
                value = unicode(value)
            else :
                value = None

            method(target, value)
    
    # XXX: explicitly enable?
    def irk_privmsg (self, target, value) :
        """
            Send PRIVMSG to target.
        """
        
        if not value :
            # legacy
            return

        target.privmsg(value)

    def irk_notice (self, target, value) :
        """
            Send NOTICE to target.
        """

        if not value :
            raise ValueError("empty notice")

        target.notice(value)

    # TODO: refcounting vs join!
    def irk_part (self, target, value) :
        """
            PART target.
        """
        
        # value is optional
        target.part(value)