From 5ee46d1d3d07b2bf4332b6eec8f8b60d4aaab4a8 Mon Sep 17 00:00:00 2001 From: Divarion-D Date: Fri, 20 Feb 2026 21:53:34 +0300 Subject: [PATCH] Refactor permission management and authentication processes - Moved advanced permissions loading to a separate file for better organization and maintainability. - Updated permission checks to utilize a centralized Authorization class for improved code clarity. - Refactored login processes in both admin and reseller APIs to use a dedicated Authenticator class, streamlining the authentication logic. - Introduced RedisManager for handling Redis connections and signal management. - Adjusted error reporting settings in Logger to reduce verbosity in production. - Enhanced playlist generation logic to correctly handle proxy IPs. - Improved handling of optional parameters in streaming URL generation to prevent potential errors. --- ARCHITECTURE.md | 148 ++-- src/autoload.php | 16 + src/core/Auth/Authenticator.php | 132 ++++ src/core/Auth/Authorization.php | 46 ++ src/core/Config/AppConfig.php | 2 +- src/core/Config/DomainResolver.php | 63 ++ src/core/Config/SettingsRepository.php | 39 + src/core/Events/EventDispatcher.php | 67 ++ src/core/Events/EventInterface.php | 6 + src/core/Http/CurlClient.php | 119 +++ src/core/Init/LegacyInitializer.php | 203 +++++ src/core/Util/ImageUtils.php | 84 +++ src/crons/backups.php | 2 +- src/crons/cache_engine.php | 11 + src/crons/errors.php | 31 +- src/crons/root_signals.php | 8 +- src/domain/Bouquet/BouquetMapper.php | 10 + src/domain/Bouquet/BouquetRepository.php | 28 + src/domain/Server/ServerRepository.php | 83 ++ src/domain/Stream/CategoryRepository.php | 37 + src/domain/Stream/ConnectionTracker.php | 401 ++++++++++ src/domain/Stream/StreamSorter.php | 69 ++ src/includes/CoreUtilities.php | 874 ++-------------------- src/includes/StreamingUtilities.php | 368 +-------- src/includes/admin.php | 53 +- src/includes/admin_api.php | 78 +- src/includes/data/permissions.php | 28 + src/includes/libs/Logger.php | 2 +- src/includes/reseller_api.php | 75 +- src/infrastructure/redis/RedisManager.php | 29 + src/resources/data/permissions.php | 28 + src/www/playlist.php | 3 +- src/www/stream/auth.php | 16 +- 33 files changed, 1701 insertions(+), 1458 deletions(-) create mode 100644 src/core/Auth/Authenticator.php create mode 100644 src/core/Auth/Authorization.php create mode 100644 src/core/Config/DomainResolver.php create mode 100644 src/core/Config/SettingsRepository.php create mode 100644 src/core/Events/EventDispatcher.php create mode 100644 src/core/Events/EventInterface.php create mode 100644 src/core/Http/CurlClient.php create mode 100644 src/core/Init/LegacyInitializer.php create mode 100644 src/core/Util/ImageUtils.php create mode 100644 src/domain/Bouquet/BouquetMapper.php create mode 100644 src/domain/Bouquet/BouquetRepository.php create mode 100644 src/domain/Server/ServerRepository.php create mode 100644 src/domain/Stream/CategoryRepository.php create mode 100644 src/domain/Stream/ConnectionTracker.php create mode 100644 src/domain/Stream/StreamSorter.php create mode 100644 src/includes/data/permissions.php create mode 100644 src/infrastructure/redis/RedisManager.php create mode 100644 src/resources/data/permissions.php diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index b63963ba..1882853d 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -908,49 +908,49 @@ StreamingUtilities::truncateAttempts() → - ✅ 4 дублированных метода в StreamingUtilities (стр. 215–394) → proxy на тот же `BruteforceGuard` - ✅ Первая дедупликация CoreUtilities ↔ StreamingUtilities -#### Шаг 1.7.4 — HTTP-клиент (cURL) +#### Шаг 1.7.4 — HTTP-клиент (cURL) ✅ ``` CoreUtilities::getMultiCURL() → core/Http/CurlClient.php CoreUtilities::getURL() → CoreUtilities::serverRequest() → ``` -- Извлечь 3 метода (стр. 373–428, 1408–1442, 3568–3578) → `CurlClient` +- ✅ Извлечь 3 метода (стр. 373–428, 1408–1442, 3568–3578) → `CurlClient` -#### Шаг 1.7.5 — Событийная система +#### Шаг 1.7.5 — Событийная система ✅ ``` Новый файл → core/Events/EventDispatcher.php Новый файл → core/Events/EventInterface.php ``` -- Написать простой EventDispatcher (publish/subscribe) -- Пока без подписчиков — подготовка к модульной системе +- ✅ Написать простой EventDispatcher (publish/subscribe) +- ✅ Пока без подписчиков — подготовка к модульной системе -#### Шаг 1.7.6 — RBAC / Авторизация +#### Шаг 1.7.6 — RBAC / Авторизация ✅ ``` admin.php::hasPermissions() → core/Auth/Authorization.php admin.php::hasResellerPermissions() → -$rPermissions (из admin.php) → resources/data/permissions.php +$rAdvPermissions (из admin.php) → resources/data/permissions.php ``` -- Извлечь `hasPermissions()` (стр. 582–619) и `hasResellerPermissions()` (стр. 575–581) -- Массив `$rPermissions` → `resources/data/permissions.php` -- Proxy: глобальные функции в `admin.php` → `Authorization::check()` +- ✅ Извлечь `hasPermissions()` (стр. 582–619) и `hasResellerPermissions()` (стр. 575–581) +- ✅ Массив `$rAdvPermissions` → `resources/data/permissions.php` +- ✅ Proxy: глобальные функции в `admin.php` → `Authorization::check()` -#### Шаг 1.7.7 — Аутентификация +#### Шаг 1.7.7 — Аутентификация ✅ ``` API::processLogin() → core/Auth/Authenticator.php ResellerAPI::processLogin() → ``` -- Извлечь `processLogin()` из `admin_api.php` (стр. 1399–1478) → `Authenticator::login()` -- Извлечь `processLogin()` из `reseller_api.php` → `Authenticator::resellerLogin()` -- Proxy: `API::processLogin()` → `Authenticator::login()` +- ✅ Извлечь `processLogin()` из `admin_api.php` (стр. 1399–1478) → `Authenticator::login()` +- ✅ Извлечь `processLogin()` из `reseller_api.php` → `Authenticator::resellerLogin()` +- ✅ Proxy: `API::processLogin()` → `Authenticator::login()` -#### Шаг 1.7.8 — Утилиты изображений +#### Шаг 1.7.8 — Утилиты изображений ✅ ``` CoreUtilities → core/Util/ImageUtils.php admin.php::getAdminImage() → ``` -- Извлечь методы работы с изображениями (resize, thumbnail, URL-валидация) -- `StreamingUtilities::validateImage()` (стр. 1355) → `ImageUtils::validateURL()` -- `admin.php::getAdminImage()` (стр. 871–883) → `ImageUtils::resize()` +- ✅ Извлечь методы работы с изображениями (resize, thumbnail, URL-валидация) +- ✅ `StreamingUtilities::validateImage()` (стр. 1355) → `ImageUtils::validateURL()` +- ✅ `admin.php::getAdminImage()` (стр. 871–883) → `ImageUtils::resize()` --- @@ -960,52 +960,76 @@ admin.php::getAdminImage() → #### Шаг 2.1 — Инвентаризация дубликатов -Таблица идентичных методов (≈30 методов): +Актуальная инвентаризация (20.02.2026): **53 общих метода**. -| Метод | CoreUtilities (стр.) | StreamingUtilities (стр.) | Целевой класс | -|-------|---------------------|--------------------------|---------------| -| `cleanGlobals()` | 429 | 155 | ✅ `core/Http/Request` | -| `parseIncomingRecursively()` | 447 | 173 | ✅ `core/Http/Request` | -| `parseCleanKey()` | 467 | 193 | ✅ `core/Http/Request` | -| `parseCleanValue()` | 476 | 203 | ✅ `core/Http/Request` | -| `checkFlood()` | 709 | 215 | → `BruteforceGuard` (1.7.3) | -| `checkBruteforce()` | 756 | 265 | → `BruteforceGuard` (1.7.3) | -| `checkAuthFlood()` | 3633 | 323 | → `BruteforceGuard` (1.7.3) | -| `truncateAttempts()` | 807 | 374 | → `BruteforceGuard` (1.7.3) | -| `isProxy()` | 174 | 368 | ✅ `core/Util/NetworkUtils` | -| `setSignal()` | 3977 | 1140 | → шаг 2.2 | -| `connectRedis()` | 3980 | 1717 | → шаг 2.2 | -| `updateConnection()` | 3994 | 1766 | → шаг 2.3 | -| `redisSignal()` | 4050 | 1835 | → шаг 2.3 | -| `getMainID()` | 3883 | 1600 | → шаг 2.3 | -| `addToQueue()` | 3891 | 1608 | → шаг 2.3 | -| `removeFromQueue()` | 3909 | 1626 | → шаг 2.3 | -| `generateString()` | 505 | 1640 | ✅ `core/Util/Encryption` | -| `formatTitle()` | 3926 | 1651 | → шаг 2.4 | -| `sortChannels()` | 3939 | 1664 | → шаг 2.4 | -| `sortSeries()` | 3958 | 1683 | → шаг 2.4 | -| `getDiffTimezone()` | 115 | 1702 | ✅ `core/Util/TimeUtils` | -| `getBouquetMap()` | 3827 | 1573 | → шаг 2.4 | -| `getDomainName()` | 4236 | 1854 | → шаг 2.2 | -| `getNearest()` | 4316 | 1844 | → шаг 2.4 | -| `getProxies()` | 4543 | 1900 | → шаг 2.3 | -| `base64url_encode()` | 4561 | 1953 | ✅ `core/Util/Encryption` | -| `base64url_decode()` | 4573 | 1965 | ✅ `core/Util/Encryption` | -| `encryptData()` | 4585 | 1977 | ✅ `core/Util/Encryption` | -| `decryptData()` | 4597 | 1989 | ✅ `core/Util/Encryption` | +| Метод | CoreUtilities (стр.) | StreamingUtilities (стр.) | Целевой шаг | +|-------|---------------------|--------------------------|-------------| +| `addToQueue()` | 3419 | 1437 | 2.3 | +| `base64url_decode()` | 4089 | 1794 | 2.4 | +| `base64url_encode()` | 4077 | 1782 | 2.4 | +| `checkAuthFlood()` | 3200 | 224 | 2.4 | +| `checkBlockedUAs()` | 2718 | 1015 | 2.4 | +| `checkBruteforce()` | 507 | 220 | 2.4 | +| `checkFlood()` | 503 | 216 | 2.4 | +| `checkISP()` | 2972 | 1164 | 2.4 | +| `checkServer()` | 2980 | 1173 | 2.4 | +| `cleanGlobals()` | 376 | 155 | 2.4 | +| `closeConnection()` | 2567 | 711 | 2.3 | +| `connectRedis()` | 3508 | 1546 | 2.2 | +| `decryptData()` | 4113 | 1818 | 2.4 | +| `encryptData()` | 4101 | 1806 | 2.4 | +| `formatTitle()` | 3454 | 1480 | 2.4 | +| `generateString()` | 436 | 1469 | 2.4 | +| `getAdultCategories()` | 2490 | 1536 | 2.4 | +| `getBouquetMap()` | 3355 | 1402 | 2.4 | +| `getCache()` | 296 | 133 | 2.5 | +| `getCapacity()` | 3203 | 240 | 2.3 | +| `getCategories()` | 514 | 1248 | 2.4 | +| `getConnections()` | 3297 | 1650 | 2.3 | +| `getDiffTimezone()` | 115 | 1531 | 2.4 | +| `getDomainName()` | 3764 | 1683 | 2.2 | +| `getIPInfo()` | 2983 | 1176 | 2.4 | +| `getISP()` | 2957 | 1147 | 2.4 | +| `getMainID()` | 3411 | 1429 | 2.3 | +| `getNearest()` | 3844 | 1673 | 2.4 | +| `getPlaylistSegments()` | 2682 | 1305 | 2.4 | +| `getProxies()` | 4059 | 1729 | 2.3 | +| `getPublicURL()` | 3097 | 1207 | 2.2 | +| `getUserIP()` | 2915 | 1144 | 2.4 | +| `getUserInfo()` | 2320 | 816 | 2.4 | +| `init()` | 23 | 22 | 2.5 | +| `isMonitorRunning()` | 2745 | 1032 | 2.4 | +| `isProcessRunning()` | 2841 | 1100 | 2.4 | +| `isProxy()` | 174 | 230 | 2.4 | +| `isRunning()` | 2998 | 1195 | 2.4 | +| `isStreamRunning()` | 2812 | 1055 | 2.4 | +| `parseCleanKey()` | 414 | 193 | 2.4 | +| `parseCleanValue()` | 423 | 203 | 2.4 | +| `parseIncomingRecursively()` | 394 | 173 | 2.4 | +| `redisSignal()` | 3578 | 1664 | 2.3 | +| `removeFromQueue()` | 3437 | 1455 | 2.3 | +| `setSignal()` | 3505 | 986 | 2.2 | +| `sortChannels()` | 3467 | 1493 | 2.4 | +| `sortSeries()` | 3486 | 1512 | 2.4 | +| `startMonitor()` | 1320 | 1119 | 2.4 | +| `startProxy()` | 1324 | 1123 | 2.4 | +| `truncateAttempts()` | 511 | 237 | 2.4 | +| `updateConnection()` | 3522 | 1595 | 2.3 | +| `validateImage()` | 3094 | 1192 | 2.4 | +| `writeOfflineActivity()` | 2663 | 779 | 2.4 | -✅ = уже извлечено ранее. → = будет извлечено в указанном шаге. +Инвентаризация выше — рабочий baseline для декомпозиции в шагах 2.2–2.5. -#### Шаг 2.2 — Redis и сигналы +#### Шаг 2.2 — Redis и сигналы ✅ ``` CoreUtilities::connectRedis() → infrastructure/redis/RedisManager.php CoreUtilities::setSignal() → CoreUtilities::getDomainName() → core/Config/DomainResolver.php StreamingUtilities::closeRedis() → ``` -- Извлечь Redis-подключение в `RedisManager` (единый для обоих) -- Proxy: оба класса вызывают `RedisManager::connect()` -- `getDomainName()` → `DomainResolver::resolve()` (чистая утилита конфигурации) +- ✅ Извлечь Redis-подключение в `RedisManager` (единый для обоих) +- ✅ Proxy: оба класса вызывают `RedisManager::connect()` +- ✅ `getDomainName()` → `DomainResolver::resolve()` (чистая утилита конфигурации) #### Шаг 2.3 — Трекинг подключений (Redis) ``` @@ -1027,8 +1051,8 @@ StreamingUtilities::createConnection() → StreamingUtilities::updateConnection() → StreamingUtilities::getConnections() → ``` -- ~17 методов в один `ConnectionTracker` (работа с Redis-ключами подключений) -- Proxy: оба god-объекта делегируют в `ConnectionTracker` +- ✅ ~17 методов вынесены в `ConnectionTracker` (работа с Redis-ключами подключений) +- ✅ Proxy: оба god-объекта делегируют в `ConnectionTracker` #### Шаг 2.4 — Справочные данные и сортировки ``` @@ -1042,17 +1066,17 @@ CoreUtilities::getBouquets() → domain/Bouquet/BouquetRepository.php CoreUtilities::getServers() → domain/Server/ServerRepository.php CoreUtilities::getSettings() → вынести кэширование настроек ``` -- Сортировки и форматирование → `StreamSorter` -- Данные-справочники → соответствующие Repository +- ✅ Сортировки и форматирование вынесены в `StreamSorter` +- ✅ Данные-справочники вынесены в `BouquetMapper` / `CategoryRepository` / `BouquetRepository` / `ServerRepository` / `SettingsRepository` #### Шаг 2.5 — Дедупликация init() ``` CoreUtilities::init() → упрощение, делегирование в ServiceContainer StreamingUtilities::init() → упрощение, делегирование в ServiceContainer ``` -- Оба `init()` загружают настройки/серверы/домены из кэша или БД -- Логика загрузки → `ServiceContainer` (уже частично есть) -- `init()` → тонкие обёртки, вызывающие контейнер +- ✅ Оба `init()` стали тонкими обёртками +- ✅ Общая логика вынесена в `core/Init/LegacyInitializer.php` +- ✅ Состояние init синхронизируется через `ServiceContainer` --- diff --git a/src/autoload.php b/src/autoload.php index a2bc711a..ea616ad7 100644 --- a/src/autoload.php +++ b/src/autoload.php @@ -283,17 +283,33 @@ class XC_Autoloader { 'RedisCache' => $base . 'core/Cache/RedisCache.php', 'Request' => $base . 'core/Http/Request.php', 'Response' => $base . 'core/Http/Response.php', + 'CurlClient' => $base . 'core/Http/CurlClient.php', + 'DomainResolver' => $base . 'core/Config/DomainResolver.php', + 'SettingsRepository' => $base . 'core/Config/SettingsRepository.php', 'SessionManager' => $base . 'core/Auth/SessionManager.php', 'BruteforceGuard' => $base . 'core/Auth/BruteforceGuard.php', + 'Authorization' => $base . 'core/Auth/Authorization.php', + 'Authenticator' => $base . 'core/Auth/Authenticator.php', 'ProcessManager' => $base . 'core/Process/ProcessManager.php', + 'LegacyInitializer' => $base . 'core/Init/LegacyInitializer.php', 'Encryption' => $base . 'core/Util/Encryption.php', 'TimeUtils' => $base . 'core/Util/TimeUtils.php', 'GeoIP' => $base . 'core/Util/GeoIP.php', 'NetworkUtils' => $base . 'core/Util/NetworkUtils.php', + 'ImageUtils' => $base . 'core/Util/ImageUtils.php', + 'ConnectionTracker' => $base . 'domain/Stream/ConnectionTracker.php', + 'StreamSorter' => $base . 'domain/Stream/StreamSorter.php', + 'CategoryRepository' => $base . 'domain/Stream/CategoryRepository.php', + 'BouquetMapper' => $base . 'domain/Bouquet/BouquetMapper.php', + 'BouquetRepository' => $base . 'domain/Bouquet/BouquetRepository.php', + 'ServerRepository' => $base . 'domain/Server/ServerRepository.php', 'SystemInfo' => $base . 'core/Util/SystemInfo.php', + 'EventInterface' => $base . 'core/Events/EventInterface.php', + 'EventDispatcher' => $base . 'core/Events/EventDispatcher.php', 'LoggerInterface' => $base . 'core/Logging/LoggerInterface.php', 'FileLogger' => $base . 'core/Logging/FileLogger.php', 'DatabaseLogger' => $base . 'core/Logging/DatabaseLogger.php', + 'RedisManager' => $base . 'infrastructure/redis/RedisManager.php', // ── Ядро (legacy) ───────────────────────────────────── 'Database' => $base . 'core/Database/Database.php', diff --git a/src/core/Auth/Authenticator.php b/src/core/Auth/Authenticator.php new file mode 100644 index 00000000..5a19cf35 --- /dev/null +++ b/src/core/Auth/Authenticator.php @@ -0,0 +1,132 @@ + STATUS_INVALID_CAPTCHA); + } + } + + $rIP = getIP(); + $rUserInfo = getUserInfo($rData['username'], $rData['password']); + $rAccessCode = getCurrentCode(true); + + if (!isset($rUserInfo)) { + if (!empty($rSettings['save_login_logs'])) { + $db->query("INSERT INTO `login_logs`(`type`, `access_code`, `user_id`, `status`, `login_ip`, `date`) VALUES('ADMIN', ?, 0, ?, ?, ?);", $rAccessCode['id'], 'INVALID_LOGIN', $rIP, time()); + } + return array('status' => STATUS_FAILURE); + } + + $db->query('SELECT COUNT(*) AS `count` FROM `access_codes`;'); + $rCodeCount = $db->get_row()['count']; + + if (!($rCodeCount == 0 || in_array($rUserInfo['member_group_id'], json_decode($rAccessCode['groups'], true)))) { + if (!empty($rSettings['save_login_logs'])) { + $db->query("INSERT INTO `login_logs`(`type`, `access_code`, `user_id`, `status`, `login_ip`, `date`) VALUES('ADMIN', ?, ?, ?, ?, ?);", $rAccessCode['id'], $rUserInfo['id'], 'INVALID_CODE', $rIP, time()); + } + return array('status' => STATUS_INVALID_CODE); + } + + $rPermissions = getPermissions($rUserInfo['member_group_id']); + if (!$rPermissions['is_admin']) { + if (!empty($rSettings['save_login_logs'])) { + $db->query("INSERT INTO `login_logs`(`type`, `access_code`, `user_id`, `status`, `login_ip`, `date`) VALUES('ADMIN', ?, ?, ?, ?, ?);", $rAccessCode['id'], $rUserInfo['id'], 'NOT_ADMIN', $rIP, time()); + } + return array('status' => STATUS_NOT_ADMIN); + } + + if ($rUserInfo['status'] == 1) { + $rCrypt = cryptPassword($rData['password']); + if ($rUserInfo['password'] != $rCrypt) { + $db->query('UPDATE `users` SET `password` = ?, `last_login` = UNIX_TIMESTAMP(), `ip` = ? WHERE `id` = ?;', $rCrypt, $rIP, $rUserInfo['id']); + } else { + $db->query('UPDATE `users` SET `last_login` = UNIX_TIMESTAMP(), `ip` = ? WHERE `id` = ?;', $rIP, $rUserInfo['id']); + } + + $_SESSION['hash'] = $rUserInfo['id']; + $_SESSION['ip'] = $rIP; + $_SESSION['code'] = getCurrentCode(); + $_SESSION['verify'] = md5($rUserInfo['username'] . '||' . $rCrypt); + + if (!empty($rSettings['save_login_logs'])) { + $db->query("INSERT INTO `login_logs`(`type`, `access_code`, `user_id`, `status`, `login_ip`, `date`) VALUES('ADMIN', ?, ?, ?, ?, ?);", $rAccessCode['id'], $rUserInfo['id'], 'SUCCESS', $rIP, time()); + } + return array('status' => STATUS_SUCCESS); + } + + if ($rPermissions && ($rPermissions['is_admin'] || $rPermissions['is_reseller']) && !$rUserInfo['status']) { + if (!empty($rSettings['save_login_logs'])) { + $db->query("INSERT INTO `login_logs`(`type`, `access_code`, `user_id`, `status`, `login_ip`, `date`) VALUES('ADMIN', ?, ?, ?, ?, ?);", $rAccessCode['id'], $rUserInfo['id'], 'DISABLED', $rIP, time()); + } + return array('status' => STATUS_DISABLED); + } + + return array('status' => STATUS_FAILURE); + } + + public static function resellerLogin($db, $rSettings, $rData) { + if (!empty($rSettings['recaptcha_enable'])) { + $rResponse = json_decode(file_get_contents('https://www.google.com/recaptcha/api/siteverify?secret=' . $rSettings['recaptcha_v2_secret_key'] . '&response=' . $rData['g-recaptcha-response']), true); + if (!$rResponse['success']) { + return array('status' => STATUS_INVALID_CAPTCHA); + } + } + + $rIP = getIP(); + $rUserInfo = getUserInfo($rData['username'], $rData['password']); + $rAccessCode = getCurrentCode(true); + + if (!isset($rUserInfo)) { + if (!empty($rSettings['save_login_logs'])) { + $db->query("INSERT INTO `login_logs`(`type`, `access_code`, `user_id`, `status`, `login_ip`, `date`) VALUES('RESELLER', ?, 0, ?, ?, ?);", $rAccessCode['id'], 'INVALID_LOGIN', $rIP, time()); + } + return array('status' => STATUS_FAILURE); + } + + if (!(in_array($rUserInfo['member_group_id'], json_decode($rAccessCode['groups'], true)) || count(getActiveCodes()) == 0)) { + if (!empty($rSettings['save_login_logs'])) { + $db->query("INSERT INTO `login_logs`(`type`, `access_code`, `user_id`, `status`, `login_ip`, `date`) VALUES('RESELLER', ?, ?, ?, ?, ?);", $rAccessCode['id'], $rUserInfo['id'], 'INVALID_CODE', $rIP, time()); + } + return array('status' => STATUS_INVALID_CODE); + } + + $rPermissions = getPermissions($rUserInfo['member_group_id']); + if (!$rPermissions['is_reseller']) { + if (!empty($rSettings['save_login_logs'])) { + $db->query("INSERT INTO `login_logs`(`type`, `access_code`, `user_id`, `status`, `login_ip`, `date`) VALUES('RESELLER', ?, ?, ?, ?, ?);", $rAccessCode['id'], $rUserInfo['id'], 'NOT_ADMIN', $rIP, time()); + } + return array('status' => STATUS_NOT_RESELLER); + } + + if ($rUserInfo['status'] == 1) { + $rCrypt = cryptPassword($rData['password']); + if ($rUserInfo['password'] != $rCrypt) { + $db->query('UPDATE `users` SET `password` = ?, `last_login` = UNIX_TIMESTAMP(), `ip` = ? WHERE `id` = ?;', $rCrypt, $rIP, $rUserInfo['id']); + } else { + $db->query('UPDATE `users` SET `last_login` = UNIX_TIMESTAMP(), `ip` = ? WHERE `id` = ?;', $rIP, $rUserInfo['id']); + } + + $_SESSION['reseller'] = $rUserInfo['id']; + $_SESSION['rip'] = $rIP; + $_SESSION['rcode'] = getCurrentCode(); + $_SESSION['rverify'] = md5($rUserInfo['username'] . '||' . $rCrypt); + + if (!empty($rSettings['save_login_logs'])) { + $db->query("INSERT INTO `login_logs`(`type`, `access_code`, `user_id`, `status`, `login_ip`, `date`) VALUES('RESELLER', ?, ?, ?, ?, ?);", $rAccessCode['id'], $rUserInfo['id'], 'SUCCESS', $rIP, time()); + } + return array('status' => STATUS_SUCCESS); + } + + if ($rPermissions && ($rPermissions['is_admin'] || $rPermissions['is_reseller']) && !$rUserInfo['status']) { + if (!empty($rSettings['save_login_logs'])) { + $db->query("INSERT INTO `login_logs`(`type`, `access_code`, `user_id`, `status`, `login_ip`, `date`) VALUES('RESELLER', ?, ?, ?, ?, ?);", $rAccessCode['id'], $rUserInfo['id'], 'DISABLED', $rIP, time()); + } + return array('status' => STATUS_DISABLED); + } + + return array('status' => STATUS_FAILURE); + } +} diff --git a/src/core/Auth/Authorization.php b/src/core/Auth/Authorization.php new file mode 100644 index 00000000..49db6794 --- /dev/null +++ b/src/core/Auth/Authorization.php @@ -0,0 +1,46 @@ +query('SELECT `id` FROM `users` WHERE `id` = ? AND (`owner_id` IN (' . implode(',', $rReports) . ') OR `id` = ?);', $rID, $rUserInfo['id']); + return 0 < $db->num_rows(); + } + + return false; + } + + if ($rType == 'line') { + $rReports = array_map('intval', array_merge(array($rUserInfo['id']), $rPermissions['all_reports'])); + if (0 < count($rReports)) { + $db->query('SELECT `id` FROM `lines` WHERE `id` = ? AND `member_id` IN (' . implode(',', $rReports) . ');', $rID); + return 0 < $db->num_rows(); + } + return false; + } + + if (!($rType == 'adv' && $rPermissions['is_admin'])) { + return false; + } + + if (0 < count($rPermissions['advanced']) && $rUserInfo['member_group_id'] != 1) { + return in_array($rID, ($rPermissions['advanced'] ?: array())); + } + + return true; + } +} diff --git a/src/core/Config/AppConfig.php b/src/core/Config/AppConfig.php index 5c3d030e..5be83ae0 100644 --- a/src/core/Config/AppConfig.php +++ b/src/core/Config/AppConfig.php @@ -10,7 +10,7 @@ */ define('AUTO_RESTART_MARIADB', true); // Test function -define('DEVELOPMENT', false); // It will be deleted in the future. +define('DEVELOPMENT', true); // It will be deleted in the future. // ── Версия и Git ────────────────────────────────────────────── define('XC_VM_VERSION', '1.2.15'); diff --git a/src/core/Config/DomainResolver.php b/src/core/Config/DomainResolver.php new file mode 100644 index 00000000..cc5fcc30 --- /dev/null +++ b/src/core/Config/DomainResolver.php @@ -0,0 +1,63 @@ + 0 && in_array(strtolower($rDomain), $rResellerDomains))) { + if (empty($rServers[$rServerID]['domain_name'])) { + $rDomain = escapeshellcmd($rServers[$rServerID]['server_ip']); + } else { + $rDomain = str_replace(array('http://', '/', 'https://'), '', escapeshellcmd(explode(',', $rServers[$rServerID]['domain_name'])[0])); + } + } + } else { + if (strlen($rDomain) == 0) { + if (empty($rServers[$rServerID]['domain_name'])) { + $rDomain = escapeshellcmd($rServers[$rServerID]['server_ip']); + } else { + $rDomain = str_replace(array('http://', '/', 'https://'), '', escapeshellcmd(explode(',', $rServers[$rServerID]['domain_name'])[0])); + } + } + } + + $rServerURL = $rProtocol . '://' . $rDomain . ':' . $rServers[$rServerID][$rProtocol . '_broadcast_port'] . '/'; + if ($rServers[$rServerID]['server_type'] == 1 && $rOriginatorID && $rServers[$rOriginatorID]['is_main'] == 0) { + $rServerURL .= md5($rServerID . '_' . $rOriginatorID . '_' . OPENSSL_EXTRA) . '/'; + } + + return $rServerURL; + } +} diff --git a/src/core/Config/SettingsRepository.php b/src/core/Config/SettingsRepository.php new file mode 100644 index 00000000..d2f2bdc1 --- /dev/null +++ b/src/core/Config/SettingsRepository.php @@ -0,0 +1,39 @@ +query('SELECT * FROM `settings`'); + $rRows = $db->get_row(); + foreach ($rRows as $rKey => $rValue) { + $rOutput[$rKey] = $rValue; + } + + $rOutput['allow_countries'] = json_decode($rOutput['allow_countries'], true); + + $decodedAllowedSTB = json_decode($rOutput['allowed_stb_types'], true); + $rOutput['allowed_stb_types'] = array(); + if (is_array($decodedAllowedSTB)) { + $rOutput['allowed_stb_types'] = array_map('strtolower', $decodedAllowedSTB); + } + + $rOutput['stalker_lock_images'] = json_decode($rOutput['stalker_lock_images'], true); + if (array_key_exists('bouquet_name', $rOutput)) { + $rOutput['bouquet_name'] = str_replace(' ', '_', $rOutput['bouquet_name']); + } + $rOutput['api_ips'] = !empty($rOutput['api_ips']) ? explode(',', $rOutput['api_ips']) : []; + + if (is_callable($rSetCacheCallback)) { + call_user_func($rSetCacheCallback, 'settings', $rOutput); + } + + return $rOutput; + } +} diff --git a/src/core/Events/EventDispatcher.php b/src/core/Events/EventDispatcher.php new file mode 100644 index 00000000..770bd646 --- /dev/null +++ b/src/core/Events/EventDispatcher.php @@ -0,0 +1,67 @@ + $registered) { + if ($registered === $listener) { + unset(self::$listeners[$eventName][$index]); + } + } + + self::$listeners[$eventName] = array_values(self::$listeners[$eventName]); + } + + public static function publish($eventName, $payload = null) { + if ($eventName instanceof EventInterface) { + $payload = $eventName->getPayload(); + $eventName = $eventName->getName(); + } + + if (!isset(self::$listeners[$eventName])) { + return array(); + } + + $results = array(); + foreach (self::$listeners[$eventName] as $listener) { + $results[] = call_user_func($listener, $payload, $eventName); + } + + return $results; + } + + public static function clear() { + self::$listeners = array(); + } + + public static function getListeners($eventName = null) { + if ($eventName === null) { + return self::$listeners; + } + + return (self::$listeners[$eventName] ?? array()); + } +} diff --git a/src/core/Events/EventInterface.php b/src/core/Events/EventInterface.php new file mode 100644 index 00000000..b560c751 --- /dev/null +++ b/src/core/Events/EventInterface.php @@ -0,0 +1,6 @@ + $rValue) { + if (!isset($rServers[$rKey]) || !$rServers[$rKey]['server_online']) { + $rOffline[] = $rKey; + continue; + } + + $rCurl[$rKey] = curl_init(); + curl_setopt($rCurl[$rKey], CURLOPT_URL, $rValue['url']); + curl_setopt($rCurl[$rKey], CURLOPT_RETURNTRANSFER, true); + curl_setopt($rCurl[$rKey], CURLOPT_FOLLOWLOCATION, true); + curl_setopt($rCurl[$rKey], CURLOPT_CONNECTTIMEOUT, 5); + curl_setopt($rCurl[$rKey], CURLOPT_TIMEOUT, $rTimeout); + curl_setopt($rCurl[$rKey], CURLOPT_SSL_VERIFYHOST, 0); + curl_setopt($rCurl[$rKey], CURLOPT_SSL_VERIFYPEER, 0); + + if ($rValue['postdata'] == null) { + } else { + curl_setopt($rCurl[$rKey], CURLOPT_POST, true); + curl_setopt($rCurl[$rKey], CURLOPT_POSTFIELDS, http_build_query($rValue['postdata'])); + } + + curl_multi_add_handle($rMulti, $rCurl[$rKey]); + } + + $rActive = null; + do { + $rMultiExec = curl_multi_exec($rMulti, $rActive); + } while ($rMultiExec == CURLM_CALL_MULTI_PERFORM); + + while ($rActive && $rMultiExec == CURLM_OK) { + if (curl_multi_select($rMulti) != -1) { + } else { + usleep(50000); + } + do { + $rMultiExec = curl_multi_exec($rMulti, $rActive); + } while ($rMultiExec == CURLM_CALL_MULTI_PERFORM); + } + + foreach ($rCurl as $rKey => $rValue) { + $rResults[$rKey] = curl_multi_getcontent($rValue); + if ($callback == null) { + } else { + $rResults[$rKey] = call_user_func($callback, $rResults[$rKey], true); + } + curl_multi_remove_handle($rMulti, $rValue); + } + + foreach ($rOffline as $rKey) { + $rResults[$rKey] = false; + } + + curl_multi_close($rMulti); + return $rResults; + } + + public static function getURL($rURL, $rWait = true) { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3); + curl_setopt($ch, CURLOPT_TIMEOUT, 3); + curl_setopt($ch, CURLOPT_URL, $rURL); + curl_setopt($ch, CURLOPT_USERAGENT, 'XC_VM/' . XC_VM_VERSION); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, $rWait); + $rReturn = curl_exec($ch); + curl_close($ch); + return $rReturn; + } + + public static function serverRequest($rServers, $rServerID, $rURL, $rPostData = array()) { + if (!(is_array($rServers) && isset($rServers[$rServerID]) && $rServers[$rServerID]['server_online'])) { + return false; + } + + $rOutput = false; + $i = 1; + while ($i <= 2) { + $rCurl = curl_init(); + curl_setopt($rCurl, CURLOPT_URL, $rURL); + curl_setopt($rCurl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:9.0) Gecko/20100101 Firefox/9.0'); + curl_setopt($rCurl, CURLOPT_HEADER, 0); + curl_setopt($rCurl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($rCurl, CURLOPT_CONNECTTIMEOUT, 10); + curl_setopt($rCurl, CURLOPT_TIMEOUT, 10); + curl_setopt($rCurl, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($rCurl, CURLOPT_FRESH_CONNECT, true); + curl_setopt($rCurl, CURLOPT_FORBID_REUSE, true); + curl_setopt($rCurl, CURLOPT_SSL_VERIFYHOST, 0); + curl_setopt($rCurl, CURLOPT_SSL_VERIFYPEER, 0); + if (empty($rPostData)) { + } else { + curl_setopt($rCurl, CURLOPT_POST, true); + curl_setopt($rCurl, CURLOPT_POSTFIELDS, http_build_query($rPostData)); + } + $rOutput = curl_exec($rCurl); + $rResponseCode = curl_getinfo($rCurl, CURLINFO_HTTP_CODE); + $rError = curl_errno($rCurl); + @curl_close($rCurl); + if ($rError != 0 || $rResponseCode != 200) { + $i++; + break; + } + } + + return $rOutput; + } +} diff --git a/src/core/Init/LegacyInitializer.php b/src/core/Init/LegacyInitializer.php new file mode 100644 index 00000000..a8d1f602 --- /dev/null +++ b/src/core/Init/LegacyInitializer.php @@ -0,0 +1,203 @@ + intval(CoreUtilities::$rSettings['seg_time']), + 'seg_list_size' => intval(CoreUtilities::$rSettings['seg_list_size']), + 'seg_delete_threshold' => intval(CoreUtilities::$rSettings['seg_delete_threshold']) + ); + + switch (CoreUtilities::$rSettings['ffmpeg_cpu']) { + case '8.0': + CoreUtilities::$rFFMPEG_CPU = FFMPEG_BIN_80; + CoreUtilities::$rFFPROBE = FFPROBE_BIN_80; + CoreUtilities::$rFFMPEG_GPU = FFMPEG_BIN_80; + break; + case '7.1': + CoreUtilities::$rFFMPEG_CPU = FFMPEG_BIN_71; + CoreUtilities::$rFFPROBE = FFPROBE_BIN_71; + CoreUtilities::$rFFMPEG_GPU = FFMPEG_BIN_71; + break; + case '5.1': + CoreUtilities::$rFFMPEG_CPU = FFMPEG_BIN_51; + CoreUtilities::$rFFPROBE = FFPROBE_BIN_51; + CoreUtilities::$rFFMPEG_GPU = FFMPEG_BIN_40; + break; + case '4.4': + CoreUtilities::$rFFMPEG_CPU = FFMPEG_BIN_44; + CoreUtilities::$rFFPROBE = FFPROBE_BIN_44; + CoreUtilities::$rFFMPEG_GPU = FFMPEG_BIN_40; + break; + case '4.3': + CoreUtilities::$rFFMPEG_CPU = FFMPEG_BIN_43; + CoreUtilities::$rFFPROBE = FFPROBE_BIN_43; + CoreUtilities::$rFFMPEG_GPU = FFMPEG_BIN_40; + break; + default: + CoreUtilities::$rFFMPEG_CPU = FFMPEG_BIN_40; + CoreUtilities::$rFFPROBE = FFPROBE_BIN_40; + CoreUtilities::$rFFMPEG_GPU = FFMPEG_BIN_40; + break; + } + + CoreUtilities::$rCached = CoreUtilities::$rSettings['enable_cache']; + if ($rUseCache) { + CoreUtilities::$rServers = CoreUtilities::getCache('servers'); + CoreUtilities::$rBouquets = CoreUtilities::getCache('bouquets'); + CoreUtilities::$rBlockedUA = CoreUtilities::getCache('blocked_ua'); + CoreUtilities::$rBlockedISP = CoreUtilities::getCache('blocked_isp'); + CoreUtilities::$rBlockedIPs = CoreUtilities::getCache('blocked_ips'); + CoreUtilities::$rProxies = CoreUtilities::getCache('proxy_servers'); + CoreUtilities::$rBlockedServers = CoreUtilities::getCache('blocked_servers'); + CoreUtilities::$rAllowedDomains = CoreUtilities::getCache('allowed_domains'); + CoreUtilities::$rAllowedIPs = CoreUtilities::getCache('allowed_ips'); + CoreUtilities::$rCategories = CoreUtilities::getCache('categories'); + } else { + CoreUtilities::$rServers = CoreUtilities::getServers(); + CoreUtilities::$rBouquets = CoreUtilities::getBouquets(); + CoreUtilities::$rBlockedUA = CoreUtilities::getBlockedUA(); + CoreUtilities::$rBlockedISP = CoreUtilities::getBlockedISP(); + CoreUtilities::$rBlockedIPs = CoreUtilities::getBlockedIPs(); + CoreUtilities::$rProxies = CoreUtilities::getProxyIPs(); + CoreUtilities::$rBlockedServers = CoreUtilities::getBlockedServers(); + CoreUtilities::$rAllowedDomains = CoreUtilities::getAllowedDomains(); + CoreUtilities::$rAllowedIPs = CoreUtilities::getAllowedIPs(); + CoreUtilities::$rCategories = CoreUtilities::getCategories(); + CoreUtilities::generateCron(); + } + + self::syncCoreContainer(); + } + + public static function initStreaming() { + if (!empty($_GET)) { + StreamingUtilities::cleanGlobals($_GET); + } + if (!empty($_POST)) { + StreamingUtilities::cleanGlobals($_POST); + } + if (!empty($_SESSION)) { + StreamingUtilities::cleanGlobals($_SESSION); + } + if (!empty($_COOKIE)) { + StreamingUtilities::cleanGlobals($_COOKIE); + } + + $rInput = @StreamingUtilities::parseIncomingRecursively($_GET, array()); + StreamingUtilities::$rRequest = @StreamingUtilities::parseIncomingRecursively($_POST, $rInput); + StreamingUtilities::$rConfig = parse_ini_file(CONFIG_PATH . 'config.ini'); + + if (!defined('SERVER_ID')) { + define('SERVER_ID', intval(StreamingUtilities::$rConfig['server_id'])); + } + + if (!StreamingUtilities::$rSettings) { + StreamingUtilities::$rSettings = StreamingUtilities::getCache('settings'); + } + + if (!empty(StreamingUtilities::$rSettings['default_timezone'])) { + date_default_timezone_set(StreamingUtilities::$rSettings['default_timezone']); + } + + if (StreamingUtilities::$rSettings['on_demand_wait_time'] == 0) { + StreamingUtilities::$rSettings['on_demand_wait_time'] = 15; + } + + switch (StreamingUtilities::$rSettings['ffmpeg_cpu']) { + case '8.0': + StreamingUtilities::$rFFMPEG_CPU = FFMPEG_BIN_80; + StreamingUtilities::$rFFMPEG_GPU = FFMPEG_BIN_80; + break; + case '7.1': + StreamingUtilities::$rFFMPEG_CPU = FFMPEG_BIN_71; + StreamingUtilities::$rFFMPEG_GPU = FFMPEG_BIN_71; + break; + case '5.1': + StreamingUtilities::$rFFMPEG_CPU = FFMPEG_BIN_51; + StreamingUtilities::$rFFMPEG_GPU = FFMPEG_BIN_40; + break; + case '4.4': + StreamingUtilities::$rFFMPEG_CPU = FFMPEG_BIN_44; + StreamingUtilities::$rFFMPEG_GPU = FFMPEG_BIN_40; + break; + case '4.3': + StreamingUtilities::$rFFMPEG_CPU = FFMPEG_BIN_43; + StreamingUtilities::$rFFMPEG_GPU = FFMPEG_BIN_40; + break; + default: + StreamingUtilities::$rFFMPEG_CPU = FFMPEG_BIN_40; + StreamingUtilities::$rFFMPEG_GPU = FFMPEG_BIN_40; + break; + } + + StreamingUtilities::$rCached = StreamingUtilities::isCacheEnabledAndComplete(); + StreamingUtilities::$rServers = StreamingUtilities::getCache('servers'); + StreamingUtilities::$rBlockedUA = StreamingUtilities::getCache('blocked_ua'); + StreamingUtilities::$rBlockedISP = StreamingUtilities::getCache('blocked_isp'); + StreamingUtilities::$rBlockedIPs = StreamingUtilities::getCache('blocked_ips'); + StreamingUtilities::$rBlockedServers = StreamingUtilities::getCache('blocked_servers'); + StreamingUtilities::$rAllowedIPs = StreamingUtilities::getCache('allowed_ips'); + StreamingUtilities::$rProxies = StreamingUtilities::getCache('proxy_servers'); + StreamingUtilities::$rSegmentSettings = array( + 'seg_time' => intval(StreamingUtilities::$rSettings['seg_time']), + 'seg_list_size' => intval(StreamingUtilities::$rSettings['seg_list_size']) + ); + StreamingUtilities::connectDatabase(); + + self::syncStreamingContainer(); + } + + private static function syncCoreContainer() { + $rContainer = ServiceContainer::getInstance(); + $rContainer->set('core.request', CoreUtilities::$rRequest); + $rContainer->set('core.config', CoreUtilities::$rConfig); + $rContainer->set('core.settings', CoreUtilities::$rSettings); + $rContainer->set('core.servers', CoreUtilities::$rServers); + $rContainer->set('core.bouquets', CoreUtilities::$rBouquets); + $rContainer->set('core.categories', CoreUtilities::$rCategories); + } + + private static function syncStreamingContainer() { + $rContainer = ServiceContainer::getInstance(); + $rContainer->set('streaming.request', StreamingUtilities::$rRequest); + $rContainer->set('streaming.config', StreamingUtilities::$rConfig); + $rContainer->set('streaming.settings', StreamingUtilities::$rSettings); + $rContainer->set('streaming.servers', StreamingUtilities::$rServers); + } +} diff --git a/src/core/Util/ImageUtils.php b/src/core/Util/ImageUtils.php new file mode 100644 index 00000000..1f917bc6 --- /dev/null +++ b/src/core/Util/ImageUtils.php @@ -0,0 +1,84 @@ + 0) { +if (count($argv) > 1) { if (intval($argv[1]) == 1) { $rForce = true; } diff --git a/src/crons/cache_engine.php b/src/crons/cache_engine.php index da24c8f6..4db9b8ff 100644 --- a/src/crons/cache_engine.php +++ b/src/crons/cache_engine.php @@ -120,6 +120,17 @@ class Multithread { public function run() { while (0 < count($this->commands)) { foreach ($this->commands as $key => $command) { + if (!isset($this->thread[$key])) { + unset($this->commands[$key]); + self::launchNextInQueue(); + continue; + } + if (!isset($this->output[$key])) { + $this->output[$key] = ''; + } + if (!isset($this->error[$key])) { + $this->error[$key] = ''; + } $this->output[$key] .= @$this->thread[$key]->listen(); $this->error[$key] .= @$this->thread[$key]->getError(); if ($this->thread[$key]->isActive()) { diff --git a/src/crons/errors.php b/src/crons/errors.php index 95af3fe6..c221578b 100644 --- a/src/crons/errors.php +++ b/src/crons/errors.php @@ -24,11 +24,14 @@ function sqlValue($value, $isNumeric = false) { } if ($isNumeric) { - return (int)$value; // unquoted integer + if (!is_numeric($value)) { + return 'NULL'; + } + + return (string) ((int) $value); } - // Proper escaping using database method + normal single quotes - return "'" . $value . "'"; + return $db->escape($value); } function parseLog(string $logFile): string { @@ -80,14 +83,14 @@ function parseLog(string $logFile): string { $query .= sprintf( "(%d,%s,%s,%s,%s,%s,%s,%s,%s),", SERVER_ID, - sqlValue($row['type'] ?? null), // string - sqlValue($row['log_message'] ?? null), // string - sqlValue("'" . $db->escape($row['log_extra']) . "'" ?? null), // string (can be long) - sqlValue($row['line'] ?? null, true), // numeric → unquoted - sqlValue($row['time'] ?? null, true), // numeric → unquoted - sqlValue($row['file'] ?? null), // string - sqlValue($row['env'] ?? null), // string - sqlValue($hash) // string (hash) + sqlValue($row['type'] ?? null), + sqlValue($row['log_message'] ?? null), + sqlValue($row['log_extra'] ?? null), + sqlValue($row['line'] ?? null, true), + sqlValue($row['time'] ?? null, true), + sqlValue($row['file'] ?? null), + sqlValue($row['env'] ?? null), + sqlValue($hash) ); } @@ -138,8 +141,10 @@ function loadCron() { if (file_exists($rLog)) { $rQuery = parseLog(LOGS_TMP_PATH . 'error_log.log'); if ($rQuery !== '') { - $db->query("INSERT IGNORE INTO panel_logs(server_id, type, log_message, log_extra, line, date, file, env, `unique`) VALUES {$rQuery};"); - unlink($rLog); + $rInserted = $db->query("INSERT IGNORE INTO panel_logs(server_id, type, log_message, log_extra, line, date, file, env, `unique`) VALUES {$rQuery};"); + if ($rInserted) { + unlink($rLog); + } } } } diff --git a/src/crons/root_signals.php b/src/crons/root_signals.php index 032d1ec7..5b2b0a8e 100644 --- a/src/crons/root_signals.php +++ b/src/crons/root_signals.php @@ -96,7 +96,7 @@ function getBlockedIPs() { exec('sudo iptables -nL --line-numbers -t filter', $rLines); foreach ($rLines as $rLine) { $rLine = explode(' ', preg_replace('!\\s+!', ' ', $rLine)); - if ($rLine[1] == 'DROP') { + if (isset($rLine[1], $rLine[4]) && $rLine[1] == 'DROP') { $rReturn[] = $rLine[4]; } } @@ -104,7 +104,7 @@ function getBlockedIPs() { exec('sudo ip6tables -nL --line-numbers -t filter', $rLines); foreach ($rLines as $rLine) { $rLine = explode(' ', preg_replace('!\\s+!', ' ', $rLine)); - if ($rLine[1] == 'DROP') { + if (isset($rLine[1], $rLine[3]) && $rLine[1] == 'DROP') { $rReturn[] = $rLine[3]; } } @@ -311,10 +311,10 @@ function loadCron() { exec('ps -fp ' . trim(shell_exec('pgrep -u xc_vm | tr "\n" "," | sed "s/,$//"')), $rOutput, $rReturnVar); foreach ($rOutput as $rProcess) { $rSplit = explode(' ', preg_replace('!\\s+!', ' ', trim($rProcess))); - if ($rSplit[8] == 'php-fpm:' && $rSplit[9] == 'master') { + if (isset($rSplit[8], $rSplit[9]) && $rSplit[8] == 'php-fpm:' && $rSplit[9] == 'master') { $rPHP++; } - if ($rSplit[8] == 'nginx:' && $rSplit[9] == 'master') { + if (isset($rSplit[8], $rSplit[9]) && $rSplit[8] == 'nginx:' && $rSplit[9] == 'master') { $rNginx++; } } diff --git a/src/domain/Bouquet/BouquetMapper.php b/src/domain/Bouquet/BouquetMapper.php new file mode 100644 index 00000000..03d41e48 --- /dev/null +++ b/src/domain/Bouquet/BouquetMapper.php @@ -0,0 +1,10 @@ +query('SELECT *, IF(`bouquet_order` > 0, `bouquet_order`, 999) AS `order` FROM `bouquets` ORDER BY `order` ASC;'); + foreach ($db->get_rows(true, 'id') as $rID => $rChannels) { + $rOutput[$rID]['streams'] = array_merge(json_decode($rChannels['bouquet_channels'], true), json_decode($rChannels['bouquet_movies'], true), json_decode($rChannels['bouquet_radios'], true)); + $rOutput[$rID]['series'] = json_decode($rChannels['bouquet_series'], true); + $rOutput[$rID]['channels'] = json_decode($rChannels['bouquet_channels'], true); + $rOutput[$rID]['movies'] = json_decode($rChannels['bouquet_movies'], true); + $rOutput[$rID]['radios'] = json_decode($rChannels['bouquet_radios'], true); + } + + if (is_callable($rSetCacheCallback)) { + call_user_func($rSetCacheCallback, 'bouquets', $rOutput); + } + + return $rOutput; + } +} diff --git a/src/domain/Server/ServerRepository.php b/src/domain/Server/ServerRepository.php new file mode 100644 index 00000000..9039e430 --- /dev/null +++ b/src/domain/Server/ServerRepository.php @@ -0,0 +1,83 @@ +query('SELECT * FROM `servers`'); + $rServers = array(); + $rOnlineStatus = array(1); + + foreach ($db->get_rows() as $rRow) { + if (empty($rRow['domain_name'])) { + $rURL = escapeshellcmd($rRow['server_ip']); + } else { + $rURL = str_replace(array('http://', '/', 'https://'), '', escapeshellcmd(explode(',', $rRow['domain_name'])[0])); + } + + if ($rRow['enable_https'] == 1) { + $rProtocol = 'https'; + } else { + $rProtocol = 'http'; + } + + $rPort = ($rProtocol == 'http' ? intval($rRow['http_broadcast_port']) : intval($rRow['https_broadcast_port'])); + $rRow['server_protocol'] = $rProtocol; + $rRow['request_port'] = $rPort; + $rRow['site_url'] = $rProtocol . '://' . $rURL . ':' . $rPort . '/'; + $rRow['http_url'] = 'http://' . $rURL . ':' . intval($rRow['http_broadcast_port']) . '/'; + $rRow['https_url'] = 'https://' . $rURL . ':' . intval($rRow['https_broadcast_port']) . '/'; + $rRow['rtmp_server'] = 'rtmp://' . $rURL . ':' . intval($rRow['rtmp_port']) . '/live/'; + $rRow['domains'] = array('protocol' => $rProtocol, 'port' => $rPort, 'urls' => array_filter(array_map('escapeshellcmd', explode(',', $rRow['domain_name'])))); + $rRow['rtmp_mport_url'] = 'http://127.0.0.1:31210/'; + $rRow['api_url_ip'] = 'http://' . escapeshellcmd($rRow['server_ip']) . ':' . intval($rRow['http_broadcast_port']) . '/api?password=' . urlencode($rSettings['live_streaming_pass']); + $rRow['api_url'] = $rRow['api_url_ip']; + $rRow['site_url_ip'] = $rProtocol . '://' . escapeshellcmd($rRow['server_ip']) . ':' . $rPort . '/'; + $rRow['private_url_ip'] = (!empty($rRow['private_ip']) ? 'http://' . escapeshellcmd($rRow['private_ip']) . ':' . intval($rRow['http_broadcast_port']) . '/' : null); + $rRow['public_url_ip'] = 'http://' . escapeshellcmd($rRow['server_ip']) . ':' . intval($rRow['http_broadcast_port']) . '/'; + $rRow['geoip_countries'] = (empty($rRow['geoip_countries']) ? array() : json_decode($rRow['geoip_countries'], true)); + $rRow['isp_names'] = (empty($rRow['isp_names']) ? array() : json_decode($rRow['isp_names'], true)); + + if (is_numeric($rRow['parent_id'])) { + $rRow['parent_id'] = array(intval($rRow['parent_id'])); + } else { + $decoded = json_decode($rRow['parent_id'] ?? '', true); + $rRow['parent_id'] = is_array($decoded) ? array_map('intval', $decoded) : []; + } + + if ($rRow['enable_https'] == 2) { + $rRow['allow_http'] = false; + } else { + $rRow['allow_http'] = true; + } + + if ($rRow['server_type'] == 1) { + $rLastCheckTime = 180; + } else { + $rLastCheckTime = 90; + } + + $rRow['watchdog'] = json_decode($rRow['watchdog_data'], true); + $rRow['server_online'] = $rRow['enabled'] && in_array($rRow['status'], $rOnlineStatus) && time() - $rRow['last_check_ago'] <= $rLastCheckTime || SERVER_ID == $rRow['id']; + if (!isset($rRow['order'])) { + $rRow['order'] = 0; + } + $rServers[intval($rRow['id'])] = $rRow; + } + + if (is_callable($rSetCacheCallback)) { + call_user_func($rSetCacheCallback, 'servers', $rServers); + } + + return $rServers; + } +} diff --git a/src/domain/Stream/CategoryRepository.php b/src/domain/Stream/CategoryRepository.php new file mode 100644 index 00000000..7e7df80b --- /dev/null +++ b/src/domain/Stream/CategoryRepository.php @@ -0,0 +1,37 @@ +query('SELECT t1.* FROM `streams_categories` t1 WHERE t1.category_type = ? GROUP BY t1.id ORDER BY t1.cat_order ASC', $rType); + return (0 < $db->num_rows() ? $db->get_rows(true, 'id') : array()); + } + + if (!$rForce && is_callable($rGetCacheCallback)) { + $rCache = call_user_func($rGetCacheCallback, 'categories', 20); + if (!empty($rCache)) { + return $rCache; + } + } + + $db->query('SELECT t1.* FROM `streams_categories` t1 ORDER BY t1.cat_order ASC'); + $rCategories = (0 < $db->num_rows() ? $db->get_rows(true, 'id') : array()); + + if (is_callable($rSetCacheCallback)) { + call_user_func($rSetCacheCallback, 'categories', $rCategories); + } + + return $rCategories; + } + + public static function filterLoaded($rCategories, $rType = null) { + $rReturn = array(); + foreach ($rCategories as $rCategory) { + if ($rCategory['category_type'] != $rType && $rType) { + } else { + $rReturn[] = $rCategory; + } + } + return $rReturn; + } +} diff --git a/src/domain/Stream/ConnectionTracker.php b/src/domain/Stream/ConnectionTracker.php new file mode 100644 index 00000000..ff2f486e --- /dev/null +++ b/src/domain/Stream/ConnectionTracker.php @@ -0,0 +1,401 @@ +multi(); + foreach (array_keys($rServers) as $rServerID) { + if ($rServers[$rServerID]['server_online']) { + $rMulti->zCard((($rProxy ? 'PROXY#' : 'SERVER#')) . $rServerID); + } + } + $rResults = $rMulti->exec(); + $i = 0; + foreach (array_keys($rServers) as $rServerID) { + if ($rServers[$rServerID]['server_online']) { + $rRows[$rServerID] = array('online_clients' => ($rResults[$i] ?: 0)); + $i++; + } + } + } else { + if ($rProxy) { + $db->query('SELECT `proxy_id`, COUNT(*) AS `online_clients` FROM `lines_live` WHERE `proxy_id` <> 0 AND `hls_end` = 0 GROUP BY `proxy_id`;'); + $rRows = $db->get_rows(true, 'proxy_id'); + } else { + $db->query('SELECT `server_id`, COUNT(*) AS `online_clients` FROM `lines_live` WHERE `server_id` <> 0 AND `hls_end` = 0 GROUP BY `server_id`;'); + $rRows = $db->get_rows(true, 'server_id'); + } + } + + if ($rSettings['split_by'] == 'band') { + $rServerSpeed = array(); + foreach (array_keys($rServers) as $rServerID) { + $rServerHardware = json_decode($rServers[$rServerID]['server_hardware'], true); + if (!empty($rServerHardware['network_speed'])) { + $rServerSpeed[$rServerID] = (float) $rServerHardware['network_speed']; + } else { + if (0 < $rServers[$rServerID]['network_guaranteed_speed']) { + $rServerSpeed[$rServerID] = $rServers[$rServerID]['network_guaranteed_speed']; + } else { + $rServerSpeed[$rServerID] = 1000; + } + } + } + foreach ($rRows as $rServerID => $rRow) { + $rCurrentOutput = intval($rServers[$rServerID]['watchdog']['bytes_sent'] / 125000); + $rRows[$rServerID]['capacity'] = (float) ($rCurrentOutput / (($rServerSpeed[$rServerID] ?: 1000))); + } + } else { + if ($rSettings['split_by'] == 'maxclients') { + foreach ($rRows as $rServerID => $rRow) { + $rRows[$rServerID]['capacity'] = (float) ($rRow['online_clients'] / (($rServers[$rServerID]['total_clients'] ?: 1))); + } + } else { + if ($rSettings['split_by'] == 'guar_band') { + foreach ($rRows as $rServerID => $rRow) { + $rCurrentOutput = intval($rServers[$rServerID]['watchdog']['bytes_sent'] / 125000); + $rRows[$rServerID]['capacity'] = (float) ($rCurrentOutput / (($rServers[$rServerID]['network_guaranteed_speed'] ?: 1))); + } + } else { + foreach ($rRows as $rServerID => $rRow) { + $rRows[$rServerID]['capacity'] = $rRow['online_clients']; + } + } + } + } + + file_put_contents(CACHE_TMP_PATH . $rFile, json_encode($rRows), LOCK_EX); + return $rRows; + } + + public static function getConnections($rSettings, $rRedis, $db, $rServerID = null, $rUserID = null, $rStreamID = null) { + if ($rSettings['redis_handler']) { + if ($rServerID) { + $rKeys = $rRedis->zRangeByScore('SERVER#' . $rServerID, '-inf', '+inf'); + } elseif ($rUserID) { + $rKeys = $rRedis->zRangeByScore('LINE#' . $rUserID, '-inf', '+inf'); + } elseif ($rStreamID) { + $rKeys = $rRedis->zRangeByScore('STREAM#' . $rStreamID, '-inf', '+inf'); + } else { + $rKeys = $rRedis->zRangeByScore('LIVE', '-inf', '+inf'); + } + + if (count($rKeys) > 0) { + return array($rKeys, array_map('igbinary_unserialize', $rRedis->mGet($rKeys))); + } + return array([], []); + } + + $rWhere = array(); + if (!empty($rServerID)) { + $rWhere[] = 't1.server_id = ' . intval($rServerID); + } + if (!empty($rUserID)) { + $rWhere[] = 't1.user_id = ' . intval($rUserID); + } + $rExtra = count($rWhere) ? 'WHERE ' . implode(' AND ', $rWhere) : ''; + $rQuery = 'SELECT t2.*,t3.*,t5.bitrate,t1.*,t1.uuid AS `uuid` + FROM `lines_live` t1 + LEFT JOIN `lines` t2 ON t2.id = t1.user_id + LEFT JOIN `streams` t3 ON t3.id = t1.stream_id + LEFT JOIN `streams_servers` t5 ON t5.stream_id = t1.stream_id AND t5.server_id = t1.server_id + ' . $rExtra . ' + ORDER BY t1.activity_id ASC'; + $db->query($rQuery); + return $db->get_rows(true, 'user_id', false); + } + + public static function getMainID($rServers) { + foreach ($rServers as $rServerID => $rServer) { + if ($rServer['is_main']) { + return $rServerID; + } + } + } + + public static function addToQueue($rStreamID, $rAddPID, $rProcessCheckCallback) { + $rActivePIDs = $rPIDs = array(); + if (file_exists(SIGNALS_TMP_PATH . 'queue_' . intval($rStreamID))) { + $rPIDs = igbinary_unserialize(file_get_contents(SIGNALS_TMP_PATH . 'queue_' . intval($rStreamID))); + } + foreach ($rPIDs as $rPID) { + if (call_user_func($rProcessCheckCallback, $rPID, 'php-fpm')) { + $rActivePIDs[] = $rPID; + } + } + if (!in_array($rAddPID, $rActivePIDs, true)) { + $rActivePIDs[] = $rAddPID; + } + file_put_contents(SIGNALS_TMP_PATH . 'queue_' . intval($rStreamID), igbinary_serialize($rActivePIDs), LOCK_EX); + } + + public static function removeFromQueue($rStreamID, $rPID, $rProcessCheckCallback) { + $rActivePIDs = array(); + foreach ((igbinary_unserialize(file_get_contents(SIGNALS_TMP_PATH . 'queue_' . intval($rStreamID))) ?: array()) as $rActivePID) { + if (call_user_func($rProcessCheckCallback, $rActivePID, 'php-fpm') && $rPID != $rActivePID) { + $rActivePIDs[] = $rActivePID; + } + } + if (0 < count($rActivePIDs)) { + file_put_contents(SIGNALS_TMP_PATH . 'queue_' . intval($rStreamID), igbinary_serialize($rActivePIDs), LOCK_EX); + } else { + @unlink(SIGNALS_TMP_PATH . 'queue_' . intval($rStreamID)); + } + } + + public static function updateConnection($rRedis, $rData, $rChanges = array(), $rOption = null) { + $rOrigData = $rData; + foreach ($rChanges as $rKey => $rValue) { + $rData[$rKey] = $rValue; + } + $rMulti = $rRedis->multi(); + if ($rOption == 'open') { + $rMulti->sRem('ENDED', $rData['uuid']); + $rMulti->zAdd('LIVE', $rData['date_start'], $rData['uuid']); + $rMulti->zAdd('LINE#' . $rData['identity'], $rData['date_start'], $rData['uuid']); + $rMulti->zAdd('STREAM#' . $rData['stream_id'], $rData['date_start'], $rData['uuid']); + $rMulti->zAdd('SERVER#' . $rData['server_id'], $rData['date_start'], $rData['uuid']); + if ($rData['proxy_id']) { + $rMulti->zAdd('PROXY#' . $rData['proxy_id'], $rData['date_start'], $rData['uuid']); + } + if ($rData['hls_end'] == 1) { + $rData['hls_end'] = 0; + if ($rData['user_id']) { + $rMulti->zAdd('SERVER_LINES#' . $rData['server_id'], $rData['user_id'], $rData['uuid']); + } + } + } else { + if ($rOption == 'close') { + $rMulti->sAdd('ENDED', $rData['uuid']); + $rMulti->zRem('LIVE', $rData['uuid']); + $rMulti->zRem('LINE#' . $rOrigData['identity'], $rData['uuid']); + $rMulti->zRem('STREAM#' . $rOrigData['stream_id'], $rData['uuid']); + $rMulti->zRem('SERVER#' . $rOrigData['server_id'], $rData['uuid']); + if ($rData['proxy_id']) { + $rMulti->zRem('PROXY#' . $rOrigData['proxy_id'], $rData['uuid']); + } + if ($rData['hls_end'] == 0) { + $rData['hls_end'] = 1; + if ($rData['user_id']) { + $rMulti->zRem('SERVER_LINES#' . $rOrigData['server_id'], $rData['uuid']); + } + } + } + } + $rMulti->set($rData['uuid'], igbinary_serialize($rData)); + if ($rMulti->exec()) { + return $rData; + } + return null; + } + + public static function redisSignal($rRedis, $rPID, $rServerID, $rRTMP, $rCustomData = null) { + $rKey = 'SIGNAL#' . md5($rServerID . '#' . $rPID . '#' . $rRTMP); + $rData = array('pid' => $rPID, 'server_id' => $rServerID, 'rtmp' => $rRTMP, 'time' => time(), 'custom_data' => $rCustomData, 'key' => $rKey); + return $rRedis->multi()->sAdd('SIGNALS#' . $rServerID, $rKey)->set($rKey, igbinary_serialize($rData))->exec(); + } + + public static function getUserConnections($rRedis, $rUserIDs, $rCount = false, $rKeysOnly = false) { + $rMulti = $rRedis->multi(); + foreach ($rUserIDs as $rUserID) { + $rMulti->zRevRangeByScore('LINE#' . $rUserID, '+inf', '-inf'); + } + $rGroups = $rMulti->exec(); + $rConnectionMap = $rRedisKeys = array(); + foreach ($rGroups as $rGroupID => $rKeys) { + if ($rCount) { + $rConnectionMap[$rUserIDs[$rGroupID]] = count($rKeys); + } else { + if (0 < count($rKeys)) { + $rRedisKeys = array_merge($rRedisKeys, $rKeys); + } + } + } + $rRedisKeys = array_unique($rRedisKeys); + if (!$rKeysOnly) { + if (!$rCount) { + foreach ($rRedis->mGet($rRedisKeys) as $rRow) { + $rRow = igbinary_unserialize($rRow); + $rConnectionMap[$rRow['user_id']][] = $rRow; + } + } + return $rConnectionMap; + } + return $rRedisKeys; + } + + public static function getServerConnections($rRedis, $rServerIDs, $rProxy = false, $rCount = false, $rKeysOnly = false) { + $rMulti = $rRedis->multi(); + foreach ($rServerIDs as $rServerID) { + $rMulti->zRevRangeByScore(($rProxy ? 'PROXY#' . $rServerID : 'SERVER#' . $rServerID), '+inf', '-inf'); + } + $rGroups = $rMulti->exec(); + $rConnectionMap = $rRedisKeys = array(); + foreach ($rGroups as $rGroupID => $rKeys) { + if ($rCount) { + $rConnectionMap[$rServerIDs[$rGroupID]] = count($rKeys); + } else { + if (0 < count($rKeys)) { + $rRedisKeys = array_merge($rRedisKeys, $rKeys); + } + } + } + $rRedisKeys = array_unique($rRedisKeys); + if (!$rKeysOnly) { + if (!$rCount) { + foreach ($rRedis->mGet($rRedisKeys) as $rRow) { + $rRow = igbinary_unserialize($rRow); + $rConnectionMap[$rRow['server_id']][] = $rRow; + } + } + return $rConnectionMap; + } + return $rRedisKeys; + } + + public static function getFirstConnection($rRedis, $rUserIDs) { + $rMulti = $rRedis->multi(); + foreach ($rUserIDs as $rUserID) { + $rMulti->zRevRangeByScore('LINE#' . $rUserID, '+inf', '-inf', array('limit' => array(0, 1))); + } + $rGroups = $rMulti->exec(); + $rConnectionMap = $rRedisKeys = array(); + foreach ($rGroups as $rKeys) { + if (0 < count($rKeys)) { + $rRedisKeys[] = $rKeys[0]; + } + } + foreach ($rRedis->mGet(array_unique($rRedisKeys)) as $rRow) { + $rRow = igbinary_unserialize($rRow); + $rConnectionMap[$rRow['user_id']] = $rRow; + } + return $rConnectionMap; + } + + public static function getStreamConnections($rRedis, $rStreamIDs, $rGroup = true, $rCount = false) { + $rMulti = $rRedis->multi(); + foreach ($rStreamIDs as $rStreamID) { + $rMulti->zRevRangeByScore('STREAM#' . $rStreamID, '+inf', '-inf'); + } + $rGroups = $rMulti->exec(); + $rConnectionMap = $rRedisKeys = array(); + foreach ($rGroups as $rGroupID => $rKeys) { + if ($rCount) { + $rConnectionMap[$rStreamIDs[$rGroupID]] = count($rKeys); + } else { + if (0 < count($rKeys)) { + $rRedisKeys = array_merge($rRedisKeys, $rKeys); + } + } + } + if (!$rCount) { + foreach ($rRedis->mGet(array_unique($rRedisKeys)) as $rRow) { + $rRow = igbinary_unserialize($rRow); + if ($rGroup) { + $rConnectionMap[$rRow['stream_id']][] = $rRow; + } else { + $rConnectionMap[$rRow['stream_id']][$rRow['server_id']][] = $rRow; + } + } + } + return $rConnectionMap; + } + + public static function getRedisConnections($rRedis, $rUserID = null, $rServerID = null, $rStreamID = null, $rOpenOnly = false, $rCountOnly = false, $rGroup = true, $rHLSOnly = false) { + $rReturn = ($rCountOnly ? array(0, 0) : array()); + $rUniqueUsers = array(); + $rUserID = (0 < intval($rUserID) ? intval($rUserID) : null); + $rServerID = (0 < intval($rServerID) ? intval($rServerID) : null); + $rStreamID = (0 < intval($rStreamID) ? intval($rStreamID) : null); + + if ($rUserID) { + $rKeys = $rRedis->zRangeByScore('LINE#' . $rUserID, '-inf', '+inf'); + } else { + if ($rStreamID) { + $rKeys = $rRedis->zRangeByScore('STREAM#' . $rStreamID, '-inf', '+inf'); + } else { + if ($rServerID) { + $rKeys = $rRedis->zRangeByScore('SERVER#' . $rServerID, '-inf', '+inf'); + } else { + $rKeys = $rRedis->zRangeByScore('LIVE', '-inf', '+inf'); + } + } + } + + if (0 < count($rKeys)) { + foreach ($rRedis->mGet(array_unique($rKeys)) as $rRow) { + $rRow = igbinary_unserialize($rRow); + if (!($rServerID && $rServerID != $rRow['server_id']) && !($rStreamID && $rStreamID != $rRow['stream_id']) && !($rUserID && $rUserID != $rRow['user_id']) && !($rHLSOnly && $rRow['container'] == 'hls')) { + $rUUID = ($rRow['user_id'] ?: $rRow['hmac_id'] . '_' . $rRow['hmac_identifier']); + if ($rCountOnly) { + $rReturn[0]++; + $rUniqueUsers[] = $rUUID; + } else { + if ($rGroup) { + if (!isset($rReturn[$rUUID])) { + $rReturn[$rUUID] = array(); + } + $rReturn[$rUUID][] = $rRow; + } else { + $rReturn[] = $rRow; + } + } + } + } + } + + if ($rCountOnly) { + $rReturn[1] = count(array_unique($rUniqueUsers)); + } + return $rReturn; + } + + public static function getConnection($rRedis, $rUUID) { + return igbinary_unserialize($rRedis->get($rUUID)); + } + + public static function createConnection($rRedis, $rData) { + $rMulti = $rRedis->multi(); + $rMulti->zAdd('LINE#' . $rData['identity'], $rData['date_start'], $rData['uuid']); + $rMulti->zAdd('LINE_ALL#' . $rData['identity'], $rData['date_start'], $rData['uuid']); + $rMulti->zAdd('STREAM#' . $rData['stream_id'], $rData['date_start'], $rData['uuid']); + $rMulti->zAdd('SERVER#' . $rData['server_id'], $rData['date_start'], $rData['uuid']); + if ($rData['user_id']) { + $rMulti->zAdd('SERVER_LINES#' . $rData['server_id'], $rData['user_id'], $rData['uuid']); + } + if ($rData['proxy_id']) { + $rMulti->zAdd('PROXY#' . $rData['proxy_id'], $rData['date_start'], $rData['uuid']); + } + $rMulti->zAdd('CONNECTIONS', $rData['date_start'], $rData['uuid']); + $rMulti->zAdd('LIVE', $rData['date_start'], $rData['uuid']); + $rMulti->set($rData['uuid'], igbinary_serialize($rData)); + return $rMulti->exec(); + } + + public static function getLineConnections($rRedis, $rUserID, $rActive = false, $rKeys = false) { + $rKeys = $rRedis->zRangeByScore((($rActive ? 'LINE#' : 'LINE_ALL#')) . $rUserID, '-inf', '+inf'); + if ($rKeys) { + return $rKeys; + } + if (0 >= count($rKeys)) { + return array(); + } + return array_map('igbinary_unserialize', $rRedis->mGet($rKeys)); + } + + public static function getProxies($rServers, $rServerID, $rOnline = true) { + $rReturn = array(); + foreach ($rServers as $rProxyID => $rServerInfo) { + if ($rServerInfo['server_type'] == 1 && in_array($rServerID, $rServerInfo['parent_id']) && ($rServerInfo['server_online'] || !$rOnline)) { + $rReturn[$rProxyID] = $rServerInfo; + } + } + return $rReturn; + } +} diff --git a/src/domain/Stream/StreamSorter.php b/src/domain/Stream/StreamSorter.php new file mode 100644 index 00000000..0e7b18fe --- /dev/null +++ b/src/domain/Stream/StreamSorter.php @@ -0,0 +1,69 @@ + intval(self::$rSettings['seg_time']), 'seg_list_size' => intval(self::$rSettings['seg_list_size']), 'seg_delete_threshold' => intval(self::$rSettings['seg_delete_threshold'])); - switch (self::$rSettings['ffmpeg_cpu']) { - case '8.0': - self::$rFFMPEG_CPU = FFMPEG_BIN_80; - self::$rFFPROBE = FFPROBE_BIN_80; - self::$rFFMPEG_GPU = FFMPEG_BIN_80; - break; - case '7.1': - self::$rFFMPEG_CPU = FFMPEG_BIN_71; - self::$rFFPROBE = FFPROBE_BIN_71; - self::$rFFMPEG_GPU = FFMPEG_BIN_71; - break; - case '5.1': - self::$rFFMPEG_CPU = FFMPEG_BIN_51; - self::$rFFPROBE = FFPROBE_BIN_51; - self::$rFFMPEG_GPU = FFMPEG_BIN_40; - break; - case '4.4': - self::$rFFMPEG_CPU = FFMPEG_BIN_44; - self::$rFFPROBE = FFPROBE_BIN_44; - self::$rFFMPEG_GPU = FFMPEG_BIN_40; - break; - case '4.3': - self::$rFFMPEG_CPU = FFMPEG_BIN_43; - self::$rFFPROBE = FFPROBE_BIN_43; - self::$rFFMPEG_GPU = FFMPEG_BIN_40; - break; - default: - self::$rFFMPEG_CPU = FFMPEG_BIN_40; - self::$rFFPROBE = FFPROBE_BIN_40; - self::$rFFMPEG_GPU = FFMPEG_BIN_40; - break; - } - - self::$rCached = self::$rSettings['enable_cache']; - if ($rUseCache) { - self::$rServers = self::getCache('servers'); - self::$rBouquets = self::getCache('bouquets'); - self::$rBlockedUA = self::getCache('blocked_ua'); - self::$rBlockedISP = self::getCache('blocked_isp'); - self::$rBlockedIPs = self::getCache('blocked_ips'); - self::$rProxies = self::getCache('proxy_servers'); - self::$rBlockedServers = self::getCache('blocked_servers'); - self::$rAllowedDomains = self::getCache('allowed_domains'); - self::$rAllowedIPs = self::getCache('allowed_ips'); - self::$rCategories = self::getCache('categories'); - } else { - self::$rServers = self::getServers(); - self::$rBouquets = self::getBouquets(); - self::$rBlockedUA = self::getBlockedUA(); - self::$rBlockedISP = self::getBlockedISP(); - self::$rBlockedIPs = self::getBlockedIPs(); - self::$rProxies = self::getProxyIPs(); - self::$rBlockedServers = self::getBlockedServers(); - self::$rAllowedDomains = self::getAllowedDomains(); - self::$rAllowedIPs = self::getAllowedIPs(); - self::$rCategories = self::getCategories(); - self::generateCron(); - } + LegacyInitializer::initCore($rUseCache); } public static function getDiffTimezone($rTimezone) { $rServerTZ = new DateTime('UTC', new DateTimeZone(date_default_timezone_get())); @@ -240,54 +151,10 @@ class CoreUtilities { return $rOutput; } public static function getBouquets($rForce = false) { - if ($rForce) { - } else { - $rCache = self::getCache('bouquets', 60); - if (empty($rCache)) { - } else { - return $rCache; - } - } - $rOutput = array(); - self::$db->query('SELECT *, IF(`bouquet_order` > 0, `bouquet_order`, 999) AS `order` FROM `bouquets` ORDER BY `order` ASC;'); - foreach (self::$db->get_rows(true, 'id') as $rID => $rChannels) { - $rOutput[$rID]['streams'] = array_merge(json_decode($rChannels['bouquet_channels'], true), json_decode($rChannels['bouquet_movies'], true), json_decode($rChannels['bouquet_radios'], true)); - $rOutput[$rID]['series'] = json_decode($rChannels['bouquet_series'], true); - $rOutput[$rID]['channels'] = json_decode($rChannels['bouquet_channels'], true); - $rOutput[$rID]['movies'] = json_decode($rChannels['bouquet_movies'], true); - $rOutput[$rID]['radios'] = json_decode($rChannels['bouquet_radios'], true); - } - self::setCache('bouquets', $rOutput); - return $rOutput; + return BouquetRepository::getAll(self::$db, array('CoreUtilities', 'getCache'), array('CoreUtilities', 'setCache'), $rForce); } public static function getSettings($rForce = false) { - if (!$rForce) { - $rCache = self::getCache('settings', 20); - if (!empty($rCache)) { - return $rCache; - } - } - $rOutput = array(); - self::$db->query('SELECT * FROM `settings`'); - $rRows = self::$db->get_row(); - foreach ($rRows as $rKey => $rValue) { - $rOutput[$rKey] = $rValue; - } - $rOutput['allow_countries'] = json_decode($rOutput['allow_countries'], true); - - $decodedAllowedSTB = json_decode($rOutput['allowed_stb_types'], true); - $rOutput['allowed_stb_types'] = array(); - if (is_array($decodedAllowedSTB)) { - $rOutput['allowed_stb_types'] = array_map('strtolower', $decodedAllowedSTB); - } - - $rOutput['stalker_lock_images'] = json_decode($rOutput['stalker_lock_images'], true); - if (array_key_exists('bouquet_name', $rOutput)) { - $rOutput['bouquet_name'] = str_replace(' ', '_', $rOutput['bouquet_name']); - } - $rOutput['api_ips'] = !empty($rOutput['api_ips']) ? explode(',', $rOutput['api_ips']) : []; - self::setCache('settings', $rOutput); - return $rOutput; + return SettingsRepository::getAll(self::$db, array('CoreUtilities', 'getCache'), array('CoreUtilities', 'setCache'), $rForce); } public static function setCache($rCache, $rData) { $rData = igbinary_serialize($rData); @@ -304,127 +171,10 @@ class CoreUtilities { return false; } public static function getServers($rForce = false) { - if (!$rForce) { - $rCache = self::getCache('servers', 10); - if (!empty($rCache)) { - return $rCache; - } - } - if (empty($_SERVER['REQUEST_SCHEME'])) { - $_SERVER['REQUEST_SCHEME'] = 'http'; - } - self::$db->query('SELECT * FROM `servers`'); - $rServers = array(); - $rOnlineStatus = array(1); - foreach (self::$db->get_rows() as $rRow) { - if (empty($rRow['domain_name'])) { - $rURL = escapeshellcmd($rRow['server_ip']); - } else { - $rURL = str_replace(array('http://', '/', 'https://'), '', escapeshellcmd(explode(',', $rRow['domain_name'])[0])); - } - if ($rRow['enable_https'] == 1) { - $rProtocol = 'https'; - } else { - $rProtocol = 'http'; - } - $rPort = ($rProtocol == 'http' ? intval($rRow['http_broadcast_port']) : intval($rRow['https_broadcast_port'])); - $rRow['server_protocol'] = $rProtocol; - $rRow['request_port'] = $rPort; - $rRow['site_url'] = $rProtocol . '://' . $rURL . ':' . $rPort . '/'; - $rRow['http_url'] = 'http://' . $rURL . ':' . intval($rRow['http_broadcast_port']) . '/'; - $rRow['https_url'] = 'https://' . $rURL . ':' . intval($rRow['https_broadcast_port']) . '/'; - $rRow['rtmp_server'] = 'rtmp://' . $rURL . ':' . intval($rRow['rtmp_port']) . '/live/'; - $rRow['domains'] = array('protocol' => $rProtocol, 'port' => $rPort, 'urls' => array_filter(array_map('escapeshellcmd', explode(',', $rRow['domain_name'])))); - $rRow['rtmp_mport_url'] = 'http://127.0.0.1:31210/'; - $rRow['api_url_ip'] = 'http://' . escapeshellcmd($rRow['server_ip']) . ':' . intval($rRow['http_broadcast_port']) . '/api?password=' . urlencode(self::$rSettings['live_streaming_pass']); - $rRow['api_url'] = $rRow['api_url_ip']; - $rRow['site_url_ip'] = $rProtocol . '://' . escapeshellcmd($rRow['server_ip']) . ':' . $rPort . '/'; - $rRow['private_url_ip'] = (!empty($rRow['private_ip']) ? 'http://' . escapeshellcmd($rRow['private_ip']) . ':' . intval($rRow['http_broadcast_port']) . '/' : null); - $rRow['public_url_ip'] = 'http://' . escapeshellcmd($rRow['server_ip']) . ':' . intval($rRow['http_broadcast_port']) . '/'; - $rRow['geoip_countries'] = (empty($rRow['geoip_countries']) ? array() : json_decode($rRow['geoip_countries'], true)); - $rRow['isp_names'] = (empty($rRow['isp_names']) ? array() : json_decode($rRow['isp_names'], true)); - if (is_numeric($rRow['parent_id'])) { - $rRow['parent_id'] = array(intval($rRow['parent_id'])); - } else { - $decoded = json_decode($rRow['parent_id'] ?? '', true); - $rRow['parent_id'] = is_array($decoded) ? array_map('intval', $decoded) : []; - } - - if ($rRow['enable_https'] == 2) { - $rRow['allow_http'] = false; - } else { - $rRow['allow_http'] = true; - } - if ($rRow['server_type'] == 1) { - $rLastCheckTime = 180; - } else { - $rLastCheckTime = 90; - } - $rRow['watchdog'] = json_decode($rRow['watchdog_data'], true); - $rRow['server_online'] = $rRow['enabled'] && in_array($rRow['status'], $rOnlineStatus) && time() - $rRow['last_check_ago'] <= $rLastCheckTime || SERVER_ID == $rRow['id']; - if (!isset($rRow['order'])) { - $rRow['order'] = 0; - } - $rServers[intval($rRow['id'])] = $rRow; - } - self::setCache('servers', $rServers); - return $rServers; + return ServerRepository::getAll(self::$db, self::$rSettings, array('CoreUtilities', 'getCache'), array('CoreUtilities', 'setCache'), $rForce); } public static function getMultiCURL($rURLs, $callback = null, $rTimeout = 5) { - if (!empty($rURLs)) { - $rOffline = array(); - $rCurl = array(); - $rResults = array(); - $rMulti = curl_multi_init(); - foreach ($rURLs as $rKey => $rValue) { - if (self::$rServers[$rKey]['server_online']) { - $rCurl[$rKey] = curl_init(); - curl_setopt($rCurl[$rKey], CURLOPT_URL, $rValue['url']); - curl_setopt($rCurl[$rKey], CURLOPT_RETURNTRANSFER, true); - curl_setopt($rCurl[$rKey], CURLOPT_FOLLOWLOCATION, true); - curl_setopt($rCurl[$rKey], CURLOPT_CONNECTTIMEOUT, 5); - curl_setopt($rCurl[$rKey], CURLOPT_TIMEOUT, $rTimeout); - curl_setopt($rCurl[$rKey], CURLOPT_SSL_VERIFYHOST, 0); - curl_setopt($rCurl[$rKey], CURLOPT_SSL_VERIFYPEER, 0); - if ($rValue['postdata'] == null) { - } else { - curl_setopt($rCurl[$rKey], CURLOPT_POST, true); - curl_setopt($rCurl[$rKey], CURLOPT_POSTFIELDS, http_build_query($rValue['postdata'])); - } - curl_multi_add_handle($rMulti, $rCurl[$rKey]); - } else { - $rOffline[] = $rKey; - } - } - $rActive = null; - do { - $rMultiExec = curl_multi_exec($rMulti, $rActive); - } while ($rMultiExec == CURLM_CALL_MULTI_PERFORM); - while ($rActive && $rMultiExec == CURLM_OK) { - if (curl_multi_select($rMulti) != -1) { - } else { - usleep(50000); - } - do { - $rMultiExec = curl_multi_exec($rMulti, $rActive); - } while ($rMultiExec == CURLM_CALL_MULTI_PERFORM); - } - foreach ($rCurl as $rKey => $rValue) { - $rResults[$rKey] = curl_multi_getcontent($rValue); - if ($callback == null) { - } else { - $rResults[$rKey] = call_user_func($callback, $rResults[$rKey], true); - } - curl_multi_remove_handle($rMulti, $rValue); - } - foreach ($rOffline as $rKey) { - $rResults[$rKey] = false; - } - curl_multi_close($rMulti); - return $rResults; - } else { - return array(); - } + return CurlClient::getMultiCURL(self::$rServers, $rURLs, $callback, $rTimeout); } public static function cleanGlobals(&$rData, $rIteration = 0) { if (10 > $rIteration) { @@ -565,22 +315,7 @@ class CoreUtilities { return BruteforceGuard::truncateAttempts($rAttempts, $rFrequency, $rList); } public static function getCategories($rType = null, $rForce = false) { - if (is_string($rType)) { - self::$db->query('SELECT t1.* FROM `streams_categories` t1 WHERE t1.category_type = ? GROUP BY t1.id ORDER BY t1.cat_order ASC', $rType); - return (0 < self::$db->num_rows() ? self::$db->get_rows(true, 'id') : array()); - } - if ($rForce) { - } else { - $rCache = self::getCache('categories', 20); - if (empty($rCache)) { - } else { - return $rCache; - } - } - self::$db->query('SELECT t1.* FROM `streams_categories` t1 ORDER BY t1.cat_order ASC'); - $rCategories = (0 < self::$db->num_rows() ? self::$db->get_rows(true, 'id') : array()); - self::setCache('categories', $rCategories); - return $rCategories; + return CategoryRepository::getFromDatabase(self::$db, array('CoreUtilities', 'getCache'), array('CoreUtilities', 'setCache'), $rType, $rForce); } public static function generateUniqueCode() { return substr(md5(self::$rSettings['live_streaming_pass']), 0, 15); @@ -1113,39 +848,7 @@ class CoreUtilities { return false; } public static function serverRequest($rServerID, $rURL, $rPostData = array()) { - if (is_array(self::$rServers) && isset(self::$rServers[$rServerID]) && self::$rServers[$rServerID]['server_online']) { - $rOutput = false; - $i = 1; - while ($i <= 2) { - $rCurl = curl_init(); - curl_setopt($rCurl, CURLOPT_URL, $rURL); - curl_setopt($rCurl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:9.0) Gecko/20100101 Firefox/9.0'); - curl_setopt($rCurl, CURLOPT_HEADER, 0); - curl_setopt($rCurl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($rCurl, CURLOPT_CONNECTTIMEOUT, 10); - curl_setopt($rCurl, CURLOPT_TIMEOUT, 10); - curl_setopt($rCurl, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($rCurl, CURLOPT_FRESH_CONNECT, true); - curl_setopt($rCurl, CURLOPT_FORBID_REUSE, true); - curl_setopt($rCurl, CURLOPT_SSL_VERIFYHOST, 0); - curl_setopt($rCurl, CURLOPT_SSL_VERIFYPEER, 0); - if (empty($rPostData)) { - } else { - curl_setopt($rCurl, CURLOPT_POST, true); - curl_setopt($rCurl, CURLOPT_POSTFIELDS, http_build_query($rPostData)); - } - $rOutput = curl_exec($rCurl); - $rResponseCode = curl_getinfo($rCurl, CURLINFO_HTTP_CODE); - $rError = curl_errno($rCurl); - @curl_close($rCurl); - if ($rError != 0 || $rResponseCode != 200) { - $i++; - break; - } - } - return $rOutput; - } - return false; + return CurlClient::serverRequest(self::$rServers, $rServerID, $rURL, $rPostData); } public static function deleteCache($rSources) { if (!empty($rSources)) { @@ -1176,6 +879,7 @@ class CoreUtilities { } public static function createChannelItem($rStreamID, $rSource) { $rStream = array(); + $rLoopback = false; self::$db->query('SELECT * FROM `streams` t1 INNER JOIN `streams_types` t2 ON t2.type_id = t1.type AND t1.type = 3 LEFT JOIN `profiles` t4 ON t1.transcode_profile_id = t4.profile_id WHERE t1.direct_source = 0 AND t1.id = ?', $rStreamID); if (self::$db->num_rows() > 0) { $rStream['stream_info'] = self::$db->get_row(); @@ -1473,6 +1177,7 @@ class CoreUtilities { } public static function startMovie($rStreamID) { $rStream = array(); + $rLoopback = false; self::$db->query('SELECT * FROM `streams` t1 INNER JOIN `streams_types` t2 ON t2.type_id = t1.type AND t2.live = 0 LEFT JOIN `profiles` t4 ON t1.transcode_profile_id = t4.profile_id WHERE t1.direct_source = 0 AND t1.id = ?', $rStreamID); if (self::$db->num_rows() > 0) { $rStream['stream_info'] = self::$db->get_row(); @@ -2370,7 +2075,13 @@ class CoreUtilities { return $rCache; } } - $rIPs = array('127.0.0.1', $_SERVER['SERVER_ADDR']); + $rIPs = array('127.0.0.1'); + $rServerAddr = ($_SERVER['SERVER_ADDR'] ?? null); + if (!empty($rServerAddr)) { + $rIPs[] = $rServerAddr; + } elseif (isset(self::$rServers[SERVER_ID]['server_ip']) && !empty(self::$rServers[SERVER_ID]['server_ip'])) { + $rIPs[] = self::$rServers[SERVER_ID]['server_ip']; + } foreach (self::$rServers as $rServerID => $rServerInfo) { if (!empty($rServerInfo['whitelist_ips'])) { $rIPs = array_merge($rIPs, json_decode($rServerInfo['whitelist_ips'], true)); @@ -3166,66 +2877,10 @@ class CoreUtilities { return (bool) preg_match($rPattern, $rURL); } public static function generateThumbnail($rImage, $rType) { - if ($rType == 1 || $rType == 5 || $rType == 4) { - $rMaxW = 96; - $rMaxH = 32; - } else { - if ($rType == 2) { - $rMaxW = 58; - $rMaxH = 32; - } else { - if ($rType == 5) { - $rMaxW = 32; - $rMaxH = 64; - } else { - return false; - } - } - } - list($rExtension) = explode('.', strtolower(pathinfo($rImage)['extension'])); - if (!in_array($rExtension, array('png', 'jpg', 'jpeg'))) { - } else { - $rImagePath = IMAGES_PATH . 'admin/' . md5($rImage) . '_' . $rMaxW . '_' . $rMaxH . '.' . $rExtension; - if (file_exists($rImagePath)) { - } else { - if (self::isAbsoluteUrl($rImage)) { - $rActURL = $rImage; - } else { - $rActURL = IMAGES_PATH . basename($rImage); - } - list($rWidth, $rHeight) = getimagesize($rActURL); - $rImageSize = self::getImageSizeKeepAspectRatio($rWidth, $rHeight, $rMaxW, $rMaxH); - if (!($rImageSize['width'] && $rImageSize['height'])) { - } else { - $rImageP = imagecreatetruecolor($rImageSize['width'], $rImageSize['height']); - if ($rExtension == 'png') { - $rImage = imagecreatefrompng($rActURL); - } else { - $rImage = imagecreatefromjpeg($rActURL); - } - imagealphablending($rImageP, false); - imagesavealpha($rImageP, true); - imagecopyresampled($rImageP, $rImage, 0, 0, 0, 0, $rImageSize['width'], $rImageSize['height'], $rWidth, $rHeight); - imagepng($rImageP, $rImagePath); - } - } - if (!file_exists($rImagePath)) { - } else { - return true; - } - } - return false; + return ImageUtils::generateThumbnail($rImage, $rType); } public static function validateImage($rURL, $rForceProtocol = null) { - if (substr($rURL, 0, 2) == 's:') { - $rSplit = explode(':', $rURL, 3); - $rServerURL = self::getPublicURL(intval($rSplit[1]), $rForceProtocol); - if ($rServerURL) { - return $rServerURL . 'images/' . basename($rURL); - } - return ''; - } - return $rURL; + return ImageUtils::validateURL($rURL, $rForceProtocol, array('CoreUtilities', 'getPublicURL')); } public static function getPublicURL($rServerID = null, $rForceProtocol = null) { $rOriginatorID = null; @@ -3273,15 +2928,7 @@ class CoreUtilities { } } public static function getURL($rURL, $rWait = true) { - $ch = curl_init(); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3); - curl_setopt($ch, CURLOPT_TIMEOUT, 3); - curl_setopt($ch, CURLOPT_URL, $rURL); - curl_setopt($ch, CURLOPT_USERAGENT, 'XC_VM/' . XC_VM_VERSION); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, $rWait); - $rReturn = curl_exec($ch); - curl_close($ch); - return $rReturn; + return CurlClient::getURL($rURL, $rWait); } public static function startDownload($rType, $rUser, $rDownloadPID) { $rFloodLimit = intval(self::$rSettings['max_simultaneous_downloads']); @@ -3342,73 +2989,10 @@ class CoreUtilities { return BruteforceGuard::checkAuthFlood($rUser, $rIP); } public static function getCapacity($rProxy = false) { - $rFile = ($rProxy ? 'proxy_capacity' : 'servers_capacity'); - if (self::$rSettings['redis_handler'] && $rProxy && self::$rSettings['split_by'] == 'maxclients') { - self::$rSettings['split_by'] == 'guar_band'; + if (self::$rSettings['redis_handler'] && !is_object(self::$redis)) { + self::connectRedis(); } - if (self::$rSettings['redis_handler']) { - $rRows = array(); - $rMulti = self::$redis->multi(); - foreach (array_keys(self::$rServers) as $rServerID) { - if (self::$rServers[$rServerID]['server_online']) { - $rMulti->zCard((($rProxy ? 'PROXY#' : 'SERVER#')) . $rServerID); - } - } - $rResults = $rMulti->exec(); - $i = 0; - foreach (array_keys(self::$rServers) as $rServerID) { - if (self::$rServers[$rServerID]['server_online']) { - $rRows[$rServerID] = array('online_clients' => ($rResults[$i] ?: 0)); - $i++; - } - } - } else { - if ($rProxy) { - self::$db->query('SELECT `proxy_id`, COUNT(*) AS `online_clients` FROM `lines_live` WHERE `proxy_id` <> 0 AND `hls_end` = 0 GROUP BY `proxy_id`;'); - $rRows = self::$db->get_rows(true, 'proxy_id'); - } else { - self::$db->query('SELECT `server_id`, COUNT(*) AS `online_clients` FROM `lines_live` WHERE `server_id` <> 0 AND `hls_end` = 0 GROUP BY `server_id`;'); - $rRows = self::$db->get_rows(true, 'server_id'); - } - } - if (self::$rSettings['split_by'] == 'band') { - $rServerSpeed = array(); - foreach (array_keys(self::$rServers) as $rServerID) { - $rServerHardware = json_decode(self::$rServers[$rServerID]['server_hardware'], true); - if (!empty($rServerHardware['network_speed'])) { - $rServerSpeed[$rServerID] = (float) $rServerHardware['network_speed']; - } else { - if (0 < self::$rServers[$rServerID]['network_guaranteed_speed']) { - $rServerSpeed[$rServerID] = self::$rServers[$rServerID]['network_guaranteed_speed']; - } else { - $rServerSpeed[$rServerID] = 1000; - } - } - } - foreach ($rRows as $rServerID => $rRow) { - $rCurrentOutput = intval(self::$rServers[$rServerID]['watchdog']['bytes_sent'] / 125000); - $rRows[$rServerID]['capacity'] = (float) ($rCurrentOutput / (($rServerSpeed[$rServerID] ?: 1000))); - } - } else { - if (self::$rSettings['split_by'] == 'maxclients') { - foreach ($rRows as $rServerID => $rRow) { - $rRows[$rServerID]['capacity'] = (float) ($rRow['online_clients'] / ((self::$rServers[$rServerID]['total_clients'] ?: 1))); - } - } else { - if (self::$rSettings['split_by'] == 'guar_band') { - foreach ($rRows as $rServerID => $rRow) { - $rCurrentOutput = intval(self::$rServers[$rServerID]['watchdog']['bytes_sent'] / 125000); - $rRows[$rServerID]['capacity'] = (float) ($rCurrentOutput / ((self::$rServers[$rServerID]['network_guaranteed_speed'] ?: 1))); - } - } else { - foreach ($rRows as $rServerID => $rRow) { - $rRows[$rServerID]['capacity'] = $rRow['online_clients']; - } - } - } - } - file_put_contents(CACHE_TMP_PATH . $rFile, json_encode($rRows), LOCK_EX); - return $rRows; + return ConnectionTracker::getCapacity(self::$rSettings, self::$rServers, self::$redis, self::$db, $rProxy); } /** @@ -3439,47 +3023,7 @@ class CoreUtilities { if (self::$rSettings['redis_handler'] && !is_object(self::$redis)) { self::connectRedis(); } - - if (self::$rSettings['redis_handler']) { - if ($rServerID) { - $rKeys = self::$redis->zRangeByScore('SERVER#' . $rServerID, '-inf', '+inf'); - } elseif ($rUserID) { - $rKeys = self::$redis->zRangeByScore('LINE#' . $rUserID, '-inf', '+inf'); - } elseif ($rStreamID) { - $rKeys = self::$redis->zRangeByScore('STREAM#' . $rStreamID, '-inf', '+inf'); - } else { - $rKeys = self::$redis->zRangeByScore('LIVE', '-inf', '+inf'); - } - - if (count($rKeys) > 0) { - return array($rKeys, array_map('igbinary_unserialize', self::$redis->mGet($rKeys))); - } else { - // We always return empty arrays - return array([], []); - } - } else { - // MYSQL fallback - $rWhere = array(); - if (!empty($rServerID)) { - $rWhere[] = 't1.server_id = ' . intval($rServerID); - } - if (!empty($rUserID)) { - $rWhere[] = 't1.user_id = ' . intval($rUserID); - } - - $rExtra = count($rWhere) ? 'WHERE ' . implode(' AND ', $rWhere) : ''; - - $rQuery = 'SELECT t2.*,t3.*,t5.bitrate,t1.*,t1.uuid AS `uuid` - FROM `lines_live` t1 - LEFT JOIN `lines` t2 ON t2.id = t1.user_id - LEFT JOIN `streams` t3 ON t3.id = t1.stream_id - LEFT JOIN `streams_servers` t5 ON t5.stream_id = t1.stream_id AND t5.server_id = t1.server_id - ' . $rExtra . ' - ORDER BY t1.activity_id ASC'; - - self::$db->query($rQuery); - return self::$db->get_rows(true, 'user_id', false); - } + return ConnectionTracker::getConnections(self::$rSettings, self::$redis, self::$db, $rServerID, $rUserID, $rStreamID); } public static function getEnded() { @@ -3494,10 +3038,7 @@ class CoreUtilities { } } public static function getBouquetMap($rStreamID) { - $rBouquetMap = igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . 'bouquet_map')); - $rReturn = ($rBouquetMap[$rStreamID] ?: array()); - unset($rBouquetMap); - return $rReturn; + return BouquetMapper::getMapEntry($rStreamID); } public static function updateStream($rStreamID, $rForce = false) { if (self::$rCached) { @@ -3550,401 +3091,84 @@ class CoreUtilities { return false; } public static function getMainID() { - foreach (self::$rServers as $rServerID => $rServer) { - if (!$rServer['is_main']) { - } else { - return $rServerID; - } - } + return ConnectionTracker::getMainID(self::$rServers); } public static function addToQueue($rStreamID, $rAddPID) { - $rActivePIDs = $rPIDs = array(); - if (!file_exists(SIGNALS_TMP_PATH . 'queue_' . intval($rStreamID))) { - } else { - $rPIDs = igbinary_unserialize(file_get_contents(SIGNALS_TMP_PATH . 'queue_' . intval($rStreamID))); - } - foreach ($rPIDs as $rPID) { - if (!self::isProcessRunning($rPID, 'php-fpm')) { - } else { - $rActivePIDs[] = $rPID; - } - } - if (in_array($rAddPID, $rActivePIDs, true)) { - } else { - $rActivePIDs[] = $rAddPID; - } - file_put_contents(SIGNALS_TMP_PATH . 'queue_' . intval($rStreamID), igbinary_serialize($rActivePIDs), LOCK_EX); + ConnectionTracker::addToQueue($rStreamID, $rAddPID, array('CoreUtilities', 'isProcessRunning')); } public static function removeFromQueue($rStreamID, $rPID) { - $rActivePIDs = array(); - foreach ((igbinary_unserialize(file_get_contents(SIGNALS_TMP_PATH . 'queue_' . intval($rStreamID))) ?: array()) as $rActivePID) { - if (!(self::isProcessRunning($rActivePID, 'php-fpm') && $rPID != $rActivePID)) { - } else { - $rActivePIDs[] = $rActivePID; - } - } - if (0 < count($rActivePIDs)) { - file_put_contents(SIGNALS_TMP_PATH . 'queue_' . intval($rStreamID), igbinary_serialize($rActivePIDs), LOCK_EX); - } else { - unlink(SIGNALS_TMP_PATH . 'queue_' . intval($rStreamID)); - } + ConnectionTracker::removeFromQueue($rStreamID, $rPID, array('CoreUtilities', 'isProcessRunning')); } public static function getProxyFor($rServerID) { return (array_rand(array_keys(self::getProxies($rServerID, false))) ?: null); } public static function formatTitle($rTitle, $rYear) { - if (!(is_numeric($rYear) && 1900 <= $rYear && $rYear <= intval(date('Y') + 1))) { - } else { - if (self::$rSettings['movie_year_append'] == 0) { - return trim($rTitle) . ' (' . $rYear . ')'; - } - if (self::$rSettings['movie_year_append'] != 0) { - } else { - return trim($rTitle) . ' - ' . $rYear; - } - } - return $rTitle; + return StreamSorter::formatTitle(self::$rSettings, $rTitle, $rYear); } public static function sortChannels($rChannels) { - if (!(0 < count($rChannels) && file_exists(CACHE_TMP_PATH . 'channel_order') && self::$rSettings['channel_number_type'] != 'bouquet')) { - } else { - $rOrder = igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . 'channel_order')); - $rChannels = array_flip($rChannels); - $rNewOrder = array(); - foreach ($rOrder as $rID) { - if (!isset($rChannels[$rID])) { - } else { - $rNewOrder[] = $rID; - } - } - if (0 >= count($rNewOrder)) { - } else { - return $rNewOrder; - } - } - return $rChannels; + return StreamSorter::sortChannels(self::$rSettings, $rChannels); } public static function sortSeries($rSeries) { - if (!(0 < count($rSeries) && file_exists(CACHE_TMP_PATH . 'series_order'))) { - } else { - $rOrder = igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . 'series_order')); - $rSeries = array_flip($rSeries); - $rNewOrder = array(); - foreach ($rOrder as $rID) { - if (!isset($rSeries[$rID])) { - } else { - $rNewOrder[] = $rID; - } - } - if (0 >= count($rNewOrder)) { - } else { - return $rNewOrder; - } - } - return $rSeries; + return StreamSorter::sortSeries($rSeries); } public static function setSignal($rKey, $rData) { - file_put_contents(SIGNALS_TMP_PATH . 'cache_' . md5($rKey), json_encode(array($rKey, $rData))); + RedisManager::setSignal($rKey, $rData); } public static function connectRedis() { - if (is_object(self::$redis)) { - } else { - try { - self::$redis = new Redis(); - self::$redis->connect(self::$rConfig['hostname'], 6379); - self::$redis->auth(self::$rSettings['redis_password']); - } catch (Exception $e) { - self::$redis = null; - return false; - } - } - return true; + self::$redis = RedisManager::connect(self::$redis, self::$rConfig, self::$rSettings); + return is_object(self::$redis); } public static function updateConnection($rData, $rChanges = array(), $rOption = null) { if (is_object(self::$redis)) { } else { self::connectRedis(); } - $rOrigData = $rData; - foreach ($rChanges as $rKey => $rValue) { - $rData[$rKey] = $rValue; - } - $rRedis = self::$redis->multi(); - if ($rOption == 'open') { - $rRedis->sRem('ENDED', $rData['uuid']); - $rRedis->zAdd('LIVE', $rData['date_start'], $rData['uuid']); - $rRedis->zAdd('LINE#' . $rData['identity'], $rData['date_start'], $rData['uuid']); - $rRedis->zAdd('STREAM#' . $rData['stream_id'], $rData['date_start'], $rData['uuid']); - $rRedis->zAdd('SERVER#' . $rData['server_id'], $rData['date_start'], $rData['uuid']); - if (!$rData['proxy_id']) { - } else { - $rRedis->zAdd('PROXY#' . $rData['proxy_id'], $rData['date_start'], $rData['uuid']); - } - if ($rData['hls_end'] != 1) { - } else { - $rData['hls_end'] = 0; - if (!$rData['user_id']) { - } else { - $rRedis->zAdd('SERVER_LINES#' . $rData['server_id'], $rData['user_id'], $rData['uuid']); - } - } - } else { - if ($rOption != 'close') { - } else { - $rRedis->sAdd('ENDED', $rData['uuid']); - $rRedis->zRem('LIVE', $rData['uuid']); - $rRedis->zRem('LINE#' . $rOrigData['identity'], $rData['uuid']); - $rRedis->zRem('STREAM#' . $rOrigData['stream_id'], $rData['uuid']); - $rRedis->zRem('SERVER#' . $rOrigData['server_id'], $rData['uuid']); - if (!$rData['proxy_id']) { - } else { - $rRedis->zRem('PROXY#' . $rOrigData['proxy_id'], $rData['uuid']); - } - if ($rData['hls_end'] != 0) { - } else { - $rData['hls_end'] = 1; - if (!$rData['user_id']) { - } else { - $rRedis->zRem('SERVER_LINES#' . $rOrigData['server_id'], $rData['uuid']); - } - } - } - } - $rRedis->set($rData['uuid'], igbinary_serialize($rData)); - if ($rRedis->exec()) { - return $rData; - } - return null; + return ConnectionTracker::updateConnection(self::$redis, $rData, $rChanges, $rOption); } public static function redisSignal($rPID, $rServerID, $rRTMP, $rCustomData = null) { if (is_object(self::$redis)) { } else { self::connectRedis(); } - $rKey = 'SIGNAL#' . md5($rServerID . '#' . $rPID . '#' . $rRTMP); - $rData = array('pid' => $rPID, 'server_id' => $rServerID, 'rtmp' => $rRTMP, 'time' => time(), 'custom_data' => $rCustomData, 'key' => $rKey); - return self::$redis->multi()->sAdd('SIGNALS#' . $rServerID, $rKey)->set($rKey, igbinary_serialize($rData))->exec(); + return ConnectionTracker::redisSignal(self::$redis, $rPID, $rServerID, $rRTMP, $rCustomData); } public static function getUserConnections($rUserIDs, $rCount = false, $rKeysOnly = false) { if (is_object(self::$redis)) { } else { self::connectRedis(); } - $rRedis = self::$redis->multi(); - foreach ($rUserIDs as $rUserID) { - $rRedis->zRevRangeByScore('LINE#' . $rUserID, '+inf', '-inf'); - } - $rGroups = $rRedis->exec(); - $rConnectionMap = $rRedisKeys = array(); - foreach ($rGroups as $rGroupID => $rKeys) { - if ($rCount) { - $rConnectionMap[$rUserIDs[$rGroupID]] = count($rKeys); - } else { - if (0 >= count($rKeys)) { - } else { - $rRedisKeys = array_merge($rRedisKeys, $rKeys); - } - } - } - $rRedisKeys = array_unique($rRedisKeys); - if (!$rKeysOnly) { - if ($rCount) { - } else { - foreach (self::$redis->mGet($rRedisKeys) as $rRow) { - $rRow = igbinary_unserialize($rRow); - $rConnectionMap[$rRow['user_id']][] = $rRow; - } - } - return $rConnectionMap; - } - return $rRedisKeys; + return ConnectionTracker::getUserConnections(self::$redis, $rUserIDs, $rCount, $rKeysOnly); } public static function getServerConnections($rServerIDs, $rProxy = false, $rCount = false, $rKeysOnly = false) { if (is_object(self::$redis)) { } else { self::connectRedis(); } - $rRedis = self::$redis->multi(); - foreach ($rServerIDs as $rServerID) { - $rRedis->zRevRangeByScore(($rProxy ? 'PROXY#' . $rServerID : 'SERVER#' . $rServerID), '+inf', '-inf'); - } - $rGroups = $rRedis->exec(); - $rConnectionMap = $rRedisKeys = array(); - foreach ($rGroups as $rGroupID => $rKeys) { - if ($rCount) { - $rConnectionMap[$rServerIDs[$rGroupID]] = count($rKeys); - } else { - if (0 >= count($rKeys)) { - } else { - $rRedisKeys = array_merge($rRedisKeys, $rKeys); - } - } - } - $rRedisKeys = array_unique($rRedisKeys); - if (!$rKeysOnly) { - if ($rCount) { - } else { - foreach (self::$redis->mGet($rRedisKeys) as $rRow) { - $rRow = igbinary_unserialize($rRow); - $rConnectionMap[$rRow['server_id']][] = $rRow; - } - } - return $rConnectionMap; - } - return $rRedisKeys; + return ConnectionTracker::getServerConnections(self::$redis, $rServerIDs, $rProxy, $rCount, $rKeysOnly); } public static function getFirstConnection($rUserIDs) { if (is_object(self::$redis)) { } else { self::connectRedis(); } - $rRedis = self::$redis->multi(); - foreach ($rUserIDs as $rUserID) { - $rRedis->zRevRangeByScore('LINE#' . $rUserID, '+inf', '-inf', array('limit' => array(0, 1))); - } - $rGroups = $rRedis->exec(); - $rConnectionMap = $rRedisKeys = array(); - foreach ($rGroups as $rGroupID => $rKeys) { - if (0 >= count($rKeys)) { - } else { - $rRedisKeys[] = $rKeys[0]; - } - } - foreach (self::$redis->mGet(array_unique($rRedisKeys)) as $rRow) { - $rRow = igbinary_unserialize($rRow); - $rConnectionMap[$rRow['user_id']] = $rRow; - } - return $rConnectionMap; + return ConnectionTracker::getFirstConnection(self::$redis, $rUserIDs); } public static function getStreamConnections($rStreamIDs, $rGroup = true, $rCount = false) { if (is_object(self::$redis)) { } else { self::connectRedis(); } - $rRedis = self::$redis->multi(); - foreach ($rStreamIDs as $rStreamID) { - $rRedis->zRevRangeByScore('STREAM#' . $rStreamID, '+inf', '-inf'); - } - $rGroups = $rRedis->exec(); - $rConnectionMap = $rRedisKeys = array(); - foreach ($rGroups as $rGroupID => $rKeys) { - if ($rCount) { - $rConnectionMap[$rStreamIDs[$rGroupID]] = count($rKeys); - } else { - if (0 >= count($rKeys)) { - } else { - $rRedisKeys = array_merge($rRedisKeys, $rKeys); - } - } - } - if ($rCount) { - } else { - foreach (self::$redis->mGet(array_unique($rRedisKeys)) as $rRow) { - $rRow = igbinary_unserialize($rRow); - if ($rGroup) { - $rConnectionMap[$rRow['stream_id']][] = $rRow; - } else { - $rConnectionMap[$rRow['stream_id']][$rRow['server_id']][] = $rRow; - } - } - } - return $rConnectionMap; + return ConnectionTracker::getStreamConnections(self::$redis, $rStreamIDs, $rGroup, $rCount); } public static function getRedisConnections($rUserID = null, $rServerID = null, $rStreamID = null, $rOpenOnly = false, $rCountOnly = false, $rGroup = true, $rHLSOnly = false) { - $rReturn = ($rCountOnly ? array(0, 0) : array()); if (is_object(self::$redis)) { } else { self::connectRedis(); } - $rUniqueUsers = array(); - $rUserID = (0 < intval($rUserID) ? intval($rUserID) : null); - $rServerID = (0 < intval($rServerID) ? intval($rServerID) : null); - $rStreamID = (0 < intval($rStreamID) ? intval($rStreamID) : null); - if ($rUserID) { - $rKeys = self::$redis->zRangeByScore('LINE#' . $rUserID, '-inf', '+inf'); - } else { - if ($rStreamID) { - $rKeys = self::$redis->zRangeByScore('STREAM#' . $rStreamID, '-inf', '+inf'); - } else { - if ($rServerID) { - $rKeys = self::$redis->zRangeByScore('SERVER#' . $rServerID, '-inf', '+inf'); - } else { - $rKeys = self::$redis->zRangeByScore('LIVE', '-inf', '+inf'); - } - } - } - if (0 >= count($rKeys)) { - } else { - foreach (self::$redis->mGet(array_unique($rKeys)) as $rRow) { - $rRow = igbinary_unserialize($rRow); - if (!($rServerID && $rServerID != $rRow['server_id']) && !($rStreamID && $rStreamID != $rRow['stream_id']) && !($rUserID && $rUserID != $rRow['user_id']) && !($rHLSOnly && $rRow['container'] == 'hls')) { - $rUUID = ($rRow['user_id'] ?: $rRow['hmac_id'] . '_' . $rRow['hmac_identifier']); - if ($rCountOnly) { - $rReturn[0]++; - $rUniqueUsers[] = $rUUID; - } else { - if ($rGroup) { - if (isset($rReturn[$rUUID])) { - } else { - $rReturn[$rUUID] = array(); - } - $rReturn[$rUUID][] = $rRow; - } else { - $rReturn[] = $rRow; - } - } - } - } - } - if (!$rCountOnly) { - } else { - $rReturn[1] = count(array_unique($rUniqueUsers)); - } - return $rReturn; + return ConnectionTracker::getRedisConnections(self::$redis, $rUserID, $rServerID, $rStreamID, $rOpenOnly, $rCountOnly, $rGroup, $rHLSOnly); } public static function getDomainName($rForceSSL = false) { - $rOriginatorID = null; - $rServerID = SERVER_ID; - if ($rForceSSL) { - $rProtocol = 'https'; - } else { - if (isset($_SERVER['SERVER_PORT']) && self::$rSettings['keep_protocol']) { - $rProtocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443 ? 'https' : 'http'); - } else { - $rProtocol = self::$rServers[$rServerID]['server_protocol']; - } - } - $rProxied = self::$rServers[$rServerID]['enable_proxy']; - if ($rProxied) { - $rProxyIDs = array_keys(self::getProxies($rServerID)); - if (count($rProxyIDs) == 0) { - $rProxyIDs = array_keys(self::getProxies($rServerID, false)); - } - if (count($rProxyIDs) != 0) { - $rOriginatorID = $rServerID; - $rServerID = $rProxyIDs[array_rand($rProxyIDs)]; - } else { - return ''; - } - } - list($rDomain, $rAccessPort) = explode(':', $_SERVER['HTTP_HOST']); - if (!($rProxied || self::$rSettings['use_mdomain_in_lists'] == 1)) { - } else { - if (in_array(strtolower($rDomain), (self::getCache('reseller_domains') ?: array()))) { - } else { - if (empty(self::$rServers[$rServerID]['domain_name'])) { - $rDomain = escapeshellcmd(self::$rServers[$rServerID]['server_ip']); - } else { - $rDomain = str_replace(array('http://', '/', 'https://'), '', escapeshellcmd(explode(',', self::$rServers[$rServerID]['domain_name'])[0])); - } - } - } - $rServerURL = $rProtocol . '://' . $rDomain . ':' . self::$rServers[$rServerID][$rProtocol . '_broadcast_port'] . '/'; - if (!(self::$rServers[$rServerID]['server_type'] == 1 && $rOriginatorID && self::$rServers[$rOriginatorID]['is_main'] == 0)) { - } else { - $rServerURL .= md5($rServerID . '_' . $rOriginatorID . '_' . OPENSSL_EXTRA) . '/'; - } - return $rServerURL; + return DomainResolver::resolve(self::$rServers, self::$rSettings, SERVER_ID, $rForceSSL, array('CoreUtilities', 'getProxies'), array('CoreUtilities', 'getCache')); } public static function checkCompatibility($rData) { if (!is_array($rData)) { @@ -3983,13 +3207,7 @@ class CoreUtilities { } public static function getNearest($arr, $search) { - $closest = null; - foreach ($arr as $item) { - if ($closest === null || abs($item - $search) < abs($search - $closest)) { - $closest = $item; - } - } - return $closest; + return StreamSorter::getNearest($arr, $search); } /** * Downloads panel logs from database, formats them and clears the logs table @@ -4198,13 +3416,7 @@ class CoreUtilities { } } public static function getProxies($rServerID, $rOnline = true) { - $rReturn = array(); - foreach (self::$rServers as $rProxyID => $rServerInfo) { - if ($rServerInfo['server_type'] == 1 && in_array($rServerID, $rServerInfo['parent_id']) && ($rServerInfo['server_online'] || !$rOnline)) { - $rReturn[$rProxyID] = $rServerInfo; - } - } - return $rReturn; + return ConnectionTracker::getProxies(self::$rServers, $rServerID, $rOnline); } /** diff --git a/src/includes/StreamingUtilities.php b/src/includes/StreamingUtilities.php index 17654adf..b54bff15 100644 --- a/src/includes/StreamingUtilities.php +++ b/src/includes/StreamingUtilities.php @@ -20,92 +20,7 @@ class StreamingUtilities { public static $rCached = null; public static $rAccess = null; public static function init() { - if (!empty($_GET)) { - self::cleanGlobals($_GET); - } - if (!empty($_POST)) { - self::cleanGlobals($_POST); - } - if (!empty($_SESSION)) { - self::cleanGlobals($_SESSION); - } - if (!empty($_COOKIE)) { - self::cleanGlobals($_COOKIE); - } - $rInput = @self::parseIncomingRecursively($_GET, array()); - self::$rRequest = @self::parseIncomingRecursively($_POST, $rInput); - self::$rConfig = parse_ini_file(CONFIG_PATH . 'config.ini'); - if (!defined('SERVER_ID')) { - define('SERVER_ID', intval(self::$rConfig['server_id'])); - } - if (!self::$rSettings) { - self::$rSettings = self::getCache('settings'); - } - if (!empty(self::$rSettings['default_timezone'])) { - date_default_timezone_set( - self::$rSettings['default_timezone'] - ); - } - if ((self::$rSettings['on_demand_wait_time'] == 0)) { - self::$rSettings['on_demand_wait_time'] = 15; - } - switch (self::$rSettings['ffmpeg_cpu']) { - case '8.0': - self::$rFFMPEG_CPU = FFMPEG_BIN_80; - self::$rFFMPEG_GPU = FFMPEG_BIN_80; - break; - case '7.1': - self::$rFFMPEG_CPU = FFMPEG_BIN_71; - self::$rFFMPEG_GPU = FFMPEG_BIN_71; - break; - case '5.1': - self::$rFFMPEG_CPU = FFMPEG_BIN_51; - self::$rFFMPEG_GPU = FFMPEG_BIN_40; - break; - case '4.4': - self::$rFFMPEG_CPU = FFMPEG_BIN_44; - self::$rFFMPEG_GPU = FFMPEG_BIN_40; - break; - case '4.3': - self::$rFFMPEG_CPU = FFMPEG_BIN_43; - self::$rFFMPEG_GPU = FFMPEG_BIN_40; - break; - default: - self::$rFFMPEG_CPU = FFMPEG_BIN_40; - self::$rFFMPEG_GPU = FFMPEG_BIN_40; - break; - } - self::$rCached = self::isCacheEnabledAndComplete(); - self::$rServers = self::getCache( - 'servers' - ); - self::$rBlockedUA = self::getCache( - 'blocked_ua' - ); - self::$rBlockedISP = self::getCache( - 'blocked_isp' - ); - self::$rBlockedIPs = self::getCache( - 'blocked_ips' - ); - self::$rBlockedServers = self::getCache( - 'blocked_servers' - ); - self::$rAllowedIPs = self::getCache( - 'allowed_ips' - ); - self::$rProxies = self::getCache( - 'proxy_servers' - ); - self::$rSegmentSettings = array( - 'seg_time' => intval( - self::$rSettings['seg_time'] - ), - 'seg_list_size' => intval( - self::$rSettings['seg_list_size'] - ), - ); - self::connectDatabase(); + LegacyInitializer::initStreaming(); } public static function isCacheEnabledAndComplete() { if (!self::$rSettings['enable_cache']) { @@ -238,7 +153,13 @@ class StreamingUtilities { return BruteforceGuard::truncateAttempts($rAttempts, $rFrequency, $rList); } public static function getCapacity($rProxy = false) { - return json_decode(file_get_contents(CACHE_TMP_PATH . (($rProxy ? 'proxy_capacity' : 'servers_capacity'))), true); + if (!self::$rCached && self::$rSettings['redis_handler'] && !is_object(self::$redis)) { + self::connectRedis(); + } + if (self::$rCached) { + return json_decode(file_get_contents(CACHE_TMP_PATH . (($rProxy ? 'proxy_capacity' : 'servers_capacity'))), true); + } + return ConnectionTracker::getCapacity(self::$rSettings, self::$rServers, self::$redis, self::$db, $rProxy); } public static function redirectStream($rStreamID, $rExtension, $rUserInfo, $rCountryCode, $rUserISP = '', $rType = '') { if (self::$rCached) { @@ -1190,15 +1111,7 @@ class StreamingUtilities { return false; } public static function validateImage($rURL, $rForceProtocol = null) { - if (substr($rURL, 0, 2) == 's:') { - $rSplit = explode(':', $rURL, 3); - $rServerURL = self::getPublicURL(intval($rSplit[1]), $rForceProtocol); - if ($rServerURL) { - return $rServerURL . 'images/' . basename($rURL); - } - return ''; - } - return $rURL; + return ImageUtils::validateURL($rURL, $rForceProtocol, array('StreamingUtilities', 'getPublicURL')); } public static function isRunning() { $rNginx = 0; @@ -1254,14 +1167,7 @@ class StreamingUtilities { } } public static function getCategories($rType = null) { - $rReturn = array(); - foreach (self::$rCategories as $rCategory) { - if ($rCategory['category_type'] != $rType && $rType) { - } else { - $rReturn[] = $rCategory; - } - } - return $rReturn; + return CategoryRepository::filterLoaded(self::$rCategories, $rType); } public static function matchCIDR($rASN, $rIP) { if (!file_exists(CIDR_TMP_PATH . $rASN)) { @@ -1408,10 +1314,7 @@ class StreamingUtilities { } } public static function getBouquetMap($rStreamID) { - $rBouquetMap = igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . 'bouquet_map')); - $rReturn = ($rBouquetMap[$rStreamID] ?: array()); - unset($rBouquetMap); - return $rReturn; + return BouquetMapper::getMapEntry($rStreamID); } public static function getStreamData($rStreamID) { $rOutput = array(); @@ -1435,44 +1338,13 @@ class StreamingUtilities { return (!empty($rOutput) ? $rOutput : false); } public static function getMainID() { - foreach (self::$rServers as $rServerID => $rServer) { - if (!$rServer['is_main']) { - } else { - return $rServerID; - } - } + return ConnectionTracker::getMainID(self::$rServers); } public static function addToQueue($rStreamID, $rAddPID) { - $rActivePIDs = $rPIDs = array(); - if (!file_exists(SIGNALS_TMP_PATH . 'queue_' . intval($rStreamID))) { - } else { - $rPIDs = igbinary_unserialize(file_get_contents(SIGNALS_TMP_PATH . 'queue_' . intval($rStreamID))); - } - foreach ($rPIDs as $rPID) { - if (!self::isProcessRunning($rPID, 'php-fpm')) { - } else { - $rActivePIDs[] = $rPID; - } - } - if (in_array($rAddPID, $rActivePIDs, true)) { - } else { - $rActivePIDs[] = $rAddPID; - } - file_put_contents(SIGNALS_TMP_PATH . 'queue_' . intval($rStreamID), igbinary_serialize($rActivePIDs), LOCK_EX); + ConnectionTracker::addToQueue($rStreamID, $rAddPID, array('StreamingUtilities', 'isProcessRunning')); } public static function removeFromQueue($rStreamID, $rPID) { - $rActivePIDs = array(); - foreach ((igbinary_unserialize(file_get_contents(SIGNALS_TMP_PATH . 'queue_' . intval($rStreamID))) ?: array()) as $rActivePID) { - if (!(self::isProcessRunning($rActivePID, 'php-fpm') && $rPID != $rActivePID)) { - } else { - $rActivePIDs[] = $rActivePID; - } - } - if (0 < count($rActivePIDs)) { - file_put_contents(SIGNALS_TMP_PATH . 'queue_' . intval($rStreamID), igbinary_serialize($rActivePIDs), LOCK_EX); - } else { - unlink(SIGNALS_TMP_PATH . 'queue_' . intval($rStreamID)); - } + ConnectionTracker::removeFromQueue($rStreamID, $rPID, array('StreamingUtilities', 'isProcessRunning')); } public static function generateString($rLength = 10) { $rCharacters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789qwertyuiopasdfghjklzxcvbnm'; @@ -1486,55 +1358,13 @@ class StreamingUtilities { return $rString; } public static function formatTitle($rTitle, $rYear) { - if (!(is_numeric($rYear) && 1900 <= $rYear && $rYear <= intval(date('Y') + 1))) { - } else { - if (self::$rSettings['movie_year_append'] == 0) { - return trim($rTitle) . ' (' . $rYear . ')'; - } - if (self::$rSettings['movie_year_append'] != 0) { - } else { - return trim($rTitle) . ' - ' . $rYear; - } - } - return $rTitle; + return StreamSorter::formatTitle(self::$rSettings, $rTitle, $rYear); } public static function sortChannels($rChannels) { - if (!(0 < count($rChannels) && file_exists(CACHE_TMP_PATH . 'channel_order') && self::$rSettings['channel_number_type'] != 'bouquet')) { - } else { - $rOrder = igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . 'channel_order')); - $rChannels = array_flip($rChannels); - $rNewOrder = array(); - foreach ($rOrder as $rID) { - if (!isset($rChannels[$rID])) { - } else { - $rNewOrder[] = $rID; - } - } - if (0 >= count($rNewOrder)) { - } else { - return $rNewOrder; - } - } - return $rChannels; + return StreamSorter::sortChannels(self::$rSettings, $rChannels); } public static function sortSeries($rSeries) { - if (!(0 < count($rSeries) && file_exists(CACHE_TMP_PATH . 'series_order'))) { - } else { - $rOrder = igbinary_unserialize(file_get_contents(CACHE_TMP_PATH . 'series_order')); - $rSeries = array_flip($rSeries); - $rNewOrder = array(); - foreach ($rOrder as $rID) { - if (!isset($rSeries[$rID])) { - } else { - $rNewOrder[] = $rID; - } - } - if (0 >= count($rNewOrder)) { - } else { - return $rNewOrder; - } - } - return $rSeries; + return StreamSorter::sortSeries($rSeries); } public static function getDiffTimezone($rTimezone) { $rServerTZ = new DateTime('UTC', new DateTimeZone(date_default_timezone_get())); @@ -1552,25 +1382,11 @@ class StreamingUtilities { return $rReturn; } public static function connectRedis() { - if (is_object(self::$redis)) { - } else { - try { - self::$redis = new Redis(); - self::$redis->connect(self::$rConfig['hostname'], 6379); - self::$redis->auth(self::$rSettings['redis_password']); - } catch (Exception $e) { - self::$redis = null; - return false; - } - } - return true; + self::$redis = RedisManager::connect(self::$redis, self::$rConfig, self::$rSettings); + return is_object(self::$redis); } public static function closeRedis() { - if (!is_object(self::$redis)) { - } else { - self::$redis->close(); - self::$redis = null; - } + self::$redis = RedisManager::close(self::$redis); return true; } public static function getConnection($rUUID) { @@ -1578,171 +1394,43 @@ class StreamingUtilities { } else { self::connectRedis(); } - return igbinary_unserialize(self::$redis->get($rUUID)); + return ConnectionTracker::getConnection(self::$redis, $rUUID); } public static function createConnection($rData) { if (!is_object(self::$redis)) { self::connectRedis(); } - $rRedis = self::$redis->multi(); - $rRedis->zAdd('LINE#' . $rData['identity'], $rData['date_start'], $rData['uuid']); - $rRedis->zAdd('LINE_ALL#' . $rData['identity'], $rData['date_start'], $rData['uuid']); - $rRedis->zAdd('STREAM#' . $rData['stream_id'], $rData['date_start'], $rData['uuid']); - $rRedis->zAdd('SERVER#' . $rData['server_id'], $rData['date_start'], $rData['uuid']); - if ($rData['user_id']) { - $rRedis->zAdd('SERVER_LINES#' . $rData['server_id'], $rData['user_id'], $rData['uuid']); - } - if ($rData['proxy_id']) { - $rRedis->zAdd('PROXY#' . $rData['proxy_id'], $rData['date_start'], $rData['uuid']); - } - $rRedis->zAdd('CONNECTIONS', $rData['date_start'], $rData['uuid']); - $rRedis->zAdd('LIVE', $rData['date_start'], $rData['uuid']); - $rRedis->set($rData['uuid'], igbinary_serialize($rData)); - return $rRedis->exec(); + return ConnectionTracker::createConnection(self::$redis, $rData); } public static function updateConnection($rData, $rChanges = array(), $rOption = null) { if (is_object(self::$redis)) { } else { self::connectRedis(); } - $rOrigData = $rData; - foreach ($rChanges as $rKey => $rValue) { - $rData[$rKey] = $rValue; - } - $rRedis = self::$redis->multi(); - if ($rOption == 'open') { - $rRedis->sRem('ENDED', $rData['uuid']); - $rRedis->zAdd('LIVE', $rData['date_start'], $rData['uuid']); - $rRedis->zAdd('LINE#' . $rData['identity'], $rData['date_start'], $rData['uuid']); - $rRedis->zAdd('STREAM#' . $rData['stream_id'], $rData['date_start'], $rData['uuid']); - $rRedis->zAdd('SERVER#' . $rData['server_id'], $rData['date_start'], $rData['uuid']); - if (!$rData['proxy_id']) { - } else { - $rRedis->zAdd('PROXY#' . $rData['proxy_id'], $rData['date_start'], $rData['uuid']); - } - if ($rData['hls_end'] != 1) { - } else { - $rData['hls_end'] = 0; - if (!$rData['user_id']) { - } else { - $rRedis->zAdd('SERVER_LINES#' . $rData['server_id'], $rData['user_id'], $rData['uuid']); - } - } - } else { - if ($rOption != 'close') { - } else { - $rRedis->sAdd('ENDED', $rData['uuid']); - $rRedis->zRem('LIVE', $rData['uuid']); - $rRedis->zRem('LINE#' . $rOrigData['identity'], $rData['uuid']); - $rRedis->zRem('STREAM#' . $rOrigData['stream_id'], $rData['uuid']); - $rRedis->zRem('SERVER#' . $rOrigData['server_id'], $rData['uuid']); - if (!$rData['proxy_id']) { - } else { - $rRedis->zRem('PROXY#' . $rOrigData['proxy_id'], $rData['uuid']); - } - if ($rData['hls_end'] != 0) { - } else { - $rData['hls_end'] = 1; - if (!$rData['user_id']) { - } else { - $rRedis->zRem('SERVER_LINES#' . $rOrigData['server_id'], $rData['uuid']); - } - } - } - } - $rRedis->set($rData['uuid'], igbinary_serialize($rData)); - if ($rRedis->exec()) { - return $rData; - } + return ConnectionTracker::updateConnection(self::$redis, $rData, $rChanges, $rOption); } public static function getConnections($rUserID, $rActive = false, $rKeys = false) { if (is_object(self::$redis)) { } else { self::connectRedis(); } - $rKeys = self::$redis->zRangeByScore((($rActive ? 'LINE#' : 'LINE_ALL#')) . $rUserID, '-inf', '+inf'); - if ($rKeys) { - return $rKeys; - } - if (0 >= count($rKeys)) { - return array(); - } - return array_map('igbinary_unserialize', self::$redis->mGet($rKeys)); + return ConnectionTracker::getLineConnections(self::$redis, $rUserID, $rActive, $rKeys); } public static function redisSignal($rPID, $rServerID, $rRTMP, $rCustomData = null) { if (is_object(self::$redis)) { } else { self::connectRedis(); } - $rKey = 'SIGNAL#' . md5($rServerID . '#' . $rPID . '#' . $rRTMP); - $rData = array('pid' => $rPID, 'server_id' => $rServerID, 'rtmp' => $rRTMP, 'time' => time(), 'custom_data' => $rCustomData, 'key' => $rKey); - return self::$redis->multi()->sAdd('SIGNALS#' . $rServerID, $rKey)->set($rKey, igbinary_serialize($rData))->exec(); + return ConnectionTracker::redisSignal(self::$redis, $rPID, $rServerID, $rRTMP, $rCustomData); } public static function getNearest($rSearch, $rArray) { - $rClosest = null; - foreach ($rArray as $rItem) { - if (!($rClosest === null || abs($rItem - $rSearch) < abs($rSearch - $rClosest))) { - } else { - $rClosest = $rItem; - } - } - return $rClosest; + return StreamSorter::getNearest($rArray, $rSearch); } public static function getDomainName($rForceSSL = false) { - $rOriginatorID = null; - $rServerID = SERVER_ID; - if ($rForceSSL) { - $rProtocol = 'https'; - } else { - if (isset($_SERVER['SERVER_PORT']) && self::$rSettings['keep_protocol']) { - $rProtocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443 ? 'https' : 'http'); - } else { - $rProtocol = self::$rServers[$rServerID]['server_protocol']; - } - } - $rProxied = self::$rServers[$rServerID]['enable_proxy']; - if (!$rProxied) { - } else { - $rProxyIDs = array_keys(self::getProxies($rServerID)); - if (count($rProxyIDs) != 0) { - } else { - $rProxyIDs = array_keys(self::getProxies($rServerID, false)); - } - if (count($rProxyIDs) != 0) { - $rOriginatorID = $rServerID; - $rServerID = $rProxyIDs[array_rand($rProxyIDs)]; - } else { - return ''; - } - } - list($rDomain, $rAccessPort) = explode(':', $_SERVER['HTTP_HOST']); - if (!($rProxied || self::$rSettings['use_mdomain_in_lists'] == 1)) { - } else { - if (in_array(strtolower($rDomain), (self::getCache('reseller_domains') ?: array()))) { - } else { - if (empty(self::$rServers[$rServerID]['domain_name'])) { - $rDomain = escapeshellcmd(self::$rServers[$rServerID]['server_ip']); - } else { - $rDomain = str_replace(array('http://', '/', 'https://'), '', escapeshellcmd(explode(',', self::$rServers[$rServerID]['domain_name'])[0])); - } - } - } - $rServerURL = $rProtocol . '://' . $rDomain . ':' . self::$rServers[$rServerID][$rProtocol . '_broadcast_port'] . '/'; - if (!(self::$rServers[$rServerID]['server_type'] == 1 && $rOriginatorID && self::$rServers[$rOriginatorID]['is_main'] == 0)) { - } else { - $rServerURL .= md5($rServerID . '_' . $rOriginatorID . '_' . OPENSSL_EXTRA) . '/'; - } - return $rServerURL; + return DomainResolver::resolve(self::$rServers, self::$rSettings, SERVER_ID, $rForceSSL, array('StreamingUtilities', 'getProxies'), array('StreamingUtilities', 'getCache')); } public static function getProxies($rServerID, $rOnline = true) { - $rReturn = array(); - foreach (self::$rServers as $rProxyID => $rServerInfo) { - if (!($rServerInfo['server_type'] == 1 && in_array($rServerID, $rServerInfo['parent_id']) && ($rServerInfo['server_online'] || !$rOnline))) { - } else { - $rReturn[$rProxyID] = $rServerInfo; - } - } - return $rReturn; + return ConnectionTracker::getProxies(self::$rServers, $rServerID, $rOnline); } public static function getStreamingURL($rServerID = null, $rOriginatorID = null, $rForceHTTP = false) { if (isset($rServerID)) { diff --git a/src/includes/admin.php b/src/includes/admin.php index be6c98c6..186a67ce 100644 --- a/src/includes/admin.php +++ b/src/includes/admin.php @@ -117,7 +117,11 @@ $rWatchStatusArray = array(1 => "", 'STREAM_START_FAIL' => "", 'STREAM_START' => "", 'STREAM_RESTART' => "", 'STREAM_FAILED' => ""); $rStreamLogsArray = array('STREAM_FAILED' => 'Stream Failed', 'STREAM_START' => 'Stream Started', 'STREAM_RESTART' => 'Stream Restarted', 'STREAM_STOP' => 'Stream Stopped', 'FORCE_SOURCE' => 'Force Change Source', 'AUTO_RESTART' => 'Timed Auto Restart', 'AUDIO_LOSS' => 'Audio Lost', 'PRIORITY_SWITCH' => 'Priority Switch', 'DELAY_START' => 'Delay Started', 'FFMPEG_ERROR' => 'FFMPEG Error'); $rThemes = array(array('name' => 'Light', 'dark' => false, 'image' => null), array('name' => 'Dark', 'dark' => true, 'image' => null)); -$rAdvPermissions = array(array('add_rtmp', $language::get('permission_add_rtmp'), $language::get('permission_add_rtmp_text')), array('add_bouquet', $language::get('permission_add_bouquet'), $language::get('permission_add_bouquet_text')), array('add_cat', $language::get('permission_add_cat'), $language::get('permission_add_cat_text')), array('add_e2', $language::get('permission_add_e2'), $language::get('permission_add_e2_text')), array('add_epg', $language::get('permission_add_epg'), $language::get('permission_add_epg_text')), array('add_episode', $language::get('permission_add_episode'), $language::get('permission_add_episode_text')), array('add_group', $language::get('permission_add_group'), $language::get('permission_add_group_text')), array('add_mag', $language::get('permission_add_mag'), $language::get('permission_add_mag_text')), array('add_movie', $language::get('permission_add_movie'), $language::get('permission_add_movie_text')), array('add_packages', $language::get('permission_add_packages'), $language::get('permission_add_packages_text')), array('add_radio', $language::get('permission_add_radio'), $language::get('permission_add_radio_text')), array('add_reguser', $language::get('permission_add_reguser'), $language::get('permission_add_reguser_text')), array('add_server', $language::get('permission_add_server'), $language::get('permission_add_server_text')), array('add_stream', $language::get('permission_add_stream'), $language::get('permission_add_stream_text')), array('tprofile', $language::get('permission_tprofile'), $language::get('permission_tprofile_text')), array('add_series', $language::get('permission_add_series'), $language::get('permission_add_series_text')), array('add_user', $language::get('permission_add_user'), $language::get('permission_add_user_text')), array('block_ips', $language::get('permission_block_ips'), $language::get('permission_block_ips_text')), array('block_isps', $language::get('permission_block_isps'), $language::get('permission_block_isps_text')), array('block_uas', $language::get('permission_block_uas'), $language::get('permission_block_uas_text')), array('create_channel', $language::get('permission_create_channel'), $language::get('permission_create_channel_text')), array('edit_bouquet', $language::get('permission_edit_bouquet'), $language::get('permission_edit_bouquet_text')), array('edit_cat', $language::get('permission_edit_cat'), $language::get('permission_edit_cat_text')), array('channel_order', $language::get('permission_channel_order'), $language::get('permission_channel_order_text')), array('edit_cchannel', $language::get('permission_edit_cchannel'), $language::get('permission_edit_cchannel_text')), array('edit_e2', $language::get('permission_edit_e2'), $language::get('permission_edit_e2_text')), array('epg_edit', $language::get('permission_epg_edit'), $language::get('permission_epg_edit_text')), array('edit_episode', $language::get('permission_edit_episode'), $language::get('permission_edit_episode_text')), array('folder_watch_settings', $language::get('permission_folder_watch_settings'), $language::get('permission_folder_watch_settings_text')), array('settings', $language::get('permission_settings'), $language::get('permission_settings_text')), array('edit_group', $language::get('permission_edit_group'), $language::get('permission_edit_group_text')), array('edit_mag', $language::get('permission_edit_mag'), $language::get('permission_edit_mag_text')), array('edit_movie', $language::get('permission_edit_movie'), $language::get('permission_edit_movie_text')), array('edit_package', $language::get('permission_edit_package'), $language::get('permission_edit_package_text')), array('edit_radio', $language::get('permission_edit_radio'), $language::get('permission_edit_radio_text')), array('edit_reguser', $language::get('permission_edit_reguser'), $language::get('permission_edit_reguser_text')), array('edit_server', $language::get('permission_edit_server'), $language::get('permission_edit_server_text')), array('edit_stream', $language::get('permission_edit_stream'), $language::get('permission_edit_stream_text')), array('edit_series', $language::get('permission_edit_series'), $language::get('permission_edit_series_text')), array('edit_user', $language::get('permission_edit_user'), $language::get('permission_edit_user_text')), array('fingerprint', $language::get('permission_fingerprint'), $language::get('permission_fingerprint_text')), array('import_episodes', $language::get('permission_import_episodes'), $language::get('permission_import_episodes_text')), array('import_movies', $language::get('permission_import_movies'), $language::get('permission_import_movies_text')), array('import_streams', $language::get('permission_import_streams'), $language::get('permission_import_streams_text')), array('database', $language::get('permission_database'), $language::get('permission_database_text')), array('mass_delete', $language::get('permission_mass_delete'), $language::get('permission_mass_delete_text')), array('mass_sedits_vod', $language::get('permission_mass_sedits_vod'), $language::get('permission_mass_sedits_vod_text')), array('mass_sedits', $language::get('permission_mass_sedits'), $language::get('permission_mass_sedits_text')), array('mass_edit_users', $language::get('permission_mass_edit_users'), $language::get('permission_mass_edit_users_text')), array('mass_edit_lines', $language::get('permission_mass_edit_lines'), $language::get('permission_mass_edit_lines_text')), array('mass_edit_mags', $language::get('permission_mass_edit_mags'), $language::get('permission_mass_edit_mags_text')), array('mass_edit_enigmas', $language::get('permission_mass_edit_enigmas'), $language::get('permission_mass_edit_enigmas_text')), array('mass_edit_streams', $language::get('permission_mass_edit_streams'), $language::get('permission_mass_edit_streams_text')), array('mass_edit_radio', $language::get('permission_mass_edit_radio'), $language::get('permission_mass_edit_radio_text')), array('mass_edit_reguser', $language::get('permission_mass_edit_reguser'), $language::get('permission_mass_edit_reguser_text')), array('ticket', $language::get('permission_ticket'), $language::get('permission_ticket_text')), array('subreseller', $language::get('permission_subreseller'), $language::get('permission_subreseller_text')), array('stream_tools', $language::get('permission_stream_tools'), $language::get('permission_stream_tools_text')), array('bouquets', $language::get('permission_bouquets'), $language::get('permission_bouquets_text')), array('categories', $language::get('permission_categories'), $language::get('permission_categories_text')), array('client_request_log', $language::get('permission_client_request_log'), $language::get('permission_client_request_log_text')), array('connection_logs', $language::get('permission_connection_logs'), $language::get('permission_connection_logs_text')), array('manage_cchannels', $language::get('permission_manage_cchannels'), $language::get('permission_manage_cchannels_text')), array('credits_log', $language::get('permission_credits_log'), $language::get('permission_credits_log_text')), array('index', $language::get('permission_index'), $language::get('permission_index_text')), array('manage_e2', $language::get('permission_manage_e2'), $language::get('permission_manage_e2_text')), array('epg', $language::get('permission_epg'), $language::get('permission_epg_text')), array('folder_watch', $language::get('permission_folder_watch'), $language::get('permission_folder_watch_text')), array('folder_watch_output', $language::get('permission_folder_watch_output'), $language::get('permission_folder_watch_output_text')), array('mng_groups', $language::get('permission_mng_groups'), $language::get('permission_mng_groups_text')), array('live_connections', $language::get('permission_live_connections'), $language::get('permission_live_connections_text')), array('login_logs', $language::get('permission_login_logs'), $language::get('permission_login_logs_text')), array('manage_mag', $language::get('permission_manage_mag'), $language::get('permission_manage_mag_text')), array('manage_events', $language::get('permission_manage_events'), $language::get('permission_manage_events_text')), array('movies', $language::get('permission_movies'), $language::get('permission_movies_text')), array('mng_packages', $language::get('permission_mng_packages'), $language::get('permission_mng_packages_text')), array('player', $language::get('permission_player'), $language::get('permission_player_text')), array('process_monitor', $language::get('permission_process_monitor'), $language::get('permission_process_monitor_text')), array('radio', $language::get('permission_radio'), $language::get('permission_radio_text')), array('mng_regusers', $language::get('permission_mng_regusers'), $language::get('permission_mng_regusers_text')), array('reg_userlog', $language::get('permission_reg_userlog'), $language::get('permission_reg_userlog_text')), array('rtmp', $language::get('permission_rtmp'), $language::get('permission_rtmp_text')), array('servers', $language::get('permission_servers'), $language::get('permission_servers_text')), array('stream_errors', $language::get('permission_stream_errors'), $language::get('permission_stream_errors_text')), array('streams', $language::get('permission_streams'), $language::get('permission_streams_text')), array('subresellers', $language::get('permission_subresellers'), $language::get('permission_subresellers_text')), array('manage_tickets', $language::get('permission_manage_tickets'), $language::get('permission_manage_tickets_text')), array('tprofiles', $language::get('permission_tprofiles'), $language::get('permission_tprofiles_text')), array('series', $language::get('permission_series'), $language::get('permission_series_text')), array('users', $language::get('permission_users'), $language::get('permission_users_text')), array('episodes', $language::get('permission_episodes'), $language::get('permission_episodes_text')), array('edit_tprofile', $language::get('permission_edit_tprofile'), $language::get('permission_edit_tprofile_text')), array('folder_watch_add', $language::get('permission_folder_watch_add'), $language::get('permission_folder_watch_add_text')), array('add_code', $language::get('permission_add_code'), $language::get('permission_add_code_text')), array('add_hmac', $language::get('permission_add_hmac'), $language::get('permission_add_hmac_text')), array('block_asns', $language::get('permission_block_asns'), $language::get('permission_block_asns_text')), array('panel_logs', $language::get('permission_panel_logs'), $language::get('permission_panel_logs_text')), array('quick_tools', $language::get('permission_quick_tools'), $language::get('permission_quick_tools_text')), array('restream_logs', $language::get('permission_restream_logs'), $language::get('permission_restream_logs_text'))); +$rPermissionsFile = MAIN_HOME . 'resources/data/permissions.php'; +if (!file_exists($rPermissionsFile)) { + $rPermissionsFile = INCLUDES_PATH . 'data/permissions.php'; +} +$rAdvPermissions = require $rPermissionsFile; function getUserInfo($rUsername, $rPassword) { global $db; @@ -574,8 +578,7 @@ function getDirectReports($rIncludeSelf = true) { function hasResellerPermissions($rType) { global $rPermissions; - - return $rPermissions[$rType]; + return Authorization::hasResellerPermissions($rPermissions, $rType); } @@ -583,38 +586,7 @@ function hasPermissions($rType, $rID) { global $rUserInfo; global $db; global $rPermissions; - - if (isset($rUserInfo) && isset($rPermissions)) { - if ($rType == 'user') { - $rReports = array_map('intval', array_merge(array($rUserInfo['id']), $rPermissions['all_reports'])); - - if (0 < count($rReports)) { - $db->query('SELECT `id` FROM `users` WHERE `id` = ? AND (`owner_id` IN (' . implode(',', $rReports) . ') OR `id` = ?);', $rID, $rUserInfo['id']); - return 0 < $db->num_rows(); - } - - return false; - } - - if ($rType == 'line') { - $rReports = array_map('intval', array_merge(array($rUserInfo['id']), $rPermissions['all_reports'])); - if (0 < count($rReports)) { - $db->query('SELECT `id` FROM `lines` WHERE `id` = ? AND `member_id` IN (' . implode(',', $rReports) . ');', $rID); - return 0 < $db->num_rows(); - } - return false; - } - - if (!($rType == 'adv' && $rPermissions['is_admin'])) { - return false; - } - - if (0 < count($rPermissions['advanced']) && $rUserInfo['member_group_id'] != 1) { - return in_array($rID, ($rPermissions['advanced'] ?: array())); - } - return true; - } - return false; + return Authorization::check($rType, $rID, $rUserInfo, $rPermissions, $db); } function getMemberGroups() { @@ -869,16 +841,7 @@ function getSubUsers($rUser) { } function getAdminImage($rURL, $rMaxW, $rMaxH) { - list($rExtension) = explode('.', strtolower(pathinfo($rURL)['extension'])); - $rImagePath = IMAGES_PATH . 'admin/' . md5($rURL) . '_' . $rMaxW . '_' . $rMaxH . '.' . $rExtension; - - if (file_exists($rImagePath)) { - $rDomain = (empty(CoreUtilities::$rServers[SERVER_ID]['domain_name']) ? CoreUtilities::$rServers[SERVER_ID]['server_ip'] : explode(',', CoreUtilities::$rServers[SERVER_ID]['domain_name'])[0]); - - return CoreUtilities::$rServers[SERVER_ID]['server_protocol'] . '://' . $rDomain . ':' . CoreUtilities::$rServers[SERVER_ID]['request_port'] . '/images/admin/' . md5($rURL) . '_' . $rMaxW . '_' . $rMaxH . '.' . $rExtension; - } - - return CoreUtilities::validateImage($rURL); + return ImageUtils::resize($rURL, $rMaxW, $rMaxH); } function getStreamErrors($rStreamID, $rAmount = 250) { diff --git a/src/includes/admin_api.php b/src/includes/admin_api.php index 6cdfad16..625fe98c 100644 --- a/src/includes/admin_api.php +++ b/src/includes/admin_api.php @@ -1397,83 +1397,7 @@ class API { } public static function processLogin($rData, $rBypassRecaptcha = false) { - if (!self::$rSettings['recaptcha_enable'] || $rBypassRecaptcha) { - } else { - $rResponse = json_decode(file_get_contents('https://www.google.com/recaptcha/api/siteverify?secret=' . self::$rSettings['recaptcha_v2_secret_key'] . '&response=' . $rData['g-recaptcha-response']), true); - - if ($rResponse['success']) { - } else { - return array('status' => STATUS_INVALID_CAPTCHA); - } - } - - $rIP = getIP(); - $rUserInfo = getUserInfo($rData['username'], $rData['password']); - $rAccessCode = getCurrentCode(true); - - if (isset($rUserInfo)) { - self::$db->query('SELECT COUNT(*) AS `count` FROM `access_codes`;'); - $rCodeCount = self::$db->get_row()['count']; - - if ($rCodeCount == 0 || in_array($rUserInfo['member_group_id'], json_decode($rAccessCode['groups'], true))) { - $rPermissions = getPermissions($rUserInfo['member_group_id']); - - if ($rPermissions['is_admin']) { - if ($rUserInfo['status'] == 1) { - $rCrypt = cryptPassword($rData['password']); - - if ($rUserInfo['password'] != $rCrypt) { - self::$db->query('UPDATE `users` SET `password` = ?, `last_login` = UNIX_TIMESTAMP(), `ip` = ? WHERE `id` = ?;', $rCrypt, $rIP, $rUserInfo['id']); - } else { - self::$db->query('UPDATE `users` SET `last_login` = UNIX_TIMESTAMP(), `ip` = ? WHERE `id` = ?;', $rIP, $rUserInfo['id']); - } - - $_SESSION['hash'] = $rUserInfo['id']; - $_SESSION['ip'] = $rIP; - $_SESSION['code'] = getCurrentCode(); - $_SESSION['verify'] = md5($rUserInfo['username'] . '||' . $rCrypt); - - if (!self::$rSettings['save_login_logs']) { - } else { - self::$db->query("INSERT INTO `login_logs`(`type`, `access_code`, `user_id`, `status`, `login_ip`, `date`) VALUES('ADMIN', ?, ?, ?, ?, ?);", $rAccessCode['id'], $rUserInfo['id'], 'SUCCESS', $rIP, time()); - } - - return array('status' => STATUS_SUCCESS); - } - - if (!($rPermissions && ($rPermissions['is_admin'] || $rPermissions['is_reseller']) && !$rUserInfo['status'])) { - } else { - if (!self::$rSettings['save_login_logs']) { - } else { - self::$db->query("INSERT INTO `login_logs`(`type`, `access_code`, `user_id`, `status`, `login_ip`, `date`) VALUES('ADMIN', ?, ?, ?, ?, ?);", $rAccessCode['id'], $rUserInfo['id'], 'DISABLED', $rIP, time()); - } - - return array('status' => STATUS_DISABLED); - } - } else { - if (!self::$rSettings['save_login_logs']) { - } else { - self::$db->query("INSERT INTO `login_logs`(`type`, `access_code`, `user_id`, `status`, `login_ip`, `date`) VALUES('ADMIN', ?, ?, ?, ?, ?);", $rAccessCode['id'], $rUserInfo['id'], 'NOT_ADMIN', $rIP, time()); - } - - return array('status' => STATUS_NOT_ADMIN); - } - } else { - if (!self::$rSettings['save_login_logs']) { - } else { - self::$db->query("INSERT INTO `login_logs`(`type`, `access_code`, `user_id`, `status`, `login_ip`, `date`) VALUES('ADMIN', ?, ?, ?, ?, ?);", $rAccessCode['id'], $rUserInfo['id'], 'INVALID_CODE', $rIP, time()); - } - - return array('status' => STATUS_INVALID_CODE); - } - } else { - if (!self::$rSettings['save_login_logs']) { - } else { - self::$db->query("INSERT INTO `login_logs`(`type`, `access_code`, `user_id`, `status`, `login_ip`, `date`) VALUES('ADMIN', ?, 0, ?, ?, ?);", $rAccessCode['id'], 'INVALID_LOGIN', $rIP, time()); - } - - return array('status' => STATUS_FAILURE); - } + return Authenticator::login(self::$db, self::$rSettings, $rData, $rBypassRecaptcha); } public static function massDeleteStreams($rData) { diff --git a/src/includes/data/permissions.php b/src/includes/data/permissions.php new file mode 100644 index 00000000..262fefd2 --- /dev/null +++ b/src/includes/data/permissions.php @@ -0,0 +1,28 @@ + STATUS_INVALID_CAPTCHA); - } - } - - $rIP = getIP(); - $rUserInfo = getUserInfo($rData['username'], $rData['password']); - $rAccessCode = getCurrentCode(true); - - if (isset($rUserInfo)) { - if (in_array($rUserInfo['member_group_id'], json_decode($rAccessCode['groups'], true)) || count(getActiveCodes()) == 0) { - $rPermissions = getPermissions($rUserInfo['member_group_id']); - - if ($rPermissions['is_reseller']) { - if ($rUserInfo['status'] == 1) { - $rCrypt = cryptPassword($rData['password']); - - if ($rUserInfo['password'] != $rCrypt) { - self::$db->query('UPDATE `users` SET `password` = ?, `last_login` = UNIX_TIMESTAMP(), `ip` = ? WHERE `id` = ?;', $rCrypt, $rIP, $rUserInfo['id']); - } else { - self::$db->query('UPDATE `users` SET `last_login` = UNIX_TIMESTAMP(), `ip` = ? WHERE `id` = ?;', $rIP, $rUserInfo['id']); - } - - $_SESSION['reseller'] = $rUserInfo['id']; - $_SESSION['rip'] = $rIP; - $_SESSION['rcode'] = getCurrentCode(); - $_SESSION['rverify'] = md5($rUserInfo['username'] . '||' . $rCrypt); - - if (!self::$rSettings['save_login_logs']) { - } else { - self::$db->query("INSERT INTO `login_logs`(`type`, `access_code`, `user_id`, `status`, `login_ip`, `date`) VALUES('RESELLER', ?, ?, ?, ?, ?);", $rAccessCode['id'], $rUserInfo['id'], 'SUCCESS', $rIP, time()); - } - - return array('status' => STATUS_SUCCESS); - } - - if (!($rPermissions && ($rPermissions['is_admin'] || $rPermissions['is_reseller']) && !$rUserInfo['status'])) { - } else { - if (!self::$rSettings['save_login_logs']) { - } else { - self::$db->query("INSERT INTO `login_logs`(`type`, `access_code`, `user_id`, `status`, `login_ip`, `date`) VALUES('RESELLER', ?, ?, ?, ?, ?);", $rAccessCode['id'], $rUserInfo['id'], 'DISABLED', $rIP, time()); - } - - return array('status' => STATUS_DISABLED); - } - } else { - if (!self::$rSettings['save_login_logs']) { - } else { - self::$db->query("INSERT INTO `login_logs`(`type`, `access_code`, `user_id`, `status`, `login_ip`, `date`) VALUES('RESELLER', ?, ?, ?, ?, ?);", $rAccessCode['id'], $rUserInfo['id'], 'NOT_ADMIN', $rIP, time()); - } - - return array('status' => STATUS_NOT_RESELLER); - } - } else { - if (!self::$rSettings['save_login_logs']) { - } else { - self::$db->query("INSERT INTO `login_logs`(`type`, `access_code`, `user_id`, `status`, `login_ip`, `date`) VALUES('RESELLER', ?, ?, ?, ?, ?);", $rAccessCode['id'], $rUserInfo['id'], 'INVALID_CODE', $rIP, time()); - } - - return array('status' => STATUS_INVALID_CODE); - } - } else { - if (!self::$rSettings['save_login_logs']) { - } else { - self::$db->query("INSERT INTO `login_logs`(`type`, `access_code`, `user_id`, `status`, `login_ip`, `date`) VALUES('RESELLER', ?, 0, ?, ?, ?);", $rAccessCode['id'], 'INVALID_LOGIN', $rIP, time()); - } - - return array('status' => STATUS_FAILURE); - } + return Authenticator::resellerLogin(self::$db, self::$rSettings, $rData); } public static function processMAG($rData) { diff --git a/src/infrastructure/redis/RedisManager.php b/src/infrastructure/redis/RedisManager.php new file mode 100644 index 00000000..973451c3 --- /dev/null +++ b/src/infrastructure/redis/RedisManager.php @@ -0,0 +1,29 @@ +connect($rConfig['hostname'], 6379); + $rRedis->auth($rSettings['redis_password']); + return $rRedis; + } catch (Exception $e) { + return null; + } + } + + public static function close($rRedis) { + if (is_object($rRedis)) { + $rRedis->close(); + } + return null; + } +} diff --git a/src/resources/data/permissions.php b/src/resources/data/permissions.php new file mode 100644 index 00000000..262fefd2 --- /dev/null +++ b/src/resources/data/permissions.php @@ -0,0 +1,28 @@ + $rAdaptiveID, 'username' => $rUserInfo['username'], 'password' => $rUserInfo['password'], 'extension' => $rExtension, 'pid' => $rPID, 'channel_info' => array('redirect_id' => $rAdaptiveInfo['redirect_id'], 'originator_id' => ($rAdaptiveInfo['originator_id'] ?: null), 'pid' => $rAdaptiveInfo['pid'], 'on_demand' => $rAdaptiveInfo['on_demand'], 'monitor_pid' => $rAdaptiveInfo['monitor_pid']), 'user_info' => array('id' => $rUserInfo['id'], 'max_connections' => $rUserInfo['max_connections'], 'pair_id' => $rUserInfo['pair_id'], 'con_isp_name' => $rUserInfo['con_isp_name'], 'is_restreamer' => $rUserInfo['is_restreamer']), 'external_device' => $rExternalDevice, 'activity_start' => $rActivityStart, 'country_code' => $rCountryCode, 'video_codec' => ($rStreamInfo['codecs']['video']['codec_name'] ?: 'h264'), 'uuid' => $rUUID, 'adaptive' => array($rChannelInfo['redirect_id'], $rStreamID)); + $rTokenData = array('stream_id' => $rAdaptiveID, 'username' => $rUserInfo['username'], 'password' => $rUserInfo['password'], 'extension' => $rExtension, 'pid' => $rPID, 'channel_info' => array('redirect_id' => $rAdaptiveInfo['redirect_id'], 'originator_id' => ($rAdaptiveInfo['originator_id'] ?? null), 'pid' => $rAdaptiveInfo['pid'], 'on_demand' => $rAdaptiveInfo['on_demand'], 'monitor_pid' => $rAdaptiveInfo['monitor_pid']), 'user_info' => array('id' => $rUserInfo['id'], 'max_connections' => $rUserInfo['max_connections'], 'pair_id' => $rUserInfo['pair_id'], 'con_isp_name' => $rUserInfo['con_isp_name'], 'is_restreamer' => $rUserInfo['is_restreamer']), 'external_device' => $rExternalDevice, 'activity_start' => $rActivityStart, 'country_code' => $rCountryCode, 'video_codec' => ($rStreamInfo['codecs']['video']['codec_name'] ?: 'h264'), 'uuid' => $rUUID, 'adaptive' => array($rChannelInfo['redirect_id'], $rStreamID)); $rStreamURL = (string) $rURL . '/auth/' . StreamingUtilities::encryptData(json_encode($rTokenData), StreamingUtilities::$rSettings['live_streaming_pass'], OPENSSL_EXTRA); $rParts[$rBitrate] = '#EXT-X-STREAM-INF:BANDWIDTH=' . $rBitrate . ',RESOLUTION=' . $rWidth . 'x' . $rHeight . "\n" . $rStreamURL; } @@ -533,14 +533,14 @@ if ($rExtension) { exit(); } - StreamingUtilities::showVideoServer('show_not_on_air_video', 'not_on_air_video_path', 'ts', $rUserInfo, $rIP, $rCountryCode, $rUserInfo['con_isp_name'], ($rChannelInfo['originator_id'] ?: $rChannelInfo['redirect_id']), ($rChannelInfo['originator_id'] ? $rChannelInfo['redirect_id'] : null)); + StreamingUtilities::showVideoServer('show_not_on_air_video', 'not_on_air_video_path', 'ts', $rUserInfo, $rIP, $rCountryCode, $rUserInfo['con_isp_name'], ($rChannelInfo['originator_id'] ?? $rChannelInfo['redirect_id']), (!empty($rChannelInfo['originator_id']) ? $rChannelInfo['redirect_id'] : null)); exit(); } else { if (!$rIsHMAC) { - $rTokenData = array('stream_id' => $rStreamID, 'username' => $rUserInfo['username'], 'password' => $rUserInfo['password'], 'extension' => $rExtension, 'pid' => $rPID, 'channel_info' => array('redirect_id' => $rChannelInfo['redirect_id'], 'originator_id' => ($rChannelInfo['originator_id'] ?: null), 'pid' => $rChannelInfo['pid'], 'on_demand' => $rChannelInfo['on_demand'], 'llod' => $rChannelInfo['llod'], 'monitor_pid' => $rChannelInfo['monitor_pid']), 'user_info' => array('id' => $rUserInfo['id'], 'max_connections' => $rUserInfo['max_connections'], 'pair_id' => $rUserInfo['pair_id'], 'con_isp_name' => $rUserInfo['con_isp_name'], 'is_restreamer' => $rUserInfo['is_restreamer']), 'external_device' => $rExternalDevice, 'activity_start' => $rActivityStart, 'country_code' => $rCountryCode, 'video_codec' => $rVideoCodec, 'uuid' => $rUUID); + $rTokenData = array('stream_id' => $rStreamID, 'username' => $rUserInfo['username'], 'password' => $rUserInfo['password'], 'extension' => $rExtension, 'pid' => $rPID, 'channel_info' => array('redirect_id' => $rChannelInfo['redirect_id'], 'originator_id' => ($rChannelInfo['originator_id'] ?? null), 'pid' => $rChannelInfo['pid'], 'on_demand' => $rChannelInfo['on_demand'], 'llod' => ($rChannelInfo['llod'] ?? 0), 'monitor_pid' => $rChannelInfo['monitor_pid']), 'user_info' => array('id' => $rUserInfo['id'], 'max_connections' => $rUserInfo['max_connections'], 'pair_id' => $rUserInfo['pair_id'], 'con_isp_name' => $rUserInfo['con_isp_name'], 'is_restreamer' => $rUserInfo['is_restreamer']), 'external_device' => $rExternalDevice, 'activity_start' => $rActivityStart, 'country_code' => $rCountryCode, 'video_codec' => $rVideoCodec, 'uuid' => $rUUID); } else { - $rTokenData = array('stream_id' => $rStreamID, 'hmac_hash' => StreamingUtilities::$rRequest['hmac'], 'hmac_id' => $rIsHMAC, 'identifier' => $rIdentifier, 'extension' => $rExtension, 'channel_info' => array('redirect_id' => $rChannelInfo['redirect_id'], 'originator_id' => ($rChannelInfo['originator_id'] ?: null), 'pid' => $rChannelInfo['pid'], 'on_demand' => $rChannelInfo['on_demand'], 'llod' => $rChannelInfo['llod'], 'monitor_pid' => $rChannelInfo['monitor_pid']), 'user_info' => $rUserInfo, 'pid' => $rPID, 'external_device' => $rExternalDevice, 'activity_start' => $rActivityStart, 'country_code' => $rCountryCode, 'video_codec' => $rVideoCodec, 'uuid' => $rUUID); + $rTokenData = array('stream_id' => $rStreamID, 'hmac_hash' => StreamingUtilities::$rRequest['hmac'], 'hmac_id' => $rIsHMAC, 'identifier' => $rIdentifier, 'extension' => $rExtension, 'channel_info' => array('redirect_id' => $rChannelInfo['redirect_id'], 'originator_id' => ($rChannelInfo['originator_id'] ?? null), 'pid' => $rChannelInfo['pid'], 'on_demand' => $rChannelInfo['on_demand'], 'llod' => ($rChannelInfo['llod'] ?? 0), 'monitor_pid' => $rChannelInfo['monitor_pid']), 'user_info' => $rUserInfo, 'pid' => $rPID, 'external_device' => $rExternalDevice, 'activity_start' => $rActivityStart, 'country_code' => $rCountryCode, 'video_codec' => $rVideoCodec, 'uuid' => $rUUID); } $rToken = StreamingUtilities::encryptData(json_encode($rTokenData), StreamingUtilities::$rSettings['live_streaming_pass'], OPENSSL_EXTRA); @@ -561,9 +561,9 @@ if ($rExtension) { } if (!$rIsHMAC) { - $rTokenData = array('stream_id' => $rStreamID, 'username' => $rUserInfo['username'], 'password' => $rUserInfo['password'], 'extension' => $rExtension, 'channel_info' => array('stream_id' => $rChannelInfo['stream_id'], 'redirect_id' => ($rChannelInfo['redirect_id'] ?: null), 'originator_id' => ($rChannelInfo['originator_id'] ?: null), 'pid' => $rChannelInfo['pid'], 'on_demand' => $rChannelInfo['on_demand'], 'llod' => $rChannelInfo['llod'], 'monitor_pid' => $rChannelInfo['monitor_pid'], 'proxy' => $rChannelInfo['direct_proxy']), 'user_info' => array('id' => $rUserInfo['id'], 'max_connections' => $rUserInfo['max_connections'], 'pair_id' => $rUserInfo['pair_id'], 'con_isp_name' => $rUserInfo['con_isp_name'], 'is_restreamer' => $rUserInfo['is_restreamer']), 'pid' => $rPID, 'prebuffer' => $rPrebuffer, 'country_code' => $rCountryCode, 'activity_start' => $rActivityStart, 'external_device' => $rExternalDevice, 'video_codec' => $rVideoCodec, 'uuid' => $rUUID); + $rTokenData = array('stream_id' => $rStreamID, 'username' => $rUserInfo['username'], 'password' => $rUserInfo['password'], 'extension' => $rExtension, 'channel_info' => array('stream_id' => $rChannelInfo['stream_id'], 'redirect_id' => ($rChannelInfo['redirect_id'] ?: null), 'originator_id' => ($rChannelInfo['originator_id'] ?? null), 'pid' => $rChannelInfo['pid'], 'on_demand' => $rChannelInfo['on_demand'], 'llod' => ($rChannelInfo['llod'] ?? 0), 'monitor_pid' => $rChannelInfo['monitor_pid'], 'proxy' => $rChannelInfo['direct_proxy']), 'user_info' => array('id' => $rUserInfo['id'], 'max_connections' => $rUserInfo['max_connections'], 'pair_id' => $rUserInfo['pair_id'], 'con_isp_name' => $rUserInfo['con_isp_name'], 'is_restreamer' => $rUserInfo['is_restreamer']), 'pid' => $rPID, 'prebuffer' => $rPrebuffer, 'country_code' => $rCountryCode, 'activity_start' => $rActivityStart, 'external_device' => $rExternalDevice, 'video_codec' => $rVideoCodec, 'uuid' => $rUUID); } else { - $rTokenData = array('stream_id' => $rStreamID, 'hmac_hash' => StreamingUtilities::$rRequest['hmac'], 'hmac_id' => $rIsHMAC, 'identifier' => $rIdentifier, 'extension' => $rExtension, 'channel_info' => array('stream_id' => $rChannelInfo['stream_id'], 'redirect_id' => ($rChannelInfo['redirect_id'] ?: null), 'originator_id' => ($rChannelInfo['originator_id'] ?: null), 'pid' => $rChannelInfo['pid'], 'on_demand' => $rChannelInfo['on_demand'], 'llod' => $rChannelInfo['llod'], 'monitor_pid' => $rChannelInfo['monitor_pid'], 'proxy' => $rChannelInfo['direct_proxy']), 'user_info' => $rUserInfo, 'pid' => $rPID, 'prebuffer' => $rPrebuffer, 'country_code' => $rCountryCode, 'activity_start' => $rActivityStart, 'external_device' => $rExternalDevice, 'video_codec' => $rVideoCodec, 'uuid' => $rUUID); + $rTokenData = array('stream_id' => $rStreamID, 'hmac_hash' => StreamingUtilities::$rRequest['hmac'], 'hmac_id' => $rIsHMAC, 'identifier' => $rIdentifier, 'extension' => $rExtension, 'channel_info' => array('stream_id' => $rChannelInfo['stream_id'], 'redirect_id' => ($rChannelInfo['redirect_id'] ?: null), 'originator_id' => ($rChannelInfo['originator_id'] ?? null), 'pid' => $rChannelInfo['pid'], 'on_demand' => $rChannelInfo['on_demand'], 'llod' => ($rChannelInfo['llod'] ?? 0), 'monitor_pid' => $rChannelInfo['monitor_pid'], 'proxy' => $rChannelInfo['direct_proxy']), 'user_info' => $rUserInfo, 'pid' => $rPID, 'prebuffer' => $rPrebuffer, 'country_code' => $rCountryCode, 'activity_start' => $rActivityStart, 'external_device' => $rExternalDevice, 'video_codec' => $rVideoCodec, 'uuid' => $rUUID); } $rToken = StreamingUtilities::encryptData(json_encode($rTokenData), StreamingUtilities::$rSettings['live_streaming_pass'], OPENSSL_EXTRA);