2003-03-12 07:26:59 +01:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
###
|
|
|
|
# Copyright (c) 2002, Jeremiah Fincher
|
|
|
|
# All rights reserved.
|
|
|
|
#
|
|
|
|
# Redistribution and use in source and binary forms, with or without
|
|
|
|
# modification, are permitted provided that the following conditions are met:
|
|
|
|
#
|
|
|
|
# * Redistributions of source code must retain the above copyright notice,
|
|
|
|
# this list of conditions, and the following disclaimer.
|
|
|
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
# this list of conditions, and the following disclaimer in the
|
|
|
|
# documentation and/or other materials provided with the distribution.
|
|
|
|
# * Neither the name of the author of this software nor the name of
|
|
|
|
# contributors to this software may be used to endorse or promote products
|
|
|
|
# derived from this software without specific prior written consent.
|
|
|
|
#
|
|
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
# POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
###
|
|
|
|
|
|
|
|
from fix import *
|
|
|
|
|
2003-03-27 07:57:09 +01:00
|
|
|
import os
|
2003-07-31 08:20:58 +02:00
|
|
|
import sets
|
2003-03-12 07:26:59 +01:00
|
|
|
import time
|
|
|
|
import string
|
|
|
|
|
|
|
|
import conf
|
2003-04-03 10:29:21 +02:00
|
|
|
import debug
|
2003-09-12 22:06:58 +02:00
|
|
|
import utils
|
2003-03-12 07:26:59 +01:00
|
|
|
import world
|
|
|
|
import ircutils
|
|
|
|
|
|
|
|
def fromChannelCapability(capability):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""Returns a (channel, capability) tuple from a channel capability."""
|
2003-09-06 20:32:10 +02:00
|
|
|
if not isChannelCapability(capability):
|
|
|
|
raise ValueError, '%s is not a channel capability' % capability
|
2003-09-22 11:18:13 +02:00
|
|
|
#return capability.rsplit('.', 1)
|
|
|
|
return rsplit(capability, '.', 1)
|
2003-03-12 07:26:59 +01:00
|
|
|
|
|
|
|
def isChannelCapability(capability):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""Returns True if capability is a channel capability; False otherwise."""
|
2003-03-12 07:26:59 +01:00
|
|
|
if '.' in capability:
|
2003-04-20 23:52:53 +02:00
|
|
|
(channel, capability) = capability.split('.', 1)
|
2003-03-12 07:26:59 +01:00
|
|
|
return ircutils.isChannel(channel)
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
|
|
|
def makeChannelCapability(channel, capability):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""Makes a channel capability given a channel and a capability."""
|
2003-03-12 07:26:59 +01:00
|
|
|
return '%s.%s' % (channel, capability)
|
|
|
|
|
|
|
|
def isAntiCapability(capability):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""Returns True if capability is an anticapability; False otherwise."""
|
2003-03-12 07:26:59 +01:00
|
|
|
if isChannelCapability(capability):
|
|
|
|
(_, capability) = fromChannelCapability(capability)
|
2003-10-05 13:13:20 +02:00
|
|
|
return capability[0] == '-'
|
2003-03-12 07:26:59 +01:00
|
|
|
|
|
|
|
def makeAntiCapability(capability):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""Returns the anticapability of a given capability."""
|
|
|
|
assert not isAntiCapability(capability), 'makeAntiCapability does not ' \
|
|
|
|
'work on anticapabilities; you probably want invertCapability.'
|
2003-03-12 07:26:59 +01:00
|
|
|
if '.' in capability:
|
|
|
|
(channel, capability) = fromChannelCapability(capability)
|
2003-10-05 13:13:20 +02:00
|
|
|
return '%s.-%s' % (channel, capability)
|
2003-03-12 07:26:59 +01:00
|
|
|
else:
|
2003-10-05 13:13:20 +02:00
|
|
|
return '-' + capability
|
2003-03-12 07:26:59 +01:00
|
|
|
|
2003-04-20 23:52:53 +02:00
|
|
|
def unAntiCapability(capability):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""Takes an anticapability and returns the non-anti form."""
|
2003-09-06 20:32:10 +02:00
|
|
|
if not isAntiCapability(capability):
|
|
|
|
raise ValueError, '%s is not an anti capability' % capability
|
2003-04-20 23:52:53 +02:00
|
|
|
if isChannelCapability(capability):
|
|
|
|
(channel, capability) = fromChannelCapability(capability)
|
|
|
|
return '.'.join((channel, capability[1:]))
|
|
|
|
else:
|
|
|
|
return capability[1:]
|
|
|
|
|
|
|
|
def invertCapability(capability):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""Make a capability into an anticapability and vice versa."""
|
2003-04-20 23:52:53 +02:00
|
|
|
if isAntiCapability(capability):
|
|
|
|
return unAntiCapability(capability)
|
|
|
|
else:
|
|
|
|
return makeAntiCapability(capability)
|
|
|
|
|
2003-03-12 07:26:59 +01:00
|
|
|
_normal = string.maketrans('\r\n', ' ')
|
2003-09-13 19:29:56 +02:00
|
|
|
def _normalize(s):
|
2003-03-12 07:26:59 +01:00
|
|
|
return s.translate(_normal)
|
|
|
|
|
|
|
|
|
2003-07-31 08:20:58 +02:00
|
|
|
class CapabilitySet(sets.Set):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""A subclass of set handling basic capability stuff."""
|
2003-04-20 23:52:53 +02:00
|
|
|
def __init__(self, capabilities=()):
|
2003-07-31 08:20:58 +02:00
|
|
|
sets.Set.__init__(self)
|
2003-04-20 23:52:53 +02:00
|
|
|
for capability in capabilities:
|
|
|
|
self.add(capability)
|
|
|
|
|
|
|
|
def add(self, capability):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Adds a capability to the set."""
|
2003-04-20 23:52:53 +02:00
|
|
|
capability = ircutils.toLower(capability)
|
|
|
|
inverted = invertCapability(capability)
|
2003-07-31 08:20:58 +02:00
|
|
|
if sets.Set.__contains__(self, inverted):
|
|
|
|
sets.Set.remove(self, inverted)
|
|
|
|
sets.Set.add(self, capability)
|
2003-04-20 23:52:53 +02:00
|
|
|
|
|
|
|
def remove(self, capability):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Removes a capability from the set."""
|
2003-04-20 23:52:53 +02:00
|
|
|
capability = ircutils.toLower(capability)
|
2003-07-31 08:20:58 +02:00
|
|
|
sets.Set.remove(self, capability)
|
2003-04-20 23:52:53 +02:00
|
|
|
|
|
|
|
def __contains__(self, capability):
|
|
|
|
capability = ircutils.toLower(capability)
|
2003-07-31 08:20:58 +02:00
|
|
|
if sets.Set.__contains__(self, capability):
|
2003-04-20 23:52:53 +02:00
|
|
|
return True
|
2003-07-31 08:20:58 +02:00
|
|
|
if sets.Set.__contains__(self, invertCapability(capability)):
|
2003-04-20 23:52:53 +02:00
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
|
|
|
def check(self, capability):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Returns the appropriate boolean for whether a given capability is
|
|
|
|
'allowed' given its (or its anticapability's) presence in the set.
|
|
|
|
"""
|
2003-04-20 23:52:53 +02:00
|
|
|
capability = ircutils.toLower(capability)
|
2003-07-31 08:20:58 +02:00
|
|
|
if sets.Set.__contains__(self, capability):
|
2003-04-20 23:52:53 +02:00
|
|
|
return True
|
2003-07-31 08:20:58 +02:00
|
|
|
elif sets.Set.__contains__(self, invertCapability(capability)):
|
2003-04-20 23:52:53 +02:00
|
|
|
return False
|
|
|
|
else:
|
|
|
|
raise KeyError, capability
|
|
|
|
|
2003-10-04 14:00:56 +02:00
|
|
|
def __repr__(self):
|
2003-10-05 13:42:58 +02:00
|
|
|
return '%s([%s])' % (self.__class__.__name__,
|
|
|
|
', '.join(map(repr, self)))
|
2003-04-20 23:52:53 +02:00
|
|
|
|
2003-10-05 13:42:58 +02:00
|
|
|
antiOwner = makeAntiCapability('owner')
|
2003-04-20 23:52:53 +02:00
|
|
|
class UserCapabilitySet(CapabilitySet):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""A subclass of CapabilitySet to handle the owner capability correctly."""
|
2003-04-20 23:52:53 +02:00
|
|
|
def __contains__(self, capability):
|
|
|
|
capability = ircutils.toLower(capability)
|
2003-10-05 13:42:58 +02:00
|
|
|
if capability == 'owner' or capability == antiOwner:
|
|
|
|
return True
|
|
|
|
elif CapabilitySet.__contains__(self, 'owner'):
|
2003-04-20 23:52:53 +02:00
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return CapabilitySet.__contains__(self, capability)
|
|
|
|
|
|
|
|
def check(self, capability):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Returns the appropriate boolean for whether a given capability is
|
|
|
|
'allowed' given its (or its anticapability's) presence in the set.
|
|
|
|
Differs from CapabilitySet in that it handles the 'owner' capability
|
|
|
|
appropriately.
|
|
|
|
"""
|
2003-04-20 23:52:53 +02:00
|
|
|
capability = ircutils.toLower(capability)
|
2003-10-05 13:42:58 +02:00
|
|
|
if capability == 'owner' or capability == antiOwner:
|
2003-04-20 23:52:53 +02:00
|
|
|
if CapabilitySet.__contains__(self, 'owner'):
|
2003-10-05 13:42:58 +02:00
|
|
|
return not isAntiCapability(capability)
|
2003-04-20 23:52:53 +02:00
|
|
|
else:
|
2003-10-05 13:42:58 +02:00
|
|
|
return isAntiCapability(capability)
|
|
|
|
elif CapabilitySet.__contains__(self, 'owner'):
|
2003-04-20 23:52:53 +02:00
|
|
|
if isAntiCapability(capability):
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return CapabilitySet.check(self, capability)
|
|
|
|
|
|
|
|
def add(self, capability):
|
2003-10-05 13:13:20 +02:00
|
|
|
"""Adds a capability to the set. Just make sure it's not -owner."""
|
2003-04-20 23:52:53 +02:00
|
|
|
capability = ircutils.toLower(capability)
|
2003-10-05 13:13:20 +02:00
|
|
|
assert capability != '-owner', '"-owner" disallowed.'
|
2003-04-20 23:52:53 +02:00
|
|
|
CapabilitySet.add(self, capability)
|
2003-08-20 18:26:23 +02:00
|
|
|
|
2003-03-12 07:26:59 +01:00
|
|
|
class IrcUser(object):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""This class holds the capabilities and authentications for a user."""
|
2003-09-12 22:06:58 +02:00
|
|
|
def __init__(self, ignore=False, password='', name='',
|
2003-10-03 11:57:52 +02:00
|
|
|
capabilities=(), hostmasks=None, secure=False):
|
2003-09-12 22:06:58 +02:00
|
|
|
self.auth = None # The (time, hostmask) a user authenticated under
|
|
|
|
self.name = name # The name of the user.
|
2003-03-12 07:26:59 +01:00
|
|
|
self.ignore = ignore # A boolean deciding if the person is ignored.
|
2003-10-03 11:57:52 +02:00
|
|
|
self.secure = secure # A boolean describing if hostmasks *must* match.
|
2003-03-12 07:26:59 +01:00
|
|
|
self.password = password # password (plaintext? hashed?)
|
2003-04-20 23:52:53 +02:00
|
|
|
self.capabilities = UserCapabilitySet()
|
|
|
|
for capability in capabilities:
|
|
|
|
self.capabilities.add(capability)
|
2003-03-12 07:26:59 +01:00
|
|
|
if hostmasks is None:
|
|
|
|
self.hostmasks = [] # A list of hostmasks used for recognition
|
|
|
|
else:
|
|
|
|
self.hostmasks = hostmasks
|
|
|
|
|
|
|
|
def __repr__(self):
|
2003-09-12 22:06:58 +02:00
|
|
|
return '%s(ignore=%s, password=%r, name=%r, '\
|
2003-10-03 11:57:52 +02:00
|
|
|
'capabilities=%r, hostmasks=%r, secure=%r)\n' %\
|
2003-09-12 22:06:58 +02:00
|
|
|
(self.__class__.__name__, self.ignore, self.password,
|
2003-10-03 11:57:52 +02:00
|
|
|
self.name, self.capabilities, self.hostmasks, self.secure)
|
2003-03-12 07:26:59 +01:00
|
|
|
|
|
|
|
def addCapability(self, capability):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Gives the user the given capability."""
|
2003-03-12 07:26:59 +01:00
|
|
|
self.capabilities.add(capability)
|
|
|
|
|
|
|
|
def removeCapability(self, capability):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Takes from the user the given capability."""
|
2003-09-23 19:04:53 +02:00
|
|
|
self.capabilities.remove(capability)
|
2003-03-12 07:26:59 +01:00
|
|
|
|
|
|
|
def checkCapability(self, capability):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Checks the user for a given capability."""
|
2003-04-20 23:52:53 +02:00
|
|
|
if self.ignore:
|
2003-03-12 07:26:59 +01:00
|
|
|
if isAntiCapability(capability):
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
else:
|
2003-04-20 23:52:53 +02:00
|
|
|
return self.capabilities.check(capability)
|
2003-03-12 07:26:59 +01:00
|
|
|
|
|
|
|
def setPassword(self, password):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Sets the user's password."""
|
2003-03-12 07:26:59 +01:00
|
|
|
self.password = password
|
|
|
|
|
|
|
|
def checkPassword(self, password):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Checks the user's password."""
|
2003-03-12 07:26:59 +01:00
|
|
|
return (self.password == password)
|
|
|
|
|
2003-10-03 11:57:52 +02:00
|
|
|
def checkHostmask(self, hostmask, useAuth=True):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Checks a given hostmask against the user's hostmasks or current
|
|
|
|
authentication. If useAuth is False, only checks against the user's
|
|
|
|
hostmasks.
|
|
|
|
"""
|
2003-10-03 11:57:52 +02:00
|
|
|
if useAuth and self.auth and (hostmask == self.auth[1]):
|
2003-03-12 07:26:59 +01:00
|
|
|
return True
|
|
|
|
for pat in self.hostmasks:
|
|
|
|
if ircutils.hostmaskPatternEqual(pat, hostmask):
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
def addHostmask(self, hostmask):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Adds a hostmask to the user's hostmasks."""
|
2003-03-12 07:26:59 +01:00
|
|
|
self.hostmasks.append(hostmask)
|
|
|
|
|
|
|
|
def removeHostmask(self, hostmask):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Removes a hostmask from the user's hostmasks."""
|
2003-09-14 04:52:40 +02:00
|
|
|
self.hostmasks.remove(hostmask)
|
2003-03-12 07:26:59 +01:00
|
|
|
|
|
|
|
def setAuth(self, hostmask):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Sets a user's authenticated hostmask. This times out in 1 hour."""
|
2003-03-12 07:26:59 +01:00
|
|
|
self.auth = (time.time(), hostmask)
|
|
|
|
|
|
|
|
def unsetAuth(self):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Unsets a use's authenticated hostmask."""
|
2003-03-12 07:26:59 +01:00
|
|
|
self.auth = None
|
|
|
|
|
|
|
|
|
|
|
|
class IrcChannel(object):
|
|
|
|
"""This class holds the capabilities, bans, and ignores of a channel.
|
|
|
|
"""
|
2003-04-02 13:08:34 +02:00
|
|
|
defaultOff = ('op', 'halfop', 'voice', 'protected')
|
2003-03-12 07:26:59 +01:00
|
|
|
def __init__(self, bans=None, ignores=None, capabilities=None,
|
|
|
|
lobotomized=False, defaultAllow=True):
|
|
|
|
self.defaultAllow = defaultAllow
|
|
|
|
if bans is None:
|
|
|
|
self.bans = []
|
|
|
|
else:
|
|
|
|
self.bans = bans
|
|
|
|
if ignores is None:
|
|
|
|
self.ignores = []
|
|
|
|
else:
|
|
|
|
self.ignores = ignores
|
|
|
|
if capabilities is None:
|
2003-04-20 23:52:53 +02:00
|
|
|
self.capabilities = CapabilitySet()
|
2003-03-12 07:26:59 +01:00
|
|
|
else:
|
|
|
|
self.capabilities = capabilities
|
|
|
|
for capability in self.defaultOff:
|
|
|
|
if capability not in self.capabilities:
|
2003-04-02 13:08:34 +02:00
|
|
|
self.capabilities.add(makeAntiCapability(capability))
|
2003-03-12 07:26:59 +01:00
|
|
|
self.lobotomized = lobotomized
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return '%s(bans=%r, ignores=%r, capabilities=%r, '\
|
|
|
|
'lobotomized=%r, defaultAllow=%s)\n' %\
|
|
|
|
(self.__class__.__name__, self.bans, self.ignores,
|
|
|
|
self.capabilities, self.lobotomized,
|
|
|
|
self.defaultAllow)
|
|
|
|
|
|
|
|
def addBan(self, hostmask):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Adds a ban to the channel banlist."""
|
2003-03-12 07:26:59 +01:00
|
|
|
self.bans.append(hostmask)
|
|
|
|
|
|
|
|
def removeBan(self, hostmask):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Removes a ban from the channel banlist."""
|
2003-03-12 07:26:59 +01:00
|
|
|
self.bans = [s for s in self.bans if s != hostmask]
|
|
|
|
|
|
|
|
def checkBan(self, hostmask):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Checks whether a given hostmask is banned by the channel banlist."""
|
2003-03-12 07:26:59 +01:00
|
|
|
for pat in self.bans:
|
|
|
|
if ircutils.hostmaskPatternEqual(pat, hostmask):
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
def addIgnore(self, hostmask):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Adds an ignore to the channel ignore list."""
|
2003-03-12 07:26:59 +01:00
|
|
|
self.ignores.append(hostmask)
|
|
|
|
|
|
|
|
def removeIgnore(self, hostmask):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Removes an ignore from the channel ignore list."""
|
2003-03-12 07:26:59 +01:00
|
|
|
self.ignores = [s for s in self.ignores if s != hostmask]
|
|
|
|
|
2003-04-02 13:08:34 +02:00
|
|
|
def addCapability(self, capability):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Adds a capability to the channel's default capabilities."""
|
2003-04-02 13:08:34 +02:00
|
|
|
self.capabilities.add(capability)
|
2003-03-12 07:26:59 +01:00
|
|
|
|
|
|
|
def removeCapability(self, capability):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Removes a capability from the channel's default capabilities."""
|
2003-04-02 13:08:34 +02:00
|
|
|
self.capabilities.remove(capability)
|
2003-03-12 07:26:59 +01:00
|
|
|
|
2003-04-02 13:08:34 +02:00
|
|
|
def setDefaultCapability(self, b):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Sets the default capability in the channel."""
|
2003-04-02 13:08:34 +02:00
|
|
|
self.defaultAllow = b
|
2003-03-12 07:26:59 +01:00
|
|
|
|
|
|
|
def checkCapability(self, capability):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Checks whether a certain capability is allowed by the channel."""
|
2003-03-12 07:26:59 +01:00
|
|
|
if capability in self.capabilities:
|
2003-04-20 23:52:53 +02:00
|
|
|
return self.capabilities.check(capability)
|
2003-03-12 07:26:59 +01:00
|
|
|
else:
|
2003-04-20 23:52:53 +02:00
|
|
|
if isAntiCapability(capability):
|
|
|
|
return not self.defaultAllow
|
2003-04-02 13:08:34 +02:00
|
|
|
else:
|
|
|
|
return self.defaultAllow
|
2003-03-12 07:26:59 +01:00
|
|
|
|
|
|
|
def checkIgnored(self, hostmask):
|
2003-10-04 14:00:56 +02:00
|
|
|
"""Checks whether a given hostmask is to be ignored by the channel."""
|
2003-03-12 07:26:59 +01:00
|
|
|
if self.lobotomized:
|
|
|
|
return True
|
|
|
|
for mask in self.bans:
|
|
|
|
if ircutils.hostmaskPatternEqual(mask, hostmask):
|
|
|
|
return True
|
|
|
|
for mask in self.ignores:
|
|
|
|
if ircutils.hostmaskPatternEqual(mask, hostmask):
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2003-09-12 22:06:58 +02:00
|
|
|
class UsersDB(object):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""A simple serialized-to-file User Database."""
|
2003-03-12 07:26:59 +01:00
|
|
|
def __init__(self, filename):
|
|
|
|
self.filename = filename
|
2003-09-12 22:06:58 +02:00
|
|
|
if os.path.exists(filename):
|
|
|
|
fd = file(filename, 'r')
|
|
|
|
s = fd.read()
|
|
|
|
fd.close()
|
|
|
|
IrcSet = ircutils.IrcSet
|
2003-09-13 19:29:56 +02:00
|
|
|
(self.nextId, self.users) = eval(_normalize(s))
|
2003-03-12 07:26:59 +01:00
|
|
|
else:
|
2003-09-12 22:06:58 +02:00
|
|
|
self.nextId = 1
|
|
|
|
self.users = [IrcUser(capabilities=['owner'],
|
|
|
|
password=utils.mktemp())]
|
|
|
|
self._nameCache = {}
|
|
|
|
self._hostmaskCache = {}
|
|
|
|
|
|
|
|
def reload(self):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""Reloads the database from its file."""
|
2003-09-12 22:06:58 +02:00
|
|
|
self.__init__(self.filename)
|
|
|
|
|
|
|
|
def flush(self):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""Flushes the database to its file."""
|
2003-09-12 22:06:58 +02:00
|
|
|
fd = file(self.filename, 'w')
|
|
|
|
fd.write(repr((self.nextId, self.users)))
|
|
|
|
fd.close()
|
2003-03-12 07:26:59 +01:00
|
|
|
|
2003-09-12 22:06:58 +02:00
|
|
|
def getUserId(self, s):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""Returns the user ID of a given name or hostmask."""
|
2003-03-12 07:26:59 +01:00
|
|
|
if ircutils.isUserHostmask(s):
|
2003-04-08 09:28:45 +02:00
|
|
|
try:
|
2003-09-12 22:06:58 +02:00
|
|
|
return self._hostmaskCache[s]
|
2003-04-08 09:28:45 +02:00
|
|
|
except KeyError:
|
2003-09-12 22:06:58 +02:00
|
|
|
ids = []
|
|
|
|
for (id, user) in enumerate(self.users):
|
|
|
|
if user is None:
|
|
|
|
continue
|
|
|
|
if user.checkHostmask(s):
|
|
|
|
ids.append(id)
|
|
|
|
if len(ids) == 1:
|
|
|
|
id = ids[0]
|
|
|
|
self._hostmaskCache[s] = id
|
|
|
|
self._hostmaskCache.setdefault(id, sets.Set()).add(s)
|
|
|
|
return id
|
|
|
|
elif len(ids) == 0:
|
|
|
|
raise KeyError, s
|
|
|
|
else:
|
|
|
|
raise ValueError, 'Ids %r matched.' % ids
|
|
|
|
else: # Not a hostmask, must be a name.
|
2003-09-14 09:05:41 +02:00
|
|
|
s = s.lower()
|
2003-09-12 22:06:58 +02:00
|
|
|
try:
|
|
|
|
return self._nameCache[s]
|
|
|
|
except KeyError:
|
|
|
|
for (id, user) in enumerate(self.users):
|
|
|
|
if user is None:
|
|
|
|
continue
|
2003-09-14 09:05:41 +02:00
|
|
|
if s == user.name.lower():
|
2003-09-12 22:06:58 +02:00
|
|
|
self._nameCache[s] = id
|
2003-09-14 09:05:41 +02:00
|
|
|
self._nameCache[id] = s
|
2003-09-12 22:06:58 +02:00
|
|
|
return id
|
|
|
|
else:
|
|
|
|
raise KeyError, s
|
2003-03-12 07:26:59 +01:00
|
|
|
|
2003-09-12 22:06:58 +02:00
|
|
|
def getUser(self, id):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""Returns a user given its id, name, or hostmask."""
|
2003-09-12 22:06:58 +02:00
|
|
|
if not isinstance(id, int):
|
|
|
|
# Must be a string. Get the UserId first.
|
|
|
|
id = self.getUserId(id)
|
|
|
|
try:
|
|
|
|
ret = self.users[id]
|
|
|
|
if ret is None:
|
|
|
|
raise KeyError, id
|
|
|
|
return ret
|
|
|
|
except IndexError:
|
|
|
|
raise KeyError, id
|
|
|
|
|
|
|
|
def hasUser(self, id):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""Returns the database has a user given its id, name, or hostmask."""
|
2003-09-12 22:06:58 +02:00
|
|
|
try:
|
|
|
|
self.getUser(id)
|
|
|
|
return True
|
|
|
|
except KeyError:
|
|
|
|
return False
|
2003-03-12 07:26:59 +01:00
|
|
|
|
2003-09-12 22:06:58 +02:00
|
|
|
def setUser(self, id, user):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""Sets a user (given its id) to the IrcUser given it."""
|
2003-09-12 22:06:58 +02:00
|
|
|
assert isinstance(id, int), 'setUser takes an integer userId.'
|
|
|
|
if not 0 <= id < len(self.users) or self.users[id] is None:
|
|
|
|
raise KeyError, id
|
2003-03-31 11:26:51 +02:00
|
|
|
try:
|
2003-09-12 22:06:58 +02:00
|
|
|
if self.getUserId(user.name) != id:
|
|
|
|
raise ValueError, \
|
|
|
|
'%s is already registered to someone else.' % user.name
|
2003-03-31 11:26:51 +02:00
|
|
|
except KeyError:
|
|
|
|
pass
|
2003-09-12 22:06:58 +02:00
|
|
|
for hostmask in user.hostmasks:
|
|
|
|
try:
|
|
|
|
if self.getUserId(hostmask) != id:
|
|
|
|
raise ValueError, \
|
|
|
|
'%s is already registered to someone else.'% hostmask
|
|
|
|
except KeyError:
|
|
|
|
continue
|
|
|
|
if id in self._nameCache:
|
2003-09-14 09:05:41 +02:00
|
|
|
del self._nameCache[self._nameCache[id]]
|
2003-09-12 22:06:58 +02:00
|
|
|
del self._nameCache[id]
|
|
|
|
if id in self._hostmaskCache:
|
|
|
|
for hostmask in self._hostmaskCache[id]:
|
|
|
|
del self._hostmaskCache[hostmask]
|
|
|
|
del self._hostmaskCache[id]
|
|
|
|
### FIXME: what if the new hostmasks overlap with another hostmask?
|
|
|
|
self.users[id] = user
|
|
|
|
|
|
|
|
def delUser(self, id):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""Removes a user from the database."""
|
2003-09-12 22:06:58 +02:00
|
|
|
if not 0 <= id < len(self.users) or self.users[id] is None:
|
|
|
|
raise KeyError, id
|
|
|
|
self.users[id] = None
|
2003-09-14 09:05:41 +02:00
|
|
|
if id in self._nameCache:
|
|
|
|
del self._nameCache[self._nameCache[id]]
|
|
|
|
del self._nameCache[id]
|
2003-09-18 12:25:20 +02:00
|
|
|
if id in self._hostmaskCache:
|
|
|
|
for hostmask in self._hostmaskCache[id]:
|
|
|
|
del self._hostmaskCache[hostmask]
|
|
|
|
del self._hostmaskCache[id]
|
2003-09-12 22:06:58 +02:00
|
|
|
|
|
|
|
def newUser(self):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""Allocates a new user in the database and returns it and its id."""
|
2003-09-12 22:06:58 +02:00
|
|
|
user = IrcUser()
|
|
|
|
id = self.nextId
|
|
|
|
self.nextId += 1
|
|
|
|
self.users.append(user)
|
|
|
|
return (id, user)
|
2003-03-12 07:26:59 +01:00
|
|
|
|
|
|
|
|
|
|
|
class ChannelsDictionary(object):
|
|
|
|
def __init__(self, filename):
|
|
|
|
self.filename = filename
|
2003-09-12 22:06:58 +02:00
|
|
|
if os.path.exists(filename):
|
|
|
|
fd = file(filename, 'r')
|
|
|
|
s = fd.read()
|
|
|
|
fd.close()
|
|
|
|
Set = sets.Set
|
2003-09-13 19:29:56 +02:00
|
|
|
self.dict = eval(_normalize(s))
|
2003-09-12 22:06:58 +02:00
|
|
|
else:
|
|
|
|
self.dict = {}
|
2003-03-12 07:26:59 +01:00
|
|
|
|
|
|
|
def getChannel(self, channel):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""Returns an IrcChannel object for the given channel."""
|
2003-03-12 07:26:59 +01:00
|
|
|
channel = channel.lower()
|
|
|
|
if channel in self.dict:
|
|
|
|
return self.dict[channel]
|
|
|
|
else:
|
|
|
|
c = IrcChannel()
|
|
|
|
self.dict[channel] = c
|
|
|
|
return c
|
|
|
|
|
|
|
|
def setChannel(self, channel, ircChannel):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""Sets a given channel to the IrcChannel object given."""
|
2003-03-12 07:26:59 +01:00
|
|
|
channel = channel.lower()
|
|
|
|
self.dict[channel] = ircChannel
|
|
|
|
|
|
|
|
def flush(self):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""Flushes the channel database to its file."""
|
2003-03-12 07:26:59 +01:00
|
|
|
fd = file(self.filename, 'w')
|
|
|
|
fd.write(repr(self.dict))
|
|
|
|
fd.close()
|
|
|
|
|
|
|
|
def reload(self):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""Reloads the channel database from its file."""
|
2003-03-12 07:26:59 +01:00
|
|
|
self.__init__(self.filename)
|
|
|
|
|
|
|
|
|
|
|
|
###
|
|
|
|
# Later, I might add some special handling for botnet.
|
|
|
|
###
|
2003-09-18 09:44:25 +02:00
|
|
|
users = UsersDB(os.path.join(conf.confDir, conf.userfile))
|
|
|
|
channels = ChannelsDictionary(os.path.join(conf.confDir, conf.channelfile))
|
2003-03-12 07:26:59 +01:00
|
|
|
|
|
|
|
world.flushers.append(users.flush)
|
|
|
|
world.flushers.append(channels.flush)
|
|
|
|
|
|
|
|
###
|
|
|
|
# Useful functions for checking credentials.
|
|
|
|
###
|
|
|
|
def checkIgnored(hostmask, recipient='', users=users, channels=channels):
|
|
|
|
"""checkIgnored(hostmask, recipient='') -> True/False
|
|
|
|
|
|
|
|
Checks if the user is ignored by the recipient of the message.
|
|
|
|
"""
|
|
|
|
for ignore in conf.ignores:
|
|
|
|
if ircutils.hostmaskPatternEqual(ignore, hostmask):
|
|
|
|
return True
|
|
|
|
try:
|
2003-09-12 22:06:58 +02:00
|
|
|
id = users.getUserId(hostmask)
|
|
|
|
user = users.getUser(id)
|
2003-03-12 07:26:59 +01:00
|
|
|
except KeyError:
|
|
|
|
# If there's no user...
|
|
|
|
if ircutils.isChannel(recipient):
|
|
|
|
channel = channels.getChannel(recipient)
|
|
|
|
return channel.checkIgnored(hostmask)
|
|
|
|
else:
|
|
|
|
return conf.defaultIgnore
|
|
|
|
if user.checkCapability('owner'):
|
|
|
|
# Owners shouldn't ever be ignored.
|
|
|
|
return False
|
|
|
|
elif user.ignore:
|
|
|
|
return True
|
|
|
|
elif recipient:
|
|
|
|
if ircutils.isChannel(recipient):
|
|
|
|
channel = channels.getChannel(recipient)
|
|
|
|
return channel.checkIgnored(hostmask)
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
2003-04-21 07:38:13 +02:00
|
|
|
def _x(capability, ret):
|
|
|
|
if isAntiCapability(capability):
|
|
|
|
return not ret
|
|
|
|
else:
|
|
|
|
return ret
|
2003-08-20 18:26:23 +02:00
|
|
|
|
2003-10-04 00:19:22 +02:00
|
|
|
def _checkCapabilityForUnknownUser(capability, users=users, channels=channels):
|
|
|
|
if isChannelCapability(capability):
|
|
|
|
#debug.printf('isChannelCapability true.')
|
|
|
|
(channel, capability) = fromChannelCapability(capability)
|
|
|
|
try:
|
|
|
|
c = channels.getChannel(channel)
|
|
|
|
if capability in c.capabilities:
|
|
|
|
#debug.printf('capability in c.capabilities')
|
|
|
|
return c.checkCapability(capability)
|
|
|
|
else:
|
|
|
|
#debug.printf('capability not in c.capabilities')
|
|
|
|
return _x(capability, c.defaultAllow)
|
|
|
|
except KeyError:
|
|
|
|
#debug.printf('no such channel %s' % channel)
|
|
|
|
pass
|
|
|
|
if capability in conf.defaultCapabilities:
|
|
|
|
#debug.printf('capability in conf.defaultCapability')
|
|
|
|
return True
|
|
|
|
elif invertCapability(capability) in conf.defaultCapabilities:
|
|
|
|
#debug.printf('inverse capability in conf.defaultCapability')
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
#debug.printf('returning appropriate value given no good reason')
|
|
|
|
return _x(capability, conf.defaultAllow)
|
|
|
|
|
2003-03-12 07:26:59 +01:00
|
|
|
def checkCapability(hostmask, capability, users=users, channels=channels):
|
2003-09-13 19:29:56 +02:00
|
|
|
"""Checks that the user specified by name/hostmask has the capabilty given.
|
|
|
|
"""
|
2003-04-21 07:23:31 +02:00
|
|
|
#debug.printf('*** checking %s for %s' % (hostmask, capability))
|
2003-03-26 00:42:37 +01:00
|
|
|
if world.startup:
|
2003-04-21 07:23:31 +02:00
|
|
|
#debug.printf('world.startup is active.')
|
2003-04-21 07:38:13 +02:00
|
|
|
return _x(capability, True)
|
2003-03-12 07:26:59 +01:00
|
|
|
try:
|
2003-09-13 19:29:56 +02:00
|
|
|
u = users.getUser(hostmask)
|
2003-10-03 11:57:52 +02:00
|
|
|
if u.secure and not u.checkHostmask(hostmask, useAuth=False):
|
|
|
|
debug.printf('Secure user with non-matching hostmask.')
|
|
|
|
raise KeyError
|
2003-04-20 23:52:53 +02:00
|
|
|
except KeyError:
|
2003-10-04 00:19:22 +02:00
|
|
|
# Raised when no hostmasks match.
|
|
|
|
return _checkCapabilityForUnknownUser(capability, users=users,
|
|
|
|
channels=channels)
|
|
|
|
except ValueError, e:
|
|
|
|
# Raised when multiple hostmasks match.
|
|
|
|
debug.msg('%s: %s' % (hostmask, e))
|
|
|
|
return _checkCapabilityForUnknownUser(capability, users=users,
|
|
|
|
channels=channels)
|
2003-04-21 07:23:31 +02:00
|
|
|
#debug.printf('user found.')
|
2003-04-20 23:52:53 +02:00
|
|
|
if capability in u.capabilities:
|
2003-04-21 07:23:31 +02:00
|
|
|
#debug.printf('found capability in u.capabilities.')
|
2003-04-20 23:52:53 +02:00
|
|
|
return u.checkCapability(capability)
|
|
|
|
else:
|
|
|
|
if isChannelCapability(capability):
|
2003-04-21 07:23:31 +02:00
|
|
|
#debug.printf('isChannelCapability true, user found too.')
|
2003-04-20 23:52:53 +02:00
|
|
|
(channel, capability) = fromChannelCapability(capability)
|
|
|
|
try:
|
2003-09-13 14:59:22 +02:00
|
|
|
chanop = makeChannelCapability(channel, 'op')
|
|
|
|
if u.checkCapability(chanop):
|
|
|
|
return _x(capability, True)
|
2003-04-20 23:52:53 +02:00
|
|
|
except KeyError:
|
|
|
|
pass
|
2003-09-13 14:59:22 +02:00
|
|
|
c = channels.getChannel(channel)
|
|
|
|
if capability in c.capabilities:
|
|
|
|
#debug.printf('capability in c.capabilities')
|
|
|
|
return c.checkCapability(capability)
|
|
|
|
else:
|
|
|
|
#debug.printf('capability not in c.capabilities')
|
|
|
|
return _x(capability, c.defaultAllow)
|
2003-04-20 23:52:53 +02:00
|
|
|
if capability in conf.defaultCapabilities:
|
2003-04-21 07:23:31 +02:00
|
|
|
#debug.printf('capability in conf.defaultCapabilities')
|
2003-03-12 07:26:59 +01:00
|
|
|
return True
|
2003-04-20 23:52:53 +02:00
|
|
|
elif invertCapability(capability) in conf.defaultCapabilities:
|
2003-04-21 07:23:31 +02:00
|
|
|
#debug.printf('inverse capability in conf.defaultCapabilities')
|
2003-04-20 23:52:53 +02:00
|
|
|
return False
|
2003-04-02 13:08:34 +02:00
|
|
|
else:
|
2003-04-21 07:23:31 +02:00
|
|
|
#debug.printf('returning appropriate value given no good reason')
|
2003-04-21 07:38:13 +02:00
|
|
|
return _x(capability, conf.defaultAllow)
|
2003-08-20 18:26:23 +02:00
|
|
|
|
2003-03-12 07:26:59 +01:00
|
|
|
|
|
|
|
def checkCapabilities(hostmask, capabilities, requireAll=False):
|
|
|
|
"""Checks that a user has capabilities in a list.
|
|
|
|
|
|
|
|
requireAll is the True if *all* capabilities in the list must be had, False
|
|
|
|
if *any* of the capabilities in the list must be had.
|
|
|
|
"""
|
|
|
|
for capability in capabilities:
|
|
|
|
if requireAll:
|
|
|
|
if not checkCapability(hostmask, capability):
|
|
|
|
return False
|
|
|
|
else:
|
|
|
|
if checkCapability(hostmask, capability):
|
|
|
|
return True
|
|
|
|
if requireAll:
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
2003-03-28 08:38:16 +01:00
|
|
|
|
2003-03-24 09:41:19 +01:00
|
|
|
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:
|