1<?php defined('SYSPATH') OR die('No direct access allowed.');
2
3/**
4 * Retrieves and manipulates current status of hosts (and services?)
5 */
6class Data_Model extends System_Model
7{
8
9    private $XML   = array();
10    public  $DS    = array();
11    public  $MACRO = array();
12    private $RRD   = array();
13    public  $STRUCT  = array();
14    public  $TIMERANGE  = array();
15    public  $PAGE_DEF   = array();
16    public  $PAGE_GRAPH = array();
17    public  $XPORT = "";
18    public  $TEMPLATE_FILE = "";
19    public  $GRAPH_TYPE = 'normal';
20    /*
21    *
22    *
23    */
24    public function __construct(){
25        $this->config = new Config_Model();
26        $this->config->read_config();
27        $this->auth = new Auth_Model();
28    }
29
30    /*
31    * Get All Special Templates
32    *
33    */
34    public function getSpecialTemplates(){
35        $conf = $this->config->conf;
36        $templates = array();
37        if (is_dir($conf['special_template_dir'])){
38            if ($dh = opendir($conf['special_template_dir'])) {
39                while (($file = readdir($dh)) !== false) {
40                    if ($file == "." || $file == "..")
41                        continue;
42                    if (!preg_match("/(.*)\.php$/", $file, $template))
43                        continue;
44                    $templates[] = $template[1];
45                }
46            }
47        }
48        if(empty($templates)){
49            return FALSE;
50        }else{
51            sort($templates);
52            return $templates;
53        }
54    }
55
56    public function getFirstSpecialTemplate(){
57        $templates = $this->getSpecialTemplates();
58        if($templates === FALSE){
59            return FALSE;
60        }else{
61            return $templates[0];
62        }
63    }
64    /*
65    *
66    *
67    */
68    public function getHosts() {
69        $hosts = array();
70        $conf = $this->config->conf;
71        $i = 0;
72            if (is_dir($conf['rrdbase'])) {
73                if ($dh = opendir($conf['rrdbase'])) {
74                    while (($file = readdir($dh)) !== false) {
75                        if ($file == "." || $file == ".." || $file == ".pnp-internal")
76                            continue;
77
78                        if (is_file($conf['rrdbase'] . "/" . $file) )
79                            continue;
80
81                        if($this->auth->is_authorized($file) === FALSE)
82                            continue;
83
84                        $stat = stat($conf['rrdbase'] . "/" . $file);
85                        $age = (time() - $stat['mtime']);
86                        $hosts[$i]['name'] = $file;
87                        $hosts[$i]['sort'] = strtoupper($file);
88                        if ($age < $conf['max_age']) {
89                            $hosts[$i]['state'] = 'active';
90                        } else {
91                            $hosts[$i]['state'] = 'inactive';
92                        }
93                $i++;
94                   }
95                   closedir($dh);
96               } else {
97                    throw new Kohana_User_Exception('Perfdata Dir', "Can not open $path");
98               }
99           }
100        if(empty($hosts)){
101            throw new Kohana_Exception('error.perfdata-dir-empty', $conf['rrdbase'] );
102        }else{
103            # Obtain a list of columns
104            foreach ($hosts as $key => $row) {
105                $sort[$key]  = $row['sort'];
106            }
107            # Sort the data with volume descending, edition ascending
108            # Add $data as the last parameter, to sort by the common key
109            array_multisort($sort, SORT_ASC, $hosts);
110        }
111        return $hosts;
112    }
113
114
115    /*
116    *
117    *
118    */
119    function getRawServices($hostname) {
120        $services = array ();
121        $host     = array();
122        $conf     = $this->config->conf;
123        $i        = 0;
124        $path     = $conf['rrdbase'] . $hostname;
125        if (is_dir($path)) {
126            if ($dh = opendir($path)) {
127                while ( ($file = readdir($dh) ) !== false) {
128                    if ($file == "." || $file == "..")
129                        continue;
130
131                    if (!preg_match("/(.*)\.xml$/", $file, $servicedesc))
132                        continue;
133
134                    $fullpath = $path . "/" . $file;
135                    $stat = stat("$fullpath");
136                    $age = (time() - $stat['mtime']);
137
138                    $state = "active";
139                    if ($age > $conf['max_age']) { # 6Stunden
140                        $state = "inactive";
141                    }
142                    $services[$i]['state'] = $state;
143                    $services[$i]['name'] = $servicedesc[1];
144                    $i++;
145                }
146            }
147        }else{
148            throw new Kohana_Exception('error.perfdata-dir-for-host', $path, $hostname );
149        }
150        if( is_array($services) && (!empty($services))){
151            # Obtain a list of columns
152            foreach ($services as $key => $row) {
153                $sort[$key]  = $row['name'];
154            }
155            # Sort the data with volume descending, edition ascending
156            # Add $data as the last parameter, to sort by the common key
157            array_multisort($sort, SORT_STRING, $services);
158        }else{
159            throw new Kohana_Exception('error.host-perfdata-dir-empty', $path, $hostname );
160        }
161        return $services;
162    }
163    /*
164    *
165    *
166    */
167    function getServices($hostname) {
168        $services     = array ();
169        $host         = array();
170        $i            = 0;
171        $service_list = $this->getRawServices($hostname);
172        foreach( $service_list as $s ){
173            if(!$this->readXML($hostname, $s['name'], FALSE)){
174                continue;
175            }
176            if($s['name'] == "_HOST_"){
177                // Check authorization
178                if($this->auth->is_authorized((string) $this->XML->NAGIOS_AUTH_HOSTNAME, "_HOST_") === FALSE)
179                    continue;
180
181                $host[0]['name']             = "_HOST_";
182                $host[0]['hostname']         = (string) $this->XML->NAGIOS_HOSTNAME;
183                $host[0]['state']            = $s['state'];
184                $host[0]['servicedesc']      = "Host Perfdata";
185                $host[0]['is_multi']         = (string) $this->XML->DATASOURCE[0]->IS_MULTI[0];
186            }else{
187                // Check authorization
188                if($this->auth->is_authorized((string) $this->XML->NAGIOS_AUTH_HOSTNAME, (string) $this->XML->NAGIOS_AUTH_SERVICEDESC) === FALSE )
189                    continue;
190
191                $services[$i]['name']        = $s['name'];
192                // Sorting check_multi
193                if( (string) $this->XML->NAGIOS_MULTI_PARENT == ""){
194                    $services[$i]['sort']        = strtoupper($s['name']);
195                }else{
196                    $services[$i]['sort']        = strtoupper((string) $this->XML->NAGIOS_MULTI_PARENT);
197                    $services[$i]['sort']       .= (string) $this->XML->DATASOURCE[0]->IS_MULTI[0];
198                    $services[$i]['sort']       .= strtoupper($s['name']);
199                }
200                $services[$i]['state']       = $s['state'];
201                $services[$i]['hostname']    = (string) $this->XML->NAGIOS_DISP_HOSTNAME;
202                $services[$i]['servicedesc'] = (string) $this->XML->NAGIOS_DISP_SERVICEDESC;
203                $services[$i]['is_multi']    = (string) $this->XML->DATASOURCE[0]->IS_MULTI[0];
204            }
205            $i++;
206        }
207        #print Kohana::debug($services);
208        if( is_array($services) && (!empty($services))){
209            # Obtain a list of columns
210            foreach ($services as $key => $row) {
211                $sort[$key]  = $row['sort'];
212            }
213            # Sort the data with volume descending, edition ascending
214            # Add $data as the last parameter, to sort by the common key
215            array_multisort($sort, SORT_STRING, $services);
216        }
217        if(is_array($host) && (!empty($host))){
218            array_unshift($services, $host[0]);
219        }
220        return $services;
221    }
222
223    /*
224    *
225    *
226    */
227    public function getFirstService($hostname) {
228        $conf = $this->config->conf;
229        $services = $this->getServices($hostname);
230        foreach ($services as $srv) {
231            if ($srv['state'] == "active" ) {
232                break;
233            }
234        }
235        if(empty($srv)){
236            throw new Kohana_Exception('error.get-first-service', $hostname );
237        }
238        return $srv['name'];
239    }
240
241    /*
242    *
243    *
244    */
245    public function getFirstHost() {
246        $conf = $this->config->conf;
247        $hosts = $this->getHosts();
248        foreach ($hosts as $host) {
249            if ($host['state'] == "active" ) {
250                break;
251            }
252        }
253        if(empty($host)){
254            throw new Kohana_Exception('error.get-first-host');
255        }
256        return $host['name'];
257    }
258
259    /*
260    *
261    *
262    */
263    public function readXML ($hostname, $servicedesc, $throw_exception=TRUE){
264        $conf        = $this->config->conf;
265        $xmlfile     = $conf['rrdbase'].$hostname."/".$servicedesc.".xml";
266        $xml         = array();
267        if (file_exists($xmlfile)) {
268            libxml_use_internal_errors(TRUE);
269            libxml_clear_errors(TRUE);
270            if(! $xml = simplexml_load_file($xmlfile) ){;
271                if( $throw_exception == TRUE ){
272                    $errors = '<br>';
273                    foreach(libxml_get_errors() as $error) {
274                         $errors .= $error->message."<br>";
275                    }
276                    throw new Kohana_Exception('error.xml-generic_error',$xmlfile, $errors);
277                }else{
278                    return FALSE;
279                }
280            }
281            $this->XML   = array();
282            $this->MACRO = array();
283            $this->MACRO['AUTH_SERVICEDESC'] = '';
284            $this->MACRO['AUTH_HOSTNAME'] = '';
285            $this->DS    = array();
286            // Throw excaption without a valid structure version
287            if(!isset($xml->XML->VERSION) && $throw_exception == TRUE){
288                throw new Kohana_Exception('error.xml-structure-without-version-tag',$xmlfile);
289            }
290            if(!isset($xml->XML->VERSION) && $throw_exception == FALSE){
291                return FALSE;
292            }
293            foreach ( $xml as $key=>$val ){
294                if(preg_match('/^NAGIOS_(.*)$/', $key, $match)){
295                    #print $match[1]." => ".$val."\n";
296                    $key = $match[1];
297                    $this->MACRO[$key] = (string) $val;
298                }
299            }
300            $i=0;
301            foreach ( $xml->DATASOURCE as $datasource ){
302                foreach ( $datasource  as $key=>$val){
303                    #print "$key => $val\n";
304                    #$$key[$i] = (string) $val;
305                    $this->DS[$i][$key] = (string) $val;
306                }
307                $i++;
308            }
309            $this->XML = $xml;
310            return TRUE;
311        }else{
312            throw new Kohana_Exception('error.xml-not-found', $xmlfile);
313        }
314    }
315
316    /*
317    *
318    *
319    */
320    public function buildDataStruct ($host = FALSE, $service = FALSE, $view = NULL, $source = NULL){
321        if($host === FALSE && $service === FALSE){
322            return FALSE;
323        }
324        $conf = $this->config->conf;
325
326        /*
327         * Special templates without Host/Service
328         */
329        if($host == '__special' ){
330            // $service contains the template name
331            $this->includeTemplate($service,'special');
332        }else{
333            if( $this->readXML($host,$service) == FALSE ){
334                throw new Kohana_Exception('error.xml-not-found', "Undefined error");
335                return false;
336            }
337            $this->includeTemplate($this->DS[0]['TEMPLATE']);
338        }
339        if(isset($this->TIMERANGE['type']) && $this->TIMERANGE['type'] == "start-end"){
340            $view = intval($view);
341            $i=0;
342            foreach( $this->RRD['def'] as $key=>$val){
343                if( ! is_null($source) && $source != $key ){
344                    continue;
345                }
346                $tmp_struct = array();
347                $tmp_struct['LEVEL']         = $i;
348                $tmp_struct['VIEW']          = $view;
349                $tmp_struct['TEMPLATE_FILE'] = $this->TEMPLATE_FILE;;
350                $tmp_struct['SOURCE']        = $key;
351                $tmp_struct['RRD_CALL']      = $this->TIMERANGE['cmd'] . " ". $this->RRD['opt'][$key] . " " . $this->RRD['def'][$key];
352                $tmp_struct['TIMERANGE']     = $this->TIMERANGE;
353                $tmp_struct['GRAPH_WIDTH']   = $this->getGraphDimensions('width',  $tmp_struct['RRD_CALL']);
354                $tmp_struct['GRAPH_HEIGHT']  = $this->getGraphDimensions('height', $tmp_struct['RRD_CALL']);
355                if(isset($this->RRD['ds_name'][$key]) ){
356                     $tmp_struct['ds_name']   = $this->RRD['ds_name'][$key];
357                }elseif(array_key_exists($i, $this->DS)){
358                     $tmp_struct['ds_name']   = $this->DS[$i]['NAME'];
359                }else{
360                     $tmp_struct['ds_name']   = "UNDEF";
361                }
362                $tmp_struct['MACRO']   = $this->MACRO;
363                if(isset($this->XML->XML->VERSION)){
364                    $tmp_struct['VERSION']   = pnp::xml_version_check( (string) $this->XML->XML->VERSION);
365                }else{
366                    $tmp_struct['VERSION']   = pnp::xml_version_check("0");
367                }
368                $this->addToDataStruct($tmp_struct);
369                $i++;
370            }
371        return;
372        }
373        if( $view === ""){
374            $v = 0;
375            foreach($this->config->views as $view_key=>$view_val){
376                $i=0;
377                foreach( $this->RRD['def'] as $key=>$val){
378                    if( ! is_null($source) && $source != $key ){
379                        continue;
380                    }
381                    $tmp_struct = array();
382                    $tmp_struct['LEVEL']         = $i;
383                    $tmp_struct['VIEW']          = $view_key;
384                    $tmp_struct['TEMPLATE_FILE'] = $this->TEMPLATE_FILE;;
385                    $tmp_struct['SOURCE']        = $key;
386                    $tmp_struct['RRD_CALL']      = $this->TIMERANGE[$v]['cmd'] . " " . $this->RRD['opt'][$key] . " " . $this->RRD['def'][$key];
387                    $tmp_struct['GRAPH_WIDTH']   = $this->getGraphDimensions('width',  $tmp_struct['RRD_CALL']);
388                    $tmp_struct['GRAPH_HEIGHT']  = $this->getGraphDimensions('height', $tmp_struct['RRD_CALL']);
389                    if(isset($this->RRD['ds_name'][$key]) ){
390                        $tmp_struct['ds_name']   = $this->RRD['ds_name'][$key];
391                    }elseif(array_key_exists($i, $this->DS)){
392                        $tmp_struct['ds_name']   = $this->DS[$i]['NAME'];
393                    }else{
394                        $tmp_struct['ds_name']   = "UNDEF";
395                    }
396                    $tmp_struct['TIMERANGE']     = $this->TIMERANGE[$v];
397                    $tmp_struct['MACRO']         = $this->MACRO;
398                    if(isset($this->XML->XML->VERSION)){
399                        $tmp_struct['VERSION']   = pnp::xml_version_check( (string) $this->XML->XML->VERSION);
400                    }else{
401                        $tmp_struct['VERSION']   = pnp::xml_version_check("0");
402                    }
403                    $this->addToDataStruct($tmp_struct);
404                    $i++;
405                }
406            $v++;
407            }
408        }else{
409            $view = intval($view);
410            $i=0;
411            foreach( $this->RRD['def'] as $key=>$val){
412                if( ! is_null($source) && $source != $key ){
413                    continue;
414                }
415                $tmp_struct = array();
416                $tmp_struct['LEVEL']         = $i;
417                $tmp_struct['VIEW']          = $view;
418                $tmp_struct['TEMPLATE_FILE'] = $this->TEMPLATE_FILE;;
419                $tmp_struct['SOURCE']        = $key;
420                $tmp_struct['RRD_CALL']      = $this->TIMERANGE[$view]['cmd'] . " ". $this->RRD['opt'][$key] . " " . $this->RRD['def'][$key];
421                $tmp_struct['TIMERANGE']     = $this->TIMERANGE[$view];
422                $tmp_struct['GRAPH_WIDTH']   = $this->getGraphDimensions('width',  $tmp_struct['RRD_CALL']);
423                $tmp_struct['GRAPH_HEIGHT']  = $this->getGraphDimensions('height', $tmp_struct['RRD_CALL']);
424                if(isset($this->RRD['ds_name'][$key]) ){
425                    $tmp_struct['ds_name']   = $this->RRD['ds_name'][$key];
426                }elseif(array_key_exists($i, $this->DS)){
427                    $tmp_struct['ds_name']   = $this->DS[$i]['NAME'];
428                }else{
429                    $tmp_struct['ds_name']   = "UNDEF";
430                }
431                $tmp_struct['MACRO']         = $this->MACRO;
432                if(isset($this->XML->XML->VERSION)){
433                    $tmp_struct['VERSION']   = pnp::xml_version_check( (string) $this->XML->XML->VERSION);
434                }else{
435                    $tmp_struct['VERSION']   = pnp::xml_version_check("0");
436                }
437                   $this->addToDataStruct($tmp_struct);
438                   $i++;
439            }
440        }
441    }
442
443    /*
444    *
445    *
446    */
447    private function addToDataStruct ($data=FALSE) {
448        if($data === FALSE)
449            return FALSE;
450
451        array_push($this->STRUCT, $data);
452    }
453
454    /*
455    *
456    *
457    */
458    private function includeTemplate($template=FALSE,$type='normal'){
459        if($template===FALSE){
460            return FALSE;
461        }
462        $this->RRD = array();
463        /*
464        * Normal PNP Templates
465        */
466        if($type == 'normal'){
467            $template_file = $this->findTemplate( $template );
468            $this->TEMPLATE_FILE = $template_file;
469            $hostname      = $this->MACRO['HOSTNAME'];
470            $servicedesc   = $this->MACRO['SERVICEDESC'];
471            $TIMERANGE     = $this->TIMERANGE;
472        }elseif($type == 'special'){
473            $template_file = $this->findTemplate( $template, $type );
474            $TIMERANGE     = $this->TIMERANGE;
475        }
476        $def     = FALSE;
477        $opt     = FALSE;
478        $ds_name = FALSE;
479        /*
480        * 0.4.x Template compatibility
481        */
482        foreach($this->DS as $key=>$val ){
483            $key++;
484            foreach(array_keys($val) as $tag){
485                ${$tag}[$key] = $val[$tag];
486            }
487        }
488        foreach($this->MACRO as $key=>$val ){
489            ${"NAGIOS_".$key} = $val;
490        }
491        if(isset($RRDFILE[1])){
492            $rrdfile = $RRDFILE[1];
493        }
494        // Include template
495        if($template_file == FALSE){
496            throw new Kohana_Exception('error.no-templates-found');
497        }else{
498            ob_start();
499            include($template_file);
500            ob_end_clean();
501        }
502        // Compatibility for very old Templates
503        if(!is_array($def) && $def != FALSE){
504            $tmp[1] = $def;
505            $def = $tmp;
506        }
507        if(!is_array($opt) && $opt != FALSE){
508            $tmp[1] = $opt;
509            $opt = $tmp;
510        }
511        if(!is_array($ds_name) && $ds_name != FALSE){
512            $tmp[1] = $ds_name;
513            $ds_name = $tmp;
514        }
515        //
516        if($def != FALSE){
517            $this->RRD['def'] = $this->array_reindex($def);
518        }else{
519            throw new Kohana_Exception('error.template-without-def', $template_file);
520        }
521        if($opt != FALSE ){
522            $this->RRD['opt'] = $this->array_reindex($opt);
523        }else{
524            throw new Kohana_Exception('error.template-without-opt', $template_file);
525        }
526        if( $ds_name != FALSE ){
527            $this->RRD['ds_name'] = $this->array_reindex($ds_name);
528        }
529        return TRUE;
530    }
531
532    #
533    #
534    #
535    private function getGraphDimensions($search, $command){
536        if($search == 'width'){
537            if(preg_match_all('/(-w|--width|--width=)\s([0-9]+)\s/i',$command,$match)){
538                $value = array_pop($match[2]);
539                return $value;
540            }else{
541                return $this->config->conf['graph_width'];
542            }
543        }
544        if($search == 'height'){
545            if(preg_match_all('/(-h|--height|--height=)\s([0-9]+)\s/i',$command,$match)){
546                $value = array_pop($match[2]);
547                return $value;
548            }else{
549                return $this->config->conf['graph_height'];
550            }
551        }
552        return FALSE;
553    }
554    #
555    #
556    #
557    private function array_reindex($data){
558        $i=0;
559        foreach($data as $d){
560            $tmp[$i] = $d;
561            $i++;
562        }
563        return $tmp;
564    }
565
566    /*
567    *
568    *
569    */
570    public function findTemplate($template,$type='normal'){
571        $conf = $this->config->conf;
572        /*
573         * Normal templates
574         */
575        if($type == 'normal'){
576            // Build a list of directories to search for templates
577            $template_dirs = array();
578            if(array_key_exists('template_dirs', $this->config->conf)){
579                foreach($this->config->conf['template_dirs'] as $dir){
580                    $template_dirs[] = $dir;
581                }
582            }
583            foreach(Kohana::config('core.template_dirs') as $dir){
584                $template_dirs[] = $dir;
585            }
586            #throw new Kohana_Exception(print_r($template_dirs,TRUE));
587            foreach($template_dirs as $dir){
588                $match = $this->findRecursiveTemplate($template,$dir);
589                if($match != FALSE){
590                    return $match;
591                }
592            }
593            /*
594             * Fallback to default templates
595             */
596            foreach($template_dirs as $dir){
597                $match = $this->findRecursiveTemplate('default',$dir);
598                if($match != FALSE){
599                    return $match;
600                }
601            }
602            return FALSE;
603        }
604        /*
605         * Special Templates
606         */
607        if($type == 'special'){
608            if (is_readable($conf['special_template_dir'] . '/' . $template . '.php')) {
609                $template_file = $conf['special_template_dir'].'/' . $template . '.php';
610            }else{
611                throw new Kohana_Exception("Special Template '$template' not found");
612            }
613            return $template_file;
614        }
615    }
616
617    /*
618    *
619    *
620    */
621    function findRecursiveTemplate($template, $dir) {
622        if(!is_readable($dir)){
623            return FALSE;
624        }
625        $template_file = "";
626        $r_template_file = "";
627        $r_template = "";
628        $recursive = explode("_", $template);
629        if($this->config->conf['enable_recursive_template_search'] == 1){
630            $i = 0;
631            foreach ($recursive as $value) {
632                if ($i == 0) {
633                    $r_template = $value;
634                } else {
635                    $r_template = $r_template . '_' . $value;
636                }
637                $file = $dir . '/' . $r_template . '.php';
638                if (is_readable($file)) {
639                    $r_template_file = $file;
640                }
641                $i++;
642            }
643            if($r_template_file != ""){
644                return $r_template_file;
645            }else{
646                return FALSE;
647            }
648        }else{
649            $file = $dir . '/' . $template . '.php';
650            if (is_readable($file)) {
651                return $file;
652            }else{
653                return FALSE;
654            }
655        }
656    }
657
658    public function getTimeRange($start=FALSE ,$end=FALSE ,$view="") {
659        if($end != FALSE){
660            // we are in a custom timerange
661            if(!is_numeric($end)){
662                $timestamp = strtotime($end);
663                if(!$timestamp){
664                    throw new Kohana_User_Exception('Wrong Format', "End -> $end");
665                }else{
666                    $end = $timestamp;
667                }
668            }
669        }else{
670            $end = time();
671        }
672        if($start != FALSE ){
673            // we are in a custom timerange
674            if(!is_numeric($start)){
675                $timestamp = strtotime($start);
676                if(!$timestamp){
677                    throw new Kohana_User_Exception('Wrong Format', "Start -> $start");
678                }else{
679                    $start = $timestamp;
680                }
681            }
682        }
683        if($start && $end){
684            $timerange['title']   = Kohana::lang('common.timerange-selector-link');
685            $timerange['start']   = $start;
686            $timerange['f_start'] = date($this->config->conf['date_fmt'],$start);
687            $timerange['end']     = $end;
688            $timerange['f_end']   = date($this->config->conf['date_fmt'],$end);
689            $timerange['cmd']     = " --start $start --end $end ";
690            $timerange['type']    = "start-end";
691            $this->TIMERANGE = $timerange;
692            return;
693        }
694
695           $view=intval( pnp::clean($view) );
696           if($view >= count($this->config->views)){
697               $view = 1;
698           }
699
700           if(!$end){
701               $end = time();
702           }elseif(!is_numeric($end)){
703               $timestamp = strtotime($end);
704               if(!$timestamp){
705                throw new Kohana_User_Exception('Wrong Format', "$end");
706               }else{
707                   $end = $timestamp;
708               }
709           }else{
710               $end = $end;
711           }
712
713           if(!$start){
714               $start = ( $end - $this->config->views[$view]['start']);
715           }elseif(!is_numeric($start)){
716               $timestamp = strtotime($start);
717               if(!$timestamp){
718                throw new Kohana_User_Exception('Wrong Format', "$start");
719               }else{
720                   $start = $timestamp;
721               }
722           }else{
723            $start = pnp::clean($start);
724           }
725
726           if($start >= $end){
727            //Fixme i18n
728            throw new Kohana_User_Exception('Wrong Timerange', "start >= end");
729           }
730
731           $timerange['title']   = $this->config->views[$view]['title'];
732           $timerange['start']   = $start;
733           $timerange['f_start'] = date($this->config->conf['date_fmt'],$start);
734           $timerange['end']     = $end;
735           $timerange['f_end']   = date($this->config->conf['date_fmt'],$end);
736           $timerange['cmd']     = " --start $start --end $end ";
737           $timerange['type']    = "views";
738           for ($i = 0; $i < count($this->config->views); $i++) {
739               $timerange[$i]['title']   = $this->config->views[$i]['title'];
740               $timerange[$i]['start']   = $end - $this->config->views[$i]['start'];
741               $timerange[$i]['f_start'] = date($this->config->conf['date_fmt'],$end - $this->config->views[$i]['start']);
742               $timerange[$i]['end']     = $end;
743               $timerange[$i]['f_end']   = date($this->config->conf['date_fmt'],$end);
744               $timerange[$i]['cmd']     = " --start " . ($end - $this->config->views[$i]['start']) . " --end  $end" ;
745           }
746           $this->TIMERANGE = $timerange;
747    }
748
749    public function buildBasketStruct($basket,$view = NULL){
750        if(is_array($basket) && (!empty($basket))){
751	    if($view == ""){
752                 $views = array_keys($this->config->views);
753            }else{
754		 $views = array($view);
755            }
756            foreach($views as $view){
757                foreach($basket as $item){
758                    # explode host::service::source
759                    $slices = explode("::",$item);
760                    if(count($slices) == 2)
761                        $this->buildDataStruct($slices[0], $slices[1], $view);
762                    if(count($slices) == 3)
763                        $this->buildDataStruct($slices[0], $slices[1], $view, $slices[2]);
764                }
765                # break on custom time ranges
766		if(isset($this->TIMERANGE['type']) && $this->TIMERANGE['type'] == "start-end"){
767                    break;
768                }
769            }
770        }
771    }
772
773    public function buildPageStruct($page,$view){
774        $servicelist = array();
775        $this->parse_page_cfg($page);
776        $hosts = $this->getHostsByPage();
777        # No regex so we keep the order defined by config
778        if($this->PAGE_DEF['use_regex'] == 0){
779            #Loop through graph definitions
780            foreach($this->PAGE_GRAPH as $graph){
781                $hosts_to_search_for = explode(",", $graph['host_name']);
782                foreach($hosts_to_search_for as $host){
783                    if(in_array($host, $hosts)){
784                        $services = $this->getServices($host);
785                        foreach($services as $service) {
786                            // search for definition
787                            $data = $this->filterServiceByPage($graph,$host,$service);
788                            if($data){
789                                $servicelist[] = array( 'host' => $host, 'service' => $service['name'], 'source' => $data['source']);
790
791                            }
792                        }
793                    }
794                }
795            }
796        }else{
797            foreach($hosts as $host){
798                $services = $this->getServices($host);
799                foreach($services as $service) {
800                    // search for definition
801                    $data = $this->filterServiceByPage($this->PAGE_GRAPH,$host,$service);
802                    if($data){
803                        $servicelist[] = array( 'host' => $host, 'service' => $service['name'], 'source' => $data['source']);
804                    }
805                }
806            }
807        }
808        #print Kohana::debug($servicelist);
809        if(empty($servicelist)){
810            $this->ERROR = "ERROR: ". Kohana::lang('error.no-data-for-page', $page.".cfg" );
811        }else{
812            foreach($servicelist as $s){
813                $this->buildDataStruct($s['host'],$s['service'],$view,$s['source']);
814            }
815        }
816    }
817
818
819    public function parse_page_cfg($page){
820        $page_cfg = $this->config->conf['page_dir'].$page.".cfg";
821        if(is_readable($page_cfg)){
822            $data = file($page_cfg);
823        }else{
824            throw new Kohana_Exception('error.page-not-readable', $page.".cfg" );
825        }
826        $l = 0;
827        $line = "";
828        $tag = "";
829        $inside=0;
830        $this->PAGE_DEF['page_name'] = 'UNDEF';
831        $this->PAGE_DEF['use_regex'] = 0;
832        $this->PAGE_GRAPH = array();
833        $allowed_tags = array("page", "graph");
834        foreach($data as $line){
835            if(preg_match('/(^#|^;)/',$line)) {
836                continue;
837            }
838
839            preg_match('/define\s+(\w+)\W+{/' ,$line, $tag);
840            if(isset($tag[1]) && in_array($tag[1],$allowed_tags)){
841                $inside = 1;
842                $t = $tag[1];
843                $l++;
844                continue;
845            }
846            if(preg_match('/\s?(\w+)\s+([^#]+)(#.*)?$/',$line, $key) && $inside == 1){
847                $k=strtolower($key[1]);
848                $v=$key[2];
849                if($t=='page'){
850                    $this->PAGE_DEF[$k] = trim($v);
851                }elseif($t=='graph'){
852                    $this->PAGE_GRAPH[$l][$k] = trim($v);
853                }
854            }
855            if(preg_match('/}/',$line)){
856                $inside=0;
857                $t = "";
858                continue;
859            }
860        }
861    }
862
863    /*
864    *
865    */
866    public function getHostsByPage(){
867        $hosts = $this->getHosts();
868        $new_hosts = array();
869        foreach( $hosts as $host){
870            if($host['state'] == "inactive"){
871                continue;
872            }
873            if($tmp = $this->filterHostByPage($host['name'])){
874                $new_hosts[] = $tmp;
875            }
876        }
877        return $new_hosts;
878    }
879    /*
880    *
881    */
882    private function filterHostByPage($host){
883        if(isset($this->PAGE_DEF['use_regex']) && $this->PAGE_DEF['use_regex'] == 1){
884            // Search Host by regex
885            foreach( $this->PAGE_GRAPH as $g ){
886                if(isset($g['host_name']) && preg_match('/'.$g['host_name'].'/',$host)){
887                    return $host;
888                }
889            }
890        }else{
891            foreach( $this->PAGE_GRAPH as $g ){
892                $hosts_to_search_for = explode(",", $g['host_name']);
893                if(isset($g['host_name']) && in_array($host ,$hosts_to_search_for) ){
894                    return $host;
895                }
896            }
897        }
898        return FALSE;
899    }
900
901    private function filterServiceByPage($g,$host,$service){
902        $data = array();
903        if(isset($this->PAGE_DEF['use_regex']) && $this->PAGE_DEF['use_regex'] == 1){
904            // Search Host by regex
905            foreach( $this->PAGE_GRAPH as $g ){
906                if(isset($g['host_name']) && preg_match('/'.$g['host_name'].'/',$host)){
907                    if(isset($g['service_desc']) && preg_match('/'.$g['service_desc'].'/',$service['name'])){
908                        $data['service_desc'] = $g['service_desc'];
909                        $data['host_name'] = $g['host_name'];
910                        $data['source'] = NULL;
911                        // if we only want a single image
912                        if(isset($g['source'])){
913                            $this->readXML($host,$service['name']);
914                            $this->includeTemplate($this->DS[0]['TEMPLATE']);
915                            $source = intval($g['source']);
916                            if(array_key_exists($source,$this->RRD['def'])){
917                                $data['source'] = $source;
918                            }
919                        }
920                        return $data;
921                    }
922                }
923            }
924        }else{
925            $hosts_to_search_for = explode(",", $g['host_name']);
926            $services_to_search_for = explode(",", $g['service_desc']);
927            if(isset($g['host_name']) && in_array($host ,$hosts_to_search_for) ){
928                if(isset($g['service_desc']) && in_array($service['name'] ,$services_to_search_for) ){
929                    $data['service_desc'] = $g['service_desc'];
930                    $data['host_name'] = $g['host_name'];
931                    $data['source'] = NULL;
932                    // if we only want a single image
933                    if(isset($g['source'])){
934                        $this->readXML($host,$service['name']);
935                        $this->includeTemplate($this->DS[0]['TEMPLATE']);
936                        $source = intval($g['source']);
937                        if(array_key_exists($source,$this->RRD['def'])){
938                            $data['source'] = $source;
939                        }
940                    }
941                    return $data;
942                }
943            }
944        }
945        return FALSE;
946    }
947
948    /*
949    *
950    */
951    public function getPages() {
952        $pages = array();
953        if (is_dir($this->config->conf['page_dir'])) {
954            if ($dh = opendir($this->config->conf['page_dir'])) {
955                 while (($file = readdir($dh)) !== false) {
956                      if(preg_match('/(.*)\.cfg$/',basename($file),$page)){
957                           $pages[] = urlencode($page[1]);
958                      }
959                 }
960                closedir($dh);
961            } else {
962                die("Cannot open directory:  $path");
963            }
964        }
965        if(empty($pages)){
966            return FALSE;
967        }else{
968
969            natsort($pages);
970        }
971        return $pages;
972    }
973
974    /*
975    *
976    */
977    public function getFirstPage(){
978        $pages = $this->getPages();
979        if(empty($pages)){
980            return FALSE;
981        }else{
982            return urldecode($pages[0]);
983        }
984    }
985
986    /*
987    *
988    */
989    public function getPageDetails($page){
990        $this->parse_page_cfg($page);
991        return $this->PAGE_DEF['page_name'];
992    }
993
994    /*
995    *
996    */
997    public function buildXport($host,$service){
998        // FIXME add max rows to config
999        $this->XPORT = " -m 2000";
1000        $this->XPORT .= " --start=".$this->TIMERANGE['start'];
1001        $this->XPORT .= " --end=".$this->TIMERANGE['end'];
1002        $this->readXML($host,$service);
1003        $count = 0;
1004        $RRAs = array('MIN','MAX','AVERAGE');
1005        foreach($this->DS as $key=>$value){
1006            foreach($RRAs as $RRA){
1007                $this->XPORT .= sprintf(" DEF:%d%s=%s:%d:%s ",$count,$RRA,$value['RRDFILE'],$value['DS'],$RRA);
1008                $this->XPORT .= sprintf(" XPORT:%d%s:%s_%s "    ,$count,$RRA,$value['NAME'],$RRA);
1009            }
1010            $count++;
1011        }
1012    }
1013    /*
1014    *
1015    */
1016    public function xml2csv($string){
1017        $xml = simplexml_load_string($string);
1018        $csv = "timestamp";
1019        foreach($xml->meta->legend->entry as $key=>$value){
1020            $csv .= ";" . $value ;
1021        }
1022
1023        /*
1024        * from rrdtool 1.5 on, timestamp ( value->t ) is not set anymore
1025        * from 1.6 on it can be reactivated by -- showrime option to rrdtool xport
1026        * here we take care of the timestamp ourselves regardless which
1027        * rrdtool version is used
1028        */
1029        $timestamp2 = $xml->meta->start;
1030        $step = $xml->meta->step;
1031        $csv .= "\n";
1032        foreach($xml->data->row as $key=>$value){
1033            $csv .= $timestamp2;
1034            foreach($value->v as $item){
1035                $csv .= ";".floatval((string) $item);
1036            }
1037            $csv .= "\n";
1038            $timestamp2 = $timestamp2 + $step;
1039        }
1040        return $csv;
1041    }
1042
1043    /*
1044    *
1045    * Used in Special Templates to gather data
1046    */
1047    public function tplGetData ($hostname, $servicedesc, $throw_exception=TRUE){
1048        $conf        = $this->config->conf;
1049        $xmlfile     = $conf['rrdbase'].$hostname."/".$servicedesc.".xml";
1050        $data = array();
1051        if (file_exists($xmlfile)) {
1052            $xml = simplexml_load_file($xmlfile);
1053            // Throw excaption without a valid structure version
1054            if(!isset($xml->XML->VERSION) && $throw_exception == TRUE){
1055                throw new Kohana_Exception('error.xml-structure-without-version-tag',$xmlfile);
1056            }
1057            if(!isset($xml->XML->VERSION) && $throw_exception == FALSE){
1058                return FALSE;
1059            }
1060            foreach ( $xml as $key=>$val ){
1061                if(preg_match('/^NAGIOS_(.*)$/', $key, $match)){
1062                    $key = $match[1];
1063                    $data['MACRO'][$key] = (string) $val;
1064                }
1065            }
1066            $i=0;
1067            foreach ( $xml->DATASOURCE as $datasource ){
1068                foreach ( $datasource  as $key=>$val){
1069                    $data['DS'][$i][$key] = (string) $val;
1070                }
1071                $i++;
1072            }
1073            return $data;
1074        }else{
1075            throw new Kohana_Exception('error.xml-not-found', $xmlfile);
1076        }
1077    }
1078    /*
1079    *
1080    * Used in Special Templates to gather data
1081    */
1082    public function tplGetServices ($hostregex=FALSE, $serviceregex = ''){
1083        if($hostregex === FALSE){
1084            return FALSE;
1085        }
1086        $hostregex    = sprintf("/%s/",$hostregex);
1087        $serviceregex = sprintf("/%s/",$serviceregex);
1088        $hosts = $this->getHosts();
1089        $new_hosts = array();
1090        foreach( $hosts as $host){
1091            if(preg_match($hostregex,$host['name'])){
1092                $new_hosts[] = $host['name'];
1093            }
1094        }
1095
1096        if(empty($new_hosts)){
1097            throw new Kohana_Exception('error.tpl-no-hosts-found', $hostregex);
1098        }
1099
1100        $i = 0;
1101        $new_services = array();
1102        foreach($new_hosts as $host){
1103            $services = $this->getRawServices($host);
1104            if(empty($services)){
1105                throw new Kohana_Exception('error.tpl-no-services-found', $serviceregex);
1106            }
1107            foreach($services as $service){
1108                if(preg_match($serviceregex, $service['name'])){
1109                    $new_services[$i]['hostname'] = $host;
1110                    $new_services[$i]['host']     = $host;
1111                    $new_services[$i]['service_description'] = $service['name'];
1112                    $new_services[$i]['service']             = $service['name'];
1113                    $i++;
1114                }
1115            }
1116        }
1117
1118        if(empty($new_services)){
1119            throw new Kohana_Exception('error.tpl-no-services-found', $serviceregex);
1120        }
1121
1122        return $new_services;
1123
1124    }
1125}
1126