1<?php
2# MantisBT - A PHP based bugtracking system
3
4# MantisBT is free software: you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation, either version 2 of the License, or
7# (at your option) any later version.
8#
9# MantisBT is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with MantisBT.  If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Utility API
19 *
20 * Utility functions are *small* functions that are used often and therefore
21 * have *no* prefix, to keep their names short.
22 *
23 * Utility functions have *no* dependencies on any other APIs, since they are
24 * included first in order to make them available to all the APIs.
25 * Miscellaneous functions that provide functionality on top of other APIS
26 * are found in the helper_api.
27 *
28 * @package CoreAPI
29 * @subpackage UtilityAPI
30 * @copyright Copyright 2000 - 2002  Kenzaburo Ito - kenito@300baud.org
31 * @copyright Copyright 2002  MantisBT Team - mantisbt-dev@lists.sourceforge.net
32 * @link http://www.mantisbt.org
33 *
34 * @uses config_api.php
35 * @uses constant_inc.php
36 * @uses error_api.php
37 */
38
39require_api( 'config_api.php' );
40require_api( 'constant_inc.php' );
41require_api( 'error_api.php' );
42
43/**
44 * converts a 1 value to X
45 * converts a 0 value to a space
46 * @param integer $p_num A numeric to translate as a boolean for display.
47 * @return string X or space
48 * @access public
49 */
50function trans_bool( $p_num ) {
51	if( 0 == $p_num ) {
52		return '&#160;';
53	} else {
54		return icon_get( 'fa-check', 'fa-lg' );
55	}
56}
57
58/**
59 * Add a trailing DIRECTORY_SEPARATOR to a string if it isn't present
60 * @param string $p_path A string representing a file system path.
61 * @return string
62 * @access public
63 */
64function terminate_directory_path( $p_path ) {
65	return rtrim( $p_path, DIRECTORY_SEPARATOR ) . DIRECTORY_SEPARATOR;
66}
67
68/**
69 * Return true if the parameter is an empty string or a string
70 * containing only whitespace, false otherwise
71 * @param string $p_var String to test whether it is blank.
72 * @return boolean
73 * @access public
74 */
75function is_blank( $p_var ) {
76	$p_var = trim( $p_var );
77	$t_str_len = strlen( $p_var );
78	if( 0 == $t_str_len ) {
79		return true;
80	}
81	return false;
82}
83
84/**
85 * Get the named php ini variable but return it as a boolean
86 * @param string $p_name A php.ini variable to evaluate.
87 * @return boolean
88 * @access public
89 */
90function ini_get_bool( $p_name ) {
91	$t_result = ini_get( $p_name );
92
93	if( is_string( $t_result ) ) {
94		switch( strtolower( $t_result ) ) {
95			case 'off':
96			case 'false':
97			case 'no':
98			case 'none':
99			case '':
100			case '0':
101				return false;
102				break;
103			case 'on':
104			case 'true':
105			case 'yes':
106			case '1':
107				return true;
108				break;
109		}
110	}
111	return (bool)$t_result;
112}
113
114/**
115 * Get the named php.ini variable but return it as a number after converting
116 * the giga (g/G), mega (m/M) and kilo (k/K) postfixes. These postfixes do not
117 * adhere to IEEE 1541 in that k=1024, not k=1000. For more information see
118 * http://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
119 * @param string $p_name Name of the configuration option to read.
120 * @return int Integer value of the configuration option.
121 * @access public
122 */
123function ini_get_number( $p_name ) {
124	$t_value = ini_get( $p_name );
125
126	switch( substr( $t_value, -1 ) ) {
127		case 'G':
128		case 'g':
129			$t_result = (int)$t_value * 1073741824;
130			break;
131		case 'M':
132		case 'm':
133			$t_result = (int)$t_value * 1048576;
134			break;
135		case 'K':
136		case 'k':
137			$t_result = (int)$t_value * 1024;
138			break;
139		default:
140			$t_result = (int)$t_value;
141			break;
142	}
143	return $t_result;
144}
145
146/**
147 * Sort a multi-dimensional array by one of its keys
148 * @param array   $p_array     Array to sort.
149 * @param string  $p_key       Array key to sort array on.
150 * @param integer $p_direction Sort direction.
151 * @return array sorted array
152 * @access public
153 */
154function multi_sort( array $p_array, $p_key, $p_direction = ASCENDING ) {
155	if( DESCENDING == $p_direction ) {
156		$t_factor = -1;
157	} else {
158		# might as well allow everything else to mean ASC rather than erroring
159		$t_factor = 1;
160	}
161
162	if( empty( $p_array ) ) {
163		return $p_array;
164	}
165	if( !is_array( current( $p_array ) ) ) {
166		error_parameters( 'tried to multisort an invalid multi-dimensional array' );
167		trigger_error( ERROR_GENERIC, ERROR );
168	}
169
170	# Security measure: see http://www.mantisbt.org/bugs/view.php?id=9704 for details
171	if( array_key_exists( $p_key, current( $p_array ) ) ) {
172		uasort(
173			$p_array,
174			function( $a, $b ) use( $t_factor, $p_key ) {
175				return $t_factor * strnatcasecmp( $a[$p_key], $b[$p_key] );
176			}
177		);
178	} else {
179		trigger_error( ERROR_INVALID_SORT_FIELD, ERROR );
180	}
181	return $p_array;
182}
183
184/**
185 * Return GD version
186 * It doesn't use gd_info() so it works with PHP < 4.3.0 as well
187 * @return int represents gd version
188 * @access public
189 */
190function get_gd_version() {
191	$t_GDfuncList = get_extension_funcs( 'gd' );
192	if( !is_array( $t_GDfuncList ) ) {
193		return 0;
194	} else {
195		if( in_array( 'imagegd2', $t_GDfuncList ) ) {
196			return 2;
197		} else {
198			return 1;
199		}
200	}
201}
202
203/**
204 * return true or false if string matches current page name
205 * @param string $p_string To test against the php script name.
206 * @return boolean
207 * @access public
208 */
209function is_page_name( $p_string ) {
210	return isset( $_SERVER['SCRIPT_NAME'] ) && ( 0 < strpos( $_SERVER['SCRIPT_NAME'], $p_string ) );
211}
212
213/**
214 * return true or false if the host operating system is windows
215 * @return boolean
216 * @access public
217 */
218function is_windows_server() {
219	if( defined( 'PHP_WINDOWS_VERSION_MAJOR' ) ) {
220		return (PHP_WINDOWS_VERSION_MAJOR > 0);
221	}
222	return ('WIN' == substr( PHP_OS, 0, 3 ) );
223}
224
225/**
226 * return array of class properties (via reflection api)
227 * @param string  $p_classname      Class name.
228 * @param string  $p_type           Property type - public/private/protected/static.
229 * @param boolean $p_return_object  Whether to return array of property objects.
230 * @param boolean $p_include_parent Whether to include properties of parent classes.
231 * @return array
232 * @access public
233 */
234function getClassProperties( $p_classname, $p_type = 'public', $p_return_object = false, $p_include_parent = false ) {
235	$t_ref = new ReflectionClass( $p_classname );
236	$t_props = $t_ref->getProperties();
237	$t_props_arr = array();
238	foreach( $t_props as $t_prop ){
239		$t_name = $t_prop->getName();
240		if( $t_prop->isPublic() and (stripos( $p_type, 'public' ) === false) ) {
241			continue;
242		}
243		if( $t_prop->isPrivate() and (stripos( $p_type, 'private' ) === false) ) {
244			continue;
245		}
246		if( $t_prop->isProtected() and (stripos( $p_type, 'protected' ) === false) ) {
247			continue;
248		}
249		if( $t_prop->isStatic() and (stripos( $p_type, 'static' ) === false) ) {
250			continue;
251		}
252		if( $p_return_object ) {
253			$t_props_arr[$t_name] = $t_prop;
254		} else {
255			$t_props_arr[$t_name] = true;
256		}
257	}
258	if( $p_include_parent ) {
259		if( $t_parentclass = $t_ref->getParentClass() ) {
260			$t_parent_props_arr = getClassProperties( $t_parentclass->getName() );
261			if( count( $t_parent_props_arr ) > 0 ) {
262				$t_props_arr = array_merge( $t_parent_props_arr, $t_props_arr );
263			}
264		}
265	}
266	return $t_props_arr;
267}
268
269/**
270 * return string of system font path
271 * @access public
272 * @return string representing system path to font location
273 */
274function get_font_path() {
275		$t_font_path = config_get_global( 'system_font_folder' );
276		if( $t_font_path == '' ) {
277			if( is_windows_server() ) {
278				$t_system_root = $_SERVER['SystemRoot'];
279				if( empty( $t_system_root ) ) {
280					return '';
281				} else {
282					$t_font_path = $t_system_root . '/fonts/';
283				}
284			} else {
285				if( file_exists( '/usr/share/fonts/corefonts/' ) ) {
286					$t_font_path = '/usr/share/fonts/corefonts/';
287				} else if( file_exists( '/usr/share/fonts/truetype/msttcorefonts/' ) ) {
288					$t_font_path = '/usr/share/fonts/truetype/msttcorefonts/';
289				} else if( file_exists( '/usr/share/fonts/msttcorefonts/' ) ) {
290					$t_font_path = '/usr/share/fonts/msttcorefonts/';
291				} else {
292					$t_font_path = '/usr/share/fonts/truetype/';
293				}
294			}
295		}
296		return $t_font_path;
297}
298
299/**
300 * unserialize() with Exception instead of PHP notice.
301 *
302 * When given invalid data, unserialize() throws a PHP notice; this function
303 * relies on a custom error handler to throw an Exception instead.
304 *
305 * @param string $p_string The serialized string.
306 * @return mixed The converted value
307 *
308 * @throws ErrorException
309 */
310function safe_unserialize( $p_string ) {
311	set_error_handler( 'error_convert_to_exception' );
312	try {
313		$t_data = unserialize( $p_string );
314	}
315	catch( ErrorException $e ) {
316		restore_error_handler();
317		throw $e;
318	}
319	restore_error_handler();
320	return $t_data;
321}
322