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=0.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 }
79 return($this->is_connected);
80 }
83 /*! \brief Disconnect from gosa daemon.
84 */
85 public function disconnect()
86 {
87 $this->o_sock->close();
88 $this->is_connected = FALSE;
89 }
92 /*! \brief Sets an error message, which can be returned with get_error().
93 @param string The Error message,
94 */
95 private function set_error($str)
96 {
97 $this->b_error = TRUE;
98 $this->s_error = $str;
99 }
102 /*! \brief Sets an error message, which can be returned with get_error().
103 @param string The Error message,
104 */
105 private function reset_error()
106 {
107 $this->b_error = FALSE;
108 $this->s_error = "";
109 }
112 /*! \brief Checks if an error occured.
113 @return boolean returns TRUE or FALSE, whether there is an error or not.
114 */
115 public function is_error()
116 {
117 return($this->b_error);
118 }
121 /*! \brief Returns the last error.
122 @return Returns the last error.
123 */
124 public function get_error()
125 {
126 $str = $this->s_error;
127 $str = preg_replace("/ /"," ",$str);
128 return($str);
129 }
132 /*! \brief Returns an array containing all queued entries.
133 @return Array All queued entries as an array.
134 */
135 public function get_queued_entries($event_types = array("*"),$from=-1,$to=-1,$sort="timestamp DESC")
136 {
137 $this->reset_error();
138 $ret = array();
140 $tags = "";
141 foreach($event_types as $type){
142 $tags .= "<phrase><headertag>".$type."</headertag></phrase>";
143 }
144 if(count($event_types) > 1){
145 $tags = "<connector>or</connector>".$tags;
146 }
147 if(count($event_types)){
148 $tags = "<where><clause>".$tags."</clause></where>";
149 }
151 $xml_msg = "<xml>
152 <header>gosa_query_jobdb</header>
153 <target>GOSA</target>
154 <source>GOSA</source>
155 ".$tags."
157 <orderby>".$sort."</orderby>";
158 if($from != -1 && $to != -1){
159 $xml_msg.= "
160 <limit>
161 <from>".$from."</from>
162 <to>".$to."</to>
163 </limit>";
164 }
165 $xml_msg.= "
166 </xml>";
168 if($this->connect()){
169 $this->o_sock->write($xml_msg);
170 $str = trim($this->o_sock->read());
171 $entries = $this->xml_to_array($str);
172 if(isset($entries['XML']) && is_array($entries['XML'])){
174 /* Check if returned values represent a valid answer */
175 if(isset($entries['XML'])){
177 /* Unset header tags */
178 foreach(array("HEADER","SOURCE","TARGET") as $type){
179 unset($entries['XML'][$type]);
180 }
181 $ret = $entries['XML'];
182 }
183 }
184 }
186 return($ret);
187 }
190 /*! \brief Checks if the given ids are used queue ids.
191 @param Array The ids we want to check..
192 @return Array An array containing all ids as index and TRUE/FALSE as value.
193 */
194 public function ids_exist($ids)
195 {
196 if(!is_array($ids)){
197 trigger_error("Requires an array as parameter.");
198 return;
199 }
200 $this->reset_error();
202 $ret = array();
204 $xml_msg = "<xml>
205 <header>gosa_query_jobdb</header>
206 <target>GOSA</target>
207 <source>GOSA</source>
208 <where>
209 <clause>
210 <connector>or</connector>";
211 foreach($ids as $id){
212 $xml_msg .= "<phrase>
213 <operator>eq</operator>
214 <id>".$id."</id>
215 </phrase>";
216 }
217 $xml_msg .= "</clause>
218 </where>
219 </xml>";
221 if($this->connect()){
222 $this->o_sock->write($xml_msg);
223 $str = trim($this->o_sock->read());
224 $entries = $this->xml_to_array($str);
225 if(isset($entries['XML']) && is_array($entries['XML'])){
226 foreach($entries['XML'] as $entry){
227 if(isset($entry['ID'])){
228 $ret[] = $entry['ID'];
229 }
230 }
231 }
232 }
233 return($ret);
234 }
237 /*! \brief Returns an entry containing all requested ids.
238 @param Array The IDs of the entries we want to return.
239 @return Array Of the requested entries.
240 */
241 public function get_entries_by_id($ids)
242 {
243 if(!is_array($ids)){
244 trigger_error("Requires an array as parameter.");
245 return;
246 }
247 $this->reset_error();
249 $ret = array();
251 $xml_msg = "<xml>
252 <header>gosa_query_jobdb</header>
253 <target>GOSA</target>
254 <source>GOSA</source>
255 <where>
256 <clause>
257 <connector>or</connector>";
258 foreach($ids as $id){
259 $xml_msg .= "<phrase>
260 <operator>eq</operator>
261 <id>".$id."</id>
262 </phrase>";
263 }
264 $xml_msg .= "</clause>
265 </where>
266 </xml>";
268 if($this->connect()){
269 $this->o_sock->write($xml_msg);
270 $str = trim($this->o_sock->read());
271 $entries = $this->xml_to_array($str);
272 if(isset($entries['XML'])){
273 foreach($entries['XML'] as $name => $entry){
274 if(preg_match("/^ANSWER[0-9]*$/",$name)){
275 $ret[$name] = $entry;
276 }
277 }
278 }
279 }
280 return($ret);
281 }
284 /*! \brief Checks if the given id is in use.
285 @param Integer The ID of the entry.
286 @return Boolean TRUE if entry exists.
287 */
288 public function id_exists($id)
289 {
290 if(!is_numeric($id)){
291 trigger_error("Requires an integer as parameter.");
292 return;
293 }
295 $this->reset_error();
297 $xml_msg = "<xml>
298 <header>gosa_query_jobdb</header>
299 <target>GOSA</target>
300 <source>GOSA</source>
301 <where>
302 <clause>
303 <phrase>
304 <operator>eq</operator>
305 <id>".$id."</id>
306 </phrase>
307 </clause>
308 </where>
309 </xml>";
311 if($this->connect()){
312 $this->o_sock->write($xml_msg);
313 $str = trim($this->o_sock->read());
314 $entries = $this->xml_to_array($str);
315 if( isset($entries['XML']['HEADER']) &&
316 $entries['XML']['HEADER']=="answer" &&
317 isset($entries['XML']['ANSWER1'])){
318 return(TRUE);
319 }
320 }
321 return(FALSE);
322 }
325 /*! \brief Returns an entry from the gosaSupportQueue
326 @param Integer The ID of the entry we want to return.
327 @return Array Of the requested entry.
328 */
329 public function get_entry_by_id($id)
330 {
331 if(!is_numeric($id)){
332 trigger_error("Requires an integer as parameter.");
333 return;
334 }
335 $this->reset_error();
337 $ret = array();
338 $xml_msg = "<xml>
339 <header>gosa_query_jobdb</header>
340 <target>GOSA</target>
341 <source>GOSA</source>
342 <where>
343 <clause>
344 <phrase>
345 <operator>eq</operator>
346 <id>".$id."</id>
347 </phrase>
348 </clause>
349 </where>
350 </xml>";
351 if($this->connect()){
352 $this->o_sock->write($xml_msg);
353 $str = trim($this->o_sock->read());
354 $entries = $this->xml_to_array($str);
355 if( isset($entries['XML']['HEADER']) &&
356 $entries['XML']['HEADER']=="answer" &&
357 isset($entries['XML']['ANSWER1'])){
358 $ret = $entries['XML']['ANSWER1'];
359 }
360 }
361 return($ret);
362 }
365 /*! \brief Removes a set of entries from the GOsa support queue.
366 @param Array The IDs to remove.
367 @return Boolean True on success.
368 */
369 public function remove_entries($ids)
370 {
371 if(!is_array($ids)){
372 trigger_error("Requires an array as parameter.");
373 return;
374 }
376 $this->reset_error();
378 $ret = array();
380 $xml_msg = "<xml>
381 <header>gosa_delete_jobdb_entry</header>
382 <target>GOSA</target>
383 <source>GOSA</source>
384 <where>
385 <clause>
386 <connector>or</connector>";
387 foreach($ids as $id){
388 $xml_msg .= "<phrase>
389 <operator>eq</operator>
390 <id>".$id."</id>
391 </phrase>";
392 }
393 $xml_msg .= "</clause>
394 </where>
395 </xml>";
397 if($this->connect()){
398 $this->o_sock->write($xml_msg);
399 $str = $this->o_sock->read();
400 $entries = $this->xml_to_array($str);
401 if(isset($entries['XML']) || isset($entries['COUNT'])){
402 return(TRUE);
403 }
404 }
405 return(FALSE);
406 }
410 /*! \brief Removes an entry from the GOsa support queue.
411 @param Integer The ID of the entry we want to remove.
412 @return Boolean True on success.
413 */
414 public function remove_entry($id)
415 {
416 $this->reset_error();
418 $xml_msg = "<xml>
419 <header>gosa_delete_jobdb_entry</header>
420 <target>GOSA</target>
421 <source>GOSA</source>
422 <where>
423 <clause>
424 <phrase>
425 <operator>eq</operator>
426 <id>".$id."</id>
427 </phrase>
428 </clause>
429 </where>
430 </xml>";
431 if($this->connect()){
432 $this->o_sock->write($xml_msg);
433 $str = $this->o_sock->read();
434 $entries = $this->xml_to_array($str);
435 if(isset($entries['XML'])){
436 return(TRUE);
437 }
438 }
439 return(FALSE);
440 }
443 /*! \brief Parses the given xml string into an array
444 @param String XML string
445 @return Array Returns an array containing the xml structure.
446 */
447 private function xml_to_array($xml)
448 {
449 $params = array();
450 $level = array();
451 $parser = xml_parser_create_ns();
452 xml_parse_into_struct($parser, $xml, $vals, $index);
454 $err_id = xml_get_error_code($parser);
455 if($err_id){
456 xml_parser_free($parser);
457 }else{
458 xml_parser_free($parser);
460 foreach ($vals as $xml_elem) {
461 if ($xml_elem['type'] == 'open') {
462 if (array_key_exists('attributes',$xml_elem)) {
463 list($level[$xml_elem['level']],$extra) = array_values($xml_elem['attributes']);
464 } else {
465 $level[$xml_elem['level']] = $xml_elem['tag'];
466 }
467 }
468 if ($xml_elem['type'] == 'complete') {
469 $start_level = 1;
470 $php_stmt = '$params';
471 while($start_level < $xml_elem['level']) {
472 $php_stmt .= '[$level['.$start_level.']]';
473 $start_level++;
474 }
475 $php_stmt .= '[$xml_elem[\'tag\']] = $xml_elem[\'value\'];';
476 @eval($php_stmt);
477 }
478 }
479 }
481 if(!isset($params['XML'])){
482 if (!array_key_exists('XML', $params)){
483 $this->set_error(_("Could not parse XML."));
484 }
485 $params = array("COUNT" => 0);
486 }
488 return($params);
489 }
492 /*! \brief Updates an entry with a set of new values,
493 @param Integer The ID of the entry, we want to update.
494 @param Array The variables to update.
495 @return Boolean Returns TRUE on success.
496 */
497 public function update_entries($ids,$data)
498 {
499 $this->reset_error();
500 if(!is_array($ids)){
501 trigger_error("Requires an array as first parameter.");
502 return;
503 }
505 if(!is_array($data)){
506 trigger_error("Requires an array as second parameter.");
507 return;
508 }
510 $attr = "";
511 foreach($data as $key => $value){
512 if(is_array($value)){
513 foreach($value as $sub_value){
514 $attr.= "<$key>".strtolower($sub_value)."</$key>\n";
515 }
516 }else{
517 $attr.= "<$key>".strtolower($value)."</$key>\n";
518 }
519 }
521 $xml_msg = "<xml>
522 <header>gosa_update_status_jobdb_entry</header>
523 <target>GOSA</target>
524 <source>GOSA</source>
525 <where>
526 <clause>
527 <connector>or</connector>";
528 foreach($ids as $id){
529 $xml_msg .= "<phrase>
530 <operator>eq</operator>
531 <id>".$id."</id>
532 </phrase>";
533 }
534 $xml_msg .= "</clause>
535 </where>
536 <update>
537 ".$attr."
538 </update>
539 </xml>";
541 if($this->connect()){
543 $this->o_sock->write($xml_msg);
544 $str = trim($this->o_sock->read());
545 $entries = $this->xml_to_array($str);
546 if(isset($entries['XML'])){
547 if(isset($entries['XML']['ERROR_STRING'])) {
548 $this->set_error($entries['XML']['ERROR_STRING']);
549 return(FALSE);
550 }
551 return(TRUE);
552 }
553 }
554 return(FALSE);
555 }
558 /*! \brief Returns the number of currently queued objects.
559 @return Integer
560 */
561 public function number_of_queued_entries()
562 {
563 $xml_msg ="<xml><header>gosa_count_jobdb</header><target>GOSA</target><source>GOSA</source></xml>";
564 $this->connect();
565 if($this->connect()){
566 $this->o_sock->write($xml_msg);
567 $str = trim($this->o_sock->read());
568 $entries = $this->xml_to_array($str);
569 if(isset($entries['XML'])){
570 return($entries['XML']['COUNT']);
571 }
572 }
573 return(-1);
574 }
577 public function send_data($header, $to, $data= array(), $answer_expected = FALSE)
578 {
579 $xml_message= "";
581 /* Prepare data */
582 foreach ($data as $key => $value){
583 if(is_array($value)){
584 foreach($value as $sub_val){
585 $xml_message.= "<$key>$sub_value</$key>";
586 }
587 }else{
588 $xml_message.= "<$key>$value</$key>";
589 }
590 }
592 /* Multiple targets? */
593 if (!is_array($to)){
594 $to_targets= array($to);
595 } else {
596 $to_targets= $to;
597 }
599 /* Build target strings */
600 $target ="";
601 foreach($to_targets as $to){
602 $target.= "<target>$to</target>";
603 }
605 return $this->_send("<xml><header>$header</header><source>GOSA</source>$target".$xml_message."</xml>",$answer_expected);
606 }
609 /* Allows simply appending a new DaemonEvent
610 */
611 public function append($event)
612 {
613 if(!($event instanceof DaemonEvent)){
614 return(FALSE);
615 }
617 $this->reset_error();
619 /* Add to queue if new
620 */
621 if($event->is_new()){
623 $request_answer = FALSE;
624 if($event->get_type() == SCHEDULED_EVENT){
625 $action = $event->get_schedule_action();
626 $request_answer = TRUE;
627 }elseif($event->get_type() == TRIGGERED_EVENT){
628 $action = $event->get_trigger_action();
629 }else{
630 trigger_error("Unknown type of queue event given.");
631 return(FALSE);
632 }
634 /* Get event informations, like targets..
635 */
636 $targets = $event->get_targets();
637 $data = $event->save();
639 /* Append an entry for each target
640 */
641 foreach($targets as $target){
642 $data['macaddress'] = $target;
643 $this->send_data($action,$target,$data,$request_answer);
645 if($this->is_error()){
646 return(FALSE);
647 }
648 }
649 return(TRUE);
650 }else{
652 /* Updated edited entry.
653 */
654 $id = $event->get_id();
655 $data = $event->save();
656 return($this->update_entries(array($id),$data));
657 }
659 return(FALSE);
660 }
663 /*! \brief Returns an array containing all queued entries.
664 @return Array All queued entries as an array.
665 */
666 public function _send($data, $answer_expected= FALSE)
667 {
668 $this->reset_error();
669 $ret = array();
671 if($this->connect()){
672 $this->o_sock->write($data);
673 if ($answer_expected){
674 $str = trim($this->o_sock->read());
675 $entries = $this->xml_to_array($str);
676 if(isset($entries['XML']) && is_array($entries['XML'])){
677 $ret = $entries;
678 if(isset($entries['XML']['ERROR_STRING'])) {
679 $this->set_error($entries['XML']['ERROR_STRING']);
680 }
681 }
682 }
683 }
684 return($ret);
685 }
688 static function send($header, $to, $data= array(), $answer_expected = FALSE)
689 {
690 $xml_message= "";
692 /* Get communication object */
693 $d= new gosaSupportDaemon(TRUE,10);
695 /* Prepare data */
696 foreach ($data as $key => $value){
697 if(is_array($value)){
698 foreach($value as $sub_val){
699 $xml_message.= "<$key>$sub_value</$key>";
700 }
701 }else{
702 $xml_message.= "<$key>$value</$key>";
703 }
704 }
706 /* Multiple targets? */
707 if (!is_array($to)){
708 $to_targets= array($to);
709 } else {
710 $to_targets= $to;
711 }
713 /* Build target strings */
714 $target ="";
715 foreach($to_targets as $to){
716 $target.= "<target>$to</target>";
717 }
719 return $d->_send("<xml><header>$header</header><source>GOSA</source>$target".$xml_message."</xml>",$answer_expected);
720 }
723 /*! \brief Removes all jobs from the queue that are tiggered with a specific macAddress.
724 @param String $mac The mac address for which we want to remove all jobs.
725 */
726 function clean_queue_from_mac($mac)
727 {
728 global $config;
730 /* First of all we have to check which jobs are startet
731 * for $mac
732 */
733 $xml_msg ="
734 <xml>
735 <header>gosa_query_jobdb</header>
736 <target>GOSA</target>
737 <source>GOSA</source>
738 <where>
739 <clause>
740 <phrase>
741 <macaddress>".$mac."</macaddress>
742 </phrase>
743 </clause>
744 </where>
745 </xml>
746 ";
748 $data = $this->_send($xml_msg,TRUE);
749 if(is_array($data) && isset($data['XML'])){
750 $already_aborted = FALSE;
751 foreach($data['XML'] as $name => $entry){
752 if(preg_match("/answer[0-9]*/i",$name)){
753 $entry['STATUS'] = strtoupper($entry['STATUS']);
754 switch($entry['STATUS']){
756 case 'PROCESSING' :
758 /* Send abort event, but only once
759 */
760 if($already_aborted){
761 break;
762 }elseif(class_available("DaemonEvent_faireboot")){
763 $already_aborted = TRUE;
764 $tmp = new DaemonEvent_faireboot($config);
765 $tmp->add_targets(array($mac));
766 $tmp->set_type(TRIGGERED_EVENT);
767 if(!$this->append($tmp)){
768 msg_dialog::display(_("Error"), sprintf(_("Cannot send abort event for entry: %s"),$entry['ID']) , ERROR_DIALOG);
769 }
770 ;break;
771 }else{
772 /* Couldn't find abort event, just remove entry */
773 }
775 case 'WAITING':
776 case 'ERROR':
777 default :
779 /* Simply remove entries from queue.
780 * Failed or waiting events, can be removed without any trouble.
781 */
782 if(!$this->remove_entries(array($entry['ID']))){
783 msg_dialog::display(_("Error"), sprintf(_("Cannot remove entry: %s"),$entry['ID']) , ERROR_DIALOG);
784 }
785 ;break;
786 }
788 }
789 }
790 }
791 }
794 static function ping($target)
795 {
796 if (tests::is_mac($target)){
797 /* Get communication object */
798 $d= new gosaSupportDaemon(TRUE,0.5);
799 $answer= $d->_send("<xml><header>gosa_ping</header><source>GOSA</source><target>$target</target></xml>", TRUE);
800 return (count($answer) ? TRUE:FALSE);
801 }
803 return (FALSE);
804 }
806 }
808 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
809 ?>