2024-06-28 17:05:32 +00:00
< ? php
2024-07-01 09:45:07 +00:00
/**
* Jilo web logs observer
*
* Description : A web interface to Jilo ( JItsi Logs Observer ), written in PHP
* Author : Yasen Pramatarov
* License : GPLv2
* Project URL : https :// lindeas . com / jilo
2025-01-15 17:06:12 +00:00
* Year : 2024 - 2025
2025-04-12 14:21:34 +00:00
* Version : 0.4
2024-07-01 09:45:07 +00:00
*/
2025-05-08 16:37:35 +00:00
// error reporting, comment out in production
//ini_set('display_errors', 1);
//ini_set('display_startup_errors', 1);
//error_reporting(E_ALL);
2025-06-17 09:23:13 +00:00
// Prepare config loader
require_once __DIR__ . '/../app/core/ConfigLoader.php' ;
use App\Core\ConfigLoader ;
// Load configuration
$config = ConfigLoader :: loadConfig ([
__DIR__ . '/../app/config/jilo-web.conf.php' ,
__DIR__ . '/../jilo-web.conf.php' ,
'/srv/jilo-web/jilo-web.conf.php' ,
'/opt/jilo-web/jilo-web.conf.php' ,
]);
// Make config available globally
$GLOBALS [ 'config' ] = $config ;
// Expose config file path for pages
$config_file = ConfigLoader :: getConfigPath ();
$localConfigPath = str_replace ( __DIR__ . '/..' , '' , $config_file );
// Set app root with default
$app_root = $config [ 'folder' ] ? ? '/' ;
2025-04-16 17:23:27 +00:00
// Preparing plugins and hooks
2025-06-18 09:34:56 +00:00
// Initialize HookDispatcher and plugin system
2025-04-24 10:32:45 +00:00
require_once __DIR__ . '/../app/core/HookDispatcher.php' ;
require_once __DIR__ . '/../app/core/PluginManager.php' ;
use App\Core\HookDispatcher ;
use App\Core\PluginManager ;
2025-05-08 16:37:35 +00:00
// Global allowed URLs registration
register_hook ( 'filter_allowed_urls' , function ( $urls ) {
if ( isset ( $GLOBALS [ 'plugin_controllers' ]) && is_array ( $GLOBALS [ 'plugin_controllers' ])) {
foreach ( $GLOBALS [ 'plugin_controllers' ] as $controllers ) {
foreach ( $controllers as $ctrl ) {
$urls [] = $ctrl ;
}
}
}
return $urls ;
});
2025-04-24 10:32:45 +00:00
// Hook registration and dispatch helpers
function register_hook ( string $hook , callable $callback ) : void {
HookDispatcher :: register ( $hook , $callback );
2025-04-16 17:23:27 +00:00
}
2025-04-24 10:32:45 +00:00
function do_hook ( string $hook , array $context = []) : void {
HookDispatcher :: dispatch ( $hook , $context );
2025-04-16 17:23:27 +00:00
}
2025-04-24 10:32:45 +00:00
function filter_public_pages ( array $pages ) : array {
return HookDispatcher :: applyFilters ( 'filter_public_pages' , $pages );
}
function filter_allowed_urls ( array $urls ) : array {
return HookDispatcher :: applyFilters ( 'filter_allowed_urls' , $urls );
2025-04-16 17:23:27 +00:00
}
2025-04-24 10:32:45 +00:00
// Load enabled plugins
$plugins_dir = dirname ( __DIR__ ) . '/plugins/' ;
$enabled_plugins = PluginManager :: load ( $plugins_dir );
$GLOBALS [ 'enabled_plugins' ] = $enabled_plugins ;
2025-04-16 10:11:51 +00:00
// Define CSRF token include path globally
if ( ! defined ( 'CSRF_TOKEN_INCLUDE' )) {
define ( 'CSRF_TOKEN_INCLUDE' , dirname ( __DIR__ ) . '/app/includes/csrf_token.php' );
}
2025-04-22 12:31:50 +00:00
// Global cnstants
require_once '../app/includes/constants.php' ;
2025-04-07 09:32:54 +00:00
// we start output buffering and
2024-08-19 10:25:09 +00:00
// flush it later only when there is no redirect
ob_start ();
2025-04-13 16:34:13 +00:00
// Start session before any session-dependent code
require_once '../app/classes/session.php' ;
2025-06-17 09:23:13 +00:00
// Initialize themes system after session is started
require_once __DIR__ . '/../app/helpers/theme.php' ;
use app\Helpers\Theme ;
2025-04-13 16:34:13 +00:00
Session :: startSession ();
2025-04-26 12:36:41 +00:00
// Define page variable early via sanitize
require_once __DIR__ . '/../app/includes/sanitize.php' ;
// Ensure $page is defined to avoid undefined variable
if ( ! isset ( $page )) {
$page = 'dashboard' ;
}
2025-02-17 13:52:46 +00:00
2025-06-17 09:23:13 +00:00
// List of pages that don't require authentication
$public_pages = [ 'login' , 'help' , 'about' ];
// Let plugins filter/extend public_pages
$public_pages = filter_public_pages ( $public_pages );
2025-04-26 12:36:41 +00:00
// Middleware pipeline for security, sanitization & CSRF
require_once __DIR__ . '/../app/core/MiddlewarePipeline.php' ;
$pipeline = new \App\Core\MiddlewarePipeline ();
$pipeline -> add ( function () {
// Apply security headers
require_once __DIR__ . '/../app/includes/security_headers_middleware.php' ;
return true ;
});
2024-10-04 08:36:45 +00:00
2025-06-17 09:23:13 +00:00
// For public pages, we don't need to validate the session
// The Router will handle authentication for protected pages
$validSession = false ;
$userId = null ;
2025-01-30 16:48:46 +00:00
2025-06-17 09:23:13 +00:00
// Only check session for non-public pages
if ( ! in_array ( $page , $public_pages )) {
$validSession = Session :: isValidSession ( true );
if ( $validSession ) {
$userId = Session :: getUserId ();
}
}
2025-04-14 07:39:58 +00:00
2025-02-17 12:36:00 +00:00
// Initialize feedback message system
require_once '../app/classes/feedback.php' ;
$system_messages = [];
2025-01-06 09:13:28 +00:00
require '../app/includes/errors.php' ;
2024-07-01 09:45:07 +00:00
// list of available pages
// edit accordingly, add 'pages/PAGE.php'
$allowed_urls = [
2024-09-13 11:02:59 +00:00
'dashboard' ,
2025-04-25 09:10:29 +00:00
'conferences' , 'participants' , 'components' ,
'graphs' , 'latest' , 'livejs' , 'agents' ,
'profile' , 'credentials' , 'config' , 'security' ,
2025-06-26 11:27:54 +00:00
'settings' , 'theme' , 'theme-asset' ,
2025-09-25 08:37:54 +00:00
'admin-tools' ,
2024-10-19 13:09:16 +00:00
'status' ,
2025-05-08 16:37:35 +00:00
'help' , 'about' ,
2025-04-25 09:10:29 +00:00
'login' , 'logout' ,
2024-07-01 09:45:07 +00:00
];
2025-04-16 17:23:27 +00:00
// Let plugins filter/extend allowed_urls
$allowed_urls = filter_allowed_urls ( $allowed_urls );
2025-04-24 09:37:59 +00:00
// Dispatch routing and auth
require_once __DIR__ . '/../app/core/Router.php' ;
2025-04-25 07:20:57 +00:00
use App\Core\Router ;
$currentUser = Router :: checkAuth ( $config , $app_root , $public_pages , $page );
2024-06-28 17:05:32 +00:00
2025-04-25 09:10:29 +00:00
// Connect to DB via DatabaseConnector
2025-04-24 11:12:24 +00:00
require_once __DIR__ . '/../app/core/DatabaseConnector.php' ;
use App\Core\DatabaseConnector ;
2025-04-25 09:10:29 +00:00
$db = DatabaseConnector :: connect ( $config );
2024-09-04 09:53:02 +00:00
2025-04-25 07:20:57 +00:00
// Logging: default to NullLogger, plugin can override
require_once __DIR__ . '/../app/core/NullLogger.php' ;
use App\Core\NullLogger ;
$logObject = new NullLogger ();
// Get the user IP
require_once __DIR__ . '/../app/helpers/ip_helper.php' ;
$user_IP = '' ;
// Plugin: initialize logging system plugin if available
2025-04-25 09:10:29 +00:00
do_hook ( 'logger.system_init' , [ 'db' => $db ]);
2025-04-25 07:20:57 +00:00
// Override defaults if plugin provided real logger
if ( isset ( $GLOBALS [ 'logObject' ])) {
$logObject = $GLOBALS [ 'logObject' ];
}
if ( isset ( $GLOBALS [ 'user_IP' ])) {
$user_IP = $GLOBALS [ 'user_IP' ];
}
2024-09-16 14:09:37 +00:00
2025-09-24 16:44:38 +00:00
// Check for pending DB migrations (non-intrusive: warn only)
try {
$migrationsDir = __DIR__ . '/../doc/database/migrations' ;
if ( is_dir ( $migrationsDir )) {
require_once __DIR__ . '/../app/core/MigrationRunner.php' ;
$runner = new \App\Core\MigrationRunner ( $db , $migrationsDir );
if ( $runner -> hasPendingMigrations ()) {
$pending = $runner -> listPendingMigrations ();
2025-09-25 14:26:03 +00:00
$msg = 'Database schema is out of date. There are pending migrations. Run "<code>php scripts/migrate.php up</code>" or use the <a href="?page=admin-tools">Admin tools</a>' ;
2025-09-24 16:44:38 +00:00
// Log and show as a system message
$logObject -> log ( 'warning' , $msg , [ 'scope' => 'system' ]);
2025-09-25 14:26:03 +00:00
Feedback :: flash ( 'SYSTEM' , 'MIGRATIONS_PENDING' , $msg , false , true , false );
2025-09-24 16:44:38 +00:00
}
}
} catch ( \Throwable $e ) {
// Do not break the app; log only
error_log ( 'Migration check failed: ' . $e -> getMessage ());
}
2025-04-26 12:36:41 +00:00
// CSRF middleware and run pipeline
$pipeline -> add ( function () {
// Initialize security middleware
require_once __DIR__ . '/../app/includes/csrf_middleware.php' ;
require_once __DIR__ . '/../app/helpers/security.php' ;
$security = SecurityHelper :: getInstance ();
// Verify CSRF token for POST requests
return applyCsrfMiddleware ();
});
$pipeline -> add ( function () {
// Init rate limiter
global $db , $page , $userId ;
require_once __DIR__ . '/../app/includes/rate_limit_middleware.php' ;
return checkRateLimit ( $db , $page , $userId );
});
$pipeline -> add ( function () {
// Init user functions
global $db , $userObject ;
require_once __DIR__ . '/../app/classes/user.php' ;
include __DIR__ . '/../app/helpers/profile.php' ;
$userObject = new User ( $db );
return true ;
});
if ( ! $pipeline -> run ()) {
exit ;
}
2025-01-03 16:44:08 +00:00
2025-09-24 17:27:17 +00:00
// Maintenance mode: show maintenance page to non-superusers
try {
require_once __DIR__ . '/../app/core/Maintenance.php' ;
if ( \App\Core\Maintenance :: isEnabled ()) {
$isSuperuser = false ;
if ( $validSession && isset ( $userId ) && isset ( $userObject ) && method_exists ( $userObject , 'hasRight' )) {
// user 1 is always superuser per implementation, but also check explicit right
$isSuperuser = ( $userId === 1 ) || ( bool ) $userObject -> hasRight ( $userId , 'superuser' );
}
if ( ! $isSuperuser ) {
http_response_code ( 503 );
// Show themed maintenance page
\App\Helpers\Theme :: include ( 'page-header' );
\App\Helpers\Theme :: include ( 'page-menu' );
include __DIR__ . '/../app/templates/maintenance.php' ;
\App\Helpers\Theme :: include ( 'page-footer' );
ob_end_flush ();
exit ;
}
}
} catch ( \Throwable $e ) {
// Do not break app if maintenance check fails
}
2025-09-24 13:56:14 +00:00
// Apply per-user theme from DB into session (without persisting) once user is known
if ( $validSession && isset ( $userId ) && isset ( $userObject ) && is_object ( $userObject ) && method_exists ( $userObject , 'getUserTheme' )) {
try {
$dbTheme = $userObject -> getUserTheme (( int ) $userId );
if ( $dbTheme ) {
\App\Helpers\Theme :: setCurrentTheme ( $dbTheme , false );
}
} catch ( \Throwable $e ) {
// Non-fatal if theme load fails
}
}
2024-09-04 09:53:02 +00:00
// get platforms details
require '../app/classes/platform.php' ;
2025-04-25 09:10:29 +00:00
$platformObject = new Platform ( $db );
2024-09-04 09:53:02 +00:00
$platformsAll = $platformObject -> getPlatformDetails ();
2024-08-17 08:20:08 +00:00
// by default we connect ot the first configured platform
2024-10-04 08:36:45 +00:00
if ( $platform_id == '' ) {
$platform_id = $platformsAll [ 0 ][ 'id' ];
}
2024-09-04 09:53:02 +00:00
$platformDetails = $platformObject -> getPlatformDetails ( $platform_id );
2024-08-17 08:20:08 +00:00
2024-09-13 08:05:11 +00:00
// logout is a special case, as we can't use session vars for notices
if ( $page == 'logout' ) {
2025-06-17 09:23:13 +00:00
// Save config before destroying session
$savedConfig = $config ;
2024-09-13 08:05:11 +00:00
// clean up session
2025-04-13 16:34:13 +00:00
Session :: destroySession ();
2025-01-30 16:48:46 +00:00
// start new session for the login page
2025-04-13 16:34:13 +00:00
Session :: startSession ();
2025-01-30 16:48:46 +00:00
2025-06-17 09:23:13 +00:00
// Restore config to global scope
$config = $savedConfig ;
$GLOBALS [ 'config' ] = $config ;
2024-09-13 08:05:11 +00:00
setcookie ( 'username' , " " , time () - 100 , $config [ 'folder' ], $config [ 'domain' ], isset ( $_SERVER [ 'HTTPS' ]), true );
2024-06-28 17:05:32 +00:00
2025-01-06 09:13:28 +00:00
// Log successful logout
2025-04-27 16:00:58 +00:00
$logObject -> log ( 'info' , " Logout: User \" $currentUser\ " logged out . IP : $user_IP " , ['user_id' => $userId , 'scope' => 'user']);
2024-09-30 09:52:06 +00:00
2025-01-06 09:13:28 +00:00
// Set success message
2025-02-16 08:18:26 +00:00
Feedback :: flash ( 'LOGIN' , 'LOGOUT_SUCCESS' );
2025-01-04 10:30:44 +00:00
2025-05-22 11:39:20 +00:00
// Use theme helper to include templates
\App\Helpers\Theme :: include ( 'page-header' );
\App\Helpers\Theme :: include ( 'page-menu' );
2025-01-06 09:13:28 +00:00
include '../app/pages/login.php' ;
2025-05-22 11:39:20 +00:00
\App\Helpers\Theme :: include ( 'page-footer' );
2025-01-04 10:30:44 +00:00
2024-09-13 08:05:11 +00:00
} else {
2024-09-13 09:13:00 +00:00
// if user is logged in, we need user details and rights
2025-04-13 16:34:13 +00:00
if ( $validSession ) {
2024-10-23 12:28:45 +00:00
// If by error a logged in user requests the login page
if ( $page === 'login' ) {
header ( 'Location: ' . htmlspecialchars ( $app_root ));
exit ();
}
2025-04-14 07:39:58 +00:00
$userDetails = $userObject -> getUserDetails ( $userId );
$userRights = $userObject -> getUserRights ( $userId );
2025-04-07 09:32:54 +00:00
$userTimezone = ( ! empty ( $userDetails [ 0 ][ 'timezone' ])) ? $userDetails [ 0 ][ 'timezone' ] : 'UTC' ; // Default to UTC if no timezone is set (or is missing)
2024-10-23 12:28:45 +00:00
2024-10-23 12:13:01 +00:00
// check if the Jilo Server is running
require '../app/classes/server.php' ;
2025-04-25 09:10:29 +00:00
$serverObject = new Server ( $db );
2024-10-23 12:13:01 +00:00
$server_host = '127.0.0.1' ;
$server_port = '8080' ;
$server_endpoint = '/health' ;
$server_status = $serverObject -> getServerStatus ( $server_host , $server_port , $server_endpoint );
if ( ! $server_status ) {
2025-02-16 08:18:26 +00:00
Feedback :: flash ( 'ERROR' , 'DEFAULT' , 'The Jilo Server is not running. Some data may be old and incorrect.' , false , true );
2024-10-23 12:13:01 +00:00
}
2024-10-18 12:41:15 +00:00
}
2025-04-17 07:20:37 +00:00
// --- Plugin loading logic for all enabled plugins ---
2025-05-08 16:37:35 +00:00
// Ensure all enabled plugin bootstraps are loaded before mapping controllers
2025-04-17 07:20:37 +00:00
foreach ( $GLOBALS [ 'enabled_plugins' ] as $plugin_name => $plugin_info ) {
2025-05-08 16:37:35 +00:00
$bootstrap_path = $plugin_info [ 'path' ] . '/bootstrap.php' ;
if ( file_exists ( $bootstrap_path )) {
require_once $bootstrap_path ;
}
}
// Plugin controller mapping logic (we add each controller listed in bootstrap as a page)
$mapped_plugin_controllers = [];
foreach ( $GLOBALS [ 'enabled_plugins' ] as $plugin_name => $plugin_info ) {
if ( isset ( $GLOBALS [ 'plugin_controllers' ][ $plugin_name ])) {
foreach ( $GLOBALS [ 'plugin_controllers' ][ $plugin_name ] as $plugin_page ) {
$controller_path = $plugin_info [ 'path' ] . '/controllers/' . $plugin_page . '.php' ;
if ( file_exists ( $controller_path )) {
$mapped_plugin_controllers [ $plugin_page ] = $controller_path ;
}
}
2025-04-17 07:20:37 +00:00
}
}
2024-09-13 08:05:11 +00:00
// page building
if ( in_array ( $page , $allowed_urls )) {
2025-05-08 16:37:35 +00:00
// The page is in allowed URLs
if ( isset ( $mapped_plugin_controllers [ $page ]) && file_exists ( $mapped_plugin_controllers [ $page ])) {
// The page is from a plugin controller
if ( defined ( 'PLUGIN_PAGE_DIRECT_OUTPUT' ) && PLUGIN_PAGE_DIRECT_OUTPUT === true ) {
// Barebone page controller, we don't output anything extra
include $mapped_plugin_controllers [ $page ];
ob_end_flush ();
exit ;
} else {
2025-05-22 11:39:20 +00:00
\App\Helpers\Theme :: include ( 'page-header' );
\App\Helpers\Theme :: include ( 'page-menu' );
2025-05-08 16:37:35 +00:00
if ( $validSession ) {
2025-05-22 11:39:20 +00:00
\App\Helpers\Theme :: include ( 'page-sidebar' );
2025-05-08 16:37:35 +00:00
}
include $mapped_plugin_controllers [ $page ];
2025-05-22 11:39:20 +00:00
\App\Helpers\Theme :: include ( 'page-footer' );
2025-05-08 16:37:35 +00:00
}
2025-04-25 09:10:29 +00:00
} else {
2025-05-08 16:37:35 +00:00
// The page is from a core controller
2025-05-22 11:39:20 +00:00
\App\Helpers\Theme :: include ( 'page-header' );
\App\Helpers\Theme :: include ( 'page-menu' );
2025-05-08 16:37:35 +00:00
if ( $validSession ) {
2025-05-22 11:39:20 +00:00
\App\Helpers\Theme :: include ( 'page-sidebar' );
2025-05-08 16:37:35 +00:00
}
if ( file_exists ( " ../app/pages/ { $page } .php " )) {
include " ../app/pages/ { $page } .php " ;
} else {
include '../app/templates/error-notfound.php' ;
}
2025-06-18 09:34:56 +00:00
\App\Helpers\Theme :: include ( 'page-footer' );
2025-04-25 09:10:29 +00:00
}
2024-07-01 09:45:07 +00:00
} else {
2025-05-08 16:37:35 +00:00
// The page is not in allowed URLs
2025-06-18 09:34:56 +00:00
\App\Helpers\Theme :: include ( 'page-header' );
\App\Helpers\Theme :: include ( 'page-menu' );
2025-05-08 16:37:35 +00:00
if ( $validSession ) {
2025-06-18 09:34:56 +00:00
\App\Helpers\Theme :: include ( 'page-sidebar' );
2025-05-08 16:37:35 +00:00
}
2024-09-13 11:02:59 +00:00
include '../app/templates/error-notfound.php' ;
2025-06-18 09:34:56 +00:00
\App\Helpers\Theme :: include ( 'page-footer' );
2024-08-06 07:40:52 +00:00
}
2024-06-28 17:05:32 +00:00
}
2024-08-19 10:25:09 +00:00
// flush the output buffer and show the page
ob_end_flush ();
2024-07-01 09:45:07 +00:00
// clear errors and notices before next page just in case
unset ( $_SESSION [ 'error' ]);
unset ( $_SESSION [ 'notice' ]);