1 <?php
3 class configRegistry{
5 public $config = NULL;
6 public $properties = array();
7 public $ldapStoredProperties = array();
8 public $fileStoredProperties = array();
9 public $classToName = array();
11 public $status = 'none';
13 // Excludes property values defined in ldap
14 public $ignoreLdapProperties = FALSE;
16 function __construct($config)
17 {
18 $this->config = &$config;
19 $this->reload();
20 }
22 function reload($force = FALSE)
23 {
24 // Do not reload the properties everytime, once we have
25 // everything loaded and registrered skip the reload.
26 // Status is 'finished' once we had a ldap connection (logged in)
27 if(!$force && $this->status == 'finished') return;
29 // Reset everything
30 $this->ldapStoredProperties = array();
31 $this->fileStoredProperties = array();
32 $this->properties = array();
33 $this->mapByName = array();
35 // Search for config flags defined in the config file (TAB section)
36 foreach($this->config->data['TABS'] as $tabname => $tabdefs){
37 foreach($tabdefs as $info){
39 // Check if the info is valid
40 if(isset($info['NAME']) && isset($info['CLASS'])){
42 // Check if there is nore than just the plugin definition
43 if(count($info) > 2){
44 foreach($info as $name => $value){
46 if(!in_array($name, array('CLASS','NAME'))){
47 $class= $info['CLASS'];
48 $this->fileStoredProperties[$class][strtolower($name)] = $value;
49 }
50 }
51 }
52 }
53 }
54 }
56 // Search for config flags defined in the config file (MENU section)
57 foreach($this->config->data['MENU'] as $section => $entries){
58 foreach($entries as $entry){
59 if(count($entry) > 2 && isset($entry['CLASS'])){
60 $class = $entry['CLASS'];
61 foreach($entry as $name => $value){
62 if(!in_array($name, array('CLASS','ACL'))){
63 $this->fileStoredProperties[strtolower($class)][strtolower($name)] = $value;
64 }
65 }
66 }
67 }
68 }
70 // Search for config flags defined in the config file (MAIN section)
71 foreach($this->config->data['MAIN'] as $name => $value){
72 $this->fileStoredProperties['core'][strtolower($name)] = $value;
73 }
75 // Search for config flags defined in the config file (Current LOCATION section)
76 if(isset($this->config->current)){
77 foreach($this->config->current as $name => $value){
78 $this->fileStoredProperties['core'][strtolower($name)] = $value;
79 }
80 }
82 // Skip searching for LDAP defined properties if 'ignoreLdapProperties' is set to 'true'
83 // in the config.
84 $this->ignoreLdapProperties = FALSE;
85 if(isset($this->fileStoredProperties['core'][strtolower('ignoreLdapProperties')]) &&
86 preg_match("/(true|on)/i", $this->fileStoredProperties['core'][strtolower('ignoreLdapProperties')])){
87 $this->ignoreLdapProperties = TRUE;
88 }
90 // Search for all config flags defined in the LDAP - BUT only if we ARE logged in.
91 if(!empty($this->config->current['CONFIG'])){
92 $ldap = $this->config->get_ldap_link();
93 $ldap->cd($this->config->current['CONFIG']);
94 $ldap->search('(&(objectClass=gosaConfig)(gosaSetting=*))', array('cn','gosaSetting'));
95 while($attrs = $ldap->fetch()){
96 $class = $attrs['cn'][0];
97 for($i=0; $i<$attrs['gosaSetting']['count']; $i++){
98 list($name,$value) = preg_split("/:/",$attrs['gosaSetting'][$i],2);
99 $this->ldapStoredProperties[$class][$name] = $value;
100 }
101 }
102 $this->status = 'finished';
103 }
105 global $class_mapping;
106 foreach ($class_mapping as $cname => $path){
107 $cmethods = get_class_methods($cname);
108 if (is_array($cmethods) && in_array_ics('plInfo',$cmethods)){
110 // Get plugin definitions
111 $def = call_user_func(array($cname, 'plInfo'));;
113 // Register Post Events (postmodfiy,postcreate,postremove,checkhook)
114 if(count($def)){
116 $name = $cname;
117 $name = (isset($def['plShortName'])) ? $def['plShortName'] : $cname;
118 $name = (isset($def['plDescription'])) ? $def['plDescription'] : $cname;
120 $this->classToName[$cname] = $name;
121 $data = array('name' => 'postcreate','type' => 'command');
122 $this->register($cname, $data);
123 $data = array('name' => 'postremove','type' => 'command');
124 $this->register($cname, $data);
125 $data = array('name' => 'postmodify','type' => 'command');
126 $this->register($cname, $data);
127 $data = array('name' => 'check', 'type' => 'command');
128 $this->register($cname, $data);
129 }
131 if(isset($def['plProperties'])){
132 foreach($def['plProperties'] as $property){
133 $this->register($cname, $property);
134 }
135 }
136 }
137 }
138 }
140 function register($class,$data)
141 {
142 $id = count($this->properties);
143 $this->properties[$id] = new gosaProperty($this,$class,$data);
144 $p = strtolower("{$class}::{$data['name']}");
145 $this->mapByName[$p] = $id;
146 }
148 public function getAllProperties()
149 {
150 return($this->properties);
151 }
153 function propertyExists($class,$name)
154 {
155 $p = strtolower("{$class}::{$name}");
156 return(isset($this->mapByName[$p]));
157 }
159 private function getId($class,$name)
160 {
161 $p = strtolower("{$class}::{$name}");
162 if(!isset($this->mapByName[$p])){
163 return(-1);
164 }
165 return($this->mapByName[$p]);
166 }
168 function getProperty($class,$name)
169 {
170 if($this->propertyExists($class,$name)){
171 return($this->properties[$this->getId($class,$name)]);
172 }
173 return(NULL);
174 }
176 function getPropertyValue($class,$name)
177 {
178 if($this->propertyExists($class,$name)){
179 $tmp = $this->getProperty($class,$name);
180 return($tmp->getValue());
181 }
182 return("");
183 }
185 function setPropertyValue($class,$name, $value)
186 {
187 if($this->propertyExists($class,$name)){
188 $tmp = $this->getProperty($class,$name);
189 return($tmp->setValue($value));
190 }
191 return("");
192 }
194 function saveChanges()
195 {
196 $migrate = array();
197 foreach($this->properties as $prop){
199 // Is this property modified
200 if(in_array($prop->getStatus(),array('modified','removed'))){
202 // Check if we've to migrate something before we can make the changes effective.
203 if($prop->migrationRequired()){
204 $migrate[] = $prop;
205 }else{
206 $prop->save();
207 }
208 }
209 }
210 return($migrate);
211 }
212 }
215 class gosaProperty
216 {
217 protected $name = "";
218 protected $class = "";
219 protected $value = "";
220 protected $tmp_value = ""; // Used when modified but not saved
221 protected $type = "string";
222 protected $default = "";
223 protected $defaults = "";
224 protected $description = "";
225 protected $check = "";
226 protected $migrate = "";
227 protected $mandatory = FALSE;
228 protected $group = "default";
229 protected $parent = NULL;
230 protected $data = array();
232 protected $migrationClass = NULL;
234 /*! The current property status
235 * 'ldap' Property is stored in ldap
236 * 'file' Property is stored in the config file
237 * 'undefined' Property is currently not stored anywhere
238 * 'modified' Property has been modified (should be saved)
239 */
240 protected $status = 'undefined';
242 protected $attributes = array('name','type','default','description','check',
243 'migrate','mandatory','group','defaults');
248 function __construct($parent,$classname,$data)
249 {
250 // Set some basic infos
251 $this->parent = &$parent;
252 $this->class = $classname;
253 $this->data = $data;
255 // Get all relevant information from the data array (comes from plInfo)
256 foreach($this->attributes as $aName){
257 if(isset($data[$aName])){
258 $this->$aName = $data[$aName];
259 }
260 }
262 // Initialize with the current value
263 $this->_restoreCurrentValue();
265 }
267 function migrationRequired()
268 {
269 // Instantiate migration class
270 if(!empty($this->migrate) && $this->migrationClass == NULL){
271 if(!class_available($this->migrate)){
272 trigger_error("Cannot start migration for gosaProperty::'{$this->getName()}' class not found ({$this->migrate})!");
273 }else{
274 $class = $this->migrate;
275 $tmp = new $class($this->parent->config,$this);
276 if(! $tmp instanceof propertyMigration){
277 trigger_error("Cannot start migration for gosaProperty::'{$this->getName()}' doesn't implement propertyMigration!");
278 }else{
279 $this->migrationClass = $tmp;
280 }
281 }
282 }
283 if(empty($this->migrate) || $this->migrationClass == NULL){
284 return(FALSE);
285 }
286 return($this->migrationClass->checkForIssues());
287 }
289 function getMigrationClass()
290 {
291 return($this->migrationClass);
292 }
294 function check()
295 {
296 $val = $this->getValue(TRUE);
297 $return = TRUE;
298 if($this->mandatory && empty($val)){
299 $return = FALSE;
300 }
302 $check = $this->getCheck();
303 if(!empty($val) && !empty($check)){
304 $res = call_user_func(preg_split("/::/", $this->check),$messages=TRUE, $this->class,$this->name,$val, $this->type);
305 if(!$res){
306 $return = FALSE;
307 }
308 }
309 return($return);
310 }
312 static function isBool($message,$class,$name,$value, $type)
313 {
314 $match = in_array($value,array('true','false',''));
316 // Display the reason for failing this check.
317 if($message && ! $match){
318 msg_dialog::display(_("Warning"),
319 sprintf(_("The value '%s' specified for '%s:%s' is invalid. A bool value is required here!"),
320 bold($value),bold($class),bold($name)),
321 WARNING_DIALOG);
322 }
324 return($match);
325 }
327 static function isString($message,$class,$name,$value, $type)
328 {
329 $match = TRUE;
331 // Display the reason for failing this check.
332 if($message && ! $match){
333 msg_dialog::display(_("Warning"),
334 sprintf(_("The value '%s' specified for '%s:%s' is invalid. A string value is required here!"),
335 bold($value),bold($class),bold($name)),
336 WARNING_DIALOG);
337 }
339 return($match);
340 }
342 static function isInteger($message,$class,$name,$value, $type)
343 {
344 $match = is_numeric($value) && !preg_match("/[^0-9]/", $value);
346 // Display the reason for failing this check.
347 if($message && ! $match){
348 msg_dialog::display(_("Warning"),
349 sprintf(_("The value '%s' specified for '%s:%s' is invalid. A numeric value is required here!"),
350 bold($value),bold($class),bold($name)),
351 WARNING_DIALOG);
352 }
354 return($match);
355 }
357 static function isPath($message,$class,$name,$value, $type)
358 {
359 $match = preg_match("#^(/[^/]*/){1}#", $value);
361 // Display the reason for failing this check.
362 if($message && ! $match){
363 msg_dialog::display(_("Warning"),
364 sprintf(_("The path '%s' specified for '%s:%s' is invalid!"),
365 bold($value),bold($class),bold($name)),
366 WARNING_DIALOG);
367 }
369 return($match);
370 }
372 static function isReadablePath($message,$class,$name,$value, $type)
373 {
374 $match = !empty($value)&&is_dir($value)&&is_writeable($value);
376 // Display the reason for failing this check.
377 if($message && ! $match){
378 if(!is_dir($value)){
379 msg_dialog::display(_("Warning"),
380 sprintf(_("The folder '%s' specified for '%s:%s' does not exists!"),
381 bold($value),bold($class),bold($name)),
382 WARNING_DIALOG);
383 }elseif(!is_readable($value)){
384 msg_dialog::display(_("Warning"),
385 sprintf(_("The folder '%s' specified for '%s:%s' cannot be used for reading!"),
386 bold($value),bold($class),bold($name)),
387 WARNING_DIALOG);
388 }
389 }
391 return($match);
392 }
394 static function isWriteablePath($message,$class,$name,$value, $type)
395 {
396 $match = !empty($value)&&is_dir($value)&&is_writeable($value);
398 // Display the reason for failing this check.
399 if($message && ! $match){
400 if(!is_dir($value)){
401 msg_dialog::display(_("Warning"),
402 sprintf(_("The folder '%s' specified for '%s:%s' does not exists!"),
403 bold($value),bold($class),bold($name)),
404 WARNING_DIALOG);
405 }elseif(!is_writeable($value)){
406 msg_dialog::display(_("Warning"),
407 sprintf(_("The folder '%s' specified for '%s:%s' cannot be used for writing!"),
408 bold($value),bold($class),bold($name)),
409 WARNING_DIALOG);
410 }
411 }
413 return($match);
414 }
416 static function isReadableFile($message,$class,$name,$value, $type)
417 {
418 $match = !empty($value) && is_readable($value) && is_file($value);
420 // Display the reason for failing this check.
421 if($message && ! $match){
423 if(!is_file($value)){
424 msg_dialog::display(_("Warning"),
425 sprintf(_("The file '%s' specified for '%s:%s' does not exists!"),
426 bold($value),bold($class),bold($name)),
427 WARNING_DIALOG);
428 }elseif(!is_readable($value)){
429 msg_dialog::display(_("Warning"),
430 sprintf(_("The file '%s' specified for '%s:%s' cannot be read!"),
431 bold($value),bold($class),bold($name)),
432 WARNING_DIALOG);
433 }
434 }
436 return($match);
437 }
439 static function isCommand($message,$class,$name,$value, $type)
440 {
441 $match = TRUE;
443 // Display the reason for failing this check.
444 if($message && ! $match){
445 msg_dialog::display(_("Warning"),
446 sprintf(_("The command '%s' specified for '%s:%s' is invalid!"),
447 bold($value),bold($class),bold($name)),
448 WARNING_DIALOG);
449 }
451 return($match);
452 }
454 static function isDn($message,$class,$name,$value, $type)
455 {
456 $match = preg_match("/^([a-z]*=[^=,]*,)*[^=]*=[^=]*$/i", $value);
458 // Display the reason for failing this check.
459 if($message && ! $match){
460 msg_dialog::display(_("Warning"),
461 sprintf(_("The dn '%s' specified for '%s:%s' is invalid!"),
462 bold($value),bold($class),bold($name)),
463 WARNING_DIALOG);
464 }
466 return($match);
467 }
469 static function isRdn($message,$class,$name,$value, $type)
470 {
471 $match = preg_match("/^([a-z]*=[^=,]*,)*[^=]*=[^=,]*,?$/i", $value);
473 // Display the reason for failing this check.
474 if($message && ! $match){
475 msg_dialog::display(_("Warning"),
476 sprintf(_("The rdn '%s' specified for '%s:%s' is invalid!"),
477 bold($value),bold($class),bold($name)),
478 WARNING_DIALOG);
479 }
481 return($match);
482 }
484 private function _restoreCurrentValue()
485 {
486 // First check for values in the LDAP Database.
487 if(isset($this->parent->ldapStoredProperties[$this->class][$this->name])){
488 $this->setStatus('ldap');
489 $this->value = $this->parent->ldapStoredProperties[$this->class][$this->name];
490 return;
491 }
493 // Second check for values in the config file.
494 if(isset($this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)])){
495 $this->setStatus('file');
496 $this->value = $this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)];
497 return;
498 }
500 // If there still wasn't found anything then fallback to the default.
501 if($this->getStatus() == 'undefined'){
502 $this->value = $this->getDefault();
503 }
504 }
506 function getMigrate() { return($this->migrate); }
507 function getCheck() { return($this->check); }
508 function getName() { return($this->name); }
509 function getClass() { return($this->class); }
510 function getGroup() { return($this->group); }
511 function getType() { return($this->type); }
512 function getDescription() { return($this->description); }
513 function getDefault() { return($this->default); }
514 function getDefaults() { return($this->defaults); }
515 function getStatus() { return($this->status); }
516 function isMandatory() { return($this->mandatory); }
518 function setValue($str)
519 {
520 if(in_array($this->getStatus(), array('modified'))){
521 $this->tmp_value = $str;
522 }elseif($this->value != $str){
523 $this->setStatus('modified');
524 $this->tmp_value = $str;
525 }
526 }
528 function getValue($temporary = FALSE)
529 {
530 if($temporary){
531 if(in_array($this->getStatus(), array('modified','removed'))){
532 return($this->tmp_value);
533 }else{
534 return($this->value);
535 }
536 }else{
538 // Do not return ldap values if we've to ignore them.
539 if($this->parent->ignoreLdapProperties){
540 if(isset($this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)])){
541 return($this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)]);
542 }else{
543 return($this->getDefault());
544 }
545 }else{
546 return($this->value);
547 }
548 }
549 }
551 function restoreDefault()
552 {
553 if(in_array($this->getStatus(),array('ldap'))){
554 $this->setStatus('removed');
556 // Second check for values in the config file.
557 if(isset($this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)])){
558 $this->tmp_value = $this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)];
559 }else{
560 $this->tmp_value = $this->getDefault();
561 }
562 }
563 }
565 function save()
566 {
567 if($this->getStatus() == 'modified'){
568 $ldap = $this->parent->config->get_ldap_link();
569 $ldap->cd($this->parent->config->current['BASE']);
570 $dn = "cn={$this->class},".$this->parent->config->current['CONFIG'];
571 $ldap->cat($dn);
572 if(!$ldap->count()){
573 $ldap->cd($dn);
574 $data = array(
575 'cn' => $this->class,
576 'objectClass' => array('top','gosaConfig'),
577 'gosaSetting' => $this->name.":".$this->tmp_value);
579 $ldap->add($data);
580 if(!$ldap->success()){
581 echo $ldap->get_error();
582 }
584 }else{
585 $attrs = $ldap->fetch();
586 $data = array();
587 $found = false;
588 if(isset($attrs['gosaSetting']['count'])){
589 for($i = 0;$i<$attrs['gosaSetting']['count']; $i ++){
590 $set = $attrs['gosaSetting'][$i];
591 if(preg_match("/^{$this->name}:/", $set)){
592 $set = "{$this->name}:{$this->tmp_value}";
593 $found = true;
594 }
595 $data['gosaSetting'][] = $set;
596 }
597 }
598 if(!$found) $data['gosaSetting'][] = "{$this->name}:{$this->tmp_value}";
599 $ldap->cd($dn);
600 $ldap->modify($data);
601 if(!$ldap->success()){
602 echo $ldap->get_error();
603 }
604 }
605 $this->value = $this->tmp_value;
606 $this->setStatus('ldap');
607 }elseif($this->getStatus() == 'removed'){
608 $ldap = $this->parent->config->get_ldap_link();
609 $ldap->cd($this->parent->config->current['BASE']);
610 $dn = "cn={$this->class},".$this->parent->config->current['CONFIG'];
611 $ldap->cat($dn);
612 $attrs = $ldap->fetch();
613 $data = array('gosaSetting' => array());
614 for($i = 0;$i<$attrs['gosaSetting']['count']; $i ++){
615 $set = $attrs['gosaSetting'][$i];
616 if(preg_match("/^{$this->name}:/", $set)){
617 continue;
618 }
619 $data['gosaSetting'][] = $set;
620 }
621 $ldap->cd($dn);
622 $ldap->modify($data);
623 if(!$ldap->success()){
624 echo $ldap->get_error();
625 }
626 $this->_restoreCurrentValue();
627 }
628 }
630 private function setStatus($state)
631 {
632 if(!in_array($state, array('ldap','file','undefined','modified','removed'))) {
633 trigger_error("Unknown property status given '{$state}' for {$this->class}:{$this->name}!");
634 }else{
635 $this->status = $state;
636 }
637 }
639 function isValid()
640 {
641 return(TRUE);
642 }
643 }
647 interface propertyMigration
648 {
649 function __construct($config,$property);
650 }
653 ?>