1<?php 2/** 3 * Classes and functions for the template engine. 4 * 5 * Templates are either: 6 * + Creating or Editing, (a Container or DN passed to the object) 7 * + A predefined template, or a default template (template ID passed to the object) 8 * 9 * The template object will know which attributes are mandatory (MUST 10 * attributes) and which attributes are optional (MAY attributes). It will also 11 * contain a list of optional attributes. These are attributes that the schema 12 * will allow data for (they are MAY attributes), but the template has not 13 * included a definition for them. 14 * 15 * The template object will be invalidated if it does contain the necessary 16 * items (objectClass, MUST attributes, etc) to make a successful LDAP update. 17 * 18 * @author The phpLDAPadmin development team 19 * @package phpLDAPadmin 20 */ 21 22/** 23 * Template Class 24 * 25 * @package phpLDAPadmin 26 * @subpackage Templates 27 * @todo RDN attributes should be treated as MUST attributes even though the schema marks them as MAY 28 * @todo RDN attributes need to be checked that are included in the schema, otherwise mark it is invalid 29 * @todo askcontainer is no longer used? 30 */ 31class Template extends xmlTemplate { 32 # If this template visible on the template choice list 33 private $visible = true; 34 # Is this template valid after parsing the XML file 35 private $invalid = false; 36 private $invalid_admin = false; 37 private $invalid_reason; 38 # The TEMPLATE structural objectclasses 39 protected $structural_oclass = array(); 40 protected $description = ''; 41 # Is this a read-only template (only valid in modification templates) 42 private $readonly = false; 43 44 # If this is set, it means we are editing an entry. 45 private $dn; 46 # Where this template will store its data 47 protected $container; 48 # Does this template prohibit children being created 49 private $noleaf = false; 50 # A regexp that determines if this template is valid in the container. 51 private $regexp; 52 # Template Title 53 public $title; 54 # Icon for the template 55 private $icon; 56 # Template RDN attributes 57 private $rdn; 58 59 public function __construct($server_id,$name=null,$filename=null,$type=null,$id=null) { 60 parent::__construct($server_id,$name,$filename,$type,$id); 61 62 # If this is the default template, we might disable leafs by default. 63 if (is_null($filename)) 64 $this->noleaf = $_SESSION[APPCONFIG]->getValue('appearance','disable_default_leaf'); 65 } 66 67 public function __clone() { 68 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 69 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 70 71 # We need to clone our attributes, when passing back a template with getTemplate 72 foreach ($this->attributes as $key => $value) 73 $this->attributes[$key] = clone $value; 74 } 75 76 /** 77 * Main processing to store the template. 78 * 79 * @param xmldata Parsed xmldata from xml2array object 80 */ 81 protected function storeTemplate($xmldata) { 82 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 83 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 84 85 $server = $this->getServer(); 86 $objectclasses = array(); 87 88 foreach ($xmldata['template'] as $xml_key => $xml_value) { 89 if (DEBUG_ENABLED) 90 debug_log('Foreach loop Key [%s] Value [%s]',4,0,__FILE__,__LINE__,__METHOD__,$xml_key,is_array($xml_value)); 91 92 switch ($xml_key) { 93 # Build our object Classes from the DN and Template. 94 case ('objectclasses'): 95 if (DEBUG_ENABLED) 96 debug_log('Case [%s]',4,0,__FILE__,__LINE__,__METHOD__,$xml_key); 97 98 if (isset($xmldata['template'][$xml_key]['objectclass'])) 99 if (is_array($xmldata['template'][$xml_key]['objectclass'])) { 100 foreach ($xmldata['template'][$xml_key]['objectclass'] as $index => $details) { 101 102 # XML files with only 1 objectClass dont have a numeric index. 103 $soc = $server->getSchemaObjectClass(strtolower($details)); 104 105 # If we havent recorded this objectclass already, do so now. 106 if (is_object($soc) && ! in_array($soc->getName(),$objectclasses)) 107 array_push($objectclasses,$soc->getName(false)); 108 109 elseif (! is_object($soc) && ! $_SESSION[APPCONFIG]->getValue('appearance','hide_template_warning')) 110 system_message(array( 111 'title'=>_('Automatically removed objectClass from template'), 112 'body'=>sprintf('%s: <b>%s</b> %s',$this->getTitle(),$details,_('removed from template as it is not defined in the schema')), 113 'type'=>'warn')); 114 } 115 116 } else { 117 # XML files with only 1 objectClass dont have a numeric index. 118 $soc = $server->getSchemaObjectClass(strtolower($xmldata['template'][$xml_key]['objectclass'])); 119 120 # If we havent recorded this objectclass already, do so now. 121 if (is_object($soc) && ! in_array($soc->getName(),$objectclasses)) 122 array_push($objectclasses,$soc->getName(false)); 123 } 124 125 break; 126 127 # Build our attribute list from the DN and Template. 128 case ('attributes'): 129 if (DEBUG_ENABLED) 130 debug_log('Case [%s]',4,0,__FILE__,__LINE__,__METHOD__,$xml_key); 131 132 if (is_array($xmldata['template'][$xml_key])) { 133 foreach ($xmldata['template'][$xml_key] as $tattrs) 134 foreach ($tattrs as $index => $details) { 135 if (DEBUG_ENABLED) 136 debug_log('Foreach tattrs Key [%s] Value [%s]',4,0,__FILE__,__LINE__,__METHOD__, 137 $index,$details); 138 139 # If there is no schema definition for the attribute, it will be ignored. 140 if ($sattr = $server->getSchemaAttribute($index)) 141 if (is_null($this->getAttribute($sattr->getName()))) 142 $this->addAttribute($sattr->getName(),$details,'XML'); 143 } 144 145 masort($this->attributes,'order'); 146 } 147 148 break; 149 150 default: 151 if (DEBUG_ENABLED) 152 debug_log('Case [%s]',4,0,__FILE__,__LINE__,__METHOD__,$xml_key); 153 154 # Some key definitions need to be an array, some must not be: 155 $allowed_arrays = array('rdn'); 156 $storelower = array('rdn'); 157 $storearray = array('rdn'); 158 159 # Items that must be stored lowercase 160 if (in_array($xml_key,$storelower)) 161 if (is_array($xml_value)) 162 foreach ($xml_value as $index => $value) 163 $xml_value[$index] = strtolower($value); 164 else 165 $xml_value = strtolower($xml_value); 166 167 # Items that must be stored as arrays 168 if (in_array($xml_key,$storearray) && ! is_array($xml_value)) 169 $xml_value = array($xml_value); 170 171 # Items that should not be an array 172 if (! in_array($xml_key,$allowed_arrays) && is_array($xml_value)) { 173 debug_dump(array(__METHOD__,'key'=>$xml_key,'value'=>$xml_value)); 174 error(sprintf(_('In the XML file (%s), [%s] is an array, it must be a string.'), 175 $this->filename,$xml_key),'error'); 176 } 177 178 $this->$xml_key = $xml_value; 179 180 if ($xml_key == 'invalid' && $xml_value) 181 $this->setInvalid(_('Disabled by XML configuration'),true); 182 } 183 } 184 185 if (! count($objectclasses)) { 186 $this->setInvalid(_('ObjectClasses in XML dont exist in LDAP server.')); 187 return; 188 189 } else { 190 $attribute = $this->addAttribute('objectClass',array('values'=>$objectclasses),'XML'); 191 $attribute->justModified(); 192 $attribute->setRequired(); 193 $attribute->hide(); 194 } 195 196 $this->rebuildTemplateAttrs(); 197 198 # Check we have some manditory items. 199 foreach (array('rdn','structural_oclass','visible') as $key) { 200 if (! isset($this->$key) 201 || (! is_array($this->$key) && ! trim($this->$key))) { 202 203 $this->setInvalid(sprintf(_('Missing %s in the XML file.'),$key)); 204 break; 205 } 206 } 207 208 # Mark our RDN attributes as RDN 209 $counter = 1; 210 foreach ($this->rdn as $key) { 211 if ((is_null($attribute = $this->getAttribute($key))) && (in_array_ignore_case('extensibleobject',$this->getObjectClasses()))) { 212 $attribute = $this->addAttribute($key,array('values'=>array())); 213 $attribute->show(); 214 } 215 216 if (! is_null($attribute)) 217 $attribute->setRDN($counter++); 218 elseif ($this->isType('creation')) 219 $this->setInvalid(sprintf(_('Missing RDN attribute %s in the XML file.'),$key)); 220 } 221 } 222 223 /** 224 * Is default templates enabled? 225 * This will disable the default template from the engine. 226 * 227 * @return boolean 228 */ 229 protected function hasDefaultTemplate() { 230 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 231 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 232 233 if ($_SESSION[APPCONFIG]->getValue('appearance','disable_default_template')) 234 return false; 235 else 236 return true; 237 } 238 239 /** 240 * Return the templates of type (creation/modification) 241 * 242 * @param $string type - creation/modification 243 * @return array - Array of templates of that type 244 */ 245 protected function readTemplates($type) { 246 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 247 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 248 249 $template_xml = new Templates($this->server_id); 250 return $template_xml->getTemplates($type); 251 } 252 253 /** 254 * This function will perform the following intialisation steps: 255 * + If a DN is set, query the ldap and load the object 256 * + Read our $_REQUEST variable and set the values 257 * After this action, the template should self describe as to whether it is an update, create 258 * or delete. 259 * (OLD values are IGNORED, we will have got them when we build this object from the LDAP server DN.) 260 */ 261 public function accept($makeVisible=false,$nocache=false) { 262 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 263 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 264 265 $server = $this->getServer(); 266 267 # If a DN is set, then query the LDAP server for the details. 268 if ($this->dn) { 269 if (! $server->dnExists($this->dn)) 270 system_message(array( 271 'title'=>__METHOD__, 272 'body'=>sprintf('DN (%s) didnt exist in LDAP?',$this->dn), 273 'type'=>'info')); 274 275 $rdnarray = rdn_explode(strtolower(get_rdn(dn_escape($this->dn)))); 276 277 $counter = 1; 278 foreach ($server->getDNAttrValues($this->dn,null,LDAP_DEREF_NEVER,array_merge(array('*'),$server->getValue('server','custom_attrs')),$nocache) as $attr => $values) { 279 # We ignore DNs. 280 if ($attr == 'dn') 281 continue; 282 283 $attribute = $this->getAttribute($attr); 284 285 if (is_null($attribute)) 286 $attribute = $this->addAttribute($attr,array('values'=>$values)); 287 else 288 if ($attribute->getValues()) { 289 # Override values to those that are defined in the XML file. 290 if ($attribute->getSource() != 'XML') 291 $attribute->setValue(array_values($values)); 292 else 293 $attribute->setOldValue(array_values($values)); 294 295 } else 296 $attribute->initValue(array_values($values)); 297 298 # Work out the RDN attributes 299 foreach ($attribute->getValues() as $index => $value) 300 if (in_array(sprintf('%s=%s', 301 $attribute->getName(),strtolower($attribute->getValue($index))),$rdnarray)) 302 $attribute->setRDN($counter++); 303 304 if ($makeVisible) 305 $attribute->show(); 306 } 307 308 # Get the Internal Attributes 309 foreach ($server->getDNAttrValues($this->dn,null,LDAP_DEREF_NEVER,array_merge(array('+'),$server->getValue('server','custom_sys_attrs'))) as $attr => $values) { 310 $attribute = $this->getAttribute($attr); 311 312 if (is_null($attribute)) 313 $attribute = $this->addAttribute($attr,array('values'=>$values)); 314 else 315 if ($attribute->getValues()) 316 $attribute->setValue(array_values($values)); 317 else 318 $attribute->initValue(array_values($values)); 319 320 if (! in_array_ignore_case($attribute->getName(),$server->getValue('server','custom_attrs'))) 321 $attribute->setInternal(); 322 } 323 324 # If this is the default template, and our $_REQUEST has defined our objectclass, then query the schema to get the attributes 325 } elseif ($this->container) { 326 if ($this->isType('default') && ! count($this->getAttributes(true)) && isset($_REQUEST['new_values']['objectclass'])) { 327 $attribute = $this->addAttribute('objectclass',array('values'=>$_REQUEST['new_values']['objectclass'])); 328 $attribute->justModified(); 329 $this->rebuildTemplateAttrs(); 330 unset($_REQUEST['new_values']['objectclass']); 331 } 332 333 } elseif (get_request('create_base')) { 334 if (get_request('rdn')) { 335 $rdn = explode('=',get_request('rdn')); 336 $attribute = $this->addAttribute($rdn[0],array('values'=>array($rdn[1]))); 337 $attribute->setRDN(1); 338 } 339 340 } else { 341 debug_dump_backtrace('No DN or CONTAINER?',1); 342 } 343 344 # Read in our new values. 345 foreach (array('new_values') as $key) { 346 if (isset($_REQUEST[$key])) 347 foreach ($_REQUEST[$key] as $attr => $values) { 348 # If it isnt an array, silently ignore it. 349 if (! is_array($values)) 350 continue; 351 352 # If _REQUEST['skip_array'] with this attr set, we'll ignore this new_value 353 if (isset($_REQUEST['skip_array'][$attr]) && $_REQUEST['skip_array'][$attr] == 'on') 354 continue; 355 356 # Prune out entries with a blank value. 357 foreach ($values as $index => $value) 358 if (! strlen(trim($value))) 359 unset($values[$index]); 360 361 $attribute = $this->getAttribute($attr); 362 # If the attribute is null, then no attribute exists, silently ignore it (unless this is the default template) 363 if (is_null($attribute) && (! $this->isType('default') && ! $this->isType(null))) 364 continue; 365 366 # If it is a binary attribute, the post should have base64 encoded the value, we'll need to reverse that 367 if ($server->isAttrBinary($attr)) 368 foreach ($values as $index => $value) 369 $values[$index] = base64_decode($value); 370 371 if (is_null($attribute)) { 372 $attribute = $this->addAttribute($attr,array('values'=>$values)); 373 374 if (count($values)) 375 $attribute->justModified(); 376 377 } else 378 $attribute->setValue(array_values($values)); 379 } 380 381 # Read in our new binary values 382 if (isset($_FILES[$key]['name'])) 383 foreach ($_FILES[$key]['name'] as $attr => $values) { 384 $new_values = array(); 385 386 foreach ($values as $index => $details) { 387 # Ignore empty files 388 if (! $_FILES[$key]['size'][$attr][$index]) 389 continue; 390 391 if (! is_uploaded_file($_FILES[$key]['tmp_name'][$attr][$index])) { 392 if (isset($_FILES[$key]['error'][$attr][$index])) 393 switch($_FILES[$key]['error'][$attr][$index]) { 394 395 # No error; possible file attack! 396 case 0: 397 $msg = _('Security error: The file being uploaded may be malicious.'); 398 break; 399 400 # Uploaded file exceeds the upload_max_filesize directive in php.ini 401 case 1: 402 $msg = _('The file you uploaded is too large. Please check php.ini, upload_max_size setting'); 403 break; 404 405 # Uploaded file exceeds the MAX_FILE_SIZE directive specified in the html form 406 case 2: 407 $msg = _('The file you uploaded is too large. Please check php.ini, upload_max_size setting'); 408 break; 409 410 # Uploaded file was only partially uploaded 411 case 3: 412 $msg = _('The file you selected was only partially uploaded, likley due to a network error.'); 413 break; 414 415 # No file was uploaded 416 case 4: 417 $msg = _('You left the attribute value blank. Please go back and try again.'); 418 break; 419 420 # A default error, just in case! :) 421 default: 422 $msg = _('Security error: The file being uploaded may be malicious.'); 423 break; 424 } 425 426 else 427 $msg = _('Security error: The file being uploaded may be malicious.'); 428 429 system_message(array( 430 'title'=>_('Upload Binary Attribute Error'),'body'=>$msg,'type'=>'warn')); 431 432 } else { 433 $binaryfile = array(); 434 $binaryfile['name'] = $_FILES[$key]['tmp_name'][$attr][$index]; 435 $binaryfile['handle'] = fopen($binaryfile['name'],'r'); 436 $binaryfile['data'] = fread($binaryfile['handle'],filesize($binaryfile['name'])); 437 fclose($binaryfile['handle']); 438 439 $new_values[$index] = $binaryfile['data']; 440 } 441 } 442 443 if (count($new_values)) { 444 $attribute = $this->getAttribute($attr); 445 446 if (is_null($attribute)) 447 $attribute = $this->addAttribute($attr,array('values'=>$new_values)); 448 else 449 foreach ($new_values as $value) 450 $attribute->addValue($value); 451 452 $attribute->justModified(); 453 } 454 } 455 } 456 457 # If there are any single item additions (from the add_attr form for example) 458 if (isset($_REQUEST['single_item_attr'])) { 459 if (isset($_REQUEST['single_item_value'])) { 460 if (! is_array($_REQUEST['single_item_value'])) 461 $values = array($_REQUEST['single_item_value']); 462 else 463 $values = $_REQUEST['single_item_value']; 464 465 } elseif (isset($_REQUEST['binary'])) { 466 /* Special case for binary attributes (like jpegPhoto and userCertificate): 467 * we must go read the data from the file and override $_REQUEST['single_item_value'] with the 468 * binary data. Secondly, we must check if the ";binary" option has to be appended to the name 469 * of the attribute. */ 470 471 if ($_FILES['single_item_value']['size'] === 0) 472 system_message(array( 473 'title'=>_('Upload Binary Attribute Error'), 474 'body'=>sprintf('%s %s',_('The file you chose is either empty or does not exist.'),_('Please go back and try again.')), 475 'type'=>'warn')); 476 477 else { 478 if (! is_uploaded_file($_FILES['single_item_value']['tmp_name'])) { 479 if (isset($_FILES['single_item_value']['error'])) 480 switch($_FILES['single_item_value']['error']) { 481 482 # No error; possible file attack! 483 case 0: 484 $msg = _('Security error: The file being uploaded may be malicious.'); 485 break; 486 487 # Uploaded file exceeds the upload_max_filesize directive in php.ini 488 case 1: 489 $msg = _('The file you uploaded is too large. Please check php.ini, upload_max_size setting'); 490 break; 491 492 # Uploaded file exceeds the MAX_FILE_SIZE directive specified in the html form 493 case 2: 494 $msg = _('The file you uploaded is too large. Please check php.ini, upload_max_size setting'); 495 break; 496 497 # Uploaded file was only partially uploaded 498 case 3: 499 $msg = _('The file you selected was only partially uploaded, likley due to a network error.'); 500 break; 501 502 # No file was uploaded 503 case 4: 504 $msg = _('You left the attribute value blank. Please go back and try again.'); 505 break; 506 507 # A default error, just in case! :) 508 default: 509 $msg = _('Security error: The file being uploaded may be malicious.'); 510 break; 511 } 512 513 else 514 $msg = _('Security error: The file being uploaded may be malicious.'); 515 516 system_message(array( 517 'title'=>_('Upload Binary Attribute Error'),'body'=>$msg,'type'=>'warn'),'index.php'); 518 } 519 520 $binaryfile = array(); 521 $binaryfile['name'] = $_FILES['single_item_value']['tmp_name']; 522 $binaryfile['handle'] = fopen($binaryfile['name'],'r'); 523 $binaryfile['data'] = fread($binaryfile['handle'],filesize($binaryfile['name'])); 524 fclose($binaryfile['handle']); 525 526 $values = array($binaryfile['data']); 527 } 528 } 529 530 if (count($values)) { 531 $attribute = $this->getAttribute($_REQUEST['single_item_attr']); 532 533 if (is_null($attribute)) 534 $attribute = $this->addAttribute($_REQUEST['single_item_attr'],array('values'=>$values)); 535 else 536 $attribute->setValue(array_values($values)); 537 538 $attribute->justModified(); 539 } 540 } 541 542 # If this is the default creation template, we need to set some additional values 543 if ($this->isType('default') && $this->getContext() == 'create') { 544 # Load our schema, based on the objectclasses that may have already been defined. 545 if (! get_request('create_base')) 546 $this->rebuildTemplateAttrs(); 547 548 # Set the RDN attribute 549 $counter = 1; 550 foreach (get_request('rdn_attribute','REQUEST',false,array()) as $key => $value) { 551 $attribute = $this->getAttribute($value); 552 553 if (! is_null($attribute)) 554 $attribute->setRDN($counter++); 555 556 else { 557 system_message(array( 558 'title'=>_('No RDN attribute'), 559 'body'=>_('No RDN attribute was selected'), 560 'type'=>'warn'),'index.php'); 561 562 die(); 563 } 564 } 565 } 566 } 567 568 /** 569 * Set the DN for this template, if we are editing entries 570 * 571 * @param dn The DN of the entry 572 */ 573 public function setDN($dn) { 574 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 575 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 576 577 if (isset($this->container)) 578 system_message(array( 579 'title'=>__METHOD__, 580 'body'=>'CONTAINER set while setting DN', 581 'type'=>'info')); 582 583 $this->dn = $dn; 584 } 585 586 /** 587 * Set the RDN attributes 588 * Given an RDN, mark the attributes as RDN attributes. If there is no defined attribute, 589 * then the remaining RDNs will be returned. 590 * 591 * @param RDN 592 * @return RDN attributes not processed 593 */ 594 public function setRDNAttributes($rdn) { 595 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 596 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 597 598 # Setup to work out our RDN. 599 $rdnarray = rdn_explode($rdn); 600 601 $counter = 1; 602 foreach ($this->getAttributes(true) as $attribute) 603 foreach ($rdnarray as $index => $rdnattr) { 604 list($attr,$value) = explode('=',$rdnattr); 605 606 if (strtolower($attr) == $attribute->getName()) { 607 $attribute->setRDN($counter++); 608 unset($rdnarray[$index]); 609 } 610 } 611 612 return $rdnarray; 613 } 614 615 /** 616 * Display the DN for this template entry. If the DN is not set (creating a new entry), then 617 * a generated DN will be produced, taken from the RDN and the CONTAINER details. 618 * 619 * @return dn 620 */ 621 public function getDN() { 622 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 623 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs,$this->dn); 624 625 if ($this->dn) 626 return $this->dn; 627 628 # If DN is not set, our DN will be made from our RDN and Container. 629 elseif ($this->getRDN() && $this->getContainer()) 630 return sprintf('%s,%s',$this->getRDN(),$this->GetContainer()); 631 632 # If container is not set, we're probably creating the base 633 elseif ($this->getRDN() && get_request('create_base')) 634 return $this->getRDN(); 635 } 636 637 public function getDNEncode($url=true) { 638 // @todo Be nice to do all this in 1 location 639 if ($url) 640 return urlencode(preg_replace('/%([0-9a-fA-F]+)/',"%25\\1",$this->getDN())); 641 else 642 return preg_replace('/%([0-9a-fA-F]+)/',"%25\\1",$this->getDN()); 643 } 644 645 /** 646 * Set the container for this template, if we are creating entries 647 * 648 * @param dn The DN of the container 649 * @todo Trigger a query to the LDAP server and generate an error if the container doesnt exist 650 */ 651 public function setContainer($container) { 652 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 653 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 654 655 if (isset($this->dn)) 656 system_message(array( 657 'title'=>__METHOD__, 658 'body'=>'DN set while setting CONTAINER', 659 'type'=>'info')); 660 661 $this->container = $container; 662 } 663 664 /** 665 * Get the DN of the container for this entry 666 * 667 * @return dn DN of the container 668 */ 669 public function getContainer() { 670 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 671 debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->container); 672 673 return $this->container; 674 } 675 676 public function getContainerEncode($url=true) { 677 // @todo Be nice to do all this in 1 location 678 if ($url) 679 return urlencode(preg_replace('/%([0-9a-fA-F]+)/',"%25\\1",$this->container)); 680 else 681 return preg_replace('/%([0-9a-fA-F]+)/',"%25\\1",$this->container); 682 } 683 684 /** 685 * Copy a DN 686 */ 687 public function copy($template,$rdn,$asnew=false) { 688 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 689 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 690 691 $rdnarray = rdn_explode($rdn); 692 693 $counter = 1; 694 foreach ($template->getAttributes(true) as $sattribute) { 695 $attribute = $this->addAttribute($sattribute->getName(false),array('values'=>$sattribute->getValues())); 696 697 # Set our new RDN, and its values 698 if (is_null($attribute)) { 699 debug_dump_backtrace('Attribute is null, it probably doesnt exist in the destination server?'); 700 701 } else { 702 703 # Mark our internal attributes. 704 if ($sattribute->isInternal()) 705 $attribute->setInternal(); 706 707 $modified = false; 708 foreach ($rdnarray as $index => $rdnattr) { 709 list($attr,$value) = explode('=',$rdnattr); 710 if (strtolower($attr) == $attribute->getName()) { 711 712 # If this is already marked as an RDN, then this multivalue RDN was updated on a previous loop 713 if (! $modified) { 714 $attribute->setValue(array($value)); 715 $attribute->setRDN($counter++); 716 $modified = true; 717 718 } else { 719 $attribute->addValue($value); 720 } 721 722 # This attribute has been taken care of, we'll drop it from our list. 723 unset($rdnarray[$index]); 724 } 725 } 726 } 727 728 // @todo If this is a Jpeg Attribute, we need to mark it read only, since it cant be deleted like text attributes can 729 if (strcasecmp(get_class($attribute),'jpegAttribute') == 0) 730 $attribute->setReadOnly(); 731 } 732 733 # If we have any RDN values left over, there werent in the original entry and need to be added. 734 foreach ($rdnarray as $rdnattr) { 735 list($attr,$value) = explode('=',$rdnattr); 736 737 $attribute = $this->addAttribute($attr,array('values'=>array($value))); 738 739 if (is_null($attribute)) 740 debug_dump_backtrace('Attribute is null, it probably doesnt exist in the destination server?'); 741 else 742 $attribute->setRDN($counter++); 743 } 744 745 # If we are copying into a new entry, we need to discard all the "old values" 746 if ($asnew) 747 foreach ($this->getAttributes(true) as $sattribute) 748 $sattribute->setOldValue(array()); 749 } 750 751 /** 752 * Get Attributes by LDAP type 753 * This function will return a list of attributes by LDAP type (MUST,MAY). 754 * 755 * @return array Array of attributes. 756 */ 757 function getAttrbyLdapType($type) { 758 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 759 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 760 761 $result = array(); 762 763 foreach ($this->attributes as $index => $attribute) { 764 if ($attribute->getLDAPtype() == strtolower($type)) 765 array_push($result,$attribute->getName()); 766 } 767 768 return $result; 769 } 770 771 /** 772 * Return true if this is a MUST,MAY attribute 773 */ 774 function isAttrType($attr,$type) { 775 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 776 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 777 778 if (in_array(strtolower($attr),$this->getAttrbyLdapType($type))) 779 return true; 780 else 781 return false; 782 } 783 784 /** 785 * Return the attributes that comprise the RDN. 786 * 787 * @return array Array of RDN objects 788 */ 789 private function getRDNObjects() { 790 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 791 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 792 793 $return = array(); 794 795 foreach ($this->attributes as $attribute) 796 if ($attribute->isRDN()) 797 array_push($return,$attribute); 798 799 masort($return,'rdn'); 800 return $return; 801 } 802 803 /** 804 * Get all the RDNs for this template, in RDN order. 805 * 806 * @return array RDNs in order. 807 */ 808 public function getRDNAttrs() { 809 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 810 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 811 812 $return = array(); 813 814 foreach ($this->getRDNObjects() as $attribute) { 815 # We'll test if two RDN's have the same number (we cant test anywhere else) 816 if (isset($return[$attribute->isRDN()]) && $this->getType() == 'creation') 817 system_message(array( 818 'title'=>_('RDN attribute sequence already defined'), 819 'body'=>sprintf('%s %s', 820 sprintf(_('There is a problem in template [%s].'),$this->getName()), 821 sprintf(_('RDN attribute sequence [%s] is already used by attribute [%s] and cant be used by attribute [%s] also.'), 822 $attribute->isRDN(),$return[$attribute->isRDN()],$attribute->getName())), 823 'type'=>'error'),'index.php'); 824 825 $return[$attribute->isRDN()] = $attribute->getName(); 826 } 827 828 return $return; 829 } 830 831 /** 832 * Return the RDN for this template. If the DN is already defined, then the RDN will be calculated from it. 833 * If the DN is not set, then the RDN will be calcuated from the template attribute definitions 834 * 835 * @return rdn RDN for this template 836 */ 837 public function getRDN() { 838 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 839 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 840 841 # If the DN is set, then the RDN will be calculated from it. 842 if ($this->dn) 843 return get_rdn($this->dn); 844 845 $rdn = ''; 846 847 foreach ($this->getRDNObjects() as $attribute) { 848 $vals = $attribute->getValues(); 849 850 # If an RDN attribute has no values, return with an empty string. The calling script should handle this. 851 if (! count($vals)) 852 return ''; 853 854 foreach ($vals as $val) 855 $rdn .= sprintf('%s=%s+',$attribute->getName(false),$val); 856 } 857 858 # Chop the last plus sign off when returning 859 return preg_replace('/\+$/','',$rdn); 860 } 861 862 /** 863 * Return the attribute name part of the RDN 864 */ 865 public function getRDNAttributeName() { 866 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 867 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 868 869 $attr = array(); 870 871 if ($this->getDN()) { 872 $i = strpos($this->getDN(),','); 873 if ($i !== false) { 874 $attrs = explode('\+',substr($this->getDN(),0,$i)); 875 foreach ($attrs as $id => $attr) { 876 list ($name,$value) = explode('=',$attr); 877 $attrs[$id] = $name; 878 } 879 880 $attr = array_unique($attrs); 881 } 882 } 883 884 return $attr; 885 } 886 887 /** 888 * Determine the type of template this is 889 */ 890 public function getContext() { 891 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 892 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 893 894 if ($this->getContainer() && get_request('cmd','REQUEST') == 'copy') 895 return 'copyasnew'; 896 elseif ($this->getContainer() || get_request('create_base')) 897 return 'create'; 898 elseif ($this->getDN()) 899 return 'edit'; 900 else 901 return 'unknown'; 902 } 903 904 /** 905 * Test if the template is visible 906 * 907 * @return boolean 908 */ 909 public function isVisible() { 910 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 911 debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->visible); 912 913 return $this->visible; 914 } 915 916 public function setVisible() { 917 $this->visible = true; 918 } 919 920 public function setInvisible() { 921 $this->visible = false; 922 } 923 924 public function getRegExp() { 925 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 926 debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->regexp); 927 928 return $this->regexp; 929 } 930 931 /** 932 * Test if this template has been marked as a read-only template 933 */ 934 public function isReadOnly() { 935 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 936 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 937 938 if ((($this->getContext() == 'edit') && $this->readonly) || $this->getServer()->isReadOnly()) 939 return true; 940 else 941 return false; 942 } 943 944 /** 945 * Get the attribute entries 946 * 947 * @param boolean Include the optional attributes 948 * @return array Array of attributes 949 */ 950 public function getAttributes($optional=false) { 951 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 952 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 953 954 if ($optional) 955 return $this->attributes; 956 957 $result = array(); 958 foreach ($this->attributes as $attribute) { 959 if (! $attribute->isRequired()) 960 continue; 961 962 array_push($result,$attribute); 963 } 964 965 return $result; 966 } 967 968 /** 969 * Return a list of attributes that should be shown 970 */ 971 public function getAttributesShown() { 972 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 973 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 974 975 $result = array(); 976 977 foreach ($this->attributes as $attribute) 978 if ($attribute->isVisible()) 979 array_push($result,$attribute); 980 981 return $result; 982 } 983 984 /** 985 * Return a list of the internal attributes 986 */ 987 public function getAttributesInternal() { 988 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 989 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 990 991 $result = array(); 992 993 foreach ($this->attributes as $attribute) 994 if ($attribute->isInternal()) 995 array_push($result,$attribute); 996 997 return $result; 998 } 999 1000 /** 1001 * Return the objectclasses defined in this template 1002 * 1003 * @return array Array of Objects 1004 */ 1005 public function getObjectClasses() { 1006 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 1007 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 1008 1009 $attribute = $this->getAttribute('objectclass'); 1010 if ($attribute) 1011 return $attribute->getValues(); 1012 else 1013 return array(); 1014 } 1015 1016 /** 1017 * Get template icon 1018 */ 1019 public function getIcon() { 1020 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 1021 debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->icon); 1022 1023 return isset($this->icon) ? sprintf('%s/%s',IMGDIR,$this->icon) : ''; 1024 } 1025 1026 /** 1027 * Return the template description 1028 * 1029 * @return string Description 1030 */ 1031 public function getDescription() { 1032 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 1033 debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->description); 1034 1035 return $this->description; 1036 } 1037 1038 /** 1039 * Set a template as invalid 1040 * 1041 * @param string Message indicating the reason the template has been invalidated 1042 */ 1043 public function setInvalid($msg,$admin=false) { 1044 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 1045 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 1046 1047 $this->invalid = true; 1048 $this->invalid_reason = $msg; 1049 $this->invalid_admin = $admin; 1050 } 1051 1052 /** 1053 * Get the template validity or the reason it is invalid 1054 * 1055 * @return string Invalid reason, or false if not invalid 1056 */ 1057 public function isInValid() { 1058 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 1059 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 1060 1061 if ($this->invalid) 1062 return $this->invalid_reason; 1063 else 1064 return false; 1065 } 1066 1067 public function isAdminDisabled() { 1068 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 1069 debug_log('Entered (%%)',5,1,__FILE__,__LINE__,__METHOD__,$fargs,$this->invalid_admin); 1070 1071 return $this->invalid_admin; 1072 } 1073 1074 /** 1075 * Set the minimum number of values for an attribute 1076 * 1077 * @param object Attribute 1078 * @param int 1079 */ 1080 private function setMinValueCount($attr,$value) { 1081 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 1082 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 1083 1084 $attribute = $this->getAttribute($attr); 1085 1086 if (! is_null($attribute)) 1087 $attribute->setMinValueCount($value); 1088 } 1089 1090 /** 1091 * Set the LDAP type property for an attribute 1092 * 1093 * @param object Attribute 1094 * @param string (MUST,MAY,OPTIONAL) 1095 */ 1096 private function setAttrLDAPtype($attr,$value) { 1097 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 1098 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 1099 1100 $attribute = $this->getAttribute($attr); 1101 1102 if (is_null($attribute)) 1103 $attribute = $this->addAttribute($attr,array('values'=>array())); 1104 1105 $attribute->setLDAPtype($value); 1106 } 1107 1108 /** 1109 * OnChangeAdd javascript processing 1110 */ 1111 public function OnChangeAdd($origin,$value) { 1112 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 1113 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 1114 1115 $attribute = $this->getAttribute($origin); 1116 1117 if (preg_match('/^=(\w+)\((.*)\)$/',$value,$matches)) { 1118 $command = $matches[1]; 1119 $arg = $matches[2]; 1120 } else 1121 return; 1122 1123 switch ($command) { 1124 /* 1125 autoFill:string 1126 string is a literal string, and may contain many fields like %attr|start-end/flags|additionalcontrolchar% 1127 to substitute values read from other fields. 1128 |start-end is optional, but must be present if the k flag is used. 1129 /flags is optional. 1130 |additionalcontrolchar is optional. 1131 1132 flags may be: 1133 T: Read display text from selection item (drop-down list), otherwise, read the value of the field 1134 For fields that aren't selection items, /T shouldn't be used, and the field value will always be read. 1135 k: Tokenize: 1136 If the "k" flag is not given: 1137 A |start-end instruction will perform a sub-string operation upon 1138 the value of the attr, passing character positions start-end through. 1139 start can be 0 for first character, or any other integer. 1140 end can be 0 for last character, or any other integer for a specific position. 1141 If the "k" flag is given: 1142 The string read will be split into fields, using : as a delimiter 1143 "start" indicates which field number to pass through. 1144 K: The string read will be split into fields, using ' ' as a delimiter "start" indicates which field number to pass through. 1145 If additionalcontrolchar is given, it will be used as delimiter (e.g. this allows for splitting e-mail addresses 1146 into domain and domain-local part). 1147 l: Make the result lower case. 1148 U: Make the result upper case. 1149 A: Remap special characters to their corresponding ASCII value 1150 */ 1151 case 'autoFill': 1152 if (! preg_match('/;/',$arg)) { 1153 system_message(array( 1154 'title'=>_('Problem with autoFill() in template'), 1155 'body'=>sprintf('%s (<b>%s</b>)',_('There is only 1 argument, when there should be two'),$attribute->getName(false)), 1156 'type'=>'warn')); 1157 1158 return; 1159 } 1160 1161 list($attr,$string) = preg_split('(([^,]+);(.*))',$arg,-1,PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); 1162 preg_match_all('/%(\w+)(\|[0-9]*-[0-9]*)?(\/[KklTUA]+)?(?:\|(.))?%/U',$string,$matchall); 1163 //print"<PRE>";print_r($matchall); //0 = highlevel match, 1 = attr, 2 = subst, 3 = mod, 4 = delimiter 1164 1165 if (! isset($attribute->js['autoFill'])) 1166 $attribute->js['autoFill'] = ''; 1167 1168 $formula = $string; 1169 $formula = preg_replace('/^([^%])/','\'$1',$formula); 1170 $formula = preg_replace('/([^%])$/','$1\'',$formula); 1171 1172 # Check that our attributes match our schema attributes. 1173 foreach ($matchall[1] as $index => $checkattr) { 1174 $sattr = $this->getServer()->getSchemaAttribute($checkattr); 1175 1176 # If the attribute is the same as in the XML file, then dont need to do anything. 1177 if (! $sattr || ! strcasecmp($sattr->getName(),$checkattr)) 1178 continue; 1179 1180 $formula = preg_replace("/$checkattr/",$sattr->getName(),$formula); 1181 $matchall[1][$index] = $sattr->getName(); 1182 } 1183 1184 $elem_id = 0; 1185 1186 foreach ($matchall[0] as $index => $null) { 1187 $match_attr = strtolower($matchall[1][$index]); 1188 $match_subst = $matchall[2][$index]; 1189 $match_mod = $matchall[3][$index]; 1190 $match_delim = $matchall[4][$index]; 1191 1192 $substrarray = array(); 1193 1194 if (! isset($varcount[$match_attr])) 1195 $varcount[$match_attr] = 0; 1196 else 1197 $varcount[$match_attr]++; 1198 1199 $js_match_attr = $match_attr; 1200 $match_attr = $js_match_attr.'xx'.$varcount[$match_attr]; 1201 1202 $formula = preg_replace('/%'.$js_match_attr.'([|\/%])/i','%'.$match_attr.'$1',$formula,1); 1203 1204 $attribute->js['autoFill'] .= sprintf(" var %s;\n",$match_attr); 1205 $attribute->js['autoFill'] .= sprintf( 1206 " var elem$elem_id = document.getElementById(pre+'%s'+suf);\n". 1207 " if (!elem$elem_id) return;\n", $js_match_attr); 1208 1209 if (strstr($match_mod,'T')) { 1210 $attribute->js['autoFill'] .= sprintf(" %s = elem$elem_id.options[elem$elem_id.selectedIndex].text;\n", 1211 $match_attr); 1212 } else { 1213 $attribute->js['autoFill'] .= sprintf(" %s = elem$elem_id.value;\n",$match_attr); 1214 } 1215 1216 $elem_id++; 1217 1218 if (strstr($match_mod,'k')) { 1219 preg_match_all('/([0-9]+)/',trim($match_subst),$substrarray); 1220 if (isset($substrarray[1][0])) { 1221 $tok_idx = $substrarray[1][0]; 1222 } else { 1223 $tok_idx = '0'; 1224 } 1225 $attribute->js['autoFill'] .= sprintf(" %s = %s.split(':')[%s];\n",$match_attr,$match_attr,$tok_idx); 1226 1227 } elseif (strstr($match_mod,'K')) { 1228 preg_match_all('/([0-9]+)/',trim($match_subst),$substrarray); 1229 if (isset($substrarray[1][0])) { 1230 $tok_idx = $substrarray[1][0]; 1231 } else { 1232 $tok_idx = '0'; 1233 } 1234 1235 if ($match_delim == '') { 1236 $delimiter = ' '; 1237 } else { 1238 $delimiter = preg_quote($match_delim); 1239 } 1240 $attribute->js['autoFill'] .= sprintf(" %s = %s.split('%s')[%s];\n",$match_attr,$match_attr,$delimiter,$tok_idx); 1241 1242 } else { 1243 preg_match_all('/([0-9]*)-([0-9]*)/',trim($match_subst),$substrarray); 1244 if ((isset($substrarray[1][0]) && $substrarray[1][0]) || (isset($substrarray[2][0]) && $substrarray[2][0])) { 1245 $attribute->js['autoFill'] .= sprintf(" %s = %s.substr(%s,%s);\n", 1246 $match_attr,$match_attr, 1247 $substrarray[1][0] ? $substrarray[1][0] : '0', 1248 $substrarray[2][0] ? $substrarray[2][0] : sprintf('%s.length',$match_attr)); 1249 } 1250 } 1251 1252 if (strstr($match_mod,'l')) { 1253 $attribute->js['autoFill'] .= sprintf(" %s = %s.toLowerCase();\n",$match_attr,$match_attr); 1254 } 1255 if (strstr($match_mod,'U')) { 1256 $attribute->js['autoFill'] .= sprintf(" %s = %s.toUpperCase();\n",$match_attr,$match_attr); 1257 } 1258 if (strstr($match_mod,'A')) { 1259 $attribute->js['autoFill'] .= sprintf(" %s = toAscii(%s);\n",$match_attr,$match_attr); 1260 } 1261 1262 # Matchfor only entry without modifiers. 1263 $formula = preg_replace('/^%('.$match_attr.')%$/U','$1 + \'\'',$formula); 1264 # Matchfor only entry with modifiers. 1265 $formula = preg_replace('/^%('.$match_attr.')(\|[0-9]*-[0-9]*)?(\/[KklTUA]+)?(?:\|(.))?%$/U','$1 + \'\'',$formula); 1266 # Matchfor begining entry. 1267 $formula = preg_replace('/^%('.$match_attr.')(\|[0-9]*-[0-9]*)?(\/[KklTUA]+)?(?:\|(.))?%/U','$1 + \'',$formula); 1268 # Matchfor ending entry. 1269 $formula = preg_replace('/%('.$match_attr.')(\|[0-9]*-[0-9]*)?(\/[KklTUA]+)?(?:\|(.))?%$/U','\' + $1 ',$formula); 1270 # Match for entries not at begin/end. 1271 $formula = preg_replace('/%('.$match_attr.')(\|[0-9]*-[0-9]*)?(\/[KklTUA]+)?(?:\|(.))?%/U','\' + $1 + \'',$formula); 1272 $attribute->js['autoFill'] .= "\n"; 1273 } 1274 1275 $attribute->js['autoFill'] .= sprintf(" fillRec(pre+'%s'+suf, %s); // %s\n",strtolower($attr),$formula,$string); 1276 $attribute->js['autoFill'] .= "\n"; 1277 break; 1278 1279 default: $return = ''; 1280 } 1281 } 1282 1283 /** 1284 * This functions main purpose is to discover our MUST attributes based on objectclass 1285 * definitions in the template file and to discover which of the objectclasses are 1286 * STRUCTURAL - without one, creating an entry will just product an LDAP error. 1287 */ 1288 private function rebuildTemplateAttrs() { 1289 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 1290 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 1291 1292 $server = $this->getServer(); 1293 1294 # Collect our structural, MUST & MAY attributes. 1295 $oclass_processed = array(); 1296 $superclasslist = array(); 1297 $allattrs = array('objectclass'); 1298 1299 foreach ($this->getObjectClasses() as $oclass) { 1300 # If we get some superclasses - then we'll need to go through them too. 1301 $supclass = true; 1302 $inherited = false; 1303 1304 while ($supclass) { 1305 $soc = $server->getSchemaObjectClass($oclass); 1306 1307 if ($soc->getType() == 'structural' && (! $inherited)) 1308 array_push($this->structural_oclass,$oclass); 1309 1310 # Make sure our MUST attributes are marked as such for this template. 1311 if ($soc->getMustAttrs()) 1312 foreach ($soc->getMustAttrs() as $index => $details) { 1313 $objectclassattr = $details->getName(); 1314 1315 # We add the 'objectClass' attribute, only if it's explicitly in the template attribute list 1316 if ((strcasecmp('objectClass',$objectclassattr) != 0) || 1317 ((strcasecmp('objectClass',$objectclassattr) == 0) && (! is_null($this->getAttribute($objectclassattr))))) { 1318 1319 # Go through the aliases, and ignore any that are already defined. 1320 $ignore = false; 1321 $sattr = $server->getSchemaAttribute($objectclassattr); 1322 foreach ($sattr->getAliases() as $alias) { 1323 if ($this->isAttrType($alias,'must')) { 1324 $ignore = true; 1325 break; 1326 } 1327 } 1328 1329 if ($ignore) 1330 continue; 1331 1332 $this->setAttrLDAPtype($sattr->getName(),'must'); 1333 $this->setMinValueCount($sattr->getName(),1); 1334 1335 # We need to mark the attributes as show, except for the objectclass attribute. 1336 if (strcasecmp('objectClass',$objectclassattr) != 0) { 1337 $attribute = $this->getAttribute($sattr->getName()); 1338 $attribute->show(); 1339 } 1340 } 1341 1342 if (! in_array($objectclassattr,$allattrs)) 1343 array_push($allattrs,$objectclassattr); 1344 } 1345 1346 if ($soc->getMayAttrs()) 1347 foreach ($soc->getMayAttrs() as $index => $details) { 1348 $objectclassattr = $details->getName(); 1349 $sattr = $server->getSchemaAttribute($objectclassattr); 1350 1351 # If it is a MUST attribute, skip to the next one. 1352 if ($this->isAttrType($objectclassattr,'must')) 1353 continue; 1354 1355 if (! $this->isAttrType($objectclassattr,'may')) 1356 $this->setAttrLDAPtype($sattr->getName(false),'may'); 1357 1358 if (! in_array($objectclassattr,$allattrs)) 1359 array_push($allattrs,$objectclassattr); 1360 } 1361 1362 # Keep a list to objectclasses we have processed, so we dont get into a loop. 1363 array_push($oclass_processed,$oclass); 1364 $supoclasses = $soc->getSupClasses(); 1365 1366 if (count($supoclasses) || count($superclasslist)) { 1367 foreach ($supoclasses as $supoclass) { 1368 if (! in_array($supoclass,$oclass_processed)) 1369 $superclasslist[] = $supoclass; 1370 } 1371 1372 $oclass = array_shift($superclasslist); 1373 if ($oclass) 1374 $inherited = true; 1375 else 1376 $supclass = false; 1377 1378 } else { 1379 $supclass = false; 1380 } 1381 } 1382 } 1383 1384 # Check that attributes are defined by an ObjectClass 1385 foreach ($this->getAttributes(true) as $index => $attribute) 1386 if (! in_array($attribute->getName(),$allattrs) && (! array_intersect($attribute->getAliases(),$allattrs)) 1387 && (! in_array_ignore_case('extensibleobject',$this->getObjectClasses())) 1388 && (! in_array_ignore_case($attribute->getName(),$server->getValue('server','custom_attrs')))) { 1389 unset($this->attributes[$index]); 1390 1391 if (! $_SESSION[APPCONFIG]->getValue('appearance','hide_template_warning')) 1392 system_message(array( 1393 'title'=>_('Automatically removed attribute from template'), 1394 'body'=>sprintf('%s: <b>%s</b> %s',$this->getTitle(),$attribute->getName(false),_('removed from template as it is not defined by an ObjectClass')), 1395 'type'=>'warn')); 1396 } 1397 } 1398 1399 /** 1400 * Return an array, that can be passed to ldap_add(). 1401 * Attributes with empty values will be excluded. 1402 */ 1403 public function getLDAPadd($attrsOnly=false) { 1404 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 1405 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 1406 1407 $return = array(); 1408 $returnattrs = array(); 1409 1410 if ($attrsOnly && count($returnattrs)) 1411 return $returnattrs; 1412 1413 foreach ($this->getAttributes(true) as $attribute) 1414 if (! $attribute->isInternal() && count($attribute->getValues())) { 1415 $return[$attribute->getName()] = $attribute->getValues(); 1416 $returnattrs[$attribute->getName()] = $attribute; 1417 } 1418 1419 # Ensure that our objectclasses has "top". 1420 if (isset($return['objectclass']) && ! in_array('top',$return['objectclass'])) 1421 array_push($return['objectclass'],'top'); 1422 1423 if ($attrsOnly) 1424 return $returnattrs; 1425 1426 return $return; 1427 } 1428 1429 /** 1430 * Return an array, that can be passed to ldap_mod_replace(). 1431 * Only attributes that have changed their value will be returned. 1432 * 1433 * This function will cache its results, so that it can be called with count() to see 1434 * if there are changes, and if they are, the 2nd call will just return the results 1435 * 1436 * @param boolean Return the attribute objects (useful for a confirmation process), or the modification array for ldap_modify() 1437 */ 1438 public function getLDAPmodify($attrsOnly=false,$index=0) { 1439 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 1440 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 1441 1442 static $return = array(); 1443 static $returnattrs = array(); 1444 1445 if ($attrsOnly && isset($returnattrs[$index]) && count($returnattrs[$index])) 1446 return $returnattrs[$index]; 1447 1448 $returnattrs[$index] = array(); 1449 $return[$index] = array(); 1450 1451 # If an objectclass is being modified, we need to remove all the orphan attributes that would result. 1452 if ($this->getAttribute('objectclass')->hasBeenModified()) { 1453 $attr_to_keep = array(); 1454 $server = $this->getServer(); 1455 1456 # Make sure that there will be a structural object class remaining. 1457 $haveStructural = false; 1458 foreach ($this->getAttribute('objectclass')->getValues() as $value) { 1459 $soc = $server->getSchemaObjectClass($value); 1460 1461 if ($soc) { 1462 if ($soc->isStructural()) 1463 $haveStructural = true; 1464 1465 # While we are looping, workout which attributes these objectclasses define. 1466 foreach ($soc->getMustAttrs(true) as $value) 1467 if (! in_array($value->getName(),$attr_to_keep)) 1468 array_push($attr_to_keep,$value->getName()); 1469 1470 foreach ($soc->getMayAttrs(true) as $value) 1471 if (! in_array($value->getName(),$attr_to_keep)) 1472 array_push($attr_to_keep,$value->getName()); 1473 } 1474 } 1475 1476 if (! $haveStructural) 1477 error(_('An entry should have one structural objectClass.'),'error','index.php'); 1478 1479 # Work out the attributes to delete. 1480 foreach ($this->getAttribute('objectclass')->getRemovedValues() as $value) { 1481 $soc = $server->getSchemaObjectClass($value); 1482 1483 foreach ($soc->getMustAttrs() as $value) { 1484 $attribute = $this->getAttribute($value->getName()); 1485 1486 if ($attribute && (! in_array($value->getName(),$attr_to_keep)) && ($value->getName() != 'objectclass')) 1487 #array_push($attr_to_delete,$value->getName(false)); 1488 $attribute->setForceDelete(); 1489 } 1490 1491 foreach ($soc->getMayAttrs() as $value) { 1492 $attribute = $this->getAttribute($value->getName()); 1493 1494 if ($attribute && (! in_array($value->getName(),$attr_to_keep)) && ($value->getName() != 'objectclass')) 1495 $attribute->setForceDelete(); 1496 } 1497 } 1498 } 1499 1500 foreach ($this->getAttributes(true) as $attribute) 1501 if ($attribute->hasBeenModified() 1502 && (count(array_diff($attribute->getValues(),$attribute->getOldValues())) || ! count($attribute->getValues()) 1503 || $attribute->isForceDelete() || (count($attribute->getValues()) != count($attribute->getOldValues())))) 1504 $returnattrs[$index][$attribute->getName()] = $attribute; 1505 1506 if ($attrsOnly) 1507 return $returnattrs[$index]; 1508 1509 foreach ($returnattrs[$index] as $attribute) 1510 $return[$index][$attribute->getName()] = $attribute->getValues(); 1511 1512 return $return[$index]; 1513 } 1514 1515 /** 1516 * Get the attributes that are marked as force delete 1517 * We'll cache this result in the event of multiple calls. 1518 */ 1519 public function getForceDeleteAttrs() { 1520 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 1521 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 1522 1523 static $result = array(); 1524 1525 if (count($result)) 1526 return $result; 1527 1528 foreach ($this->attributes as $attribute) 1529 if ($attribute->isForceDelete()) 1530 array_push($result,$attribute); 1531 1532 return $result; 1533 } 1534 1535 /** 1536 * Get available attributes 1537 */ 1538 public function getAvailAttrs() { 1539 if (DEBUG_ENABLED && (($fargs=func_get_args())||$fargs='NOARGS')) 1540 debug_log('Entered (%%)',5,0,__FILE__,__LINE__,__METHOD__,$fargs); 1541 1542 $attributes = array(); 1543 $server = $this->getServer(); 1544 1545 # Initialise the Attribute Factory. 1546 $attribute_factory = new AttributeFactory(); 1547 1548 if (in_array_ignore_case('extensibleobject',$this->getObjectClasses())) { 1549 foreach ($server->SchemaAttributes() as $sattr) { 1550 $attribute = $attribute_factory->newAttribute($sattr->getName(),array('values'=>array()),$server->getIndex(),null); 1551 array_push($attributes,$attribute); 1552 } 1553 1554 } else { 1555 $attrs = array(); 1556 1557 foreach ($this->getObjectClasses() as $oc) { 1558 $soc = $server->getSchemaObjectClass($oc); 1559 $attrs = array_merge($attrs,$soc->getMustAttrNames(true),$soc->getMayAttrNames(true)); 1560 $attrs = array_unique($attrs); 1561 } 1562 1563 foreach ($attrs as $attr) 1564 if (is_null($this->getAttribute($attr))) { 1565 $attribute = $attribute_factory->newAttribute($attr,array('values'=>array()),$server->getIndex(),null); 1566 array_push($attributes,$attribute); 1567 } 1568 } 1569 1570 masort($attributes,'name'); 1571 return $attributes; 1572 } 1573 1574 public function isNoLeaf() { 1575 return $this->noleaf; 1576 } 1577 1578 public function sort() { 1579 usort($this->attributes,'sortAttrs'); 1580 } 1581} 1582?> 1583