xref: /dokuwiki/install.php (revision db5812548dce8c3d33de113cdc1d22a1dc818420)
1<?php
2
3/*><div style="width:60%; margin: auto; background-color: #fcc;
4                border: 1px solid #faa; padding: 0.5em 1em;">
5    <h1 style="font-size: 120%">No PHP Support</h1>
6
7    It seems this server has no PHP support enabled. You will need to
8    enable PHP before you can install and run DokuWiki. Contact your hosting
9    provider if you're unsure what this means.
10
11</div>*/
12/**
13 * Dokuwiki installation assistance
14 *
15 * @author      Chris Smith <chris@jalakai.co.uk>
16 */
17
18if (!defined('DOKU_INC')) define('DOKU_INC', dirname(__FILE__) . '/');
19if (!defined('DOKU_CONF')) define('DOKU_CONF', DOKU_INC . 'conf/');
20if (!defined('DOKU_LOCAL')) define('DOKU_LOCAL', DOKU_INC . 'conf/');
21
22// load and initialize the core system
23require_once(DOKU_INC . 'inc/init.php');
24
25// check for error reporting override or set error reporting to sane values
26if (!defined('DOKU_E_LEVEL')) {
27    error_reporting(E_ALL ^ E_NOTICE);
28} else {
29    error_reporting(DOKU_E_LEVEL);
30}
31
32// language strings
33require_once(DOKU_INC . 'inc/lang/en/lang.php');
34if (isset($_REQUEST['l']) && !is_array($_REQUEST['l'])) {
35    $LC = preg_replace('/[^a-z\-]+/', '', $_REQUEST['l']);
36}
37if (empty($LC)) $LC = 'en';
38if ($LC && $LC != 'en') {
39    require_once(DOKU_INC . 'inc/lang/' . $LC . '/lang.php');
40}
41
42// initialise variables ...
43$error = array();
44
45// begin output
46header('Content-Type: text/html; charset=utf-8');
47?>
48<!DOCTYPE html>
49<html lang="<?php echo $LC?>" dir="<?php echo $lang['direction']?>">
50<head>
51    <meta charset="utf-8" />
52    <title><?php echo $lang['i_installer']?></title>
53    <style>
54        body { width: 90%; margin: 0 auto; font: 84% Verdana, Helvetica, Arial, sans-serif; }
55        img { border: none }
56        br.cl { clear:both; }
57        code { font-size: 110%; color: #800000; }
58        fieldset { border: none }
59        label { display: block; margin-top: 0.5em; }
60        select.text, input.text { width: 30em; margin: 0 0.5em; }
61        a {text-decoration: none}
62    </style>
63    <script>
64        function acltoggle(){
65            var cb = document.getElementById('acl');
66            var fs = document.getElementById('acldep');
67            if(!cb || !fs) return;
68            if(cb.checked){
69                fs.style.display = '';
70            }else{
71                fs.style.display = 'none';
72            }
73        }
74        window.onload = function(){
75            acltoggle();
76            var cb = document.getElementById('acl');
77            if(cb) cb.onchange = acltoggle;
78        };
79    </script>
80</head>
81<body style="">
82    <h1 style="float:left">
83        <img src="lib/exe/fetch.php?media=wiki:dokuwiki-128.png"
84             style="vertical-align: middle;" alt="" height="64" width="64" />
85        <?php echo $lang['i_installer']?>
86    </h1>
87    <div style="float:right; margin: 1em;">
88        <?php langsel()?>
89    </div>
90    <br class="cl" />
91
92    <div style="float: right; width: 34%;">
93        <?php
94        if (file_exists(DOKU_INC . 'inc/lang/' . $LC . '/install.html')) {
95            include(DOKU_INC . 'inc/lang/' . $LC . '/install.html');
96        } else {
97            print "<div lang=\"en\" dir=\"ltr\">\n";
98            include(DOKU_INC . 'inc/lang/en/install.html');
99            print "</div>\n";
100        }
101        ?>
102        <a style="
103                background: transparent
104                url(data/dont-panic-if-you-see-this-in-your-logs-it-means-your-directory-permissions-are-correct.png)
105                left top no-repeat;
106                display: block; width:380px; height:73px; border:none; clear:both;"
107           target="_blank"
108           href="http://www.dokuwiki.org/security#web_access_security"></a>
109    </div>
110
111    <div style="float: left; width: 58%;">
112        <?php
113        try {
114            if (! (check_functions() && check_permissions())) {
115                echo '<p>' . $lang['i_problems'] . '</p>';
116                print_errors();
117                print_retry();
118            } elseif (!check_configs()) {
119                echo '<p>' . $lang['i_modified'] . '</p>';
120                print_errors();
121            } elseif (check_data($_REQUEST['d'])) {
122                // check_data has sanitized all input parameters
123                if (!store_data($_REQUEST['d'])) {
124                    echo '<p>' . $lang['i_failure'] . '</p>';
125                    print_errors();
126                } else {
127                    echo '<p>' . $lang['i_success'] . '</p>';
128                }
129            } else {
130                print_errors();
131                print_form($_REQUEST['d']);
132            }
133        } catch (Exception $e) {
134            echo 'Caught exception: ',  $e->getMessage(), "\n";
135        }
136        ?>
137    </div>
138
139
140<div style="clear: both">
141  <a href="http://dokuwiki.org/"><img src="lib/tpl/dokuwiki/images/button-dw.png" alt="driven by DokuWiki" /></a>
142  <a href="http://php.net"><img src="lib/tpl/dokuwiki/images/button-php.gif" alt="powered by PHP" /></a>
143</div>
144</body>
145</html>
146<?php
147
148/**
149 * Print the input form
150 *
151 * @param array $d submitted entry 'd' of request data
152 */
153function print_form($d)
154{
155    global $lang;
156    global $LC;
157
158    include(DOKU_CONF . 'license.php');
159
160    if (!is_array($d)) $d = array();
161    $d = array_map('hsc', $d);
162
163    if (!isset($d['acl'])) $d['acl'] = 1;
164    if (!isset($d['pop'])) $d['pop'] = 1;
165
166    ?>
167    <form action="" method="post">
168    <input type="hidden" name="l" value="<?php echo $LC ?>" />
169    <fieldset>
170        <label for="title"><?php echo $lang['i_wikiname']?>
171        <input type="text" name="d[title]" id="title" value="<?php echo $d['title'] ?>" style="width: 20em;" />
172        </label>
173
174        <fieldset style="margin-top: 1em;">
175            <label for="acl">
176            <input type="checkbox" name="d[acl]" id="acl" <?php echo(($d['acl'] ? ' checked="checked"' : ''));?> />
177            <?php echo $lang['i_enableacl']?></label>
178
179            <fieldset id="acldep">
180                <label for="superuser"><?php echo $lang['i_superuser']?></label>
181                <input class="text" type="text" name="d[superuser]" id="superuser"
182                       value="<?php echo $d['superuser'] ?>" />
183
184                <label for="fullname"><?php echo $lang['fullname']?></label>
185                <input class="text" type="text" name="d[fullname]" id="fullname"
186                       value="<?php echo $d['fullname'] ?>" />
187
188                <label for="email"><?php echo $lang['email']?></label>
189                <input class="text" type="text" name="d[email]" id="email" value="<?php echo $d['email'] ?>" />
190
191                <label for="password"><?php echo $lang['pass']?></label>
192                <input class="text" type="password" name="d[password]" id="password" />
193
194                <label for="confirm"><?php echo $lang['passchk']?></label>
195                <input class="text" type="password" name="d[confirm]" id="confirm" />
196
197                <label for="policy"><?php echo $lang['i_policy']?></label>
198                <select class="text" name="d[policy]" id="policy">
199                    <option value="0" <?php echo ($d['policy'] == 0) ? 'selected="selected"' : '' ?>><?php
200                        echo $lang['i_pol0']?></option>
201                    <option value="1" <?php echo ($d['policy'] == 1) ? 'selected="selected"' : '' ?>><?php
202                        echo $lang['i_pol1']?></option>
203                    <option value="2" <?php echo ($d['policy'] == 2) ? 'selected="selected"' : '' ?>><?php
204                        echo $lang['i_pol2']?></option>
205                </select>
206
207                <label for="allowreg">
208                    <input type="checkbox" name="d[allowreg]" id="allowreg" <?php
209                        echo(($d['allowreg'] ? ' checked="checked"' : ''));?> />
210                    <?php echo $lang['i_allowreg']?>
211                </label>
212            </fieldset>
213        </fieldset>
214
215        <fieldset>
216            <p><?php echo $lang['i_license']?></p>
217            <?php
218            array_push($license, array('name' => $lang['i_license_none'], 'url' => ''));
219            if (empty($d['license'])) $d['license'] = 'cc-by-sa';
220            foreach ($license as $key => $lic) {
221                echo '<label for="lic_' . $key . '">';
222                echo '<input type="radio" name="d[license]" value="' . hsc($key) . '" id="lic_' . $key . '"' .
223                     (($d['license'] === $key) ? ' checked="checked"' : '') . '>';
224                echo hsc($lic['name']);
225                if ($lic['url']) echo ' <a href="' . $lic['url'] . '" target="_blank"><sup>[?]</sup></a>';
226                echo '</label>';
227            }
228            ?>
229        </fieldset>
230
231        <fieldset>
232            <p><?php echo $lang['i_pop_field']?></p>
233            <label for="pop">
234                <input type="checkbox" name="d[pop]" id="pop" <?php
235                    echo(($d['pop'] ? ' checked="checked"' : ''));?> />
236                <?php echo $lang['i_pop_label']?>
237                <a href="http://www.dokuwiki.org/popularity" target="_blank"><sup>[?]</sup></a>
238            </label>
239        </fieldset>
240
241    </fieldset>
242    <fieldset id="process">
243        <button type="submit" name="submit"><?php echo $lang['btn_save']?></button>
244    </fieldset>
245    </form>
246    <?php
247}
248
249function print_retry()
250{
251    global $lang;
252    global $LC;
253    ?>
254    <form action="" method="get">
255      <fieldset>
256        <input type="hidden" name="l" value="<?php echo $LC ?>" />
257        <button type="submit"><?php echo $lang['i_retry'];?></button>
258      </fieldset>
259    </form>
260    <?php
261}
262
263/**
264 * Check validity of data
265 *
266 * @author Andreas Gohr
267 *
268 * @param array $d
269 * @return bool ok?
270 */
271function check_data(&$d)
272{
273    static $form_default = array(
274        'title'     => '',
275        'acl'       => '1',
276        'superuser' => '',
277        'fullname'  => '',
278        'email'     => '',
279        'password'  => '',
280        'confirm'   => '',
281        'policy'    => '0',
282        'allowreg'  => '0',
283        'license'   => 'cc-by-sa'
284    );
285    global $lang;
286    global $error;
287
288    if (!is_array($d)) $d = array();
289    foreach ($d as $k => $v) {
290        if (is_array($v))
291            unset($d[$k]);
292        else $d[$k] = (string)$v;
293    }
294
295    //autolowercase the username
296    $d['superuser'] = isset($d['superuser']) ? strtolower($d['superuser']) : "";
297
298    $ok = false;
299
300    if (isset($_REQUEST['submit'])) {
301        $ok = true;
302
303        // check input
304        if (empty($d['title'])) {
305            $error[] = sprintf($lang['i_badval'], $lang['i_wikiname']);
306            $ok      = false;
307        }
308        if (isset($d['acl'])) {
309            if (!preg_match('/^[a-z0-9_]+$/', $d['superuser'])) {
310                $error[] = sprintf($lang['i_badval'], $lang['i_superuser']);
311                $ok      = false;
312            }
313            if (empty($d['password'])) {
314                $error[] = sprintf($lang['i_badval'], $lang['pass']);
315                $ok      = false;
316            } elseif (!isset($d['confirm']) || $d['confirm'] != $d['password']) {
317                $error[] = sprintf($lang['i_badval'], $lang['passchk']);
318                $ok      = false;
319            }
320            if (empty($d['fullname']) || strstr($d['fullname'], ':')) {
321                $error[] = sprintf($lang['i_badval'], $lang['fullname']);
322                $ok      = false;
323            }
324            if (empty($d['email']) || strstr($d['email'], ':') || !strstr($d['email'], '@')) {
325                $error[] = sprintf($lang['i_badval'], $lang['email']);
326                $ok      = false;
327            }
328        } else {
329            // Since default = 1, browser won't send acl=0 when user untick acl
330            $d['acl'] = '0';
331        }
332    }
333    $d = array_merge($form_default, $d);
334    return $ok;
335}
336
337/**
338 * Writes the data to the config files
339 *
340 * @author  Chris Smith <chris@jalakai.co.uk>
341 *
342 * @param array $d
343 * @return bool
344 */
345function store_data($d)
346{
347    global $LC;
348    $ok = true;
349    $d['policy'] = (int) $d['policy'];
350
351    // create local.php
352    $now    = gmdate('r');
353    $output = <<<EOT
354<?php
355/**
356 * Dokuwiki's Main Configuration File - Local Settings
357 * Auto-generated by install script
358 * Date: $now
359 */
360
361EOT;
362    // add any config options set by a previous installer
363    $preset = __DIR__ . '/install.conf';
364    if (file_exists($preset)) {
365        $output .= "# preset config options\n";
366        $output .= file_get_contents($preset);
367        $output .= "\n\n";
368        $output .= "# options selected in installer\n";
369        @unlink($preset);
370    }
371
372    $output .= '$conf[\'title\'] = \'' . addslashes($d['title']) . "';\n";
373    $output .= '$conf[\'lang\'] = \'' . addslashes($LC) . "';\n";
374    $output .= '$conf[\'license\'] = \'' . addslashes($d['license']) . "';\n";
375    if ($d['acl']) {
376        $output .= '$conf[\'useacl\'] = 1' . ";\n";
377        $output .= "\$conf['superuser'] = '@admin';\n";
378    }
379    if (!$d['allowreg']) {
380        $output .= '$conf[\'disableactions\'] = \'register\'' . ";\n";
381    }
382    $ok = $ok && fileWrite(DOKU_LOCAL . 'local.php', $output);
383
384    if ($d['acl']) {
385        // hash the password
386        $phash = new \dokuwiki\PassHash();
387        $pass = $phash->hash_bcrypt($d['password']);
388
389        // create users.auth.php
390        $output = <<<EOT
391# users.auth.php
392# <?php exit()?>
393# Don't modify the lines above
394#
395# Userfile
396#
397# Auto-generated by install script
398# Date: $now
399#
400# Format:
401# login:passwordhash:Real Name:email:groups,comma,separated
402
403EOT;
404        // --- user:bcryptpasswordhash:Real Name:email:groups,comma,seperated
405        $output = $output . "\n" . join(":", array($d['superuser'], $pass, $d['fullname'], $d['email'], 'admin,user')) . "\n";
406        $ok = $ok && fileWrite(DOKU_LOCAL . 'users.auth.php', $output);
407
408        // create acl.auth.php
409        $output = <<<EOT
410# acl.auth.php
411# <?php exit()?>
412# Don't modify the lines above
413#
414# Access Control Lists
415#
416# Auto-generated by install script
417# Date: $now
418
419EOT;
420        if ($d['policy'] == 2) {
421            $output .=  "*               @ALL          0\n";
422            $output .=  "*               @user         8\n";
423        } elseif ($d['policy'] == 1) {
424            $output .=  "*               @ALL          1\n";
425            $output .=  "*               @user         8\n";
426        } else {
427            $output .=  "*               @ALL          8\n";
428        }
429        $ok = $ok && fileWrite(DOKU_LOCAL . 'acl.auth.php', $output);
430    }
431
432    // enable popularity submission
433    if (isset($d['pop']) && $d['pop']) {
434        @touch(DOKU_INC . 'data/cache/autosubmit.txt');
435    }
436
437    // disable auth plugins til needed
438    $output = <<<EOT
439<?php
440/*
441 * Local plugin enable/disable settings
442 *
443 * Auto-generated by install script
444 * Date: $now
445 */
446
447\$plugins['authad']    = 0;
448\$plugins['authldap']  = 0;
449\$plugins['authmysql'] = 0;
450\$plugins['authpgsql'] = 0;
451
452EOT;
453    $ok = $ok && fileWrite(DOKU_LOCAL . 'plugins.local.php', $output);
454
455    return $ok;
456}
457
458/**
459 * Write the given content to a file
460 *
461 * @author  Chris Smith <chris@jalakai.co.uk>
462 *
463 * @param string $filename
464 * @param string $data
465 * @return bool
466 */
467function fileWrite($filename, $data)
468{
469    global $error;
470    global $lang;
471
472    if (($fp = @fopen($filename, 'wb')) === false) {
473        $filename = str_replace($_SERVER['DOCUMENT_ROOT'], '{DOCUMENT_ROOT}/', $filename);
474        $error[]  = sprintf($lang['i_writeerr'], $filename);
475        return false;
476    }
477
478    if (!empty($data)) {
479        fwrite($fp, $data);
480    }
481    fclose($fp);
482    return true;
483}
484
485
486/**
487 * check installation dependent local config files and tests for a known
488 * unmodified main config file
489 *
490 * @author      Chris Smith <chris@jalakai.co.uk>
491 *
492 * @return bool
493 */
494function check_configs()
495{
496    global $error;
497    global $lang;
498
499    $ok = true;
500
501    $config_files = array(
502        'local' => DOKU_LOCAL . 'local.php',
503        'users' => DOKU_LOCAL . 'users.auth.php',
504        'auth'  => DOKU_LOCAL . 'acl.auth.php'
505    );
506
507    // configs shouldn't exist
508    foreach ($config_files as $file) {
509        if (file_exists($file) && filesize($file)) {
510            $file    = str_replace($_SERVER['DOCUMENT_ROOT'], '{DOCUMENT_ROOT}/', $file);
511            $error[] = sprintf($lang['i_confexists'], $file);
512            $ok      = false;
513        }
514    }
515    return $ok;
516}
517
518
519/**
520 * Check other installation dir/file permission requirements
521 *
522 * @author      Chris Smith <chris@jalakai.co.uk>
523 *
524 * @return bool
525 */
526function check_permissions()
527{
528    global $error;
529    global $lang;
530
531    $dirs = array(
532        'conf'        => DOKU_LOCAL,
533        'data'        => DOKU_INC . 'data',
534        'pages'       => DOKU_INC . 'data/pages',
535        'attic'       => DOKU_INC . 'data/attic',
536        'media'       => DOKU_INC . 'data/media',
537        'media_attic' => DOKU_INC . 'data/media_attic',
538        'media_meta'  => DOKU_INC . 'data/media_meta',
539        'meta'        => DOKU_INC . 'data/meta',
540        'cache'       => DOKU_INC . 'data/cache',
541        'locks'       => DOKU_INC . 'data/locks',
542        'index'       => DOKU_INC . 'data/index',
543        'tmp'         => DOKU_INC . 'data/tmp'
544    );
545
546    $ok = true;
547    foreach ($dirs as $dir) {
548        if (!file_exists("$dir/.") || !is_writable($dir)) {
549            $dir     = str_replace($_SERVER['DOCUMENT_ROOT'], '{DOCUMENT_ROOT}', $dir);
550            $error[] = sprintf($lang['i_permfail'], $dir);
551            $ok      = false;
552        }
553    }
554    return $ok;
555}
556
557/**
558 * Check the availability of functions used in DokuWiki and the PHP version
559 *
560 * @author Andreas Gohr <andi@splitbrain.org>
561 *
562 * @return bool
563 */
564function check_functions()
565{
566    global $error;
567    global $lang;
568    $ok = true;
569
570    if (version_compare(phpversion(), '5.6.0', '<')) {
571        $error[] = sprintf($lang['i_phpver'], phpversion(), '5.6.0');
572        $ok = false;
573    }
574
575    if (ini_get('mbstring.func_overload') != 0) {
576        $error[] = $lang['i_mbfuncoverload'];
577        $ok = false;
578    }
579
580    try {
581        random_bytes(1);
582    } catch (\Exception $th) {
583        // If an appropriate source of randomness cannot be found, an Exception will be thrown by PHP 7+
584        $error[] = $lang['i_urandom'];
585        $ok = false;
586    }
587
588    if (ini_get('mbstring.func_overload') != 0) {
589        $error[] = $lang['i_mbfuncoverload'];
590        $ok = false;
591    }
592
593    $funcs = explode(' ', 'addslashes call_user_func chmod copy fgets ' .
594                         'file file_exists fseek flush filesize ftell fopen ' .
595                         'glob header ignore_user_abort ini_get mkdir ' .
596                         'ob_start opendir parse_ini_file readfile realpath ' .
597                         'rename rmdir serialize session_start unlink usleep ' .
598                         'preg_replace file_get_contents htmlspecialchars_decode ' .
599                         'spl_autoload_register stream_select fsockopen pack xml_parser_create');
600
601    if (!function_exists('mb_substr')) {
602        $funcs[] = 'utf8_encode';
603        $funcs[] = 'utf8_decode';
604    }
605
606    if (!function_exists('mail')) {
607        if (strpos(ini_get('disable_functions'), 'mail') !== false) {
608            $disabled = $lang['i_disabled'];
609        } else {
610            $disabled = "";
611        }
612        $error[] = sprintf($lang['i_funcnmail'], $disabled);
613    }
614
615    foreach ($funcs as $func) {
616        if (!function_exists($func)) {
617            $error[] = sprintf($lang['i_funcna'], $func);
618            $ok = false;
619        }
620    }
621    return $ok;
622}
623
624/**
625 * Print language selection
626 *
627 * @author Andreas Gohr <andi@splitbrain.org>
628 */
629function langsel()
630{
631    global $lang;
632    global $LC;
633
634    $dir = DOKU_INC . 'inc/lang';
635    $dh  = opendir($dir);
636    if (!$dh) return;
637
638    $langs = array();
639    while (($file = readdir($dh)) !== false) {
640        if (preg_match('/^[\._]/', $file)) continue;
641        if (is_dir($dir . '/' . $file) && file_exists($dir . '/' . $file . '/lang.php')) {
642            $langs[] = $file;
643        }
644    }
645    closedir($dh);
646    sort($langs);
647
648    echo '<form action="">';
649    echo $lang['i_chooselang'];
650    echo ': <select name="l" onchange="submit()">';
651    foreach ($langs as $l) {
652        $sel = ($l == $LC) ? 'selected="selected"' : '';
653        echo '<option value="' . $l . '" ' . $sel . '>' . $l . '</option>';
654    }
655    echo '</select> ';
656    echo '<button type="submit">' . $lang['btn_update'] . '</button>';
657    echo '</form>';
658}
659
660/**
661 * Print global error array
662 *
663 * @author Andreas Gohr <andi@splitbrain.org>
664 */
665function print_errors()
666{
667    global $error;
668    if (!empty($error)) {
669        echo '<ul>';
670        foreach ($error as $err) {
671            echo "<li>$err</li>";
672        }
673        echo '</ul>';
674    }
675}
676