Subversion Repositories oidplus

Rev

Rev 1181 | Rev 1190 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

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