示例#1
0
    def handleCmd_EOS(self, prefix, args):
        if prefix != self.server_name:
            return
        if self.ism.syncd:
            return

        LOG.info("Finished receiving IRC sync data.")

        self.showirc = True

        # Check for conflicting bridges.
        if self.ism.findConflictingBridge():
            LOG.error("My nick prefix is in use! Terminating.")
            self.transport.loseConnection()
            reactor.stop()
            return

        # Set up nick reservation
        scfg = getServiceConfig()
        self.sendLine("TKL + Q * %s* %s 0 %d :Reserved for Dtella" %
                      (cfg.dc_to_irc_prefix, scfg.my_host, time.time()))
        self.ism.killConflictingUsers()

        # Send my own bridge nick
        self.pushBotJoin(do_nick=True)

        # When we enter the syncd state, register this instance with Dtella.
        # This will eventually trigger event_DtellaUp, where we send our state.
        self.schedulePing()
        self.ism.addMeToMain()
示例#2
0
    def connectionMade(self):
        scfg = getServiceConfig()
        LOG.info("Connected to IRC server.")

        self.challenge = make_challenge()
        my_capabs = [
            ("NICKMAX", 32),
            ("HALFOP", 1),
            ("CHANMAX", 65),
            ("MAXMODES", 20),
            ("IDENTMAX", 12),
            ("MAXQUIT", 255),
            ("MAXTOPIC", 307),
            ("MAXKICK", 255),
            ("MAXGECOS", 128),
            ("MAXAWAY", 200),
            ("IP6SUPPORT", 1),
            ("PROTOCOL", 1201),
            ("CHALLENGE", self.challenge),
        ]
        my_capabs_str = ' '.join("%s=%s" % x for x in my_capabs)

        self.sendLine("CAPAB START")
        self.sendLine("CAPAB CAPABILITIES :%s" % my_capabs_str)
        self.sendLine("CAPAB END")
示例#3
0
    def handleCmd_SERVER(self, prefix, args):
        if prefix:
            # Not from our connected server
            return

        if self.server_name:
            # Could be a dupe?  Ignore it.
            return

        # We got a reply from the our connected IRC server, so our password
        # was just accepted.  Send the Dtella state information into IRC.

        # Save server name
        CHECK(args[0])
        self.server_name = args[0]

        LOG.info("IRC Server Name: %s" % self.server_name)

        # Tell the ReconnectingClientFactory that we're cool
        self.factory.resetDelay()

        # This isn't very correct, because the Dtella nicks
        # haven't been sent yet, but it's the best we can practically do.
        scfg = getServiceConfig()
        cloak_checksum = scfg.hostmasker.getChecksum()
        self.sendLine("NETINFO 0 %d 0 %s 0 0 0 :%s" %
                      (time.time(), cloak_checksum, scfg.network_name))
        self.sendLine(":%s EOS" % scfg.my_host)
示例#4
0
    def handleCmd_EOS(self, prefix, args):
        if prefix != self.server_name:
            return
        if self.ism.syncd:
            return

        LOG.info("Finished receiving IRC sync data.")

        self.showirc = True

        # Check for conflicting bridges.
        if self.ism.findConflictingBridge():
            LOG.error("My nick prefix is in use! Terminating.")
            self.transport.loseConnection()
            reactor.stop()
            return

        # Set up nick reservation
        scfg = getServiceConfig()
        self.sendLine(
            "TKL + Q * %s* %s 0 %d :Reserved for Dtella" %
            (cfg.dc_to_irc_prefix, scfg.my_host, time.time()))
        self.ism.killConflictingUsers()

        # Send my own bridge nick
        self.pushBotJoin(do_nick=True)

        # When we enter the syncd state, register this instance with Dtella.
        # This will eventually trigger event_DtellaUp, where we send our state.
        self.schedulePing()
        self.ism.addMeToMain()
示例#5
0
 def handleCmd_PING(self, prefix, args):
     LOG.info("PING? PONG!")
     scfg = getServiceConfig()
     if len(args) == 1:
         self.sendLine("PONG %s :%s" % (scfg.my_host, args[0]))
     elif len(args) == 2:
         self.sendLine("PONG %s :%s" % (args[1], args[0]))
示例#6
0
    def handleCmd_SERVER(self, prefix, args):
        if prefix:
            # Not from our connected server
            return

        if self.server_name:
            # Could be a dupe?  Ignore it.
            return

        # We got a reply from the our connected IRC server, so our password
        # was just accepted.  Send the Dtella state information into IRC.

        # Save server name
        CHECK(args[0])
        self.server_name = args[0]

        LOG.info("IRC Server Name: %s" % self.server_name)

        # Tell the ReconnectingClientFactory that we're cool
        self.factory.resetDelay()

        # This isn't very correct, because the Dtella nicks
        # haven't been sent yet, but it's the best we can practically do.
        scfg = getServiceConfig()
        cloak_checksum = scfg.hostmasker.getChecksum()
        self.sendLine("NETINFO 0 %d 0 %s 0 0 0 :%s" %
                      (time.time(), cloak_checksum, scfg.network_name))
        self.sendLine(":%s EOS" % scfg.my_host)
示例#7
0
    def connectionLost(self, result):
        LOG.info("Lost IRC connection.")
        if self.ism.syncd:
            self.ism.removeMeFromMain()

        if self.shutdown_deferred:
            self.shutdown_deferred.callback("Bye!")
示例#8
0
 def handleCmd_PING(self, prefix, args):
     LOG.info("PING? PONG!")
     scfg = getServiceConfig()
     if len(args) == 1:
         self.sendLine("PONG %s :%s" % (scfg.my_host, args[0]))
     elif len(args) == 2:
         self.sendLine("PONG %s :%s" % (args[1], args[0]))
示例#9
0
    def handleCmd_ENDBURST(self, prefix, args):
        if self.ism.syncd:
            # FIXME
            LOG.warning("Ignoring ENDBURST")
            return

        CHECK(self.server_name)
        LOG.info("Finished receiving IRC sync data.")

        self.showirc = True

        # Check for conflicting bridges.
        if self.ism.findConflictingBridge():
            LOG.error("My nick prefix is in use! Terminating.")
            self.transport.loseConnection()
            reactor.stop()
            return

        # Set up nick reservation
        scfg = getServiceConfig()

        self.ism.killConflictingUsers()
        self.sendLine(":%s ADDLINE Q %s* %s %d 0 :%s" %
                      (self.sid, cfg.dc_to_irc_prefix, scfg.my_host,
                       time.time(), self.qline_reason))

        # Send my own bridge nick
        self.pushBotJoin(do_nick=True)

        # When we enter the syncd state, register this instance with Dtella.
        # This will eventually trigger event_DtellaUp, where we send our state.
        self.schedulePing()
        self.ism.addMeToMain()
示例#10
0
文件: inspircd.py 项目: LiPeK/dtella
    def handleCmd_ENDBURST(self, prefix, args):
        if self.ism.syncd:
            # FIXME
            LOG.warning("Ignoring ENDBURST")
            return

        CHECK(self.server_name)
        LOG.info("Finished receiving IRC sync data.")

        self.showirc = True

        # Check for conflicting bridges.
        if self.ism.findConflictingBridge():
            LOG.error("My nick prefix is in use! Terminating.")
            self.transport.loseConnection()
            reactor.stop()
            return

        # Set up nick reservation
        scfg = getServiceConfig()

        self.ism.killConflictingUsers()
        self.sendLine(
            ":%s ADDLINE Q %s* %s %d 0 :%s" %
            (self.sid, cfg.dc_to_irc_prefix, scfg.my_host,
             time.time(), self.qline_reason))

        # Send my own bridge nick
        self.pushBotJoin(do_nick=True)

        # When we enter the syncd state, register this instance with Dtella.
        # This will eventually trigger event_DtellaUp, where we send our state.
        self.schedulePing()
        self.ism.addMeToMain()
示例#11
0
文件: inspircd.py 项目: LiPeK/dtella
    def connectionMade(self):
        scfg = getServiceConfig()
        LOG.info("Connected to IRC server.")

        self.challenge = make_challenge()
        my_capabs = [
            ("NICKMAX", 32),
            ("HALFOP", 1),
            ("CHANMAX", 65),
            ("MAXMODES", 20),
            ("IDENTMAX", 12),
            ("MAXQUIT", 255),
            ("MAXTOPIC", 307),
            ("MAXKICK", 255),
            ("MAXGECOS", 128),
            ("MAXAWAY", 200),
            ("IP6SUPPORT", 1),
            ("PROTOCOL", 1201),
            ("CHALLENGE", self.challenge),
            ]
        my_capabs_str = ' '.join("%s=%s" % x for x in my_capabs)

        self.sendLine("CAPAB START")
        self.sendLine("CAPAB CAPABILITIES :%s" % my_capabs_str)
        self.sendLine("CAPAB END")
示例#12
0
    def connectionLost(self, result):
        LOG.info("Lost IRC connection.")
        if self.ism.syncd:
            self.ism.removeMeFromMain()

        if self.shutdown_deferred:
            self.shutdown_deferred.callback("Bye!")
示例#13
0
 def killConflictingUsers(self):
     # Find any reserved nicks, and KILL them.
     CHECK(self.ircs and not self.syncd)
     bad_users = [u for u in set(self.users.itervalues())
                  if matches_dc_to_irc_prefix(u.inick)]
     LOG.info("Conflicting users: %r" % bad_users)
     for u in bad_users:
         self.ircs.event_KillUser(u)
         self.removeUser(u)
示例#14
0
    def cleanupOnExit(self):
        LOG.info("Reactor is shutting down.  Doing cleanup.")

        self.shutdown(reconnect='no')
        self.state.saveState()

        # Cleanly close the IRC connection before terminating
        if self.ism:
            return self.ism.shutdown()
示例#15
0
    def cleanupOnExit(self):
        LOG.info("Reactor is shutting down.  Doing cleanup.")

        self.shutdown(reconnect='no')
        self.state.saveState()

        # Cleanly close the IRC connection before terminating
        if self.ism:
            return self.ism.shutdown()
示例#16
0
    def removeQLine(self, nickmask):
        self.qlines.pop(nickmask, None)
        LOG.info("Removed Q-line: " + nickmask)

        # If some other bridge removes our reservation, abort.
        if self.syncd and (nickmask == cfg.dc_to_irc_prefix + "*"):
            LOG.error("My own Q-line was removed! Terminating.")
            if self.ircs:
                self.ircs.transport.loseConnection()
            reactor.stop()
示例#17
0
    def removeQLine(self, nickmask):
        self.qlines.pop(nickmask, None)
        LOG.info("Removed Q-line: " + nickmask)

        # If some other bridge removes our reservation, abort.
        if self.syncd and (nickmask == cfg.dc_to_irc_prefix + "*"):
            LOG.error("My own Q-line was removed! Terminating.")
            if self.ircs:
                self.ircs.transport.loseConnection()
            reactor.stop()
示例#18
0
 def killConflictingUsers(self):
     # Find any reserved nicks, and KILL them.
     CHECK(self.ircs and not self.syncd)
     bad_users = [
         u for u in set(self.users.itervalues())
         if matches_dc_to_irc_prefix(u.inick)
     ]
     LOG.info("Conflicting users: %r" % bad_users)
     for u in bad_users:
         self.ircs.event_KillUser(u)
         self.removeUser(u)
示例#19
0
    def addQLine(self, nickmask, reason):
        nick_re = wild_to_regex(nickmask)

        # After EOS, auto-remove any Q-lines which conflict with mine.
        # This may cause a conflicting bridge to abort.
        if self.syncd and nick_re.match(cfg.dc_to_irc_prefix):
            if self.ircs:
                self.ircs.pushRemoveQLine(nickmask)
            LOG.info("Conflicted Q-line: " + nickmask)
            return

        self.qlines[nickmask] = (nick_re, reason)
        LOG.info("Added Q-line: " + nickmask)
示例#20
0
    def addQLine(self, nickmask, reason):
        nick_re = wild_to_regex(nickmask)

        # After EOS, auto-remove any Q-lines which conflict with mine.
        # This may cause a conflicting bridge to abort.
        if self.syncd and nick_re.match(cfg.dc_to_irc_prefix):
            if self.ircs:
                self.ircs.pushRemoveQLine(nickmask)
            LOG.info("Conflicted Q-line: " + nickmask)
            return

        self.qlines[nickmask] = (nick_re, reason)
        LOG.info("Added Q-line: " + nickmask)
示例#21
0
def runClient(dc_port):
    #Logging for Dtella Client
    setLogFile("dtella.log", 1<<20, 1)
    LOG.debug("Client Logging Manager Initialized")

    from dtella.client.main import DtellaMain_Client
    dtMain = DtellaMain_Client()

    import dtella.local_config as local
    from dtella.common.util import get_version_string

    def botErrorReporter(text):
        dch = dtMain.dch
        if dch:
            dch.bot.say(
                "Something bad happened.  You might want to email this to "
                "[email protected] so we'll know about it:\n"
                "Version: %s %s\n%s" %
                (local.hub_name, get_version_string()[3:], text))

    addTwistedErrorCatcher(botErrorReporter)
    addTwistedErrorCatcher(LOG.critical)

    from dtella.client.dc import DCFactory
    dfactory = DCFactory(dtMain, dc_port)

    LOG.info("%s %s" % (local.hub_name, local.version))

    def cb(first):
        try:
            reactor.listenTCP(dc_port, dfactory, interface='127.0.0.1')
        except twisted.internet.error.CannotListenError:
            if first:
                LOG.warning("TCP bind failed.  Killing old process...")
                if terminate(dc_port):
                    LOG.info("Ok.  Sleeping...")
                    reactor.callLater(2.0, cb, False)
                else:
                    LOG.error("Kill failed.  Giving up.")
                    reactor.stop()
            else:
                LOG.error("Bind failed again.  Giving up.")
                reactor.stop()
        else:
            LOG.info("Listening on 127.0.0.1:%d" % dc_port)
            dtMain.startConnecting()

    reactor.callWhenRunning(cb, True)
    reactor.run()
示例#22
0
def runClient(dc_port):
    #Logging for Dtella Client
    setLogFile("dtella.log", 1 << 20, 1)
    LOG.debug("Client Logging Manager Initialized")

    from dtella.client.main import DtellaMain_Client
    dtMain = DtellaMain_Client()

    import dtella.local_config as local
    from dtella.common.util import get_version_string

    def botErrorReporter(text):
        dch = dtMain.dch
        if dch:
            dch.bot.say(
                "Something bad happened.  You might want to email this to "
                "[email protected] so we'll know about it:\n"
                "Version: %s %s\n%s" %
                (local.hub_name, get_version_string()[3:], text))

    addTwistedErrorCatcher(botErrorReporter)
    addTwistedErrorCatcher(LOG.critical)

    from dtella.client.dc import DCFactory
    dfactory = DCFactory(dtMain, dc_port)

    LOG.info("%s %s" % (local.hub_name, local.version))

    def cb(first):
        try:
            reactor.listenTCP(dc_port, dfactory, interface='127.0.0.1')
        except twisted.internet.error.CannotListenError:
            if first:
                LOG.warning("TCP bind failed.  Killing old process...")
                if terminate(dc_port):
                    LOG.info("Ok.  Sleeping...")
                    reactor.callLater(2.0, cb, False)
                else:
                    LOG.error("Kill failed.  Giving up.")
                    reactor.stop()
            else:
                LOG.error("Bind failed again.  Giving up.")
                reactor.stop()
        else:
            LOG.info("Listening on 127.0.0.1:%d" % dc_port)
            dtMain.startConnecting()

    reactor.callWhenRunning(cb, True)
    reactor.run()
示例#23
0
    def event_DtellaUp(self):
        osm = self.getOnlineStateManager()

        LOG.info("Sending Dtella state to IRC...")

        nicks = osm.nkm.nickmap.values()
        nicks.sort(key=lambda n: n.nick)

        for n in nicks:
            try:
                # This event does everything we need.
                self.event_AddNick(n)
            except NickError:
                osm.nkm.removeNode(n, "Bad Nick")
                n.setNoUser()
示例#24
0
    def event_DtellaUp(self):
        osm = self.getOnlineStateManager()

        LOG.info("Sending Dtella state to IRC...")

        nicks = osm.nkm.nickmap.values()
        nicks.sort(key=lambda n: n.nick)

        for n in nicks:
            try:
                # This event does everything we need.
                self.event_AddNick(n)
            except NickError:
                osm.nkm.removeNode(n, "Bad Nick")
                n.setNoUser()
示例#25
0
 def cb(first):
     try:
         reactor.listenTCP(dc_port, dfactory, interface='127.0.0.1')
     except twisted.internet.error.CannotListenError:
         if first:
             LOG.warning("TCP bind failed.  Killing old process...")
             if terminate(dc_port):
                 LOG.info("Ok.  Sleeping...")
                 reactor.callLater(2.0, cb, False)
             else:
                 LOG.error("Kill failed.  Giving up.")
                 reactor.stop()
         else:
             LOG.error("Bind failed again.  Giving up.")
             reactor.stop()
     else:
         LOG.info("Listening on 127.0.0.1:%d" % dc_port)
         dtMain.startConnecting()
示例#26
0
 def cb(first):
     try:
         reactor.listenTCP(dc_port, dfactory, interface='127.0.0.1')
     except twisted.internet.error.CannotListenError:
         if first:
             LOG.warning("TCP bind failed.  Killing old process...")
             if terminate(dc_port):
                 LOG.info("Ok.  Sleeping...")
                 reactor.callLater(2.0, cb, False)
             else:
                 LOG.error("Kill failed.  Giving up.")
                 reactor.stop()
         else:
             LOG.error("Bind failed again.  Giving up.")
             reactor.stop()
     else:
         LOG.info("Listening on 127.0.0.1:%d" % dc_port)
         dtMain.startConnecting()
示例#27
0
文件: inspircd.py 项目: LiPeK/dtella
    def handleCmd_SQUIT(self, prefix, args):
        # :n.dhirc.com SQUIT remote.dtella.org :Remote host closed
        hostname = args[0]
        try:
            # All servers have been registered as users.
            sid = self.ism.findUser(hostname).uuid.lower()
            if len(sid) != 3:
                raise KeyError
        except KeyError:
            LOG.error("SQUIT: unknown server: %s" % hostname)
            return

        # Find all the users belonging to this server.  This should
        # never match nicks, because only UUIDs start with a number.
        remove_uuids = [uuid for uuid in self.ism.users
                        if uuid.startswith(sid)]

        # Drop them off the network.
        for uuid in remove_uuids:
            LOG.info("SQUIT: removing user: %s" % uuid)
            self.ism.removeUser(self.ism.findUser(uuid))
示例#28
0
    def findConflictingBridge(self):
        # Determine if another bridge conflicts with me.
        # Return True if we need to abort.
        CHECK(self.ircs and not self.syncd)

        stale_qlines = []
        for nickmask, (q, reason) in self.qlines.iteritems():
            # Look for Q-lines which conflict with my prefix.
            if not q.match(cfg.dc_to_irc_prefix):
                continue
            LOG.info("Found a conflicting Q-line: %s" % nickmask)

            # If any nicks exist under that Q-line, we'll need to abort.
            for u in set(self.users.itervalues()):
                if q.match(u.inick):
                    LOG.info("... and a nick to go with it: %s" % u.inick)
                    return True

            stale_qlines.append(nickmask)

        # Remove all stale Q-lines from the network.
        LOG.info("Stale qlines: %r" % stale_qlines)
        for nickmask in stale_qlines:
            del self.qlines[nickmask]
            self.ircs.pushRemoveQLine(nickmask)

        # Conflict has been neutralized.
        return False
示例#29
0
    def findConflictingBridge(self):
        # Determine if another bridge conflicts with me.
        # Return True if we need to abort.
        CHECK(self.ircs and not self.syncd)

        stale_qlines = []
        for nickmask, (q, reason) in self.qlines.iteritems():
            # Look for Q-lines which conflict with my prefix.
            if not q.match(cfg.dc_to_irc_prefix):
                continue
            LOG.info("Found a conflicting Q-line: %s" % nickmask)

            # If any nicks exist under that Q-line, we'll need to abort.
            for u in set(self.users.itervalues()):
                if q.match(u.inick):
                    LOG.info("... and a nick to go with it: %s" % u.inick)
                    return True

            stale_qlines.append(nickmask)

        # Remove all stale Q-lines from the network.
        LOG.info("Stale qlines: %r" % stale_qlines)
        for nickmask in stale_qlines:
            del self.qlines[nickmask]
            self.ircs.pushRemoveQLine(nickmask)

        # Conflict has been neutralized.
        return False
示例#30
0
    def handleCmd_SQUIT(self, prefix, args):
        # :n.dhirc.com SQUIT remote.dtella.org :Remote host closed
        hostname = args[0]
        try:
            # All servers have been registered as users.
            sid = self.ism.findUser(hostname).uuid.lower()
            if len(sid) != 3:
                raise KeyError
        except KeyError:
            LOG.error("SQUIT: unknown server: %s" % hostname)
            return

        # Find all the users belonging to this server.  This should
        # never match nicks, because only UUIDs start with a number.
        remove_uuids = [
            uuid for uuid in self.ism.users if uuid.startswith(sid)
        ]

        # Drop them off the network.
        for uuid in remove_uuids:
            LOG.info("SQUIT: removing user: %s" % uuid)
            self.ism.removeUser(self.ism.findUser(uuid))
示例#31
0
    def handleCmd_SERVER(self, prefix, args):
        # :foo.dhirc.com SERVER s.dhirc.com * 1 00A :Services
        hostname = args[0]
        password = args[1]
        sid = args[3]

        # Register each server as a user, because it's easier.
        LOG.info("Recording server: hostname=%s sid=%s" % (hostname, sid))
        self.ism.addUser(hostname, sid)

        if prefix:
            # Not my directly-connected server.
            return

        if self.server_name:
            # Could be a dupe?  Ignore it.
            return

        # We got a reply from the our connected IRC server, so our password
        # was just accepted.  Send the Dtella state information into IRC.

        scfg = getServiceConfig()

        # Save server name
        CHECK(args[0])
        self.server_name = args[0]

        # Verify challenge response.
        if scfg.recvpass is None:
            LOG.info("Skipping validation of recvpass")
        else:
            ch_response = args[1]
            if ch_response != ihmac(scfg.recvpass, self.challenge):
                raise ValueError("Incorrect recvpass")
            LOG.info("Correct recvpass")

        LOG.info("IRC Server Name: %s" % self.server_name)

        # Tell the ReconnectingClientFactory that we're cool
        self.factory.resetDelay()

        self.sendLine("BURST %d" % time.time())
        self.sendLine("ENDBURST")
示例#32
0
文件: inspircd.py 项目: LiPeK/dtella
    def handleCmd_SERVER(self, prefix, args):
        # :foo.dhirc.com SERVER s.dhirc.com * 1 00A :Services
        hostname = args[0]
        password = args[1]
        sid = args[3]

        # Register each server as a user, because it's easier.
        LOG.info("Recording server: hostname=%s sid=%s" % (hostname, sid))
        self.ism.addUser(hostname, sid)

        if prefix:
            # Not my directly-connected server.
            return

        if self.server_name:
            # Could be a dupe?  Ignore it.
            return

        # We got a reply from the our connected IRC server, so our password
        # was just accepted.  Send the Dtella state information into IRC.

        scfg = getServiceConfig()

        # Save server name
        CHECK(args[0])
        self.server_name = args[0]

        # Verify challenge response.
        if scfg.recvpass is None:
            LOG.info("Skipping validation of recvpass")
        else:
            ch_response = args[1]
            if ch_response != ihmac(scfg.recvpass, self.challenge):
                raise ValueError("Incorrect recvpass")
            LOG.info("Correct recvpass")

        LOG.info("IRC Server Name: %s" % self.server_name)

        # Tell the ReconnectingClientFactory that we're cool
        self.factory.resetDelay()

        self.sendLine("BURST %d" % time.time())
        self.sendLine("ENDBURST")
示例#33
0
 def event_KillUser(self, u):
     scfg = getServiceConfig()
     LOG.info("Killing nick: " + u.inick)
     self.sendLine(":%s KILL %s :%s (nick reserved for Dtella)"
                   % (scfg.my_host, u.inick, scfg.my_host))
示例#34
0
 def pushRemoveQLine(self, nickmask):
     scfg = getServiceConfig()
     LOG.info("Telling network to remove Q-line: %s" % nickmask)
     self.sendLine("TKL - Q * %s %s" % (nickmask, scfg.my_host))
示例#35
0
 def pushRemoveQLine(self, nickmask):
     scfg = getServiceConfig()
     LOG.info("Telling network to remove Q-line: %s" % nickmask)
     self.sendLine(":%s DELLINE Q %s" % (self.sid, nickmask))
示例#36
0
class IRCStateManager(object):
    implements(IDtellaStateObserver)

    def __init__(self, main, ircs=None, uuid_generator=None):
        self.main = main
        self.ircs = ircs
        self.syncd = False

        self.uuid_generator = uuid_generator

        # inick.lower() -> User object
        # *also* indexed by uuid.lower(), if those are enabled.
        self.users = {}

        # If we're using UUIDs, create dicts to keep track of them.
        if self.uuid_generator:
            # uuid.lower() -> Node()
            self.dt_uuids = {}

        # Set of all User()s in the Dtella channel.
        self.chanusers = set()

        self.topic = ""
        self.topic_whoset = ""  # always a dnick
        self.topic_locked = False

        self.moderated = False

        # string -> compiled regex
        self.chanbans = {}
        # string -> (compiled regex, reason)
        self.qlines = {}

        # Network bans: (ip, mask), both ints.
        self.bans = set()

        # Set up the dc->irc bot.
        if self.uuid_generator:
            bot_uuid = self.uuid_generator()
        else:
            bot_uuid = None
        self.bot_user = User(cfg.dc_to_irc_bot, bot_uuid)

    # --- These methods are called from the IRC server ---
    def addMeToMain(self):
        CHECK(not self.syncd)
        self.syncd = True
        self.main.addIRCStateManager(self)

    def removeMeFromMain(self):
        # After calling this, I'm basically a dead object.
        self.main.removeIRCStateManager(self)

    def addUser(self, inick, uuid=None):
        # Start tracking a new IRC user.
        CHECK(inick.lower() not in self.users)

        if self.uuid_generator:
            CHECK(number_prefix(uuid))
            CHECK(uuid.lower() not in self.users)

            # If the nick is a uuid, it must be its own uuid.
            if number_prefix(inick):
                CHECK(inick.lower() == uuid.lower())
        else:
            CHECK(uuid is None)

        u = User(inick, uuid)

        # Don't allow nicks which match my prefix.
        if self.syncd and matches_dc_to_irc_prefix(u.inick):
            if self.ircs:
                self.ircs.event_KillUser(u)
            return

        self.users[inick.lower()] = u
        if self.uuid_generator:
            self.users[uuid.lower()] = u
        return u

    def removeUser(self, u, message=None):
        self.partChannel(u, message)

        # If inick is not a uuid, remove from index.
        if not (self.uuid_generator and number_prefix(u.inick)):
            CHECK(self.users.pop(u.inick.lower()) is u)

        # If a uuid is defined, remove from index.
        if self.uuid_generator:
            CHECK(self.users.pop(u.uuid.lower()) is u)

    def findUser(self, inick):
        # Note: inick may also be a uuid.
        return self.users[inick.lower()]

    def changeNick(self, u, new_inick):
        old_inick = u.inick
        if old_inick.lower() == new_inick.lower():
            # TODO: report case changes to Dtella?
            u.inick = new_inick
            return

        if self.uuid_generator and number_prefix(new_inick):
            # If nick is changing to a uuid, it must be its own uuid.
            CHECK(new_inick.lower() == u.uuid.lower())
        else:
            # Otherwise, the nick must not already exist.
            CHECK(new_inick.lower() not in self.users)

        if not (self.uuid_generator and number_prefix(u.inick)):
            # If old nick is not a uuid, remove it from the index.
            CHECK(self.users.pop(u.inick.lower()) is u)

        # Don't allow IRC nicks which match my prefix.
        conflicted = (self.syncd and matches_dc_to_irc_prefix(new_inick))

        if conflicted:
            # Report exit from Dtella before updating nick.
            self.partChannel(u)

        # Update nick.  Note that UUID is unchanged.
        u.inick = new_inick
        self.users[new_inick.lower()] = u

        if conflicted:
            # Nick matches my prefix, diediedie!
            if self.ircs:
                self.ircs.event_KillUser(u)
            self.removeUser(u)
            return

        scfg = getServiceConfig()

        # Report the change to Dtella.
        if u in self.chanusers:
            try:
                osm = self.getOnlineStateManager()
            except NotOnline:
                return

            infoindex = scfg.chan_umodes.getUserInfoIndex(u)

            chunks = []
            osm.bsm.addChatChunk(
                chunks, cfg.irc_to_dc_bot, "%s is now known as %s" %
                (irc_to_dc(old_inick), irc_to_dc(new_inick)))
            osm.bsm.addNickChunk(chunks, irc_to_dc(old_inick), 0xFF)
            osm.bsm.addNickChunk(chunks, irc_to_dc(new_inick), infoindex)
            osm.bsm.sendBridgeChange(chunks)

    def isMyBot(self, inick):
        # Identify the dc_to_irc_bot, by either nick or uuid.
        return (inick.lower() == self.bot_user.inick.lower()
                or inick.lower() == self.bot_user.uuid.lower())

    def joinChannel(self, u):
        if u in self.chanusers:
            LOG.error("joinChannel: %r already in channel." % u)
            return

        self.chanusers.add(u)
        u.chanmodes.clear()

        try:
            osm = self.getOnlineStateManager()
        except NotOnline:
            return

        scfg = getServiceConfig()
        infoindex = scfg.chan_umodes.getUserInfoIndex(u)
        chunks = []
        osm.bsm.addNickChunk(chunks, irc_to_dc(u.inick), infoindex)
        osm.bsm.sendBridgeChange(chunks)

    def partChannel(self, u, message=None):
        # Remove user from the Dtella channel. Return True if successful.
        try:
            self.chanusers.remove(u)
        except KeyError:
            return False

        try:
            osm = self.getOnlineStateManager()
        except NotOnline:
            # Removal was successful, even if it's not broadcasted.
            return True

        chunks = []
        if message:
            osm.bsm.addChatChunk(chunks, cfg.irc_to_dc_bot, message)
        osm.bsm.addNickChunk(chunks, irc_to_dc(u.inick), 0xFF)
        osm.bsm.sendBridgeChange(chunks)
        return True

    def findDtellaNode(self, inick=None, dnick=None):
        # Try to find a user on Dtella.
        # note: inick may also be a UUID.
        try:
            osm = self.getOnlineStateManager()
        except NotOnline:
            return None

        if inick:
            # First, try parsing inick as a UUID.
            if self.uuid_generator:
                try:
                    return self.dt_uuids[inick.lower()]
                except KeyError:
                    pass
            try:
                dnick = dc_from_irc(inick)
            except NickError:
                pass
        if not dnick:
            return None

        try:
            return osm.nkm.lookupNick(dnick)
        except KeyError:
            return None

    def kickDtellaNode(self, n, l33t_inick, reason, send_quit=True):
        # Handler for KICK/KILL of an existing Dtella user.
        # Caller should get 'n' from findDtellaNode()

        # Exception shouldn't happen here; don't catch.
        osm = self.getOnlineStateManager()

        # Send a kick message.
        chunks = []
        osm.bsm.addKickChunk(chunks,
                             n,
                             irc_to_dc(l33t_inick),
                             reason,
                             rejoin=True,
                             silent=False)
        osm.bsm.sendBridgeChange(chunks)

        # Forget this nick.
        if not send_quit:
            del n.inick
        osm.nkm.removeNode(n, "Kicked")
        n.setNoUser()

    def sendPrivateMessage(self, n, src_inick, text, flags):
        # Send a private message to a Dtella node.
        # Caller should get 'n' from findDtellaNode()

        # Exception shouldn't happen here; don't catch.
        osm = self.getOnlineStateManager()

        chunks = []
        osm.bsm.addMessageChunk(chunks, irc_to_dc(src_inick), text, flags)
        osm.bsm.sendPrivateBridgeChange(n, chunks)

    def sendChannelMessage(self, src_inick, text, flags):
        # Send text to all Dtella nodes.
        try:
            osm = self.getOnlineStateManager()
        except NotOnline:
            return

        chunks = []
        osm.bsm.addChatChunk(chunks, irc_to_dc(src_inick), text, flags)
        osm.bsm.sendBridgeChange(chunks)

    def setTopic(self, whoset, topic):
        try:
            # DC nick
            dnick = dc_from_irc(whoset)
        except NickError:
            # IRC nick
            dnick = irc_to_dc(whoset)

        self.topic = topic
        self.topic_whoset = dnick

        try:
            osm = self.getOnlineStateManager()
        except NotOnline:
            return

        chunks = []
        osm.bsm.addTopicChunk(chunks, dnick, topic, changed=True)
        osm.bsm.sendBridgeChange(chunks)

    def setModerated(self, whoset, on_off):
        self.moderated = on_off
        try:
            osm = self.getOnlineStateManager()
        except NotOnline:
            return

        if on_off:
            action = "enabled"
        else:
            action = "disabled"

        chunks = []
        osm.bsm.addModeratedChunk(chunks, on_off)
        osm.bsm.addChatChunk(chunks, cfg.irc_to_dc_bot,
                             "%s %s moderation." % (irc_to_dc(whoset), action))
        osm.bsm.sendBridgeChange(chunks)

    def setTopicLocked(self, whoset, on_off):
        self.topic_locked = on_off
        try:
            osm = self.getOnlineStateManager()
        except NotOnline:
            return

        if on_off:
            action = "locked"
        else:
            action = "unlocked"

        chunks = []
        osm.bsm.addChatChunk(chunks, cfg.irc_to_dc_bot,
                             "%s %s the topic." % (irc_to_dc(whoset), action))
        osm.bsm.sendBridgeChange(chunks)

    def setChannelBan(self, whoset, on_off, banmask):
        if on_off:
            self.chanbans[banmask] = wild_to_regex(banmask)
            action = "added"
        else:
            self.chanbans.pop(banmask, None)
            action = "removed"
        LOG.debug("bans= %s" % self.chanbans.keys())

        try:
            osm = self.getOnlineStateManager()
        except NotOnline:
            return

        chunks = []
        osm.bsm.addChatChunk(
            chunks, cfg.irc_to_dc_bot,
            "%s %s ban: %s" % (irc_to_dc(whoset), action, banmask))
        osm.bsm.sendBridgeChange(chunks)

    def setChannelUserModes(self, whoset, u, changes):
        # changes: dict of {mode -> on_off}
        if u not in self.chanusers:
            LOG.error("setChannelUserModes: %r not in channel." % u)
            return

        scfg = getServiceConfig()

        # Save old index, apply changes, and get new index.
        old_infoindex = scfg.chan_umodes.getUserInfoIndex(u)
        for mode, on_off in changes.iteritems():
            if on_off:
                u.chanmodes.add(mode)
            else:
                u.chanmodes.discard(mode)
        new_infoindex = scfg.chan_umodes.getUserInfoIndex(u)

        try:
            osm = self.getOnlineStateManager()
        except NotOnline:
            return

        chunks = []
        if new_infoindex == old_infoindex:
            friendly_change = "well that was pointless"
        else:
            friendly_change = "%s -> %s" % (
                scfg.chan_umodes.friendly[old_infoindex],
                scfg.chan_umodes.friendly[new_infoindex])
            osm.bsm.addNickChunk(chunks, irc_to_dc(u.inick), new_infoindex)

        osm.bsm.addChatChunk(
            chunks, cfg.irc_to_dc_bot, "%s set mode %s for %s: %s" %
            (irc_to_dc(whoset), self.formatChannelUserModes(changes),
             irc_to_dc(u.inick), friendly_change))

        osm.bsm.sendBridgeChange(chunks)

    def addQLine(self, nickmask, reason):
        nick_re = wild_to_regex(nickmask)

        # After EOS, auto-remove any Q-lines which conflict with mine.
        # This may cause a conflicting bridge to abort.
        if self.syncd and nick_re.match(cfg.dc_to_irc_prefix):
            if self.ircs:
                self.ircs.pushRemoveQLine(nickmask)
            LOG.info("Conflicted Q-line: " + nickmask)
            return

        self.qlines[nickmask] = (nick_re, reason)
        LOG.info("Added Q-line: " + nickmask)

    def removeQLine(self, nickmask):
        self.qlines.pop(nickmask, None)
        LOG.info("Removed Q-line: " + nickmask)

        # If some other bridge removes our reservation, abort.
        if self.syncd and (nickmask == cfg.dc_to_irc_prefix + "*"):
            LOG.error("My own Q-line was removed! Terminating.")
            if self.ircs:
                self.ircs.transport.loseConnection()
            reactor.stop()

    def setNetworkBan(self, cidr, on_off):
        # See if this is a valid 1.2.3.4/5 CIDR string.
        try:
            ipmask = ipv4.CidrStringToIPMask(cidr)
        except ValueError, e:
            LOG.error("Bad CIDR string: %s")
            return

        # Convert back, to get a normalized string.
        cidr = ipv4.IPMaskToCidrString(ipmask)

        if on_off:
            if ipmask in self.bans:
                LOG.warning("Duplicate ban: %s" % cidr)
                return
            self.bans.add(ipmask)
            LOG.info("Added ban: %s" % cidr)
        else:
            try:
                self.bans.remove(ipmask)
            except KeyError:
                LOG.warning("Ban not found: %s" % cidr)
                return
            LOG.info("Removed ban: %s" % cidr)

        # If we're online, broadcast the ban.
        try:
            osm = self.getOnlineStateManager()
        except NotOnline:
            pass
        else:
            ip, mask = ipmask
            chunks = []
            osm.bsm.addBanChunk(chunks, ip, mask, on_off)
            osm.bsm.sendBridgeChange(chunks)

        # If we're even sort-of online, update local bans.
        if self.main.osm:
            self.main.osm.banm.scheduleRebuildBans()
示例#37
0
 def cleanupOnExit(self):
     LOG.info("Reactor is shutting down.  Doing cleanup.")
     self.shutdown(reconnect="no")
     self.state.saveState()
示例#38
0
 def connectionMade(self):
     scfg = getServiceConfig()
     LOG.info("Connected to IRC server.")
     self.sendLine("PASS :%s" % (scfg.sendpass,))
     self.sendLine(
         "SERVER %s 1 :%s" % (scfg.my_host, scfg.my_name))
示例#39
0
文件: inspircd.py 项目: LiPeK/dtella
 def pushRemoveQLine(self, nickmask):
     scfg = getServiceConfig()
     LOG.info("Telling network to remove Q-line: %s" % nickmask)
     self.sendLine(":%s DELLINE Q %s" % (self.sid, nickmask))
示例#40
0
 def showLoginStatus(self, text, counter=None):
     LOG.info(text)
示例#41
0
 def connectionMade(self):
     scfg = getServiceConfig()
     LOG.info("Connected to IRC server.")
     self.sendLine("PASS :%s" % (scfg.sendpass, ))
     self.sendLine("SERVER %s 1 :%s" % (scfg.my_host, scfg.my_name))
示例#42
0
 def event_KillUser(self, u):
     scfg = getServiceConfig()
     LOG.info("Killing nick: " + u.inick)
     self.sendLine(":%s KILL %s :%s (nick reserved for Dtella)" %
                   (scfg.my_host, u.inick, scfg.my_host))
示例#43
0
 def cleanupOnExit(self):
     LOG.info("Reactor is shutting down.  Doing cleanup.")
     if self.dch:
         self.dch.state = 'shutdown'
     self.shutdown(reconnect='no')
     self.state.saveState()
示例#44
0
 def showLoginStatus(self, text, counter=None):
     LOG.info(text)
示例#45
0
 def cleanupOnExit(self):
     LOG.info("Reactor is shutting down.  Doing cleanup.")
     if self.dch:
         self.dch.state = 'shutdown'
     self.shutdown(reconnect='no')
     self.state.saveState()
示例#46
0
 def pushRemoveQLine(self, nickmask):
     scfg = getServiceConfig()
     LOG.info("Telling network to remove Q-line: %s" % nickmask)
     self.sendLine("TKL - Q * %s %s" % (nickmask, scfg.my_host))