Completed
Push — master ( 9d38bf...1f84af )
by Ralf
125:16 queued 103:35
created

setup/inc/class.setup.inc.php (17 issues)

1
<?php
2
/**
3
 * Setup
4
 *
5
 * @link http://www.egroupware.org
6
 * @package setup
7
 * @author Joseph Engo <[email protected]>
8
 * @author Dan Kuykendall <[email protected]>
9
 * @author Mark Peters <[email protected]>
10
 * @author Miles Lott <[email protected]>
11
 * @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
12
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
13
 */
14
15
use EGroupware\Api;
16
use EGroupware\Api\Link;
17
use EGroupware\Api\Egw;
18
use EGroupware\Api\Vfs;
19
20
class setup
21
{
22
	var $db;
23
	var $config_table       = 'egw_config';
24
	var $applications_table = 'egw_applications';
25
	var $acl_table          = 'egw_acl';
26
	var $accounts_table     = 'egw_accounts';
27
	var $prefs_table        = 'egw_preferences';
28
	var $lang_table         = 'egw_lang';
29
	var $languages_table    = 'egw_languages';
30
	var $hooks_table        = 'egw_hooks';
31
	var $cats_table         = 'egw_categories';
32
	var $oProc;
33
	var $cookie_domain;
34
35
	/**
36
	 * @var setup_detection
37
	 */
38
	var $detection;
39
	/**
40
	 * @var setup_process
41
	 */
42
	var $process;
43
	/**
44
	 * @var setup_translation
45
	 */
46
	var $translation;
47
	/**
48
	 * @var setup_html
49
	 */
50
	var $html;
51
52
	var $system_charset;
53
	var $lang;
54
55
	var $ConfigDomain;
56
57
	/* table name vars */
58
	var $tbl_apps;
59
	var $tbl_config;
60
	var $tbl_hooks;
61
62
	/**
63
	 * php version required by eGroupware
64
	 *
65
	 * @var float
66
	 */
67
	var $required_php_version = 7.1;
68
	/**
69
	 * PHP Version recommended for eGroupware
70
	 *
71
	 * @var string
72
	 */
73
	var $recommended_php_version = '7.3';
74
75
	function __construct($html=False, $translation=False)
76
	{
77
		// setup us as $GLOBALS['egw_setup'], as this gets used in our sub-objects
78
		$GLOBALS['egw_setup'] =& $this;
79
80
		if (!is_object($GLOBALS['egw']))
81
		{
82
			$GLOBALS['phpgw'] = $GLOBALS['egw'] = new Egw\Base();
83
		}
84
		$this->detection = new setup_detection();
85
		$this->process   = new setup_process();
86
87
		if (preg_match('/^[a-z0-9-]+$/i', $_REQUEST['system_charset'])) $this->system_charset = $_REQUEST['system_charset'];
88
89
		/* The setup application needs these */
90
		if ($html) $this->html = new setup_html();
91
		if ($translation) $this->translation = new setup_translation();
92
	}
93
94
	/**
95
	 * include api db class for the ConfigDomain and connect to the db
96
	 */
97
	function loaddb($connect_and_setcharset=true)
98
	{
99
		if(!isset($this->ConfigDomain) || empty($this->ConfigDomain))
100
		{
101
			$this->ConfigDomain = isset($_REQUEST['ConfigDomain']) ? $_REQUEST['ConfigDomain'] : $_POST['FormDomain'];
102
		}
103
		$GLOBALS['egw_info']['server']['db_type'] = $GLOBALS['egw_domain'][$this->ConfigDomain]['db_type'];
104
105
		if ($GLOBALS['egw_info']['server']['db_type'] == 'pgsql')
106
		{
107
			$GLOBALS['egw_info']['server']['db_persistent'] = False;
108
		}
109
110
		try {
111
			$GLOBALS['egw']->db = $this->db = new Api\Db\Deprecated($GLOBALS['egw_domain'][$this->ConfigDomain]);
112
			$this->db->connect();
113
		}
114
		catch (Exception $e) {
115
			unset($e);
116
			return;
117
		}
118
		$this->db->set_app(Api\Db::API_APPNAME);
119
120
		if ($connect_and_setcharset)
121
		{
122
			try {
123
				$this->set_table_names();		// sets/checks config- and applications-table-name
124
125
				// Set the DB's client charset if a system-charset is set
126
				if (($this->system_charset = $this->db->select($this->config_table,'config_value',array(
127
						'config_app'  => 'phpgwapi',
128
						'config_name' => 'system_charset',
129
					),__LINE__,__FILE__)->fetchColumn()))
130
				{
131
					$this->db_charset_was = $this->db->Link_ID->GetCharSet();	// needed for the update
132
133
					// we can NOT set the DB charset for mysql, if the api version < 1.0.1.019, as it would mess up the DB content!!!
134
					if (substr($this->db->Type,0,5) == 'mysql')	// we need to check the api version
135
					{
136
						$api_version = $this->db->select($this->applications_table,'app_version',array(
137
								'app_name'  => 'phpgwapi',
138
							),__LINE__,__FILE__)->fetchColumn();
139
					}
140
					if (!$api_version || !$this->alessthanb($api_version,'1.0.1.019'))
141
					{
142
						$this->db->Link_ID->SetCharSet($this->system_charset);
143
					}
144
				}
145
			}
146
			catch (Api\Db\Exception $e) {
147
				// table might not be created at that stage
148
			}
149
		}
150
	}
151
152
	/**
153
	* Set the domain used for cookies
154
	*
155
	* @return string domain
156
	*/
157
	static function cookiedomain()
158
	{
159
		// Use HTTP_X_FORWARDED_HOST if set, which is the case behind a none-transparent proxy
160
		$cookie_domain = Api\Header\Http::host();
161
162
		// remove port from HTTP_HOST
163
		$arr = null;
164
		if (preg_match("/^(.*):(.*)$/",$cookie_domain,$arr))
165
		{
166
			$cookie_domain = $arr[1];
167
		}
168
		if (count(explode('.',$cookie_domain)) <= 1)
169
		{
170
			// setcookie dont likes domains without dots, leaving it empty, gets setcookie to fill the domain in
171
			$cookie_domain = '';
172
		}
173
		return $cookie_domain;
174
	}
175
176
	/**
177
	* Set a cookie
178
	*
179
	* @param string $cookiename name of cookie to be set
180
	* @param string $cookievalue value to be used, if unset cookie is cleared (optional)
181
	* @param int $cookietime when cookie should expire, 0 for session only (optional)
182
	*/
183
	function set_cookie($cookiename,$cookievalue='',$cookietime=0)
184
	{
185
		if(!isset($this->cookie_domain))
186
		{
187
			$this->cookie_domain = self::cookiedomain();
188
		}
189
		setcookie($cookiename, $cookievalue, $cookietime, '/', $this->cookie_domain,
190
			// if called via HTTPS, only send cookie for https and only allow cookie access via HTTP (true)
191
			Api\Header\Http::schema() === 'https', true);
192
	}
193
194
	/**
195
	 * Get configuration language from $_POST or $_SESSION and validate it
196
	 *
197
	 * @param boolean $use_accept_language =false true: use Accept-Language header as fallback
198
	 * @return string
199
	 */
200
	static function get_lang($use_accept_language=false)
201
	{
202
		if (isset($_POST['ConfigLang']))
203
		{
204
			$ConfigLang = $_POST['ConfigLang'];
205
		}
206
		else
207
		{
208
			if (!isset($_SESSION)) self::session_start();
209
			$ConfigLang = $_SESSION['ConfigLang'];
210
		}
211
		$matches = null;
212
		if ($use_accept_language && empty($ConfigLang) &&
213
			preg_match_all('/(^|,)([a-z-]+)/', strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE']), $matches))
214
		{
215
			$available = Api\Translation::get_available_langs(false);
216
			foreach($matches[2] as $lang)
217
			{
218
				if (isset($available[$lang]))
219
				{
220
					$ConfigLang = $lang;
221
					break;
222
				}
223
			}
224
		}
225
		if (!preg_match('/^[a-z]{2}(-[a-z]{2})?$/',$ConfigLang))
226
		{
227
			$ConfigLang = null;	// not returning 'en', as it suppresses the language selection in check_install and manageheader
228
		}
229
		//error_log(__METHOD__."() \$_POST['ConfigLang']=".array2string($_POST['ConfigLang']).", \$_SESSION['ConfigLang']=".array2string($_SESSION['ConfigLang']).", HTTP_ACCEPT_LANGUAGE=$_SERVER[HTTP_ACCEPT_LANGUAGE] --> returning ".array2string($ConfigLang));
230
		return $ConfigLang;
231
	}
232
233
	/**
234
	 * Name of session cookie
235
	 */
236
	const SESSIONID = 'sessionid';
237
238
	/**
239
	 * Session timeout in secs (1200 = 20min)
240
	 */
241
	const TIMEOUT = 1200;
242
243
	/**
244
	 * Start session
245
	 */
246
	private static function session_start()
247
	{
248
		// make sure we have session extension available, otherwise fail with exception that we need it
249
		check_load_extension('session', true);
250
251
		switch(session_status())
252
		{
253
			case PHP_SESSION_DISABLED:
254
				throw new \ErrorException('EGroupware requires PHP session extension!');
255
			case PHP_SESSION_NONE:
256
				if (headers_sent()) return false;
257
				ini_set('session.use_cookie', true);
258
				session_name(self::SESSIONID);
259
				session_set_cookie_params(0, '/', self::cookiedomain(),
260
					// if called via HTTPS, only send cookie for https and only allow cookie access via HTTP (true)
261
					Api\Header\Http::schema() === 'https', true);
262
263
				if (isset($_COOKIE[self::SESSIONID])) session_id($_COOKIE[self::SESSIONID]);
264
265
				$ok = @session_start();	// suppress notice if session already started or warning in CLI
266
				break;
267
			case PHP_SESSION_ACTIVE:
268
				$ok = true;
269
		}
270
		// need to decrypt session, in case session encryption is switched on in header.inc.php
271
		Api\Session::decrypt();
272
		//error_log(__METHOD__."() returning ".array2string($ok).' _SESSION='.array2string($_SESSION));
273
		return $ok;
274
	}
275
276
	/**
277
	 * authenticate the setup user
278
	 *
279
	 * @param string $_auth_type ='config' 'config' or 'header' (caseinsensitiv)
280
	 */
281
	function auth($_auth_type='config')
282
	{
283
		$auth_type = strtolower($_auth_type);
284
		$GLOBALS['egw_info']['setup']['HeaderLoginMSG'] = $GLOBALS['egw_info']['setup']['ConfigLoginMSG'] = '';
285
286
		if(!$this->checkip(isset($_SERVER['HTTP_X_FORWARDED_FOR']) ?
287
			$_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR']))
288
		{
289
			//error_log(__METHOD__."('$auth_type') invalid IP");
290
			return false;
291
		}
292
293
		if (!self::session_start() || isset($_REQUEST['FormLogout']) ||
294
			empty($_SESSION['egw_setup_auth_type']) || $_SESSION['egw_setup_auth_type'] != $auth_type)
295
		{
296
			//error_log(__METHOD__."('$auth_type') \$_COOKIE['".self::SESSIONID."'] = ".array2string($_COOKIE[self::SESSIONID]).", \$_SESSION=".array2string($_SESSION).", \$_POST=".array2string($_POST));
297
			if (isset($_REQUEST['FormLogout']))
298
			{
299
				$this->set_cookie(self::SESSIONID, '', time()-86400);
300
				session_destroy();
301
				if ($_REQUEST['FormLogout'] == 'config')
302
				{
303
					$GLOBALS['egw_info']['setup']['ConfigLoginMSG'] = lang('You have successfully logged out');
304
				}
305
				else
306
				{
307
					$GLOBALS['egw_info']['setup']['HeaderLoginMSG'] = lang('You have successfully logged out');
308
				}
309
				return false;
310
			}
311
			if (!isset($_POST['FormUser']) || !isset($_POST['FormPW']))
312
			{
313
				return false;
314
			}
315
			switch($auth_type)
316
			{
317
				case 'config':
318
					if (!isset($GLOBALS['egw_domain'][$_POST['FormDomain']]) ||
319
						!$this->check_auth($_POST['FormUser'], $_POST['FormPW'],
320
							$GLOBALS['egw_domain'][$_POST['FormDomain']]['config_user'],
321
							$GLOBALS['egw_domain'][$_POST['FormDomain']]['config_passwd']) &&
322
						!$this->check_auth($_POST['FormUser'], $_POST['FormPW'],
323
							$GLOBALS['egw_info']['server']['header_admin_user'],
324
							$GLOBALS['egw_info']['server']['header_admin_password']))
325
					{
326
						//error_log(__METHOD__."() Invalid password: $_POST[FormDomain]: used $_POST[FormUser]/md5($_POST[FormPW])=".md5($_POST['FormPW'])." != {$GLOBALS['egw_info']['server']['header_admin_user']}/{$GLOBALS['egw_info']['server']['header_admin_password']}");
327
						$GLOBALS['egw_info']['setup']['ConfigLoginMSG'] = lang('Invalid password');
328
						return false;
329
					}
330
					$this->ConfigDomain = $_SESSION['egw_setup_domain'] = $_POST['FormDomain'];
331
					break;
332
333
				default:
334
				case 'header':
335
					if (!$this->check_auth($_POST['FormUser'], $_POST['FormPW'],
336
								$GLOBALS['egw_info']['server']['header_admin_user'],
337
								$GLOBALS['egw_info']['server']['header_admin_password']))
338
					{
339
						$GLOBALS['egw_info']['setup']['ConfigLoginMSG'] = lang('Invalid password');
340
						return false;
341
					}
342
					break;
343
			}
344
			$_SESSION['egw_setup_auth_type'] = $auth_type;
345
			$_SESSION['ConfigLang'] = self::get_lang();
346
			$_SESSION['egw_last_action_time'] = time();
347
			session_regenerate_id(true);
348
			$this->set_cookie(self::SESSIONID, session_id(), 0);
349
350
			return true;
351
		}
352
		//error_log(__METHOD__."('$auth_type') \$_COOKIE['".self::SESSIONID."'] = ".array2string($_COOKIE[self::SESSIONID]).", \$_SESSION=".array2string($_SESSION));
353
		if ($_SESSION['egw_last_action_time'] < time() - self::TIMEOUT)
354
		{
355
			$this->set_cookie(self::SESSIONID, '', time()-86400);
356
			session_destroy();
357
			$GLOBALS['egw_info']['setup'][$_SESSION['egw_setup_auth_type'] == 'config' ? 'ConfigLoginMSG' : 'HeaderLoginMSG'] =
358
				lang('Session expired');
359
			return false;
360
		}
361
		if ($auth_type == 'config')
362
		{
363
			$this->ConfigDomain = $_SESSION['egw_setup_domain'];
364
		}
365
		// update session timeout
366
		$_SESSION['egw_last_action_time'] = time();
367
368
		// we have a valid session for $auth_type
369
		return true;
370
	}
371
372
    /**
373
    * check if username and password is valid
374
    *
375
    * this function compares the supplied and stored username and password
376
	* as any of the passwords can be clear text or md5 we convert them to md5
377
	* internal and compare always the md5 hashs
378
    *
379
    * @param string $user the user supplied username
380
    * @param string $pw the user supplied password
381
    * @param string $conf_user the configured username
382
    * @param string $hash hash to check password agains (no {prefix} for plain and md5!)
383
    * @returns bool true on success
384
    */
385
	static function check_auth($user, $pw, $conf_user, $hash)
386
	{
387
		if ($user !== $conf_user)
388
		{
389
			$ret = false; // wrong username
390
		}
391
		else
392
		{
393
			// support for old plain-text passwords without "{plain}" prefix
394
			if ($hash[0] !== '{' && !preg_match('/^[0-9a-f]{32}$/', $hash))
395
			{
396
				$hash = '{plain}'.$hash;
397
			}
398
			$ret = Api\Auth::compare_password($pw, $hash, 'md5');
399
		}
400
		//error_log(__METHOD__."('$user', '$pw', '$conf_user', '$hash') returning ".array2string($ret));
401
		return $ret;
402
	}
403
404
	/**
405
	 * Check for correct IP, if an IP address should be enforced
406
	 *
407
	 * @param string $remoteip
408
	 * @return boolean
409
	 */
410
	function checkip($remoteip='')
411
	{
412
		//echo "<p>setup::checkip($remoteip) against setup_acl='".$GLOBALS['egw_info']['server']['setup_acl']."'</p>\n";
413
		$allowed_ips = explode(',',@$GLOBALS['egw_info']['server']['setup_acl']);
414
		if(empty($GLOBALS['egw_info']['server']['setup_acl']) || !is_array($allowed_ips))
415
		{
416
			return True;	// no test
417
		}
418
		$remotes = explode('.',$remoteip);
419
		foreach($allowed_ips as $value)
420
		{
421
			if (!preg_match('/^[0-9.]+$/',$value))
422
			{
423
				$value = gethostbyname($was=$value);		// resolve domain-name, eg. a dyndns account
424
				//echo "resolving '$was' to '$value'<br>\n";
425
			}
426
			$values = explode('.',$value);
427
			for($i = 0; $i < count($values); ++$i)
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
428
			{
429
				if ((int) $values[$i] != (int) $remotes[$i])
430
				{
431
					break;
432
				}
433
			}
434
			if ($i == count($values))
435
			{
436
				return True;	// match
437
			}
438
		}
439
		$GLOBALS['egw_info']['setup']['ConfigLoginMSG'] = lang('Invalid IP address').' '.$remoteip;
440
		error_log(__METHOD__.'-> checking IP failed:'.print_r($remoteip,true));
441
		return False;
442
	}
443
444
	/**
445
	 * Return X.X.X major version from X.X.X.X versionstring
446
	 *
447
	 * @param string $versionstring
448
	 */
449
	function get_major($versionstring)
450
	{
451
		if(!$versionstring)
452
		{
453
			return False;
454
		}
455
456
		$version = str_replace('pre','.',$versionstring);
457
		$varray  = explode('.',$version);
458
		$major   = implode('.',array($varray[0],$varray[1],$varray[2]));
459
460
		return $major;
461
	}
462
463
	/**
464
	 * Clear system/user level cache so as to have it rebuilt with the next access
465
	 *
466
	 * @deprecated AFAIK this code is not used anymore -- RalfBecker 2005/11/04
467
	 */
468
	function clear_session_cache()
469
	{
470
	}
471
472
	/**
473
	 * Add an application to the phpgw_applications table
474
	 *
475
	 * @param string $appname Application 'name' with a matching $setup_info[$appname] array slice
476
	 * @param $_enable =99 set to True/False to override setup.inc.php setting
0 ignored issues
show
Documentation Bug introduced by
The doc comment =99 at position 0 could not be parsed: Unknown type name '=99' at position 0 in =99.
Loading history...
477
	 * @param array $setup_info =null default use $GLOBALS['setup_info']
478
	 */
479
	function register_app($appname, $_enable=99, array $setup_info=null)
480
	{
481
		if (!isset($setup_info)) $setup_info = $GLOBALS['setup_info'];
482
483
		if(!$appname)
484
		{
485
			return False;
486
		}
487
488
		if($_enable == 99)
489
		{
490
			$_enable = $setup_info[$appname]['enable'];
491
		}
492
		$enable = (int)$_enable;
493
494
		if($GLOBALS['DEBUG'])
495
		{
496
			echo '<br>register_app(): ' . $appname . ', version: ' . $setup_info[$appname]['version'] . ', tables: ' . implode(', ',$setup_info[$appname]['tables']) . '<br>';
497
			// _debug_array($setup_info[$appname]);
498
		}
499
500
		if($setup_info[$appname]['version'])
501
		{
502
			if($setup_info[$appname]['tables'])
503
			{
504
				$tables = implode(',',$setup_info[$appname]['tables']);
505
			}
506
			if ($setup_info[$appname]['tables_use_prefix'] == True)
507
			{
508
				if($GLOBALS['DEBUG'])
509
				{
510
					echo "<br>$appname uses tables_use_prefix, storing ". $setup_info[$appname]['tables_prefix']." as prefix for tables\n";
511
				}
512
				$this->db->insert($this->config_table,array(
513
						'config_app'	=> $appname,
514
						'config_name'	=> $appname.'_tables_prefix',
515
						'config_value'	=> $setup_info[$appname]['tables_prefix'],
516
					),False,__LINE__,__FILE__);
517
			}
518
			try {
519
				$this->db->insert($this->applications_table,array(
520
						'app_name'		=> $appname,
521
						'app_enabled'	=> $enable,
522
						'app_order'		=> $setup_info[$appname]['app_order'],
523
						'app_tables'	=> (string)$tables,	// app_tables is NOT NULL
524
						'app_version'	=> $setup_info[$appname]['version'],
525
						'app_index'     => $setup_info[$appname]['index'],
526
						'app_icon'      => $setup_info[$appname]['icon'],
527
						'app_icon_app'  => $setup_info[$appname]['icon_app'],
528
					),False,__LINE__,__FILE__);
529
			}
530
			catch (Api\Db\Exception\InvalidSql $e)
531
			{
532
				// ease update from pre 1.6 eg. 1.4 not having app_index, app_icon, app_icon_app columns
533
				_egw_log_exception($e);
534
535
				$this->db->insert($this->applications_table,array(
536
						'app_name'		=> $appname,
537
						'app_enabled'	=> $enable,
538
						'app_order'		=> $setup_info[$appname]['app_order'],
539
						'app_tables'	=> (string)$tables,	// app_tables is NOT NULL
540
						'app_version'	=> $setup_info[$appname]['version'],
541
					),False,__LINE__,__FILE__);
542
			}
543
			Api\Egw\Applications::invalidate();
544
		}
545
	}
546
547
	/**
548
	 * Check if an application has info in the db
549
	 *
550
	 * @param	$appname	Application 'name' with a matching $setup_info[$appname] array slice
0 ignored issues
show
The type Application was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
551
	 * @param	$enabled	optional, set to False to not enable this app
0 ignored issues
show
The type optional was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
552
	 */
553
	function app_registered($appname)
554
	{
555
		if(!$appname)
556
		{
557
			return False;
558
		}
559
560
		if(@$GLOBALS['DEBUG'])
561
		{
562
			echo '<br>app_registered(): checking ' . $appname . ', table: ' . $this->applications_table;
563
			// _debug_array($setup_info[$appname]);
564
		}
565
566
		if ($this->db->select($this->applications_table,'COUNT(*)',array('app_name' => $appname),__LINE__,__FILE__)->fetchColumn())
567
		{
568
			if(@$GLOBALS['DEBUG'])
569
			{
570
				echo '... app previously registered.';
571
			}
572
			return True;
573
		}
574
		if(@$GLOBALS['DEBUG'])
575
		{
576
			echo '... app not registered';
577
		}
578
		return False;
579
	}
580
581
	/**
582
	 * Update application info in the db
583
	 *
584
	 * @param string $appname	Application 'name' with a matching $setup_info[$appname] array slice
585
	 * @param array $setup_info =null default use $GLOBALS['setup_info']
586
	 */
587
	function update_app($appname, array $setup_info=null)
588
	{
589
		if (!isset($setup_info)) $setup_info = $GLOBALS['setup_info'];
590
591
		if(!$appname)
592
		{
593
			return False;
594
		}
595
596
		if($GLOBALS['DEBUG'])
597
		{
598
			echo '<br>update_app(): ' . $appname . ', version: ' . $setup_info[$appname]['currentver'] . ', table: ' . $this->applications_table . '<br>';
599
			// _debug_array($setup_info[$appname]);
600
		}
601
602
		if(!$this->app_registered($appname))
603
		{
604
			return False;
605
		}
606
607
		if($setup_info[$appname]['version'])
608
		{
609
			//echo '<br>' . $setup_info[$appname]['version'];
610
			if($setup_info[$appname]['tables'])
611
			{
612
				$tables = implode(',',$setup_info[$appname]['tables']);
613
			}
614
			$this->db->update($this->applications_table,array(
615
					'app_enabled'	=> $setup_info[$appname]['enable'],
616
					'app_order'		=> $setup_info[$appname]['app_order'],
617
					'app_tables'	=> (string)$tables,	// app_tables is NOT NULL
618
					'app_version'	=> $setup_info[$appname]['version'],
619
					'app_index'     => $setup_info[$appname]['index'],
620
					'app_icon'      => $setup_info[$appname]['icon'],
621
					'app_icon_app'  => $setup_info[$appname]['icon_app'],
622
				),array('app_name'=>$appname),__LINE__,__FILE__);
623
624
			Api\Egw\Applications::invalidate();
625
		}
626
	}
627
628
	/**
629
	 * Update application version in applications table, post upgrade
630
	 *
631
	 * @param	$setup_info		 * Array of application information (multiple apps or single)
632
	 * @param	$appname		 * Application 'name' with a matching $setup_info[$appname] array slice
633
	 * @param	$tableschanged	???
0 ignored issues
show
Documentation Bug introduced by
The doc comment ??? at position 0 could not be parsed: Unknown type name '??' at position 0 in ???.
Loading history...
634
	 */
635
	function update_app_version($setup_info, $appname, $tableschanged = True)
636
	{
637
		if(!$appname)
638
		{
639
			return False;
640
		}
641
642
		if($tableschanged == True)
643
		{
644
			$GLOBALS['egw_info']['setup']['tableschanged'] = True;
645
		}
646
		if($setup_info[$appname]['currentver'])
647
		{
648
			$this->db->update($this->applications_table,array(
649
					'app_version'	=> $setup_info[$appname]['currentver'],
650
				),array('app_name'=>$appname),__LINE__,__FILE__);
651
652
			Api\Egw\Applications::invalidate();
653
		}
654
		return $setup_info;
655
	}
656
657
	/**
658
	 * de-Register an application
659
	 *
660
	 * @param	$appname	Application 'name' with a matching $setup_info[$appname] array slice
661
	 */
662
	function deregister_app($appname)
663
	{
664
		if(!$appname)
665
		{
666
			return False;
667
		}
668
669
		// Remove categories
670
		$this->db->delete(Api\Categories::TABLE, array('cat_appname'=>$appname),__LINE__,__FILE__);
671
		Api\Categories::invalidate_cache($appname);
672
673
		// Remove config, if we are not deinstalling old phpgwapi (as that's global api config!)
674
		if ($appname != 'phpgwapi')
675
		{
676
			$this->db->delete(Api\Config::TABLE, array('config_app'=>$appname),__LINE__,__FILE__);
677
		}
678
		//echo 'DELETING application: ' . $appname;
679
		$this->db->delete($this->applications_table,array('app_name'=>$appname),__LINE__,__FILE__);
680
681
		Api\Egw\Applications::invalidate();
682
683
		// Remove links to the app
684
		Link::unlink(0, $appname);
685
	}
686
687
	/**
688
	 * Register an application's hooks
689
	 *
690
	 * @param	$appname	Application 'name' with a matching $setup_info[$appname] array slice
691
	 */
692
	function register_hooks($appname)
693
	{
694
		if(!$appname)
695
		{
696
			return False;
697
		}
698
699
		Api\Hooks::read(true);
700
	}
701
702
	/**
703
	 * Setup default and forced preferences, when an application gets installed
704
	 *
705
	 * @param string $appname
706
	 * @return boolean false app not found or no hook settings, true settings found and defaull & forced prefs stored, if there are any defined
707
	 */
708
	function set_default_preferences($appname)
709
	{
710
		$setup_info = $GLOBALS['setup_info'][$appname];
711
712
		if (!isset($setup_info) || !isset($setup_info['hooks']))
713
		{
714
			return false;	// app not found or no hook
715
		}
716
		$GLOBALS['settings'] = array();
717
		$hook_data = array('location' => 'settings','setup' => true);
718
		if (isset($setup_info['hooks']['settings']))
719
		{
720
			$settings = ExecMethod($setup_info['hooks']['settings'],$hook_data);
0 ignored issues
show
Deprecated Code introduced by
The function ExecMethod() has been deprecated: use autoloadable class-names, instanciate and call method or use static methods ( Ignorable by Annotation )

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

720
			$settings = /** @scrutinizer ignore-deprecated */ ExecMethod($setup_info['hooks']['settings'],$hook_data);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
721
		}
722
		elseif(in_array('settings',$setup_info['hooks']) && file_exists($file = EGW_INCLUDE_ROOT.'/'.$appname.'/inc/hook_settings.inc.php'))
723
		{
724
			include_once($file);
725
		}
726
		if (!isset($settings) || !is_array($settings))
727
		{
728
			$settings = $GLOBALS['settings'];	// old file hook or not updated new hook
729
		}
730
		if (!is_array($settings) || !count($settings))
731
		{
732
			return false;
733
		}
734
		// include idots template prefs for (common) preferences
735
		if ($appname == 'preferences' && (file_exists($file = EGW_INCLUDE_ROOT.'/pixelegg/hook_settings.inc.php') ||
736
			file_exists($file = EGW_INCLUDE_ROOT.'/jdots/hook_settings.inc.php') ||
737
			file_exists($file = EGW_INCLUDE_ROOT.'/phpgwapi/templates/idots/hook_settings.inc.php')))
738
		{
739
			$GLOBALS['settings'] = array();
740
			include_once($file);
741
			if ($GLOBALS['settings']) $settings = array_merge($settings,$GLOBALS['settings']);
742
		}
743
		$default = $forced = array();
744
		foreach($settings as $name => $setting)
745
		{
746
			if (isset($setting['default']))
747
			{
748
				$default[$name] = $setting['default'] === false && $setting['type'] === 'check' ? '0' : (string)$setting['default'];
749
			}
750
			if (isset($setting['forced']))
751
			{
752
				$forced[$name] = $setting['forced'] === false && $setting['type'] === 'check' ? '0' : (string)$setting['forced'];
753
			}
754
		}
755
		// store default/forced preferences, if any found
756
		$preferences = new Api\Preferences();
757
		$preferences->read_repository(false);
758
		foreach(array(
759
			'default' => $default,
760
			'forced'  => $forced,
761
		) as $type => $prefs)
762
		{
763
			if ($prefs)
764
			{
765
				foreach($prefs as $name => $value)
766
				{
767
					$preferences->add($appname == 'preferences' ? 'common' : $appname, $name, $value, $type);
768
				}
769
				$preferences->save_repository(false, $type);
770
				//error_log(__METHOD__."('$appname') storing ".($owner==preferences::DEFAULT_ID?'default':'forced')." prefs=".array2string($prefs));
771
			}
772
		}
773
		return true;
774
	}
775
776
	/**
777
	  * call the hooks for a single application
778
	  *
779
	  * @param $location hook location - required
0 ignored issues
show
The type hook was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
780
	  * @param $appname application name - optional
0 ignored issues
show
The type application was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
781
	 */
782
	static function hook($location, $appname='')
783
	{
784
		return Api\Hooks::single($location,$appname,True,True);
785
	}
786
787
	/**
788
	 * egw version checking, is param 1 < param 2 in phpgw versionspeak?
789
	 * @param	$a	phpgw version number to check if less than $b
790
	 * @param	$b	phpgw version number to check $a against
791
	 * @return	True if $a < $b
792
	 */
793
	function alessthanb($a,$b,$DEBUG=False)
794
	{
795
		$num = array('1st','2nd','3rd','4th');
796
797
		if($DEBUG)
798
		{
799
			echo'<br>Input values: '
800
				. 'A="'.$a.'", B="'.$b.'"';
801
		}
802
		$newa = str_replace('pre','.',$a);
803
		$newb = str_replace('pre','.',$b);
804
		$testa = explode('.',$newa);
805
		if(@$testa[1] == '')
806
		{
807
			$testa[1] = 0;
808
		}
809
810
		$testb = explode('.',$newb);
811
		if(@$testb[1] == '')
812
		{
813
			$testb[1] = 0;
814
		}
815
		if(@$testb[3] == '')
816
		{
817
			$testb[3] = 0;
818
		}
819
		$less = 0;
820
821
		for($i=0;$i<count($testa);$i++)
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
822
		{
823
			if($DEBUG) { echo'<br>Checking if '. (int)$testa[$i] . ' is less than ' . (int)$testb[$i] . ' ...'; }
824
			if((int)$testa[$i] < (int)$testb[$i])
825
			{
826
				if ($DEBUG) { echo ' yes.'; }
827
				$less++;
828
				if($i<3)
829
				{
830
					/* Ensure that this is definitely smaller */
831
					if($DEBUG) { echo"  This is the $num[$i] octet, so A is definitely less than B."; }
832
					$less = 5;
833
					break;
834
				}
835
			}
836
			elseif((int)$testa[$i] > (int)$testb[$i])
837
			{
838
				if($DEBUG) { echo ' no.'; }
839
				$less--;
840
				if($i<2)
841
				{
842
					/* Ensure that this is definitely greater */
843
					if($DEBUG) { echo"  This is the $num[$i] octet, so A is definitely greater than B."; }
844
					$less = -5;
845
					break;
846
				}
847
			}
848
			else
849
			{
850
				if($DEBUG) { echo ' no, they are equal or of different length.'; }
851
				// makes sure eg. '1.0.0' is counted less the '1.0.0.xxx' !
852
				$less = count($testa) < count($testb) ? 1 : 0;
853
			}
854
		}
855
		if($DEBUG) { echo '<br>Check value is: "'.$less.'"'; }
856
		if($less>0)
857
		{
858
			if($DEBUG) { echo '<br>A is less than B'; }
859
			return True;
860
		}
861
		elseif($less<0)
862
		{
863
			if($DEBUG) { echo '<br>A is greater than B'; }
864
			return False;
865
		}
866
		else
867
		{
868
			if($DEBUG) { echo '<br>A is equal to B'; }
869
			return False;
870
		}
871
	}
872
873
	/**
874
	 * egw version checking, is param 1 > param 2 in phpgw versionspeak?
875
	 *
876
	 * @param	$a	phpgw version number to check if more than $b
877
	 * @param	$b	phpgw version number to check $a against
878
	 * @return	True if $a < $b
879
	 */
880
	function amorethanb($a,$b,$DEBUG=False)
881
	{
882
		$num = array('1st','2nd','3rd','4th');
883
884
		if($DEBUG)
885
		{
886
			echo'<br>Input values: '
887
				. 'A="'.$a.'", B="'.$b.'"';
888
		}
889
		$newa = str_replace('pre','.',$a);
890
		$newb = str_replace('pre','.',$b);
891
		$testa = explode('.',$newa);
892
		if($testa[3] == '')
893
		{
894
			$testa[3] = 0;
895
		}
896
		$testb = explode('.',$newb);
897
		if($testb[3] == '')
898
		{
899
			$testb[3] = 0;
900
		}
901
		$less = 0;
902
903
		for($i=0;$i<count($testa);$i++)
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
904
		{
905
			if($DEBUG) { echo'<br>Checking if '. (int)$testa[$i] . ' is more than ' . (int)$testb[$i] . ' ...'; }
906
			if((int)$testa[$i] > (int)$testb[$i])
907
			{
908
				if($DEBUG) { echo ' yes.'; }
909
				$less++;
910
				if($i<3)
911
				{
912
					/* Ensure that this is definitely greater */
913
					if($DEBUG) { echo"  This is the $num[$i] octet, so A is definitely greater than B."; }
914
					$less = 5;
915
					break;
916
				}
917
			}
918
			elseif((int)$testa[$i] < (int)$testb[$i])
919
			{
920
				if($DEBUG) { echo ' no.'; }
921
				$less--;
922
				if($i<2)
923
				{
924
					/* Ensure that this is definitely smaller */
925
					if($DEBUG) { echo"  This is the $num[$i] octet, so A is definitely less than B."; }
926
					$less = -5;
927
					break;
928
				}
929
			}
930
			else
931
			{
932
				if($DEBUG) { echo ' no, they are equal.'; }
933
				$less = 0;
934
			}
935
		}
936
		if($DEBUG) { echo '<br>Check value is: "'.$less.'"'; }
937
		if($less>0)
938
		{
939
			if($DEBUG) { echo '<br>A is greater than B'; }
940
			return True;
941
		}
942
		elseif($less<0)
943
		{
944
			if($DEBUG) { echo '<br>A is less than B'; }
945
			return False;
946
		}
947
		else
948
		{
949
			if($DEBUG) { echo '<br>A is equal to B'; }
950
			return False;
951
		}
952
	}
953
954
	/**
955
	 * Own instance of the accounts class
956
	 *
957
	 * @var Api\Accounts
958
	 */
959
	var $accounts;
960
961
	function setup_account_object(array $config=array())
962
	{
963
		if (!isset($this->accounts) || $this->accounts->config || $config)
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->accounts->config of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Bug Best Practice introduced by
The expression $config of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
964
		{
965
			if (!is_object($this->db))
966
			{
967
				$this->loaddb();
968
			}
969
			if (!$config)
0 ignored issues
show
Bug Best Practice introduced by
The expression $config of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
970
			{
971
				// load the configuration from the database
972
				foreach($this->db->select($this->config_table,'config_name,config_value',
973
					"config_name LIKE 'ads%' OR config_name LIKE 'ldap%' OR config_name LIKE 'account_%' OR config_name LIKE '%encryption%' OR config_name='auth_type'",
974
					__LINE__,__FILE__) as $row)
975
				{
976
					$GLOBALS['egw_info']['server'][$row['config_name']] = $config[$row['config_name']] = $row['config_value'];
977
				}
978
			}
979
			try {
980
				$this->accounts = new Api\Accounts($config);
981
			}
982
			catch (Exception $e) {
983
				echo "<p><b>".$e->getMessage()."</b></p>\n";
984
				return false;
985
			}
986
			if (!isset($GLOBALS['egw']->accounts)) $GLOBALS['egw']->accounts = $this->accounts;
987
			Api\Accounts::cache_invalidate();	// the cache is shared for all instances of the class
988
		}
989
		return true;
990
	}
991
992
	/**
993
	 * Used to store and share password of user "anonymous" between calls to add_account from different app installs
994
	 * @var unknown
0 ignored issues
show
The type unknown was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
995
	 */
996
	protected $anonpw;
997
998
	/**
999
	 * add an user account or a user group
1000
	 *
1001
	 * If the $username already exists, only the id is returned, no new user / group gets created,
1002
	 * but if a non-empty password is given it will be changed to that.
1003
	 *
1004
	 * @param string $username alphanumerical username or groupname (account_lid)
1005
	 * @param string $first first name
1006
	 * @param string $last last name
1007
	 * @param string $_passwd cleartext pw or empty or '*unchanged*', to not change pw for existing users
1008
	 * @param string/boolean $primary_group Groupname for users primary group or False for a group, default 'Default'
0 ignored issues
show
Documentation Bug introduced by
The doc comment string/boolean at position 0 could not be parsed: Unknown type name 'string/boolean' at position 0 in string/boolean.
Loading history...
1009
	 * @param boolean $changepw user has right to change pw, default False = Pw change NOT allowed
1010
	 * @param string $email
1011
	 * @param string &$anonpw=null on return password for anonymous user
1012
	 * @return int the numerical user-id
1013
	 */
1014
	function add_account($username,$first,$last,$_passwd,$primary_group='Default',$changepw=False,$email='',&$anonpw=null)
1015
	{
1016
		$this->setup_account_object();
1017
1018
		$primary_group_id = $primary_group ? $this->accounts->name2id($primary_group) : False;
1019
1020
		$passwd = $_passwd;
1021
		if ($username == 'anonymous')
1022
		{
1023
			if (!isset($this->anonpw)) $this->anonpw = Api\Auth::randomstring(16);
0 ignored issues
show
Documentation Bug introduced by
It seems like EGroupware\Api\Auth::randomstring(16) of type string is incompatible with the declared type unknown of property $anonpw.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
1024
			$passwd = $anonpw = $this->anonpw;
1025
		}
1026
1027
		if(!($accountid = $this->accounts->name2id($username, 'account_lid', $primary_group ? 'u' : 'g')))
1028
		{
1029
			$account = array(
1030
				'account_type'      => $primary_group ? 'u' : 'g',
1031
				'account_lid'       => $username,
1032
				'account_passwd'    => $passwd,
1033
				'account_firstname' => $first,
1034
				'account_lastname'  => $last,
1035
				'account_status'    => 'A',
1036
				'account_primary_group' => $primary_group_id,
1037
				'account_expires'   => -1,
1038
				'account_email'     => $email,
1039
				'account_members'   => array()
1040
			);
1041
1042
			// check if egw_accounts.account_description already exists, as the update otherwise fails
1043
			$meta = $GLOBALS['egw_setup']->db->metadata('egw_accounts', true);
1044
			if (!isset($meta['meta']['account_description']))
1045
			{
1046
				$GLOBALS['egw_setup']->oProc->AddColumn('egw_accounts','account_description',array(
1047
					'type' => 'varchar',
1048
					'precision' => '255',
1049
					'comment' => 'group description'
1050
				));
1051
			}
1052
1053
			if (!($accountid = $this->accounts->save($account)))
1054
			{
1055
				error_log("setup::add_account('$username','$first','$last',\$passwd,'$primary_group',$changepw,'$email') failed! accountid=$accountid");
1056
				return false;
1057
			}
1058
		}
1059
		// set password for existing account, if given and not '*unchanged*'
1060
		elseif($_passwd && $_passwd != '*unchanged*')
1061
		{
1062
			try {
1063
				$auth = new Api\Auth;
1064
				$pw_changed = $auth->change_password(null, $passwd, $accountid);
1065
			}
1066
			catch (Exception $e)
1067
			{
1068
				unset($e);
1069
				$pw_changed = false;
1070
			}
1071
			if (!$pw_changed)
1072
			{
1073
				error_log("setup::add_account('$username','$first','$last',\$passwd,'$primary_group',$changepw,'$email') changin password of exisiting account #$accountid failed!");
1074
				return false;
1075
			}
1076
		}
1077
		// call Vfs\Hooks::add{account|group} hook to create the vfs-home-dirs
1078
		// calling general add{account|group} hook fails, as we are only in setup
1079
		// --> setup_cmd_admin execs "admin/admin-cli.php --edit-user" to run them
1080
		if ($primary_group)
1081
		{
1082
			Vfs\Hooks::addAccount(array(
1083
				'account_id' => $accountid,
1084
				'account_lid' => $username,
1085
			));
1086
		}
1087
		else
1088
		{
1089
			Vfs\Hooks::addGroup(array(
1090
				'account_id' => $accountid,
1091
				'account_lid' => $username,
1092
			));
1093
		}
1094
		if ($primary_group)	// only for users, NOT groups
1095
		{
1096
			$this->set_memberships(array($primary_group_id), $accountid);
1097
1098
			if (!$changepw) $this->add_acl('preferences','nopasswordchange',$accountid);
1099
		}
1100
		//error_log("setup::add_account('$username','$first','$last',\$passwd,'$primary_group',$changepw,'$email') successfull created accountid=$accountid");
1101
		return $accountid;
1102
	}
1103
1104
	/**
1105
	 * Adding memberships to an account (keeping existing ones!)
1106
	 *
1107
	 * @param array $_groups array of group-id's to add
1108
	 * @param int $user account_id
1109
	 */
1110
	function set_memberships($_groups, $user)
1111
	{
1112
		$this->setup_account_object();
1113
1114
		if (!($groups = $this->accounts->memberships($user, true)))
1115
		{
1116
			$groups = $_groups;
1117
		}
1118
		else
1119
		{
1120
			$groups = array_unique(array_merge($groups, $_groups));
1121
		}
1122
		$this->accounts->set_memberships($groups, $user);
1123
	}
1124
1125
	/**
1126
	 * Check if accounts other then the automatically installed anonymous account exist
1127
	 *
1128
	 * We check via the account object, to deal with different account-storages
1129
	 *
1130
	 * @return boolean
1131
	 */
1132
	function accounts_exist()
1133
	{
1134
		try {
1135
			if (!$this->setup_account_object()) return false;
1136
1137
			$this->accounts->search(array(
1138
				'type'   => 'accounts',
1139
				'start'  => 0,
1140
				'offset' => 2	// we only need to check 2 accounts, if we just check for not anonymous
1141
			));
1142
1143
			if ($this->accounts->total != 1)
1144
			{
1145
				return $this->accounts->total > 1;
1146
			}
1147
1148
			// one account, need to check it's not the anonymous one
1149
			$this->accounts->search(array(
1150
				'type'   => 'accounts',
1151
				'start'  => 0,
1152
				'offset' => 0,
1153
				'query_type' => 'lid',
1154
				'query'  => 'anonymous',
1155
			));
1156
		}
1157
		catch (Api\Db\Exception $e) {
1158
			_egw_log_exception($e);
1159
			return false;
1160
		}
1161
		return !$this->accounts->total;
1162
	}
1163
1164
	/**
1165
	 * Add ACL rights
1166
	 *
1167
	 * Dont use it to set group-membership, use set_memberships instead!
1168
	 *
1169
	 * @param string|array $apps app-names
1170
	 * @param string $location eg. "run"
1171
	 * @param int|string $account accountid or account_lid
1172
	 * @param int $rights rights to set, default 1
1173
	 */
1174
	function add_acl($apps,$location,$account,$rights=1)
1175
	{
1176
		//error_log("setup::add_acl(".(is_array($apps) ? "array('".implode("','",$apps)."')" : "'$apps'").",'$location',$account,$rights)");
1177
		if (!is_numeric($account))
1178
		{
1179
			$this->setup_account_object();
1180
			$account = $this->accounts->name2id($account);
1181
		}
1182
		if(!is_object($this->db))
1183
		{
1184
			$this->loaddb();
1185
		}
1186
1187
		if(!is_array($apps))
1188
		{
1189
			$apps = array($apps);
1190
		}
1191
		foreach($apps as $app)
1192
		{
1193
			$this->db->delete($this->acl_table,array(
1194
				'acl_appname'  => $app,
1195
				'acl_location' => $location,
1196
				'acl_account'  => $account
1197
			),__LINE__,__FILE__);
1198
1199
			if ((int) $rights)
1200
			{
1201
				$this->db->insert($this->acl_table,array(
1202
					'acl_rights' => $rights
1203
				),array(
1204
					'acl_appname'  => $app,
1205
					'acl_location' => $location,
1206
					'acl_account'  => $account,
1207
				),__LINE__,__FILE__);
1208
			}
1209
		}
1210
	}
1211
1212
	/**
1213
	 * checks if one of the given tables exist, returns the first match
1214
	 *
1215
	 * @param array $tables array with possible table-names
1216
	 * @return string/boolean tablename or false
0 ignored issues
show
Documentation Bug introduced by
The doc comment string/boolean at position 0 could not be parsed: Unknown type name 'string/boolean' at position 0 in string/boolean.
Loading history...
1217
	 */
1218
	function table_exist($tables,$force_refresh=False)
1219
	{
1220
		static $table_names = False;
1221
1222
		if (!$table_names || $force_refresh) $table_names = $this->db->table_names();
1223
1224
		if (!$table_names) return false;
1225
1226
		foreach($table_names as $data)
1227
		{
1228
			if (($key = array_search($data['table_name'],$tables)) !== false)
1229
			{
1230
				return $tables[$key];
1231
			}
1232
		}
1233
		return false;
1234
	}
1235
1236
	/**
1237
	 * Checks and set the names of the tables, which get accessed before an update: eg. config- and applications-table
1238
	 *
1239
	 * Other tables can always use the most up to date name
1240
	 */
1241
	function set_table_names($force_refresh=False)
1242
	{
1243
		foreach(array(
1244
			'config_table'       => array('egw_config','phpgw_config','config'),
1245
			'applications_table' => array('egw_applications','phpgw_applications','applications'),
1246
			'accounts_table'     => array('egw_accounts','phpgw_accounts'),
1247
			'acl_table'          => array('egw_acl','phpgw_acl'),
1248
			'lang_table'         => array('egw_lang','phpgw_lang','lang'),
1249
			'languages_table'    => array('egw_languages','phpgw_languages','languages'),
1250
		) as $name => $tables)
1251
		{
1252
			$table = $this->table_exist($tables,$force_refresh);
1253
1254
			if ($table && $table != $this->$name)	// only overwrite the default name, if we realy got one (important for new installs)
1255
			{
1256
				$this->$name = $table;
1257
			}
1258
			//echo "<p>setup::set_table_names: $name = '{$this->$name}'</p>\n";
1259
		}
1260
	}
1261
}
1262