_ucr_secret()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
#!/usr/bin/env php
2
<?php
3
/**
4
 * EGroupware - RPM post install: automatic install or update EGroupware
5
 *
6
 * @link http://www.egroupware.org
7
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
8
 * @author [email protected]
9
 */
10
11
if (php_sapi_name() !== 'cli')	// security precaution: forbit calling post_install as web-page
12
{
13
	die('<h1>post_install.php must NOT be called as web-page --> exiting !!!</h1>');
14
}
15
$verbose = false;
16
$config = array(
17
	'php'         => PHP_BINARY,
18
	'source_dir'  => realpath(__DIR__.'/../..'),
19
	'data_dir'    => '/var/lib/egroupware',
20
	'header'      => '$data_dir/header.inc.php',	// symlinked to source_dir by rpm
21
	'setup-cli'   => '$source_dir/setup/setup-cli.php',
22
	'domain'      => 'default',
23
	'config_user' => 'admin',
24
	'config_passwd'   => randomstring(),
25
	'db_type'     => 'mysqli',
26
	'db_host'     => 'localhost',
27
	'db_port'     => 3306,
28
	'db_name'     => 'egroupware',
29
	'db_user'     => 'egroupware',
30
	'db_pass'     => randomstring(),
31
	'db_grant_host' => 'localhost',
32
	'db_root'     => 'root',	// mysql root user/pw to create database
33
	'db_root_pw'  => '',
34
	'backup'      => '',
35
	'admin_user'  => 'sysop',
36
	'admin_passwd'=> randomstring(),
37
	'admin_email' => '',
38
	'lang'        => 'en',	// languages for admin user and extra lang to install
39
	'charset'     => 'utf-8',
40
	'start_db'    => '/sbin/service mysqld',
41
	'autostart_db' => '/sbin/chkconfig --level 345 mysqld on',
42
	'start_webserver' => '/sbin/service httpd',
43
	'autostart_webserver' => '/sbin/chkconfig --level 345 httpd on',
44
	'distro'      => 'rh',
45
	'account-auth'  => 'sql',
46
	'account_min_id' => '',
47
	'ldap_suffix'   => 'dc=local',
48
	'ldap_host'     => 'localhost',
49
	'ldap_admin'    => 'cn=admin,$suffix',
50
	'ldap_admin_pw' => '',
51
	'ldap_base'     => 'o=$domain,$suffix',
52
	'ldap_root_dn'  => 'cn=admin,$base',
53
	'ldap_root_pw'  => randomstring(),
54
	'ldap_context'  => 'ou=accounts,$base',
55
	'ldap_search_filter' => '(uid=%user)',
56
	'ldap_group_context' => 'ou=groups,$base',
57
	'ldap_encryption_type' => '',
58
	'sambaadmin/sambasid'=> '',	// SID for sambaadmin
59
	'mailserver'    => '',
60
	'smtpserver'    => 'localhost,25',
61
	'smtp'          => '',	// see setup-cli.php --help config
62
	'imap'          => '',
63
	'sieve'         => '',
64
	'folder'        => '',
65
	'install-update-app' => '',	// install or update a single (non-default) app
66
	'webserver_user'=> 'apache',	// required to fix permissions
67
	'apache_config'   => '/etc/httpd/conf.d/egroupware.conf',
68
	'php5enmod'     => '',
69
);
70
71
// read language from LANG enviroment variable
72
if (($lang = isset($_ENV['LANG']) ? $_ENV['LANG'] : (isset($_SERVER['LANG']) ? $_SERVER['LANG'] : null)))
73
{
74
	@list($lang,$nat) = preg_split('/[_.]/',$lang);
75
	if (in_array($lang.'-'.strtolower($nat),array('es-es','pt-br','zh-tw')))
76
	{
77
		$lang .= '-'.strtolower($nat);
78
	}
79
	$config['lang'] = $lang;
80
}
81
$config['source_dir'] = dirname(dirname(dirname(__FILE__)));
82
83
/**
84
 * Set distribution spezific defaults
85
 *
86
 * @param string $distro =null default autodetect
87
 */
88
function set_distro_defaults($distro=null)
89
{
90
	global $config;
91
	if (is_null($distro))
92
	{
93
		$matches = null;
94
		// check for ID in /etc/os-release and use it
95
		if (file_exists('/etc/os-release') && preg_match('/^ID="?([^"=]+)"?$/m', $os_release=file_get_contents('/etc/os-release'), $matches))
96
		{
97
			$distro = $matches[1];
98
		}
99
		// old detections based on distro specific /etc/*release files
100
		else
101
		{
102
			$distro = file_exists('/etc/SuSE-release') ? 'suse' :
103
				(file_exists('/etc/mandriva-release') ? 'mandriva' :
104
				(file_exists('/etc/lsb-release') && preg_match('/^DISTRIB_ID="?Univention"?$/mi',
105
					file_get_contents('/etc/lsb-release')) ? 'univention' :
106
				(file_exists('/etc/debian_version') ? 'debian' : 'rh')));
107
		}
108
	}
109
	switch (($config['distro'] = $distro))
110
	{
111
		case 'suse': case 'opensuse-leap': case 'opensuse':
112
			// openSUSE 12.1+ no longer uses php5
113
			if (file_exists('/usr/bin/php5')) $config['php'] = '/usr/bin/php5';
114
			if (file_exists('/usr/bin/php7')) $config['php'] = '/usr/bin/php7';
115
			$config['start_db'] = '/sbin/service mysql';
116
			$config['autostart_db'] = '/sbin/chkconfig --level 345 mysql on';
117
			$config['start_webserver'] = '/sbin/service apache2';
118
			$config['autostart_webserver'] = '/sbin/chkconfig --level 345 apache2 on';
119
			$config['ldap_suffix'] = 'dc=site';
120
			$config['ldap_admin'] = $config['ldap_root_dn'] = 'cn=Administrator,$suffix';
121
			$config['ldap_root_pw'] = '$admin_pw';
122
			$config['ldap_base'] = '$suffix';
123
			$config['ldap_context'] = 'ou=people,$base';
124
			$config['ldap_group_context'] = 'ou=group,$base';
125
			$config['webserver_user'] = 'wwwrun';
126
			$config['apache_config'] = '/etc/apache2/conf.d/egroupware.conf';
127
			break;
128
		case 'debian': case 'ubuntu':
129
			// service not in Debian5, only newer Ubuntu, which complains about /etc/init.d/xx
130
			if (file_exists('/usr/sbin/service'))
131
			{
132
				$config['start_db'] = '/usr/sbin/service mysql';
133
				$config['start_webserver'] = '/usr/sbin/service apache2';
134
			}
135
			else
136
			{
137
				$config['start_db'] = '/etc/init.d/mysql';
138
				$config['start_webserver'] = '/etc/init.d/apache2';
139
			}
140
			$config['autostart_db'] = '/usr/sbin/update-rc.d mysql defaults';
141
			$config['autostart_webserver'] = '/usr/sbin/update-rc.d apache2 defaults';
142
			$config['webserver_user'] = 'www-data';
143
			$config['apache_config'] = '/etc/egroupware/apache.conf';
144
			break;
145
		case 'mandriva':
146
			$config['ldap_suffix'] = 'dc=site';
147
			$config['ldap_admin'] = $config['ldap_root_dn'] = 'uid=LDAP Admin,ou=System Accounts,$suffix';
148
			$config['ldap_root_pw'] = '$admin_pw';
149
			$config['ldap_base'] = '$suffix';
150
			$config['ldap_context'] = 'ou=People,$base';
151
			$config['ldap_group_context'] = 'ou=Group,$base';
152
			$config['apache_config'] = '/etc/apache2/conf.d/egroupware.conf';
153
			break;
154
		case 'univention':
155
			set_univention_defaults();
156
			break;
157
		default:
158
			// if we dont support ID from os-release, look for first one in ID_LIKE
159
			if (!empty($os_release) && preg_match('/^ID_LIKE="?([^"=]+)"?$/m', $os_release, $matches))
160
			{
161
				list($distro) = explode(' ', $matches[1]);
162
				return set_distro_defaults($distro);
0 ignored issues
show
Bug introduced by
Are you sure the usage of set_distro_defaults($distro) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
163
			}
164
			// old default: rh
165
			$config['distro'] = 'rh';
166
			// fall through
167
		case 'rh': case 'rhel': case 'centos': case 'fedora':
168
			// some MySQL packages (mysql.com, MariaDB, ...) use "mysql" as service name instead of RH default "mysqld"
169
			if (file_exists('/usr/bin/systemctl'))	// RHEL 7
170
			{
171
				$config['start_db'] = '/usr/bin/systemctl %s mariadb';
172
				$config['autostart_db'] = build_cmd('start_db', 'enable');
173
				$config['start_webserver'] = '/usr/bin/systemctl %s httpd';
174
				$config['autostart_webserver'] = build_cmd('start_webserver', 'enable');
175
			}
176
			elseif (!file_exists('/etc/init.d/mysqld') && file_exists('/etc/init.d/mysql'))
177
			{
178
				foreach(array('start_db','autostart_db') as $name)
179
				{
180
					$config[$name] = str_replace('mysqld','mysql',$config[$name]);
181
				}
182
			}
183
			break;
184
	}
185
}
186
set_distro_defaults();
187
188
// read EGW_* environment variables overwriting default config above allowing to set all parameters via environment
189
foreach($config as $name => $value)
190
{
191
	if (isset($_SERVER[$env='EGW_'.strtoupper(strtr($name, ['-'=>'_','/'=>'_']))]) && $_SERVER[$env] !== $value)
192
	{
193
		$config[$name] = $_SERVER[$env];
194
	}
195
}
196
197
// read config from command line
198
$argv = str_replace(array("''", '""'), '', $_SERVER['argv']);
199
$prog = array_shift($argv);
200
201
// check if we have EGW_POST_INSTALL set and prepend it to the command line (command line has precedence)
202
if (($config_set = isset($_ENV['EGW_POST_INSTALL']) ? $_ENV['EGW_POST_INSTALL'] : @$_SERVER['EGW_POST_INSTALL']))
203
{
204
	$conf = array();
205
	$config_set = preg_split('/[ \t]+/',trim($config_set));
206
	while($config_set)
207
	{
208
		$val = array_shift($config_set);
209
		if (($quote = $val[0]) == "'" || $quote == '"')	// arguments might be quoted with ' or "
210
		{
211
			while (substr($val,-1) != $quote)
212
			{
213
				if (!$config_set) throw new Exception('Invalid EGW_POST_INSTALL enviroment variable!');
214
				$val .= ' '.array_shift($config_set);
215
			}
216
			$val = substr($val,1,-1);
217
		}
218
		$conf[] = $val;
219
	}
220
	$argv = array_merge($conf,$argv);
221
}
222
223
$auth_type_given = false;
224
while(($arg = array_shift($argv)))
225
{
226
	if ($arg == '-v' || $arg == '--verbose')
227
	{
228
		$verbose = true;
229
	}
230
	elseif($arg == '-h' || $arg == '--help')
231
	{
232
		usage();
233
	}
234
	elseif($arg == '--suse')
235
	{
236
		set_distro_defaults('suse');
237
	}
238
	elseif($arg == '--distro')
239
	{
240
		set_distro_defaults(array_shift($argv));
241
	}
242
	elseif(substr($arg,0,2) == '--' && isset($config[$name=substr($arg,2)]))
243
	{
244
		$config[$name] = array_shift($argv);
245
246
		// replace empty pw with a random one
247
		if ($name === 'db_pass' && $config[$name] === '')
248
		{
249
			$config[$name] = randomstring();
250
		}
251
252
		switch($name)
253
		{
254
			case 'auth_type':
255
				$auth_type_given = true;
256
				break;
257
258
			case 'account_repository':	// auth-type defaults to account-repository
259
				if (!$auth_type_given)
260
				{
261
					$config['auth_type'] = $config[$name];
262
				}
263
				break;
264
		}
265
	}
266
	else
267
	{
268
		usage("Unknown argument '$arg'!");
269
	}
270
}
271
272
$replace = array();
273
foreach($config as $name => $value)
274
{
275
	$replace['$'.$name] = $value;
276
	if (strpos($value,'$') !== false)
277
	{
278
		$config[$name] = strtr($value,$replace);
279
	}
280
}
281
// basic config checks
282
foreach(array('php','source_dir','data_dir','setup-cli') as $name)
283
{
284
	if (!file_exists($config[$name])) bail_out(1,$config[$name].' not found!');
285
}
286
287
// fix important php.ini and conf.d/*.ini settings
288
check_fix_php_apc_ini();
289
290
// not limiting memory, as backups might fail with limit we set
291
$setup_cli = $config['php'].' -d memory_limit=-1 '.$config['setup-cli'];
292
293
// if we have a header, include it
294
if (file_exists($config['header']) && filesize($config['header']) >= 200)	// default header redirecting to setup is 147 bytes
295
{
296
	$GLOBALS['egw_info'] = array(
297
		'flags' => array(
298
			'noapi' => true,
299
			'currentapp' => 'login',	// stop PHP Notice: Undefined index "currentapp" in pre 16.1 header
300
		)
301
	);
302
	include $config['header'];
303
304
	// get user from header and replace password, as we dont know it
305
	$old_password = patch_header($config['header'],$config['config_user'],$config['config_passwd']);
306
	// register a shutdown function to put old password back in any case
307
	register_shutdown_function(function() use (&$config, $old_password)
308
	{
309
		patch_header($config['header'], $config['config_user'], $old_password);
310
	});
311
}
312
// new header or does not include requested domain (!= "default") --> new install
313
if (!isset($GLOBALS['egw_domain']) ||  $config['domain'] !== 'default' && !isset($GLOBALS['egw_domain'][$config['domain']]))
314
{
315
	// --> new install
316
	$extra_config = '';
317
318
	// check for localhost if database server is started and start it (permanent) if not
319
	if ($config['db_host'] == 'localhost' && $config['start_db'])
320
	{
321
		exec(build_cmd('start_db', 'status'), $dummy, $ret);
322
		if ($ret)
323
		{
324
			system(build_cmd('start_db', 'start'));
325
			if (!empty($config['autostart_db'])) system($config['autostart_db']);
326
		}
327
	}
328
	// create database
329
	$setup_db = $setup_cli.' --setup-cmd-database sub_command=create_db';
330
	foreach(array('domain','db_type','db_host','db_port','db_name','db_user','db_pass','db_root','db_root_pw','db_grant_host') as $name)
331
	{
332
		$setup_db .= ' '.escapeshellarg($name.'='.$config[$name]);
333
	}
334
	run_cmd($setup_db);
0 ignored issues
show
Bug introduced by
The call to run_cmd() has too few arguments starting with name. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

334
	/** @scrutinizer ignore-call */ 
335
 run_cmd($setup_db);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
335
336
	// check if ldap is required and initialise it
337
	// we need to specify account_repository and auth_type to --install as extra config, otherwise install happens for sql!
338
	@list($config['account_repository'],$config['auth_type'],$rest) = explode(',',$config['account-auth'],3);
339
	$extra_config .= ' '.escapeshellarg('account_repository='.$config['account_repository']);
340
	$extra_config .= ' '.escapeshellarg('auth_type='.(empty($config['auth_type']) ? $config['account_repository'] : $config['auth_type']));
341
	if (empty($rest)) unset($config['account-auth']);
342
	if (array_intersect(array($config['account_repository'], $config['auth_type']), array('ldap', 'univention')))
343
	{
344
		// set account_min_id to 1100 if not specified to NOT clash with system accounts
345
		$extra_config .= ' '.escapeshellarg('account_min_id='.(!empty($config['account_min_id']) ? $config['account_min_id'] : 1100));
346
347
		$setup_ldap = $setup_cli.' --setup-cmd-ldap sub_command='.
348
			($config['account_repository'] == 'ldap' ? 'create_ldap' : 'test_ldap');
349
		foreach(array(
350
			'domain','ldap_suffix','ldap_host','ldap_admin','ldap_admin_pw',	// non-egw params: only used for create
351
			'ldap_base','ldap_root_dn','ldap_root_pw','ldap_context','ldap_search_filter','ldap_group_context',	// egw params
352
			'ldap_encryption_type', 'sambaadmin/sambasid',
353
		) as $name)
354
		{
355
			if (strpos($value=$config[$name],'$') !== false)
356
			{
357
				$config[$name] = $value = strtr($value,array(
358
					'$suffix' => $config['ldap_suffix'],
359
					'$base' => $config['ldap_base'],
360
					'$admin_pw' => $config['ldap_admin_pw'],
361
				));
362
			}
363
			$setup_ldap .= ' '.escapeshellarg($name.'='.$value);
364
365
			if (!in_array($name,array('domain','ldap_suffix','ldap_admin','ldap_admin_pw')))
366
			{
367
				$extra_config .= ' '.escapeshellarg($name.'='.$value);
368
			}
369
		}
370
		run_cmd($setup_ldap);
371
	}
372
	// enable mcrypt extension eg. for Ubuntu 14.04+
373
	if (!empty($config['php5enmod']))
374
	{
375
		run_cmd($config['php5enmod']);
376
	}
377
378
	// create or edit header header
379
	$setup_header = $setup_cli.(isset($GLOBALS['egw_domain']) ? ' --edit-header ' : ' --create-header ').
380
		escapeshellarg($config['config_passwd'].','.$config['config_user']).
381
		' --domain '.escapeshellarg($config['domain'].','.$config['db_name'].','.$config['db_user'].','.$config['db_pass'].
382
			','.$config['db_type'].','.$config['db_host'].','.$config['db_port']);
383
	run_cmd($setup_header);
384
	// fix permissions of header.inc.php
385
	chown($config['header'], $config['webserver_user']);
386
	chmod($config['header'], 0600);
387
388
	// install egroupware
389
	$setup_install = $setup_cli.' --install '.escapeshellarg($config['domain'].','.$config['config_user'].','.$config['config_passwd'].','.$config['backup'].','.$config['charset'].','.$config['lang'])
390
		.$extra_config;
391
	run_cmd($setup_install);
392
393
	if ($config['data_dir'] != '/var/lib/egroupware')
394
	{
395
		// set files dir different from default
396
		$setup_config = $setup_cli.' --config '.escapeshellarg($config['domain'].','.$config['config_user'].','.$config['config_passwd']).
397
			' --files-dir '.escapeshellarg($config['data_dir'].'/files').' --backup-dir '.escapeshellarg($config['data_dir'].'/backup');
398
		run_cmd($setup_config);
399
	}
400
	// create mailserver config (fmail requires at least minimal config given as default, otherwise fatal error)
401
	$setup_mailserver = $setup_cli.' --config '.escapeshellarg($config['domain'].','.$config['config_user'].','.$config['config_passwd']);
402
	foreach(array('account-auth','smtpserver','smtp','postfix','mailserver','imap','cyrus','sieve','folder') as $name)
403
	{
404
		if (!empty($config[$name])) $setup_mailserver .= ' --'.$name.' '.escapeshellarg($config[$name]);
405
	}
406
	run_cmd($setup_mailserver);
407
408
	// create first user
409
	$setup_admin = $setup_cli.' --admin '.escapeshellarg($config['domain'].','.$config['config_user'].','.$config['config_passwd'].','.
410
		$config['admin_user'].','.$config['admin_passwd'].',,,'.$config['admin_email'].','.$config['lang']);
411
	run_cmd($setup_admin);
412
413
	// check if webserver is started and start it (permanent) if not
414
	if ($config['start_webserver'])
415
	{
416
		exec(build_cmd('start_webserver', 'status'),$dummy,$ret);
417
		if ($ret)
418
		{
419
			system(build_cmd('start_webserver', 'start'));
420
			if (!empty($config['autostart_webserver'])) system($config['autostart_webserver']);
421
		}
422
		else
423
		{
424
			system(build_cmd('start_webserver', 'reload'));
425
		}
426
	}
427
	// fix egw_cache evtl. created by root, stoping webserver from accessing it
428
	fix_perms();
429
430
	echo "\n";
431
	echo "EGroupware successful installed\n";
432
	echo "===============================\n";
433
	echo "\n";
434
	echo "Please note the following user names and passwords:\n";
435
	echo "\n";
436
	echo "Setup username:      $config[config_user]\n";
437
	echo "      password:      $config[config_passwd]\n";
438
	echo "\n";
439
	echo "EGroupware username: $config[admin_user]\n";
440
	echo "           password: $config[admin_passwd]\n";
441
	echo "\n";
442
	echo "You can log into EGroupware by pointing your browser to http://localhost/egroupware/\n";
443
	echo "Please replace localhost with the appropriate hostname, if you connect remote.\n\n";
444
445
	if (empty($config['db_root_pw']))
446
	{
447
		echo "*** Database has no root password set, please fix that immediatly".
448
			(substr($config['db_type'], 0, 5) === 'mysql' ? ": mysqladmin -u root password NEWPASSWORD\n\n" : "!\n\n");
449
	}
450
}
451
else
452
{
453
	// --> existing install --> update
454
455
	// update egroupware, or single app(s), in later case skip backup
456
	$setup_update = $setup_cli.' --update '.escapeshellarg('all,'.$config['config_user'].','.$config['config_passwd'].
457
		(empty($config['install-update-app']) ? '' : ',no,'.$config['install-update-app']));
458
	$ret = run_cmd($setup_update,$output,array(4,15));
0 ignored issues
show
Unused Code introduced by
The call to run_cmd() has too many arguments starting with array(4, 15). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

458
	$ret = /** @scrutinizer ignore-call */ run_cmd($setup_update,$output,array(4,15));

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
459
460
	switch($ret)
461
	{
462
		case 4:		// header needs an update
463
			$header_update = $setup_cli.' --update-header '.escapeshellarg($config['config_passwd'].','.$config['config_user']);
464
			run_cmd($header_update);
465
			$ret = run_cmd($setup_update,$output,15);
466
			if ($ret != 15) break;
467
			// fall through
468
		case 15:	// missing configuration (eg. mailserver)
469
			if (!$verbose) echo implode("\n",(array)$output)."\n";
470
			break;
471
472
		case 0:
473
			echo "\nEGroupware successful updated\n";
474
			break;
475
	}
476
	// fix egw_cache evtl. created by root, stoping webserver from accessing it
477
	fix_perms();
478
479
	if (!empty($config['start_webserver']))
480
	{
481
		// restart running Apache, to force APC to update changed sources and/or Apache configuration
482
		$output = array();
483
		run_cmd(build_cmd('start_webserver', 'status').' && '.build_cmd('start_webserver', 'restart'), $output, true);
484
	}
485
	exit($ret);
486
}
487
488
/**
489
 * Build command to execute
490
 *
491
 * @param string $cmd command or index into $config, which either incl. %s for arg or arg with be appended
492
 * @param string $arg argument
493
 * @return string
494
 */
495
function build_cmd($cmd, $arg)
496
{
497
	global $config;
498
499
	if (isset($config[$cmd])) $cmd = $config[$cmd];
500
501
	if (strpos($cmd, '%s')) return str_replace('%s', $arg, $cmd);
502
503
	return $cmd.' '.$arg;
504
}
505
506
/**
507
 * Patches a given password (for header admin) into the EGroupware header.inc.php and returns the old one
508
 *
509
 * @param string $filename
510
 * @param string &$user username on return(!)
511
 * @param string $password new password
512
 * @return string old password
513
 */
514
function patch_header($filename,&$user,$password)
515
{
516
	$header = file_get_contents($filename);
517
518
	$umatches = $pmatches = null;
519
	if (!preg_match('/'.preg_quote("\$GLOBALS['egw_info']['server']['header_admin_user'] = '", '/')."([^']+)';/m",$header,$umatches) ||
520
		!preg_match('/'.preg_quote("\$GLOBALS['egw_info']['server']['header_admin_password'] = '", '/')."([^']*)';/m",$header,$pmatches))
521
	{
522
		bail_out(99,"$filename is no regular EGroupware header.inc.php!");
523
	}
524
	//error_log(__FUNCTION__."('$filename', '$user', '$password') header_admin_password was '$pmatches[1]'");
525
526
	file_put_contents($filename,$header_new=preg_replace('/'.preg_quote("\$GLOBALS['egw_info']['server']['header_admin_password'] = '", '/')."([^']*)';/m",
527
		// $ in password needs to be escaped as they otherwise address captures in the regualar expression
528
		"\$GLOBALS['egw_info']['server']['header_admin_password'] = '".str_replace('$', '\\$', $password)."';",$header));
529
530
	/*$xmatches = null;
531
	preg_match('/'.preg_quote("\$GLOBALS['egw_info']['server']['header_admin_password'] = '", '/')."([^']*)';/m",$header_new,$xmatches);
532
	error_log(__FUNCTION__."('$filename', '$user', '$password') header_admin_password now '$xmatches[1]' returning '$pmatches[1]'");*/
533
534
	$user = $umatches[1];
535
536
	// try fixing crypt passwords broken by unescaped replace
537
	return str_replace('{crypt}a$', '{crypt}$2a$12$', $pmatches[1]);
538
}
539
540
/**
541
 * Runs given shell command, exists with error-code after echoing the output of the failed command (if not already running verbose)
542
 *
543
 * @param string $cmd
544
 * @param array &$output=null $output of command
545
 * @param int|array|true $no_bailout =null exit code(s) to NOT bail out, or true to never bail out
546
 * @return int exit code of $cmd
547
 */
548
function run_cmd($cmd,array &$output=null,$no_bailout=null)
549
{
550
	global $verbose;
551
552
	if ($verbose)
553
	{
554
		echo $cmd."\n";
555
		$ret = null;
556
		system($cmd,$ret);
557
	}
558
	else
559
	{
560
		$output[] = $cmd;
561
		exec($cmd,$output,$ret);
562
	}
563
	if ($ret && $no_bailout !== true && !in_array($ret,(array)$no_bailout))
564
	{
565
		bail_out($ret,$verbose?null:$output);
566
	}
567
	return $ret;
568
}
569
570
/**
571
 * Stop programm execution with a given exit code and optional extra message
572
 *
573
 * @param int $ret =1
574
 * @param array|string $output line(s) to output before temination notice
575
 */
576
function bail_out($ret=1,$output=null)
577
{
578
	if ($output) echo implode("\n",(array)$output);
579
	echo "\n\nInstallation failed --> exiting!\n\n";
580
	exit($ret);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
581
}
582
583
/**
584
 * Return a rand string, eg. to generate passwords
585
 *
586
 * @param int $len =16
587
 * @return string
588
 */
589
function randomstring($len=16)
590
{
591
	static $usedchars = array(
592
		'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f',
593
		'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
594
		'w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L',
595
		'M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
596
		'@','!','&','(',')','=','?',';',':','#','_','-','<',
597
		'>','|','[',']','}',	// dont add %, /\,'"{ as we have problems dealing with them
598
	);
599
600
	// use cryptographically secure random_int available in PHP 7+
601
	$func = function_exists('random_int') ? 'random_int' : 'mt_rand';
602
603
	$str = '';
604
	for($i=0; $i < $len; $i++)
605
	{
606
		$str .= $usedchars[$func(0,count($usedchars)-1)];
607
	}
608
	return $str;
609
}
610
611
/**
612
 * Give usage information and an optional error-message, before stoping program execution with exit-code 90 or 0
613
 *
614
 * @param string $error =null optional error-message
615
 */
616
function usage($error=null)
617
{
618
	global $prog,$config;
619
620
	echo "Usage: $prog [-h|--help] [-v|--verbose] [--distro=(suse|rh|debian)] [options, ...]\n\n";
621
	echo "options and their defaults:\n";
622
	foreach($config as $name => $default)
623
	{
624
		if (in_array($name, array('postfix','cyrus'))) continue;	// do NOT report deprecated options
625
		if (in_array($name,array('config_passwd','db_pass','admin_passwd','ldap_root_pw')) && strlen($config[$name]) == 16)
626
		{
627
			$default = '<16 char random string>';
628
		}
629
		echo '--'.str_pad($name,20).$default."\n";
630
	}
631
	if ($error)
632
	{
633
		echo "$error\n\n";
634
		exit(90);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
635
	}
636
	exit(0);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
637
}
638
639
/**
640
 * fix egw_cache and files_dir perms evtl. created by root, stoping webserver from accessing it
641
 */
642
function fix_perms()
643
{
644
	global $config;
645
646
	// chown only works as root (uid=0)
647
	if (function_exists('posix_getuid') && posix_geteuid()) return;
648
649
	if (file_exists('/tmp/egw_cache') && !empty($config['webserver_user']))
650
	{
651
		system('/bin/chown -R '.$config['webserver_user'].' /tmp/egw_cache');
652
		system('/bin/chmod 700 /tmp/egw_cache');
653
	}
654
	// in case update changes something in filesystem
655
	if (file_exists($config['data_dir']) && !empty($config['webserver_user']))
656
	{
657
		@system('/bin/chown -R '.$config['webserver_user'].' '.$config['data_dir'].'/*/files/sqlfs');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for system(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

657
		/** @scrutinizer ignore-unhandled */ @system('/bin/chown -R '.$config['webserver_user'].' '.$config['data_dir'].'/*/files/sqlfs');

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
658
	}
659
}
660
661
/**
662
 * Set Univention UCS specific defaults
663
 *
664
 * Defaults are read from ucr registry and /etc/*.secret files
665
 *
666
 * There are 4 types of Univention servers:
667
 * - master DC: /etc/machine.secret, /etc/ldap.secret, ldap/server/type=master
668
 * - backup DC: /etc/machine.secret, /etc/ldap.secret, /etc/ldap-backup.secret, ldap/server/type=slave (not backup!)
669
 * - slave:     /etc/machine.secret, /etc/ldap-backup.secret, ldap/server/type=slave
670
 * - member:    /etc/machine.secret, no ldap/server/type
671
 *
672
 * univention-ldapsearch works on all 4 types.
673
 *
674
 * ucr get ldap/server/(ip|port) points to local ldap (not member).
675
 * ucr get ldap/master(/port) ldap/base points to master (on all servers)
676
 *
677
 * @todo slave and member have no /etc/ldap.secret
678
 */
679
function set_univention_defaults()
680
{
681
	global $config;
682
683
	set_distro_defaults('debian');
684
	$config['distro'] = 'univention';
685
686
	// set lang from ucr locale, as cloud-config at least never has anything but EN set in enviroment
687
	@list($lang,$nat) = preg_split('/[_.]/', _ucr_get('locale/default'));
688
	if (in_array($lang.'-'.strtolower($nat),array('es-es','pt-br','zh-tw')))
689
	{
690
		$lang .= '-'.strtolower($nat);
691
	}
692
	$config['lang'] = $lang;
693
694
	// mysql settings
695
	$config['db_root_pw'] = _ucr_secret('mysql');
696
697
	// check if ucs ldap server is configured
698
	if (_ucr_get('ldap/server/ip'))
699
	{
700
		// ldap settings, see http://docs.univention.de/developer-reference.html#join:secret
701
		$config['ldap_suffix'] = $config['ldap_base'] = _ucr_get('ldap/base');
702
		// port is ldap allowing starttls (zertificate/CA is correctly set in /etc/ldap/ldap.conf)
703
		$config['ldap_host'] = 'tls://'._ucr_get('ldap/master').':'._ucr_get('ldap/master/port');
704
		$config['ldap_admin'] = $config['ldap_root'] = 'cn=admin,$suffix';
705
		$config['ldap_admin_pw'] = $config['ldap_root_pw'] = _ucr_secret('ldap');
706
		$config['ldap_context'] = 'cn=users,$base';
707
		$config['ldap_group_context'] = 'cn=groups,$base';
708
		$config['ldap_search_filter'] = '(uid=%user)';
709
710
		// ldap password hash (our default blowfish_crypt seems not to work)
711
		$config['ldap_encryption_type'] = 'sha512_crypt';
712
713
		$config['account_min_id'] = 1200;	// UCS use 11xx for internal users/groups
714
715
		$config['account-auth'] = 'univention,univention';
716
717
		// set sambaadmin sambaSID
718
		$config['sambaadmin/sambasid'] = exec('/usr/bin/univention-ldapsearch -x "(objectclass=sambadomain)" sambaSID|sed -n "s/sambaSID: \(.*\)/\1/p"');
719
720
		// mailserver, see setup-cli.php --help config
721
		if (($mailserver = exec('/usr/bin/univention-ldapsearch -x "(univentionAppID=mailserver_*)" univentionAppInstalledOnServer|sed -n "s/univentionAppInstalledOnServer: \(.*\)/\1/p"')) &&
722
			// only set on host mailserver app is installed: _ucr_get('mail/cyrus/imap') == 'yes' &&
723
			($domains=_ucr_get('mail/hosteddomains')))
724
		{
725
			if (!is_array($domains)) $domains = explode("\n", $domains);
0 ignored issues
show
introduced by
The condition is_array($domains) is always false.
Loading history...
726
			$domain = array_shift($domains);
727
			// set "use auth with session credentials",tls,"not user editable","further identities"
728
			$config['smtpserver'] = "$mailserver,465,,,yes,tls,no,yes";
729
			$config['smtp'] = ',Smtp\\Univention';
730
			$config['mailserver'] = "$mailserver,993,$domain,email,tls";
731
			if (_ucr_get('mail/dovecot') == 'yes')
732
			{
733
				$matches = null;
734
				if (file_exists('/etc/dovecot/master-users') &&
735
					preg_match('/^([^:]+):{PLAIN}([^:]+):/i', file_get_contents('/etc/dovecot/master-users'), $matches))
736
				{
737
					$config['imap'] = $matches[1].','.$matches[2].',Imap\\Dovecot';
738
				}
739
				else
740
				{
741
					$config['imap'] = ',,Imap\\Dovecot';
742
				}
743
				// default with sieve port to 4190, as config is only available on host mailserver app is installed
744
				if (!($sieve_port = _ucr_get('mail/dovecot/sieve/port'))) $sieve_port = 4190;
745
			}
746
			else
747
			{
748
				$config['imap'] = /*'cyrus,'._ucr_secret('cyrus')*/','.',Imap\\Cyrus';
749
				// default with sieve port to 4190, as config is only available on host mailserver app is installed
750
				if (!($sieve_port = _ucr_get('mail/cyrus/sieve/port'))) $sieve_port = 4190;
751
			}
752
			// set folders so mail creates them on first login, UCS does not automatic
753
			$config['folder'] = 'INBOX/Sent,INBOX/Trash,INBOX/Drafts,INBOX/Templates,Spam,,Ham';
754
			$config['sieve'] = "$mailserver,$sieve_port,starttls";
755
			// set an email address for sysop user so mail works right away
756
			$config['admin_email'] = '$admin_user@'.$domain;
757
		}
758
		$config['apache_config'] = '/etc/egroupware/apache-univention.conf';
759
	}
760
}
761
762
/**
763
 * Get a value from Univention registry
764
 *
765
 * @param string $name
766
 * @return string
767
 */
768
function _ucr_get($name)
769
{
770
	static $values=null;
771
	if (!isset($values))
772
	{
773
		$output = $matches = null;
774
		exec('/usr/sbin/ucr dump', $output);
775
		foreach($output as $line)
776
		{
777
			if (preg_match("/^([^:]+): (.*)\n?$/", $line, $matches))
778
			{
779
				$values[$matches[1]] = $matches[2];
780
			}
781
		}
782
	}
783
	return $values[$name];
784
}
785
786
/**
787
 * Read one Univention secret/password eg. _ucr_secret('mysql')
788
 *
789
 * @param string $name
790
 * @return string|boolean
791
 */
792
function _ucr_secret($name)
793
{
794
	if (!file_exists($filename = '/etc/'.basename($name).'.secret'))
795
	{
796
		return false;
797
	}
798
	return trim(file_get_contents($filename));
799
}
800
801
/**
802
 * Check and evtl. fix APC(u) shared memory size (apc.shm_segments * apc.shm_size) >= 64M
803
 *
804
 * We check for < 64M to allow to use that for small installs manually, but set 128M by default.
805
 */
806
function check_fix_php_apc_ini()
807
{
808
	if (extension_loaded('apc') || extension_loaded('apcu'))
809
	{
810
		$shm_size = ini_get('apc.shm_size');
811
		$shm_segments = ini_get('apc.shm_segments');
812
		// ancent APC (3.1.3) in Debian 6/Squezze has size in MB without a unit
813
		if (($numeric_size = is_numeric($shm_size) && $shm_size <= 1048576)) $shm_size .= 'M';
814
815
		$size = _size_with_unit($shm_size) * $shm_segments;
816
		//echo "shm_size=$shm_size, shm_segments=$shm_segments --> $size, numeric_size=$numeric_size\n";
817
818
		// check if we have less then 64MB (eg. default 32M) --> set it to 128MB
819
		if ($size < _size_with_unit('64M'))
820
		{
821
			ob_start();
822
			phpinfo();
823
			$phpinfo = ob_get_clean();
824
			$matches = null;
825
			if (preg_match('#(/[a-z0-9./-]+apcu?.ini)(,| |$)#mi', $phpinfo, $matches) &&
826
				file_exists($path = $matches[1]) && ($apc_ini = file_get_contents($path)))
827
			{
828
				$new_shm_size = 128 / $shm_segments;
829
				if (!$numeric_size) $new_shm_size .= 'M';
830
				if (preg_match('|^apc.shm_size\s*=\s*(\d+[KMG]?)$|m', $apc_ini))
831
				{
832
					file_put_contents($path, preg_replace('|^apc.shm_size\s*=\s*(\d+[KMG]?)$|m', 'apc.shm_size='.$new_shm_size, $apc_ini));
833
				}
834
				else
835
				{
836
					file_put_contents($path, $apc_ini."\napc.shm_size=$new_shm_size\n");
837
				}
838
				echo "Fix APC(u) configuration, set apc.shm_size=$new_shm_size in $path\n";
839
			}
840
		}
841
	}
842
}
843
844
/**
845
 * Check if CA certificates are added to open_basedir to be accessible
846
 *
847
 * Different distros use different CA directories:
848
 * - Debian/Ubuntu: /usr/lib/ssl/certs with files symlinked from /usr/share/ca-certificates
849
 * - RHEL/CentOS: /etc/pki/tls/certs with files symlinks from /etc/pki/ca-trust
850
 * - openSUSE/SLES: /var/lib/ca-certificates/openssl
851
 */
852
function check_fix_open_basedir_certs()
853
{
854
	global $config;
855
856
	if (extension_loaded('openssl') && function_exists('openssl_get_cert_locations') &&
857
		($locations = openssl_get_cert_locations()) &&
858
		file_exists($default_cert_dir = $locations['default_cert_dir']))
859
	{
860
		$check_dirs = array($default_cert_dir);
861
		foreach(scandir($default_cert_dir) as $cert)
862
		{
863
			$cert = $default_cert_dir.'/'.$cert;
864
			if (is_link($cert) && ($link = readlink($cert)) &&
865
				dirname($link) != '.' && !in_array(dirname($link), $check_dirs))
866
			{
867
				$check_dirs[] = dirname($link);
868
			}
869
		}
870
		//echo "Checking if OpenSSL CA dirs are included in open_basedir: ".implode(', ', $check_dirs)."\n";
871
		$matches = null;
872
		if (($content = file_get_contents($config['apache_config'])) &&
873
			preg_match('/^\s*php_admin_value\s+open_basedir\s+(.*)$/m', $content, $matches))
874
		{
875
			//echo "$config[apache_config] contains open_basedir $matches[1]\n";
876
			$open_basedirs = explode(':', $matches[1]);
877
			$need_adding = array();
878
			foreach($check_dirs as $dir)
879
			{
880
				if (!in_array($dir, $open_basedirs)) $need_adding[] = $dir;
881
			}
882
			if ($need_adding)
883
			{
884
				$content = preg_replace('/^\s*php_admin_value\s+open_basedir\s+(.*)$/m',
885
					'\\0:'.implode(':', $need_adding), $content);
886
				if (file_put_contents($config['apache_config'], $content))
887
				{
888
					echo "Added OpenSSL CA directories ".implode(', ', $need_adding)." to Apache config $config[apache_config].\n";
889
				}
890
				else
891
				{
892
					echo "Failed to add OpenSSL CA directories ".implode(', ', $need_adding)." to Apache config $config[apache_config]!\n";
893
				}
894
			}
895
		}
896
	}
897
}
898
899
/**
900
 * Convert a size with unit eg. 32M to a number
901
 * @param int|string $_size
902
 * @return int
903
 */
904
function _size_with_unit($_size)
905
{
906
	$size = (int)$_size;
907
	switch(strtoupper(substr($_size, -1)))
908
	{
909
		case 'G':
910
			$size *= 1024;
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
911
		case 'M':
912
			$size *= 1024;
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
913
		case 'K':
914
			$size *= 1024;
915
	}
916
	return $size;
917
}
918