MAJOR=4
MINOR=16
DEVNAME=tty16
  "   ˆl–äY>” T†» ÕÀ5q°)‹FÿÂƒyÆÜÁÀ !Ð    # Authors: Karl MacMillan <kmacmillan@mentalrootkit.com>
#
# Copyright (C) 2006 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2 only
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#

"""
Classes and algorithms for matching requested access to access vectors.
"""

import itertools

from . import access
from . import objectmodel
from . import util


class Match(util.Comparison):
    def __init__(self, interface=None, dist=0):
        self.interface = interface
        self.dist = dist
        self.info_dir_change = False
        # when implementing __eq__ also __hash__ is needed on py2
        # if object is muttable __hash__ should be None
        self.__hash__ = None

    def _compare(self, other, method):
        try:
            a = (self.dist, self.info_dir_change)
            b = (other.dist, other.info_dir_change)
            return method(a, b)
        except (AttributeError, TypeError):
            # trying to compare to foreign type
            return NotImplemented

class MatchList:
    DEFAULT_THRESHOLD = 150
    def __init__(self):
        # Match objects that pass the threshold
        self.children = []
        # Match objects over the threshold
        self.bastards = []
        self.threshold = self.DEFAULT_THRESHOLD
        self.allow_info_dir_change = False
        self.av = None

    def best(self):
        if len(self.children):
            return self.children[0]
        if len(self.bastards):
            return self.bastards[0]
        return None

    def __len__(self):
        # Only return the length of the matches so
        # that this can be used to test if there is
        # a match.
        return len(self.children) + len(self.bastards)

    def __iter__(self):
        return iter(self.children)

    def all(self):
        return itertools.chain(self.children, self.bastards)

    def append(self, match):
        if match.dist <= self.threshold:
            if not match.info_dir_change or self.allow_info_dir_change:
                self.children.append(match)
            else:
                self.bastards.append(match)
        else:
            self.bastards.append(match)

    def sort(self):
        self.children.sort()
        self.bastards.sort()
                

class AccessMatcher:
    def __init__(self, perm_maps=None):
        self.type_penalty = 10
        self.obj_penalty = 10
        if perm_maps:
            self.perm_maps = perm_maps
        else:
            self.perm_maps = objectmodel.PermMappings()
        # We want a change in the information flow direction
        # to be a strong penalty - stronger than access to
        # a few unrelated types.
        self.info_dir_penalty = 100

    def type_distance(self, a, b):
        if a == b or access.is_idparam(b):
            return 0
        else:
            return -self.type_penalty


    def perm_distance(self, av_req, av_prov):
        # First check that we have enough perms
        diff = av_req.perms.difference(av_prov.perms)

        if len(diff) != 0:
            total = self.perm_maps.getdefault_distance(av_req.obj_class, diff)
            return -total
        else:
            diff = av_prov.perms.difference(av_req.perms)
            return self.perm_maps.getdefault_distance(av_req.obj_class, diff)

    def av_distance(self, req, prov):
        """Determine the 'distance' between 2 access vectors.

        This function is used to find an acces