1<?php 2/** 3 * EGgroupware setup - create or update the header.inc.php 4 * 5 * @link http://www.egroupware.org 6 * @author Ralf Becker <RalfBecker-AT-outdoor-training.de> 7 * @package setup 8 * @copyright (c) 2007-19 by Ralf Becker <RalfBecker-AT-outdoor-training.de> 9 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License 10 */ 11 12use EGroupware\Api; 13 14/** 15 * setup command: create or update the header.inc.php 16 * 17 * @ToDo: incorporate setup_header here 18 */ 19class setup_cmd_header extends setup_cmd 20{ 21 /** 22 * Instance of setup's header object 23 * 24 * @var setup_header 25 */ 26 private $setup_header; 27 /** 28 * Full path of the header.inc.php 29 * 30 * @var string 31 */ 32 private $header_path; 33 34 /** 35 * Constructor 36 * 37 * @param string|array $sub_command ='create' 'create','edit','delete'(-domain) or array with all arguments 38 * @param array $arguments =null comand line arguments 39 */ 40 function __construct($sub_command='create',$arguments=null) 41 { 42 if (!is_array($sub_command)) 43 { 44 $sub_command = array( 45 'sub_command' => $sub_command, 46 'arguments' => $arguments, 47 ); 48 } 49 //echo __CLASS__.'::__construct()'; _debug_array($domain); 50 admin_cmd::__construct($sub_command); 51 52 // header is 3 levels lower then this command in setup/inc 53 $this->header_path = dirname(dirname(__DIR__)).'/header.inc.php'; 54 55 // if header is a symlink --> work on it's target 56 if (is_link($this->header_path)) 57 { 58 $this->header_path = readlink($this->header_path); 59 if ($this->header_path[0] != '/' && $this->header_path[1] != ':') 60 { 61 $this->header_path = dirname(dirname(__DIR__)).'/'.$this->header_path; 62 } 63 } 64 $this->setup_header = new setup_header(); 65 } 66 67 /** 68 * Create or update header.inc.php 69 * 70 * @param boolean $check_only =false only run the checks (and throw the exceptions), but not the command itself 71 * @return string serialized $GLOBALS defined in the header.inc.php 72 * @throws Exception(lang('Wrong credentials to access the header.inc.php file!'),2); 73 * @throws Exception('header.inc.php not found!'); 74 */ 75 protected function exec($check_only=false) 76 { 77 if ($check_only && $this->remote_id) 78 { 79 return true; // can only check locally 80 } 81 if (!file_exists($this->header_path) || filesize($this->header_path) < 200) // redirect header in rpms is ~150 byte 82 { 83 if ($this->sub_command != 'create') 84 { 85 throw new Api\Exception\WrongUserinput(lang('EGroupware configuration file (header.inc.php) does NOT exist.')."\n".lang('Use --create-header to create the configuration file (--usage gives more options).'),1); 86 } 87 $this->defaults(false); 88 } 89 else 90 { 91 if ($this->sub_command == 'create') 92 { 93 throw new Api\Exception\WrongUserinput( 94 lang('EGroupware configuration file header.inc.php already exists, you need to use --edit-header or delete it first!'),20); 95 } 96 if ($this->arguments) 97 { 98 list($this->header_admin_password,$this->header_admin_user) = explode(',',$this->arguments[1]); 99 } 100 $this->check_setup_auth($this->header_admin_user,$this->header_admin_password); // no domain, we require header access! 101 102 $GLOBALS['egw_info']['server']['server_root'] = EGW_SERVER_ROOT; 103 $GLOBALS['egw_info']['server']['include_root'] = EGW_INCLUDE_ROOT; 104 } 105 106 if ($this->arguments) // we have command line arguments 107 { 108 $this->_parse_cli_arguments(); 109 } 110 elseif ($this->sub_command == 'delete') 111 { 112 self::_delete_domain($this->domain); 113 } 114 else 115 { 116 $this->_parse_properties(); 117 } 118 if (($errors = $this->validation_errors($GLOBALS['egw_info']['server']['server_root'], 119 $GLOBALS['egw_info']['server']['include_root']))) 120 { 121 if ($this->arguments) 122 { 123 unset($GLOBALS['egw_info']['flags']); 124 echo '$GLOBALS[egw_info] = '; print_r($GLOBALS['egw_info']); 125 echo '$GLOBALS[egw_domain] = '; print_r($GLOBALS['egw_domain']); 126 } 127 throw new Api\Exception\WrongUserinput(lang('Configuration errors:')."\n- ".implode("\n- ",$errors)."\n".lang("You need to fix the above errors, before the configuration file header.inc.php can be written!"),23); 128 } 129 if ($check_only) 130 { 131 return true; 132 } 133 // check if php has persistent mysql connections disabled --> disable it in header, to not fill the log with warnings 134 if ($GLOBALS['egw_info']['server']['db_persistent']) 135 { 136 $GLOBALS['egw_info']['server']['db_persistent'] = $this->check_db_persistent($GLOBALS['egw_domain']); 137 } 138 $header = $this->generate($GLOBALS['egw_info'],$GLOBALS['egw_domain']); 139 140 if ($this->arguments) 141 { 142 echo $header; // for cli, we echo the header 143 } 144 if (file_exists($this->header_path) && is_writable($this->header_path) || is_writable(dirname($this->header_path)) || 145 function_exists('posix_getuid') && !posix_getuid()) // root has all rights 146 { 147 if (file_exists($this->header_path) && !is_writable($this->header_path)) 148 { 149 unlink($this->header_path); 150 } 151 if (($f = fopen($this->header_path,'wb')) && fwrite($f,$header)) 152 { 153 fclose($f); 154 return lang('header.inc.php successful written.'); 155 } 156 } 157 throw new Api\Exception\NoPermission(lang("Failed writing configuration file header.inc.php, check the permissions !!!"),24); 158 } 159 160 /** 161 * Magic method to allow to call all methods from setup_header, as if they were our own 162 * 163 * @param string $method 164 * @param array $args =null 165 * @return mixed 166 */ 167 function __call($method,array $args=null) 168 { 169 if (method_exists($this->setup_header,$method)) 170 { 171 return call_user_func_array(array($this->setup_header,$method),$args); 172 } 173 } 174 175 /** 176 * Available options and allowed arguments 177 * 178 * @var array 179 */ 180 static $options = array( 181 '--create-header' => array( 182 'header_admin_password' => 'egw_info/server/', 183 'header_admin_user' => 'egw_info/server/', 184 ), 185 '--edit-header' => array( 186 'header_admin_password' => 'egw_info/server/', 187 'header_admin_user' => 'egw_info/server/', 188 'new_admin_password' => 'egw_info/server/header_admin_password', 189 'new_admin_user' => 'egw_info/server/header_admin_user', 190 ), 191 '--server-root' => 'egw_info/server/server_root', 192 '--include-root' => 'egw_info/server/include_root', 193 '--session-type' => array( 194 'sessions_type' => array( 195 'type' => 'egw_info/server/', 196 'allowed' => array('php'=>'php4','php4'=>'php4','php-restore'=>'php4-restore','php4-restore'=>'php4-restore','db'=>'db'), 197 ), 198 ), 199 '--session-handler' => array( 200 'session_handler' => array( 201 'type' => 'egw_info/server/', 202 'allowed' => array('files'=>'files','memcache'=>'memcache','db'=>'db'), 203 ), 204 ), 205 '--limit-access' => 'egw_info/server/setup_acl', // name used in setup 206 '--setup-acl' => 'egw_info/server/setup_acl', // alias to match the real name 207 '--mcrypt' => array( 208 'mcrypt_enabled' => array( 209 'type' => 'egw_info/server/', 210 'allowed' => array('on' => true,'off' => false), 211 ), 212 'mcrypt_iv' => 'egw_info/server/', 213 'mcrypt' => 'egw_info/versions/mcrypt', 214 ), 215 '--domain-selectbox' => array( 216 'show_domain_selectbox' => array( 217 'type' => 'egw_info/server/', 218 'allowed' => array('on' => true,'off' => false), 219 ), 220 ), 221 '--db-persistent' => array( 222 'db_persistent' => array( 223 'type' => 'egw_info/server/', 224 'allowed' => array('on' => true,'off' => false), 225 ), 226 ), 227 '--domain' => array( 228 'domain' => '@', 229 'db_name' => 'egw_domain/@/', 230 'db_user' => 'egw_domain/@/', 231 'db_pass' => 'egw_domain/@/', 232 'db_type' => 'egw_domain/@/', 233 'db_host' => 'egw_domain/@/', 234 'db_port' => 'egw_domain/@/', 235 'config_user' => 'egw_domain/@/', 236 'config_passwd' => 'egw_domain/@/', 237 ), 238 '--delete-domain' => true, 239 ); 240 241 /** 242 * Parses properties from this object 243 */ 244 private function _parse_properties() 245 { 246 foreach(self::$options as $arg => $option) 247 { 248 foreach(is_array($option) ? $option : array($option => $option) as $name => $data) 249 { 250 if (strpos($name,'/') !== false) 251 { 252 $name = array_pop($parts = explode('/',$name)); 253 } 254 if (isset($this->$name)) 255 { 256 $this->_parse_value($arg,$name,$data,$this->$name); 257 } 258 } 259 } 260 } 261 262 /** 263 * Parses command line arguments in $this->arguments 264 */ 265 private function _parse_cli_arguments() 266 { 267 $arguments = $this->arguments; 268 while(($arg = array_shift($arguments))) 269 { 270 $values = count($arguments) && substr($arguments[0],0,2) !== '--' ? array_shift($arguments) : 'on'; 271 272 if ($arg == '--delete-domain') 273 { 274 $this->_delete_domain($values); 275 continue; 276 } 277 278 if (!isset(self::$options[$arg])) 279 { 280 throw new Api\Exception\WrongUserinput(lang("Unknown option '%1' !!!",$arg),90); 281 } 282 283 $option = self::$options[$arg]; 284 $vals = !is_array($option) ? array($values) : explode(',',$values); 285 if (!is_array($option)) $option = array($option => $option); 286 $n = 0; 287 foreach($option as $name => $data) 288 { 289 if ($n >= count($vals)) break; 290 291 $this->_parse_value($arg,$name,$data,$vals[$n++]); 292 } 293 } 294 } 295 296 /** 297 * Delete a given domain/instance from the header 298 * 299 * @param string $domain 300 */ 301 private static function _delete_domain($domain) 302 { 303 if (!isset($GLOBALS['egw_domain'][$domain])) 304 { 305 throw new Api\Exception\WrongUserinput(lang("Domain '%1' does NOT exist !!!",$domain),92); 306 } 307 unset($GLOBALS['egw_domain'][$domain]); 308 } 309 310 /** 311 * Parses a single value 312 * 313 * @param string $arg current cli argument processed 314 * @param string $name name of the property 315 * @param array/string $data string with type or array containing values for type, allowed 316 * @param mixed $value value to set 317 */ 318 private function _parse_value($arg,$name,$data,$value) 319 { 320 static $domain=null; 321 322 if (!is_array($data)) $data = array('type' => $data); 323 $type = $data['type']; 324 325 if (isset($data['allowed'])) 326 { 327 if (!isset($data['allowed'][$value])) 328 { 329 throw new Api\Exception\WrongUserinput(lang("'%1' is not allowed as %2. arguments of option %3 !!!",$value,1,$arg),91); 330 } 331 $value = $data['allowed'][$value]; 332 } 333 if ($type == '@') 334 { 335 $domain = $arg == '--domain' && !$value ? 'default' : $value; 336 if ($arg == '--domain' && (!isset($GLOBALS['egw_domain'][$domain]) || $this->sub_command == 'create')) 337 { 338 $GLOBALS['egw_domain'][$domain] = $this->domain_defaults($GLOBALS['egw_info']['server']['header_admin_user'],$GLOBALS['egw_info']['server']['header_admin_password']); 339 } 340 } 341 elseif ($value !== '') 342 { 343 self::_set_value($GLOBALS,str_replace('@',$domain,$type),$name,$value); 344 if ($type == 'egw_info/server/server_root') 345 { 346 self::_set_value($GLOBALS,'egw_info/server/include_root',$name,$value); 347 } 348 } 349 } 350 351 /** 352 * Set a value in the given array $arr with (multidimensional) key $index[/$name] 353 * 354 * @param array &$arr 355 * @param string $index multidimensional index written with / as separator, eg. egw_info/server/ 356 * @param string $name additional index to use if $index end with a slash 357 * @param mixed $value value to set 358 */ 359 static private function _set_value(&$arr,$index,$name,$value) 360 { 361 if (substr($index,-1) == '/') $index .= $name; 362 363 $var =& $arr; 364 foreach(explode('/',$index) as $name) 365 { 366 $var =& $var[$name]; 367 } 368 if (true) $var = $value; 369 } 370} 371