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