mirror of
https://github.com/Mikaela/Limnoria.git
synced 2024-11-19 08:59:27 +01:00
ircutils: Add formatWhois function
Parsing through the various WHOIS replies to build a formatted string isn't a trivial task, especially since there is some privacy related information. Consolidate this handling into a single function so there's one place to fix bugs. Also fix an issue with people putting (unterminated) formatted text into the "realname" field of their IRC client (c.f., ProgVal/Limnoria#1083). Signed-off-by: James McCoy <vega.james@gmail.com>
This commit is contained in:
parent
a8cd99f121
commit
c3695c9419
@ -1,6 +1,6 @@
|
||||
###
|
||||
# Copyright (c) 2002-2004, Jeremiah Fincher
|
||||
# Copyright (c) 2010, James McCoy
|
||||
# Copyright (c) 2010,2015 James McCoy
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -158,87 +158,9 @@ class Network(callbacks.Plugin):
|
||||
if (irc, loweredNick) not in self._whois:
|
||||
return
|
||||
(replyIrc, replyMsg, d) = self._whois[(irc, loweredNick)]
|
||||
hostmask = '@'.join(d['311'].args[2:4])
|
||||
user = d['311'].args[-1]
|
||||
if '319' in d:
|
||||
channels = d['319'].args[-1].split()
|
||||
ops = []
|
||||
voices = []
|
||||
normal = []
|
||||
halfops = []
|
||||
for channel in channels:
|
||||
origchan = channel
|
||||
channel = channel.lstrip('@%+~!')
|
||||
# UnrealIRCd uses & for user modes and disallows it as a
|
||||
# channel-prefix, flying in the face of the RFC. Have to
|
||||
# handle this specially when processing WHOIS response.
|
||||
testchan = channel.lstrip('&')
|
||||
if testchan != channel and irc.isChannel(testchan):
|
||||
channel = testchan
|
||||
diff = len(channel) - len(origchan)
|
||||
modes = origchan[:diff]
|
||||
chan = irc.state.channels.get(channel)
|
||||
# The user is in a channel the bot is in, so the ircd may have
|
||||
# responded with otherwise private data.
|
||||
if chan:
|
||||
# Skip channels the callee isn't in. This helps prevents
|
||||
# us leaking information when the channel is +s or the
|
||||
# target is +i
|
||||
if replyMsg.nick not in chan.users:
|
||||
continue
|
||||
# Skip +s channels the target is in only if the reply isn't
|
||||
# being sent to that channel
|
||||
if 's' in chan.modes and \
|
||||
not ircutils.strEqual(replyMsg.args[0], channel):
|
||||
continue
|
||||
if not modes:
|
||||
normal.append(channel)
|
||||
elif utils.iter.any(lambda c: c in modes,('@', '&', '~', '!')):
|
||||
ops.append(channel[1:])
|
||||
elif utils.iter.any(lambda c: c in modes, ('%',)):
|
||||
halfops.append(channel[1:])
|
||||
elif utils.iter.any(lambda c: c in modes, ('+',)):
|
||||
voices.append(channel[1:])
|
||||
L = []
|
||||
if ops:
|
||||
L.append(format('is an op on %L', ops))
|
||||
if halfops:
|
||||
L.append(format('is a halfop on %L', halfops))
|
||||
if voices:
|
||||
L.append(format('is voiced on %L', voices))
|
||||
if normal:
|
||||
if L:
|
||||
L.append(format('is also on %L', normal))
|
||||
else:
|
||||
L.append(format('is on %L', normal))
|
||||
else:
|
||||
L = ['isn\'t on any non-secret channels']
|
||||
channels = format('%L', L)
|
||||
if '317' in d:
|
||||
idle = utils.timeElapsed(d['317'].args[2])
|
||||
signon = time.strftime(conf.supybot.reply.format.time(),
|
||||
time.localtime(float(d['317'].args[3])))
|
||||
else:
|
||||
idle = '<unknown>'
|
||||
signon = '<unknown>'
|
||||
if '312' in d:
|
||||
server = d['312'].args[2]
|
||||
else:
|
||||
server = '<unknown>'
|
||||
if '301' in d:
|
||||
away = ' %s is away: %s.' % (nick, d['301'].args[2])
|
||||
else:
|
||||
away = ''
|
||||
if '320' in d:
|
||||
if d['320'].args[2]:
|
||||
identify = ' identified'
|
||||
else:
|
||||
identify = ''
|
||||
else:
|
||||
identify = ''
|
||||
s = '%s (%s) has been%s on server %s since %s (idle for %s) and ' \
|
||||
'%s.%s' % (user, hostmask, identify, server, signon, idle,
|
||||
channels, away)
|
||||
d['318'] = msg
|
||||
s = ircutils.formatWhois(irc, d, caller=replyMsg.nick,
|
||||
channel=replyMsg.args[0])
|
||||
replyIrc.reply(s)
|
||||
del self._whois[(irc, loweredNick)]
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
###
|
||||
# Copyright (c) 2002-2004, Jeremiah Fincher
|
||||
# Copyright (c) 2010, James McCoy
|
||||
# Copyright (c) 2010,2015 James McCoy
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -196,64 +196,9 @@ class Relay(callbacks.Plugin):
|
||||
if (irc, loweredNick) not in self._whois:
|
||||
return
|
||||
(replyIrc, replyMsg, d) = self._whois[(irc, loweredNick)]
|
||||
hostmask = '@'.join(d['311'].args[2:4])
|
||||
user = d['311'].args[-1]
|
||||
if '319' in d:
|
||||
channels = d['319'].args[-1].split()
|
||||
ops = []
|
||||
voices = []
|
||||
normal = []
|
||||
halfops = []
|
||||
for channel in channels:
|
||||
if channel.startswith('@'):
|
||||
ops.append(channel[1:])
|
||||
elif channel.startswith('%'):
|
||||
halfops.append(channel[1:])
|
||||
elif channel.startswith('+'):
|
||||
voices.append(channel[1:])
|
||||
else:
|
||||
normal.append(channel)
|
||||
L = []
|
||||
if ops:
|
||||
L.append(format('is an op on %L', ops))
|
||||
if halfops:
|
||||
L.append(format('is a halfop on %L', halfups))
|
||||
if voices:
|
||||
L.append(format('is voiced on %L', voices))
|
||||
if normal:
|
||||
if L:
|
||||
L.append(format('is also on %L', normal))
|
||||
else:
|
||||
L.append(format('is on %L', normal))
|
||||
else:
|
||||
L = ['isn\'t on any non-secret channels']
|
||||
channels = format('%L', L)
|
||||
if '317' in d:
|
||||
idle = utils.timeElapsed(d['317'].args[2])
|
||||
signon = time.strftime(conf.supybot.reply.format.time(),
|
||||
time.localtime(float(d['317'].args[3])))
|
||||
else:
|
||||
idle = '<unknown>'
|
||||
signon = '<unknown>'
|
||||
if '312' in d:
|
||||
server = d['312'].args[2]
|
||||
else:
|
||||
server = '<unknown>'
|
||||
if '301' in d:
|
||||
away = format(' %s is away: %s.', nick, d['301'].args[2])
|
||||
else:
|
||||
away = ''
|
||||
if '320' in d:
|
||||
if d['320'].args[2]:
|
||||
identify = ' identified'
|
||||
else:
|
||||
identify = ''
|
||||
else:
|
||||
identify = ''
|
||||
s = format('%s (%s) has been%s on server %s since %s (idle for %s) '
|
||||
'and %s.%s',
|
||||
user, hostmask, identify, server, signon, idle,
|
||||
channels, away)
|
||||
d['318'] = msg
|
||||
s = ircutils.formatWhois(irc, d, caller=replyMsg.nick,
|
||||
channel=replyMsg.args[0])
|
||||
replyIrc.reply(s)
|
||||
del self._whois[(irc, loweredNick)]
|
||||
|
||||
|
106
src/ircutils.py
106
src/ircutils.py
@ -1,6 +1,6 @@
|
||||
###
|
||||
# Copyright (c) 2002-2005, Jeremiah Fincher
|
||||
# Copyright (c) 2009,2011, James McCoy
|
||||
# Copyright (c) 2009,2011,2015 James McCoy
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -338,6 +338,110 @@ def stripFormatting(s):
|
||||
s = stripUnderline(s)
|
||||
return s.replace('\x0f', '').replace('\x0F', '')
|
||||
|
||||
_containsFormattingRe = re.compile(r'[\x02\x03\x16\x1f]')
|
||||
def formatWhois(irc, replies, caller='', channel=''):
|
||||
"""Returns a string describing the target of a WHOIS command.
|
||||
|
||||
Arguments are:
|
||||
* irc: the irclib.Irc object on which the replies was received
|
||||
|
||||
* replies: a dict mapping the reply codes ('311', '312', etc.) to their
|
||||
corresponding ircmsg.IrcMsg
|
||||
|
||||
* caller: an optional nick specifying who requested the whois information
|
||||
|
||||
* channel: an optional channel specifying where the reply will be sent
|
||||
|
||||
If provided, caller and channel will be used to avoid leaking information
|
||||
that the caller/channel shouldn't be privy to.
|
||||
"""
|
||||
hostmask = '@'.join(replies['311'].args[2:4])
|
||||
nick = replies['318'].args[1]
|
||||
user = replies['311'].args[-1]
|
||||
if _containsFormattingRe.search(user) and user[-1] != '\x0f':
|
||||
# For good measure, disable any formatting
|
||||
user = '%s\x0f' % user
|
||||
if '319' in replies:
|
||||
channels = replies['319'].args[-1].split()
|
||||
ops = []
|
||||
voices = []
|
||||
normal = []
|
||||
halfops = []
|
||||
for chan in channels:
|
||||
origchan = chan
|
||||
chan = chan.lstrip('@%+~!')
|
||||
# UnrealIRCd uses & for user modes and disallows it as a
|
||||
# channel-prefix, flying in the face of the RFC. Have to
|
||||
# handle this specially when processing WHOIS response.
|
||||
testchan = chan.lstrip('&')
|
||||
if testchan != chan and irc.isChannel(testchan):
|
||||
chan = testchan
|
||||
diff = len(chan) - len(origchan)
|
||||
modes = origchan[:diff]
|
||||
chanState = irc.state.channels.get(chan)
|
||||
# The user is in a channel the bot is in, so the ircd may have
|
||||
# responded with otherwise private data.
|
||||
if chanState:
|
||||
# Skip channels the callee isn't in. This helps prevents
|
||||
# us leaking information when the channel is +s or the
|
||||
# target is +i
|
||||
if caller not in chanState.users:
|
||||
continue
|
||||
# Skip +s channels the target is in only if the reply isn't
|
||||
# being sent to that channel
|
||||
if 's' in chanState.modes and \
|
||||
not ircutils.strEqual(channel or '', chan):
|
||||
continue
|
||||
if not modes:
|
||||
normal.append(chan)
|
||||
elif utils.iter.any(lambda c: c in modes,('@', '&', '~', '!')):
|
||||
ops.append(chan[1:])
|
||||
elif utils.iter.any(lambda c: c in modes, ('%',)):
|
||||
halfops.append(chan[1:])
|
||||
elif utils.iter.any(lambda c: c in modes, ('+',)):
|
||||
voices.append(chan[1:])
|
||||
L = []
|
||||
if ops:
|
||||
L.append(format('is an op on %L', ops))
|
||||
if halfops:
|
||||
L.append(format('is a halfop on %L', halfops))
|
||||
if voices:
|
||||
L.append(format('is voiced on %L', voices))
|
||||
if normal:
|
||||
if L:
|
||||
L.append(format('is also on %L', normal))
|
||||
else:
|
||||
L.append(format('is on %L', normal))
|
||||
else:
|
||||
L = ['isn\'t on any non-secret channels']
|
||||
channels = format('%L', L)
|
||||
if '317' in replies:
|
||||
idle = utils.timeElapsed(replies['317'].args[2])
|
||||
signon = utils.str.timestamp(float(replies['317'].args[3]))
|
||||
else:
|
||||
idle = '<unknown>'
|
||||
signon = '<unknown>'
|
||||
if '312' in replies:
|
||||
server = replies['312'].args[2]
|
||||
else:
|
||||
server = '<unknown>'
|
||||
if '301' in replies:
|
||||
away = ' %s is away: %s.' % (nick, replies['301'].args[2])
|
||||
else:
|
||||
away = ''
|
||||
if '320' in replies:
|
||||
if replies['320'].args[2]:
|
||||
identify = ' identified'
|
||||
else:
|
||||
identify = ''
|
||||
else:
|
||||
identify = ''
|
||||
s = utils.str.format('%s (%s) has been%s on server %s since %s '
|
||||
'(idle for %s) and %s.%s',
|
||||
user, hostmask, identify, server, signon, idle,
|
||||
channels, away)
|
||||
return s
|
||||
|
||||
class FormatContext(object):
|
||||
def __init__(self):
|
||||
self.reset()
|
||||
|
Loading…
Reference in New Issue
Block a user