1<?php 2use Symfony\Component\DependencyInjection\ContainerBuilder; 3use Symfony\Component\Config\FileLocator; 4use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; 5use Symfony\Component\DependencyInjection\Dumper\PhpDumper; 6use Symfony\Component\Yaml\Yaml; 7use Tiki\Package\ExtensionManager as PackageExtensionManager; 8use Symfony\Component\Config\Exception\FileLocatorFileNotFoundException; 9 10/** 11 * Tiki initialization functions and classes 12 * 13 * @package TikiWiki 14 * @subpackage lib\init 15 * @copyright (c) Copyright by authors of the Tiki Wiki CMS Groupware Project. All Rights Reserved. See copyright.txt for details and a complete list of authors. 16 * @licence Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details. 17 */ 18// $Id$ 19 20//this script may only be included - so its better to die if called directly. 21if (strpos($_SERVER['SCRIPT_NAME'], basename(__FILE__)) !== false) { 22 header('location: index.php'); 23 exit; 24} 25 26if (! file_exists(__DIR__ . '/../../vendor_bundled/vendor/autoload.php')) { 27 $error = "Your Tiki is not completely installed because Composer has not been run to fetch package dependencies.\n" . 28 "You need to run 'sh setup.sh' from the command line.\n" . 29 "See https://doc.tiki.org/Composer for details.\n"; 30 31 if (http_response_code() === false) { // if running in cli 32 $error = "\033[31m" . $error . "\e[0m\n"; 33 } 34 die($error); 35} 36 37require_once __DIR__ . '/../../vendor_bundled/vendor/autoload.php'; // vendor libs bundled into tiki 38 39// vendor libs managed by the user using composer (if any) 40if (file_exists(__DIR__ . '/../../vendor/autoload.php')) { 41 // In some cases, the vendor folder may contain the files from the old vendor folder before migrating to 42 // vendor_bundled. In these cases eg. when unzipping a Tiki => 17.x on top of an existing Tiki <= 16.x instance, 43 // loading the autoload from the vendor folder will cause issues. 44 // We check for some core libraries (ZendFramework, Smarty and Adodb), if they are all present in the 45 // vendor folder we will consider that there is a old vendor folder, and skip loading the autoload.php unless 46 // there is a file called do_not_clean.txt inside the vendor folder (we will only check the file exists) 47 if (file_exists(__DIR__ . '/../../vendor/do_not_clean.txt') 48 || ! ( // check the existence of critical files denoting a legacy vendor folder 49 (file_exists(__DIR__ . '/../../vendor/zendframework/zend-config/src/Config.php') //ZF2 50 || file_exists(__DIR__ . '/../../vendor/bombayworks/zendframework1/library/Zend/Config.php')) //ZF1 51 && (file_exists(__DIR__ . '/../../vendor/smarty/smarty/libs/Smarty.class.php') //Smarty 52 || file_exists(__DIR__ . '/../../vendor/smarty/smarty/distribution/libs/Smarty.class.php')) //Smarty 53 && file_exists(__DIR__ . '/../../vendor/adodb/adodb/adodb.inc.php') //Adodb 54 )) { 55 $autoloader = require_once __DIR__ . '/../../vendor/autoload.php'; 56 // Autoload extension packages libs 57 foreach (\Tiki\Package\ExtensionManager::getEnabledPackageExtensions(false) as $package) { 58 if (is_dir($package['path'] . '/lib/') && strpos($package['path'], 'vendor_custom') === false) { 59 $autoloader->addPsr4(str_replace('/', '\\', $package['name']) . '\\', $package['path'] . '/lib/'); 60 } 61 } 62 } 63} 64 65// vendor libraries managed by the user, packaged (if any) 66if (is_dir(__DIR__ . '/../../vendor_custom')) { 67 foreach (new DirectoryIterator(__DIR__ . '/../../vendor_custom') as $fileInfo) { 68 if (! $fileInfo->isDir() || $fileInfo->isDot()) { 69 continue; 70 } 71 if (file_exists($fileInfo->getPathname() . '/autoload.php')) { 72 require_once $fileInfo->getPathname() . '/autoload.php'; 73 // Autoload extension packages libs 74 $packagePath = $fileInfo->getPathname(); 75 if (is_dir($packagePath . '/lib/') && $composerJson = json_decode(file_get_contents($packagePath . '/composer.json'), true)) { 76 $packageName = $composerJson['name'] ?? ''; 77 if ($packageName && \Tiki\Package\ExtensionManager::isExtension($packageName, $packagePath) && \Tiki\Package\ExtensionManager::isEnabled($packageName)) { 78 $autoloader->addPsr4(str_replace('/', '\\', $packageName) . '\\', $packagePath . '/lib/'); 79 } 80 } 81 } 82 } 83} 84 85spl_autoload_register('Tiki_Autoload::autoload'); 86 87/** 88 * performs some checks on the underlying system, before initializing Tiki. 89 * @package TikiWiki\lib\init 90 */ 91class TikiInit 92{ 93 /** 94 * dummy constructor 95 */ 96 function __construct() 97 { 98 } 99 100 static function getContainer() 101 { 102 /** @var ContainerBuilder $container */ 103 static $container; 104 105 if ($container) { 106 return $container; 107 } 108 109 require_once 'lib/setup/twversion.class.php'; 110 $TWV = new TWVersion(); 111 $version = $TWV->getVersion(); 112 113 $cache = TIKI_PATH . '/temp/cache/container.php'; 114 if (is_readable($cache)) { 115 require_once $cache; 116 117 if (! class_exists('TikiCachedContainer')) { 118 // mangled or otherwise invalid container 119 unlink($cache); 120 } else { 121 $container = new TikiCachedContainer; 122 123 /* If the server moved or was upgraded, the container must be recreated */ 124 if (TIKI_PATH == $container->getParameter('kernel.root_dir') && 125 $container->hasParameter('tiki.version') && // no version before 15.0 126 $container->getParameter('tiki.version') === $version) { 127 if (TikiDb::get()) { 128 $container->set('tiki.lib.db', TikiDb::get()); 129 } 130 return $container; 131 } else { 132 /* This server moved or was upgraded, container must be recreated */ 133 unlink($cache); 134 } 135 } 136 } 137 138 $path = TIKI_PATH . '/db/config'; 139 $container = new ContainerBuilder; 140 $container->addCompilerPass(new \Tiki\MailIn\Provider\CompilerPass); 141 $container->addCompilerPass(new \Tiki\Recommendation\Engine\CompilerPass); 142 $container->addCompilerPass(new \Tiki\Wiki\SlugManager\CompilerPass); 143 $container->addCompilerPass(new \Search\Federated\CompilerPass); 144 $container->addCompilerPass(new \Tracker\CompilerPass); 145 146 $container->setParameter('kernel.root_dir', TIKI_PATH); 147 $container->setParameter('tiki.version', $version); 148 149 $loader = new XmlFileLoader($container, new FileLocator($path)); 150 151 $loader->load('tiki.xml'); 152 $loader->load('controllers.xml'); 153 $loader->load('mailin.xml'); 154 155 try { 156 $loader->load('custom.xml'); 157 } catch (InvalidArgumentException $e) { 158 // Do nothing, absence of custom.xml file is expected 159 } 160 161 $extensionPackagesDefinition = PackageExtensionManager::getEnabledPackageExtensions(false); 162 $container->setParameter('tiki.packages.extensions', $extensionPackagesDefinition); 163 foreach ($extensionPackagesDefinition as $packageDefinition) { 164 try { 165 $path = sprintf('%s/%s/config/services.xml', TIKI_PATH, $packageDefinition['path']); 166 $loader->load($path); 167 } catch (InvalidArgumentException $e) { 168 // Do nothing, absence of services.xml file is expected 169 } 170 } 171 172 if (TikiDb::get()) { 173 $container->set('tiki.lib.db', TikiDb::get()); 174 } 175 176 $container->compile(); 177 178 $dumper = new PhpDumper($container); 179 file_put_contents($cache, $dumper->dump([ 180 'class' => 'TikiCachedContainer', 181 ])); 182 183 return $container; 184 } 185 186/** Return 'windows' if windows, otherwise 'unix' 187 * \static 188 */ 189 function os() 190 { 191 static $os; 192 if (! isset($os)) { 193 if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') { 194 $os = 'windows'; 195 } else { 196 $os = 'unix'; 197 } 198 } 199 return $os; 200 } 201 202 203/** Return true if windows, otherwise false 204 * @static 205 */ 206 static function isWindows() 207 { 208 static $windows; 209 if (! isset($windows)) { 210 $windows = strtoupper(substr(PHP_OS, 0, 3)) == 'WIN'; 211 } 212 return $windows; 213 } 214 215 /** 216 * Copes with Windows permissions 217 * 218 * @param string $path directory to test 219 * 220 * @return bool 221 */ 222 static function is_writeable($path) 223 { 224 if (self::isWindows()) { 225 return self::is__writable($path); 226 } else { 227 return is_writeable($path); 228 } 229 } 230 231 /** 232 * From the php is_writable manual (thanks legolas558 d0t users dot sf dot net) 233 * Note the two underscores and no "e". 234 * 235 * will work in despite of Windows ACLs bug 236 * NOTE: use a trailing slash for folders!!! 237 * {@see http://bugs.php.net/bug.php?id=27609} 238 * {@see http://bugs.php.net/bug.php?id=30931} 239 * 240 * @param string $path directory to test NOTE: use a trailing slash for folders!!! 241 * @return bool 242 */ 243 static function is__writable($path) 244 { 245 if ($path{strlen($path) - 1} == '/') { // recursively return a temporary file path 246 return self::is__writable($path . uniqid(mt_rand()) . '.tmp'); 247 } elseif (is_dir($path)) { 248 return self::is__writable($path . '/' . uniqid(mt_rand()) . '.tmp'); 249 } 250 // check tmp file for read/write capabilities 251 $rm = file_exists($path); 252 $f = @fopen($path, 'a'); 253 if ($f === false) { 254 return false; 255 } 256 fclose($f); 257 if (! $rm) { 258 unlink($path); 259 } 260 return true; 261 } 262 263 264 /** Prepend $path to the include path 265 * @static 266 * @param string $path the path to prepend 267 * @return string 268 */ 269 static function prependIncludePath($path) 270 { 271 $include_path = ini_get('include_path'); 272 $paths = explode(PATH_SEPARATOR, $include_path); 273 274 if ($include_path && ! in_array($path, $paths)) { 275 $include_path = $path . PATH_SEPARATOR . $include_path; 276 } elseif (! $include_path) { 277 $include_path = $path; 278 } 279 280 return set_include_path($include_path); 281 } 282 283 284 /** Append $path to the include path 285 * @static 286 * @param mixed $path 287 */ 288 static function appendIncludePath($path) 289 { 290 $include_path = ini_get('include_path'); 291 $paths = explode(PATH_SEPARATOR, $include_path); 292 293 if ($include_path && ! in_array($path, $paths)) { 294 $include_path .= PATH_SEPARATOR . $path; 295 } elseif (! $include_path) { 296 $include_path = $path; 297 } 298 299 return set_include_path($include_path); 300 } 301 302 303 /** Return system defined temporary directory. 304 * In Unix, this is usually /tmp 305 * In Windows, this is usually c:\windows\temp or c:\winnt\temp 306 * @static 307 * @deprecated by sys_get_temp_dir() 308 */ 309 static function tempdir() 310 { 311 return sys_get_temp_dir(); 312 } 313 314 /** 315 * Convert a string to UTF-8. Fixes a bug in PHP decode 316 * From http://w3.org/International/questions/qa-forms-utf-8.html 317 * @static 318 * @param string String to be converted 319 * @return UTF-8 representation of the string 320 */ 321 static function to_utf8($string) 322 { 323 if (preg_match( 324 '%^(?: 325 [\x09\x0A\x0D\x20-\x7E] # ASCII 326 | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte 327 | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs 328 | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte 329 | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates 330 | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 331 | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 332 | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 333 )*$%xs', 334 $string 335 ) 336 ) { 337 return $string; 338 } else { 339 return iconv('CP1252', 'UTF-8', $string); 340 } 341 } 342 343 /** 344 * Determine if the web server is an IIS server 345 * @return true if IIS server, else false 346 * @static 347 */ 348 static function isIIS() 349 { 350 static $IIS; 351 352 // Sample value Microsoft-IIS/7.5 353 if (! isset($IIS) && isset($_SERVER['SERVER_SOFTWARE'])) { 354 $IIS = substr($_SERVER['SERVER_SOFTWARE'], 0, 13) == 'Microsoft-IIS'; 355 } 356 357 return $IIS; 358 } 359 360 /** 361 * Determine if the web server is an IIS server 362 * @return true if IIS server, else false 363 * \static 364 */ 365 static function hasIIS_UrlRewriteModule() 366 { 367 return isset($_SERVER['IIS_UrlRewriteModule']) == true; 368 } 369 370 static function getCredentialsFile() 371 { 372 global $default_api_tiki, $api_tiki, $db_tiki, $dbversion_tiki, $host_tiki, $user_tiki, $pass_tiki, $dbs_tiki, $tikidomain, $tikidomainslash, $dbfail_url; 373 // Please use the local.php file instead containing these variables 374 // If you set sessions to store in the database, you will need a local.php file 375 // Otherwise you will be ok. 376 //$api_tiki = 'pear'; 377 //$api_tiki = 'pdo'; 378 $api_tiki = 'pdo'; 379 $db_tiki = 'mysql'; 380 $dbversion_tiki = '2.0'; 381 $host_tiki = 'localhost'; 382 $user_tiki = 'root'; 383 $pass_tiki = ''; 384 $dbs_tiki = 'tiki'; 385 $tikidomain = ''; 386 $dbfail_url = ''; 387 388 /* 389 SVN Developers: Do not change any of the above. 390 Instead, create a file, called db/local.php, containing any of 391 the variables listed above that are different for your 392 development environment. This will protect you from 393 accidentally committing your username/password to SVN! 394 395 example of db/local.php 396 <?php 397 $host_tiki = 'myhost'; 398 $user_tiki = 'myuser'; 399 $pass_tiki = 'mypass'; 400 $dbs_tiki = 'mytiki'; 401 $api_tiki = 'adodb'; 402 403 ** Multi-tiki 404 ************************************** 405 see http://tikiwiki.org/MultiTiki19 406 407 Setup of virtual tikis is done using setup.sh script 408 ----------------------------------------------------------- 409 -> Multi-tiki trick for virtualhosting 410 411 $tikidomain variable is set to : 412 or TIKI_VIRTUAL 413 That is set in apache virtual conf : SetEnv TIKI_VIRTUAL myvirtual 414 or SERVER_NAME 415 From apache directive ServerName set for that virtualhost block 416 or HTTP_HOST 417 From the real domain name called in the browser 418 (can be ServerAlias from apache conf) 419 420 */ 421 422 if (! isset($local_php) or ! is_file($local_php)) { 423 $local_php = 'db/local.php'; 424 } else { 425 $local_php = preg_replace(['/\.\./', '/^db\//'], ['',''], $local_php); 426 } 427 $tikidomain = ''; 428 if (is_file('db/virtuals.inc')) { 429 if (isset($_SERVER['TIKI_VIRTUAL']) and is_file('db/' . $_SERVER['TIKI_VIRTUAL'] . '/local.php')) { 430 $tikidomain = $_SERVER['TIKI_VIRTUAL']; 431 } elseif (isset($_SERVER['SERVER_NAME']) and is_file('db/' . $_SERVER['SERVER_NAME'] . '/local.php')) { 432 $tikidomain = $_SERVER['SERVER_NAME']; 433 } elseif (isset($_REQUEST['multi']) && is_file('db/' . $_REQUEST['multi'] . '/local.php')) { 434 $tikidomain = $_REQUEST['multi']; 435 } elseif (isset($_SERVER['HTTP_HOST'])) { 436 if (is_file('db/' . $_SERVER['HTTP_HOST'] . '/local.php')) { 437 $tikidomain = $_SERVER['HTTP_HOST']; 438 } elseif (is_file('db/' . preg_replace('/^www\./', '', $_SERVER['HTTP_HOST']) . '/local.php')) { 439 $tikidomain = preg_replace('/^www\./', '', $_SERVER['HTTP_HOST']); 440 } 441 } 442 if (! empty($tikidomain)) { 443 $local_php = "db/$tikidomain/local.php"; 444 } 445 } 446 $tikidomainslash = (! empty($tikidomain) ? $tikidomain . '/' : ''); 447 448 $default_api_tiki = $api_tiki; 449 $api_tiki = ''; 450 451 return $local_php; 452 } 453 454 static function getEnvironmentCredentials() 455 { 456 // Load connection strings from environment variables, as used by Azure and possibly other hosts 457 $connectionString = null; 458 foreach (['MYSQLCONNSTR_Tiki', 'MYSQLCONNSTR_DefaultConnection'] as $envVar) { 459 if (isset($_SERVER[$envVar])) { 460 $connectionString = $_SERVER[$envVar]; 461 continue; 462 } 463 } 464 465 if ($connectionString && preg_match('/^Database=(?P<dbs>.+);Data Source=(?P<host>.+);User Id=(?P<user>.+);Password=(?P<pass>.+)$/', $connectionString, $parts)) { 466 $parts['charset'] = 'utf8'; 467 $parts['socket'] = null; 468 return $parts; 469 } 470 return null; 471 } 472} 473 474/** 475 * set how Tiki will report Errors 476 * @param $errno 477 * @param $errstr 478 * @param $errfile 479 * @param $errline 480 */ 481function tiki_error_handling($errno, $errstr, $errfile, $errline) 482{ 483 global $prefs, $phpErrors; 484 485 if (0 === error_reporting()) { 486 // This error was triggered when evaluating an expression prepended by the at sign (@) error control operator, but since we are in a custom error handler, we have to ignore it manually. 487 // See http://ca3.php.net/manual/en/language.operators.errorcontrol.php#98895 and http://php.net/set_error_handler 488 return; 489 } 490 491 // FIXME: Optionally return false so errors are still logged 492 $err[E_ERROR] = 'E_ERROR'; 493 $err[E_CORE_ERROR] = 'E_CORE_ERROR'; 494 $err[E_USER_ERROR] = 'E_USER_ERROR'; 495 $err[E_COMPILE_ERROR] = 'E_COMPILE_ERROR'; 496 $err[E_WARNING] = 'E_WARNING'; 497 $err[E_CORE_WARNING] = 'E_CORE_WARNING'; 498 $err[E_USER_WARNING] = 'E_USER_WARNING'; 499 $err[E_COMPILE_WARNING] = 'E_COMPILE_WARNING'; 500 $err[E_PARSE] = 'E_PARSE'; 501 $err[E_STRICT] = 'E_STRICT'; 502 $err[E_NOTICE] = 'E_NOTICE'; 503 $err[E_USER_NOTICE] = 'E_USER_NOTICE'; 504 $err[E_DEPRECATED] = 'E_DEPRECATED'; 505 $err[E_USER_DEPRECATED] = 'E_USER_DEPRECATED'; 506 507 global $tikipath; 508 $errfile = str_replace($tikipath, '', $errfile); 509 switch ($errno) { 510 case E_ERROR: 511 case E_CORE_ERROR: 512 case E_USER_ERROR: 513 case E_COMPILE_ERROR: 514 case E_WARNING: 515 case E_CORE_WARNING: 516 case E_USER_WARNING: 517 case E_COMPILE_WARNING: 518 case E_PARSE: 519 case E_RECOVERABLE_ERROR: 520 $type = 'ERROR'; 521 break; 522 case E_STRICT: 523 case E_NOTICE: 524 case E_USER_NOTICE: 525 case E_DEPRECATED: 526 case E_USER_DEPRECATED: 527 if (! defined('THIRD_PARTY_LIBS_PATTERN') || ! preg_match(THIRD_PARTY_LIBS_PATTERN, $errfile)) { 528 if (! empty($prefs['smarty_notice_reporting']) && $prefs['smarty_notice_reporting'] != 'y' && strstr($errfile, '.tpl.php')) { 529 return; 530 } 531 } 532 $type = 'NOTICE'; 533 break; 534 default: 535 return; 536 } 537 538 $back = "<div class='rbox-data p-3 mb-3' style='font-size: 12px; border: 1px solid'>"; 539 $back .= $type . " ($err[$errno]): <b>" . $errstr . "</b><br />"; 540 $back .= "At line $errline in $errfile"; // $errfile comes after $errline to ease selection for copy-pasting. 541 $back .= "</div>"; 542 543 $phpErrors[] = $back; 544} 545 546// Patch missing $_SERVER['REQUEST_URI'] on IIS6 547if (empty($_SERVER['REQUEST_URI'])) { 548 if (TikiInit::isIIS()) { 549 $_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME']; 550 } 551} 552