mirror of
https://github.com/Mikaela/Limnoria.git
synced 2024-12-19 00:22:50 +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) 2002-2004, Jeremiah Fincher
|
||||||
# Copyright (c) 2010, James McCoy
|
# Copyright (c) 2010,2015 James McCoy
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without
|
# 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:
|
if (irc, loweredNick) not in self._whois:
|
||||||
return
|
return
|
||||||
(replyIrc, replyMsg, d) = self._whois[(irc, loweredNick)]
|
(replyIrc, replyMsg, d) = self._whois[(irc, loweredNick)]
|
||||||
hostmask = '@'.join(d['311'].args[2:4])
|
d['318'] = msg
|
||||||
user = d['311'].args[-1]
|
s = ircutils.formatWhois(irc, d, caller=replyMsg.nick,
|
||||||
if '319' in d:
|
channel=replyMsg.args[0])
|
||||||
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)
|
|
||||||
replyIrc.reply(s)
|
replyIrc.reply(s)
|
||||||
del self._whois[(irc, loweredNick)]
|
del self._whois[(irc, loweredNick)]
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
###
|
###
|
||||||
# Copyright (c) 2002-2004, Jeremiah Fincher
|
# Copyright (c) 2002-2004, Jeremiah Fincher
|
||||||
# Copyright (c) 2010, James McCoy
|
# Copyright (c) 2010,2015 James McCoy
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without
|
# 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:
|
if (irc, loweredNick) not in self._whois:
|
||||||
return
|
return
|
||||||
(replyIrc, replyMsg, d) = self._whois[(irc, loweredNick)]
|
(replyIrc, replyMsg, d) = self._whois[(irc, loweredNick)]
|
||||||
hostmask = '@'.join(d['311'].args[2:4])
|
d['318'] = msg
|
||||||
user = d['311'].args[-1]
|
s = ircutils.formatWhois(irc, d, caller=replyMsg.nick,
|
||||||
if '319' in d:
|
channel=replyMsg.args[0])
|
||||||
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)
|
|
||||||
replyIrc.reply(s)
|
replyIrc.reply(s)
|
||||||
del self._whois[(irc, loweredNick)]
|
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) 2002-2005, Jeremiah Fincher
|
||||||
# Copyright (c) 2009,2011, James McCoy
|
# Copyright (c) 2009,2011,2015 James McCoy
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without
|
# Redistribution and use in source and binary forms, with or without
|
||||||
@ -338,6 +338,110 @@ def stripFormatting(s):
|
|||||||
s = stripUnderline(s)
|
s = stripUnderline(s)
|
||||||
return s.replace('\x0f', '').replace('\x0F', '')
|
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):
|
class FormatContext(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.reset()
|
self.reset()
|
||||||
|
Loading…
Reference in New Issue
Block a user