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