class Command(object):
    def hook(self, base):
        self.ircd = base
        return self
    def onUse(self, user, data):
        pass
    def processParams(self, user, params):
        return {
            "user": user,
            "params": params
        }
    def updateActivity(self, user):
        user.lastactivity = now() class RegisterUser(Command):
    arguments = [
        ("uuid", String()),
        ("nick", String()),
        ("ident", String()),
        ("host", String()),
        ("realhost", String()),
        ("gecos", String()),
        ("ip", String()),
        ("password", String()),
        ("server", String()),
        ("secure", Boolean()),
        ("signon", Integer()),
        ("nickts", Integer())
    ]
    errors = {
        HandshakeNotYetComplete: "HANDSHAKE_NOT_COMPLETE",
        NoSuchServer: "NO_SUCH_SERVER",
        UserAlreadyConnected: "UUID_ALREADY_CONNECTED"
    }
    requiresAnswer = False class RemoveUser(Command):
    arguments = [
        ("user", String()),
        ("reason", String())
    ]
    errors = {
        HandshakeNotYetComplete: "HANDSHAKE_NOT_COMPLETE",
        NoSuchUser: "NO_SUCH_USER"
    }
    requiresAnswer = False class ModuleMessage(Command):
    arguments = [
        ("destserver", String()),
        ("type", String()),
        ("args", ListOf(String()))
    ]
    errors = {
        HandshakeNotYetComplete: "HANDSHAKE_NOT_COMPLETE"
    } class SetIdent(Command):
    arguments = [
        ("user", String()),
        ("ident", String())
    ]
    errors = {
        HandshakeNotYetComplete: "HANDSHAKE_NOT_COMPLETE",
        NoSuchUser: "NO_SUCH_USER"
    }
    requiresAnswer = False class SetHost(Command):
    arguments = [
        ("user", String()),
        ("host", String())
    ]
    errors = {
        HandshakeNotYetComplete: "HANDSHAKE_NOT_COMPLETE",
        NoSuchUser: "NO_SUCH_USER"
    }
    requiresAnswer = False class SetName(Command):
    arguments = [
        ("user", String()),
        ("gecos", String())
    ]
    errors = {
        HandshakeNotYetComplete: "HANDSHAKE_NOT_COMPLETE",
        NoSuchUser: "NO_SUCH_USER"
    }
    requiresAnswer = False def chunk_message(msg, chunk_size):
    chunks = []
    msg += "\n"
    while msg:
        index = msg.find("\n",0,chunk_size+1)
        if index < 0:
            index = msg.rfind(" ",0,chunk_size+1)
        if index < 0:
            index = chunk_size
        chunks.append(msg[:index])
        msg = msg[index+1:] if msg[index] in " \n" else msg[index:]
    return chunks def crypt(word, salt=None, iterations=1000, algorithm="sha256", bytes=24):
    """PBKDF2-based unix crypt(3) replacement.

    The number of iterations specified in the salt overrides the 'iterations'
    parameter.
    """
    
    # Reserve algorithms
    algos = {
        "md5": md5,
        "sha1": sha1,
        "sha224": sha224,
        "sha256": sha256,
        "sha384": sha384,
        "sha512": sha512
    }
    
    # Generate a (pseudo-)random salt if the user hasn't provided one.
    if salt is None:
        salt = _makesalt()

    # salt must be a string or the us-ascii subset of unicode
    if isunicode(salt):
        salt = salt.encode('us-ascii').decode('us-ascii')
    elif isbytes(salt):
        salt = salt.decode('us-ascii')
    else:
        raise TypeError("salt must be a string")

    # word must be a string or unicode (in the latter case, we convert to UTF-8)
    if isunicode(word):
        word = word.encode("UTF-8")
    elif not isbytes(word):
        raise TypeError("word must be a string or unicode")

    # Try to extract the real salt and iteration count from the salt
    if ":" in salt:
        (algorithm, iterations, salt, oldhash) = salt.split(":")
        if iterations != "":
            iterations = int(iterations)
            if iterations < 1:
                raise ValueError("Invalid salt")
        bytes = len(b64decode(oldhash))

    # Make sure the salt matches the allowed character set
    allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/="
    for ch in salt:
        if ch not in allowed:
            raise ValueError("Illegal character {!r} in salt".format(ch))
    
    if algorithm not in algos:
        raise ValueError("Invalid algorithm: {}".format(algorithm))

    hash = b64encode(PBKDF2(word, salt, iterations, algos[algorithm]).read(bytes))
    return "{}:{!s}:{}:{}".format(algorithm, iterations, salt, hash) def irc_lower(str):
    return str.lower().replace("[","{").replace("]","}").replace("/","|").replace("~","^") def now():
    return datetime.datetime.utcnow().replace(microsecond=0) class CaseInsensitiveDictionary(MutableMapping):
    def __init__(self):
        self._data = {}

    def __repr__(self):
        return repr(self._data)

    def __delitem__(self, key):
        try:
            del self._data[irc_lower(key)]
        except KeyError:
            raise KeyError(key)

    def __getitem__(self, key):
        try:
            return self._data[irc_lower(key)]
        except KeyError:
            raise KeyError(key)

    def __iter__(self):
        return iter(self._data)

    def __len__(self):
        return len(self._data)

    def __setitem__(self, key, value):
        self._data[irc_lower(key)] = value
from twisted.enterprise import adbapi
from twisted.internet import reactor
from twisted.internet.defer import Deferred
from twisted.words.protocols import irc
from txircd.modbase import Command
from txircd.server import RegisterUser, RemoveUser, ModuleMessage, SetIdent, SetHost, SetName
from txircd.utils import chunk_message, crypt, irc_lower, now, CaseInsensitiveDictionary
from base64 import b64decode, b64encode
from Crypto.Random.random import getrandbits
from Crypto.Cipher import AES
from Crypto.Cipher import Blowfish
from datetime import datetime
from random import choice
import math, os, random, uuid, yaml

class Service(object):
    class ServiceSocket(object):
        class ServiceTransport(object):
            def loseConnection(self):
                pass
        
        def __init__(self):
            self.transport = self.ServiceTransport()
            self.secure = True
    
    def __init__(self, ircd, nick, ident, host, gecos, helpTexts, module):
        # We're going to try to keep Service fairly consistent with IRCUser, even if most of these variables will never be used
        # in order to prevent exceptions all over the place
        self.ircd = ircd
        self.socket = self.ServiceSocket()
        self.uuid = str(uuid.uuid1())
        self.password = None
        self.nickname = nick
        self.username = ident
        self.hostname = host
        self.realhost = host
        self.realname = gecos
        self.ip = "127.0.0.1"
        self.server = self.ircd.name
        self.signon = datetime.utcfromtimestamp(1) # Give these pseudoclients a really old time so that they won't be disconnected by remote servers
        self.lastactivity = now()

        self.lastpong = now()