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