Passed
Push — master ( f94f6f...4a1327 )
by Patrick
03:06
created

Rule::get_function()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 19
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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