示例#1
0
文件: exec.py 项目: Ro9ueAdmin/PyLink
def _exec(irc, source, args, locals_dict=None):
    """<code>

    Admin-only. Executes <code> in the current PyLink instance. This command performs backslash escaping of characters, so things like \\n and \\ will work.

    \x02**WARNING: THIS CAN BE DANGEROUS IF USED IMPROPERLY!**\x02"""
    permissions.checkPermissions(irc, source, ['exec.exec'])

    # Allow using \n in the code, while escaping backslashes correctly otherwise.
    args = bytes(' '.join(args), 'utf-8').decode("unicode_escape")
    if not args.strip():
        irc.reply('No code entered!')
        return

    log.info('(%s) Executing %r for %s', irc.name, args,
             irc.getHostmask(source))
    if locals_dict is None:
        locals_dict = locals()
    else:
        # Add irc, source, and args to the given locals_dict, to allow basic things like irc.reply()
        # to still work.
        locals_dict['irc'] = irc
        locals_dict['source'] = source
        locals_dict['args'] = args

    exec(args, globals(), locals_dict)

    irc.reply("Done.")
示例#2
0
def _shutdown(irc=None):
    """Shuts down the Pylink daemon."""
    global tried_shutdown
    if tried_shutdown:  # We froze on shutdown last time, so immediately abort.
        _print_remaining_threads()
        raise KeyboardInterrupt("Forcing shutdown.")

    tried_shutdown = True

    # HACK: run the _kill_plugins trigger with the current IRC object. XXX: We should really consider removing this
    # argument, since no plugins actually use it to do anything.
    atexit.unregister(_kill_plugins)
    _kill_plugins(irc)

    # Remove our main PyLink bot as well.
    utils.unregisterService('pylink')

    for ircobj in world.networkobjects.copy().values():
        # Disconnect all our networks.
        remove_network(ircobj)

    log.info(
        "Waiting for remaining threads to stop; this may take a few seconds. If PyLink freezes "
        "at this stage, press Ctrl-C to force a shutdown.")
    _print_remaining_threads()
示例#3
0
def _login(irc, source, username):
    """Internal function to process logins."""
    # Mangle case before we start checking for login data.
    accounts = {k.lower(): v for k, v in conf.conf['login'].get('accounts', {}).items()}

    logindata = accounts.get(username.lower(), {})
    network_filter = logindata.get('networks')
    require_oper = logindata.get('require_oper', False)
    hosts_filter = logindata.get('hosts', [])

    if network_filter and irc.name not in network_filter:
        irc.error("You are not authorized to log in to %r on this network." % username)
        log.warning("(%s) Failed login to %r from %s (wrong network: networks filter says %r but we got %r)", irc.name, username, irc.getHostmask(source), ', '.join(network_filter), irc.name)
        return

    elif require_oper and not irc.isOper(source, allowAuthed=False):
        irc.error("You must be opered to log in to %r." % username)
        log.warning("(%s) Failed login to %r from %s (needs oper)", irc.name, username, irc.getHostmask(source))
        return

    elif hosts_filter and not any(irc.matchHost(host, source) for host in hosts_filter):
        irc.error("Failed to log in to %r: hostname mismatch." % username)
        log.warning("(%s) Failed login to %r from %s (hostname mismatch)", irc.name, username, irc.getHostmask(source))
        return

    irc.users[source].account = username
    irc.reply('Successfully logged in as %s.' % username)
    log.info("(%s) Successful login to %r by %s",
             irc.name, username, irc.getHostmask(source))
示例#4
0
def load(irc, source, args):
    """<plugin name>.

    Loads a plugin from the plugin folder."""
    irc.checkAuthenticated(source, allowOper=False)
    try:
        name = args[0]
    except IndexError:
        irc.reply("Error: Not enough arguments. Needs 1: plugin name.")
        return
    if name in world.plugins:
        irc.reply("Error: %r is already loaded." % name)
        return
    log.info('(%s) Loading plugin %r for %s', irc.name, name,
             irc.getHostmask(source))
    try:
        world.plugins[name] = pl = utils.loadPlugin(name)
    except ImportError as e:
        if str(e) == ('No module named %r' % name):
            log.exception(
                'Failed to load plugin %r: The plugin could not be found.',
                name)
        else:
            log.exception('Failed to load plugin %r: ImportError.', name)
        raise
    else:
        if hasattr(pl, 'main'):
            log.debug('Calling main() function of plugin %r', pl)
            pl.main(irc)
    irc.reply("Loaded plugin %r." % name)
示例#5
0
def delacc(irc, source, args):
    """<channel/chanpair> <mask>

    Removes the Automode entry for the given mask on the given channel, if one exists.
    """
    try:
        chanpair, mask = args
    except ValueError:
        error(irc, "Invalid arguments given. Needs 2: channel, mask")
        return
    else:
        ircobj, channel = getChannelPair(irc, source, chanpair, perm='manage')

    dbentry = db.get(ircobj.name+channel)

    if dbentry is None:
        error(irc, "No Automode access entries exist for \x02%s\x02." % channel)
        return

    if mask in dbentry:
        del dbentry[mask]
        log.info('(%s) %s removed modes for %s on %s', ircobj.name, irc.getHostmask(source), mask, channel)
        reply(irc, "Done. Removed the Automode access entry for \x02%s\x02 in \x02%s\x02." % (mask, channel))
    else:
        error(irc, "No Automode access entry for \x02%s\x02 exists in \x02%s\x02." % (mask, channel))

    # Remove channels if no more entries are left.
    if not dbentry:
        log.debug("Automode: purging empty channel pair %s/%s", ircobj.name, channel)
        del db[ircobj.name+channel]
示例#6
0
def handle_partquit(irc, source, command, args):
    """Antispam part/quit message filter."""
    text = args.get('text')
    pq_settings = irc.get_service_option('antispam', 'partquit',
                                         PARTQUIT_DEFAULTS)

    if not text:
        return  # No text to match against
    elif command == 'QUIT' and not pq_settings.get('watch_quits', True):
        return  # Not enabled
    elif command == 'PART' and not pq_settings.get('watch_parts', True):
        return

    # Merge together global and local partquit filter lists.
    pq_globs = set(conf.conf.get('antispam', {}).get('partquit_globs', [])) | \
               set(irc.serverdata.get('antispam_partquit_globs', []))
    if not pq_globs:
        return

    for filterglob in pq_globs:
        if utils.match_text(filterglob, text):
            # For parts, also log the affected channels
            if command == 'PART':
                filtered_message = pq_settings.get('part_filter_message', PARTQUIT_DEFAULTS['part_filter_message'])
                log.info('(%s) antispam: filtered part message from %s on %s due to part/quit filter glob %s',
                         irc.name, irc.get_hostmask(source), ','.join(args['channels']), filterglob)
            else:
                filtered_message = pq_settings.get('quit_filter_message', PARTQUIT_DEFAULTS['quit_filter_message'])
                log.info('(%s) antispam: filtered quit message from %s due to part/quit filter glob %s',
                         irc.name, args['userdata'].nick, filterglob)
            args['text'] = filtered_message
            break
示例#7
0
def identify(irc, source, args):
    """<username> <password>

    Logs in to PyLink using the configured administrator account."""
    if utils.isChannel(irc.called_in):
        irc.reply('Error: This command must be sent in private. '
                  '(Would you really type a password inside a channel?)')
        return
    try:
        username, password = args[0], args[1]
    except IndexError:
        irc.reply('Error: Not enough arguments.')
        return
    # Usernames are case-insensitive, passwords are NOT.
    if username.lower() == irc.conf['login']['user'].lower(
    ) and password == irc.conf['login']['password']:
        realuser = irc.conf['login']['user']
        irc.users[source].identified = realuser
        irc.reply('Successfully logged in as %s.' % realuser)
        log.info("(%s) Successful login to %r by %s", irc.name, username,
                 irc.getHostmask(source))
    else:
        irc.reply('Error: Incorrect credentials.')
        u = irc.users[source]
        log.warning("(%s) Failed login to %r from %s", irc.name, username,
                    irc.getHostmask(source))
示例#8
0
def load(irc, source, args):
    """<plugin name>.

    Loads a plugin from the plugin folder."""
    # Note: reload capability is acceptable here, because all it actually does is call
    # load after unload.
    permissions.checkPermissions(irc, source, ['core.load', 'core.reload'])

    try:
        name = args[0]
    except IndexError:
        irc.reply("Error: Not enough arguments. Needs 1: plugin name.")
        return
    if name in world.plugins:
        irc.reply("Error: %r is already loaded." % name)
        return
    log.info('(%s) Loading plugin %r for %s', irc.name, name, irc.getHostmask(source))
    try:
        world.plugins[name] = pl = utils.loadPlugin(name)
    except ImportError as e:
        if str(e) == ('No module named %r' % name):
            log.exception('Failed to load plugin %r: The plugin could not be found.', name)
        else:
            log.exception('Failed to load plugin %r: ImportError.', name)
        raise
    else:
        if hasattr(pl, 'main'):
            log.debug('Calling main() function of plugin %r', pl)
            pl.main(irc=irc)
    irc.reply("Loaded plugin %r." % name)
示例#9
0
def disconnect(irc, source, args):
    """<network>

    Disconnects the network <network>. When all networks are disconnected, PyLink will automatically exit.

    To reconnect a network disconnected using this command, use REHASH to reload the networks list."""
    permissions.check_permissions(irc, source, ['networks.disconnect'])
    try:
        netname = args[0]
        network = world.networkobjects[netname]
    except IndexError:  # No argument given.
        irc.error(
            'Not enough arguments (needs 1: network name (case sensitive)).')
        return
    except KeyError:  # Unknown network.
        irc.error('No such network "%s" (case sensitive).' % netname)
        return

    if network.has_cap('virtual-server'):
        irc.error(
            '"%s" is a virtual server and cannot be directly disconnected.' %
            netname)
        return

    log.info('Disconnecting network %r per %s', netname,
             irc.get_hostmask(source))
    control.remove_network(network)
    irc.reply(
        "Done. If you want to reconnect this network, use the 'rehash' command."
    )
示例#10
0
def handle_stats(irc, source, command, args):
    """/STATS handler. Currently supports the following:

    c - link blocks
    o - oper blocks (accounts)
    u - shows uptime
    """

    stats_type = args['stats_type'][0].lower(
    )  # stats_type shouldn't be more than 1 char anyways

    perms = ['stats.%s' % stats_type]

    if stats_type == 'u':
        perms.append('stats.uptime')  # Consistency

    try:
        permissions.check_permissions(irc, source, perms)
    except utils.NotAuthorizedError as e:
        # Note, no irc.error() because this is not a command, but a handler
        irc.msg(source, 'Error: %s' % e, notice=True)
        return

    log.info('(%s) /STATS %s requested by %s', irc.name, stats_type,
             irc.get_hostmask(source))

    def _num(num, text):
        irc.numeric(args['target'], num, source, text)

    if stats_type == 'c':
        # 213/RPL_STATSCLINE: "C <host> * <name> <port> <class>"
        for netname, serverdata in sorted(conf.conf['servers'].items()):
            # We're cramming as much as we can into the class field...
            _num(
                213, "C %s * %s %s [%s:%s:%s]" %
                (serverdata.get('ip', '0.0.0.0'), netname,
                 serverdata.get('port', 0), serverdata['protocol'],
                 'ssl' if serverdata.get('ssl') else 'no-ssl',
                 serverdata.get('encoding', 'utf-8')))
    elif stats_type == 'o':
        # 243/RPL_STATSOLINE: "O <hostmask> * <nick> [:<info>]"
        # New style accounts only!
        for accountname, accountdata in conf.conf['login'].get('accounts',
                                                               {}).items():
            networks = accountdata.get('networks', [])
            if irc.name in networks or not networks:
                hosts = ' '.join(accountdata.get('hosts', ['*@*']))
                needoper = 'needoper' if accountdata.get(
                    'require_oper') else ''
                _num(243, "O %s * %s :%s" % (hosts, accountname, needoper))

    elif stats_type == 'u':
        # 242/RPL_STATSUPTIME: ":Server Up <days> days <hours>:<minutes>:<seconds>"
        _num(242, ':Server Up %s' % timediff(world.start_ts, int(time.time())))

    else:
        log.info('(%s) Unknown /STATS type %r requested by %s', irc.name,
                 stats_type, irc.get_hostmask(source))
    _num(219, "%s :End of /STATS report" % stats_type)
示例#11
0
 def on_member_add(self, event: events.GuildMemberAdd, *args, **kwargs):
     log.info('(%s) got GuildMemberAdd event for guild %s/%s: %s', self.protocol.name, event.guild.id, event.guild.name, event.member)
     try:
         pylink_netobj = self.protocol._children[event.guild.id]
     except KeyError:
         log.error("(%s) Could not burst user %s as the parent network object does not exist", self.protocol.name, event.member)
         return
     self._burst_new_client(event.guild, event.member, pylink_netobj)
示例#12
0
def shutdown(irc, source, args):
    """takes no arguments.

    Exits PyLink by disconnecting all networks."""
    permissions.check_permissions(irc, source, ['core.shutdown'])
    log.info('(%s) SHUTDOWN requested by %s, exiting...', irc.name,
             irc.get_hostmask(source))
    control.shutdown(irc=irc)
示例#13
0
 def on_server_update(self, event: events.GuildUpdate, *args, **kwargs):
     log.info('(%s) got GuildUpdate event for guild %s/%s', self.protocol.name, event.guild.id, event.guild.name)
     try:
         pylink_netobj = self.protocol._children[event.guild.id]
     except KeyError:
         log.error("(%s) Could not update guild %s/%s as the corresponding network object does not exist", self.protocol.name, event.guild.id, event.guild.name)
         return
     else:
         pylink_netobj._guild_name = event.guild.name
示例#14
0
def rehash():
    """Rehashes the PyLink daemon."""
    log.info('Reloading PyLink configuration...')
    old_conf = conf.conf.copy()
    fname = conf.fname
    new_conf = conf.load_conf(fname, errors_fatal=False, logger=log)
    conf.conf = new_conf

    # Reset any file logger options.
    _stop_file_loggers()
    files = new_conf['logging'].get('files')
    if files:
        for filename, config in files.items():
            _make_file_logger(filename, config.get('loglevel'))

    log.debug('rehash: updating console log level')
    world.console_handler.setLevel(_get_console_log_level())
    login._make_cryptcontext()  # refresh password hashing settings

    for network, ircobj in world.networkobjects.copy().items():
        # Server was removed from the config file, disconnect them.
        log.debug('rehash: checking if %r is in new conf still.', network)
        if network not in new_conf['servers']:
            log.debug(
                'rehash: removing connection to %r (removed from config).',
                network)
            remove_network(ircobj)
        else:
            # XXX: we should really just add abstraction to Irc to update config settings...
            ircobj.serverdata = new_conf['servers'][network]

            ircobj.autoconnect_active_multiplier = 1

            # Clear the IRC object's channel loggers and replace them with
            # new ones by re-running log_setup().
            while ircobj.loghandlers:
                log.removeHandler(ircobj.loghandlers.pop())

            ircobj.log_setup()

    utils._reset_module_dirs()

    for network, sdata in new_conf['servers'].items():
        # Connect any new networks or disconnected networks if they aren't already.
        if network not in world.networkobjects:
            try:
                proto = utils._get_protocol_module(sdata['protocol'])

                # API note: 2.0.x style of starting network connections
                world.networkobjects[network] = newirc = proto.Class(network)
                newirc.connect()
            except:
                log.exception(
                    'Failed to initialize network %r, skipping it...', network)

    log.info('Finished reloading PyLink configuration.')
示例#15
0
 def on_ready(self, event, *args, **kwargs):
     self.botuser = event.user.id
     log.info('(%s) got ready event, starting messaging thread',
              self.protocol.name)
     self._message_thread = threading.Thread(
         name="Messaging thread for %s" % self.name,
         target=self.protocol._message_builder,
         daemon=True)
     self._message_thread.start()
     self.protocol.connected.set()
示例#16
0
def loadDB():
    """Loads the Automode database, silently creating a new one if this fails."""
    global db
    try:
        with open(dbname, "r") as f:
            db.update(json.load(f))
    except (ValueError, IOError, OSError):
        log.info(
            "Automode: failed to load links database %s; creating a new one in "
            "memory.", dbname)
示例#17
0
def _rehash():
    """Rehashes the PyLink daemon."""
    log.info('Reloading PyLink configuration...')
    old_conf = conf.conf.copy()
    fname = conf.fname
    new_conf = conf.loadConf(fname, errors_fatal=False, logger=log)
    conf.conf = new_conf

    # Reset any file logger options.
    stopFileLoggers()
    files = new_conf['logging'].get('files')
    if files:
        for filename, config in files.items():
            makeFileLogger(filename, config.get('loglevel'))

    log.debug('rehash: updating console log level')
    world.console_handler.setLevel(getConsoleLogLevel())

    # Reset permissions.
    log.debug('rehash: resetting permissions')
    permissions.resetPermissions()

    for network, ircobj in world.networkobjects.copy().items():
        # Server was removed from the config file, disconnect them.
        log.debug('rehash: checking if %r is in new conf still.', network)
        if network not in new_conf['servers']:
            log.debug(
                'rehash: removing connection to %r (removed from config).',
                network)
            remove_network(ircobj)
        else:
            # XXX: we should really just add abstraction to Irc to update config settings...
            ircobj.conf = new_conf
            ircobj.serverdata = new_conf['servers'][network]
            ircobj.botdata = new_conf['bot']
            ircobj.autoconnect_active_multiplier = 1

            # Clear the IRC object's channel loggers and replace them with
            # new ones by re-running logSetup().
            while ircobj.loghandlers:
                log.removeHandler(ircobj.loghandlers.pop())

            ircobj.logSetup()

    utils.resetModuleDirs()

    for network, sdata in new_conf['servers'].items():
        # Connect any new networks or disconnected networks if they aren't already.
        if (network not in world.networkobjects) or (
                not world.networkobjects[network].connection_thread.is_alive()
        ):
            proto = utils.getProtocolModule(sdata['protocol'])
            world.networkobjects[network] = classes.Irc(
                network, proto, new_conf)
    log.info('Finished reloading PyLink configuration.')
示例#18
0
def hook_privmsg(irc, source, command, args):
    channel = args['target']
    text = args['text']

    # irc.pseudoclient stores the IrcUser object of the main PyLink client.
    # (i.e. the user defined in the bot: section of the config)
    if utils.isChannel(channel) and irc.pseudoclient.nick in text:
        irc.msg(channel, 'hi there!')
        # log.debug, log.info, log.warning, log.error, log.exception (within except: clauses)
        # and log.critical are supported here.
        log.info('%s said my name on channel %s (PRIVMSG hook caught)' % (source, channel))
示例#19
0
def shutdown(irc, source, args):
    """takes no arguments.

    Exits PyLink by disconnecting all networks."""
    irc.checkAuthenticated(source, allowOper=False)
    u = irc.users[source]

    log.info('(%s) SHUTDOWN requested by "%s!%s@%s", exiting...', irc.name,
             u.nick, u.ident, u.host)

    control._shutdown(irc)
示例#20
0
    def _burst_guild(self, guild):
        log.info('(%s) bursting guild %s/%s', self.protocol.name, guild.id,
                 guild.name)
        pylink_netobj = self.protocol._create_child(guild.id, guild.name)
        pylink_netobj.uplink = None

        for member in guild.members.values():
            self._burst_new_client(guild, member, pylink_netobj)

        pylink_netobj.connected.set()
        pylink_netobj.call_hooks([None, 'ENDBURST', {}])
示例#21
0
 def check_nick_collision(self, nick):
     """
     Nick collision checker.
     """
     uid = self.irc.nickToUid(nick)
     # If there is a nick collision, we simply alert plugins. Relay will purposely try to
     # lose fights and tag nicks instead, while other plugins can choose how to handle this.
     if uid:
         log.info(
             '(%s) Nick collision on %s/%s, forwarding this to plugins',
             self.irc.name, uid, nick)
         self.irc.callHooks([self.irc.sid, 'SAVE', {'target': uid}])
示例#22
0
    def on_member_remove(self, event: events.GuildMemberRemove, *args, **kwargs):
        log.info('(%s) got GuildMemberRemove event for guild %s: %s', self.protocol.name, event.guild_id, event.user)
        try:
            pylink_netobj = self.protocol._children[event.guild_id]
        except KeyError:
            log.debug("(%s) Could not remove user %s as the parent network object does not exist", self.protocol.name, event.user)
            return

        if event.user.id in pylink_netobj.users:
            pylink_netobj._remove_client(event.user.id)
            # XXX: make the message configurable
            pylink_netobj.call_hooks([event.user.id, 'QUIT', {'text': 'User left the guild'}])
示例#23
0
def shutdown(irc, source, args):
    """takes no arguments.

    Exits PyLink by disconnecting all networks."""

    permissions.checkPermissions(irc, source, ['core.shutdown'])

    u = irc.users[source]

    log.info('(%s) SHUTDOWN requested by "%s!%s@%s", exiting...', irc.name, u.nick,
             u.ident, u.host)

    control._shutdown(irc)
示例#24
0
def delacc(irc, source, args):
    """<channel/chanpair> <mask or range string>

    Removes the Automode entry for the given mask or range string, if they exist.

    Range strings are indices (entry numbers) or ranges of them joined together with commas: e.g.
    "1", "2-10", "1,3,5-8". Entry numbers are shown by LISTACC.
    """
    try:
        chanpair, mask = args
    except ValueError:
        error(irc, "Invalid arguments given. Needs 2: channel, mask")
        return
    else:
        ircobj, channel = _get_channel_pair(irc, source, chanpair, perm='manage')

    dbentry = db.get(ircobj.name+channel)

    if dbentry is None:
        error(irc, "No Automode access entries exist for \x02%s\x02." % channel)
        return

    if mask in dbentry:
        del dbentry[mask]
        log.info('(%s) %s removed modes for %s on %s', ircobj.name, irc.get_hostmask(source), mask, channel)
        reply(irc, "Done. Removed the Automode access entry for \x02%s\x02 in \x02%s\x02." % (mask, channel))
    else:
        # Treat the mask as a range string.
        try:
            new_keys = utils.remove_range(mask, sorted(dbentry.keys()))
        except ValueError:
            error(irc, "No Automode access entry for \x02%s\x02 exists in \x02%s\x02." % (mask, channel))
            return

        # XXX: Automode entries are actually unordered: what we're actually doing is sorting the keys
        # by name into a list, running remove_range on that, and removing the difference.
        removed = []
        source_host = irc.get_hostmask(source)
        for mask_entry in dbentry.copy():
            if mask_entry not in new_keys:
                del dbentry[mask_entry]
                log.info('(%s) %s removed modes for %s on %s', ircobj.name, source_host, mask_entry, channel)
                removed.append(mask_entry)

        reply(irc, 'Done. Removed \x02%d\x02 entries on \x02%s\x02: %s' % (len(removed), channel, ', '.join(removed)))

    # Remove channels if no more entries are left.
    if not dbentry:
        log.debug("Automode: purging empty channel pair %s/%s", ircobj.name, channel)
        del db[ircobj.name+channel]
        modebot.remove_persistent_channel(ircobj, 'automode', channel)
示例#25
0
def squishids(ids):
    if ids == []:
        return "0"
    else:
        found = [[ids[0]]]
        log.info("Found: %s" % found)
        for previous, next in itertools.zip_longest(ids, ids[1:]):
            if next != previous + 1:
                if previous != found[-1][0]:
                    found[-1].append(previous)
                if next is not None:
                    found.append([next])

        return ", ".join("-".join(str(r) for r in f) for f in found)
示例#26
0
def _remove_pid():
    pidfile = "%s.pid" % conf.confname
    if world._should_remove_pid:
        # Remove our pid file.
        log.info("Removing PID file %r.", pidfile)
        try:
            os.remove(pidfile)
        except OSError:
            log.exception("Failed to remove PID file %r, ignoring..." %
                          pidfile)
    else:
        log.debug(
            'Not removing PID file %s as world._should_remove_pid is False.' %
            pidfile)
示例#27
0
def _kill_plugins(irc=None):
    log.info("Shutting down plugins.")
    for name, plugin in world.plugins.items():
        # Before closing connections, tell all plugins to shutdown cleanly first.
        if hasattr(plugin, 'die'):
            log.debug(
                'coremods.control: Running die() on plugin %s due to shutdown.',
                name)
            try:
                plugin.die(irc)
            except:  # But don't allow it to crash the server.
                log.exception(
                    'coremods.control: Error occurred in die() of plugin %s, skipping...',
                    name)
示例#28
0
def _eval(irc, source, args):
    """<Python expression>

    Admin-only. Evaluates the given Python expression and returns the result.
    \x02**WARNING: THIS CAN BE DANGEROUS IF USED IMPROPERLY!**\x02"""
    irc.checkAuthenticated(source, allowOper=False)

    args = ' '.join(args)
    if not args.strip():
        irc.reply('No code entered!')
        return

    log.info('(%s) Evaluating %r for %s', irc.name, args,
             irc.getHostmask(source))
    irc.reply(eval(args))
示例#29
0
def inject(irc, source, args):
    """<text>

    Admin-only. Injects raw text into the running PyLink protocol module, replying with the hook data returned.
    \x02**WARNING: THIS CAN BREAK YOUR NETWORK IF USED IMPROPERLY!**\x02"""
    irc.checkAuthenticated(source, allowOper=False)

    args = ' '.join(args)
    if not args.strip():
        irc.reply('No text entered!')
        return

    log.info('(%s) Injecting raw text %r into protocol module for %s',
             irc.name, args, irc.getHostmask(source))
    irc.reply(irc.runline(args))
示例#30
0
def _exec(irc, source, args):
    """<code>

    Admin-only. Executes <code> in the current PyLink instance. This command performs backslash escaping of characters, so things like \\n and \\ will work.
    \x02**WARNING: THIS CAN BE DANGEROUS IF USED IMPROPERLY!**\x02"""
    irc.checkAuthenticated(source, allowOper=False)

    # Allow using \n in the code, while escaping backslashes correctly otherwise.
    args = bytes(' '.join(args), 'utf-8').decode("unicode_escape")
    if not args.strip():
        irc.reply('No code entered!')
        return

    log.info('(%s) Executing %r for %s', irc.name, args,
             irc.getHostmask(source))
    exec(args, globals(), locals())
示例#31
0
def _submit_dronebl(irc, ip, apikey, nickuserhost=None):
    reason = irc.get_service_option('badchans', 'dnsbl_reason', DEFAULT_DNSBL_REASON)

    request = '<add ip="%s" type="%s" comment="%s" />' % (ip, DRONEBL_TYPE, reason)
    xml_data = '<?xml version="1.0"?><request key="%s">%s</request>' % (apikey, request)
    headers = {'Content-Type': 'text/xml'}

    log.debug('(%s) badchans: posting to dronebl: %s', irc.name, xml_data)

    # Expecting this to block
    r = requests.post('https://dronebl.org/RPC2', data=xml_data, headers=headers)

    dronebl_response = r.text

    log.debug('(%s) badchans: got response from dronebl: %s', irc.name, dronebl_response)
    if '<success' in dronebl_response:
        log.info('(%s) badchans: got success for DroneBL on %s (%s)', irc.name, ip, nickuserhost or 'some n!u@h')
    else:
        log.warning('(%s) badchans: dronebl submission error: %s', irc.name, dronebl_response)
def _check_connection(irc, args):
    ip = args['ip']
    try:
        result = sshbl.scan(ip)
    except:
        log.exception("SSHBL scan errored:")
        return

    threshold = irc.get_service_option('sshbl', 'threshold', default=0)
    reason = irc.get_service_option('sshbl', 'reason',
        "Your host runs an SSH daemon commonly used by spammer IPs. Consider upgrading your machines "
        "or contacting network staff for an exemption.")

    if result:
        _, port, blacklisted, score = result
        if not blacklisted:
            return
        log.info("sshbl: caught IP %s:%s (%s/%s) with score %s", ip, port, irc.name, args['nick'], score)
        if args['uid'] in irc.users:
            irc.kill(irc.pseudoclient.uid, args['uid'], reason)
示例#33
0
def handle_join(irc, source, command, args):
    """
    killonjoin JOIN listener.
    """
    # Ignore our own clients and other Ulines
    if irc.is_privileged_service(source) or irc.is_internal_server(source):
        return

    badchans = irc.serverdata.get('badchans')
    if not badchans:
        return
    elif not isinstance(badchans, list):
        log.error("(%s) badchans: the 'badchans' option must be a list of strings, not a %s", irc.name, type(badchans))
        return

    use_kline = irc.get_service_option('badchans', 'use_kline', False)
    kline_duration = irc.get_service_option('badchans', 'kline_duration', DEFAULT_BAN_DURATION)
    try:
        kline_duration = utils.parse_duration(kline_duration)
    except ValueError:
        log.warning('(%s) badchans: invalid kline duration %s', irc.name, kline_duration, exc_info=True)
        kline_duration = DEFAULT_BAN_DURATION

    channel = args['channel']
    for badchan in badchans:
        if irc.match_text(badchan, channel):
            asm_uid = None
            # Try to kill from the antispam service if available
            if 'antispam' in world.services:
                asm_uid = world.services['antispam'].uids.get(irc.name)

            for user in args['users']:
                try:
                    ip = irc.users[user].ip
                    ipa = ipaddress.ip_address(ip)
                except (KeyError, ValueError):
                    log.error("(%s) badchans: could not obtain IP of user %s", irc.name, user)
                    continue
                nuh = irc.get_hostmask(user)

                exempt_hosts = set(conf.conf.get('badchans', {}).get('exempt_hosts', [])) | \
                             set(irc.serverdata.get('badchans_exempt_hosts', []))

                if not ipa.is_global:
                    irc.msg(user, "Warning: %s kills unopered users, but non-public addresses are exempt." % channel,
                            notice=True,
                            source=asm_uid or irc.pseudoclient.uid)
                    continue

                if exempt_hosts:
                    skip = False
                    for glob in exempt_hosts:
                        if irc.match_host(glob, user):
                            log.info("(%s) badchans: ignoring exempt user %s on %s (%s)", irc.name, nuh, channel, ip)
                            irc.msg(user, "Warning: %s kills unopered users, but your host is exempt." % channel,
                                    notice=True,
                                    source=asm_uid or irc.pseudoclient.uid)
                            skip = True
                            break
                    if skip:
                        continue

                if irc.is_oper(user):
                    irc.msg(user, "Warning: %s kills unopered users!" % channel,
                            notice=True,
                            source=asm_uid or irc.pseudoclient.uid)
                else:
                    log.info('(%s) badchans: punishing user %s (server: %s) for joining channel %s',
                             irc.name, nuh, irc.get_friendly_name(irc.get_server(user)), channel)
                    if use_kline:
                        irc.set_server_ban(asm_uid or irc.sid, kline_duration, host=ip, reason=REASON)
                    else:
                        irc.kill(asm_uid or irc.sid, user, REASON)

                    if ip not in seen_ips:
                        dronebl_key = irc.get_service_option('badchans', 'dronebl_key')
                        if dronebl_key:
                            log.info('(%s) badchans: submitting IP %s (%s) to DroneBL', irc.name, ip, nuh)
                            pool.submit(_submit_dronebl, irc, ip, dronebl_key, nuh)

                        dnsblim_key = irc.get_service_option('badchans', 'dnsblim_key')
                        if dnsblim_key:
                            log.info('(%s) badchans: submitting IP %s (%s) to DNSBL.im', irc.name, ip, nuh)
                            pool.submit(_submit_dnsblim, irc, ip, dnsblim_key, nuh)

                        seen_ips[ip] = time.time()

                    else:
                        log.debug('(%s) badchans: ignoring already submitted IP %s', irc.name, ip)