init.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. <?php
  2. /**
  3. * Initialize some defaults needed for DokuWiki
  4. */
  5. use dokuwiki\Extension\Event;
  6. use dokuwiki\Extension\EventHandler;
  7. /**
  8. * timing Dokuwiki execution
  9. *
  10. * @param integer $start
  11. *
  12. * @return mixed
  13. */
  14. function delta_time($start=0) {
  15. return microtime(true)-((float)$start);
  16. }
  17. define('DOKU_START_TIME', delta_time());
  18. global $config_cascade;
  19. $config_cascade = array();
  20. // if available load a preload config file
  21. $preload = fullpath(dirname(__FILE__)).'/preload.php';
  22. if (file_exists($preload)) include($preload);
  23. // define the include path
  24. if(!defined('DOKU_INC')) define('DOKU_INC',fullpath(dirname(__FILE__).'/../').'/');
  25. // define Plugin dir
  26. if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
  27. // define config path (packagers may want to change this to /etc/dokuwiki/)
  28. if(!defined('DOKU_CONF')) define('DOKU_CONF',DOKU_INC.'conf/');
  29. // check for error reporting override or set error reporting to sane values
  30. if (!defined('DOKU_E_LEVEL') && file_exists(DOKU_CONF.'report_e_all')) {
  31. define('DOKU_E_LEVEL', E_ALL);
  32. }
  33. if (!defined('DOKU_E_LEVEL')) {
  34. error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT);
  35. } else {
  36. error_reporting(DOKU_E_LEVEL);
  37. }
  38. // avoid caching issues #1594
  39. header('Vary: Cookie');
  40. // init memory caches
  41. global $cache_revinfo;
  42. $cache_revinfo = array();
  43. global $cache_wikifn;
  44. $cache_wikifn = array();
  45. global $cache_cleanid;
  46. $cache_cleanid = array();
  47. global $cache_authname;
  48. $cache_authname = array();
  49. global $cache_metadata;
  50. $cache_metadata = array();
  51. // always include 'inc/config_cascade.php'
  52. // previously in preload.php set fields of $config_cascade will be merged with the defaults
  53. include(DOKU_INC.'inc/config_cascade.php');
  54. //prepare config array()
  55. global $conf;
  56. $conf = array();
  57. // load the global config file(s)
  58. foreach (array('default','local','protected') as $config_group) {
  59. if (empty($config_cascade['main'][$config_group])) continue;
  60. foreach ($config_cascade['main'][$config_group] as $config_file) {
  61. if (file_exists($config_file)) {
  62. include($config_file);
  63. }
  64. }
  65. }
  66. //prepare license array()
  67. global $license;
  68. $license = array();
  69. // load the license file(s)
  70. foreach (array('default','local') as $config_group) {
  71. if (empty($config_cascade['license'][$config_group])) continue;
  72. foreach ($config_cascade['license'][$config_group] as $config_file) {
  73. if(file_exists($config_file)){
  74. include($config_file);
  75. }
  76. }
  77. }
  78. // set timezone (as in pre 5.3.0 days)
  79. date_default_timezone_set(@date_default_timezone_get());
  80. // define baseURL
  81. if(!defined('DOKU_REL')) define('DOKU_REL',getBaseURL(false));
  82. if(!defined('DOKU_URL')) define('DOKU_URL',getBaseURL(true));
  83. if(!defined('DOKU_BASE')){
  84. if($conf['canonical']){
  85. define('DOKU_BASE',DOKU_URL);
  86. }else{
  87. define('DOKU_BASE',DOKU_REL);
  88. }
  89. }
  90. // define whitespace
  91. if(!defined('NL')) define ('NL',"\n");
  92. if(!defined('DOKU_LF')) define ('DOKU_LF',"\n");
  93. if(!defined('DOKU_TAB')) define ('DOKU_TAB',"\t");
  94. // define cookie and session id, append server port when securecookie is configured FS#1664
  95. if (!defined('DOKU_COOKIE')) {
  96. $serverPort = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : '';
  97. define('DOKU_COOKIE', 'DW' . md5(DOKU_REL . (($conf['securecookie']) ? $serverPort : '')));
  98. unset($serverPort);
  99. }
  100. // define main script
  101. if(!defined('DOKU_SCRIPT')) define('DOKU_SCRIPT','doku.php');
  102. if(!defined('DOKU_TPL')) {
  103. /**
  104. * @deprecated 2012-10-13 replaced by more dynamic method
  105. * @see tpl_basedir()
  106. */
  107. define('DOKU_TPL', DOKU_BASE.'lib/tpl/'.$conf['template'].'/');
  108. }
  109. if(!defined('DOKU_TPLINC')) {
  110. /**
  111. * @deprecated 2012-10-13 replaced by more dynamic method
  112. * @see tpl_incdir()
  113. */
  114. define('DOKU_TPLINC', DOKU_INC.'lib/tpl/'.$conf['template'].'/');
  115. }
  116. // make session rewrites XHTML compliant
  117. @ini_set('arg_separator.output', '&amp;');
  118. // make sure global zlib does not interfere FS#1132
  119. @ini_set('zlib.output_compression', 'off');
  120. // increase PCRE backtrack limit
  121. @ini_set('pcre.backtrack_limit', '20971520');
  122. // enable gzip compression if supported
  123. $httpAcceptEncoding = isset($_SERVER['HTTP_ACCEPT_ENCODING']) ? $_SERVER['HTTP_ACCEPT_ENCODING'] : '';
  124. $conf['gzip_output'] &= (strpos($httpAcceptEncoding, 'gzip') !== false);
  125. global $ACT;
  126. if ($conf['gzip_output'] &&
  127. !defined('DOKU_DISABLE_GZIP_OUTPUT') &&
  128. function_exists('ob_gzhandler') &&
  129. // Disable compression when a (compressed) sitemap might be delivered
  130. // See https://bugs.dokuwiki.org/index.php?do=details&task_id=2576
  131. $ACT != 'sitemap') {
  132. ob_start('ob_gzhandler');
  133. }
  134. // init session
  135. if(!headers_sent() && !defined('NOSESSION')) {
  136. if(!defined('DOKU_SESSION_NAME')) define ('DOKU_SESSION_NAME', "DokuWiki");
  137. if(!defined('DOKU_SESSION_LIFETIME')) define ('DOKU_SESSION_LIFETIME', 0);
  138. if(!defined('DOKU_SESSION_PATH')) {
  139. $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir'];
  140. define ('DOKU_SESSION_PATH', $cookieDir);
  141. }
  142. if(!defined('DOKU_SESSION_DOMAIN')) define ('DOKU_SESSION_DOMAIN', '');
  143. // start the session
  144. init_session();
  145. // load left over messages
  146. if(isset($_SESSION[DOKU_COOKIE]['msg'])) {
  147. $MSG = $_SESSION[DOKU_COOKIE]['msg'];
  148. unset($_SESSION[DOKU_COOKIE]['msg']);
  149. }
  150. }
  151. // don't let cookies ever interfere with request vars
  152. $_REQUEST = array_merge($_GET,$_POST);
  153. // we don't want a purge URL to be digged
  154. if(isset($_REQUEST['purge']) && !empty($_SERVER['HTTP_REFERER'])) unset($_REQUEST['purge']);
  155. // precalculate file creation modes
  156. init_creationmodes();
  157. // make real paths and check them
  158. init_paths();
  159. init_files();
  160. // setup plugin controller class (can be overwritten in preload.php)
  161. global $plugin_controller_class, $plugin_controller;
  162. if (empty($plugin_controller_class)) $plugin_controller_class = dokuwiki\Extension\PluginController::class;
  163. // load libraries
  164. require_once(DOKU_INC.'vendor/autoload.php');
  165. require_once(DOKU_INC.'inc/load.php');
  166. // from now on everything is an exception
  167. \dokuwiki\ErrorHandler::register();
  168. // disable gzip if not available
  169. define('DOKU_HAS_BZIP', function_exists('bzopen'));
  170. define('DOKU_HAS_GZIP', function_exists('gzopen'));
  171. if($conf['compression'] == 'bz2' && !DOKU_HAS_BZIP) {
  172. $conf['compression'] = 'gz';
  173. }
  174. if($conf['compression'] == 'gz' && !DOKU_HAS_GZIP) {
  175. $conf['compression'] = 0;
  176. }
  177. // input handle class
  178. global $INPUT;
  179. $INPUT = new \dokuwiki\Input\Input();
  180. // initialize plugin controller
  181. $plugin_controller = new $plugin_controller_class();
  182. // initialize the event handler
  183. global $EVENT_HANDLER;
  184. $EVENT_HANDLER = new EventHandler();
  185. $local = $conf['lang'];
  186. Event::createAndTrigger('INIT_LANG_LOAD', $local, 'init_lang', true);
  187. // setup authentication system
  188. if (!defined('NOSESSION')) {
  189. auth_setup();
  190. }
  191. // setup mail system
  192. mail_setup();
  193. $nil = null;
  194. Event::createAndTrigger('DOKUWIKI_INIT_DONE', $nil, null, false);
  195. /**
  196. * Initializes the session
  197. *
  198. * Makes sure the passed session cookie is valid, invalid ones are ignored an a new session ID is issued
  199. *
  200. * @link http://stackoverflow.com/a/33024310/172068
  201. * @link http://php.net/manual/en/session.configuration.php#ini.session.sid-length
  202. */
  203. function init_session() {
  204. global $conf;
  205. session_name(DOKU_SESSION_NAME);
  206. session_set_cookie_params(
  207. DOKU_SESSION_LIFETIME,
  208. DOKU_SESSION_PATH,
  209. DOKU_SESSION_DOMAIN,
  210. ($conf['securecookie'] && is_ssl()),
  211. true
  212. );
  213. // make sure the session cookie contains a valid session ID
  214. if(isset($_COOKIE[DOKU_SESSION_NAME]) && !preg_match('/^[-,a-zA-Z0-9]{22,256}$/', $_COOKIE[DOKU_SESSION_NAME])) {
  215. unset($_COOKIE[DOKU_SESSION_NAME]);
  216. }
  217. session_start();
  218. }
  219. /**
  220. * Checks paths from config file
  221. */
  222. function init_paths(){
  223. global $conf;
  224. $paths = [
  225. 'datadir' => 'pages',
  226. 'olddir' => 'attic',
  227. 'mediadir' => 'media',
  228. 'mediaolddir' => 'media_attic',
  229. 'metadir' => 'meta',
  230. 'mediametadir' => 'media_meta',
  231. 'cachedir' => 'cache',
  232. 'indexdir' => 'index',
  233. 'lockdir' => 'locks',
  234. 'tmpdir' => 'tmp',
  235. 'logdir' => 'log',
  236. ];
  237. foreach($paths as $c => $p) {
  238. $path = empty($conf[$c]) ? $conf['savedir'].'/'.$p : $conf[$c];
  239. $conf[$c] = init_path($path);
  240. if(empty($conf[$c])) {
  241. $path = fullpath($path);
  242. nice_die("The $c ('$p') at $path is not found, isn't accessible or writable.
  243. You should check your config and permission settings.
  244. Or maybe you want to <a href=\"install.php\">run the
  245. installer</a>?");
  246. }
  247. }
  248. // path to old changelog only needed for upgrading
  249. $conf['changelog_old'] = init_path(
  250. (isset($conf['changelog'])) ? ($conf['changelog']) : ($conf['savedir'] . '/changes.log')
  251. );
  252. if ($conf['changelog_old']=='') { unset($conf['changelog_old']); }
  253. // hardcoded changelog because it is now a cache that lives in meta
  254. $conf['changelog'] = $conf['metadir'].'/_dokuwiki.changes';
  255. $conf['media_changelog'] = $conf['metadir'].'/_media.changes';
  256. }
  257. /**
  258. * Load the language strings
  259. *
  260. * @param string $langCode language code, as passed by event handler
  261. */
  262. function init_lang($langCode) {
  263. //prepare language array
  264. global $lang, $config_cascade;
  265. $lang = array();
  266. //load the language files
  267. require(DOKU_INC.'inc/lang/en/lang.php');
  268. foreach ($config_cascade['lang']['core'] as $config_file) {
  269. if (file_exists($config_file . 'en/lang.php')) {
  270. include($config_file . 'en/lang.php');
  271. }
  272. }
  273. if ($langCode && $langCode != 'en') {
  274. if (file_exists(DOKU_INC."inc/lang/$langCode/lang.php")) {
  275. require(DOKU_INC."inc/lang/$langCode/lang.php");
  276. }
  277. foreach ($config_cascade['lang']['core'] as $config_file) {
  278. if (file_exists($config_file . "$langCode/lang.php")) {
  279. include($config_file . "$langCode/lang.php");
  280. }
  281. }
  282. }
  283. }
  284. /**
  285. * Checks the existence of certain files and creates them if missing.
  286. */
  287. function init_files(){
  288. global $conf;
  289. $files = array($conf['indexdir'].'/page.idx');
  290. foreach($files as $file){
  291. if(!file_exists($file)){
  292. $fh = @fopen($file,'a');
  293. if($fh){
  294. fclose($fh);
  295. if($conf['fperm']) chmod($file, $conf['fperm']);
  296. }else{
  297. nice_die("$file is not writable. Check your permissions settings!");
  298. }
  299. }
  300. }
  301. }
  302. /**
  303. * Returns absolute path
  304. *
  305. * This tries the given path first, then checks in DOKU_INC.
  306. * Check for accessibility on directories as well.
  307. *
  308. * @author Andreas Gohr <andi@splitbrain.org>
  309. *
  310. * @param string $path
  311. *
  312. * @return bool|string
  313. */
  314. function init_path($path){
  315. // check existence
  316. $p = fullpath($path);
  317. if(!file_exists($p)){
  318. $p = fullpath(DOKU_INC.$path);
  319. if(!file_exists($p)){
  320. return '';
  321. }
  322. }
  323. // check writability
  324. if(!@is_writable($p)){
  325. return '';
  326. }
  327. // check accessability (execute bit) for directories
  328. if(@is_dir($p) && !file_exists("$p/.")){
  329. return '';
  330. }
  331. return $p;
  332. }
  333. /**
  334. * Sets the internal config values fperm and dperm which, when set,
  335. * will be used to change the permission of a newly created dir or
  336. * file with chmod. Considers the influence of the system's umask
  337. * setting the values only if needed.
  338. */
  339. function init_creationmodes(){
  340. global $conf;
  341. // Legacy support for old umask/dmask scheme
  342. unset($conf['dmask']);
  343. unset($conf['fmask']);
  344. unset($conf['umask']);
  345. $conf['fperm'] = false;
  346. $conf['dperm'] = false;
  347. // get system umask, fallback to 0 if none available
  348. $umask = @umask();
  349. if(!$umask) $umask = 0000;
  350. // check what is set automatically by the system on file creation
  351. // and set the fperm param if it's not what we want
  352. $auto_fmode = 0666 & ~$umask;
  353. if($auto_fmode != $conf['fmode']) $conf['fperm'] = $conf['fmode'];
  354. // check what is set automatically by the system on directory creation
  355. // and set the dperm param if it's not what we want.
  356. $auto_dmode = 0777 & ~$umask;
  357. if($auto_dmode != $conf['dmode']) $conf['dperm'] = $conf['dmode'];
  358. }
  359. /**
  360. * Returns the full absolute URL to the directory where
  361. * DokuWiki is installed in (includes a trailing slash)
  362. *
  363. * !! Can not access $_SERVER values through $INPUT
  364. * !! here as this function is called before $INPUT is
  365. * !! initialized.
  366. *
  367. * @author Andreas Gohr <andi@splitbrain.org>
  368. *
  369. * @param null|string $abs
  370. *
  371. * @return string
  372. */
  373. function getBaseURL($abs=null){
  374. global $conf;
  375. //if canonical url enabled always return absolute
  376. if(is_null($abs)) $abs = $conf['canonical'];
  377. if(!empty($conf['basedir'])){
  378. $dir = $conf['basedir'];
  379. }elseif(substr($_SERVER['SCRIPT_NAME'],-4) == '.php'){
  380. $dir = dirname($_SERVER['SCRIPT_NAME']);
  381. }elseif(substr($_SERVER['PHP_SELF'],-4) == '.php'){
  382. $dir = dirname($_SERVER['PHP_SELF']);
  383. }elseif($_SERVER['DOCUMENT_ROOT'] && $_SERVER['SCRIPT_FILENAME']){
  384. $dir = preg_replace ('/^'.preg_quote($_SERVER['DOCUMENT_ROOT'],'/').'/','',
  385. $_SERVER['SCRIPT_FILENAME']);
  386. $dir = dirname('/'.$dir);
  387. }else{
  388. $dir = '.'; //probably wrong
  389. }
  390. $dir = str_replace('\\','/',$dir); // bugfix for weird WIN behaviour
  391. $dir = preg_replace('#//+#','/',"/$dir/"); // ensure leading and trailing slashes
  392. //handle script in lib/exe dir
  393. $dir = preg_replace('!lib/exe/$!','',$dir);
  394. //handle script in lib/plugins dir
  395. $dir = preg_replace('!lib/plugins/.*$!','',$dir);
  396. //finish here for relative URLs
  397. if(!$abs) return $dir;
  398. //use config if available, trim any slash from end of baseurl to avoid multiple consecutive slashes in the path
  399. if(!empty($conf['baseurl'])) return rtrim($conf['baseurl'],'/').$dir;
  400. //split hostheader into host and port
  401. if(isset($_SERVER['HTTP_HOST'])){
  402. $parsed_host = parse_url('http://'.$_SERVER['HTTP_HOST']);
  403. $host = isset($parsed_host['host']) ? $parsed_host['host'] : null;
  404. $port = isset($parsed_host['port']) ? $parsed_host['port'] : null;
  405. }elseif(isset($_SERVER['SERVER_NAME'])){
  406. $parsed_host = parse_url('http://'.$_SERVER['SERVER_NAME']);
  407. $host = isset($parsed_host['host']) ? $parsed_host['host'] : null;
  408. $port = isset($parsed_host['port']) ? $parsed_host['port'] : null;
  409. }else{
  410. $host = php_uname('n');
  411. $port = '';
  412. }
  413. if(is_null($port)){
  414. $port = '';
  415. }
  416. if(!is_ssl()){
  417. $proto = 'http://';
  418. if ($port == '80') {
  419. $port = '';
  420. }
  421. }else{
  422. $proto = 'https://';
  423. if ($port == '443') {
  424. $port = '';
  425. }
  426. }
  427. if($port !== '') $port = ':'.$port;
  428. return $proto.$host.$port.$dir;
  429. }
  430. /**
  431. * Check if accessed via HTTPS
  432. *
  433. * Apache leaves ,$_SERVER['HTTPS'] empty when not available, IIS sets it to 'off'.
  434. * 'false' and 'disabled' are just guessing
  435. *
  436. * @returns bool true when SSL is active
  437. */
  438. function is_ssl() {
  439. // check if we are behind a reverse proxy
  440. if(isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
  441. if($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
  442. return true;
  443. } else {
  444. return false;
  445. }
  446. }
  447. if(!isset($_SERVER['HTTPS']) ||
  448. preg_match('/^(|off|false|disabled)$/i', $_SERVER['HTTPS'])) {
  449. return false;
  450. } else {
  451. return true;
  452. }
  453. }
  454. /**
  455. * checks it is windows OS
  456. * @return bool
  457. */
  458. function isWindows() {
  459. return (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') ? true : false;
  460. }
  461. /**
  462. * print a nice message even if no styles are loaded yet.
  463. *
  464. * @param integer|string $msg
  465. */
  466. function nice_die($msg){
  467. echo<<<EOT
  468. <!DOCTYPE html>
  469. <html>
  470. <head><title>DokuWiki Setup Error</title></head>
  471. <body style="font-family: Arial, sans-serif">
  472. <div style="width:60%; margin: auto; background-color: #fcc;
  473. border: 1px solid #faa; padding: 0.5em 1em;">
  474. <h1 style="font-size: 120%">DokuWiki Setup Error</h1>
  475. <p>$msg</p>
  476. </div>
  477. </body>
  478. </html>
  479. EOT;
  480. if(defined('DOKU_UNITTEST')) {
  481. throw new RuntimeException('nice_die: '.$msg);
  482. }
  483. exit(1);
  484. }
  485. /**
  486. * A realpath() replacement
  487. *
  488. * This function behaves similar to PHP's realpath() but does not resolve
  489. * symlinks or accesses upper directories
  490. *
  491. * @author Andreas Gohr <andi@splitbrain.org>
  492. * @author <richpageau at yahoo dot co dot uk>
  493. * @link http://php.net/manual/en/function.realpath.php#75992
  494. *
  495. * @param string $path
  496. * @param bool $exists
  497. *
  498. * @return bool|string
  499. */
  500. function fullpath($path,$exists=false){
  501. static $run = 0;
  502. $root = '';
  503. $iswin = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' || !empty($GLOBALS['DOKU_UNITTEST_ASSUME_WINDOWS']));
  504. // find the (indestructable) root of the path - keeps windows stuff intact
  505. if($path[0] == '/'){
  506. $root = '/';
  507. }elseif($iswin){
  508. // match drive letter and UNC paths
  509. if(preg_match('!^([a-zA-z]:)(.*)!',$path,$match)){
  510. $root = $match[1].'/';
  511. $path = $match[2];
  512. }else if(preg_match('!^(\\\\\\\\[^\\\\/]+\\\\[^\\\\/]+[\\\\/])(.*)!',$path,$match)){
  513. $root = $match[1];
  514. $path = $match[2];
  515. }
  516. }
  517. $path = str_replace('\\','/',$path);
  518. // if the given path wasn't absolute already, prepend the script path and retry
  519. if(!$root){
  520. $base = dirname($_SERVER['SCRIPT_FILENAME']);
  521. $path = $base.'/'.$path;
  522. if($run == 0){ // avoid endless recursion when base isn't absolute for some reason
  523. $run++;
  524. return fullpath($path,$exists);
  525. }
  526. }
  527. $run = 0;
  528. // canonicalize
  529. $path=explode('/', $path);
  530. $newpath=array();
  531. foreach($path as $p) {
  532. if ($p === '' || $p === '.') continue;
  533. if ($p==='..') {
  534. array_pop($newpath);
  535. continue;
  536. }
  537. array_push($newpath, $p);
  538. }
  539. $finalpath = $root.implode('/', $newpath);
  540. // check for existence when needed (except when unit testing)
  541. if($exists && !defined('DOKU_UNITTEST') && !file_exists($finalpath)) {
  542. return false;
  543. }
  544. return $finalpath;
  545. }