# admin.py: PyLink administrative commands import sys import os import inspect sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import utils from log import log class NotAuthenticatedError(Exception): pass def checkauthenticated(irc, source): lastfunc = inspect.stack()[1][3] if not irc.users[source].identified: log.warning('(%s) Access denied for %s calling %r', irc.name, utils.getHostmask(irc, source), lastfunc) raise NotAuthenticatedError("You are not authenticated!") def _exec(irc, source, args): """ Admin-only. Executes in the current PyLink instance. \x02**WARNING: THIS CAN BE DANGEROUS IF USED IMPROPERLY!**\x02""" checkauthenticated(irc, source) args = ' '.join(args) if not args.strip(): utils.msg(irc, source, 'No code entered!') return log.info('(%s) Executing %r for %s', irc.name, args, utils.getHostmask(irc, source)) exec(args, globals(), locals()) utils.add_cmd(_exec, 'exec') @utils.add_cmd def spawnclient(irc, source, args): """ Admin-only. Spawns the specified PseudoClient on the PyLink server. Note: this doesn't check the validity of any fields you give it!""" checkauthenticated(irc, source) try: nick, ident, host = args[:3] except ValueError: utils.msg(irc, source, "Error: not enough arguments. Needs 3: nick, user, host.") return irc.proto.spawnClient(irc, nick, ident, host) @utils.add_cmd def quit(irc, source, args): """ [] Admin-only. Quits the PyLink client with nick , if one exists.""" checkauthenticated(irc, source) try: nick = args[0] except IndexError: utils.msg(irc, source, "Error: not enough arguments. Needs 1-2: nick, reason (optional).") return if irc.pseudoclient.uid == utils.nickToUid(irc, nick): utils.msg(irc, source, "Error: cannot quit the main PyLink PseudoClient!") return u = utils.nickToUid(irc, nick) quitmsg = ' '.join(args[1:]) or 'Client quit' irc.proto.quitClient(irc, u, quitmsg) irc.callHooks([u, 'PYLINK_ADMIN_QUIT', {'text': quitmsg, 'parse_as': 'QUIT'}]) def joinclient(irc, source, args): """ ,[], etc. Admin-only. Joins , the nick of a PyLink client, to a comma-separated list of channels.""" checkauthenticated(irc, source) try: nick = args[0] clist = args[1].split(',') if not clist: raise IndexError except IndexError: utils.msg(irc, source, "Error: not enough arguments. Needs 2: nick, comma separated list of channels.") return u = utils.nickToUid(irc, nick) for channel in clist: if not utils.isChannel(channel): utils.msg(irc, source, "Error: Invalid channel name %r." % channel) return irc.proto.joinClient(irc, u, channel) irc.callHooks([u, 'PYLINK_ADMIN_JOIN', {'channel': channel, 'users': [u], 'modes': irc.channels[channel].modes, 'parse_as': 'JOIN'}]) utils.add_cmd(joinclient, name='join') @utils.add_cmd def nick(irc, source, args): """ Admin-only. Changes the nick of , a PyLink client, to .""" checkauthenticated(irc, source) try: nick = args[0] newnick = args[1] except IndexError: utils.msg(irc, source, "Error: not enough arguments. Needs 2: nick, newnick.") return u = utils.nickToUid(irc, nick) if newnick in ('0', u): newnick = u elif not utils.isNick(newnick): utils.msg(irc, source, 'Error: Invalid nickname %r.' % newnick) return irc.proto.nickClient(irc, u, newnick) irc.callHooks([u, 'PYLINK_ADMIN_NICK', {'newnick': newnick, 'oldnick': nick, 'parse_as': 'NICK'}]) @utils.add_cmd def part(irc, source, args): """ ,[],... [] Admin-only. Parts , the nick of a PyLink client, from a comma-separated list of channels.""" checkauthenticated(irc, source) try: nick = args[0] clist = args[1].split(',') reason = ' '.join(args[2:]) except IndexError: utils.msg(irc, source, "Error: not enough arguments. Needs 2: nick, comma separated list of channels.") return u = utils.nickToUid(irc, nick) for channel in clist: if not utils.isChannel(channel): utils.msg(irc, source, "Error: Invalid channel name %r." % channel) return irc.proto.partClient(irc, u, channel, reason) irc.callHooks([u, 'PYLINK_ADMIN_PART', {'channels': clist, 'text': reason, 'parse_as': 'PART'}]) @utils.add_cmd def kick(irc, source, args): """ [] Admin-only. Kicks from via , where is the nick of a PyLink client.""" checkauthenticated(irc, source) try: nick = args[0] channel = args[1] target = args[2] reason = ' '.join(args[3:]) except IndexError: utils.msg(irc, source, "Error: not enough arguments. Needs 3-4: source nick, channel, target, reason (optional).") return u = utils.nickToUid(irc, nick) targetu = utils.nickToUid(irc, target) if not utils.isChannel(channel): utils.msg(irc, source, "Error: Invalid channel name %r." % channel) return irc.proto.kickClient(irc, u, channel, targetu, reason) irc.callHooks([u, 'PYLINK_ADMIN_KICK', {'channel': channel, 'target': targetu, 'text': reason, 'parse_as': 'KICK'}]) @utils.add_cmd def showuser(irc, source, args): """ Admin-only. Shows information about .""" checkauthenticated(irc, source) try: target = args[0] except IndexError: utils.msg(irc, source, "Error: not enough arguments. Needs 1: nick.") return u = utils.nickToUid(irc, target) if u is None: utils.msg(irc, source, 'Error: unknown user %r' % target) return s = ['\x02%s\x02: %s' % (k, v) for k, v in sorted(irc.users[u].__dict__.items())] s = 'Information on user \x02%s\x02: %s' % (target, '; '.join(s)) utils.msg(irc, source, s) @utils.add_cmd def showchan(irc, source, args): """ Admin-only. Shows information about .""" checkauthenticated(irc, source) try: channel = args[0].lower() except IndexError: utils.msg(irc, source, "Error: not enough arguments. Needs 1: channel.") return if channel not in irc.channels: utils.msg(irc, source, 'Error: unknown channel %r' % channel) return s = ['\x02%s\x02: %s' % (k, v) for k, v in sorted(irc.channels[channel].__dict__.items())] s = 'Information on channel \x02%s\x02: %s' % (channel, '; '.join(s)) utils.msg(irc, source, s) @utils.add_cmd def mode(irc, source, args): """ Admin-only. Sets modes on from , where is either the nick of a PyLink client, or the SID of a PyLink server.""" checkauthenticated(irc, source) try: modesource, target, modes = args[0], args[1], args[2:] except IndexError: utils.msg(irc, source, 'Error: not enough arguments. Needs 3: source nick, target, modes to set.') return if not modes: utils.msg(irc, source, "Error: no modes given to set!") return parsedmodes = utils.parseModes(irc, target, modes) targetuid = utils.nickToUid(irc, target) if targetuid: target = targetuid elif not utils.isChannel(target): utils.msg(irc, source, "Error: Invalid channel or nick %r." % target) return if utils.isInternalServer(irc, modesource): irc.proto.modeServer(irc, modesource, target, parsedmodes) irc.callHooks([modesource, 'PYLINK_ADMIN_MODE', {'target': target, 'modes': parsedmodes, 'parse_as': 'MODE'}]) else: sourceuid = utils.nickToUid(irc, modesource) irc.proto.modeClient(irc, sourceuid, target, parsedmodes) irc.callHooks([sourceuid, 'PYLINK_ADMIN_MODE', {'target': target, 'modes': parsedmodes, 'parse_as': 'MODE'}]) @utils.add_cmd def msg(irc, source, args): """ Admin-only. Sends message from , where is the nick of a PyLink client.""" checkauthenticated(irc, source) try: msgsource, target, text = args[0], args[1], ' '.join(args[2:]) except IndexError: utils.msg(irc, source, 'Error: not enough arguments. Needs 3: source nick, target, text.') return sourceuid = utils.nickToUid(irc, msgsource) if not sourceuid: utils.msg(irc, source, 'Error: unknown user %r' % msgsource) return if not utils.isChannel(target): real_target = utils.nickToUid(irc, target) if real_target is None: utils.msg(irc, source, 'Error: unknown user %r' % target) return else: real_target = target if not text: utils.msg(irc, source, 'Error: no text given.') return irc.proto.messageClient(irc, sourceuid, real_target, text) irc.callHooks([sourceuid, 'PYLINK_ADMIN_MSG', {'target': real_target, 'text': text, 'parse_as': 'PRIVMSG'}])