Completed
Push — tokfix ( bf239d )
by Andreas
04:17
created

init.php ➔ init_session()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 2
nop 0
dl 0
loc 12
rs 9.2
c 0
b 0
f 0
1
<?php
2
/**
3
 * Initialize some defaults needed for DokuWiki
4
 */
5
6
/**
7
 * timing Dokuwiki execution
8
 */
9
function delta_time($start=0) {
10
    return microtime(true)-((float)$start);
11
}
12
define('DOKU_START_TIME', delta_time());
13
14
global $config_cascade;
15
$config_cascade = array();
16
17
// if available load a preload config file
18
$preload = fullpath(dirname(__FILE__)).'/preload.php';
19
if (file_exists($preload)) include($preload);
20
21
// define the include path
22
if(!defined('DOKU_INC')) define('DOKU_INC',fullpath(dirname(__FILE__).'/../').'/');
23
24
// define Plugin dir
25
if(!defined('DOKU_PLUGIN'))  define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
26
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
30
// check for error reporting override or set error reporting to sane values
31
if (!defined('DOKU_E_LEVEL') && file_exists(DOKU_CONF.'report_e_all')) {
32
    define('DOKU_E_LEVEL', E_ALL);
33
}
34
if (!defined('DOKU_E_LEVEL')) {
35
    if(defined('E_DEPRECATED')){ // since php 5.3, since php 5.4 E_STRICT is part of E_ALL
36
        error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT);
37
    }else{
38
        error_reporting(E_ALL ^ E_NOTICE);
39
    }
40
} else {
41
    error_reporting(DOKU_E_LEVEL);
42
}
43
44
// init memory caches
45
global $cache_revinfo;
46
       $cache_revinfo = array();
47
global $cache_wikifn;
48
       $cache_wikifn = array();
49
global $cache_cleanid;
50
       $cache_cleanid = array();
51
global $cache_authname;
52
       $cache_authname = array();
53
global $cache_metadata;
54
       $cache_metadata = array();
55
56
// always include 'inc/config_cascade.php'
57
// previously in preload.php set fields of $config_cascade will be merged with the defaults
58
include(DOKU_INC.'inc/config_cascade.php');
59
60
//prepare config array()
61
global $conf;
62
$conf = array();
63
64
// load the global config file(s)
65
foreach (array('default','local','protected') as $config_group) {
66
    if (empty($config_cascade['main'][$config_group])) continue;
67
    foreach ($config_cascade['main'][$config_group] as $config_file) {
68
        if (file_exists($config_file)) {
69
            include($config_file);
70
        }
71
    }
72
}
73
74
//prepare license array()
75
global $license;
76
$license = array();
77
78
// load the license file(s)
79
foreach (array('default','local') as $config_group) {
80
    if (empty($config_cascade['license'][$config_group])) continue;
81
    foreach ($config_cascade['license'][$config_group] as $config_file) {
82
        if(file_exists($config_file)){
83
            include($config_file);
84
        }
85
    }
86
}
87
88
// set timezone (as in pre 5.3.0 days)
89
date_default_timezone_set(@date_default_timezone_get());
90
91
// define baseURL
92
if(!defined('DOKU_REL')) define('DOKU_REL',getBaseURL(false));
93
if(!defined('DOKU_URL')) define('DOKU_URL',getBaseURL(true));
94
if(!defined('DOKU_BASE')){
95
    if($conf['canonical']){
96
        define('DOKU_BASE',DOKU_URL);
97
    }else{
98
        define('DOKU_BASE',DOKU_REL);
99
    }
100
}
101
102
// define whitespace
103
if(!defined('DOKU_LF')) define ('DOKU_LF',"\n");
104
if(!defined('DOKU_TAB')) define ('DOKU_TAB',"\t");
105
106
// define cookie and session id, append server port when securecookie is configured FS#1664
107
if (!defined('DOKU_COOKIE')) define('DOKU_COOKIE', 'DW'.md5(DOKU_REL.(($conf['securecookie'])?$_SERVER['SERVER_PORT']:'')));
108
109
110
// define main script
111
if(!defined('DOKU_SCRIPT')) define('DOKU_SCRIPT','doku.php');
112
113
// DEPRECATED, use tpl_basedir() instead
114
if(!defined('DOKU_TPL')) define('DOKU_TPL',
115
        DOKU_BASE.'lib/tpl/'.$conf['template'].'/');
116
117
// DEPRECATED, use tpl_incdir() instead
118
if(!defined('DOKU_TPLINC')) define('DOKU_TPLINC',
119
        DOKU_INC.'lib/tpl/'.$conf['template'].'/');
120
121
// make session rewrites XHTML compliant
122
@ini_set('arg_separator.output', '&amp;');
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
123
124
// make sure global zlib does not interfere FS#1132
125
@ini_set('zlib.output_compression', 'off');
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
126
127
// increase PCRE backtrack limit
128
@ini_set('pcre.backtrack_limit', '20971520');
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
129
130
// enable gzip compression if supported
131
$conf['gzip_output'] &= (strpos($_SERVER['HTTP_ACCEPT_ENCODING'],'gzip') !== false);
132
global $ACT;
133
if ($conf['gzip_output'] &&
134
        !defined('DOKU_DISABLE_GZIP_OUTPUT') &&
135
        function_exists('ob_gzhandler') &&
136
        // Disable compression when a (compressed) sitemap might be delivered
137
        // See https://bugs.dokuwiki.org/index.php?do=details&task_id=2576
138
        $ACT != 'sitemap') {
139
    ob_start('ob_gzhandler');
140
}
141
142
// init session
143
if(!headers_sent() && !defined('NOSESSION')) {
144
    if(!defined('DOKU_SESSION_NAME'))     define ('DOKU_SESSION_NAME', "DokuWiki");
145
    if(!defined('DOKU_SESSION_LIFETIME')) define ('DOKU_SESSION_LIFETIME', 0);
146
    if(!defined('DOKU_SESSION_PATH')) {
147
        $cookieDir = empty($conf['cookiedir']) ? DOKU_REL : $conf['cookiedir'];
148
        define ('DOKU_SESSION_PATH', $cookieDir);
149
    }
150
    if(!defined('DOKU_SESSION_DOMAIN'))   define ('DOKU_SESSION_DOMAIN', '');
151
152
    // start the session
153
    init_session();
154
155
    // load left over messages
156
    if(isset($_SESSION[DOKU_COOKIE]['msg'])) {
157
        $MSG = $_SESSION[DOKU_COOKIE]['msg'];
158
        unset($_SESSION[DOKU_COOKIE]['msg']);
159
    }
160
}
161
162
// kill magic quotes
163
if (get_magic_quotes_gpc() && !defined('MAGIC_QUOTES_STRIPPED')) {
164
    if (!empty($_GET))    remove_magic_quotes($_GET);
165
    if (!empty($_POST))   remove_magic_quotes($_POST);
166
    if (!empty($_COOKIE)) remove_magic_quotes($_COOKIE);
167
    if (!empty($_REQUEST)) remove_magic_quotes($_REQUEST);
168
    @ini_set('magic_quotes_gpc', 0);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
169
    define('MAGIC_QUOTES_STRIPPED',1);
170
}
171
if(function_exists('set_magic_quotes_runtime')) @set_magic_quotes_runtime(0);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
Deprecated Code introduced by
The function set_magic_quotes_runtime() has been deprecated with message: Deprecated as of PHP 5.3.0. Relying on this feature is highly discouraged.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
172
@ini_set('magic_quotes_sybase',0);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

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