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"), msgPool::invalid($name,$value,"",_("Use 'true', 'false' or empty if allowed")), WARNING_DIALOG);
319 }
321 return($match);
322 }
324 static function isString($message,$class,$name,$value, $type)
325 {
326 $match = TRUE;
328 // Display the reason for failing this check.
329 if($message && ! $match){
330 msg_dialog::display(_("Warning"), msgPool::invalid($name), WARNING_DIALOG);
331 }
333 return($match);
334 }
336 static function isInteger($message,$class,$name,$value, $type)
337 {
338 $match = is_numeric($value) && !preg_match("/[^0-9]/", $value);
340 // Display the reason for failing this check.
341 if($message && ! $match){
342 msg_dialog::display(_("Warning"), msgPool::invalid($name, $value,'/[0-9]/'), WARNING_DIALOG);
343 }
345 return($match);
346 }
348 static function isPath($message,$class,$name,$value, $type)
349 {
350 $match = TRUE;
352 // Display the reason for failing this check.
353 if($message && ! $match){
354 msg_dialog::display(_("Warning"), msgPool::invalid($name), WARNING_DIALOG);
355 }
357 return($match);
358 }
360 static function isWriteablePath($message,$class,$name,$value, $type)
361 {
362 $match = !empty($value)&&is_dir($value)&&is_writeable($value);
364 // Display the reason for failing this check.
365 if($message && ! $match){
367 if(!is_dir($value)){
368 msg_dialog::display(_("Warning"), sprintf(_("The specified folder does not exists '%s'."), $value), WARNING_DIALOG);
369 }elseif(!is_writeable($value)){
370 msg_dialog::display(_("Warning"), sprintf(_("The specified folder cannot be used for writing '%s'."), $value), WARNING_DIALOG);
371 }
372 }
374 return($match);
375 }
377 static function isReadableFile($message,$class,$name,$value, $type)
378 {
379 $match = !empty($value) && is_readable($value) && is_file($value);
381 // Display the reason for failing this check.
382 if($message && ! $match){
384 if(!is_file($value)){
385 msg_dialog::display(_("Warning"), sprintf(_("The specified file does not exists '%s'."), $value), WARNING_DIALOG);
386 }elseif(!is_readable($value)){
387 msg_dialog::display(_("Warning"), sprintf(_("The specified file cannot be used for reading '%s'."), $value), WARNING_DIALOG);
388 }
389 }
391 return($match);
392 }
394 static function isCommand($message,$class,$name,$value, $type)
395 {
396 $match = TRUE;
398 // Display the reason for failing this check.
399 if($message && ! $match){
400 msg_dialog::display(_("Warning"), msgPool::cmdinvalid($name,$value), WARNING_DIALOG);
401 }
403 return($match);
404 }
406 static function isDn($message,$class,$name,$value, $type)
407 {
408 $match = preg_match("/^([a-z]*=[^=,]*,)*[^=]*=[^=]*$/i", $value);
410 // Display the reason for failing this check.
411 if($message && ! $match){
412 msg_dialog::display(_("Warning"), msgPool::invalid($name,$value,'','cn=user,ou=people,dc=example,dc=de'), WARNING_DIALOG);
413 }
415 return($match);
416 }
418 static function isRdn($message,$class,$name,$value, $type)
419 {
420 $match = preg_match("/^([a-z]*=[^=,]*,)*[^=]*=[^=]*,$/i", $value);
422 // Display the reason for failing this check.
423 if($message && ! $match){
424 msg_dialog::display(_("Warning"), msgPool::invalid($name,$value,'','ou=people,'), WARNING_DIALOG);
425 }
427 return($match);
428 }
430 private function _restoreCurrentValue()
431 {
432 // First check for values in the LDAP Database.
433 if(isset($this->parent->ldapStoredProperties[$this->class][$this->name])){
434 $this->setStatus('ldap');
435 $this->value = $this->parent->ldapStoredProperties[$this->class][$this->name];
436 return;
437 }
439 // Second check for values in the config file.
440 if(isset($this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)])){
441 $this->setStatus('file');
442 $this->value = $this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)];
443 return;
444 }
446 // If there still wasn't found anything then fallback to the default.
447 if($this->getStatus() == 'undefined'){
448 $this->value = $this->getDefault();
449 }
450 }
452 function getMigrate() { return($this->migrate); }
453 function getCheck() { return($this->check); }
454 function getName() { return($this->name); }
455 function getClass() { return($this->class); }
456 function getGroup() { return($this->group); }
457 function getType() { return($this->type); }
458 function getDescription() { return($this->description); }
459 function getDefault() { return($this->default); }
460 function getDefaults() { return($this->defaults); }
461 function getStatus() { return($this->status); }
462 function isMandatory() { return($this->mandatory); }
464 function setValue($str)
465 {
466 if(in_array($this->getStatus(), array('modified'))){
467 $this->tmp_value = $str;
468 }elseif($this->value != $str){
469 $this->setStatus('modified');
470 $this->tmp_value = $str;
471 }
472 }
474 function getValue($temporary = FALSE)
475 {
476 if($temporary){
477 if(in_array($this->getStatus(), array('modified','removed'))){
478 return($this->tmp_value);
479 }else{
480 return($this->value);
481 }
482 }else{
484 // Do not return ldap values if we've to ignore them.
485 if($this->parent->ignoreLdapProperties){
486 if(isset($this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)])){
487 return($this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)]);
488 }else{
489 return($this->getDefault());
490 }
491 }else{
492 return($this->value);
493 }
494 }
495 }
497 function restoreDefault()
498 {
499 if(in_array($this->getStatus(),array('ldap'))){
500 $this->setStatus('removed');
502 // Second check for values in the config file.
503 if(isset($this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)])){
504 $this->tmp_value = $this->parent->fileStoredProperties[strtolower($this->class)][strtolower($this->name)];
505 }else{
506 $this->tmp_value = $this->getDefault();
507 }
508 }
509 }
511 function save()
512 {
513 if($this->getStatus() == 'modified'){
514 $ldap = $this->parent->config->get_ldap_link();
515 $ldap->cd($this->parent->config->current['BASE']);
516 $dn = "cn={$this->class},".$this->parent->config->current['CONFIG'];
517 $ldap->cat($dn);
518 if(!$ldap->count()){
519 $ldap->cd($dn);
520 $data = array(
521 'cn' => $this->class,
522 'objectClass' => array('top','gosaConfig'),
523 'gosaSetting' => $this->name.":".$this->tmp_value);
525 $ldap->add($data);
526 if(!$ldap->success()){
527 echo $ldap->get_error();
528 }
530 }else{
531 $attrs = $ldap->fetch();
532 $data = array();
533 $found = false;
534 if(isset($attrs['gosaSetting']['count'])){
535 for($i = 0;$i<$attrs['gosaSetting']['count']; $i ++){
536 $set = $attrs['gosaSetting'][$i];
537 if(preg_match("/^{$this->name}:/", $set)){
538 $set = "{$this->name}:{$this->tmp_value}";
539 $found = true;
540 }
541 $data['gosaSetting'][] = $set;
542 }
543 }
544 if(!$found) $data['gosaSetting'][] = "{$this->name}:{$this->tmp_value}";
545 $ldap->cd($dn);
546 $ldap->modify($data);
547 if(!$ldap->success()){
548 echo $ldap->get_error();
549 }
550 }
551 $this->value = $this->tmp_value;
552 $this->setStatus('ldap');
553 }elseif($this->getStatus() == 'removed'){
554 $ldap = $this->parent->config->get_ldap_link();
555 $ldap->cd($this->parent->config->current['BASE']);
556 $dn = "cn={$this->class},".$this->parent->config->current['CONFIG'];
557 $ldap->cat($dn);
558 $attrs = $ldap->fetch();
559 $data = array('gosaSetting' => array());
560 for($i = 0;$i<$attrs['gosaSetting']['count']; $i ++){
561 $set = $attrs['gosaSetting'][$i];
562 if(preg_match("/^{$this->name}:/", $set)){
563 continue;
564 }
565 $data['gosaSetting'][] = $set;
566 }
567 $ldap->cd($dn);
568 $ldap->modify($data);
569 if(!$ldap->success()){
570 echo $ldap->get_error();
571 }
572 $this->_restoreCurrentValue();
573 }
574 }
576 private function setStatus($state)
577 {
578 if(!in_array($state, array('ldap','file','undefined','modified','removed'))) {
579 trigger_error("Unknown property status given '{$state}' for {$this->class}:{$this->name}!");
580 }else{
581 $this->status = $state;
582 }
583 }
585 function isValid()
586 {
587 return(TRUE);
588 }
589 }
593 interface propertyMigration
594 {
595 function __construct($config,$property);
596 }
599 ?>