diff --git a/plugins/admin.py b/plugins/admin.py index 8362a10..57a2b3d 100644 --- a/plugins/admin.py +++ b/plugins/admin.py @@ -62,6 +62,7 @@ def quit(irc, source, args): 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. @@ -82,6 +83,9 @@ def joinclient(irc, source, args): 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 @@ -103,6 +107,7 @@ def nick(irc, source, args): 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): @@ -123,6 +128,7 @@ def part(irc, source, args): 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): @@ -144,6 +150,7 @@ def kick(irc, source, args): 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): @@ -205,9 +212,11 @@ def mode(irc, source, args): 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): @@ -235,3 +244,4 @@ def msg(irc, source, args): 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'}]) diff --git a/plugins/relay.py b/plugins/relay.py index 07bba7d..006e97b 100644 --- a/plugins/relay.py +++ b/plugins/relay.py @@ -61,7 +61,7 @@ def normalizeNick(irc, netname, nick, separator=None, oldnick=''): nick += suffix # FIXME: factorize while utils.nickToUid(irc, nick) or utils.nickToUid(irc, oldnick) and not \ - utils.isInternalClient(irc, utils.nickToUid(irc, nick)): + isRelayClient(irc, utils.nickToUid(irc, nick)): # The nick we want exists? Darn, create another one then, but only if # the target isn't an internal client! # Increase the separator length by 1 if the user was already tagged, @@ -281,6 +281,9 @@ utils.add_hook(handle_nick, 'NICK') def handle_part(irc, numeric, command, args): channels = args['channels'] text = args['text'] + # Don't allow the PyLink client PARTing to be relayed. + if numeric == irc.pseudoclient.uid: + return for channel in channels: for netname, user in relayusers[(irc.name, numeric)].copy().items(): remoteirc = utils.networkobjects[netname] @@ -358,7 +361,8 @@ def handle_kick(irc, source, command, args): kicker = source kicker_modes = getPrefixModes(irc, irc, channel, kicker) relay = findRelay((irc.name, channel)) - if relay is None: + # Don't allow kicks to the PyLink client to be relayed. + if relay is None or target == irc.pseudoclient.uid: return for name, remoteirc in utils.networkobjects.items(): if irc.name == name or not remoteirc.connected.is_set(): @@ -369,7 +373,7 @@ def handle_kick(irc, source, command, args): continue real_kicker = getRemoteUser(irc, remoteirc, kicker, spawnIfMissing=False) log.debug('(%s) Relay kick: real kicker for %s on %s is %s', irc.name, kicker, name, real_kicker) - if not utils.isInternalClient(irc, target): + if not isRelayClient(irc, target): log.debug('(%s) Relay kick: target %s is NOT an internal client', irc.name, target) # Both the target and kicker are external clients; i.e. # they originate from the same network. We won't have @@ -397,6 +401,8 @@ def handle_kick(irc, source, command, args): "(half)opped." % channel, notice=True) return + if not real_target: + return # Propogate the kick! if real_kicker: log.debug('(%s) Relay kick: Kicking %s from channel %s via %s on behalf of %s/%s', irc.name, real_target, remotechan,real_kicker, kicker, irc.name) @@ -417,7 +423,7 @@ def handle_kick(irc, source, command, args): remoteirc.proto.kickServer(remoteirc, remoteirc.sid, remotechan, real_target, text) - if target != irc.pseudoclient.uid and not irc.users[target].channels: + if isRelayClient(irc, target) and not irc.users[target].channels: remoteuser = getLocalUser(irc, target) del relayusers[remoteuser][irc.name] irc.proto.quitClient(irc, target, 'Left all shared channels.') @@ -632,6 +638,16 @@ def handle_kill(irc, numeric, command, args): utils.add_hook(handle_kill, 'KILL') +def isRelayClient(irc, user): + try: + if irc.users[user].remote: + # Is the .remote attribute set? If so, don't relay already + # relayed clients; that'll trigger an endless loop! + return True + except (KeyError, AttributeError): # Nope, it isn't. + pass + return False + def relayJoins(irc, channel, users, ts): for name, remoteirc in utils.networkobjects.items(): queued_users = [] @@ -645,17 +661,10 @@ def relayJoins(irc, channel, users, ts): continue log.debug('(%s) relayJoins: got %r for users', irc.name, users) for user in users.copy(): - if utils.isInternalClient(irc, user) or user not in irc.users: - # We don't need to clone PyLink pseudoclients... That's - # meaningless. + if isRelayClient(irc, user): + # Don't clone relay clients; that'll cause some bad, bad + # things to happen. continue - try: - if irc.users[user].remote: - # Is the .remote attribute set? If so, don't relay already - # relayed clients; that'll trigger an endless loop! - continue - except AttributeError: # Nope, it isn't. - pass log.debug('Okay, spawning %s/%s everywhere', user, irc.name) assert user in irc.users, "(%s) How is this possible? %r isn't in our user database." % (irc.name, user) u = getRemoteUser(irc, remoteirc, user) @@ -686,7 +695,7 @@ def relayPart(irc, channel, user): if remotechan is None or remoteuser is None: continue remoteirc.proto.partClient(remoteirc, remoteuser, remotechan, 'Channel delinked.') - if not remoteirc.users[remoteuser].channels: + if isRelayClient(remoteirc, remoteuser) and not remoteirc.users[remoteuser].channels: remoteirc.proto.quitClient(remoteirc, remoteuser, 'Left all shared channels.') del relayusers[(irc.name, user)][remoteirc.name] @@ -698,7 +707,7 @@ def removeChannel(irc, channel): relay = findRelay((irc.name, channel)) if relay: for user in irc.channels[channel].users.copy(): - if not utils.isInternalClient(irc, user): + if not isRelayClient(irc, user): relayPart(irc, channel, user) # Don't ever part the main client from any of its autojoin channels. else: @@ -893,7 +902,7 @@ def handle_save(irc, numeric, command, args): realuser = getLocalUser(irc, target) log.debug('(%s) relay handle_save: %r got in a nick collision! Real user: %r', irc.name, target, realuser) - if utils.isInternalClient(irc, target) and realuser: + if isRelayClient(irc, target) and realuser: # Nick collision! # It's one of our relay clients; try to fix our nick to the next # available normalized nick.