4511c35e0d92f3cfcad1f96aadee5129ddd97f35
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 return($ret);
188 }
191 /*! \brief Checks if the given ids are used queue ids.
192 @param Array The ids we want to check..
193 @return Array An array containing all ids as index and TRUE/FALSE as value.
194 */
195 public function ids_exist($ids)
196 {
197 if(!is_array($ids)){
198 trigger_error("Requires an array as parameter.");
199 return;
200 }
201 $this->reset_error();
203 $ret = array();
205 $xml_msg = "<xml>
206 <header>gosa_query_jobdb</header>
207 <target>GOSA</target>
208 <source>GOSA</source>
209 <where>
210 <clause>
211 <connector>or</connector>";
212 foreach($ids as $id){
213 $xml_msg .= "<phrase>
214 <operator>eq</operator>
215 <id>".$id."</id>
216 </phrase>";
217 }
218 $xml_msg .= "</clause>
219 </where>
220 </xml>";
222 if($this->connect()){
223 $this->o_sock->write($xml_msg);
224 $str = trim($this->o_sock->read());
225 $entries = $this->xml_to_array($str);
226 if(isset($entries['XML']) && is_array($entries['XML'])){
227 foreach($entries['XML'] as $entry){
228 if(isset($entry['ID'])){
229 $ret[] = $entry['ID'];
230 }
231 }
232 }
233 }
234 return($ret);
235 }
238 /*! \brief Returns an entry containing all requested ids.
239 @param Array The IDs of the entries we want to return.
240 @return Array Of the requested entries.
241 */
242 public function get_entries_by_mac($macs)
243 {
244 if(!is_array($macs)){
245 trigger_error("Requires an array as parameter.");
246 return;
247 }
248 $this->reset_error();
250 $ret = array();
252 $xml_msg = "<xml>
253 <header>gosa_query_jobdb</header>
254 <target>GOSA</target>
255 <source>GOSA</source>
256 <where>
257 <clause>
258 <connector>or</connector>";
259 foreach($macs as $mac){
260 $xml_msg .= "<phrase>
261 <operator>eq</operator>
262 <macaddress>".$mac."</macaddress>
263 </phrase>";
264 }
265 $xml_msg .= "</clause>
266 </where>
267 </xml>";
269 if($this->connect()){
270 $this->o_sock->write($xml_msg);
271 $str = trim($this->o_sock->read());
272 $entries = $this->xml_to_array($str);
273 if(isset($entries['XML'])){
274 foreach($entries['XML'] as $name => $entry){
275 if(preg_match("/^ANSWER[0-9]*$/",$name)){
276 $ret[$name] = $entry;
277 }
278 }
279 }
280 }
281 return($ret);
282 }
285 /*! \brief Returns an entry containing all requested ids.
286 @param Array The IDs of the entries we want to return.
287 @return Array Of the requested entries.
288 */
289 public function get_entries_by_id($ids)
290 {
291 if(!is_array($ids)){
292 trigger_error("Requires an array as parameter.");
293 return;
294 }
295 $this->reset_error();
297 $ret = array();
299 $xml_msg = "<xml>
300 <header>gosa_query_jobdb</header>
301 <target>GOSA</target>
302 <source>GOSA</source>
303 <where>
304 <clause>
305 <connector>or</connector>";
306 foreach($ids as $id){
307 $xml_msg .= "<phrase>
308 <operator>eq</operator>
309 <id>".$id."</id>
310 </phrase>";
311 }
312 $xml_msg .= "</clause>
313 </where>
314 </xml>";
316 if($this->connect()){
317 $this->o_sock->write($xml_msg);
318 $str = trim($this->o_sock->read());
319 $entries = $this->xml_to_array($str);
320 if(isset($entries['XML'])){
321 foreach($entries['XML'] as $name => $entry){
322 if(preg_match("/^ANSWER[0-9]*$/",$name)){
323 $ret[$name] = $entry;
324 }
325 }
326 }
327 }
328 return($ret);
329 }
332 /*! \brief Checks if the given id is in use.
333 @param Integer The ID of the entry.
334 @return Boolean TRUE if entry exists.
335 */
336 public function id_exists($id)
337 {
338 if(!is_numeric($id)){
339 trigger_error("Requires an integer as parameter.");
340 return;
341 }
343 $this->reset_error();
345 $xml_msg = "<xml>
346 <header>gosa_query_jobdb</header>
347 <target>GOSA</target>
348 <source>GOSA</source>
349 <where>
350 <clause>
351 <phrase>
352 <operator>eq</operator>
353 <id>".$id."</id>
354 </phrase>
355 </clause>
356 </where>
357 </xml>";
359 if($this->connect()){
360 $this->o_sock->write($xml_msg);
361 $str = trim($this->o_sock->read());
362 $entries = $this->xml_to_array($str);
363 if( isset($entries['XML']['HEADER']) &&
364 $entries['XML']['HEADER']=="answer" &&
365 isset($entries['XML']['ANSWER1'])){
366 return(TRUE);
367 }
368 }
369 return(FALSE);
370 }
373 /*! \brief Returns an entry from the gosaSupportQueue
374 @param Integer The ID of the entry we want to return.
375 @return Array Of the requested entry.
376 */
377 public function get_entry_by_id($id)
378 {
379 if(!is_numeric($id)){
380 trigger_error("Requires an integer as parameter.");
381 return;
382 }
383 $this->reset_error();
385 $ret = array();
386 $xml_msg = "<xml>
387 <header>gosa_query_jobdb</header>
388 <target>GOSA</target>
389 <source>GOSA</source>
390 <where>
391 <clause>
392 <phrase>
393 <operator>eq</operator>
394 <id>".$id."</id>
395 </phrase>
396 </clause>
397 </where>
398 </xml>";
399 if($this->connect()){
400 $this->o_sock->write($xml_msg);
401 $str = trim($this->o_sock->read());
402 $entries = $this->xml_to_array($str);
403 if( isset($entries['XML']['HEADER']) &&
404 $entries['XML']['HEADER']=="answer" &&
405 isset($entries['XML']['ANSWER1'])){
406 $ret = $entries['XML']['ANSWER1'];
407 }
408 }
409 return($ret);
410 }
413 /*! \brief Removes a set of entries from the GOsa support queue.
414 @param Array The IDs to remove.
415 @return Boolean True on success.
416 */
417 public function remove_entries($ids)
418 {
419 if(!is_array($ids)){
420 trigger_error("Requires an array as parameter.");
421 return;
422 }
424 $this->reset_error();
426 $ret = array();
428 $xml_msg = "<xml>
429 <header>gosa_delete_jobdb_entry</header>
430 <target>GOSA</target>
431 <source>GOSA</source>
432 <where>
433 <clause>
434 <connector>or</connector>";
435 foreach($ids as $id){
436 $xml_msg .= "<phrase>
437 <operator>eq</operator>
438 <id>".$id."</id>
439 </phrase>";
440 }
441 $xml_msg .= "</clause>
442 </where>
443 </xml>";
445 if($this->connect()){
446 $this->o_sock->write($xml_msg);
447 $str = $this->o_sock->read();
448 $entries = $this->xml_to_array($str);
449 if(isset($entries['XML']) || isset($entries['COUNT'])){
450 new log("debug","DaemonEvent (IDS) ", "gosaSupportDaemon::remove_entries()", $ids,"SUCCESS");
451 return(TRUE);
452 }else{
453 new log("debug","DaemonEvent (IDS) ", "gosaSupportDaemon::remove_entries()", $ids,"FAILED ".$this->get_error());
454 }
455 }
456 return(FALSE);
457 }
461 /*! \brief Removes an entry from the GOsa support queue.
462 @param Integer The ID of the entry we want to remove.
463 @return Boolean True on success.
464 */
465 public function remove_entry($id)
466 {
467 return($this->remove_entries(array($id)));
468 }
471 /*! \brief Parses the given xml string into an array
472 @param String XML string
473 @return Array Returns an array containing the xml structure.
474 */
475 private function xml_to_array($xml)
476 {
477 $params = array();
478 $level = array();
479 $parser = xml_parser_create_ns();
480 xml_parse_into_struct($parser, $xml, $vals, $index);
482 $err_id = xml_get_error_code($parser);
483 if($err_id){
484 xml_parser_free($parser);
485 }else{
486 xml_parser_free($parser);
488 foreach ($vals as $xml_elem) {
489 if ($xml_elem['type'] == 'open') {
490 if (array_key_exists('attributes',$xml_elem)) {
491 list($level[$xml_elem['level']],$extra) = array_values($xml_elem['attributes']);
492 } else {
493 $level[$xml_elem['level']] = $xml_elem['tag'];
494 }
495 }
496 if ($xml_elem['type'] == 'complete') {
497 $start_level = 1;
498 $php_stmt = '$params';
499 while($start_level < $xml_elem['level']) {
500 $php_stmt .= '[$level['.$start_level.']]';
501 $start_level++;
502 }
503 $php_stmt .= '[$xml_elem[\'tag\']] = $xml_elem[\'value\'];';
504 @eval($php_stmt);
505 }
506 }
507 }
509 if(!isset($params['XML'])){
510 if (!array_key_exists('XML', $params)){
511 $this->set_error(_("Could not parse XML."));
512 }
513 $params = array("COUNT" => 0);
514 }
516 return($params);
517 }
520 /*! \brief Updates an entry with a set of new values,
521 @param Integer The ID of the entry, we want to update.
522 @param Array The variables to update.
523 @return Boolean Returns TRUE on success.
524 */
525 public function update_entries($ids,$data)
526 {
527 $this->reset_error();
528 if(!is_array($ids)){
529 trigger_error("Requires an array as first parameter.");
530 return;
531 }
533 if(!is_array($data)){
534 trigger_error("Requires an array as second parameter.");
535 return;
536 }
538 $attr = "";
539 foreach($data as $key => $value){
540 if(is_array($value)){
541 foreach($value as $sub_value){
542 $attr.= "<$key>".strtolower($sub_value)."</$key>\n";
543 }
544 }else{
545 $attr.= "<$key>".strtolower($value)."</$key>\n";
546 }
547 }
549 $xml_msg = "<xml>
550 <header>gosa_update_status_jobdb_entry</header>
551 <target>GOSA</target>
552 <source>GOSA</source>
553 <where>
554 <clause>
555 <connector>or</connector>";
556 foreach($ids as $id){
557 $xml_msg .= "<phrase>
558 <operator>eq</operator>
559 <id>".$id."</id>
560 </phrase>";
561 }
562 $xml_msg .= "</clause>
563 </where>
564 <update>
565 ".$attr."
566 </update>
567 </xml>";
569 if($this->connect()){
571 $this->o_sock->write($xml_msg);
572 $str = trim($this->o_sock->read());
573 $entries = $this->xml_to_array($str);
574 if(isset($entries['XML'])){
575 if(isset($entries['XML']['ERROR_STRING'])) {
576 $this->set_error($entries['XML']['ERROR_STRING']);
577 new log("debug","DaemonEvent (IDS) ", "gosaSupportDaemon::update_entries()", $ids,"FAILED setting (".$attr.") error was ".$this->get_error());
578 return(FALSE);
579 }
580 new log("debug","DaemonEvent (IDS) ", "gosaSupportDaemon::update_entries()", $ids,"SUCCESS");
581 return(TRUE);
582 }
583 }
584 return(FALSE);
585 }
588 /*! \brief Returns the number of currently queued objects.
589 @return Integer
590 */
591 public function number_of_queued_entries()
592 {
593 $xml_msg ="<xml><header>gosa_count_jobdb</header><target>GOSA</target><source>GOSA</source></xml>";
594 $this->connect();
595 if($this->connect()){
596 $this->o_sock->write($xml_msg);
597 $str = trim($this->o_sock->read());
598 $entries = $this->xml_to_array($str);
599 if(isset($entries['XML'])){
600 return($entries['XML']['COUNT']);
601 }
602 }
603 return(-1);
604 }
607 public function send_data($header, $to, $data= array(), $answer_expected = FALSE)
608 {
609 $xml_message= "";
611 /* Prepare data */
612 foreach ($data as $key => $value){
613 if(is_array($value)){
614 foreach($value as $sub_val){
615 $xml_message.= "<$key>$sub_value</$key>";
616 }
617 }else{
618 $xml_message.= "<$key>$value</$key>";
619 }
620 }
622 /* Multiple targets? */
623 if (!is_array($to)){
624 $to_targets= array($to);
625 } else {
626 $to_targets= $to;
627 }
629 /* Build target strings */
630 $target ="";
631 foreach($to_targets as $to){
632 $target.= "<target>$to</target>";
633 }
635 return $this->_send("<xml><header>$header</header><source>GOSA</source>$target".$xml_message."</xml>",$answer_expected);
636 }
639 /* Allows simply appending a new DaemonEvent
640 */
641 public function append($event)
642 {
643 if(!($event instanceof DaemonEvent)){
644 return(FALSE);
645 }
647 $this->reset_error();
649 /* Add to queue if new
650 */
651 if($event->is_new()){
653 $request_answer = FALSE;
654 if($event->get_type() == SCHEDULED_EVENT){
655 $action = $event->get_schedule_action();
656 $request_answer = TRUE;
657 }elseif($event->get_type() == TRIGGERED_EVENT){
658 $action = $event->get_trigger_action();
659 }else{
660 trigger_error("Unknown type of queue event given.");
661 return(FALSE);
662 }
664 /* Get event informations, like targets..
665 */
666 $targets = $event->get_targets();
667 $data = $event->save();
669 /* Append an entry for each target
670 */
671 foreach($targets as $target){
672 $data['macaddress'] = $target;
673 $this->send_data($action,$target,$data,$request_answer);
675 if($this->is_error()){
676 return(FALSE);
677 }
678 }
679 return(TRUE);
680 }else{
682 /* Updated edited entry.
683 */
684 $id = $event->get_id();
685 $data = $event->save();
686 return($this->update_entries(array($id),$data));
687 }
689 return(FALSE);
690 }
693 /*! \brief Returns an array containing all queued entries.
694 @return Array All queued entries as an array.
695 */
696 public function _send($data, $answer_expected= FALSE)
697 {
698 $this->reset_error();
699 $ret = array();
701 if($this->connect()){
702 $this->o_sock->write($data);
703 if ($answer_expected){
704 $str = trim($this->o_sock->read());
705 $entries = $this->xml_to_array($str);
706 if(isset($entries['XML']) && is_array($entries['XML'])){
707 $ret = $entries;
708 if(isset($entries['XML']['ERROR_STRING'])) {
709 $this->set_error($entries['XML']['ERROR_STRING']);
710 new log("debug","DaemonEvent (IDS) ", "gosaSupportDaemon::_send()", array($data=>$data),"FAILED ".$this->get_error());
711 }else{
712 new log("debug","DaemonEvent (IDS) ", "gosaSupportDaemon::_send()", array($data=>$data),"SUCCESS");
713 }
714 }
715 }else{
716 new log("debug","DaemonEvent (IDS) ", "gosaSupportDaemon::_send()", array($data=>$data),"Fire & forget, not result.! ".$this->get_error());
717 }
718 }
719 return($ret);
720 }
723 static function send($header, $to, $data= array(), $answer_expected = FALSE)
724 {
725 $xml_message= "";
727 /* Get communication object */
728 $d= new gosaSupportDaemon(TRUE,10);
730 /* Prepare data */
731 foreach ($data as $key => $value){
732 if(is_array($value)){
733 foreach($value as $sub_val){
734 $xml_message.= "<$key>$sub_value</$key>";
735 }
736 }else{
737 $xml_message.= "<$key>$value</$key>";
738 }
739 }
741 /* Multiple targets? */
742 if (!is_array($to)){
743 $to_targets= array($to);
744 } else {
745 $to_targets= $to;
746 }
748 /* Build target strings */
749 $target ="";
750 foreach($to_targets as $to){
751 $target.= "<target>$to</target>";
752 }
754 return $d->_send("<xml><header>$header</header><source>GOSA</source>$target".$xml_message."</xml>",$answer_expected);
755 }
758 /*! \brief Removes all jobs from the queue that are tiggered with a specific macAddress.
759 @param String $mac The mac address for which we want to remove all jobs.
760 */
761 function clean_queue_from_mac($mac)
762 {
763 global $config;
765 /* First of all we have to check which jobs are startet
766 * for $mac
767 */
768 $xml_msg ="<xml><header>gosa_query_jobdb</header><target>GOSA</target><source>GOSA</source><where><clause><phrase><macaddress>".$mac."</macaddress></phrase></clause></where></xml>";
770 new log("debug","DaemonEvent ", "gosaSupportDaemon::clean_queue_from_mac()", array($mac => $mac)," start cleaning.");
772 $data = $this->_send($xml_msg,TRUE);
773 if(is_array($data) && isset($data['XML'])){
774 $already_aborted = FALSE;
775 foreach($data['XML'] as $name => $entry){
776 if(preg_match("/answer[0-9]*/i",$name)){
777 $entry['STATUS'] = strtoupper($entry['STATUS']);
778 switch($entry['STATUS']){
780 case 'PROCESSING' :
782 /* Send abort event, but only once
783 */
784 if($already_aborted){
785 break;
786 }elseif(class_available("DaemonEvent_faireboot")){
787 $already_aborted = TRUE;
788 $tmp = new DaemonEvent_faireboot($config);
789 $tmp->add_targets(array($mac));
790 $tmp->set_type(TRIGGERED_EVENT);
791 if(!$this->append($tmp)){
792 msg_dialog::display(_("Error"), sprintf(_("Cannot send abort event for entry: %s"),$entry['ID']) , ERROR_DIALOG);
793 new log("debug","DaemonEvent ", "gosaSupportDaemon::clean_queue_from_mac()", array($mac => $mac),
794 "FAILED, could not send 'DaemonEvent_faireboot' for entry ID (".$entry['ID'].") - ".$this->get_error());
795 }else{
796 new log("debug","DaemonEvent ", "gosaSupportDaemon::clean_queue_from_mac()", array($mac => $mac),
797 "SUCCESS, send 'DaemonEvent_faireboot' for entry ID (".$entry['ID'].")");
798 }
799 ;break;
800 }else{
801 /* Couldn't find abort event, just remove entry */
802 }
804 case 'WAITING':
805 case 'ERROR':
806 default :
808 /* Simply remove entries from queue.
809 * Failed or waiting events, can be removed without any trouble.
810 */
811 if(!$this->remove_entries(array($entry['ID']))){
812 msg_dialog::display(_("Error"), sprintf(_("Cannot remove entry: %s"),$entry['ID']) , ERROR_DIALOG);
813 }
814 ;break;
815 }
817 }
818 }
819 }
820 }
823 static function ping($target)
824 {
825 if (tests::is_mac($target)){
826 /* Get communication object */
827 $d= new gosaSupportDaemon(TRUE,0.5);
828 $answer= $d->_send("<xml><header>gosa_ping</header><source>GOSA</source><target>$target</target></xml>", TRUE);
829 return (count($answer) ? TRUE:FALSE);
830 }
832 return (FALSE);
833 }
835 }
837 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
838 ?>