1<?php 2/* 3** Zabbix 4** Copyright (C) 2001-2021 Zabbix SIA 5** 6** This program is free software; you can redistribute it and/or modify 7** it under the terms of the GNU General Public License as published by 8** the Free Software Foundation; either version 2 of the License, or 9** (at your option) any later version. 10** 11** This program is distributed in the hope that it will be useful, 12** but WITHOUT ANY WARRANTY; without even the implied warranty of 13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14** GNU General Public License for more details. 15** 16** You should have received a copy of the GNU General Public License 17** along with this program; if not, write to the Free Software 18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19**/ 20 21 22require_once dirname(__FILE__).'/CAutoloader.php'; 23 24class ZBase { 25 const EXEC_MODE_DEFAULT = 'default'; 26 const EXEC_MODE_SETUP = 'setup'; 27 const EXEC_MODE_API = 'api'; 28 29 /** 30 * An instance of the current Z object. 31 * 32 * @var Z 33 */ 34 protected static $instance; 35 36 /** 37 * The absolute path to the root directory. 38 * 39 * @var string 40 */ 41 protected $rootDir; 42 43 /** 44 * @var array of config data from zabbix config file 45 */ 46 protected $config = []; 47 48 /** 49 * Returns the current instance of Z. 50 * 51 * @static 52 * 53 * @return Z 54 */ 55 public static function getInstance() { 56 if (self::$instance === null) { 57 self::$instance = new Z(); 58 } 59 60 return self::$instance; 61 } 62 63 /** 64 * Init modules required to run frontend. 65 */ 66 protected function init() { 67 $this->rootDir = $this->findRootDir(); 68 $this->registerAutoloader(); 69 70 // initialize API classes 71 $apiServiceFactory = new CApiServiceFactory(); 72 73 $client = new CLocalApiClient(); 74 $client->setServiceFactory($apiServiceFactory); 75 $wrapper = new CFrontendApiWrapper($client); 76 $wrapper->setProfiler(CProfiler::getInstance()); 77 API::setWrapper($wrapper); 78 API::setApiServiceFactory($apiServiceFactory); 79 80 // system includes 81 require_once $this->getRootDir().'/include/debug.inc.php'; 82 require_once $this->getRootDir().'/include/gettextwrapper.inc.php'; 83 require_once $this->getRootDir().'/include/defines.inc.php'; 84 require_once $this->getRootDir().'/include/func.inc.php'; 85 require_once $this->getRootDir().'/include/html.inc.php'; 86 require_once $this->getRootDir().'/include/perm.inc.php'; 87 require_once $this->getRootDir().'/include/audit.inc.php'; 88 require_once $this->getRootDir().'/include/js.inc.php'; 89 require_once $this->getRootDir().'/include/users.inc.php'; 90 require_once $this->getRootDir().'/include/validate.inc.php'; 91 require_once $this->getRootDir().'/include/profiles.inc.php'; 92 require_once $this->getRootDir().'/include/locales.inc.php'; 93 require_once $this->getRootDir().'/include/db.inc.php'; 94 95 // page specific includes 96 require_once $this->getRootDir().'/include/actions.inc.php'; 97 require_once $this->getRootDir().'/include/discovery.inc.php'; 98 require_once $this->getRootDir().'/include/draw.inc.php'; 99 require_once $this->getRootDir().'/include/events.inc.php'; 100 require_once $this->getRootDir().'/include/graphs.inc.php'; 101 require_once $this->getRootDir().'/include/hostgroups.inc.php'; 102 require_once $this->getRootDir().'/include/hosts.inc.php'; 103 require_once $this->getRootDir().'/include/httptest.inc.php'; 104 require_once $this->getRootDir().'/include/ident.inc.php'; 105 require_once $this->getRootDir().'/include/images.inc.php'; 106 require_once $this->getRootDir().'/include/items.inc.php'; 107 require_once $this->getRootDir().'/include/maintenances.inc.php'; 108 require_once $this->getRootDir().'/include/maps.inc.php'; 109 require_once $this->getRootDir().'/include/media.inc.php'; 110 require_once $this->getRootDir().'/include/services.inc.php'; 111 require_once $this->getRootDir().'/include/sounds.inc.php'; 112 require_once $this->getRootDir().'/include/triggers.inc.php'; 113 require_once $this->getRootDir().'/include/valuemap.inc.php'; 114 } 115 116 /** 117 * Initializes the application. 118 */ 119 public function run($mode) { 120 $this->init(); 121 122 $this->setMaintenanceMode(); 123 set_error_handler('zbx_err_handler'); 124 125 switch ($mode) { 126 case self::EXEC_MODE_DEFAULT: 127 if (getRequest('action', '') === 'notifications.get') { 128 CWebUser::disableSessionExtension(); 129 } 130 131 $this->loadConfigFile(); 132 $this->initDB(); 133 $this->authenticateUser(); 134 $this->initLocales(CWebUser::$data); 135 $this->setLayoutModeByUrl(); 136 break; 137 138 case self::EXEC_MODE_API: 139 $this->loadConfigFile(); 140 $this->initDB(); 141 $this->initLocales(['lang' => 'en_gb']); 142 break; 143 144 case self::EXEC_MODE_SETUP: 145 try { 146 // try to load config file, if it exists we need to init db and authenticate user to check permissions 147 $this->loadConfigFile(); 148 $this->initDB(); 149 $this->authenticateUser(); 150 $this->initLocales(CWebUser::$data); 151 } 152 catch (ConfigFileException $e) {} 153 break; 154 } 155 156 // new MVC processing, otherwise we continue execution old style 157 if (hasRequest('action')) { 158 $router = new CRouter(getRequest('action')); 159 160 if ($router->getController() !== null) { 161 CProfiler::getInstance()->start(); 162 $this->processRequest($router); 163 exit; 164 } 165 } 166 } 167 168 /** 169 * Returns the absolute path to the root dir. 170 * 171 * @return string 172 */ 173 public static function getRootDir() { 174 return self::getInstance()->rootDir; 175 } 176 177 /** 178 * Returns the path to the frontend's root dir. 179 * 180 * @return string 181 */ 182 private function findRootDir() { 183 return realpath(dirname(__FILE__).'/../../..'); 184 } 185 186 /** 187 * Register autoloader. 188 */ 189 private function registerAutoloader() { 190 $autoloader = new CAutoloader($this->getIncludePaths()); 191 $autoloader->register(); 192 } 193 194 /** 195 * An array of directories to add to the autoloader include paths. 196 * 197 * @return array 198 */ 199 private function getIncludePaths() { 200 return [ 201 $this->rootDir.'/include/classes/core', 202 $this->rootDir.'/include/classes/mvc', 203 $this->rootDir.'/include/classes/api', 204 $this->rootDir.'/include/classes/api/services', 205 $this->rootDir.'/include/classes/api/managers', 206 $this->rootDir.'/include/classes/api/clients', 207 $this->rootDir.'/include/classes/api/wrappers', 208 $this->rootDir.'/include/classes/db', 209 $this->rootDir.'/include/classes/debug', 210 $this->rootDir.'/include/classes/validators', 211 $this->rootDir.'/include/classes/validators/schema', 212 $this->rootDir.'/include/classes/validators/string', 213 $this->rootDir.'/include/classes/validators/object', 214 $this->rootDir.'/include/classes/validators/hostgroup', 215 $this->rootDir.'/include/classes/validators/host', 216 $this->rootDir.'/include/classes/validators/hostprototype', 217 $this->rootDir.'/include/classes/validators/event', 218 $this->rootDir.'/include/classes/export', 219 $this->rootDir.'/include/classes/export/writers', 220 $this->rootDir.'/include/classes/export/elements', 221 $this->rootDir.'/include/classes/graph', 222 $this->rootDir.'/include/classes/graphdraw', 223 $this->rootDir.'/include/classes/import', 224 $this->rootDir.'/include/classes/import/converters', 225 $this->rootDir.'/include/classes/import/importers', 226 $this->rootDir.'/include/classes/import/preprocessors', 227 $this->rootDir.'/include/classes/import/readers', 228 $this->rootDir.'/include/classes/import/validators', 229 $this->rootDir.'/include/classes/items', 230 $this->rootDir.'/include/classes/triggers', 231 $this->rootDir.'/include/classes/server', 232 $this->rootDir.'/include/classes/screens', 233 $this->rootDir.'/include/classes/services', 234 $this->rootDir.'/include/classes/sysmaps', 235 $this->rootDir.'/include/classes/helpers', 236 $this->rootDir.'/include/classes/helpers/trigger', 237 $this->rootDir.'/include/classes/macros', 238 $this->rootDir.'/include/classes/tree', 239 $this->rootDir.'/include/classes/html', 240 $this->rootDir.'/include/classes/html/pageheader', 241 $this->rootDir.'/include/classes/html/svg', 242 $this->rootDir.'/include/classes/html/widget', 243 $this->rootDir.'/include/classes/html/interfaces', 244 $this->rootDir.'/include/classes/parsers', 245 $this->rootDir.'/include/classes/parsers/results', 246 $this->rootDir.'/include/classes/controllers', 247 $this->rootDir.'/include/classes/routing', 248 $this->rootDir.'/include/classes/json', 249 $this->rootDir.'/include/classes/user', 250 $this->rootDir.'/include/classes/setup', 251 $this->rootDir.'/include/classes/regexp', 252 $this->rootDir.'/include/classes/ldap', 253 $this->rootDir.'/include/classes/pagefilter', 254 $this->rootDir.'/include/classes/widgets/fields', 255 $this->rootDir.'/include/classes/widgets/forms', 256 $this->rootDir.'/include/classes/widgets', 257 $this->rootDir.'/local/app/controllers', 258 $this->rootDir.'/app/controllers' 259 ]; 260 } 261 262 /** 263 * An array of available themes. 264 * 265 * @return array 266 */ 267 public static function getThemes() { 268 return [ 269 'blue-theme' => _('Blue'), 270 'dark-theme' => _('Dark'), 271 'hc-light' => _('High-contrast light'), 272 'hc-dark' => _('High-contrast dark') 273 ]; 274 } 275 276 /** 277 * Check if maintenance mode is enabled. 278 * 279 * @throws Exception 280 */ 281 protected function setMaintenanceMode() { 282 require_once $this->getRootDir().'/conf/maintenance.inc.php'; 283 284 if (defined('ZBX_DENY_GUI_ACCESS')) { 285 $user_ip = (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && !empty($_SERVER['HTTP_X_FORWARDED_FOR'])) 286 ? $_SERVER['HTTP_X_FORWARDED_FOR'] 287 : $_SERVER['REMOTE_ADDR']; 288 if (!isset($ZBX_GUI_ACCESS_IP_RANGE) || !in_array($user_ip, $ZBX_GUI_ACCESS_IP_RANGE)) { 289 throw new Exception($_REQUEST['warning_msg']); 290 } 291 } 292 } 293 294 /** 295 * Load zabbix config file. 296 */ 297 protected function loadConfigFile() { 298 $configFile = $this->getRootDir().CConfigFile::CONFIG_FILE_PATH; 299 $config = new CConfigFile($configFile); 300 $this->config = $config->load(); 301 } 302 303 /** 304 * Check if frontend can connect to DB. 305 * @throws DBException 306 */ 307 protected function initDB() { 308 $error = null; 309 if (!DBconnect($error)) { 310 throw new DBException($error); 311 } 312 } 313 314 /** 315 * Initialize translations. 316 * 317 * @param array $user_data Array of user data. 318 * @param string $user_data['lang'] Language. 319 */ 320 protected function initLocales(array $user_data) { 321 init_mbstrings(); 322 323 $defaultLocales = [ 324 'C', 'POSIX', 'en', 'en_US', 'en_US.UTF-8', 'English_United States.1252', 'en_GB', 'en_GB.UTF-8' 325 ]; 326 327 if (function_exists('bindtextdomain')) { 328 // initializing gettext translations depending on language selected by user 329 $locales = zbx_locale_variants($user_data['lang']); 330 $locale_found = false; 331 foreach ($locales as $locale) { 332 // since LC_MESSAGES may be unavailable on some systems, try to set all of the locales 333 // and then revert some of them back 334 putenv('LC_ALL='.$locale); 335 putenv('LANG='.$locale); 336 putenv('LANGUAGE='.$locale); 337 setlocale(LC_TIME, $locale); 338 339 if (setlocale(LC_ALL, $locale)) { 340 $locale_found = true; 341 break; 342 } 343 } 344 345 // reset the LC_CTYPE locale so that case transformation functions would work correctly 346 // it is also required for PHP to work with the Turkish locale (https://bugs.php.net/bug.php?id=18556) 347 // WARNING: this must be done before executing any other code, otherwise code execution could fail! 348 // this will be unnecessary in PHP 5.5 349 setlocale(LC_CTYPE, $defaultLocales); 350 351 if (!$locale_found && $user_data['lang'] != 'en_GB' && $user_data['lang'] != 'en_gb') { 352 error('Locale for language "'.$user_data['lang'].'" is not found on the web server. Tried to set: '.implode(', ', $locales).'. Unable to translate Zabbix interface.'); 353 } 354 bindtextdomain('frontend', 'locale'); 355 bind_textdomain_codeset('frontend', 'UTF-8'); 356 textdomain('frontend'); 357 } 358 359 // reset the LC_NUMERIC locale so that PHP would always use a point instead of a comma for decimal numbers 360 setlocale(LC_NUMERIC, $defaultLocales); 361 362 // should be after locale initialization 363 require_once $this->getRootDir().'/include/translateDefines.inc.php'; 364 } 365 366 /** 367 * Authenticate user. 368 */ 369 protected function authenticateUser() { 370 $sessionid = CWebUser::checkAuthentication(CWebUser::getSessionCookie()); 371 372 if (!$sessionid) { 373 CWebUser::setDefault(); 374 } 375 376 // set the authentication token for the API 377 API::getWrapper()->auth = $sessionid; 378 379 // enable debug mode in the API 380 API::getWrapper()->debug = CWebUser::getDebugMode(); 381 } 382 383 /** 384 * Process request and generate response. Main entry for all processing. 385 * 386 * @param CRouter $rourer 387 */ 388 private function processRequest(CRouter $router) { 389 $controller = $router->getController(); 390 391 /** @var \CController $controller */ 392 $controller = new $controller(); 393 $controller->setAction($router->getAction()); 394 $response = $controller->run(); 395 396 // Controller returned data 397 if ($response instanceof CControllerResponseData) { 398 // if no view defined we pass data directly to layout 399 if ($router->getView() === null || !$response->isViewEnabled()) { 400 $layout = new CView($router->getLayout(), $response->getData()); 401 echo $layout->getOutput(); 402 } 403 else { 404 $view = new CView($router->getView(), $response->getData()); 405 $data['page']['title'] = $response->getTitle(); 406 $data['page']['file'] = $response->getFileName(); 407 $data['controller']['action'] = $router->getAction(); 408 $data['main_block'] = $view->getOutput(); 409 $data['javascript']['files'] = $view->getAddedJS(); 410 $data['javascript']['pre'] = $view->getIncludedJS(); 411 $data['javascript']['post'] = $view->getPostJS(); 412 $layout = new CView($router->getLayout(), $data); 413 echo $layout->getOutput(); 414 } 415 } 416 // Controller returned redirect to another page 417 else if ($response instanceof CControllerResponseRedirect) { 418 header('Content-Type: text/html; charset=UTF-8'); 419 if ($response->getMessageOk() !== null) { 420 CSession::setValue('messageOk', $response->getMessageOk()); 421 } 422 if ($response->getMessageError() !== null) { 423 CSession::setValue('messageError', $response->getMessageError()); 424 } 425 global $ZBX_MESSAGES; 426 if (isset($ZBX_MESSAGES)) { 427 CSession::setValue('messages', $ZBX_MESSAGES); 428 } 429 if ($response->getFormData() !== null) { 430 CSession::setValue('formData', $response->getFormData()); 431 } 432 433 redirect($response->getLocation()); 434 } 435 // Controller returned fatal error 436 else if ($response instanceof CControllerResponseFatal) { 437 header('Content-Type: text/html; charset=UTF-8'); 438 439 global $ZBX_MESSAGES; 440 $messages = (isset($ZBX_MESSAGES) && $ZBX_MESSAGES) ? filter_messages($ZBX_MESSAGES) : []; 441 foreach ($messages as $message) { 442 $response->addMessage($message['message']); 443 } 444 445 $response->addMessage('Controller: '.$router->getAction()); 446 ksort($_REQUEST); 447 foreach ($_REQUEST as $key => $value) { 448 // do not output SID 449 if ($key != 'sid') { 450 $response->addMessage(is_scalar($value) ? $key.': '.$value : $key.': '.gettype($value)); 451 } 452 } 453 CSession::setValue('messages', $response->getMessages()); 454 455 redirect('zabbix.php?action=system.warning'); 456 } 457 } 458 459 /** 460 * Set layout to fullscreen or kiosk mode if URL contains 'fullscreen' and/or 'kiosk' arguments. 461 */ 462 private function setLayoutModeByUrl() { 463 if (array_key_exists('kiosk', $_GET) && $_GET['kiosk'] === '1') { 464 CView::setLayoutMode(ZBX_LAYOUT_KIOSKMODE); 465 } 466 elseif (array_key_exists('fullscreen', $_GET)) { 467 CView::setLayoutMode($_GET['fullscreen'] === '1' ? ZBX_LAYOUT_FULLSCREEN : ZBX_LAYOUT_NORMAL); 468 } 469 470 // Remove $_GET arguments to prevent CUrl from generating URL with 'fullscreen'/'kiosk' arguments. 471 unset($_GET['fullscreen'], $_GET['kiosk']); 472 } 473} 474