Passed
Push — master ( ce1111...6b0d98 )
by Patrick
02:53
created

Rule::eval_getElement()   B

Complexity

Conditions 7
Paths 11

Size

Total Lines 25
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

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