Bearsampp 2026.7.11
Loading...
Searching...
No Matches
class.util.php
Go to the documentation of this file.
1<?php
2/*
3 *
4 * * Copyright (c) 2022-2025 Bearsampp
5 * * License: GNU General Public License version 3 or later; see LICENSE.txt
6 * * Website: https://bearsampp.com
7 * * Github: https://github.com/Bearsampp
8 *
9 */
10
35class Util
36{
37
46 public static function clearFolders($paths, $exclude = array())
47 {
48 $result = array();
49 foreach ($paths as $path) {
50 $result[$path] = self::clearFolder($path, $exclude);
51 }
52
53 return $result;
54 }
55
64 public static function clearFolder($path, $exclude = array())
65 {
66 $result = array();
67 $result['return'] = true;
68 $result['nb_files'] = 0;
69
70 $handle = @opendir($path);
71 if (!$handle) {
72 return null;
73 }
74
75 while (false !== ($file = readdir($handle))) {
76 if ($file == '.' || $file == '..' || in_array($file, $exclude)) {
77 continue;
78 }
79 if (is_dir($path . '/' . $file)) {
80 $r = self::clearFolder($path . '/' . $file);
81 if (!$r) {
82 $result['return'] = false;
83
84 return $result;
85 }
86 } else {
87 $r = @unlink($path . '/' . $file);
88 if ($r) {
89 $result['nb_files']++;
90 } else {
91 $result['return'] = false;
92
93 return $result;
94 }
95 }
96 }
97
98 closedir($handle);
99
100 return $result;
101 }
102
108 public static function deleteFolder($path)
109 {
110 if (is_dir($path)) {
111 $path = rtrim($path, '/\\') . '/';
112 $files = glob($path . '*', GLOB_MARK);
113
114 if ($files === false) {
115 Log::error("deleteFolder(): Failed to glob path: " . $path);
116 return;
117 }
118
119 foreach ($files as $file) {
120 $normalizedFile = rtrim($file, '/\\');
121
122 if (is_link($normalizedFile)) {
123 if (!@unlink($normalizedFile) && !@rmdir($normalizedFile)) {
124 Log::error("deleteFolder(): Failed to unlink symlink: " . $normalizedFile);
125 }
126 } elseif (is_dir($file)) {
127 self::deleteFolder($file);
128 } else {
129 if (!@unlink($normalizedFile)) {
130 Log::error("deleteFolder(): Failed to unlink file: " . $normalizedFile);
131 }
132 }
133 }
134
135 if (!@rmdir($path)) {
136 // Only log error if directory still exists (might have been deleted by recursion)
137 if (is_dir($path)) {
138 Log::error("deleteFolder(): Failed to remove directory: " . $path);
139 }
140 }
141 }
142 }
143
152 public static function findFile($startPath, $findFile)
153 {
154 $result = false;
155
156 $handle = @opendir($startPath);
157 if (!$handle) {
158 return false;
159 }
160
161 while (false !== ($file = readdir($handle))) {
162 if ($file == '.' || $file == '..') {
163 continue;
164 }
165 if (is_dir($startPath . '/' . $file)) {
166 $result = self::findFile($startPath . '/' . $file, $findFile);
167 if ($result !== false) {
168 break;
169 }
170 } elseif ($file == $findFile) {
171 $result = Path::formatUnixPath($startPath . '/' . $file);
172 break;
173 }
174 }
175
176 closedir($handle);
177
178 return $result;
179 }
180
188 public static function isValidIp($ip)
189 {
190 return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)
191 || filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
192 }
193
201 public static function isValidPort($port)
202 {
203 return is_numeric($port) && ($port > 0 && $port <= 65535);
204 }
205
212 public static function isAdmin()
213 {
214 // Only applicable on Windows
215 if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
216 // On non-Windows systems, check if running as root
217 if (function_exists('posix_geteuid')) {
218 return posix_geteuid() === 0;
219 }
220 // If we can't determine on non-Windows, assume true to avoid blocking
221 return true;
222 }
223
224 // Method 1: Try using shell_exec with 'net session' command
225 // This command only succeeds when run with admin privileges
226 $output = CommandRunner::shellExec('net session 2>&1');
227 if ($output !== null) {
228 // Check for access denied errors
229 if (stripos($output, 'Access is denied') !== false ||
230 stripos($output, 'System error 5') !== false ||
231 stripos($output, 'Zugriff verweigert') !== false) { // German
232 // Explicitly denied - not admin
233 return false;
234 }
235
236 // If we got output without errors, we likely have admin rights
237 if (stripos($output, 'There are no entries') !== false ||
238 stripos($output, 'These workstations') !== false ||
239 preg_match('/\\\\\\\\/', $output)) {
240 return true;
241 }
242 }
243
244 // Method 2: Check using whoami command (Windows Vista and later)
245 $output = CommandRunner::shellExec('whoami /groups 2>&1');
246 if ($output !== null && !empty($output)) {
247 // Look for the Administrators group or High Mandatory Level
248 if (stripos($output, 'S-1-16-12288') !== false || // High Mandatory Level
249 stripos($output, 'S-1-5-32-544') !== false) { // Administrators group
250 return true;
251 }
252
253 // If we got output but no admin indicators, we're not admin
254 if (stripos($output, 'S-1-16-8192') !== false) { // Medium Mandatory Level (not admin)
255 return false;
256 }
257 }
258
259 // Method 3: Try to write to a system directory
260 // This is a fallback method that checks if we can write to Windows directory
261 $testFile = getenv('SystemRoot') . '\\Temp\\bearsampp_admin_test_' . uniqid() . '.tmp';
262 $result = @file_put_contents($testFile, 'test');
263 if ($result !== false) {
264 @unlink($testFile);
265 return true;
266 }
267
268 // If all methods fail or indicate no admin, return false
269 return false;
270 }
271
279 public static function replaceDefine($path, $var, $value)
280 {
281 self::replaceInFile($path, array(
282 '/^define\‍((.*?)' . $var . '(.*?),/' => 'define(\'' . $var . '\', ' . (is_int($value) ? $value : '\'' . $value . '\'') . ');'
283 ));
284 }
285
292 public static function replaceInFile($path, $replaceList)
293 {
294 if (file_exists($path)) {
295 $lines = file($path);
296 $fp = fopen($path, 'w');
297 foreach ($lines as $nb => $line) {
298 $replaceDone = false;
299 foreach ($replaceList as $regex => $replace) {
300 if (preg_match($regex, $line, $matches)) {
301 $currentReplace = $replace;
302 $countParams = preg_match_all('/{{(\d+)}}/', $currentReplace, $paramsMatches);
303 if ($countParams > 0 && $countParams <= count($matches)) {
304 foreach ($paramsMatches[1] as $paramsMatch) {
305 $currentReplace = str_replace('{{' . $paramsMatch . '}}', $matches[$paramsMatch], $currentReplace);
306 }
307 }
308 Log::trace('Replace in file ' . $path . ' :');
309 Log::trace('## line_num: ' . trim($nb));
310 Log::trace('## old: ' . trim($line));
311 Log::trace('## new: ' . trim($currentReplace));
312
313 // Preserve original line ending if present in $line
314 $ending = (preg_match("/\r\n$/", $line)) ? "\r\n" : (preg_match("/\n$/", $line) ? "\n" : "");
315 fwrite($fp, rtrim($currentReplace) . $ending);
316
317 $replaceDone = true;
318 break;
319 }
320 }
321 if (!$replaceDone) {
322 fwrite($fp, $line);
323 }
324 }
325 fclose($fp);
326 }
327 }
328
337 public static function getVersionList($path)
338 {
339 $result = array();
340
341 $handle = @opendir($path);
342 if (!$handle) {
343 return false;
344 }
345
346 $prefix = basename($path);
347
348 while (false !== ($file = readdir($handle))) {
349 $filePath = $path . '/' . $file;
350 if ($file != '.' && $file != '..' && is_dir($filePath) && $file != 'current') {
351 if (strpos($file, $prefix) === 0) {
352 $version = substr($file, strlen($prefix));
353 } else {
354 $version = $file;
355 }
356 $result[] = $version;
357 }
358 }
359
360 closedir($handle);
361 natcasesort($result);
362
363 return $result;
364 }
365
371 public static function getMicrotime()
372 {
373 list($usec, $sec) = explode(' ', microtime());
374
375 return ((float)$usec + (float)$sec);
376 }
377
378
379
385 public static function isLaunchStartup()
386 {
388 return $lnk ? file_exists($lnk) : false;
389 }
390
396 public static function enableLaunchStartup()
397 {
399
400 $shortcutPath = Path::getStartupLnkPath();
401 if (!$shortcutPath) {
402 return false;
403 }
404
405 $targetPath = Path::getExeFilePath();
406 $workingDir = Path::getRootPath();
407 $description = APP_TITLE . ' ' . $bearsamppCore->getAppVersion();
408 $iconPath = Path::getIconsPath() . '/app.ico';
409
410 return Win32Native::createShortcut($shortcutPath, $targetPath, $workingDir, $description, $iconPath);
411 }
412
418 public static function disableLaunchStartup()
419 {
420 $startupLnkPath = Path::getStartupLnkPath();
421
422 // Check if file exists before attempting to delete
423 if (file_exists($startupLnkPath)) {
424 return @unlink($startupLnkPath);
425 }
426
427 // Return true if the file doesn't exist (already disabled)
428 return true;
429 }
430
431
442 public static function findRepos($initPath, $startPath, $checkFile, $maxDepth = 1)
443 {
444 $depth = substr_count(str_replace($initPath, '', $startPath), '/');
445 $result = array();
446
447 $handle = @opendir($startPath);
448 if (!$handle) {
449 return $result;
450 }
451
452 while (false !== ($file = readdir($handle))) {
453 if ($file == '.' || $file == '..') {
454 continue;
455 }
456 if (is_dir($startPath . '/' . $file) && ($initPath == $startPath || $depth <= $maxDepth)) {
457 $tmpResults = self::findRepos($initPath, $startPath . '/' . $file, $checkFile, $maxDepth);
458 foreach ($tmpResults as $tmpResult) {
459 $result[] = $tmpResult;
460 }
461 } elseif (is_file($startPath . '/' . $checkFile) && !in_array($startPath, $result)) {
462 $result[] = Path::formatUnixPath($startPath);
463 }
464 }
465
466 closedir($handle);
467
468 return $result;
469 }
470
478 public static function imgToBase64($path)
479 {
480 $type = pathinfo($path, PATHINFO_EXTENSION);
481 $data = file_get_contents($path);
482
483 return 'data:image/' . $type . ';base64,' . base64_encode($data);
484 }
485
494 public static function convertEncoding($data, $direction = 'to_cp1252')
495 {
496 if ($direction === 'to_utf8') {
497 return self::cp1252ToUtf8($data);
498 } else {
499 return self::utf8ToCp1252($data);
500 }
501 }
502
510 public static function utf8ToCp1252($data)
511 {
512 return iconv('UTF-8', 'WINDOWS-1252//IGNORE', $data);
513 }
514
522 public static function cp1252ToUtf8($data)
523 {
524 return iconv('WINDOWS-1252', 'UTF-8//IGNORE', $data);
525 }
526
530 public static function startLoading()
531 {
532 global $bearsamppCore, $bearsamppWinbinder;
533
534 Log::trace('startLoading() called');
535 Log::trace('PHP executable: ' . Path::getPhpExe());
536 Log::trace('Root file: ' . Core::isRoot_FILE);
537 Log::trace('Action: ' . Action::LOADING);
538
539 Log::trace('Executing command: ' . Path::getPhpExe() . ' ' . Core::isRoot_FILE . ' ' . Action::LOADING);
540
541 $result = $bearsamppWinbinder->exec(Path::getPhpExe(), [Core::isRoot_FILE, Action::LOADING], true, false);
542 Log::trace('exec() returned: ' . var_export($result, true));
543
544 Log::trace('startLoading() completed');
545 }
546
550 public static function stopLoading()
551 {
552 global $bearsamppCore;
553 if (file_exists($bearsamppCore->getLoadingPid())) {
554 $pids = file($bearsamppCore->getLoadingPid());
555 foreach ($pids as $pid) {
556 Win32Ps::kill($pid);
557 }
558 @unlink($bearsamppCore->getLoadingPid());
559 }
560
561 // Clean up status file
563 }
564
571 public static function updateLoadingText($text)
572 {
573 global $bearsamppCore;
574
575 $statusFile = Path::getTmpPath() . '/loading_status.txt';
576 file_put_contents($statusFile, json_encode(['text' => $text]));
577 }
578
582 public static function clearLoadingText()
583 {
584 global $bearsamppCore;
585
586 $statusFile = Path::getTmpPath() . '/loading_status.txt';
587 if (file_exists($statusFile)) {
588 @unlink($statusFile);
589 }
590 }
591
602 public static function getFilesToScan($path = null, $useCache = true, $forceRefresh = false)
603 {
604 // Generate cache key based on path parameter
605 $cacheKey = md5(serialize($path));
606
607 // Try to get from cache if enabled and not forcing refresh
608 if ($useCache && !$forceRefresh) {
609 $cachedResult = Cache::get($cacheKey);
610 if ($cachedResult !== false) {
612 Log::debug('File scan cache HIT (saved expensive scan operation)');
613 return $cachedResult;
614 }
615 }
616
618 Log::debug('File scan cache MISS (performing full scan)');
619
620 // Perform the actual scan
621 $startTime = self::getMicrotime();
622 $result = array();
623 $pathsToScan = !empty($path) ? $path : self::getPathsToScan();
624
625 foreach ($pathsToScan as $pathToScan) {
626 $pathStartTime = self::getMicrotime();
627 $findFiles = self::findFiles($pathToScan['path'], $pathToScan['includes'], $pathToScan['recursive']);
628 foreach ($findFiles as $findFile) {
629 $result[] = $findFile;
630 }
631 Log::debug($pathToScan['path'] . ' scanned in ' . round(self::getMicrotime() - $pathStartTime, 3) . 's');
632 }
633
634 $totalTime = round(self::getMicrotime() - $startTime, 3);
635 Log::info('Full file scan completed in ' . $totalTime . 's (' . count($result) . ' files found)');
636
637 // Store in cache if enabled
638 if ($useCache) {
639 Cache::set($cacheKey, $result);
640 }
641
642 return $result;
643 }
644
645
646
671 private static function getPathsToScan()
672 {
673 global $bearsamppRoot, $bearsamppCore, $bearsamppBins, $bearsamppApps, $bearsamppTools;
674 $paths = array();
675
676 // Alias
677 $paths[] = array(
678 'path' => Path::getAliasPath(),
679 'includes' => array(''),
680 'recursive' => false
681 );
682
683 // Vhosts
684 $paths[] = array(
685 'path' => Path::getVhostsPath(),
686 'includes' => array(''),
687 'recursive' => false
688 );
689
690 // OpenSSL
691 $paths[] = array(
692 'path' => Path::getOpenSslPath(),
693 'includes' => array('openssl.cfg'),
694 'recursive' => false
695 );
696
697 // Homepage
698 $paths[] = array(
699 'path' => Path::getResourcesPath() . '/homepage',
700 'includes' => array('alias.conf'),
701 'recursive' => false
702 );
703
704 // Apache
706 foreach ($folderList as $folder) {
707 $paths[] = array(
708 'path' => Path::getModuleRootPath($bearsamppBins->getApache()) . '/' . $folder,
709 'includes' => array('.ini', '.conf'),
710 'recursive' => true
711 );
712 }
713
714 // PHP
716 foreach ($folderList as $folder) {
717 $paths[] = array(
718 'path' => Path::getModuleRootPath($bearsamppBins->getPhp()) . '/' . $folder,
719 'includes' => array('.php', '.bat', '.ini', '.reg', '.inc'),
720 'recursive' => true
721 );
722 }
723
724 // MySQL
726 foreach ($folderList as $folder) {
727 $paths[] = array(
728 'path' => Path::getModuleRootPath($bearsamppBins->getMysql()) . '/' . $folder,
729 'includes' => array('my.ini'),
730 'recursive' => false
731 );
732 }
733
734 // MariaDB
735 $folderList = self::getFolderList(Path::getModuleRootPath($bearsamppBins->getMariadb()));
736 foreach ($folderList as $folder) {
737 $paths[] = array(
738 'path' => Path::getModuleRootPath($bearsamppBins->getMariadb()) . '/' . $folder,
739 'includes' => array('my.ini'),
740 'recursive' => false
741 );
742 // Also scan data directory for my.ini (created during initialization)
743 $dataPath = Path::getModuleRootPath($bearsamppBins->getMariadb()) . '/' . $folder . '/data';
744 if (is_dir($dataPath)) {
745 $paths[] = array(
746 'path' => $dataPath,
747 'includes' => array('my.ini'),
748 'recursive' => false
749 );
750 }
751 }
752
753 // PostgreSQL
754 $folderList = self::getFolderList(Path::getModuleRootPath($bearsamppBins->getPostgresql()));
755 foreach ($folderList as $folder) {
756 $paths[] = array(
757 'path' => Path::getModuleRootPath($bearsamppBins->getPostgresql()) . '/' . $folder,
758 'includes' => array( '.conf', '.bat', '.ber'),
759 'recursive' => true
760 );
761 }
762
763 // Node.js
765 foreach ($folderList as $folder) {
766 $paths[] = array(
767 'path' => Path::getModuleRootPath($bearsamppBins->getNodejs()) . '/' . $folder . '/etc',
768 'includes' => array('npmrc'),
769 'recursive' => true
770 );
771 $paths[] = array(
772 'path' => Path::getModuleRootPath($bearsamppBins->getNodejs()) . '/' . $folder . '/node_modules/npm',
773 'includes' => array('npmrc'),
774 'recursive' => false
775 );
776 }
777
778 // Composer
779 $folderList = self::getFolderList(Path::getModuleRootPath($bearsamppTools->getComposer()));
780 foreach ($folderList as $folder) {
781 $paths[] = array(
782 'path' => Path::getModuleRootPath($bearsamppTools->getComposer()) . '/' . $folder,
783 'includes' => array('giscus.json'),
784 'recursive' => false
785 );
786 }
787
788 // PowerShell
789 $folderList = self::getFolderList(Path::getModuleRootPath($bearsamppTools->getPowerShell()));
790 foreach ($folderList as $folder) {
791 $paths[] = array(
792 'path' => Path::getModuleRootPath($bearsamppTools->getPowerShell()) . '/' . $folder,
793 'includes' => array('console.xml', '.ini', '.btm'),
794 'recursive' => true
795 );
796 }
797
798 // Python
799 $folderList = self::getFolderList(Path::getModuleRootPath($bearsamppTools->getPython()));
800 foreach ($folderList as $folder) {
801 $paths[] = array(
802 'path' => Path::getModuleRootPath($bearsamppTools->getPython()) . '/' . $folder . '/bin',
803 'includes' => array('.bat'),
804 'recursive' => false
805 );
806 $paths[] = array(
807 'path' => Path::getModuleRootPath($bearsamppTools->getPython()) . '/' . $folder . '/settings',
808 'includes' => array('winpython.ini'),
809 'recursive' => false
810 );
811 }
812
813 // Ruby
814 $folderList = self::getFolderList(Path::getModuleRootPath($bearsamppTools->getRuby()));
815 foreach ($folderList as $folder) {
816 $paths[] = array(
817 'path' => Path::getModuleRootPath($bearsamppTools->getRuby()) . '/' . $folder . '/bin',
818 'includes' => array('!.dll', '!.exe'),
819 'recursive' => false
820 );
821 }
822
823 return $paths;
824 }
825
835 private static function findFiles($startPath, $includes = array(''), $recursive = true)
836 {
837 $result = array();
838
839 $handle = @opendir($startPath);
840 if (!$handle) {
841 return $result;
842 }
843
844 while (false !== ($file = readdir($handle))) {
845 if ($file == '.' || $file == '..') {
846 continue;
847 }
848 if (is_dir($startPath . '/' . $file) && $recursive) {
849 $tmpResults = self::findFiles($startPath . '/' . $file, $includes);
850 foreach ($tmpResults as $tmpResult) {
851 $result[] = $tmpResult;
852 }
853 } elseif (is_file($startPath . '/' . $file)) {
854 foreach ($includes as $include) {
855 if (UtilString::startWith($include, '!')) {
856 $include = ltrim($include, '!');
857 if (UtilString::startWith($file, '.') && !UtilString::endWith($file, $include)) {
858 $result[] = Path::formatUnixPath($startPath . '/' . $file);
859 } elseif ($file != $include) {
860 $result[] = Path::formatUnixPath($startPath . '/' . $file);
861 }
862 } elseif (UtilString::endWith($file, $include) || $file == $include || empty($include)) {
863 $result[] = Path::formatUnixPath($startPath . '/' . $file);
864 }
865 }
866 }
867 }
868
869 closedir($handle);
870
871 return $result;
872 }
873
874
882 public static function getLatestVersion($url)
883 {
885 if (empty($result)) {
886 Log::error('Cannot retrieve latest github info for: ' . $result . ' RESULT');
887
888 return null;
889 }
890
891 $resultArray = json_decode($result, true);
892 if (isset($resultArray['tag_name']) && isset($resultArray['assets'][0]['browser_download_url'])) {
893 $tagName = $resultArray['tag_name'];
894 $downloadUrl = $resultArray['assets'][0]['browser_download_url'];
895 $name = $resultArray['name'];
896 Log::debug('Latest version tag name: ' . $tagName);
897 Log::debug('Download URL: ' . $downloadUrl);
898 Log::debug('Name: ' . $name);
899
900 return ['version' => $tagName, 'html_url' => $downloadUrl, 'name' => $name];
901 } else {
902 Log::error('Tag name, download URL, or name not found in the response: ' . $result);
903
904 return null;
905 }
906 }
907
917 public static function getWebsiteUrl($path = '', $fragment = '', $utmSource = true)
918 {
919 global $bearsamppCore;
920
921 $url = APP_WEBSITE;
922 if (!empty($path)) {
923 $url .= '/' . ltrim($path, '/');
924 }
925 if ($utmSource) {
926 $url = rtrim($url, '/') . '/?utm_source=bearsampp-' . $bearsamppCore->getAppVersion();
927 }
928 if (!empty($fragment)) {
929 $url .= $fragment;
930 }
931
932 return $url;
933 }
934
943 public static function getWebsiteUrlNoUtm($path = '', $fragment = '')
944 {
945 return self::getWebsiteUrl($path, $fragment, false);
946 }
947
956 public static function getRemoteFilesize($url, $humanFileSize = true)
957 {
958 $size = 0;
959
960 $data = get_headers($url, true);
961 if (isset($data['Content-Length'])) {
962 $size = intval($data['Content-Length']);
963 }
964
965 return $humanFileSize ? self::humanFileSize($size) : $size;
966 }
967
979 public static function humanFileSize(int $size, string $unit = ''): string
980 {
981 // Forced unit mode
982 if ($unit !== '') {
983 return match ($unit) {
984 'GB' => number_format($size / (1 << 30), 2) . 'GB',
985 'MB' => number_format($size / (1 << 20), 2) . 'MB',
986 'KB' => number_format($size / (1 << 10), 2) . 'KB',
987 default => number_format($size) . ' bytes',
988 };
989 }
990
991 // Native PHP 8.3+ auto-selection
992 return human_readable_size($size, precision: 2);
993 }
994
1000 public static function is32BitsOs()
1001 {
1002 global $bearsamppRegistry;
1003 $processor = $bearsamppRegistry->getProcessorRegKey();
1004
1005 return UtilString::contains($processor, 'x86');
1006 }
1007
1015 public static function getHttpHeaders($pingUrl)
1016 {
1017 if (function_exists('curl_version')) {
1019 } else {
1021 }
1022
1023 if (!empty($result)) {
1024 $rebuildResult = array();
1025 foreach ($result as $row) {
1026 $row = trim($row);
1027 if (!empty($row)) {
1028 $rebuildResult[] = $row;
1029 }
1030 }
1031 $result = $rebuildResult;
1032
1033 Log::debug('getHttpHeaders:');
1034 foreach ($result as $header) {
1035 Log::debug('-> ' . $header);
1036 }
1037 }
1038
1039 return $result;
1040 }
1041
1053 public static function getFopenHttpHeaders($url)
1054 {
1055 $result = array();
1056
1057 $context = stream_context_create(array(
1058 'ssl' => array(
1059 'verify_peer' => false,
1060 'verify_peer_name' => false,
1061 'allow_self_signed' => true,
1062 )
1063 ));
1064
1065 $fp = @fopen($url, 'r', false, $context);
1066 if ($fp) {
1067 $meta = stream_get_meta_data($fp);
1068 $result = isset($meta['wrapper_data']) ? $meta['wrapper_data'] : $result;
1069 fclose($fp);
1070 }
1071
1072 return $result;
1073 }
1074
1086 public static function getCurlHttpHeaders($url)
1087 {
1088 $result = array();
1089
1090 $ch = curl_init();
1091 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1092 curl_setopt($ch, CURLOPT_VERBOSE, true);
1093 curl_setopt($ch, CURLOPT_HEADER, true);
1094 curl_setopt($ch, CURLOPT_URL, $url);
1095 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
1096
1097 $response = @curl_exec($ch);
1098 if (empty($response)) {
1099 return $result;
1100 }
1101
1102 Log::trace('getCurlHttpHeaders:' . $response);
1103 $responseHeaders = explode("\r\n\r\n", $response, 2);
1104 if (!isset($responseHeaders[0]) || empty($responseHeaders[0])) {
1105 return $result;
1106 }
1107
1108 return explode("\n", $responseHeaders[0]);
1109 }
1110
1124 public static function getHeaders($host, $port, $ssl = false)
1125 {
1126 $result = array();
1127 $context = stream_context_create(array(
1128 'ssl' => array(
1129 'verify_peer' => false,
1130 'verify_peer_name' => false,
1131 'allow_self_signed' => true,
1132 )
1133 ));
1134
1135 $fp = @stream_socket_client(($ssl ? 'ssl://' : '') . $host . ':' . $port, $errno, $errstr, 5, STREAM_CLIENT_CONNECT, $context);
1136 if ($fp) {
1137 $out = fgets($fp);
1138 $result = explode(PHP_EOL, $out);
1139 @fclose($fp);
1140 }
1141
1142 if (!empty($result)) {
1143 $rebuildResult = array();
1144 foreach ($result as $row) {
1145 $row = trim($row);
1146 if (!empty($row)) {
1147 $rebuildResult[] = $row;
1148 }
1149 }
1150 $result = $rebuildResult;
1151
1152 Log::debug('getHeaders:');
1153 foreach ($result as $header) {
1154 Log::debug('-> ' . $header);
1155 }
1156 }
1157
1158 return $result;
1159 }
1160
1168 public static function getApiJson($url)
1169 {
1171
1172 $ch = curl_init();
1173 curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
1174 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1175 curl_setopt($ch, CURLOPT_VERBOSE, true);
1176 curl_setopt($ch, CURLOPT_URL, $url);
1177 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
1178 curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
1179 $data = curl_exec($ch);
1180 if (curl_errno($ch)) {
1181 Log::error('CURL Error: ' . curl_error($ch));
1182 }
1183
1184 // curl_close() is deprecated in PHP 8.5+ as it has no effect since PHP 8.0
1185 // The resource is automatically closed when it goes out of scope
1186 if (PHP_VERSION_ID < 80500) {
1187 curl_close($ch);
1188 }
1189
1190 return trim($data);
1191 }
1192
1200 public static function isPortInUse($port)
1201 {
1202 // Set localIP statically
1203 $localIP = '127.0.0.1';
1204
1205 // Save current error reporting level
1206 $errorReporting = error_reporting();
1207
1208 // Disable error reporting temporarily
1209 error_reporting(0);
1210
1211 $connection = @fsockopen($localIP, $port);
1212
1213 // Restore original error reporting level
1214 error_reporting($errorReporting);
1215
1216 if (is_resource($connection)) {
1217 fclose($connection);
1219
1220 return $process != null ? $process : 'N/A';
1221 }
1222
1223 return false;
1224 }
1225
1233 public static function isValidDomainName($domainName)
1234 {
1235 return filter_var($domainName, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) !== false;
1236 }
1237
1248 public static function installService($bin, $port, $syntaxCheckCmd, $showWindow = false)
1249 {
1250 global $bearsamppLang, $bearsamppWinbinder;
1251
1252 if (method_exists($bin, 'initData')) {
1253 $bin->initData();
1254 }
1255
1256 $name = $bin->getName();
1257 $service = $bin->getService();
1258 $boxTitle = sprintf($bearsamppLang->getValue(Lang::INSTALL_SERVICE_TITLE), $name);
1259
1260 $isPortInUse = self::isPortInUse($port);
1261 if ($isPortInUse === false) {
1262 if (!$service->isInstalled()) {
1263 $service->create();
1264 if ($service->start()) {
1265 Log::info(sprintf('%s service successfully installed. (name: %s ; port: %s)', $name, $service->getName(), $port));
1266 if ($showWindow) {
1267 $bearsamppWinbinder->messageBoxInfo(
1268 sprintf($bearsamppLang->getValue(Lang::SERVICE_INSTALLED), $name, $service->getName(), $port),
1269 $boxTitle
1270 );
1271 }
1272
1273 return true;
1274 } else {
1275 $serviceError = sprintf($bearsamppLang->getValue(Lang::SERVICE_INSTALL_ERROR), $name);
1276 $serviceErrorLog = sprintf('Error during the installation of %s service', $name);
1277 if (!empty($syntaxCheckCmd)) {
1278 $cmdSyntaxCheck = $bin->getCmdLineOutput($syntaxCheckCmd);
1279 if (!$cmdSyntaxCheck['syntaxOk']) {
1280 $serviceError .= PHP_EOL . sprintf($bearsamppLang->getValue(Lang::STARTUP_SERVICE_SYNTAX_ERROR), $cmdSyntaxCheck['content']);
1281 $serviceErrorLog .= sprintf(' (conf errors detected : %s)', $cmdSyntaxCheck['content']);
1282 }
1283 }
1284 Log::error($serviceErrorLog);
1285 if ($showWindow) {
1286 $bearsamppWinbinder->messageBoxError($serviceError, $boxTitle);
1287 }
1288 }
1289 } else {
1290 Log::warning(sprintf('%s service already installed', $name));
1291 if ($showWindow) {
1292 $bearsamppWinbinder->messageBoxWarning(
1293 sprintf($bearsamppLang->getValue(Lang::SERVICE_ALREADY_INSTALLED), $name),
1294 $boxTitle
1295 );
1296 }
1297
1298 return true;
1299 }
1300 } elseif ($service->isRunning()) {
1301 Log::warning(sprintf('%s service already installed and running', $name));
1302 if ($showWindow) {
1303 $bearsamppWinbinder->messageBoxWarning(
1304 sprintf($bearsamppLang->getValue(Lang::SERVICE_ALREADY_INSTALLED), $name),
1305 $boxTitle
1306 );
1307 }
1308
1309 return true;
1310 } else {
1311 Log::error(sprintf('Port %s is used by an other application : %s', $port, $isPortInUse));
1312 if ($showWindow) {
1313 $bearsamppWinbinder->messageBoxError(
1314 sprintf($bearsamppLang->getValue(Lang::PORT_NOT_USED_BY), $port, $isPortInUse),
1315 $boxTitle
1316 );
1317 }
1318 }
1319
1320 return false;
1321 }
1322
1331 public static function removeService($service, $name)
1332 {
1333 if (!($service instanceof Win32Service)) {
1334 Log::error('$service not an instance of Win32Service');
1335
1336 return false;
1337 }
1338
1339 if ($service->isInstalled()) {
1340 if ($service->delete()) {
1341 Log::info(sprintf('%s service successfully removed', $name));
1342
1343 return true;
1344 } else {
1345 Log::error(sprintf('Error during the uninstallation of %s service', $name));
1346
1347 return false;
1348 }
1349 } else {
1350 Log::warning(sprintf('%s service does not exist', $name));
1351 }
1352
1353 return true;
1354 }
1355
1365 public static function startService($bin, $syntaxCheckCmd, $showWindow = false)
1366 {
1367 global $bearsamppLang, $bearsamppWinbinder;
1368
1369 if (method_exists($bin, 'initData')) {
1370 $bin->initData();
1371 }
1372
1373 $name = $bin->getName();
1374 $service = $bin->getService();
1375 $boxTitle = sprintf($bearsamppLang->getValue(Lang::START_SERVICE_TITLE), $name);
1376
1377 if (!$service->start()) {
1378 $serviceError = sprintf($bearsamppLang->getValue(Lang::START_SERVICE_ERROR), $name);
1379 $serviceErrorLog = sprintf('Error while starting the %s service', $name);
1380 if (!empty($syntaxCheckCmd)) {
1381 $cmdSyntaxCheck = $bin->getCmdLineOutput($syntaxCheckCmd);
1382 if (!$cmdSyntaxCheck['syntaxOk']) {
1383 $serviceError .= PHP_EOL . sprintf($bearsamppLang->getValue(Lang::STARTUP_SERVICE_SYNTAX_ERROR), $cmdSyntaxCheck['content']);
1384 $serviceErrorLog .= sprintf(' (conf errors detected : %s)', $cmdSyntaxCheck['content']);
1385 }
1386 }
1387 Log::error($serviceErrorLog);
1388 if ($showWindow) {
1389 $bearsamppWinbinder->messageBoxError($serviceError, $boxTitle);
1390 }
1391
1392 return false;
1393 }
1394
1395 return true;
1396 }
1397
1408 public static function getGithubUrl($type = 'user', $user = APP_GITHUB_USER, $repo = null, $branch = null, $path = null) {
1409 if (empty($user) || !is_string($user)) {
1410 return false;
1411 }
1412
1413 // Encode as URL path segment (not query encoding)
1414 $user = rawurlencode($user);
1415
1416 switch ($type) {
1417 case 'user':
1418 return "https://github.com/{$user}";
1419
1420 case 'repo':
1421 if (empty($repo) || !is_string($repo)) {
1422 return false;
1423 }
1424 $repo = rawurlencode($repo);
1425 return "https://github.com/{$user}/{$repo}";
1426
1427 case 'raw':
1428 if (empty($repo) || empty($branch) || empty($path) || !is_string($repo) || !is_string($branch) || !is_string($path)) {
1429 return false;
1430 }
1431 $repo = rawurlencode($repo);
1432 $branch = rawurlencode($branch);
1433
1434 $path = ltrim($path, '/');
1435 $segments = array_map('rawurlencode', explode('/', $path));
1436 $pathEncoded = implode('/', $segments);
1437
1438 return "https://raw.githubusercontent.com/{$user}/{$repo}/{$branch}/{$pathEncoded}";
1439
1440 default:
1441 return false;
1442 }
1443 }
1444
1450 public static function getGithubUserUrl()
1451 {
1452 return self::getGithubUrl('user', APP_GITHUB_USER);
1453 }
1454
1463 public static function checkInternetState()
1464 {
1465 $connected = @fsockopen('www.google.com', 80);
1466 if ($connected) {
1467 fclose($connected);
1468
1469 return true; // Internet connection is active
1470 } else {
1471 return false; // Internet connection is not active
1472 }
1473 }
1474
1482 public static function getFolderList($path)
1483 {
1484 $result = array();
1485
1486 $handle = @opendir($path);
1487 if (!$handle) {
1488 return false;
1489 }
1490
1491 while (false !== ($file = readdir($handle))) {
1492 $filePath = $path . '/' . $file;
1493 if ($file != '.' && $file != '..' && is_dir($filePath) && $file != 'current') {
1494 $result[] = $file;
1495 }
1496 }
1497
1498 closedir($handle);
1499 natcasesort($result);
1500
1501 return $result;
1502 }
1503
1504
1513 public static function openFileContent($caption, $content)
1514 {
1516
1517 $tmpFile = Path::getTmpPath() . '/' . $caption . '.txt';
1518 file_put_contents($tmpFile, $content);
1519
1520 // Open the file with the configured editor from bearsampp.conf
1521 $editor = $bearsamppConfig->getNotepad();
1522 $bearsamppCore->getWinbinder()->exec($editor, '"' . $tmpFile . '"');
1523 }
1524
1530 public static function setupCurlHeaderWithToken()
1531 {
1532 // Return headers with User-Agent, which is required by GitHub API
1533 return array(
1534 'User-Agent: ' . APP_GITHUB_USERAGENT . ' (https://github.com/' . APP_GITHUB_USER . '/' . APP_GITHUB_REPO . ')',
1535 'Accept: application/vnd.github.v3+json'
1536 );
1537 }
1538}
$result
global $bearsamppBins
global $bearsamppLang
global $bearsamppRoot
$port
global $bearsamppCore
const LOADING
static getProcessUsingPort($port)
static recordHit()
static set($cacheKey, $data)
static recordMiss()
static get($cacheKey)
static shellExec(string $command)
const isRoot_FILE
const START_SERVICE_ERROR
const START_SERVICE_TITLE
const SERVICE_INSTALLED
const INSTALL_SERVICE_TITLE
const STARTUP_SERVICE_SYNTAX_ERROR
const SERVICE_INSTALL_ERROR
const SERVICE_ALREADY_INSTALLED
const PORT_NOT_USED_BY
static info($data, $file=null)
static debug($data, $file=null)
static warning($data, $file=null)
static trace($data, $file=null)
static error($data, $file=null)
static getExeFilePath($aetrayPath=false)
static getAliasPath($aetrayPath=false)
static getRootPath($aetrayPath=false)
static getIconsPath($aetrayPath=false)
static getResourcesPath($aetrayPath=false)
static getVhostsPath($aetrayPath=false)
static getOpenSslPath($aetrayPath=false)
static formatUnixPath($path)
static getPhpExe($aetrayPath=false)
static getTmpPath($aetrayPath=false)
static getModuleRootPath($module)
static getStartupLnkPath()
static findRepos($initPath, $startPath, $checkFile, $maxDepth=1)
static isLaunchStartup()
static installService($bin, $port, $syntaxCheckCmd, $showWindow=false)
static getWebsiteUrlNoUtm($path='', $fragment='')
static disableLaunchStartup()
static getRemoteFilesize($url, $humanFileSize=true)
static getGithubUrl($type='user', $user=APP_GITHUB_USER, $repo=null, $branch=null, $path=null)
static getMicrotime()
static deleteFolder($path)
static getHeaders($host, $port, $ssl=false)
static removeService($service, $name)
static isValidPort($port)
static cp1252ToUtf8($data)
static imgToBase64($path)
static getVersionList($path)
static getHttpHeaders($pingUrl)
static utf8ToCp1252($data)
static isValidDomainName($domainName)
static getLatestVersion($url)
static getFolderList($path)
static getGithubUserUrl()
static humanFileSize(int $size, string $unit='')
static openFileContent($caption, $content)
static clearLoadingText()
static getPathsToScan()
static startLoading()
static getWebsiteUrl($path='', $fragment='', $utmSource=true)
static isAdmin()
static isPortInUse($port)
static getApiJson($url)
static findFiles($startPath, $includes=array(''), $recursive=true)
static getFilesToScan($path=null, $useCache=true, $forceRefresh=false)
static replaceDefine($path, $var, $value)
static clearFolder($path, $exclude=array())
static isValidIp($ip)
static setupCurlHeaderWithToken()
static convertEncoding($data, $direction='to_cp1252')
static stopLoading()
static is32BitsOs()
static clearFolders($paths, $exclude=array())
static getFopenHttpHeaders($url)
static startService($bin, $syntaxCheckCmd, $showWindow=false)
static findFile($startPath, $findFile)
static getCurlHttpHeaders($url)
static updateLoadingText($text)
static enableLaunchStartup()
static replaceInFile($path, $replaceList)
static checkInternetState()
static contains($string, $search)
static startWith($string, $search)
static endWith($string, $search)
static createShortcut($shortcutPath, $targetPath, $workingDir='', $description='', $iconPath='')
static kill($pid)
global $bearsamppConfig
Definition homepage.php:41
const APP_GITHUB_USERAGENT
Definition root.php:18
const APP_WEBSITE
Definition root.php:14
const APP_GITHUB_USER
Definition root.php:16
const APP_GITHUB_REPO
Definition root.php:17
const APP_TITLE
Definition root.php:13