1<?php
2/*
3 * vim:set softtabstop=4 shiftwidth=4 expandtab:
4 *
5 * LICENSE: GNU Affero General Public License, version 3 (AGPL-3.0-or-later)
6 * Copyright 2001 - 2020 Ampache.org
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20 *
21 */
22
23declare(strict_types=0);
24
25namespace Ampache\Module\System;
26
27use Ampache\Config\AmpConfig;
28use Exception;
29
30/**
31 * Core Class
32 *
33 * This is really just a namespace class, it's full of static functions
34 * would be replaced by a namespace library once that exists in php
35 */
36class Core
37{
38    /**
39     * get_global
40     * Return a $GLOBAL variable instead of calling directly
41     *
42     * @param string $variable
43     * @return mixed
44     */
45    public static function get_global($variable)
46    {
47        return $GLOBALS[$variable] ?? '';
48    }
49
50    /**
51     * get_request
52     * Return a $REQUEST variable instead of calling directly
53     *
54     * @param string $variable
55     * @return string
56     *
57     * @deprecated Use RequestParser
58     */
59    public static function get_request($variable)
60    {
61        if (filter_input(INPUT_POST, $variable, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES) !== null) {
62            return filter_input(INPUT_POST, $variable, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
63        }
64        if (filter_input(INPUT_GET, $variable, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES) !== null) {
65            return filter_input(INPUT_GET, $variable, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
66        }
67        if (isset($_REQUEST[$variable])) {
68            return filter_var($_REQUEST[$variable], FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
69        }
70        if ($_REQUEST[$variable] === null) {
71            return '';
72        }
73
74        return $_REQUEST[$variable];
75    }
76
77    /**
78     * get_get
79     * Return a $GET variable instead of calling directly
80     *
81     * @param string $variable
82     * @return string
83     */
84    public static function get_get($variable)
85    {
86        if (filter_has_var(INPUT_GET, $variable)) {
87            return filter_input(INPUT_GET, $variable, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
88        }
89        if (isset($_GET[$variable])) {
90            return filter_var($_GET[$variable], FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
91        }
92        if ($_GET[$variable] === null) {
93            return '';
94        }
95
96        return $_GET[$variable];
97    }
98
99    /**
100     * @param string $variable
101     * @return string
102     * @deprecated Not in use
103     *
104     * get_cookie
105     * Return a $COOKIE variable instead of calling directly
106     *
107     */
108    public static function get_cookie($variable)
109    {
110        if (filter_has_var(INPUT_COOKIE, $variable)) {
111            return filter_input(INPUT_COOKIE, $variable, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
112        }
113        if (isset($_COOKIE[$variable])) {
114            return filter_var($_COOKIE[$variable], FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
115        }
116        if ($_COOKIE[$variable] === null) {
117            return '';
118        }
119
120        return $_COOKIE[$variable];
121    }
122
123    /**
124     * get_server
125     * Return a $SERVER variable instead of calling directly
126     *
127     * @param string $variable
128     * @return string
129     */
130    public static function get_server($variable)
131    {
132        if (filter_has_var(INPUT_SERVER, $variable)) {
133            return filter_input(INPUT_SERVER, $variable, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
134        }
135        // INPUT_SERVER can sometimes fail
136        if (filter_has_var(INPUT_ENV, $variable)) {
137            return filter_input(INPUT_ENV, $variable, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
138        }
139        if (isset($_SERVER[$variable])) {
140            return filter_var($_SERVER[$variable], FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
141        }
142        if ($_SERVER[$variable] === null) {
143            return '';
144        }
145
146        return $_SERVER[$variable];
147    }
148
149    /**
150     * get_post
151     * Return a $POST variable instead of calling directly
152     *
153     * @param string $variable
154     * @return string
155     */
156    public static function get_post($variable)
157    {
158        if (filter_has_var(INPUT_POST, $variable)) {
159            return filter_input(INPUT_POST, $variable, FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
160        }
161        if (isset($_POST[$variable])) {
162            return filter_var($_POST[$variable], FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
163        }
164        if ($_POST[$variable] === null) {
165            return '';
166        }
167
168        return $_POST[$variable];
169    }
170
171    /**
172     * get_user_ip
173     * check for the ip of the request
174     *
175     * @return string
176     */
177    public static function get_user_ip()
178    {
179        // get the x forward if it's valid
180        if (filter_var(Core::get_server('HTTP_X_FORWARDED_FOR'), FILTER_VALIDATE_IP)) {
181            return filter_var(Core::get_server('HTTP_X_FORWARDED_FOR'), FILTER_VALIDATE_IP);
182        }
183
184        return filter_var(Core::get_server('REMOTE_ADDR'), FILTER_VALIDATE_IP);
185    }
186
187    /**
188     * form_register
189     * This registers a form with a SID, inserts it into the session
190     * variables and then returns a string for use in the HTML form
191     * @param string $name
192     * @param string $type
193     * @return string
194     */
195    public static function form_register($name, $type = 'post')
196    {
197        // Make ourselves a nice little sid
198        $sid    = md5(uniqid((string)rand(), true));
199        $window = AmpConfig::get('session_length', 3600);
200        $expire = time() + $window;
201
202        // Register it
203        $_SESSION['forms'][$sid] = array('name' => $name, 'expire' => $expire);
204        if (!isset($_SESSION['forms'][$sid])) {
205            debug_event(self::class, "Form $sid not found in session, failed to register!", 2);
206        } else {
207            debug_event(self::class, "Registered $type form $name with SID $sid and expiration $expire ($window seconds from now)", 5);
208        }
209
210        switch ($type) {
211            case 'get':
212                $string = $sid;
213                break;
214            case 'post':
215            default:
216                $string = '<input type="hidden" name="form_validation" value="' . $sid . '" />';
217                break;
218        } // end switch on type
219
220        return $string;
221    } // form_register
222
223    /**
224     * form_verify
225     *
226     * This takes a form name and then compares it with the posted sid, if
227     * they don't match then it returns false and doesn't let the person
228     * continue
229     * @param string $name
230     * @param string $type
231     * @return boolean
232     */
233    public static function form_verify($name, $type = 'post')
234    {
235        switch ($type) {
236            case 'post':
237                $sid = $_POST['form_validation'];
238                break;
239            case 'get':
240                $sid = $_GET['form_validation'];
241                break;
242            case 'cookie':
243                $sid = $_COOKIE['form_validation'];
244                break;
245            case 'request':
246                $sid = $_REQUEST['form_validation'];
247                break;
248            default:
249                return false;
250        }
251
252        if (!isset($_SESSION['forms'][$sid])) {
253            debug_event(self::class, "Form $sid not found in session, rejecting request", 2);
254
255            return false;
256        }
257
258        $form = $_SESSION['forms'][$sid];
259        unset($_SESSION['forms'][$sid]);
260
261        if ($form['name'] == $name) {
262            debug_event(self::class, "Verified SID $sid for $type form $name", 5);
263            if ($form['expire'] < time()) {
264                debug_event(self::class, "Form $sid is expired, rejecting request", 2);
265
266                return false;
267            }
268
269            return true;
270        }
271
272        // OMG HAX0RZ
273        debug_event(self::class, "$type form $sid failed consistency check, rejecting request", 2);
274
275        return false;
276    } // form_verify
277
278    /**
279     * gen_secure_token
280     *
281     * This generates a cryptographically secure token.
282     * Returns a token of the required bytes length, as a string. Returns false
283     * if it could not generate a cryptographically secure token.
284     * @param integer $length
285     * @return false|string
286     * @throws Exception
287     */
288    public static function gen_secure_token($length)
289    {
290        if (function_exists('random_bytes')) {
291            $buffer = random_bytes($length);
292        } elseif (function_exists('openssl_random_pseudo_bytes')) {
293            $buffer = openssl_random_pseudo_bytes($length);
294        } elseif (file_exists('/dev/random') && is_readable('/dev/random')) {
295            $buffer = file_get_contents('/dev/random', false, null, -1, $length);
296        } else {
297            return false;
298        }
299
300        return bin2hex($buffer);
301    }
302
303    /**
304     * image_dimensions
305     * This returns the dimensions of the passed song of the passed type
306     * returns an empty array if PHP-GD is not currently installed, returns
307     * false on error
308     *
309     * @param string $image_data
310     * @return array
311     */
312    public static function image_dimensions($image_data)
313    {
314        if (!function_exists('ImageCreateFromString')) {
315            return array('width' => 0, 'height' => 0);
316        }
317
318        if (empty($image_data)) {
319            debug_event(self::class, "Cannot create image from empty data", 2);
320
321            return array('width' => 0, 'height' => 0);
322        }
323
324        $image = ImageCreateFromString($image_data);
325
326        if ($image == false) {
327            return array('width' => 0, 'height' => 0);
328        }
329
330        $width  = imagesx($image);
331        $height = imagesy($image);
332
333        if (!$width || !$height) {
334            return array('width' => 0, 'height' => 0);
335        }
336
337        return array('width' => $width, 'height' => $height);
338    } // image_dimensions
339
340    /**
341     * is_readable
342     *
343     * Replacement function because PHP's is_readable is buggy:
344     * https://bugs.php.net/bug.php?id=49620
345     *
346     * @param string $path
347     * @return boolean
348     */
349    public static function is_readable($path)
350    {
351        if (is_dir($path)) {
352            $handle = opendir($path);
353            if ($handle === false) {
354                return false;
355            }
356            closedir($handle);
357
358            return true;
359        }
360
361        $handle = @fopen($path, 'rb');
362        if ($handle === false) {
363            return false;
364        }
365        fclose($handle);
366
367        return true;
368    }
369
370    /**
371     * get_filesize
372     * Get a file size. This because filesize() doesn't work on 32-bit OS with files > 2GB
373     * @param $filename
374     * @return integer
375     */
376    public static function get_filesize($filename)
377    {
378        if (!file_exists($filename)) {
379            return 0;
380        }
381        $size = filesize($filename);
382        if ($size === false) {
383            $filepointer = fopen($filename, 'rb');
384            if (!$filepointer) {
385                return 0;
386            }
387            $offset = PHP_INT_MAX - 1;
388            $size   = (float)$offset;
389            if (!fseek($filepointer, $offset)) {
390                return 0;
391            }
392            $chunksize = 8192;
393            while (!feof($filepointer)) {
394                $size += strlen(fread($filepointer, $chunksize));
395            }
396        } elseif ($size < 0) {
397            // Handle overflowed integer...
398            $size = sprintf("%u", $size);
399        }
400
401        return (int)$size;
402    }
403
404    /**
405     * conv_lc_file
406     *
407     * Convert site charset filename to local charset filename for file operations
408     * @param string $filename
409     * @return string
410     */
411    public static function conv_lc_file($filename)
412    {
413        $lc_filename  = $filename;
414        $site_charset = AmpConfig::get('site_charset');
415        $lc_charset   = AmpConfig::get('lc_charset');
416        if ($lc_charset && $lc_charset != $site_charset) {
417            if (function_exists('iconv')) {
418                $lc_filename = iconv($site_charset, $lc_charset, $filename);
419            }
420        }
421
422        return $lc_filename;
423    }
424
425    /**
426     * is_session_started
427     *
428     * Universal function for checking session status.
429     * @return boolean
430     */
431    public static function is_session_started()
432    {
433        if (php_sapi_name() !== 'cli') {
434            if (version_compare(phpversion(), '5.4.0', '>=')) {
435                return session_status() === PHP_SESSION_ACTIVE;
436            } else {
437                return session_id() === '' ? false : true;
438            }
439        }
440
441        return false;
442    }
443
444    /**
445     * @return string
446     */
447    public static function get_reloadutil()
448    {
449        $play_type = AmpConfig::get('play_type');
450
451        return ($play_type == "stream" || $play_type == "democratic" || !AmpConfig::get('ajax_load')) ? "reloadUtil" : "reloadDivUtil";
452    }
453
454    /**
455     * requests_options
456     * @param array $options
457     * @return array
458     */
459    public static function requests_options($options = array())
460    {
461        if (!isset($options['proxy'])) {
462            if (AmpConfig::get('proxy_host') && AmpConfig::get('proxy_port')) {
463                $proxy   = array();
464                $proxy[] = AmpConfig::get('proxy_host') . ':' . AmpConfig::get('proxy_port');
465                if (AmpConfig::get('proxy_user')) {
466                    $proxy[] = AmpConfig::get('proxy_user');
467                    $proxy[] = AmpConfig::get('proxy_pass');
468                }
469
470                $options['proxy'] = $proxy;
471            }
472        }
473
474        return $options;
475    }
476
477    /**
478     * get_tmp_dir
479     *
480     * @return string
481     */
482    public static function get_tmp_dir()
483    {
484        if (AmpConfig::get('tmp_dir_path')) {
485            return AmpConfig::get('tmp_dir_path');
486        }
487        if (function_exists('sys_get_temp_dir')) {
488            $tmp_dir = sys_get_temp_dir();
489        } else {
490            if (strpos(PHP_OS, 'WIN') === 0) {
491                $tmp_dir = $_ENV['TMP'];
492                if (!isset($tmp_dir)) {
493                    $tmp_dir = 'C:\Windows\Temp';
494                }
495            } else {
496                $tmp_dir = @$_ENV['TMPDIR'];
497                if (!isset($tmp_dir)) {
498                    $tmp_dir = '/tmp';
499                }
500            }
501        }
502
503        return $tmp_dir;
504    }
505}
506