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