Subversion Repositories oidplus

Rev

Rev 1276 | Rev 1295 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 daniel-mar 1
<?php
2
 
3
/*
4
 * OIDplus 2.0
1103 daniel-mar 5
 * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft
2 daniel-mar 6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
 
1050 daniel-mar 20
use ViaThinkSoft\OIDplus\OIDplus;
1149 daniel-mar 21
use ViaThinkSoft\OIDplus\OIDplusException;
1050 daniel-mar 22
 
1130 daniel-mar 23
/**
24
 * @param string $privKey
25
 * @return bool
26
 */
27
function is_privatekey_encrypted(string $privKey): bool {
830 daniel-mar 28
        return strpos($privKey,'BEGIN ENCRYPTED PRIVATE KEY') !== false;
29
}
30
 
1130 daniel-mar 31
/**
32
 * @param string $privKey
33
 * @param string $pubKey
34
 * @return bool
35
 */
36
function verify_private_public_key(string $privKey, string $pubKey): bool {
721 daniel-mar 37
        if (!function_exists('openssl_public_encrypt')) return false;
74 daniel-mar 38
        try {
39
                if (empty($privKey)) return false;
40
                if (empty($pubKey)) return false;
453 daniel-mar 41
                $data = generateRandomString(25);
386 daniel-mar 42
                $encrypted = '';
43
                $decrypted = '';
74 daniel-mar 44
                if (!@openssl_public_encrypt($data, $encrypted, $pubKey)) return false;
45
                if (!@openssl_private_decrypt($encrypted, $decrypted, $privKey)) return false;
46
                return $decrypted == $data;
1050 daniel-mar 47
        } catch (\Exception $e) {
74 daniel-mar 48
                return false;
49
        }
50
}
51
 
1130 daniel-mar 52
/**
53
 * @param string $privKeyOld
54
 * @param string|null $passphrase_old
55
 * @param string|null $passphrase_new
56
 * @return false|string
57
 */
58
function change_private_key_passphrase(string $privKeyOld, string $passphrase_old=null, string $passphrase_new=null) {
830 daniel-mar 59
        $pkey_config = array(
60
            //"digest_alg" => "sha512",
61
            //"private_key_bits" => 2048,
62
            //"private_key_type" => OPENSSL_KEYTYPE_RSA,
1158 daniel-mar 63
            "config" => class_exists(OIDplus::class) ? OIDplus::getOpenSslCnf() : @getenv('OPENSSL_CONF')
830 daniel-mar 64
        );
65
        $privKeyNew = @openssl_pkey_get_private($privKeyOld, $passphrase_old);
66
        if ($privKeyNew === false) return false;
1116 daniel-mar 67
        if (!@openssl_pkey_export($privKeyNew, $privKeyNewExport, $passphrase_new, $pkey_config)) return false;
68
        if ($privKeyNewExport === "") return false;
69
        return "$privKeyNewExport";
830 daniel-mar 70
}
71
 
1130 daniel-mar 72
/**
73
 * @param string $privKey
74
 * @param string $passphrase
75
 * @return false|string
76
 */
77
function decrypt_private_key(string $privKey, string $passphrase) {
830 daniel-mar 78
        return change_private_key_passphrase($privKey, $passphrase, null);
79
}
80
 
1130 daniel-mar 81
/**
82
 * @param string $privKey
83
 * @param string $passphrase
84
 * @return false|string
85
 */
86
function encrypt_private_key(string $privKey, string $passphrase) {
830 daniel-mar 87
        return change_private_key_passphrase($privKey, null, $passphrase);
88
}
89
 
1130 daniel-mar 90
/**
91
 * @param string $data
92
 * @return int
93
 */
94
function smallhash(string $data): int { // get 31 bits from SHA1. Values 0..2147483647
250 daniel-mar 95
        return (hexdec(substr(sha1($data),-4*2)) & 0x7FFFFFFF);
74 daniel-mar 96
}
180 daniel-mar 97
 
1130 daniel-mar 98
/**
99
 * @param string $name
100
 * @return array
101
 */
102
function split_firstname_lastname(string $name): array {
182 daniel-mar 103
        $ary = explode(' ', $name);
104
        $last_name = array_pop($ary);
105
        $first_name = implode(' ', $ary);
106
        return array($first_name, $last_name);
107
}
108
 
1130 daniel-mar 109
/**
110
 * @return void
111
 */
180 daniel-mar 112
function originHeaders() {
113
        // CORS
114
        // Author: Till Wehowski
426 daniel-mar 115
        // TODO: add to class OIDplus
182 daniel-mar 116
 
180 daniel-mar 117
        header("Access-Control-Allow-Credentials: true");
118
        header("Access-Control-Allow-Origin: ".strip_tags(((isset($_SERVER['HTTP_ORIGIN'])) ? $_SERVER['HTTP_ORIGIN'] : "*")));
119
 
120
        header("Access-Control-Allow-Headers: If-None-Match, X-Requested-With, Origin, X-Frdlweb-Bugs, Etag, X-Forgery-Protection-Token, X-CSRF-Token");
121
 
122
        if (isset($_SERVER['HTTP_ORIGIN'])) {
123
                header('X-Frame-Options: ALLOW-FROM '.$_SERVER['HTTP_ORIGIN']);
124
        } else {
125
                header_remove("X-Frame-Options");
126
        }
127
 
128
        $expose = array('Etag', 'X-CSRF-Token');
129
        foreach (headers_list() as $num => $header) {
130
                $h = explode(':', $header);
131
                $expose[] = trim($h[0]);
132
        }
133
        header("Access-Control-Expose-Headers: ".implode(',',$expose));
134
 
135
        header("Vary: Origin");
136
}
236 daniel-mar 137
 
346 daniel-mar 138
if (!function_exists('mb_wordwrap')) {
1130 daniel-mar 139
        /**
140
         * @param string $str
141
         * @param int $width
142
         * @param string $break
143
         * @param bool $cut
144
         * @return string
145
         */
146
        function mb_wordwrap(string $str, int $width = 75, string $break = "\n", bool $cut = false): string {
346 daniel-mar 147
                // https://stackoverflow.com/a/4988494/488539
1231 daniel-mar 148
                assert(strlen($break) > 0);
149
                $lines = explode("$break", $str);
346 daniel-mar 150
                foreach ($lines as &$line) {
151
                        $line = rtrim($line);
152
                        if (mb_strlen($line) <= $width) {
153
                                continue;
154
                        }
155
                        $words = explode(' ', $line);
156
                        $line = '';
157
                        $actual = '';
158
                        foreach ($words as $word) {
159
                                if (mb_strlen($actual.$word) <= $width) {
160
                                        $actual .= $word.' ';
161
                                } else {
162
                                        if ($actual != '') {
163
                                                $line .= rtrim($actual).$break;
164
                                        }
165
                                        $actual = $word;
166
                                        if ($cut) {
167
                                                while (mb_strlen($actual) > $width) {
168
                                                        $line .= mb_substr($actual, 0, $width).$break;
169
                                                        $actual = mb_substr($actual, $width);
170
                                                }
171
                                        }
172
                                        $actual .= ' ';
173
                                }
174
                        }
175
                        $line .= trim($actual);
176
                }
177
                return implode($break, $lines);
178
        }
179
}
355 daniel-mar 180
 
1130 daniel-mar 181
/**
182
 * @param string $out
183
 * @param string $contentType
184
 * @param string $filename
185
 * @return void
186
 */
187
function httpOutWithETag(string $out, string $contentType, string $filename='') {
379 daniel-mar 188
        $etag = md5($out);
189
        header("Etag: $etag");
190
        header("Content-MD5: $etag"); // RFC 2616 clause 14.15
191
        if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && (trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag)) {
1068 daniel-mar 192
                if (PHP_SAPI != 'cli') @http_response_code(304); // 304 Not Modified
379 daniel-mar 193
        } else {
194
                header("Content-Type: $contentType");
195
                if (!empty($filename)) {
196
                        header('Content-Disposition:inline; filename="'.$filename.'"');
197
                }
198
                echo $out;
199
        }
200
        die();
201
}
202
 
1130 daniel-mar 203
/**
204
 * @param string $str
205
 * @param array $args
206
 * @return string
207
 */
208
function my_vsprintf(string $str, array $args): string {
209
        $n = 1;
210
        foreach ($args as $val) {
211
                $str = str_replace("%$n", $val, $str);
212
                $n++;
213
        }
214
        return str_replace("%%", "%", $str);
360 daniel-mar 215
}
216
 
1130 daniel-mar 217
/**
218
 * @param string $str
219
 * @param mixed ...$sprintfArgs
220
 * @return string
221
 * @throws \ViaThinkSoft\OIDplus\OIDplusConfigInitializationException
222
 * @throws \ViaThinkSoft\OIDplus\OIDplusException
223
 */
224
function _L(string $str, ...$sprintfArgs): string {
401 daniel-mar 225
        static $translation_array = array();
226
        static $translation_loaded = null;
227
 
506 daniel-mar 228
        $str = trim($str);
229
 
1050 daniel-mar 230
        if (!class_exists(OIDplus::class)) {
438 daniel-mar 231
                return my_vsprintf($str, $sprintfArgs);
232
        }
233
 
360 daniel-mar 234
        $lang = OIDplus::getCurrentLang();
468 daniel-mar 235
        $ta = OIDplus::getTranslationArray($lang);
1162 daniel-mar 236
        $res = $ta[$lang][$str] ?? $str;
360 daniel-mar 237
 
238
        $res = str_replace('###', OIDplus::baseConfig()->getValue('TABLENAME_PREFIX', ''), $res);
239
 
1130 daniel-mar 240
        return my_vsprintf($res, $sprintfArgs);
370 daniel-mar 241
}
386 daniel-mar 242
 
1130 daniel-mar 243
/**
244
 * @param array $params
245
 * @param string $key
246
 * @return void
247
 * @throws \ViaThinkSoft\OIDplus\OIDplusConfigInitializationException
248
 * @throws \ViaThinkSoft\OIDplus\OIDplusException
249
 */
250
function _CheckParamExists(array $params, string $key) {
1050 daniel-mar 251
        if (class_exists(OIDplusException::class)) {
698 daniel-mar 252
                if (!isset($params[$key])) throw new OIDplusException(_L('Parameter %1 is missing', $key));
253
        } else {
254
                if (!isset($params[$key])) throw new Exception(_L('Parameter %1 is missing', $key));
255
        }
552 daniel-mar 256
}
257
 
1130 daniel-mar 258
/**
259
 * @param string $cont
260
 * @return array
261
 */
262
function extractHtmlContents(string $cont): array {
721 daniel-mar 263
        // make sure the program works even if the user provided HTML is not UTF-8
264
        $cont = convert_to_utf8_no_bom($cont);
265
 
386 daniel-mar 266
        $out_js = '';
267
        $m = array();
268
        preg_match_all('@<script[^>]*>(.+)</script>@ismU', $cont, $m);
269
        foreach ($m[1] as $x) {
270
                $out_js = $x . "\n\n";
271
        }
272
 
273
        $out_css = '';
274
        $m = array();
275
        preg_match_all('@<style[^>]*>(.+)</style>@ismU', $cont, $m);
276
        foreach ($m[1] as $x) {
277
                $out_css = $x . "\n\n";
278
        }
279
 
280
        $out_html = $cont;
281
        $out_html = preg_replace('@^(.+)<body[^>]*>@isU', '', $out_html);
282
        $out_html = preg_replace('@</body>.+$@isU', '', $out_html);
283
        $out_html = preg_replace('@<title>.+</title>@isU', '', $out_html);
284
        $out_html = preg_replace('@<h1>.+</h1>@isU', '', $out_html, 1);
285
        $out_html = preg_replace('@<script[^>]*>(.+)</script>@ismU', '', $out_html);
286
        $out_html = preg_replace('@<style[^>]*>(.+)</style>@ismU', '', $out_html);
287
 
288
        return array($out_html, $out_js, $out_css);
392 daniel-mar 289
}
290
 
1130 daniel-mar 291
/**
292
 * @param string $password
293
 * @param bool $raw_output
294
 * @return string
295
 * @throws Exception
296
 */
297
function sha3_512(string $password, bool $raw_output=false): string {
1103 daniel-mar 298
        if (hash_supported_natively('sha3-512')) {
400 daniel-mar 299
                return hash('sha3-512', $password, $raw_output);
392 daniel-mar 300
        } else {
1021 daniel-mar 301
                return \bb\Sha3\Sha3::hash($password, 512, $raw_output);
392 daniel-mar 302
        }
303
}
486 daniel-mar 304
 
1130 daniel-mar 305
/**
306
 * @param string $message
307
 * @param string $key
308
 * @param bool $raw_output
309
 * @return string
310
 */
311
function sha3_512_hmac(string $message, string $key, bool $raw_output=false): string {
710 daniel-mar 312
        // RFC 2104 HMAC
1103 daniel-mar 313
        if (hash_hmac_supported_natively('sha3-512')) {
847 daniel-mar 314
                return hash_hmac('sha3-512', $message, $key, $raw_output);
315
        } else {
1021 daniel-mar 316
                return \bb\Sha3\Sha3::hash_hmac($message, $key, 512, $raw_output);
847 daniel-mar 317
        }
710 daniel-mar 318
}
319
 
1130 daniel-mar 320
/**
321
 * @param string $password
322
 * @param string $salt
323
 * @param int $iterations
324
 * @param int $length
325
 * @param bool $binary
326
 * @return string
327
 */
328
function sha3_512_pbkdf2(string $password, string $salt, int $iterations, int $length=0, bool $binary=false): string {
1103 daniel-mar 329
        if (hash_pbkdf2_supported_natively('sha3-512')) {
330
                return hash_pbkdf2('sha3-512', $password, $salt, $iterations, $length, $binary);
331
        } else {
332
                return \bb\Sha3\Sha3::hash_pbkdf2($password, $salt, $iterations, 512, $length, $binary);
333
        }
334
}
335
 
1130 daniel-mar 336
/**
1181 daniel-mar 337
 * @param bool $require_ssl
338
 * @param string|null $reason
1149 daniel-mar 339
 * @return bool
1181 daniel-mar 340
 * @throws OIDplusException
341
 * @throws \ViaThinkSoft\OIDplus\OIDplusConfigInitializationException
1149 daniel-mar 342
 */
1181 daniel-mar 343
function url_post_contents_available(bool $require_ssl=true, string &$reason=null): bool {
1182 daniel-mar 344
        if (class_exists(OIDplus::class)) {
345
                if (OIDplus::baseConfig()->getValue('OFFLINE_MODE', false)) {
346
                        $reason = _L('OIDplus is running in offline mode due to the base configuration setting %1.', 'OFFLINE_MODE');
347
                        return false;
348
                }
349
        }
350
 
1181 daniel-mar 351
        if (function_exists('curl_init')) {
352
                return true;
353
        } else {
354
                $reason = _L('Please install the PHP extension %1, so that OIDplus can connect to the Internet.', '<code>php_curl</code>');
355
                return false;
356
        }
1149 daniel-mar 357
}
358
 
359
/**
1130 daniel-mar 360
 * @param string $url
1149 daniel-mar 361
 * @param array $params
362
 * @param array $extraHeaders
1130 daniel-mar 363
 * @param string $userAgent
364
 * @return string|false
1149 daniel-mar 365
 * @throws \ViaThinkSoft\OIDplus\OIDplusException
1130 daniel-mar 366
 */
1149 daniel-mar 367
function url_post_contents(string $url, array $params=array(), array $extraHeaders=array(), string $userAgent='ViaThinkSoft-OIDplus/2.0') {
1182 daniel-mar 368
        $require_ssl = str_starts_with(strtolower($url),'https:');
369
        if (!url_post_contents_available($require_ssl, $reason)) {
370
                throw new OIDplusException(_L('This feature is not available, because OIDplus cannot connect to the Internet.').' '.$reason);
371
        }
372
 
1149 daniel-mar 373
        $postFields = http_build_query($params);
374
 
375
        $headers = array(
376
                "User-Agent: $userAgent",
377
                "Content-Length: ".strlen($postFields)
378
        );
379
 
380
        foreach ($extraHeaders as $name => $val) {
381
                $headers[] = "$name: $val";
382
        }
383
 
653 daniel-mar 384
        if (function_exists('curl_init')) {
385
                $ch = curl_init();
1050 daniel-mar 386
                if (class_exists(OIDplus::class)) {
698 daniel-mar 387
                        if (ini_get('curl.cainfo') == '') curl_setopt($ch, CURLOPT_CAINFO, OIDplus::localpath() . 'vendor/cacert.pem');
388
                }
653 daniel-mar 389
                curl_setopt($ch, CURLOPT_URL, $url);
963 daniel-mar 390
                curl_setopt($ch, CURLOPT_USERAGENT, $userAgent);
1149 daniel-mar 391
                curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
392
                curl_setopt($ch, CURLOPT_POST, true);
393
                curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
653 daniel-mar 394
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
395
                curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
396
                curl_setopt($ch, CURLOPT_AUTOREFERER, true);
1006 daniel-mar 397
                $res = @curl_exec($ch);
963 daniel-mar 398
                $error_code = @curl_getinfo($ch, CURLINFO_HTTP_CODE);
1006 daniel-mar 399
                @curl_close($ch);
963 daniel-mar 400
                if ($error_code >= 400) return false;
1006 daniel-mar 401
                if ($res === false) return false;
653 daniel-mar 402
        } else {
1182 daniel-mar 403
                $res = false;
404
                assert(false);
1149 daniel-mar 405
        }
1182 daniel-mar 406
 
1149 daniel-mar 407
        return $res;
408
}
409
 
410
/**
1181 daniel-mar 411
 * @param bool $require_ssl
412
 * @param string|null $reason
413
 * @return bool
414
 * @throws OIDplusException
415
 * @throws \ViaThinkSoft\OIDplus\OIDplusConfigInitializationException
416
 */
417
function url_get_contents_available(bool $require_ssl=true, string &$reason=null): bool {
1182 daniel-mar 418
        if (class_exists(OIDplus::class)) {
419
                if (OIDplus::baseConfig()->getValue('OFFLINE_MODE', false)) {
420
                        $reason = _L('OIDplus is running in offline mode due to the base configuration setting %1.', 'OFFLINE_MODE');
421
                        return false;
422
                }
423
        }
424
 
1181 daniel-mar 425
        if (function_exists('curl_init')) {
426
                // Via cURL
427
                return true;
428
        } else {
429
                // Via file_get_contents()
430
                if (!ini_get('allow_url_fopen')) {
431
                        $reason = _L('Please install the PHP extension %1 and/or enable %2 in your PHP configuration, so that OIDplus can connect to the Internet.', '<code>php_curl</code>', '<code>allow_url_fopen</code>');
432
                        return false;
433
                }
434
                // Use extension_loaded() instead of function_exists(), because our supplement does not help...
435
                if ($require_ssl && !extension_loaded('openssl')) {
436
                        $reason = _L('Please install the PHP extension %1 and/or %2, so that OIDplus can connect to the Internet.', '<code>php_curl</code>', '<code>php_openssl</code>');
437
                        return false;
438
                }
439
                return true;
440
        }
441
}
442
 
443
/**
1149 daniel-mar 444
 * @param string $url
445
 * @param array $extraHeaders
446
 * @param string $userAgent
447
 * @return string|false
448
 */
449
function url_get_contents(string $url, array $extraHeaders=array(), string $userAgent='ViaThinkSoft-OIDplus/2.0') {
1182 daniel-mar 450
        $require_ssl = str_starts_with(strtolower($url),'https:');
451
        if (!url_get_contents_available($require_ssl, $reason)) {
452
                throw new OIDplusException(_L('This feature is not available, because OIDplus cannot connect to the Internet.').' '.$reason);
453
        }
454
 
1149 daniel-mar 455
        $headers = array("User-Agent: $userAgent");
456
        foreach ($extraHeaders as $name => $val) {
457
                $headers[] = "$name: $val";
458
        }
459
        if (function_exists('curl_init')) {
460
                $ch = curl_init();
461
                if (class_exists(OIDplus::class)) {
462
                        if (ini_get('curl.cainfo') == '') curl_setopt($ch, CURLOPT_CAINFO, OIDplus::localpath() . 'vendor/cacert.pem');
463
                }
464
                curl_setopt($ch, CURLOPT_URL, $url);
465
                curl_setopt($ch, CURLOPT_USERAGENT, $userAgent);
466
                curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
467
                curl_setopt($ch, CURLOPT_POST, false);
468
                curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
469
                curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
470
                curl_setopt($ch, CURLOPT_AUTOREFERER, true);
471
                $res = @curl_exec($ch);
472
                $error_code = @curl_getinfo($ch, CURLINFO_HTTP_CODE);
473
                @curl_close($ch);
474
                if ($error_code >= 400) return false;
475
                if ($res === false) return false;
476
        } else {
963 daniel-mar 477
                // Attention: HTTPS only works if OpenSSL extension is enabled.
478
                // Our supplement does not help...
716 daniel-mar 479
                $opts = [
480
                        "http" => [
481
                                "method" => "GET",
1149 daniel-mar 482
                                "header" => implode("\r\n",$headers)."\r\n"
716 daniel-mar 483
                        ]
484
                ];
485
                $context = stream_context_create($opts);
486
                $res = @file_get_contents($url, false, $context);
653 daniel-mar 487
                if ($res === false) return false;
488
        }
489
        return $res;
660 daniel-mar 490
}
778 daniel-mar 491
 
1130 daniel-mar 492
/**
1148 daniel-mar 493
* @param array &$rows
494
* @param string $fieldName
495
* @return void
496
*/
1158 daniel-mar 497
function natsort_field(array &$rows, string $fieldName) {
1148 daniel-mar 498
        usort($rows, function($a,$b) use($fieldName) {
499
                if ($a[$fieldName] == $b[$fieldName]) return 0; // equal
500
                $ary = array(
501
                        -1 => $a[$fieldName],
502
                        1 => $b[$fieldName]
503
                );
1150 daniel-mar 504
                natsort($ary);
1148 daniel-mar 505
                $keys = array_keys($ary);
506
                return $keys[0];
507
        });
508
}
1156 daniel-mar 509
 
510
/**
511
 * @param array $ary
512
 * @return \stdClass
513
 */
514
function array_to_stdobj(array $ary): \stdClass {
515
        $obj = new \stdClass;
516
        foreach ($ary as $name => $val) {
517
                $obj->$name = $val;
518
        }
519
        return $obj;
520
}
521
 
522
/**
523
 * @param \stdClass $obj
524
 * @return array
525
 */
526
function stdobj_to_array(\stdClass $obj): array {
527
        $ary = array();
1190 daniel-mar 528
        foreach ($obj as $name => $val) { /* @phpstan-ignore-line */
1156 daniel-mar 529
                $ary[$name] = $val;
530
        }
531
        return $ary;
532
}
1180 daniel-mar 533
 
534
/**
535
 * @return string|false
536
 */
537
function get_own_username() {
538
        $current_user = exec('whoami');
539
        if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
540
                try {
541
                        if (function_exists('mb_convert_encoding')) {
542
                                $current_user = @mb_convert_encoding($current_user, "UTF-8", "cp850");
543
                        } else if (function_exists('iconv')) {
544
                                $current_user = @iconv("cp850", "UTF-8", $current_user);
545
                        }
546
                } catch (\Exception $e) {}
547
                if (function_exists('mb_strtoupper')) {
548
                        $current_user = mb_strtoupper($current_user); // just cosmetics
549
                }
550
        }
551
        if (!$current_user) {
552
                if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
553
                        // Windows on an IIS server:
554
                        //     getenv('USERNAME')     MARSCHALL$                (That is the "machine account", see https://docs.microsoft.com/en-us/iis/manage/configuring-security/application-pool-identities#accessing-the-network )
555
                        //     get_current_user()     DefaultAppPool
556
                        //     exec('whoami')         iis apppool\defaultapppool
557
                        // Windows with XAMPP:
558
                        //     getenv('USERNAME')     dmarschall
559
                        //     get_current_user()     dmarschall               (even if script has a different NTFS owner!)
560
                        //     exec('whoami')         hickelsoft\dmarschall
561
                        $current_user = get_current_user();
562
                        if (!$current_user) {
563
                                $current_user = getenv('USERNAME');
564
                                $current_user = mb_strtoupper($current_user); // just cosmetics
565
                        }
566
                } else {
567
                        // On Linux:
568
                        $current_user = exec('id -un');
569
                        if (!$current_user) {
570
                                // PHP'S get_current_user() will get the owner of the PHP script, not the process owner!
571
                                // We want the process owner, so we use posix_geteuid() preferably.
572
                                if (function_exists('posix_geteuid')) {
573
                                        $uid = posix_geteuid();
574
                                } else {
575
                                        $temp_file = tempnam(sys_get_temp_dir(), 'TMP');
576
                                        if ($temp_file !== false) {
577
                                                $uid = fileowner($temp_file);
578
                                                if ($uid === false) $uid = -1;
579
                                                @unlink($temp_file);
580
                                        } else {
581
                                                $uid = -1;
582
                                        }
583
                                }
584
                                if ($uid >= 0) {
585
                                        $current_user = '#' . $uid;
586
                                        if (function_exists('posix_getpwuid')) {
587
                                                $userinfo = posix_getpwuid($uid); // receive username from the UID (requires read access to /etc/passwd)
588
                                                if ($userinfo !== false) $current_user = $userinfo['name'];
589
                                        }
590
                                } else {
591
                                        $current_user = get_current_user();
592
                                }
593
                        }
594
                }
595
        }
596
        return $current_user ?: false;
597
}
598
 
599
/**
600
 * @param string $path
601
 * @return bool
602
 */
603
function isFileOrPathWritable(string $path): bool {
604
        if ($writable_file = (file_exists($path) && is_writable($path))) return true;
605
        if ($writable_directory = (!file_exists($path) && is_writable(dirname($path)))) return true;
606
        return false;
1190 daniel-mar 607
}
1219 daniel-mar 608
 
609
/**
610
 * @param string $html
611
 * @return string
612
 */
613
function html_to_text(string $html): string {
614
        $html = str_replace("\n", "", $html);
615
        $html = str_ireplace('<br', "\n<br", $html);
616
        $html = str_ireplace('<p', "\n\n<p", $html);
617
        $html = strip_tags($html);
618
        $html = html_entity_decode($html, ENT_QUOTES, 'UTF-8');
619
        return $html;
620
}
1265 daniel-mar 621
 
622
/**
623
 * Get header Authorization
624
 * @see https://stackoverflow.com/questions/40582161/how-to-properly-use-bearer-tokens
625
 **/
626
function getAuthorizationHeader(){
627
    $headers = null;
628
    if (isset($_SERVER['Authorization'])) {
629
        $headers = trim($_SERVER["Authorization"]);
630
    }
631
    else if (isset($_SERVER['HTTP_AUTHORIZATION'])) { //Nginx or fast CGI
632
        $headers = trim($_SERVER["HTTP_AUTHORIZATION"]);
633
    } elseif (function_exists('apache_request_headers')) {
634
        $requestHeaders = apache_request_headers();
635
        // Server-side fix for bug in old Android versions (a nice side-effect of this fix means we don't care about capitalization for Authorization)
636
        $requestHeaders = array_combine(array_map('ucwords', array_keys($requestHeaders)), array_values($requestHeaders));
637
        //print_r($requestHeaders);
638
        if (isset($requestHeaders['Authorization'])) {
639
            $headers = trim($requestHeaders['Authorization']);
640
        }
641
    }
642
    return $headers;
643
}
644
 
645
/**
646
 * get access token from header
647
 * @see https://stackoverflow.com/questions/40582161/how-to-properly-use-bearer-tokens
648
 **/
649
function getBearerToken() {
650
    $headers = getAuthorizationHeader();
651
    // HEADER: Get the access token from the header
652
    if (!empty($headers)) {
653
        if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) {
654
            return $matches[1];
655
        }
656
    }
657
    return null;
658
}
1276 daniel-mar 659
 
660
/**
661
 * @param array $struct
662
 * @return string
663
 */
664
function array_to_html_ul_li(array $struct): string {
665
        $res = '';
666
        $res .= '<ul>';
667
        foreach ($struct as $name => $val) {
668
                $res .= '<li>';
669
                if (is_array($val)) {
670
                        $res .= $name . array_to_html_ul_li($val);
671
                } else {
672
                        $res .= $val;
673
                }
674
                $res .= '</li>';
675
        }
676
        $res .= '</ul>';
677
        return $res;
678
}
1294 daniel-mar 679
 
680
/**
681
 * @param mixed $mixed
682
 * @return bool
683
 */
684
function oidplus_is_true($mixed): bool {
685
        if (is_null($mixed)) {
686
                return false;
687
        } else if (is_string($mixed)) {
688
                return (strtolower($mixed) == 'true') || ($mixed == '1') || (strtolower($mixed) == 'y') || (strtolower($mixed) == 't');
689
        } else if (is_bool($mixed)) {
690
                return $mixed;
691
        } else if (is_numeric($mixed)) {
692
                return $mixed != 0;
693
        } else {
694
                return $mixed ? true : false; // let PHP decide...
695
        }
696
}