setup_header   B
last analyzed

Complexity

Total Complexity 45

Size/Duplication

Total Lines 289
Duplicated Lines 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 134
c 1
b 1
f 0
dl 0
loc 289
rs 8.8
wmc 45

8 Methods

Rating   Name   Duplication   Size   Complexity  
A defaults() 0 19 2
A domain_defaults() 0 15 3
A is_hashed() 0 5 2
A check_db_support() 0 28 6
A generate_mcyrpt_iv() 0 26 2
B validation_errors() 0 35 11
A check_db_persistent() 0 15 5
C generate() 0 56 14

How to fix   Complexity   

Complex Class

Complex classes like setup_header often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use setup_header, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * EGroupware Setup - Manage the EGw config file header.inc.php
4
 *
5
 * @link http://www.egroupware.org
6
 * @package setup
7
 * @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
8
 * @author Miles Lott <[email protected]>
9
 * @author Tony Puglisi (Angles)
10
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
11
 */
12
13
use EGroupware\Api;
14
use EGroupware\Api\Framework;
15
16
/**
17
 * Functions to manage the EGw config file header.inc.php
18
 *
19
 * Used by manageheader.php and the new setup command line interface setup-cli.php
20
 *
21
 * @package setup
22
 * @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
23
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
24
 */
25
class setup_header
26
{
27
	/**
28
	 * @var array with php-extension / ADOdb drive names => describtiv label
29
	 */
30
	var $db_fullnames = array(
31
		'mysqli' => 'MySQLi (recommended, incl. transactions)',
32
		'mysql'  => 'MySQL (deprecated)',
33
		'mysqlt' => 'MySQL (deprecated, transactions)',
34
		'pgsql'  => 'PostgreSQL',
35
		'mssql'  => 'MS SQL Server',
36
		'odbc_mssql'  => 'MS SQL Server via ODBC',
37
		'oracle' => 'Oracle',
38
		'odbc_oracle' => 'Oracle via ODBC',
39
		'sapdb'  => 'SAP/Max DB via ODBC',
40
	);
41
42
	/**
43
	 * @var array with php-extension / ADOdb drive names => default port used by database
44
	 */
45
	var $default_db_ports = array(
46
		'pgsql'  => 5432,
47
		'mysql'  => 3306,
48
		'mysqli' => 3306,
49
		'mysqlt' => 3306,
50
		'mssql'  => 1433,
51
		'odbc_mssql'  => '',
52
		'oracle' => 1521,
53
		'odbc_oracle' => '',
54
		'sapdb'  => '',
55
	);
56
57
	/**
58
	 * Detect settings or set defaults for the header.inc.php file (used if it does not yet exist)
59
	 *
60
	 * Sets $GLOBALS['egw_info'], $GLOBALS['egw_domains'] and the defines EGW_SERVER_ROOT and EGW_INCLUDE_ROOT,
61
	 * as if the header has been included
62
	 *
63
	 * @param string $domain ='default' domain to set
64
	 */
65
	function defaults($domain='default')
66
	{
67
		$egw_root = realpath(__DIR__.'/../..');
68
		$GLOBALS['egw_info']['server']['server_root'] = $GLOBALS['egw_info']['server']['include_root'] = $egw_root;
69
		define('EGW_SERVER_ROOT',$egw_root);	// this is usally already defined by setup and cant be changed
70
		define('EGW_INCLUDE_ROOT',$egw_root);
71
72
		$GLOBALS['egw_info']['server']['header_admin_user'] = 'admin';
73
		$GLOBALS['egw_info']['server']['header_admin_password'] = '';
74
		$GLOBALS['egw_info']['server']['setup_acl'] = '';
75
76
		if ($domain) $GLOBALS['egw_domain'][$domain] = $this->domain_defaults();
77
78
		$GLOBALS['egw_info']['server']['show_domain_selectbox'] = false;
79
		$GLOBALS['egw_info']['server']['db_persistent'] = True;
80
		$GLOBALS['egw_info']['login_template_set'] = 'default';
81
		$GLOBALS['egw_info']['server']['mcrypt_enabled'] = False;
82
		$GLOBALS['egw_info']['server']['versions']['mcrypt'] = '';
83
		$GLOBALS['egw_info']['server']['mcrypt_iv'] = $this->generate_mcyrpt_iv();
84
	}
85
86
	function domain_defaults($user='admin',$passwd='',$supported_db=null)
87
	{
88
		$null = null;
89
		if (is_null($supported_db)) $supported_db = $this->check_db_support($null);
90
		$default_db = count($supported_db) ? $supported_db[0] : 'mysqli';
91
92
		return array(
93
			'db_host' => 'localhost',
94
			'db_port' => $this->default_db_ports[$default_db],
95
			'db_name' => 'egroupware',
96
			'db_user' => 'egroupware',
97
			'db_pass' => '',
98
			'db_type' => $default_db,
99
			'config_user'   => $user,
100
			'config_passwd' => $passwd,
101
		);
102
	}
103
104
	/**
105
	 * Checks the values of the (included) header.inc.php file
106
	 *
107
	 * The values are set in $GLOBALS['egw_info'], $GLOBALS['egw_domain'] and EGW_SERVER_ROOT
108
	 *
109
	 * @return array with errors or null if no errors
110
	 */
111
	function validation_errors($path=EGW_SERVER_ROOT)
112
	{
113
		$errors = null;
114
115
		if (!is_dir($path) || !is_readable($path) || !is_dir($path.'/api'))
116
		{
117
			$errors[] = lang("%1 '%2' does NOT exist, is not readable by the webserver or contains no EGroupware installation!",lang('Server root'),$path);
0 ignored issues
show
Unused Code introduced by
The call to lang() has too many arguments starting with lang('Server root'). ( Ignorable by Annotation )

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

117
			$errors[] = /** @scrutinizer ignore-call */ lang("%1 '%2' does NOT exist, is not readable by the webserver or contains no EGroupware installation!",lang('Server root'),$path);

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...
118
		}
119
		if(!$GLOBALS['egw_info']['server']['header_admin_password'])
120
		{
121
			$errors[] = lang("You didn't enter a header admin password");
122
		}
123
		if(!$GLOBALS['egw_info']['server']['header_admin_user'])
124
		{
125
			$errors[] = lang("You didn't enter a header admin username");
126
		}
127
		if (!is_array($GLOBALS['egw_domain']) || !count($GLOBALS['egw_domain']))
128
		{
129
			$errors[] = lang('You need to add at least one EGroupware domain / database instance.');
130
		}
131
		else
132
		{
133
			foreach($GLOBALS['egw_domain'] as $domain => $data)
134
			{
135
				if (!$data['config_passwd'])
136
				{
137
					$errors[] = lang("You didn't enter a config password for domain %1",$domain);
138
				}
139
				if(!$data['config_user'])
140
				{
141
					$errors[] = lang("You didn't enter a config username for domain %1",$domain);
142
				}
143
			}
144
		}
145
		return $errors;
146
	}
147
148
	/**
149
	 * Check if any domain using mysql(i) gives a warning about disabled persistent connections
150
	 *
151
	 * @param array $egw_domains
152
	 * @param boolean $persistent =true current value
153
	 * @return boolean
154
	 */
155
	function check_db_persistent(array $egw_domains, $persistent=true)
156
	{
157
		if ($persistent !== false)
158
		{
159
			foreach($egw_domains as $data)
160
			{
161
				// check if persistent connections are allowed
162
				if (substr($data['db_type'], 0, 5) === 'mysql' && !ini_get('mysqli.allow_persistent'))
163
				{
164
					$persistent = false;
165
					break;
166
				}
167
			}
168
		}
169
		return $persistent;
170
	}
171
172
	/**
173
	 * generate header.inc.php file from given values
174
	 *
175
	 * setup_header::generate($GLOBALS['egw_info'],$GLOBALS['egw_domains'])
176
	 * should write an identical header.inc.php as the one include
177
	 *
178
	 * @param array $egw_info usual content (in server key) plus keys server_root and include_root
179
	 * @param array $egw_domain info about the existing EGw domains / DB instances
180
	 * @return string content of header.inc.php
181
	 */
182
	function generate($egw_info,$egw_domain)
183
	{
184
		$tpl = new Framework\Template('../', 'keep');	// 'keep' to not loose '{hash}' prefix of password-hashes!
185
		$tpl->set_file(array('header' => 'header.inc.php.template'));
186
		$tpl->set_block('header','domain','domain');
187
188
		$most_secure_pw_hash = null;
189
		Api\Auth::passwdhashes($most_secure_pw_hash);
190
191
		foreach($egw_domain as $domain => $data)
192
		{
193
			$var = array('DB_DOMAIN' => $domain);
194
			foreach($data as $name => $value)
195
			{
196
				if ($name == 'db_port' && !$value) $value = $this->default_db_ports[$data['db_type']];
197
				if ($name == 'config_passwd')
198
				{
199
					$var['CONFIG_PASS'] = self::is_hashed($value) ? $value : Api\Auth::encrypt_sql($value, $most_secure_pw_hash);
200
				}
201
				else
202
				{
203
					$var[strtoupper($name)] = addslashes($value);
204
				}
205
			}
206
			$tpl->set_var($var);
207
			$tpl->parse('domains','domain',True);
208
		}
209
		$tpl->set_var('domain','');
210
211
		$var = Array();
212
		foreach($egw_info['server'] as $name => $value)
213
		{
214
			if ($name == 'header_admin_password' && $value && !self::is_hashed($value))
215
			{
216
				$value = Api\Auth::encrypt_sql($value, $most_secure_pw_hash);
217
			}
218
			if ($name == 'versions')
219
			{
220
				$name = 'mcrypt_version';
221
				$value = $value['mcrypt'];
222
			}
223
			static $bools = array(
224
				'mcrypt_enabled' => 'ENABLE_MCRYPT',
225
				'db_persistent'  => 'db_persistent',
226
				'show_domain_selectbox' => 'DOMAIN_SELECTBOX',
227
			);
228
			if (isset($bools[$name]))
229
			{
230
				$name = $bools[$name];
231
				$value = $value ? 'true' : 'false';
232
			}
233
			$var[strtoupper($name)] = addslashes($value);
234
		}
235
		$tpl->set_var($var);
236
237
		return $tpl->parse('out','header');
238
	}
239
240
	/**
241
	 * Generate a random mcrypt_iv vector
242
	 *
243
	 * @return string
244
	 */
245
	function generate_mcyrpt_iv()
246
	{
247
		/*$mcrypt = mcrypt_module_open(Api\Session::MCRYPT_ALGO, '', Api\Session::MCRYPT_MODE, '');
248
		$size = mcrypt_enc_get_iv_size($mcrypt);
249
		if (function_exists('mcrypt_create_iv'))	// PHP 5.3+
250
		{
251
			$iv = mcrypt_create_iv($size, MCRYPT_DEV_URANDOM);
252
			error_log(__METHOD__."() size=$size returning ".array2string($iv));
253
			return $iv;
254
		}*/
255
		$size = 30;
256
		srand((double)microtime()*1000000);
257
		$random_char = array(
258
			'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f',
259
			'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
260
			'w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L',
261
			'M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'
262
		);
263
264
		$iv = '';
265
		for($i=0; $i < $size; $i++)
266
		{
267
			$iv .= $random_char[rand(1,count($random_char))];
268
		}
269
		//error_log(__METHOD__."() size=$size returning ".array2string($iv));
270
		return $iv;
271
	}
272
273
	function check_db_support(&$detected)
274
	{
275
		$supported_db = $detected = array();
276
		foreach(array(
277
			// short => array(extension,func_to_check,supported_db(s))
278
			'mysqli' => array('mysql','mysqli_connect','mysqli'),
279
			'mysql'  => array('mysql','mysql_connect','mysql'),
280
			'mysqlt' => array('mysql','mysql_connect','mysqlt'),
281
			'pgsql'  => array('pgsql','pg_connect','pgsql'),
282
			'mssql'  => array('mssql','mssql_connect','mssql'),
283
			'odbc'   => array('odbc',false,'sapdb','odbc_mssql','odbc_oracle'),
284
			'oracle' => array('oci8',false,'oracle'),
285
		) as $db => $data)
286
		{
287
			$ext = array_shift($data);
288
			$func_to_check = array_shift($data);
289
			$name = isset($this->db_fullnames[$db]) ? $this->db_fullnames[$db] : strtoupper($db);
290
			if (check_load_extension($ext) || $func_to_check && function_exists($func_to_check))
291
			{
292
				$detected[] = lang('You appear to have %1 support.',$name);
0 ignored issues
show
Unused Code introduced by
The call to lang() has too many 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

292
				$detected[] = /** @scrutinizer ignore-call */ lang('You appear to have %1 support.',$name);

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...
293
				$supported_db = array_merge($supported_db,$data);
294
			}
295
			else
296
			{
297
				$detected[] .= lang('No %1 support found. Disabling',$name);
298
			}
299
		}
300
		return $supported_db;
301
	}
302
303
	/**
304
	 * Check if pw is hashed
305
	 *
306
	 * @param string $pw
307
	 * @return boolean
308
	 */
309
	static function is_hashed($pw)
310
	{
311
		$ret = $pw[0] == '{' || preg_match('/^[0-9a-f]{32}$/', $pw);
312
		//error_log(__METHOD__."('$pw') returning ".array2string($ret));
313
		return $ret;
314
	}
315
}
316
317
// some constanst for pre php4.3
318
if (!defined('PHP_SHLIB_SUFFIX'))
319
{
320
	define('PHP_SHLIB_SUFFIX',strtoupper(substr(PHP_OS, 0,3)) == 'WIN' ? 'dll' : 'so');
321
}
322
if (!defined('PHP_SHLIB_PREFIX'))
323
{
324
	define('PHP_SHLIB_PREFIX',PHP_SHLIB_SUFFIX == 'dll' ? 'php_' : '');
325
}
326