1 <?php
2 /*
3 * This code is part of GOsa (http://www.gosa-project.org)
4 * Copyright (C) 2003-2008 GONICUS GmbH
5 *
6 * ID: $$Id$$
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
23 class gosaSupportDaemon
24 {
25 private $s_host = "";
26 private $i_port = 0;
27 private $s_encryption_key = "";
29 private $o_sock = NULL;
30 private $f_timeout = 2;
31 private $s_error = "";
32 private $b_error = FALSE;
34 private $is_connected = FALSE;
37 /*! \brief Creates a new gosaSupportDaemon object.
38 @param string Host The Host where the daemon is running on.
39 @param integer Port The port which the daemon use.
40 @param string Key The encryption string.
41 @param boolean Connect Directly connect to daemon socket.
42 @param float Timeout The timelimit for all socket actions.
43 */
44 public function __construct($connect=TRUE,$timeout=2)
45 {
46 #FIXME: bad idea about referencing global variables from within classes
47 global $config;
49 # load from config, store statically
50 if (isset($config->current['GOSA_SI'])){
52 if ($this->s_host == ""){
53 $this->s_host= preg_replace("/^.*@([^:]+):.*$/", "$1", $config->current['GOSA_SI']);
54 $this->i_port= preg_replace("/^.*@[^:]+:(.*)$/", "$1", $config->current['GOSA_SI']);
55 $this->s_encryption_key = preg_replace("/^(.*)@[^:]+:.*$/", "$1", $config->current['GOSA_SI']);
56 }
58 $this->f_timeout = $timeout;
59 if($connect){
60 $this->connect();
61 }
62 }
63 }
66 /*! \brief Establish daemon connection.
67 @return boolean Returns true if the connection was succesfully established.
68 */
69 public function connect()
70 {
71 $this->o_sock = new Socket_Client($this->s_host,$this->i_port,TRUE,$this->f_timeout);
72 if($this->o_sock->connected()){
73 $this->o_sock->setEncryptionKey($this->s_encryption_key);
74 $this->is_connected = TRUE;
75 }else{
76 $this->set_error($this->o_sock->get_error());
77 $this->disconnect();
78 new log("debug","gosaSupportDaemon::connect()", "Could not connect to server.", array(),$this->get_error());
79 }
80 return($this->is_connected);
81 }
84 /*! \brief Disconnect from gosa daemon.
85 */
86 public function disconnect()
87 {
88 $this->o_sock->close();
89 $this->is_connected = FALSE;
90 }
93 /*! \brief Sets an error message, which can be returned with get_error().
94 @param string The Error message,
95 */
96 private function set_error($str)
97 {
98 $this->b_error = TRUE;
99 $this->s_error = $str;
100 }
103 /*! \brief Sets an error message, which can be returned with get_error().
104 @param string The Error message,
105 */
106 private function reset_error()
107 {
108 $this->b_error = FALSE;
109 $this->s_error = "";
110 }
113 /*! \brief Checks if an error occured.
114 @return boolean returns TRUE or FALSE, whether there is an error or not.
115 */
116 public function is_error()
117 {
118 return($this->b_error);
119 }
122 /*! \brief Returns the last error.
123 @return Returns the last error.
124 */
125 public function get_error()
126 {
127 $str = $this->s_error;
128 $str = preg_replace("/ /"," ",$str);
129 return($str);
130 }
133 /*! \brief Returns an array containing all queued entries.
134 @return Array All queued entries as an array.
135 */
136 public function get_queued_entries($event_types = array("*"),$from=-1,$to=-1,$sort="timestamp DESC")
137 {
138 $this->reset_error();
139 $ret = array();
141 $tags = "";
142 foreach($event_types as $type){
143 $tags .= "<phrase><headertag>".$type."</headertag></phrase>";
144 }
145 if(count($event_types) > 1){
146 $tags = "<connector>or</connector>".$tags;
147 }
148 if(count($event_types)){
149 $tags = "<where><clause>".$tags."</clause></where>";
150 }
152 $xml_msg = "<xml>
153 <header>gosa_query_jobdb</header>
154 <target>GOSA</target>
155 <source>GOSA</source>
156 ".$tags."
158 <orderby>".$sort."</orderby>";
159 if($from != -1 && $to != -1){
160 $xml_msg.= "
161 <limit>
162 <from>".$from."</from>
163 <to>".$to."</to>
164 </limit>";
165 }
166 $xml_msg.= "
167 </xml>";
169 if($this->connect()){
170 $this->o_sock->write($xml_msg);
171 $str = trim($this->o_sock->read());
172 $entries = $this->xml_to_array($str);
173 if(isset($entries['XML']) && is_array($entries['XML'])){
175 /* Check if returned values represent a valid answer */
176 if(isset($entries['XML'])){
178 /* Unset header tags */
179 foreach(array("HEADER","SOURCE","TARGET") as $type){
180 unset($entries['XML'][$type]);
181 }
182 $ret = $entries['XML'];
183 }
184 }
185 }
187 /* Remove session ID. No one is interested in this... */
188 unset($ret['SESSION_ID']);
190 return($ret);
191 }
194 /*! \brief Checks if the given ids are used queue ids.
195 @param Array The ids we want to check..
196 @return Array An array containing all ids as index and TRUE/FALSE as value.
197 */
198 public function ids_exist($ids)
199 {
200 if(!is_array($ids)){
201 trigger_error("Requires an array as parameter.");
202 return;
203 }
204 $this->reset_error();
206 $ret = array();
208 $xml_msg = "<xml>
209 <header>gosa_query_jobdb</header>
210 <target>GOSA</target>
211 <source>GOSA</source>
212 <where>
213 <clause>
214 <connector>or</connector>";
215 foreach($ids as $id){
216 $xml_msg .= "<phrase>
217 <operator>eq</operator>
218 <id>".$id."</id>
219 </phrase>";
220 }
221 $xml_msg .= "</clause>
222 </where>
223 </xml>";
225 if($this->connect()){
226 $this->o_sock->write($xml_msg);
227 $str = trim($this->o_sock->read());
228 $entries = $this->xml_to_array($str);
229 if(isset($entries['XML']) && is_array($entries['XML'])){
230 foreach($entries['XML'] as $entry){
231 if(isset($entry['ID'])){
232 $ret[] = $entry['ID'];
233 }
234 }
235 }
236 }
237 return($ret);
238 }
241 /*! \brief Returns an entry containing all requested ids.
242 @param Array The IDs of the entries we want to return.
243 @return Array Of the requested entries.
244 */
245 public function get_entries_by_mac($macs)
246 {
247 if(!is_array($macs)){
248 trigger_error("Requires an array as parameter.");
249 return;
250 }
251 $this->reset_error();
253 $ret = array();
255 $xml_msg = "<xml>
256 <header>gosa_query_jobdb</header>
257 <target>GOSA</target>
258 <source>GOSA</source>
259 <where>
260 <clause>
261 <connector>or</connector>";
262 foreach($macs as $mac){
263 $xml_msg .= "<phrase>
264 <operator>eq</operator>
265 <macaddress>".$mac."</macaddress>
266 </phrase>";
267 }
268 $xml_msg .= "</clause>
269 </where>
270 </xml>";
272 if($this->connect()){
273 $this->o_sock->write($xml_msg);
274 $str = trim($this->o_sock->read());
275 $entries = $this->xml_to_array($str);
276 if(isset($entries['XML'])){
277 foreach($entries['XML'] as $name => $entry){
278 if(preg_match("/^ANSWER[0-9]*$/",$name)){
279 $ret[$name] = $entry;
280 }
281 }
282 }
283 }
284 return($ret);
285 }
288 /*! \brief Returns an entry containing all requested ids.
289 @param Array The IDs of the entries we want to return.
290 @return Array Of the requested entries.
291 */
292 public function get_entries_by_id($ids)
293 {
294 if(!is_array($ids)){
295 trigger_error("Requires an array as parameter.");
296 return;
297 }
298 $this->reset_error();
300 $ret = array();
302 $xml_msg = "<xml>
303 <header>gosa_query_jobdb</header>
304 <target>GOSA</target>
305 <source>GOSA</source>
306 <where>
307 <clause>
308 <connector>or</connector>";
309 foreach($ids as $id){
310 $xml_msg .= "<phrase>
311 <operator>eq</operator>
312 <id>".$id."</id>
313 </phrase>";
314 }
315 $xml_msg .= "</clause>
316 </where>
317 </xml>";
319 if($this->connect()){
320 $this->o_sock->write($xml_msg);
321 $str = trim($this->o_sock->read());
322 $entries = $this->xml_to_array($str);
323 if(isset($entries['XML'])){
324 foreach($entries['XML'] as $name => $entry){
325 if(preg_match("/^ANSWER[0-9]*$/",$name)){
326 $ret[$name] = $entry;
327 }
328 }
329 }
330 }
331 return($ret);
332 }
335 /*! \brief Checks if the given id is in use.
336 @param Integer The ID of the entry.
337 @return Boolean TRUE if entry exists.
338 */
339 public function id_exists($id)
340 {
341 if(!is_numeric($id)){
342 trigger_error("Requires an integer as parameter.");
343 return;
344 }
346 $this->reset_error();
348 $xml_msg = "<xml>
349 <header>gosa_query_jobdb</header>
350 <target>GOSA</target>
351 <source>GOSA</source>
352 <where>
353 <clause>
354 <phrase>
355 <operator>eq</operator>
356 <id>".$id."</id>
357 </phrase>
358 </clause>
359 </where>
360 </xml>";
362 if($this->connect()){
363 $this->o_sock->write($xml_msg);
364 $str = trim($this->o_sock->read());
365 $entries = $this->xml_to_array($str);
366 if( isset($entries['XML']['HEADER']) &&
367 $entries['XML']['HEADER']=="answer" &&
368 isset($entries['XML']['ANSWER1'])){
369 return(TRUE);
370 }
371 }
372 return(FALSE);
373 }
376 /*! \brief Returns an entry from the gosaSupportQueue
377 @param Integer The ID of the entry we want to return.
378 @return Array Of the requested entry.
379 */
380 public function get_entry_by_id($id)
381 {
382 if(!is_numeric($id)){
383 trigger_error("Requires an integer as parameter.");
384 return;
385 }
386 $this->reset_error();
388 $ret = array();
389 $xml_msg = "<xml>
390 <header>gosa_query_jobdb</header>
391 <target>GOSA</target>
392 <source>GOSA</source>
393 <where>
394 <clause>
395 <phrase>
396 <operator>eq</operator>
397 <id>".$id."</id>
398 </phrase>
399 </clause>
400 </where>
401 </xml>";
402 if($this->connect()){
403 $this->o_sock->write($xml_msg);
404 $str = trim($this->o_sock->read());
405 $entries = $this->xml_to_array($str);
406 if( isset($entries['XML']['HEADER']) &&
407 $entries['XML']['HEADER']=="answer" &&
408 isset($entries['XML']['ANSWER1'])){
409 $ret = $entries['XML']['ANSWER1'];
410 }
411 }
412 return($ret);
413 }
416 /*! \brief Removes a set of entries from the GOsa support queue.
417 @param Array The IDs to remove.
418 @return Boolean True on success.
419 */
420 public function remove_entries($ids)
421 {
422 if(!is_array($ids)){
423 trigger_error("Requires an array as parameter.");
424 return;
425 }
427 $this->reset_error();
429 $ret = array();
431 $xml_msg = "<xml>
432 <header>gosa_delete_jobdb_entry</header>
433 <target>GOSA</target>
434 <source>GOSA</source>
435 <where>
436 <clause>
437 <connector>or</connector>";
438 foreach($ids as $id){
439 $xml_msg .= "<phrase>
440 <operator>eq</operator>
441 <id>".$id."</id>
442 </phrase>";
443 }
444 $xml_msg .= "</clause>
445 </where>
446 </xml>";
448 if($this->connect()){
449 $this->o_sock->write($xml_msg);
450 $str = $this->o_sock->read();
451 $entries = $this->xml_to_array($str);
452 if(isset($entries['XML']) || isset($entries['COUNT'])){
453 new log("debug","DaemonEvent (IDS) ", "gosaSupportDaemon::remove_entries()", $ids,"SUCCESS");
454 return(TRUE);
455 }else{
456 new log("debug","DaemonEvent (IDS) ", "gosaSupportDaemon::remove_entries()", $ids,"FAILED ".$this->get_error());
457 }
458 }
459 return(FALSE);
460 }
464 /*! \brief Removes an entry from the GOsa support queue.
465 @param Integer The ID of the entry we want to remove.
466 @return Boolean True on success.
467 */
468 public function remove_entry($id)
469 {
470 return($this->remove_entries(array($id)));
471 }
474 /*! \brief Parses the given xml string into an array
475 @param String XML string
476 @return Array Returns an array containing the xml structure.
477 */
478 private function xml_to_array($xml)
479 {
480 $params = array();
481 $level = array();
482 $parser = xml_parser_create_ns();
483 xml_parse_into_struct($parser, $xml, $vals, $index);
485 $err_id = xml_get_error_code($parser);
486 if($err_id){
487 xml_parser_free($parser);
488 }else{
489 xml_parser_free($parser);
491 foreach ($vals as $xml_elem) {
492 if ($xml_elem['type'] == 'open') {
493 if (array_key_exists('attributes',$xml_elem)) {
494 list($level[$xml_elem['level']],$extra) = array_values($xml_elem['attributes']);
495 } else {
496 $level[$xml_elem['level']] = $xml_elem['tag'];
497 }
498 }
499 if ($xml_elem['type'] == 'complete') {
500 $start_level = 1;
501 $php_stmt = '$params';
502 while($start_level < $xml_elem['level']) {
503 $php_stmt .= '[$level['.$start_level.']]';
504 $start_level++;
505 }
506 $php_stmt .= '[$xml_elem[\'tag\']] = $xml_elem[\'value\'];';
507 @eval($php_stmt);
508 }
509 }
510 }
512 if(!isset($params['XML'])){
513 if (!array_key_exists('XML', $params)){
514 $this->set_error(_("Cannot not parse XML!"));
515 }
516 $params = array("COUNT" => 0);
517 }
519 return($params);
520 }
523 /*! \brief Updates an entry with a set of new values,
524 @param Integer The ID of the entry, we want to update.
525 @param Array The variables to update.
526 @return Boolean Returns TRUE on success.
527 */
528 public function update_entries($ids,$data)
529 {
530 $this->reset_error();
531 if(!is_array($ids)){
532 trigger_error("Requires an array as first parameter.");
533 return;
534 }
536 if(!is_array($data)){
537 trigger_error("Requires an array as second parameter.");
538 return;
539 }
541 $attr = "";
542 foreach($data as $key => $value){
543 if(is_array($value)){
544 foreach($value as $sub_value){
545 $attr.= "<$key>".strtolower($sub_value)."</$key>\n";
546 }
547 }else{
548 $attr.= "<$key>".strtolower($value)."</$key>\n";
549 }
550 }
552 $xml_msg = "<xml>
553 <header>gosa_update_status_jobdb_entry</header>
554 <target>GOSA</target>
555 <source>GOSA</source>
556 <where>
557 <clause>
558 <connector>or</connector>";
559 foreach($ids as $id){
560 $xml_msg .= "<phrase>
561 <operator>eq</operator>
562 <id>".$id."</id>
563 </phrase>";
564 }
565 $xml_msg .= "</clause>
566 </where>
567 <update>
568 ".$attr."
569 </update>
570 </xml>";
572 if($this->connect()){
574 $this->o_sock->write($xml_msg);
575 $str = trim($this->o_sock->read());
576 $entries = $this->xml_to_array($str);
577 if(isset($entries['XML'])){
578 if(isset($entries['XML']['ERROR_STRING'])) {
579 $this->set_error($entries['XML']['ERROR_STRING']);
580 new log("debug","DaemonEvent (IDS) ", "gosaSupportDaemon::update_entries()", $ids,"FAILED setting (".$attr.") error was ".$this->get_error());
581 return(FALSE);
582 }
583 new log("debug","DaemonEvent (IDS) ", "gosaSupportDaemon::update_entries()", $ids,"SUCCESS");
584 return(TRUE);
585 }
586 }
587 return(FALSE);
588 }
591 /*! \brief Returns the number of currently queued objects.
592 @return Integer
593 */
594 public function number_of_queued_entries()
595 {
596 $xml_msg ="<xml><header>gosa_count_jobdb</header><target>GOSA</target><source>GOSA</source></xml>";
597 $this->connect();
598 if($this->connect()){
599 $this->o_sock->write($xml_msg);
600 $str = trim($this->o_sock->read());
601 $entries = $this->xml_to_array($str);
602 if(isset($entries['XML'])){
603 return($entries['XML']['COUNT']);
604 }
605 }
606 return(-1);
607 }
610 public function send_data($header, $to, $data= array(), $answer_expected = FALSE)
611 {
612 $xml_message= "";
614 /* Prepare data */
615 foreach ($data as $key => $value){
616 if(is_array($value)){
617 foreach($value as $sub_val){
618 $xml_message.= "<$key>$sub_value</$key>";
619 }
620 }else{
621 $xml_message.= "<$key>$value</$key>";
622 }
623 }
625 /* Multiple targets? */
626 if (!is_array($to)){
627 $to_targets= array($to);
628 } else {
629 $to_targets= $to;
630 }
632 /* Build target strings */
633 $target ="";
634 foreach($to_targets as $to){
635 $target.= "<target>$to</target>";
636 }
638 return $this->_send("<xml><header>$header</header><source>GOSA</source>$target".$xml_message."</xml>",$answer_expected);
639 }
642 /* Allows simply appending a new DaemonEvent
643 */
644 public function append($event)
645 {
646 if(!($event instanceof DaemonEvent)){
647 return(FALSE);
648 }
650 $this->reset_error();
652 /* Add to queue if new
653 */
654 if($event->is_new()){
656 $request_answer = FALSE;
657 if($event->get_type() == SCHEDULED_EVENT){
658 $action = $event->get_schedule_action();
659 }elseif($event->get_type() == TRIGGERED_EVENT){
660 $action = $event->get_trigger_action();
661 }else{
662 trigger_error("Unknown type of queue event given.");
663 return(FALSE);
664 }
666 /* Get event informations, like targets..
667 */
668 $targets = $event->get_targets();
669 $data = $event->save();
671 /* Append an entry for each target
672 */
673 foreach($targets as $target){
674 $data['macaddress'] = $target;
675 $this->send_data($action,$target,$data,$request_answer);
677 if($this->is_error()){
678 return(FALSE);
679 }
680 }
681 return(TRUE);
682 }else{
684 /* Updated edited entry.
685 */
686 $id = $event->get_id();
687 $data = $event->save();
688 return($this->update_entries(array($id),$data));
689 }
691 return(FALSE);
692 }
695 /*! \brief Returns an array containing all queued entries.
696 @return Array All queued entries as an array.
697 */
698 public function _send($data, $answer_expected= FALSE)
699 {
700 $this->reset_error();
701 $ret = array();
703 if($this->connect()){
704 $this->o_sock->write($data);
705 if ($answer_expected){
706 $str = trim($this->o_sock->read());
707 $entries = $this->xml_to_array($str);
708 if(isset($entries['XML']) && is_array($entries['XML'])){
709 $ret = $entries;
710 if(isset($entries['XML']['ERROR_STRING'])) {
711 $this->set_error($entries['XML']['ERROR_STRING']);
712 new log("debug","DaemonEvent (IDS) ", "gosaSupportDaemon::_send()", array($data=>$data),"FAILED ".$this->get_error());
713 }else{
714 new log("debug","DaemonEvent (IDS) ", "gosaSupportDaemon::_send()", array($data=>$data),"SUCCESS");
715 }
716 }
717 }else{
718 new log("debug","DaemonEvent (IDS) ", "gosaSupportDaemon::_send()", array($data=>$data),"Fire & forget, not result.! ".$this->get_error());
719 }
720 }
721 return($ret);
722 }
725 static function send($header, $to, $data= array(), $answer_expected = FALSE)
726 {
727 $xml_message= "";
729 /* Get communication object */
730 $d= new gosaSupportDaemon(TRUE,10);
732 /* Prepare data */
733 foreach ($data as $key => $value){
734 if(is_array($value)){
735 foreach($value as $sub_val){
736 $xml_message.= "<$key>$sub_value</$key>";
737 }
738 }else{
739 $xml_message.= "<$key>$value</$key>";
740 }
741 }
743 /* Multiple targets? */
744 if (!is_array($to)){
745 $to_targets= array($to);
746 } else {
747 $to_targets= $to;
748 }
750 /* Build target strings */
751 $target ="";
752 foreach($to_targets as $to){
753 $target.= "<target>$to</target>";
754 }
756 return $d->_send("<xml><header>$header</header><source>GOSA</source>$target".$xml_message."</xml>",$answer_expected);
757 }
760 /*! \brief Removes all jobs from the queue that are tiggered with a specific macAddress.
761 @param String $mac The mac address for which we want to remove all jobs.
762 */
763 function clean_queue_from_mac($mac)
764 {
765 global $config;
767 /* First of all we have to check which jobs are startet
768 * for $mac
769 */
770 $xml_msg ="<xml><header>gosa_query_jobdb</header><target>GOSA</target><source>GOSA</source><where><clause><phrase><macaddress>".$mac."</macaddress></phrase></clause></where></xml>";
772 new log("debug","DaemonEvent ", "gosaSupportDaemon::clean_queue_from_mac()", array($mac => $mac)," start cleaning.");
774 $data = $this->_send($xml_msg,TRUE);
775 if(is_array($data) && isset($data['XML'])){
776 $already_aborted = FALSE;
777 foreach($data['XML'] as $name => $entry){
778 if(preg_match("/answer[0-9]*/i",$name)){
779 $entry['STATUS'] = strtoupper($entry['STATUS']);
780 switch($entry['STATUS']){
782 case 'PROCESSING' :
784 /* Send abort event, but only once
785 */
786 if($already_aborted){
787 break;
788 }elseif(class_available("DaemonEvent_faireboot")){
789 $already_aborted = TRUE;
790 $tmp = new DaemonEvent_faireboot($config);
791 $tmp->add_targets(array($mac));
792 $tmp->set_type(TRIGGERED_EVENT);
793 if(!$this->append($tmp)){
794 msg_dialog::display(_("Error"), sprintf(_("Cannot send abort event for entry %s!"),$entry['ID']) , ERROR_DIALOG);
795 new log("debug","DaemonEvent ", "gosaSupportDaemon::clean_queue_from_mac()", array($mac => $mac),
796 "FAILED, could not send 'DaemonEvent_faireboot' for entry ID (".$entry['ID'].") - ".$this->get_error());
797 }else{
798 new log("debug","DaemonEvent ", "gosaSupportDaemon::clean_queue_from_mac()", array($mac => $mac),
799 "SUCCESS, send 'DaemonEvent_faireboot' for entry ID (".$entry['ID'].")");
800 }
801 ;break;
802 }else{
803 /* Couldn't find abort event, just remove entry */
804 }
806 case 'WAITING':
807 case 'ERROR':
808 default :
810 /* Simply remove entries from queue.
811 * Failed or waiting events, can be removed without any trouble.
812 */
813 if(!$this->remove_entries(array($entry['ID']))){
814 msg_dialog::display(_("Error"), sprintf(_("Cannot remove entry %s!"),$entry['ID']) , ERROR_DIALOG);
815 }
816 ;break;
817 }
819 }
820 }
821 }
822 }
825 static function ping($target)
826 {
827 if (tests::is_mac($target)){
828 /* Get communication object */
829 $d= new gosaSupportDaemon(TRUE,0.5);
830 $answer= $d->_send("<xml><header>gosa_ping</header><source>GOSA</source><target>$target</target></xml>", TRUE);
831 return (count($answer) ? TRUE:FALSE);
832 }
834 return (FALSE);
835 }
837 }
839 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
840 ?>