示例#1
0
文件: inspircd.py 项目: LiPeK/dtella
    def handleCmd_KILL(self, prefix, args):
        l33t = prefix
        n00b = args[0]
        reason = irc_strip(args[1])

        # In most cases, n00b is a UUID, but Anope seems to still use a nick.
        # Thus, we have to try both everywhere :-/

        # Reconnect the bot if it's killed.
        if self.ism.isMyBot(n00b):
            if self.ism.syncd:
                self.pushBotJoin(do_nick=True)
            return

        l33t = self.ism.findUser(l33t).inick

        # If n00b is a Dtella node, kick 'em.
        n = self.ism.findDtellaNode(inick=n00b)
        if n:
            self.ism.kickDtellaNode(
                n, l33t, "KILL: " + reason, send_quit=False)
            return

        # If n00b is an IRC user, treat it like a QUIT.
        try:
            n00b_u = self.ism.findUser(n00b)
        except KeyError:
            LOG.warning("Tried to KILL unknown user: %s" % n00b)
            return
        message = (
            "%s has KILL'd %s: %s" %
            (irc_to_dc(l33t), irc_to_dc(n00b_u.inick), reason))
        self.ism.removeUser(n00b_u, message)
示例#2
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()
示例#3
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()
示例#4
0
    def handleCmd_KILL(self, prefix, args):
        l33t = prefix
        n00b = args[0]
        reason = irc_strip(args[1])

        # In most cases, n00b is a UUID, but Anope seems to still use a nick.
        # Thus, we have to try both everywhere :-/

        # Reconnect the bot if it's killed.
        if self.ism.isMyBot(n00b):
            if self.ism.syncd:
                self.pushBotJoin(do_nick=True)
            return

        l33t = self.ism.findUser(l33t).inick

        # If n00b is a Dtella node, kick 'em.
        n = self.ism.findDtellaNode(inick=n00b)
        if n:
            self.ism.kickDtellaNode(n,
                                    l33t,
                                    "KILL: " + reason,
                                    send_quit=False)
            return

        # If n00b is an IRC user, treat it like a QUIT.
        try:
            n00b_u = self.ism.findUser(n00b)
        except KeyError:
            LOG.warning("Tried to KILL unknown user: %s" % n00b)
            return
        message = ("%s has KILL'd %s: %s" %
                   (irc_to_dc(l33t), irc_to_dc(n00b_u.inick), reason))
        self.ism.removeUser(n00b_u, message)
示例#5
0
文件: inspircd.py 项目: LiPeK/dtella
 def handleCmd_QUIT(self, prefix, args):
     uuid = prefix
     try:
         u = self.ism.findUser(uuid)
     except KeyError:
         LOG.warning("Can't quit user: %s" % uuid)
     else:
         self.ism.removeUser(u)
示例#6
0
 def handleCmd_QUIT(self, prefix, args):
     uuid = prefix
     try:
         u = self.ism.findUser(uuid)
     except KeyError:
         LOG.warning("Can't quit user: %s" % uuid)
     else:
         self.ism.removeUser(u)
示例#7
0
文件: inspircd.py 项目: LiPeK/dtella
 def handleCmd_NICK(self, prefix, args):
     # :268AAAAAF NICK Paul 1238303566
     old_uuid = prefix
     new_nick = args[0]
     try:
         u = self.ism.findUser(old_uuid)
     except KeyError:
         # This might be an echo from our KICK.
         LOG.warning("NICK: can't find source: %s" % old_uuid)
         return
     self.ism.changeNick(u, new_nick)
示例#8
0
 def handleCmd_NICK(self, prefix, args):
     # :268AAAAAF NICK Paul 1238303566
     old_uuid = prefix
     new_nick = args[0]
     try:
         u = self.ism.findUser(old_uuid)
     except KeyError:
         # This might be an echo from our KICK.
         LOG.warning("NICK: can't find source: %s" % old_uuid)
         return
     self.ism.changeNick(u, new_nick)
示例#9
0
    def receivedBlockRequest(self, src_ipp, bhash):
        try:
            b = self.cached_blocks[bhash]
        except KeyError:
            LOG.warning("Requested block not found")
            return

        b.scheduleExpire(self.cached_blocks, bhash)

        packet = ['bB']
        packet.append(self.main.osm.me.ipp)
        packet.append(struct.pack('!H', len(b.data)))
        packet.append(b.data)

        ad = Ad().setRawIPPort(src_ipp)
        self.main.ph.sendPacket(''.join(packet), ad.getAddrTuple())
示例#10
0
    def receivedBlockRequest(self, src_ipp, bhash):
        try:
            b = self.cached_blocks[bhash]
        except KeyError:
            LOG.warning("Requested block not found")
            return

        b.scheduleExpire(self.cached_blocks, bhash)

        packet = ['bB']
        packet.append(self.main.osm.me.ipp)
        packet.append(struct.pack('!H', len(b.data)))
        packet.append(b.data)

        ad = Ad().setRawIPPort(src_ipp)
        self.main.ph.sendPacket(''.join(packet), ad.getAddrTuple())
示例#11
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()
示例#12
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()
示例#13
0
    def updateFailed(self, why):
        self.busy = False

        LOG.warning("Dconfig Update Failed: %s" % why)

        self.scheduleUpdate(cfg.dconfig_push_interval)
示例#14
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()
示例#15
0
    def updateFailed(self, why):
        self.busy = False

        LOG.warning("Dconfig Update Failed: %s" % why)

        self.scheduleUpdate(cfg.dconfig_push_interval)