Completed
Push — stable ( e349ed...3291b9 )
by
unknown
03:56
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
 * @link http://php.net/manual/en/session.configuration.php#ini.session.sid-length
236
 */
237
function 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
 */
254
function 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
 */
291
function 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
 */
319
function 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 <[email protected]>
344
 */
345
function init_path($path){
346
    // check existence
347
    $p = fullpath($path);
348
    if(!file_exists($p)){
349
        $p = fullpath(DOKU_INC.$path);
350
        if(!file_exists($p)){
351
            return '';
352
        }
353
    }
354
355
    // check writability
356
    if(!@is_writable($p)){
357
        return '';
358
    }
359
360
    // check accessability (execute bit) for directories
361
    if(@is_dir($p) && !file_exists("$p/.")){
362
        return '';
363
    }
364
365
    return $p;
366
}
367
368
/**
369
 * Sets the internal config values fperm and dperm which, when set,
370
 * will be used to change the permission of a newly created dir or
371
 * file with chmod. Considers the influence of the system's umask
372
 * setting the values only if needed.
373
 */
374
function init_creationmodes(){
375
    global $conf;
376
377
    // Legacy support for old umask/dmask scheme
378
    unset($conf['dmask']);
379
    unset($conf['fmask']);
380
    unset($conf['umask']);
381
    unset($conf['fperm']);
382
    unset($conf['dperm']);
383
384
    // get system umask, fallback to 0 if none available
385
    $umask = @umask();
386
    if(!$umask) $umask = 0000;
387
388
    // check what is set automatically by the system on file creation
389
    // and set the fperm param if it's not what we want
390
    $auto_fmode = 0666 & ~$umask;
391
    if($auto_fmode != $conf['fmode']) $conf['fperm'] = $conf['fmode'];
392
393
    // check what is set automatically by the system on file creation
394
    // and set the dperm param if it's not what we want
395
    $auto_dmode = $conf['dmode'] & ~$umask;
396
    if($auto_dmode != $conf['dmode']) $conf['dperm'] = $conf['dmode'];
397
}
398
399
/**
400
 * remove magic quotes recursivly
401
 *
402
 * @author Andreas Gohr <[email protected]>
403
 */
404
function remove_magic_quotes(&$array) {
405
    foreach (array_keys($array) as $key) {
406
        // handle magic quotes in keynames (breaks order)
407
        $sk = stripslashes($key);
408
        if($sk != $key){
409
            $array[$sk] = $array[$key];
410
            unset($array[$key]);
411
            $key = $sk;
412
        }
413
414
        // do recursion if needed
415
        if (is_array($array[$key])) {
416
            remove_magic_quotes($array[$key]);
417
        }else {
418
            $array[$key] = stripslashes($array[$key]);
419
        }
420
    }
421
}
422
423
/**
424
 * Returns the full absolute URL to the directory where
425
 * DokuWiki is installed in (includes a trailing slash)
426
 *
427
 * !! Can not access $_SERVER values through $INPUT
428
 * !! here as this function is called before $INPUT is
429
 * !! initialized.
430
 *
431
 * @author Andreas Gohr <[email protected]>
432
 */
433
function getBaseURL($abs=null){
434
    global $conf;
435
    //if canonical url enabled always return absolute
436
    if(is_null($abs)) $abs = $conf['canonical'];
437
438
    if(!empty($conf['basedir'])){
439
        $dir = $conf['basedir'];
440
    }elseif(substr($_SERVER['SCRIPT_NAME'],-4) == '.php'){
441
        $dir = dirname($_SERVER['SCRIPT_NAME']);
442
    }elseif(substr($_SERVER['PHP_SELF'],-4) == '.php'){
443
        $dir = dirname($_SERVER['PHP_SELF']);
444
    }elseif($_SERVER['DOCUMENT_ROOT'] && $_SERVER['SCRIPT_FILENAME']){
445
        $dir = preg_replace ('/^'.preg_quote($_SERVER['DOCUMENT_ROOT'],'/').'/','',
446
                $_SERVER['SCRIPT_FILENAME']);
447
        $dir = dirname('/'.$dir);
448
    }else{
449
        $dir = '.'; //probably wrong
450
    }
451
452
    $dir = str_replace('\\','/',$dir);             // bugfix for weird WIN behaviour
453
    $dir = preg_replace('#//+#','/',"/$dir/");     // ensure leading and trailing slashes
454
455
    //handle script in lib/exe dir
456
    $dir = preg_replace('!lib/exe/$!','',$dir);
457
458
    //handle script in lib/plugins dir
459
    $dir = preg_replace('!lib/plugins/.*$!','',$dir);
460
461
    //finish here for relative URLs
462
    if(!$abs) return $dir;
463
464
    //use config option if available, trim any slash from end of baseurl to avoid multiple consecutive slashes in the path
465
    if(!empty($conf['baseurl'])) return rtrim($conf['baseurl'],'/').$dir;
466
467
    //split hostheader into host and port
468
    if(isset($_SERVER['HTTP_HOST'])){
469
        $parsed_host = parse_url('http://'.$_SERVER['HTTP_HOST']);
470
        $host = isset($parsed_host['host']) ? $parsed_host['host'] : null;
471
        $port = isset($parsed_host['port']) ? $parsed_host['port'] : null;
472
    }elseif(isset($_SERVER['SERVER_NAME'])){
473
        $parsed_host = parse_url('http://'.$_SERVER['SERVER_NAME']);
474
        $host = isset($parsed_host['host']) ? $parsed_host['host'] : null;
475
        $port = isset($parsed_host['port']) ? $parsed_host['port'] : null;
476
    }else{
477
        $host = php_uname('n');
478
        $port = '';
479
    }
480
481
    if(is_null($port)){
482
        $port = '';
483
    }
484
485
    if(!is_ssl()){
486
        $proto = 'http://';
487
        if ($port == '80') {
488
            $port = '';
489
        }
490
    }else{
491
        $proto = 'https://';
492
        if ($port == '443') {
493
            $port = '';
494
        }
495
    }
496
497
    if($port !== '') $port = ':'.$port;
498
499
    return $proto.$host.$port.$dir;
500
}
501
502
/**
503
 * Check if accessed via HTTPS
504
 *
505
 * Apache leaves ,$_SERVER['HTTPS'] empty when not available, IIS sets it to 'off'.
506
 * 'false' and 'disabled' are just guessing
507
 *
508
 * @returns bool true when SSL is active
509
 */
510
function is_ssl(){
511
    // check if we are behind a reverse proxy
512
    if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
513
        if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
514
	    return true;
515
	} else {
516
	    return false;
517
	}
518
    }
519
    if (!isset($_SERVER['HTTPS']) ||
520
        preg_match('/^(|off|false|disabled)$/i',$_SERVER['HTTPS'])){
521
        return false;
522
    }else{
523
        return true;
524
    }
525
}
526
527
/**
528
 * print a nice message even if no styles are loaded yet.
529
 */
530
function nice_die($msg){
531
    echo<<<EOT
532
<!DOCTYPE html>
533
<html>
534
<head><title>DokuWiki Setup Error</title></head>
535
<body style="font-family: Arial, sans-serif">
536
    <div style="width:60%; margin: auto; background-color: #fcc;
537
                border: 1px solid #faa; padding: 0.5em 1em;">
538
        <h1 style="font-size: 120%">DokuWiki Setup Error</h1>
539
        <p>$msg</p>
540
    </div>
541
</body>
542
</html>
543
EOT;
544
    exit(1);
545
}
546
547
/**
548
 * A realpath() replacement
549
 *
550
 * This function behaves similar to PHP's realpath() but does not resolve
551
 * symlinks or accesses upper directories
552
 *
553
 * @author Andreas Gohr <[email protected]>
554
 * @author <richpageau at yahoo dot co dot uk>
555
 * @link   http://php.net/manual/en/function.realpath.php#75992
556
 */
557
function fullpath($path,$exists=false){
558
    static $run = 0;
559
    $root  = '';
560
    $iswin = (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' || @$GLOBALS['DOKU_UNITTEST_ASSUME_WINDOWS']);
561
562
    // find the (indestructable) root of the path - keeps windows stuff intact
563
    if($path{0} == '/'){
564
        $root = '/';
565
    }elseif($iswin){
566
        // match drive letter and UNC paths
567
        if(preg_match('!^([a-zA-z]:)(.*)!',$path,$match)){
568
            $root = $match[1].'/';
569
            $path = $match[2];
570
        }else if(preg_match('!^(\\\\\\\\[^\\\\/]+\\\\[^\\\\/]+[\\\\/])(.*)!',$path,$match)){
571
            $root = $match[1];
572
            $path = $match[2];
573
        }
574
    }
575
    $path = str_replace('\\','/',$path);
576
577
    // if the given path wasn't absolute already, prepend the script path and retry
578
    if(!$root){
579
        $base = dirname($_SERVER['SCRIPT_FILENAME']);
580
        $path = $base.'/'.$path;
581
        if($run == 0){ // avoid endless recursion when base isn't absolute for some reason
582
            $run++;
583
            return fullpath($path,$exists);
584
        }
585
    }
586
    $run = 0;
587
588
    // canonicalize
589
    $path=explode('/', $path);
590
    $newpath=array();
591
    foreach($path as $p) {
592
        if ($p === '' || $p === '.') continue;
593
        if ($p==='..') {
594
            array_pop($newpath);
595
            continue;
596
        }
597
        array_push($newpath, $p);
598
    }
599
    $finalpath = $root.implode('/', $newpath);
600
601
    // check for existence when needed (except when unit testing)
602
    if($exists && !defined('DOKU_UNITTEST') && !file_exists($finalpath)) {
603
        return false;
604
    }
605
    return $finalpath;
606
}
607
608