mirror of
https://github.com/Mikaela/Limnoria.git
synced 2024-11-23 02:49:27 +01:00
Merge branch 'maint/0.83.4'
Conflicts: plugins/Misc/plugin.py src/ircutils.py Signed-off-by: James McCoy <jamessan@users.sourceforge.net>
This commit is contained in:
commit
8b73f78b3e
@ -1,6 +1,6 @@
|
||||
###
|
||||
# Copyright (c) 2003-2005, Jeremiah Fincher
|
||||
# Copyright (c) 2010, James Vega
|
||||
# Copyright (c) 2010-2011, James Vega
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -53,7 +53,7 @@ class Internet(callbacks.Plugin):
|
||||
irc.reply(hostname)
|
||||
else:
|
||||
try:
|
||||
ip = socket.gethostbyname(host)
|
||||
ip = socket.getaddrinfo(host, None)[0][4][0]
|
||||
if ip == '64.94.110.11': # Verisign sucks!
|
||||
irc.reply('Host not found.')
|
||||
else:
|
||||
@ -149,12 +149,22 @@ class Internet(callbacks.Plugin):
|
||||
|
||||
Returns the hexadecimal IP for that IP.
|
||||
"""
|
||||
quads = ip.split('.')
|
||||
ret = ""
|
||||
for quad in quads:
|
||||
i = int(quad)
|
||||
ret += '%02x' % i
|
||||
irc.reply(ret.upper())
|
||||
if utils.net.isIPV4(ip):
|
||||
quads = ip.split('.')
|
||||
for quad in quads:
|
||||
i = int(quad)
|
||||
ret += '%02X' % i
|
||||
else:
|
||||
octets = ip.split(':')
|
||||
for octet in octets:
|
||||
if octet:
|
||||
i = int(octet, 16)
|
||||
ret += '%04X' % i
|
||||
else:
|
||||
missing = (8 - len(octets)) * 4
|
||||
ret += '0' * missing
|
||||
irc.reply(ret)
|
||||
hexip = wrap(hexip, ['ip'])
|
||||
|
||||
|
||||
|
@ -58,31 +58,34 @@ class Misc(callbacks.Plugin):
|
||||
assert not msg.repliedTo, 'repliedTo msg in Misc.invalidCommand.'
|
||||
assert self is irc.callbacks[-1], 'Misc isn\'t last callback.'
|
||||
self.log.debug('Misc.invalidCommand called (tokens %s)', tokens)
|
||||
# First, we check for invalidCommand floods. This is rightfully done
|
||||
# here since this will be the last invalidCommand called, and thus it
|
||||
# will only be called if this is *truly* an invalid command.
|
||||
maximum = conf.supybot.abuse.flood.command.invalid.maximum()
|
||||
self.invalidCommands.enqueue(msg)
|
||||
if self.invalidCommands.len(msg) > maximum and \
|
||||
conf.supybot.abuse.flood.command.invalid() and \
|
||||
not ircdb.checkCapability(msg.prefix, 'owner'):
|
||||
punishment = conf.supybot.abuse.flood.command.invalid.punishment()
|
||||
banmask = '*!%s@%s' % (msg.user, msg.host)
|
||||
self.log.info('Ignoring %s for %s seconds due to an apparent '
|
||||
'invalid command flood.', banmask, punishment)
|
||||
if tokens and tokens[0] == 'Error:':
|
||||
self.log.warning('Apparent error loop with another Supybot '
|
||||
'observed. Consider ignoring this bot '
|
||||
'permanently.')
|
||||
ircdb.ignores.add(banmask, time.time() + punishment)
|
||||
if conf.supybot.abuse.flood.command.invalid.notify():
|
||||
irc.reply('You\'ve given me %s invalid commands within the last '
|
||||
'minute; I\'m now ignoring you for %s.' %
|
||||
(maximum,
|
||||
utils.timeElapsed(punishment, seconds=False)))
|
||||
return
|
||||
# Now, for normal handling.
|
||||
channel = msg.args[0]
|
||||
# Only bother with the invaildCommand flood handling if it's actually
|
||||
# enabled
|
||||
if conf.supybot.abuse.flood.command.invalid():
|
||||
# First, we check for invalidCommand floods. This is rightfully done
|
||||
# here since this will be the last invalidCommand called, and thus it
|
||||
# will only be called if this is *truly* an invalid command.
|
||||
maximum = conf.supybot.abuse.flood.command.invalid.maximum()
|
||||
banmasker = conf.supybot.protocols.irc.banmask.makeBanmask
|
||||
self.invalidCommands.enqueue(msg)
|
||||
if self.invalidCommands.len(msg) > maximum and \
|
||||
not ircdb.checkCapability(msg.prefix, 'owner'):
|
||||
penalty = conf.supybot.abuse.flood.command.invalid.punishment()
|
||||
banmask = banmasker(msg.prefix)
|
||||
self.log.info('Ignoring %s for %s seconds due to an apparent '
|
||||
'invalid command flood.', banmask, penalty)
|
||||
if tokens and tokens[0] == 'Error:':
|
||||
self.log.warning('Apparent error loop with another Supybot '
|
||||
'observed. Consider ignoring this bot '
|
||||
'permanently.')
|
||||
ircdb.ignores.add(banmask, time.time() + penalty)
|
||||
if conf.supybot.abuse.flood.command.invalid.notify():
|
||||
irc.reply('You\'ve given me %s invalid commands within '
|
||||
'the last minute; I\'m now ignoring you for %s.' %
|
||||
(maximum,
|
||||
utils.timeElapsed(penalty, seconds=False)))
|
||||
return
|
||||
# Now, for normal handling.
|
||||
if conf.get(conf.supybot.reply.whenNotCommand, channel):
|
||||
if len(tokens) >= 2:
|
||||
cb = irc.getCallback(tokens[0])
|
||||
|
@ -262,8 +262,14 @@ class RSS(callbacks.Plugin):
|
||||
def _getConverter(self, feed):
|
||||
toText = utils.web.htmlToText
|
||||
if 'encoding' in feed:
|
||||
return lambda s: toText(s).strip().encode(feed['encoding'],
|
||||
'replace')
|
||||
def conv(s):
|
||||
# encode() first so there implicit encoding doesn't happen in
|
||||
# other functions when unicode and bytestring objects are used
|
||||
# together
|
||||
s = s.encode(feed['encoding'], 'replace')
|
||||
s = toText(s).strip()
|
||||
return s
|
||||
return conv
|
||||
else:
|
||||
return lambda s: toText(s).strip()
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
###
|
||||
# Copyright (c) 2002-2004, Jeremiah Fincher
|
||||
# Copyright (c) 2010, James Vega
|
||||
# Copyright (c) 2010-2011, James Vega
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -66,7 +66,7 @@ class SeenDB(plugins.ChannelUserDB):
|
||||
|
||||
def seenWildcard(self, channel, nick):
|
||||
nicks = ircutils.IrcSet()
|
||||
nickRe = re.compile('.*'.join(nick.split('*')), re.I)
|
||||
nickRe = re.compile('^%s$' % '.*'.join(nick.split('*')), re.I)
|
||||
for (searchChan, searchNick) in self.keys():
|
||||
#print 'chan: %s ... nick: %s' % (searchChan, searchNick)
|
||||
if isinstance(searchNick, int):
|
||||
@ -75,11 +75,8 @@ class SeenDB(plugins.ChannelUserDB):
|
||||
# are keyed by nick-string
|
||||
continue
|
||||
if ircutils.strEqual(searchChan, channel):
|
||||
try:
|
||||
s = nickRe.match(searchNick).group()
|
||||
except AttributeError:
|
||||
continue
|
||||
nicks.add(s)
|
||||
if nickRe.search(searchNick) is not None:
|
||||
nicks.add(searchNick)
|
||||
L = [[nick, self.seen(channel, nick)] for nick in nicks]
|
||||
def negativeTime(x):
|
||||
return -x[1][0]
|
||||
|
@ -244,7 +244,7 @@ class Services(callbacks.Plugin):
|
||||
# You have been unbanned from (oftc)
|
||||
irc.sendMsg(networkGroup.channels.join(channel))
|
||||
elif 'isn\'t registered' in s:
|
||||
self.log.warning('Received "%s isn\'t registered" from ChanServ %',
|
||||
self.log.warning('Received "%s isn\'t registered" from ChanServ %s',
|
||||
channel, on)
|
||||
elif 'this channel has been registered' in s:
|
||||
self.log.debug('Got "Registered channel" from ChanServ %s.', on)
|
||||
|
@ -79,7 +79,12 @@ class String(callbacks.Plugin):
|
||||
<http://docs.python.org/library/codecs.html#standard-encodings>.
|
||||
"""
|
||||
try:
|
||||
irc.reply(text.decode(encoding).encode('utf-8'))
|
||||
s = text.decode(encoding)
|
||||
# Not all encodings decode to a unicode object. Only encode those
|
||||
# that do.
|
||||
if isinstance(s, unicode):
|
||||
s = s.encode('utf-8')
|
||||
irc.reply(s)
|
||||
except LookupError:
|
||||
irc.errorInvalid('encoding', encoding)
|
||||
except binascii.Error:
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
###
|
||||
# Copyright (c) 2002-2005, Jeremiah Fincher
|
||||
# Copyright (c) 2011, James Vega
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -122,9 +123,10 @@ if __name__ == '__main__':
|
||||
parser.add_option('-c', '--clean', action='store_true', default=False,
|
||||
dest='clean', help='Cleans the various data/conf/logs'
|
||||
'directories before running tests.')
|
||||
parser.add_option('-t', '--timeout', action='store', type='int',
|
||||
parser.add_option('-t', '--timeout', action='store', type='float',
|
||||
dest='timeout',
|
||||
help='Sets the timeout for tests to return responses.')
|
||||
help='Sets the timeout, in seconds, for tests to return '
|
||||
'responses.')
|
||||
parser.add_option('-v', '--verbose', action='store_true', default=False,
|
||||
help='Sets the verbose flag, logging extra information '
|
||||
'about each test that runs.')
|
||||
|
@ -1,6 +1,6 @@
|
||||
###
|
||||
# Copyright (c) 2002-2005, Jeremiah Fincher
|
||||
# Copyright (c) 2008-2009, James Vega
|
||||
# Copyright (c) 2008-2009,2011, James Vega
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -1034,7 +1034,7 @@ registerGlobalValue(supybot, 'defaultIgnore',
|
||||
class IP(registry.String):
|
||||
"""Value must be a valid IP."""
|
||||
def setValue(self, v):
|
||||
if v and not (utils.net.isIP(v) or utils.net.isIPV6(v)):
|
||||
if v and not utils.net.isIP(v):
|
||||
self.error()
|
||||
else:
|
||||
registry.String.setValue(self, v)
|
||||
|
@ -46,6 +46,13 @@ import supybot.drivers as drivers
|
||||
import supybot.schedule as schedule
|
||||
from supybot.utils.iter import imap
|
||||
|
||||
try:
|
||||
import ssl
|
||||
except ImportError:
|
||||
drivers.log.debug('ssl module is not available, '
|
||||
'cannot connect to SSL servers.')
|
||||
ssl = None
|
||||
|
||||
class SocketDriver(drivers.IrcDriver, drivers.ServersMixin):
|
||||
def __init__(self, irc):
|
||||
self.irc = irc
|
||||
@ -61,12 +68,7 @@ class SocketDriver(drivers.IrcDriver, drivers.ServersMixin):
|
||||
self.writeCheckTime = None
|
||||
self.nextReconnectTime = None
|
||||
self.resetDelay()
|
||||
# Only connect to non-SSL servers
|
||||
if self.networkGroup.get('ssl').value:
|
||||
drivers.log.error('The Socket driver can not connect to SSL '
|
||||
'servers. Try the Twisted driver instead.')
|
||||
else:
|
||||
self.connect()
|
||||
self.connect()
|
||||
|
||||
def getDelay(self):
|
||||
ret = self.currentDelay
|
||||
@ -139,6 +141,12 @@ class SocketDriver(drivers.IrcDriver, drivers.ServersMixin):
|
||||
self.irc.feedMsg(msg)
|
||||
except socket.timeout:
|
||||
pass
|
||||
except ssl.SSLError, e:
|
||||
if e.args[0] == 'The read operation timed out':
|
||||
pass
|
||||
else:
|
||||
self._handleSocketError(e)
|
||||
return
|
||||
except socket.error, e:
|
||||
self._handleSocketError(e)
|
||||
return
|
||||
@ -163,6 +171,14 @@ class SocketDriver(drivers.IrcDriver, drivers.ServersMixin):
|
||||
drivers.log.connect(self.currentServer)
|
||||
try:
|
||||
self.conn = utils.net.getSocket(server[0])
|
||||
if self.networkGroup.get('ssl').value:
|
||||
if ssl:
|
||||
self.plainconn = self.conn
|
||||
self.conn = ssl.wrap_socket(self.conn)
|
||||
else:
|
||||
drivers.log.error('ssl module not available, '
|
||||
'cannot connect to SSL servers.')
|
||||
return
|
||||
vhost = conf.supybot.protocols.irc.vhost()
|
||||
self.conn.bind((vhost, 0))
|
||||
except socket.error, e:
|
||||
|
@ -405,10 +405,10 @@ class IrcState(IrcCommandDispatcher):
|
||||
"""Handles parsing the 004 reply
|
||||
|
||||
Supported user and channel modes are cached"""
|
||||
# msg.args = [nick, server, ircd-version, umodes, modes,
|
||||
# msg.args = [server, ircd-version, umodes, modes,
|
||||
# modes that require arguments? (non-standard)]
|
||||
self.supported['umodes'] = msg.args[3]
|
||||
self.supported['chanmodes'] = msg.args[4]
|
||||
self.supported['umodes'] = msg.args[2]
|
||||
self.supported['chanmodes'] = msg.args[3]
|
||||
|
||||
_005converters = utils.InsensitivePreservingDict({
|
||||
'modes': int,
|
||||
@ -929,14 +929,14 @@ class Irc(IrcCommandDispatcher):
|
||||
# Let's reset nicks in case we had to use a weird one.
|
||||
self.alternateNicks = conf.supybot.nick.alternates()[:]
|
||||
umodes = conf.supybot.protocols.irc.umodes()
|
||||
supported = self.supported.get('umodes')
|
||||
supported = self.state.supported.get('umodes')
|
||||
if umodes:
|
||||
addSub = '+'
|
||||
if umodes[0] in '+-':
|
||||
(addSub, umodes) = (umodes[0], umodes[1:])
|
||||
if supported:
|
||||
umodes = filter(lamda m: m in supported, umodes)
|
||||
umodes = ''.join(addSub, umodes)
|
||||
umodes = ''.join([m for m in umodes if m in supported])
|
||||
umodes = ''.join([addSub, umodes])
|
||||
log.info('Sending user modes to %s: %s', self.network, umodes)
|
||||
self.sendMsg(ircmsgs.mode(self.nick, umodes))
|
||||
do377 = do422 = do376
|
||||
|
@ -1,6 +1,6 @@
|
||||
###
|
||||
# Copyright (c) 2002-2005, Jeremiah Fincher
|
||||
# Copyright (c) 2009, James Vega
|
||||
# Copyright (c) 2009,2011, James Vega
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -187,7 +187,7 @@ def banmask(hostmask):
|
||||
"""
|
||||
assert isUserHostmask(hostmask)
|
||||
host = hostFromHostmask(hostmask)
|
||||
if utils.net.isIP(host):
|
||||
if utils.net.isIPV4(host):
|
||||
L = host.split('.')
|
||||
L[-1] = '*'
|
||||
return '*!*@' + '.'.join(L)
|
||||
@ -196,7 +196,7 @@ def banmask(hostmask):
|
||||
L[-1] = '*'
|
||||
return '*!*@' + ':'.join(L)
|
||||
else:
|
||||
if '.' in host:
|
||||
if len(host.split('.')) > 2: # If it is a subdomain
|
||||
return '*!*@*%s' % host[host.find('.'):]
|
||||
else:
|
||||
return '*!*@' + host
|
||||
@ -459,8 +459,8 @@ def replyTo(msg):
|
||||
|
||||
def dccIP(ip):
|
||||
"""Converts an IP string to the DCC integer form."""
|
||||
assert utils.net.isIP(ip), \
|
||||
'argument must be a string ip in xxx.xxx.xxx.xxx format.'
|
||||
assert utils.net.isIPV4(ip), \
|
||||
'argument must be a string ip in xxx.yyy.zzz.www format.'
|
||||
i = 0
|
||||
x = 256**3
|
||||
for quad in ip.split('.'):
|
||||
|
@ -1,5 +1,6 @@
|
||||
###
|
||||
# Copyright (c) 2002-2005, Jeremiah Fincher
|
||||
# Copyright (c) 2011, James Vega
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -54,6 +55,8 @@ network = True
|
||||
# This is the global list of suites that are to be run.
|
||||
suites = []
|
||||
|
||||
timeout = 10
|
||||
|
||||
originalCallbacksGetHelp = callbacks.getHelp
|
||||
lastGetHelp = 'x' * 1000
|
||||
def cachingGetHelp(method, name=None, doc=None):
|
||||
@ -110,12 +113,12 @@ class PluginTestCase(SupyTestCase):
|
||||
"""Subclass this to write a test case for a plugin. See
|
||||
plugins/Plugin/test.py for an example.
|
||||
"""
|
||||
timeout = 10
|
||||
plugins = None
|
||||
cleanConfDir = True
|
||||
cleanDataDir = True
|
||||
config = {}
|
||||
def __init__(self, methodName='runTest'):
|
||||
self.timeout = timeout
|
||||
originalRunTest = getattr(self, methodName)
|
||||
def runTest(self):
|
||||
run = True
|
||||
|
@ -1,5 +1,6 @@
|
||||
###
|
||||
# Copyright (c) 2002-2005, Jeremiah Fincher
|
||||
# Copyright (c) 2011, James Vega
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -44,7 +45,7 @@ def getSocket(host):
|
||||
"""
|
||||
addrinfo = socket.getaddrinfo(host, None)
|
||||
host = addrinfo[0][4][0]
|
||||
if isIP(host):
|
||||
if isIPV4(host):
|
||||
return socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
elif isIPV6(host):
|
||||
return socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
||||
@ -52,16 +53,27 @@ def getSocket(host):
|
||||
raise socket.error, 'Something wonky happened.'
|
||||
|
||||
def isIP(s):
|
||||
"""Returns whether or not a given string is an IPV4 address.
|
||||
"""Returns whether or not a given string is an IP address.
|
||||
|
||||
>>> isIP('255.255.255.255')
|
||||
1
|
||||
|
||||
>>> isIP('abc.abc.abc.abc')
|
||||
>>> isIP('::1')
|
||||
0
|
||||
"""
|
||||
return isIPV4(s) or isIPV6(s)
|
||||
|
||||
def isIPV4(s):
|
||||
"""Returns whether or not a given string is an IPV4 address.
|
||||
|
||||
>>> isIPV4('255.255.255.255')
|
||||
1
|
||||
|
||||
>>> isIPV4('abc.abc.abc.abc')
|
||||
0
|
||||
"""
|
||||
try:
|
||||
return bool(socket.inet_aton(s))
|
||||
return bool(socket.inet_pton(socket.AF_INET, s))
|
||||
except socket.error:
|
||||
return False
|
||||
|
||||
|
@ -290,6 +290,13 @@ class IrcStateTestCase(SupyTestCase):
|
||||
state.addMsg(self.irc, ircmsgs.IrcMsg(':irc.inet.tele.dk 005 adkwbot WALLCHOPS KNOCK EXCEPTS INVEX MODES=4 MAXCHANNELS=20 MAXBANS=beI:100 MAXTARGETS=4 NICKLEN=9 TOPICLEN=120 KICKLEN=90 :are supported by this server'))
|
||||
self.assertEqual(state.supported['maxbans'], 100)
|
||||
|
||||
def testSupportedUmodes(self):
|
||||
state = irclib.IrcState()
|
||||
state.addMsg(self.irc, ircmsgs.IrcMsg(':charm.oftc.net 004 charm.oftc.net hybrid-7.2.2+oftc1.6.8 CDGPRSabcdfgiklnorsuwxyz biklmnopstveI bkloveI'))
|
||||
self.assertEqual(state.supported['umodes'], 'CDGPRSabcdfgiklnorsuwxyz')
|
||||
self.assertEqual(state.supported['chanmodes'],
|
||||
'biklmnopstveI')
|
||||
|
||||
def testEmptyTopic(self):
|
||||
state = irclib.IrcState()
|
||||
state.addMsg(self.irc, ircmsgs.topic('#foo'))
|
||||
|
@ -233,6 +233,10 @@ class FunctionsTestCase(SupyTestCase):
|
||||
msg.prefix),
|
||||
'%r didn\'t match %r' % (msg.prefix, banmask))
|
||||
self.assertEqual(ircutils.banmask('foobar!user@host'), '*!*@host')
|
||||
self.assertEqual(ircutils.banmask('foobar!user@host.tld'),
|
||||
'*!*@host.tld')
|
||||
self.assertEqual(ircutils.banmask('foobar!user@sub.host.tld'),
|
||||
'*!*@*.host.tld')
|
||||
self.assertEqual(ircutils.banmask('foo!bar@2001::'), '*!*@2001::*')
|
||||
|
||||
def testSeparateModes(self):
|
||||
|
@ -1,6 +1,6 @@
|
||||
###
|
||||
# Copyright (c) 2002-2005, Jeremiah Fincher
|
||||
# Copyright (c) 2009, James Vega
|
||||
# Copyright (c) 2009,2011, James Vega
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -501,11 +501,9 @@ class NetTest(SupyTestCase):
|
||||
isIP = utils.net.isIP
|
||||
self.failIf(isIP('a.b.c'))
|
||||
self.failIf(isIP('256.0.0.0'))
|
||||
self.failUnless(isIP('127.1'))
|
||||
self.failUnless(isIP('0.0.0.0'))
|
||||
self.failUnless(isIP('100.100.100.100'))
|
||||
# This test is too flaky to bother with.
|
||||
# self.failUnless(utils.isIP('255.255.255.255'))
|
||||
self.failUnless(isIP('255.255.255.255'))
|
||||
|
||||
def testIsIPV6(self):
|
||||
f = utils.net.isIPV6
|
||||
|
Loading…
Reference in New Issue
Block a user