Issues (847)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

inc/init.php (2 issues)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Initialize some defaults needed for DokuWiki
4
 */
5
6
use dokuwiki\Extension\Event;
7
use dokuwiki\Extension\EventHandler;
8
9
/**
10
 * timing Dokuwiki execution
11
 *
12
 * @param integer $start
13
 *
14
 * @return mixed
15
 */
16
function delta_time($start=0) {
17
    return microtime(true)-((float)$start);
18
}
19
define('DOKU_START_TIME', delta_time());
20
21
global $config_cascade;
22
$config_cascade = array();
23
24
// if available load a preload config file
25
$preload = fullpath(dirname(__FILE__)).'/preload.php';
26
if (file_exists($preload)) include($preload);
27
28
// define the include path
29
if(!defined('DOKU_INC')) define('DOKU_INC',fullpath(dirname(__FILE__).'/../').'/');
30
31
// define Plugin dir
32
if(!defined('DOKU_PLUGIN'))  define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
33
34
// define config path (packagers may want to change this to /etc/dokuwiki/)
35
if(!defined('DOKU_CONF')) define('DOKU_CONF',DOKU_INC.'conf/');
36
37
// check for error reporting override or set error reporting to sane values
38
if (!defined('DOKU_E_LEVEL') && file_exists(DOKU_CONF.'report_e_all')) {
39
    define('DOKU_E_LEVEL', E_ALL);
40
}
41
if (!defined('DOKU_E_LEVEL')) {
42
    error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT);
43
} else {
44
    error_reporting(DOKU_E_LEVEL);
45
}
46
47
// avoid caching issues #1594
48
header('Vary: Cookie');
49
50
// init memory caches
51
global $cache_revinfo;
52
       $cache_revinfo = array();
53
global $cache_wikifn;
54
       $cache_wikifn = array();
55
global $cache_cleanid;
56
       $cache_cleanid = array();
57
global $cache_authname;
58
       $cache_authname = array();
59
global $cache_metadata;
60
       $cache_metadata = array();
61
62
// always include 'inc/config_cascade.php'
63
// previously in preload.php set fields of $config_cascade will be merged with the defaults
64
include(DOKU_INC.'inc/config_cascade.php');
65
66
//prepare config array()
67
global $conf;
68
$conf = array();
69
70
// load the global config file(s)
71
foreach (array('default','local','protected') as $config_group) {
72
    if (empty($config_cascade['main'][$config_group])) continue;
73
    foreach ($config_cascade['main'][$config_group] as $config_file) {
74
        if (file_exists($config_file)) {
75
            include($config_file);
76
        }
77
    }
78
}
79
80
//prepare license array()
81
global $license;
82
$license = array();
83
84
// load the license file(s)
85
foreach (array('default','local') as $config_group) {
86
    if (empty($config_cascade['license'][$config_group])) continue;
87
    foreach ($config_cascade['license'][$config_group] as $config_file) {
88
        if(file_exists($config_file)){
89
            include($config_file);
90
        }
91
    }
92
}
93
94
// set timezone (as in pre 5.3.0 days)
95
date_default_timezone_set(@date_default_timezone_get());
96
97
// define baseURL
98
if(!defined('DOKU_REL')) define('DOKU_REL',getBaseURL(false));
0 ignored issues
show
false is of type boolean, but the function expects a null|string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
99
if(!defined('DOKU_URL')) define('DOKU_URL',getBaseURL(true));
0 ignored issues
show
true is of type boolean, but the function expects a null|string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

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