1#! /usr/bin/env python
2# -*- coding: utf-8 -*-
3
4"""
5This file is part of cpe package.
6
7This module contains the common characteristics of
8any name matching algorithm, associated with a version of Common Platform
9Enumeration (CPE) specification.
10
11Copyright (C) 2013  Alejandro Galindo García, Roberto Abdelkader Martínez Pérez
12
13This program is free software: you can redistribute it and/or modify
14it under the terms of the GNU Lesser General Public License as published by
15the Free Software Foundation, either version 3 of the License, or
16(at your option) any later version.
17
18This program is distributed in the hope that it will be useful,
19but WITHOUT ANY WARRANTY; without even the implied warranty of
20MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21GNU Lesser General Public License for more details.
22
23You should have received a copy of the GNU Lesser General Public License
24along with this program. If not, see <http://www.gnu.org/licenses/>.
25
26For any problems using the cpe package, or general questions and
27feedback about it, please contact:
28
29- Alejandro Galindo García: galindo.garcia.alejandro@gmail.com
30- Roberto Abdelkader Martínez Pérez: robertomartinezp@gmail.com
31"""
32
33from .cpe import CPE
34from .comp.cpecomp import CPEComponent
35
36
37class CPESet(object):
38    """
39    Represents a set of CPE Names.
40
41    This class allows:
42
43        - create a set of CPE Names.
44        - match a CPE Name against a set of CPE Names.
45    """
46
47    ####################
48    #  OBJECT METHODS  #
49    ####################
50
51    def __getitem__(self, i):
52        """
53        Returns the i'th CPE Name of set.
54
55        :param int i: CPE Name index to find
56        :returns: CPE Name found
57        :rtype: CPE
58        :exception: IndexError - list index out of range
59        """
60
61        return self.K[i]
62
63    def __init__(self):
64        """
65        Creates an empty set of CPE Names.
66
67        :returns: None
68        """
69        self.K = []
70
71    def __len__(self):
72        """
73        Returns the count of CPE Names of set.
74
75        :returns: count of components of CPE Name
76        :rtype: int
77
78        TEST: empty set
79
80        >>> from .cpeset1_1 import CPESet1_1
81        >>> s = CPESet1_1()
82        >>> len(s)
83        0
84        """
85
86        return len(self.K)
87
88    def __str__(self):
89        """
90        Returns a human-readable representation of CPE set.
91
92        :returns: Representation of CPE set as string
93        :rtype: string
94        """
95
96        setlen = self.__len__()
97
98        str = []
99        str.append("CPE Set version {0} contains {1} elements".format(
100            self.VERSION, setlen))
101        if setlen > 0:
102            str.append(":")
103
104            for i in range(0, setlen):
105                str.append("    {0}".format(self.K[i].__str__()))
106
107        return "\n".join(str)
108
109    def append(self, cpe):
110        """
111        Adds a CPE Name to the set if not already.
112
113        :param CPE cpe: CPE Name to store in set
114        :returns: None
115        :exception: NotImplementedError - Method not implemented
116        """
117
118        errmsg = "Method not implemented. Use the method of some child class"
119        raise NotImplementedError(errmsg)
120
121    def name_match(self, cpe):
122        """
123        Accepts a set of known instances of CPE Names and a candidate CPE Name,
124        and returns 'True' if the candidate can be shown to be
125        an instance based on the content of the known instances.
126        Otherwise, it returns 'False'.
127
128        :param CPESet self: A set of m known CPE Names K = {K1, K2, …, Km}.
129        :param CPE cpe: A candidate CPE Name X.
130        :returns: True if X matches K, otherwise False.
131        :rtype: boolean
132        """
133
134        # An empty set not matching with any CPE
135        if len(self) == 0:
136            return False
137
138        # If input CPE Name string is in set of CPE Name strings
139        # not do searching more because there is a matching
140        for k in self.K:
141            if (k.cpe_str == cpe.cpe_str):
142                return True
143
144        # If "cpe" is an empty CPE Name any system matches
145        if len(cpe) == 0:
146            return True
147
148        # There are not a CPE Name string in set equal to
149        # input CPE Name string
150        match = False
151
152        for p in CPE.CPE_PART_KEYS:
153            elems_cpe = cpe.get(p)
154            for ec in elems_cpe:
155                # Search of element of part of input CPE
156
157                # Each element ec of input cpe[p] is compared with
158                # each element ek of k[p] in set K
159
160                for k in self.K:
161                    if (len(k) >= len(cpe)):
162                        elems_k = k.get(p)
163
164                        for ek in elems_k:
165                            # Matching
166
167                            # Each component in element ec is compared with
168                            # each component in element ek
169                            for c in range(0, len(cpe)):
170                                key = CPEComponent.ordered_comp_parts[c]
171                                comp_cpe = ec.get(key)
172                                comp_k = ek.get(key)
173                                match = comp_k in comp_cpe
174
175                                if not match:
176                                    # Search compoment in another element ek[p]
177                                    break
178
179                                # Component analyzed
180
181                            if match:
182                                # Element matched
183                                break
184                        if match:
185                            break
186                # Next element in part in "cpe"
187
188                if not match:
189                    # cpe part not match with parts in set
190                    return False
191
192            # Next part in input CPE Name
193
194        # All parts in input CPE Name matched
195        return True
196