Passed
Push — master ( 278791...f4c14e )
by Patrick
01:54
created

Rule::check_negate_first()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
c 0
b 0
f 0
nc 2
nop 2
dl 0
loc 10
rs 10
1
<?php
2
3
namespace Trapdirector;
4
5
use Trapdirector\Logging;
6
use Trapdirector\Database;
7
use PDO;
8
use Exception;
9
use Icinga\Module\TrapDirector\Config\TrapModuleConfig;
10
11
class Rule
12
{
13
    
14
    protected $logging; //< logging class
15
    
16
    /**
17
     * Setup Rule Class
18
     * @param Logging $logClass : where to log
19
     */
20
    function __construct($logClass)
21
    {
22
        $this->logging=$logClass;
23
24
    }
25
26
/**
27
 * Get full number 
28
 * @return array<number,string>
29
 */
30
    private function get_number($rule,&$item)
31
    {
32
        $item2=$item+1;
33
        while (
34
            ($item2!=strlen($rule)) 
35
            && (preg_match('/[0-9\.]/',$rule[$item2]))) 
36
        { 
37
            $item2++ ;
38
        }
39
        $val=substr($rule,$item,$item2-$item);
40
        $item=$item2;
41
        //echo "number ".$val."\n";
42
        
43
        return array(0,$val);
44
    }
45
46
    private function get_string($rule,&$item)
47
    {
48
        $item++;
49
        $item2=$this->eval_getNext($rule,$item,'"');
50
        $val=substr($rule,$item,$item2-$item-1);
51
        $item=$item2;
52
        //echo "string : ".$val."\n";
53
        return array(1,$val);
54
        
55
    }
56
    
57
    private function get_group($rule,&$item)
58
    {
59
        $item++;
60
        $start=$item;
61
        $parenthesis_count=0;
62
        while (($item < strlen($rule)) // Not end of string AND
63
            && ( ($rule[$item] != ')' ) || $parenthesis_count > 0) ) // Closing ')' or embeded ()
64
        {
65
            if ($rule[$item] == '"' )
66
            { // pass through string
67
                $item++;
68
                $item=$this->eval_getNext($rule,$item,'"');
69
            }
70
            else{
71
                if ($rule[$item] == '(')
72
                {
73
                    $parenthesis_count++;
74
                }
75
                if ($rule[$item] == ')')
76
                {
77
                    $parenthesis_count--;
78
                }
79
                $item++;
80
            }
81
        }
82
        
83
        if ($item==strlen($rule)) {throw new Exception("no closing () in ".$rule ." at " .$item);}
84
        $val=substr($rule,$start,$item-$start);
85
        $item++;
86
        $start=0;
87
        //echo "group : ".$val."\n";
88
        // returns evaluation of group as type 2 (boolean)
89
        return array(2,$this->evaluation($val,$start));
90
    }
91
    
92
    protected function eval_getElement($rule,&$item)
93
    {
94
        if ($item >= strlen($rule))
95
        {
96
            throw new Exception("Early end of string ".$rule ." at " .$item );
97
        }
98
        while ($rule[$item]==' ') $item++;
99
        if (preg_match('/[0-9\.]/',$rule[$item]))
100
        { // number
101
            return $this->get_number($rule, $item);
102
        }
103
        if ($rule[$item] == '"')
104
        { // string
105
            return $this->get_string($rule, $item);
106
        }
107
        
108
        if ($rule[$item] == '(')
109
        { // grouping
110
            return $this->get_group($rule, $item);
111
        }
112
        throw new Exception("number/string not found in ".$rule ." at " .$item . ' : ' .$rule[$item]);
113
        
114
    }
115
    
116
    protected function eval_getNext($rule,$item,$tok)
117
    {
118
        while (
119
            ($rule[$item] != $tok ) 
120
            && ($item < strlen($rule))) 
121
        { 
122
            $item++;
123
        }
124
        if ($item==strlen($rule)) {
125
            throw new Exception("closing '".$tok."' not found in ".$rule ." at " .$item);
126
        }
127
        return $item+1;
128
    }
129
    
130
    protected function eval_getOper($rule,&$item)
131
    {
132
        while ($rule[$item]==' ') $item++;
133
        switch ($rule[$item])
134
        {
135
            case '<':
136
                if ($rule[$item+1]=='=') { $item+=2; return array(0,"<=");}
137
                $item++; return array(0,"<");
138
            case '>':
139
                if ($rule[$item+1]=='=') { $item+=2; return array(0,">=");}
140
                $item++; return array(0,">");
141
            case '=':
142
                $item++; return array(0,"=");
143
            case '!':
144
                if ($rule[$item+1]=='=') { $item+=2; return array(0,"!=");}
145
                throw new Exception("Erreur in expr - incorrect operator '!'  found in ".$rule ." at " .$item);
146
            case '~':
147
                $item++; return array(0,"~");
148
            case '|':
149
                $item++; return array(1,"|");
150
            case '&':
151
                $item++; return array(1,"&");
152
            default	:
153
                throw new Exception("Erreur in expr - operator not found in ".$rule ." at " .$item);
154
        }
155
    }
156
    
157
    private function check_negate_first($rule,&$item)
158
    {
159
        if ( $rule[$item] == '!') // If '!' found, negate next expression.
160
        {
161
            $item++;
162
            return true;
163
        }
164
        else
165
        {
166
            return false;
167
        }
168
    }
169
170
    private function do_compare($val1,$val2,$comp,$negate)
171
    {
172
        switch ($comp){
173
            case '<':	$retVal= ($val1 < $val2); break;
174
            case '<=':	$retVal= ($val1 <= $val2); break;
175
            case '>':	$retVal= ($val1 > $val2); break;
176
            case '>=':	$retVal= ($val1 >= $val2); break;
177
            case '=':	$retVal= ($val1 == $val2); break;
178
            case '!=':	$retVal= ($val1 != $val2); break;
179
            case '~':	$retVal= (preg_match('/'.preg_replace('/"/','',$val2).'/',$val1)); break;
180
            case '|':	$retVal= ($val1 || $val2); break;
181
            case '&':	$retVal= ($val1 && $val2); break;
182
            default:  throw new Exception("Error in expression - unknown comp : ".$comp);
183
        }
184
        if ($negate === true) $retVal = ! $retVal; // Inverse result if negate before expression
185
        
186
        return $retVal;
187
    }
188
    
189
    /** Evaluation : makes token and evaluate.
190
     *	Public function for expressions testing
191
     *	accepts : < > = <= >= !=  (typec = 0)
192
     *	operators : & | (typec=1)
193
     *	with : integers/float  (type 0) or strings "" (type 1) or results (type 2)
194
     *   comparison int vs strings will return null (error)
195
     *	return : bool or null on error
196
     */
197
    public function evaluation($rule,&$item)
198
    {
199
        //echo "Evaluation of ".substr($rule,$item)."\n";
200
        $negate=$this->check_negate_first($rule, $item);
201
        // First element : number, string or ()
202
        list($type1,$val1) = $this->eval_getElement($rule,$item);
203
        //echo "Elmt1: ".$val1."/".$type1." : ".substr($rule,$item)."\n";
204
        
205
        if ($item==strlen($rule)) // If only element, return value, but only boolean
206
        {
207
            if ($type1 != 2) throw new Exception("Cannot use num/string as boolean : ".$rule);
208
            if ($negate === true) $val1= ! $val1;
209
            return $val1;
210
        }
211
        
212
        // Second element : operator
213
        list($typec,$comp) = $this->eval_getOper($rule,$item);
214
        //echo "Comp : ".$comp." : ".substr($rule,$item)."\n";
215
        
216
        // Third element : number, string or ()
217
        if ( $rule[$item] == '!') // starts with a ! so evaluate whats next
218
        {
219
            $item++;
220
            if ($typec != 1) throw new Exception("Mixing boolean and comparison : ".$rule);
221
            $val2= ! $this->evaluation($rule,$item);
222
            $type2=2; // result is a boolean
223
        }
224
        else
225
        {
226
            list($type2,$val2) = $this->eval_getElement($rule,$item);
227
        }
228
        //echo "Elmt2: ".$val2."/".$type2." : ".substr($rule,$item)."\n";
229
        
230
        if ($type1!=$type2)  // cannot compare different types
231
        {
232
            throw new Exception("Cannot compare string & number : ".$rule);
233
        }
234
        if ($typec==1 && $type1 !=2) // cannot use & or | with string/number
235
        {
236
            throw new Exception("Cannot use boolean operators with string & number : ".$rule);
237
        }
238
        
239
        $retVal = $this->do_compare($val1, $val2, $comp, $negate);
240
        
241
        if ($item==strlen($rule)) return $retVal; // End of string : return evaluation
242
        // check for logical operator :
243
        switch ($rule[$item])
244
        {
245
            case '|':	$item++; return ($retVal || $this->evaluation($rule,$item) );
246
            case '&':	$item++; return ($retVal && $this->evaluation($rule,$item) );
247
            
248
            default:  throw new Exception("Erreur in expr - garbadge at end of expression : ".$rule[$item]);
249
        }
250
    }
251
    
252
    // Remove all whitespaces (when not quoted)
253
    public function eval_cleanup($rule)
254
    {
255
        $item=0;
256
        $rule2='';
257
        while ($item < strlen($rule))
258
        {
259
            if ($rule[$item]==' ') { $item++; continue; }
260
            if ($rule[$item]=='"')
261
            {
262
                $rule2.=$rule[$item];
263
                $item++;
264
                while (($item < strlen($rule)) && ($rule[$item]!='"') )
265
                {
266
                    $rule2.=$rule[$item];
267
                    $item++;
268
                }
269
                if ($item == strlen ($rule)) throw new Exception("closing '\"' not found in ".$rule ." at " .$item);
270
                $rule2.=$rule[$item];
271
                $item++;
272
                continue;
273
            }
274
            
275
            $rule2.=$rule[$item];
276
            $item++;
277
        }
278
        
279
        return $rule2;
280
    }
281
    
282
    /** Evaluation rule (uses eval_* functions recursively)
283
     *	@param string $rule : rule ( _OID(.1.3.6.1.4.1.8072.2.3.2.1)=_OID(.1.3.6.1.2.1.1.3.0) )
284
     *  @param array $oidList : OIDs values to sustitute.
285
     *	@return bool : true : rule match, false : rule don't match , throw exception on error.
286
     */
287
    
288
    public function eval_rule($rule,$oidList)
289
    {
290
        if ($rule==null || $rule == '') // Empty rule is always true
291
        {
292
            return true;
293
        }
294
        $matches=array();
295
        while (preg_match('/_OID\(([0-9\.\*]+)\)/',$rule,$matches) == 1)
296
        {
297
            $oid=$matches[1];
298
            $found=0;
299
            // ** replaced by .*
300
            $oidR=preg_replace('/\*\*/', '.*', $oid);
301
            // * replaced by [^.]*
302
            $oidR=preg_replace('/\*/', '[0-9]+', $oidR);
303
            
304
            // replace * with \* in oid for preg_replace
305
            $oid=preg_replace('/\*/', '\*', $oid);
306
            
307
            $this->logging->log('OID in rule : '.$oid.' / '.$oidR,DEBUG );
308
            
309
            foreach($oidList as $val)
310
            {
311
                if (preg_match("/^$oidR$/",$val->oid) == 1)
312
                {
313
                    if (!preg_match('/^[0-9]*\.?[0-9]+$/',$val->value))
314
                    { // If not a number, change " to ' and put " around it
315
                        $val->value=preg_replace('/"/',"'",$val->value);
316
                        $val->value='"'.$val->value.'"';
317
                    }
318
                    $rep=0;
319
                    $rule=preg_replace('/_OID\('.$oid.'\)/',$val->value,$rule,-1,$rep);
320
                    if ($rep==0)
321
                    {
322
                        $this->logging->log("Error in rule_eval",WARN,'');
323
                        return false;
324
                    }
325
                    $found=1;
326
                    break;
327
                }
328
            }
329
            if ($found==0)
330
            {	// OID not found : throw error
331
                throw new Exception('OID '.$oid.' not found in trap');
332
            }
333
        }
334
        $item=0;
335
        $rule=$this->eval_cleanup($rule);
336
        $this->logging->log('Rule after clenup: '.$rule,INFO );
337
        
338
        return  $this->evaluation($rule,$item);
339
    }
340
    
341
}