1
2
<?php
3
/**
4
 * StatusNet - the distributed open-source microblogging tool
5
 * Copyright (C) 2009, StatusNet, Inc.
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Affero General Public License as published by
9
 * the Free Software Foundation, either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
 *
20
 * @category Installation
21
 * @package  Installation
22
 *
23
 * @author   Adrian Lang <mail@adrianlang.de>
24
 * @author   Brenda Wallace <shiny@cpan.org>
25
 * @author   Brett Taylor <brett@webfroot.co.nz>
26
 * @author   Brion Vibber <brion@pobox.com>
27
 * @author   CiaranG <ciaran@ciarang.com>
28
 * @author   Craig Andrews <candrews@integralblue.com>
29
 * @author   Eric Helgeson <helfire@Erics-MBP.local>
30
 * @author   Evan Prodromou <evan@status.net>
31
 * @author   Robin Millette <millette@controlyourself.ca>
32
 * @author   Sarven Capadisli <csarven@status.net>
33
 * @author   Tom Adams <tom@holizz.com>
34
 * @author   Zach Copley <zach@status.net>
35
 * @license  GNU Affero General Public License http://www.gnu.org/licenses/
36
 * @version  0.9.x
37
 * @link     http://status.net
38
 */
39
40
define('INSTALLDIR', dirname(__FILE__));
41
42
$external_libraries=array(
43
    array(
44
        'name'=>'gettext',
45
        'url'=>'http://us.php.net/manual/en/book.gettext.php',
46
        'check_function'=>'gettext'
47
    ),
48
    array(
49
        'name'=>'PEAR',
50
        'url'=>'http://pear.php.net/',
51
        'deb'=>'php-pear',
52
        'include'=>'PEAR.php',
53
        'check_class'=>'PEAR'
54
    ),
55
    array(
56
        'name'=>'DB',
57
        'pear'=>'DB',
58
        'url'=>'http://pear.php.net/package/DB',
59
        'deb'=>'php-db',
60
        'include'=>'DB/common.php',
61
        'check_class'=>'DB_common'
62
    ),
63
    array(
64
        'name'=>'DB_DataObject',
65
        'pear'=>'DB_DataObject',
66
        'url'=>'http://pear.php.net/package/DB_DataObject',
67
        'include'=>'DB/DataObject.php',
68
        'check_class'=>'DB_DataObject'
69
    ),
70
    array(
71
        'name'=>'Console_Getopt',
72
        'pear'=>'Console_Getopt',
73
        'url'=>'http://pear.php.net/package/Console_Getopt',
74
        'include'=>'Console/Getopt.php',
75
        'check_class'=>'Console_Getopt'
76
    ),
77
    array(
78
        'name'=>'Facebook API',
79
        'url'=>'http://developers.facebook.com/',
80
        'include'=>'facebook/facebook.php',
81
        'check_class'=>'Facebook'
82
    ),
83
    array(
84
        'name'=>'htmLawed',
85
        'url'=>'http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed',
86
        'include'=>'htmLawed/htmLawed.php',
87
        'check_function'=>'htmLawed'
88
    ),
89
    array(
90
        'name'=>'HTTP_Request',
91
        'pear'=>'HTTP_Request',
92
        'url'=>'http://pear.php.net/package/HTTP_Request',
93
        'deb'=>'php-http-request',
94
        'include'=>'HTTP/Request.php',
95
        'check_class'=>'HTTP_Request'
96
    ),
97
    array(
98
        'name'=>'HTTP_Request2',
99
        'pear'=>'HTTP_Request2',
100
        'url'=>'http://pear.php.net/package/HTTP_Request2',
101
        'include'=>'HTTP/Request2.php',
102
        'check_class'=>'HTTP_Request2'
103
    ),
104
    array(
105
        'name'=>'Mail',
106
        'pear'=>'Mail',
107
        'url'=>'http://pear.php.net/package/Mail',
108
        'deb'=>'php-mail',
109
        'include'=>'Mail.php',
110
        'check_class'=>'Mail'
111
    ),
112
    array(
113
        'name'=>'Mail_mimeDecode',
114
        'pear'=>'Mail_mimeDecode',
115
        'url'=>'http://pear.php.net/package/Mail_mimeDecode',
116
        'deb'=>'php-mail-mimedecode',
117
        'include'=>'Mail/mimeDecode.php',
118
        'check_class'=>'Mail_mimeDecode'
119
    ),
120
    array(
121
        'name'=>'Mime_Type',
122
        'pear'=>'Mime_Type',
123
        'url'=>'http://pear.php.net/package/Mime_Type',
124
        'include'=>'MIME/Type.php',
125
        'check_class'=>'Mime_Type'
126
    ),
127
    array(
128
        'name'=>'Net_URL_Mapper',
129
        'pear'=>'Net_URL_Mapper',
130
        'url'=>'http://pear.php.net/package/Net_URL_Mapper',
131
        'include'=>'Net/URL/Mapper.php',
132
        'check_class'=>'Net_URL_Mapper'
133
    ),
134
    array(
135
        'name'=>'Net_LDAP2',
136
        'pear'=>'Net_LDAP2',
137
        'url'=>'http://pear.php.net/package/Net_LDAP2',
138
        'deb'=>'php-net-ldap2',
139
        'include'=>'Net/LDAP2.php',
140
        'check_class'=>'Net_LDAP2'
141
    ),
142
    array(
143
        'name'=>'Net_Socket',
144
        'pear'=>'Net_Socket',
145
        'url'=>'http://pear.php.net/package/Net_Socket',
146
        'deb'=>'php-net-socket',
147
        'include'=>'Net/Socket.php',
148
        'check_class'=>'Net_Socket'
149
    ),
150
    array(
151
        'name'=>'Net_SMTP',
152
        'pear'=>'Net_SMTP',
153
        'url'=>'http://pear.php.net/package/Net_SMTP',
154
        'deb'=>'php-net-smtp',
155
        'include'=>'Net/SMTP.php',
156
        'check_class'=>'Net_SMTP'
157
    ),
158
    array(
159
        'name'=>'Net_URL',
160
        'pear'=>'Net_URL',
161
        'url'=>'http://pear.php.net/package/Net_URL',
162
        'deb'=>'php-net-url',
163
        'include'=>'Net/URL.php',
164
        'check_class'=>'Net_URL'
165
    ),
166
    array(
167
        'name'=>'Net_URL2',
168
        'pear'=>'Net_URL2',
169
        'url'=>'http://pear.php.net/package/Net_URL2',
170
        'include'=>'Net/URL2.php',
171
        'check_class'=>'Net_URL2'
172
    ),
173
    array(
174
        'name'=>'Services_oEmbed',
175
        'pear'=>'Services_oEmbed',
176
        'url'=>'http://pear.php.net/package/Services_oEmbed',
177
        'include'=>'Services/oEmbed.php',
178
        'check_class'=>'Services_oEmbed'
179
    ),
180
    array(
181
        'name'=>'Stomp',
182
        'url'=>'http://stomp.codehaus.org/PHP',
183
        'include'=>'Stomp.php',
184
        'check_class'=>'Stomp'
185
    ),
186
    array(
187
        'name'=>'System_Command',
188
        'pear'=>'System_Command',
189
        'url'=>'http://pear.php.net/package/System_Command',
190
        'include'=>'System/Command.php',
191
        'check_class'=>'System_Command'
192
    ),
193
    array(
194
        'name'=>'XMPPHP',
195
        'url'=>'http://code.google.com/p/xmpphp',
196
        'include'=>'XMPPHP/XMPP.php',
197
        'check_class'=>'XMPPHP_XMPP'
198
    ),
199
    array(
200
        'name'=>'PHP Markdown',
201
        'url'=>'http://www.michelf.com/projects/php-markdown/',
202
        'include'=>'markdown.php',
203
        'check_class'=>'Markdown_Parser'
204
    ),
205
    array(
206
        'name'=>'OAuth',
207
        'url'=>'http://code.google.com/p/oauth-php',
208
        'include'=>'OAuth.php',
209
        'check_class'=>'OAuthRequest'
210
    ),
211
    array(
212
        'name'=>'Validate',
213
        'pear'=>'Validate',
214
        'url'=>'http://pear.php.net/package/Validate',
215
        'include'=>'Validate.php',
216
        'check_class'=>'Validate'
217
    )
218
);
219
$dbModules = array(
220
    'mysql' => array(
221
        'name' => 'MySQL',
222
        'check_module' => 'mysql', // mysqli?
223
        'installer' => 'mysql_db_installer',
224
    ),
225
    'pgsql' => array(
226
        'name' => 'PostgreSQL',
227
        'check_module' => 'pgsql',
228
        'installer' => 'pgsql_db_installer',
229
    ),
230
);
231
232
/**
233
 * the actual installation.
234
 * If call libraries are present, then install
235
 *
236
 * @return void
237
 */
238
function main()
239
{
240
    if (!checkPrereqs()) {
241
        return;
242
    }
243
244
    if (!empty($_GET['checklibs'])) {
245
        showLibs();
246
    } else {
247
        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
248
            handlePost();
249
        } else {
250
            showForm();
251
        }
252
    }
253
}
254
255
/**
256
 * checks if an external libary is present
257
 *
258
 * @param string $external_library Name of library
259
 *
260
 * @return boolean indicates if library present
261
 */
262
function haveExternalLibrary($external_library)
263
{
264
    if (isset($external_library['include']) && !haveIncludeFile($external_library['include'])) {
265
        return false;
266
    }
267
    if (isset($external_library['check_function']) && ! function_exists($external_library['check_function'])) {
268
        return false;
269
    }
270
    if (isset($external_library['check_class']) && ! class_exists($external_library['check_class'])) {
271
        return false;
272
    }
273
    return true;
274
}
275
276
// Attempt to include a PHP file and report if it worked, while
277
// suppressing the annoying warning messages on failure.
278
function haveIncludeFile($filename) {
279
    $old = error_reporting(error_reporting() & ~E_WARNING);
280
    $ok = include_once($filename);
281
    error_reporting($old);
282
    return $ok;
283
}
284
285
/**
286
 * Check if all is ready for installation
287
 *
288
 * @return void
289
 */
290
function checkPrereqs()
291
{
292
    $pass = true;
293
294
    if (file_exists(INSTALLDIR.'/config.php')) {
295
         printf('<p class="error">Config file &quot;config.php&quot; already exists.</p>');
296
        $pass = false;
297
    }
298
299
    if (version_compare(PHP_VERSION, '5.2.3', '<')) {
300
        printf('<p class="error">Require PHP version 5.2.3 or greater.</p>');
301
        $pass = false;
302
    }
303
304
    $reqs = array('gd', 'curl',
305
                  'xmlwriter', 'mbstring', 'xml', 'dom', 'simplexml');
306
307
    foreach ($reqs as $req) {
308
        if (!checkExtension($req)) {
309
            printf('<p class="error">Cannot load required extension: <code>%s</code></p>', $req);
310
            $pass = false;
311
        }
312
    }
313
    // Make sure we have at least one database module available
314
    global $dbModules;
315
    $missingExtensions = array();
316
    foreach ($dbModules as $type => $info) {
317
        if (!checkExtension($info['check_module'])) {
318
            $missingExtensions[] = $info['check_module'];
319
        }
320
    }
321
322
    if (count($missingExtensions) == count($dbModules)) {
323
        $req = implode(', ', $missingExtensions);
324
        printf('<p class="error">Cannot find mysql or pgsql extension. You need one or the other.');
325
        $pass = false;
326
    }
327
328
    if (!is_writable(INSTALLDIR)) {
329
        printf('<p class="error">Cannot write config file to: <code>%s</code></p>', INSTALLDIR);
330
        printf('<p>On your server, try this command: <code>chmod a+w %s</code>', INSTALLDIR);
331
        $pass = false;
332
    }
333
334
    // Check the subdirs used for file uploads
335
    $fileSubdirs = array('avatar', 'background', 'file');
336
    foreach ($fileSubdirs as $fileSubdir) {
337
        $fileFullPath = INSTALLDIR."/$fileSubdir/";
338
        if (!is_writable($fileFullPath)) {
339
            printf('<p class="error">Cannot write to %s directory: <code>%s</code></p>', $fileSubdir, $fileFullPath);
340
            printf('<p>On your server, try this command: <code>chmod a+w %s</code></p>', $fileFullPath);
341
            $pass = false;
342
        }
343
    }
344
345
    return $pass;
346
}
347
348
/**
349
 * Checks if a php extension is both installed and loaded
350
 *
351
 * @param string $name of extension to check
352
 *
353
 * @return boolean whether extension is installed and loaded
354
 */
355
function checkExtension($name)
356
{
357
    if (extension_loaded($name)) {
358
        return true;
359
    } elseif (function_exists('dl') && ini_get('enable_dl') && !ini_get('safe_mode')) {
360
        // dl will throw a fatal error if it's disabled or we're in safe mode.
361
        // More fun, it may not even exist under some SAPIs in 5.3.0 or later...
362
        $soname = $name . '.' . PHP_SHLIB_SUFFIX;
363
        if (PHP_SHLIB_SUFFIX == 'dll') {
364
            $soname = "php_" . $soname;
365
        }
366
        return @dl($soname);
367
    } else {
368
        return false;
369
    }
370
}
371
372
/**
373
 * Show list of libraries
374
 *
375
 * @return void
376
 */
377
function showLibs()
378
{
379
    global $external_libraries;
380
    $present_libraries=array();
381
    $absent_libraries=array();
382
    foreach ($external_libraries as $external_library) {
383
        if (haveExternalLibrary($external_library)) {
384
            $present_libraries[]=$external_library;
385
        } else {
386
            $absent_libraries[]=$external_library;
387
        }
388
    }
389
    echo<<<E_O_T
390
    <div class="instructions">
391
        <p>StatusNet comes bundled with a number of libraries required for the application to work. However, it is best that you use PEAR or you distribution to manage
392
        libraries instead, as they tend to provide security updates faster, and may offer improved performance.</p>
393
        <p>On Debian based distributions, such as Ubuntu, use a package manager (such as &quot;aptitude&quot;, &quot;apt-get&quot;, and &quot;synaptic&quot;) to install the package listed.</p>
394
        <p>On RPM based distributions, such as Red Hat, Fedora, CentOS, Scientific Linux, Yellow Dog Linux and Oracle Enterprise Linux, use a package manager (such as &quot;yum&quot;, &quot;apt-rpm&quot;, and &quot;up2date&quot;) to install the package listed.</p>
395
        <p>On servers without a package manager (such as Windows), or if the library is not packaged for your distribution, you can use PHP's PEAR to install the library. Simply run &quot;pear install &lt;name&gt;&quot;.</p>
396
    </div>
397
    <h2>Absent Libraries</h2>
398
    <ul id="absent_libraries">
399
E_O_T;
400
    foreach ($absent_libraries as $library) {
401
        echo '<li>';
402
        if (isset($library['url'])) {
403
            echo '<a href="'.$library['url'].'">'.htmlentities($library['name']).'</a>';
404
        } else {
405
            echo htmlentities($library['name']);
406
        }
407
        echo '<ul>';
408
        if (isset($library['deb'])) {
409
            echo '<li class="deb package">deb: <a href="apt:' . urlencode($library['deb']) . '">' . htmlentities($library['deb']) . '</a></li>';
410
        }
411
        if (isset($library['rpm'])) {
412
            echo '<li class="rpm package">rpm: ' . htmlentities($library['rpm']) . '</li>';
413
        }
414
        if (isset($library['pear'])) {
415
            echo '<li class="pear package">pear: ' . htmlentities($library['pear']) . '</li>';
416
        }
417
        echo '</ul>';
418
    }
419
    echo<<<E_O_T
420
    </ul>
421
    <h2>Installed Libraries</h2>
422
    <ul id="present_libraries">
423
E_O_T;
424
    foreach ($present_libraries as $library) {
425
        echo '<li>';
426
        if (isset($library['url'])) {
427
            echo '<a href="'.$library['url'].'">'.htmlentities($library['name']).'</a>';
428
        } else {
429
            echo htmlentities($library['name']);
430
        }
431
        echo '</li>';
432
    }
433
    echo<<<E_O_T
434
    </ul>
435
E_O_T;
436
}
437
438
/**
439
 * Helper class for building form
440
 */
441
class Posted {
442
    function value($name)
443
    {
444
        if (isset($_POST[$name])) {
445
            return htmlspecialchars(strval($_POST[$name]));
446
        } else {
447
            return '';
448
        }
449
    }
450
}
451
452
function showForm()
453
{
454
    global $dbModules;
455
    $post = new Posted();
456
    $dbRadios = '';
457
    if (isset($_POST['dbtype'])) {
458
        $dbtype = $_POST['dbtype'];
459
    } else {
460
        $dbtype = null;
461
    }
462
    foreach ($dbModules as $type => $info) {
463
        if (checkExtension($info['check_module'])) {
464
            if ($dbtype == null || $dbtype == $type) {
465
                $checked = 'checked="checked" ';
466
                $dbtype = $type; // if we didn't have one checked, hit the first
467
            } else {
468
                $checked = '';
469
            }
470
            $dbRadios .= "<input type=\"radio\" name=\"dbtype\" id=\"dbtype-$type\" value=\"$type\" $checked/> $info[name]<br />\n";
471
        }
472
    }
473
    echo<<<E_O_T
474
        </ul>
475
    </dd>
476
</dl>
477
<form method="post" action="install.php" class="form_settings" id="form_install">
478
    <fieldset>
479
        <fieldset id="settings_site">
480
            <legend>Site settings</legend>
481
            <ul class="form_data">
482
                <li>
483
                    <label for="sitename">Site name</label>
484
                    <input type="text" id="sitename" name="sitename" value="{$post->value('sitename')}" />
485
                    <p class="form_guide">The name of your site</p>
486
                </li>
487
                <li>
488
                    <label for="fancy-enable">Fancy URLs</label>
489
                    <input type="radio" name="fancy" id="fancy-enable" value="enable" checked='checked' /> enable<br />
490
                    <input type="radio" name="fancy" id="fancy-disable" value="" /> disable<br />
491
                    <p class="form_guide" id='fancy-form_guide'>Enable fancy (pretty) URLs. Auto-detection failed, it depends on Javascript.</p>
492
                </li>
493
            </ul>
494
        </fieldset>
495
496
        <fieldset id="settings_db">
497
            <legend>Database settings</legend>
498
            <ul class="form_data">
499
                <li>
500
                    <label for="host">Hostname</label>
501
                    <input type="text" id="host" name="host" value="{$post->value('host')}" />
502
                    <p class="form_guide">Database hostname</p>
503
                </li>
504
                <li>
505
                    <label for="dbtype">Type</label>
506
                    $dbRadios
507
                    <p class="form_guide">Database type</p>
508
                </li>
509
                <li>
510
                    <label for="database">Name</label>
511
                    <input type="text" id="database" name="database" value="{$post->value('database')}" />
512
                    <p class="form_guide">Database name</p>
513
                </li>
514
                <li>
515
                    <label for="dbusername">DB username</label>
516
                    <input type="text" id="dbusername" name="dbusername" value="{$post->value('dbusername')}" />
517
                    <p class="form_guide">Database username</p>
518
                </li>
519
                <li>
520
                    <label for="dbpassword">DB password</label>
521
                    <input type="password" id="dbpassword" name="dbpassword" value="{$post->value('dbpassword')}" />
522
                    <p class="form_guide">Database password (optional)</p>
523
                </li>
524
            </ul>
525
        </fieldset>
526
527
        <fieldset id="settings_admin">
528
            <legend>Administrator settings</legend>
529
            <ul class="form_data">
530
                <li>
531
                    <label for="admin_nickname">Administrator nickname</label>
532
                    <input type="text" id="admin_nickname" name="admin_nickname" value="{$post->value('admin_nickname')}" />
533
                    <p class="form_guide">Nickname for the initial StatusNet user (administrator)</p>
534
                </li>
535
                <li>
536
                    <label for="admin_password">Administrator password</label>
537
                    <input type="password" id="admin_password" name="admin_password" value="{$post->value('admin_password')}" />
538
                    <p class="form_guide">Password for the initial StatusNet user (administrator)</p>
539
                </li>
540
                <li>
541
                    <label for="admin_password2">Confirm password</label>
542
                    <input type="password" id="admin_password2" name="admin_password2" value="{$post->value('admin_password2')}" />
543
                </li>
544
                <li>
545
                    <label for="admin_email">Administrator e-mail</label>
546
                    <input id="admin_email" name="admin_email" value="{$post->value('admin_email')}" />
547
                    <p class="form_guide">Optional email address for the initial StatusNet user (administrator)</p>
548
                </li>
549
            </ul>
550
        </fieldset>
551
        <input type="submit" name="submit" class="submit" value="Submit" />
552
    </fieldset>
553
</form>
554
555
E_O_T;
556
}
557
558
function updateStatus($status, $error=false)
559
{
560
    echo '<li' . ($error ? ' class="error"': '' ) . ">$status</li>";
561
}
562
563
function handlePost()
564
{
565
    $host     = $_POST['host'];
566
    $dbtype   = $_POST['dbtype'];
567
    $database = $_POST['database'];
568
    $username = $_POST['dbusername'];
569
    $password = $_POST['dbpassword'];
570
    $sitename = $_POST['sitename'];
571
    $fancy    = !empty($_POST['fancy']);
572
573
    $adminNick = $_POST['admin_nickname'];
574
    $adminPass = $_POST['admin_password'];
575
    $adminPass2 = $_POST['admin_password2'];
576
    $adminEmail = $_POST['admin_email'];
577
578
    $server = $_SERVER['HTTP_HOST'];
579
    $path = substr(dirname($_SERVER['PHP_SELF']), 1);
580
581
    echo <<<STR
582
    <dl class="system_notice">
583
        <dt>Page notice</dt>
584
        <dd>
585
            <ul>
586
STR;
587
    $fail = false;
588
589
    if (empty($host)) {
590
        updateStatus("No hostname specified.", true);
591
        $fail = true;
592
    }
593
594
    if (empty($database)) {
595
        updateStatus("No database specified.", true);
596
        $fail = true;
597
    }
598
599
    if (empty($username)) {
600
        updateStatus("No username specified.", true);
601
        $fail = true;
602
    }
603
604
    if (empty($sitename)) {
605
        updateStatus("No sitename specified.", true);
606
        $fail = true;
607
    }
608
609
    if (empty($adminNick)) {
610
        updateStatus("No initial StatusNet user nickname specified.", true);
611
        $fail = true;
612
    }
613
614
    if (empty($adminPass)) {
615
        updateStatus("No initial StatusNet user password specified.", true);
616
        $fail = true;
617
    }
618
    
619
    if ($adminPass != $adminPass2) {
620
        updateStatus("Administrator passwords do not match. Did you mistype?", true);
621
        $fail = true;
622
    }
623
624
    if ($fail) {
625
        showForm();
626
        return;
627
    }
628
629
    global $dbModules;
630
    $db = call_user_func($dbModules[$dbtype]['installer'], $host, $database, $username, $password);
631
632
    if (!$db) {
633
        // database connection failed, do not move on to create config file.
634
        return false;
635
    }
636
637
    updateStatus("Writing config file...");
638
    $res = writeConf($sitename, $server, $path, $fancy, $db);
639
640
    if (!$res) {
641
        updateStatus("Can't write config file.", true);
642
        showForm();
643
        return;
644
    }
645
646
    // Okay, cross fingers and try to register an initial user
647
    if (registerInitialUser($adminNick, $adminPass, $adminEmail)) {
648
        updateStatus(
649
            "An initial user with the administrator role has been created."
650
        );
651
    } else {
652
        updateStatus(
653
            "Could not create initial StatusNet user (administrator).",
654
            true
655
        );
656
        showForm();
657
        return;
658
    }
659
660
    /*
661
        TODO https needs to be considered
662
    */
663
    $link = "http://".$server.'/'.$path;
664
665
    updateStatus("StatusNet has been installed at $link");
666
    updateStatus(
667
        "<strong>DONE!</strong> You can visit your <a href='$link'>new StatusNet site</a> (login as '$adminNick'). If this is your first StatusNet install, you may want to poke around our <a href='http://status.net/wiki/Getting_started'>Getting Started guide</a>."
668
    );
669
}
670
671
function Pgsql_Db_installer($host, $database, $username, $password)
672
{
673
    $connstring = "dbname=$database host=$host user=$username";
674
675
    //No password would mean trust authentication used.
676
    if (!empty($password)) {
677
        $connstring .= " password=$password";
678
    }
679
    updateStatus("Starting installation...");
680
    updateStatus("Checking database...");
681
    $conn = pg_connect($connstring);
682
683
    if ($conn ===false) {
684
        updateStatus("Failed to connect to database: $connstring");
685
        showForm();
686
        return false;
687
    }
688
689
    //ensure database encoding is UTF8
690
    $record = pg_fetch_object(pg_query($conn, 'SHOW server_encoding'));
691
    if ($record->server_encoding != 'UTF8') {
692
        updateStatus("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
693
        showForm();
694
        return false;
695
    }
696
697
    updateStatus("Running database script...");
698
    //wrap in transaction;
699
    pg_query($conn, 'BEGIN');
700
    $res = runDbScript(INSTALLDIR.'/db/statusnet_pg.sql', $conn, 'pgsql');
701
702
    if ($res === false) {
703
        updateStatus("Can't run database script.", true);
704
        showForm();
705
        return false;
706
    }
707
    foreach (array('sms_carrier' => 'SMS carrier',
708
                'notice_source' => 'notice source',
709
                'foreign_services' => 'foreign service')
710
          as $scr => $name) {
711
        updateStatus(sprintf("Adding %s data to database...", $name));
712
        $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn, 'pgsql');
713
        if ($res === false) {
714
            updateStatus(sprintf("Can't run %d script.", $name), true);
715
            showForm();
716
            return false;
717
        }
718
    }
719
    pg_query($conn, 'COMMIT');
720
721
    if (empty($password)) {
722
        $sqlUrl = "pgsql://$username@$host/$database";
723
    } else {
724
        $sqlUrl = "pgsql://$username:$password@$host/$database";
725
    }
726
727
    $db = array('type' => 'pgsql', 'database' => $sqlUrl);
728
729
    return $db;
730
}
731
732
function Mysql_Db_installer($host, $database, $username, $password)
733
{
734
    updateStatus("Starting installation...");
735
    updateStatus("Checking database...");
736
737
    $conn = mysql_connect($host, $username, $password);
738
    if (!$conn) {
739
        updateStatus("Can't connect to server '$host' as '$username'.", true);
740
        showForm();
741
        return false;
742
    }
743
    updateStatus("Changing to database...");
744
    $res = mysql_select_db($database, $conn);
745
    if (!$res) {
746
        updateStatus("Can't change to database.", true);
747
        showForm();
748
        return false;
749
    }
750
    updateStatus("Running database script...");
751
    $res = runDbScript(INSTALLDIR.'/db/statusnet.sql', $conn);
752
    if ($res === false) {
753
        updateStatus("Can't run database script.", true);
754
        showForm();
755
        return false;
756
    }
757
    foreach (array('sms_carrier' => 'SMS carrier',
758
                'notice_source' => 'notice source',
759
                'foreign_services' => 'foreign service')
760
          as $scr => $name) {
761
        updateStatus(sprintf("Adding %s data to database...", $name));
762
        $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn);
763
        if ($res === false) {
764
            updateStatus(sprintf("Can't run %d script.", $name), true);
765
            showForm();
766
            return false;
767
        }
768
    }
769
770
    $sqlUrl = "mysqli://$username:$password@$host/$database";
771
    $db = array('type' => 'mysql', 'database' => $sqlUrl);
772
    return $db;
773
}
774
775
function writeConf($sitename, $server, $path, $fancy, $db)
776
{
777
    // assemble configuration file in a string
778
    $cfg =  "<?php\n".
779
            "if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }\n\n".
780
781
            // site name
782
            "\$config['site']['name'] = '$sitename';\n\n".
783
784
            // site location
785
            "\$config['site']['server'] = '$server';\n".
786
            "\$config['site']['path'] = '$path'; \n\n".
787
788
            // checks if fancy URLs are enabled
789
            ($fancy ? "\$config['site']['fancy'] = true;\n\n":'').
790
791
            // database
792
            "\$config['db']['database'] = '{$db['database']}';\n\n".
793
            ($db['type'] == 'pgsql' ? "\$config['db']['quote_identifiers'] = true;\n\n":'').
794
            "\$config['db']['type'] = '{$db['type']}';\n\n";
795
    // write configuration file out to install directory
796
    $res = file_put_contents(INSTALLDIR.'/config.php', $cfg);
797
798
    return $res;
799
}
800
801
/**
802
 * Install schema into the database
803
 *
804
 * @param string $filename location of database schema file
805
 * @param dbconn $conn     connection to database
806
 * @param string $type     type of database, currently mysql or pgsql
807
 *
808
 * @return boolean - indicating success or failure
809
 */
810
function runDbScript($filename, $conn, $type = 'mysqli')
811
{
812
    $sql = trim(file_get_contents($filename));
813
    $stmts = explode(';', $sql);
814
    foreach ($stmts as $stmt) {
815
        $stmt = trim($stmt);
816
        if (!mb_strlen($stmt)) {
817
            continue;
818
        }
819
        // FIXME: use PEAR::DB or PDO instead of our own switch
820
        switch ($type) {
821
        case 'mysqli':
822
            $res = mysql_query($stmt, $conn);
823
            if ($res === false) {
824
                $error = mysql_error();
825
            }
826
            break;
827
        case 'pgsql':
828
            $res = pg_query($conn, $stmt);
829
            if ($res === false) {
830
                $error = pg_last_error();
831
            }
832
            break;
833
        default:
834
            updateStatus("runDbScript() error: unknown database type ". $type ." provided.");
835
        }
836
        if ($res === false) {
837
            updateStatus("ERROR ($error) for SQL '$stmt'");
838
            return $res;
839
        }
840
    }
841
    return true;
842
}
843
844
function registerInitialUser($nickname, $password, $email)
845
{
846
    define('STATUSNET', true);
847
    define('LACONICA', true); // compatibility
848
849
    require_once INSTALLDIR . '/lib/common.php';
850
851
    $data = array('nickname' => $nickname,
852
                  'password' => $password,
853
                  'fullname' => $nickname);
854
    if ($email) {
855
        $data['email'] = $email;
856
    }
857
    $user = User::register($data);
858
859
    if (empty($user)) {
860
        return false;
861
    }
862
863
    // give initial user carte blanche
864
865
    $user->grantRole('owner');
866
    $user->grantRole('moderator');
867
    $user->grantRole('administrator');
868
    
869
    // Attempt to do a remote subscribe to update@status.net
870
    // Will fail if instance is on a private network.
871
872
    if (class_exists('Ostatus_profile')) {
873
        try {
874
            $oprofile = Ostatus_profile::ensureProfile('http://update.status.net/');
875
            Subscription::start($user->getProfile(), $oprofile->localProfile());
876
            updateStatus("Set up subscription to <a href='http://update.status.net/'>update@status.net</a>.");
877
        } catch (Exception $e) {
878
            updateStatus("Could not set up subscription to <a href='http://update.status.net/'>update@status.net</a>.");
879
        }
880
    }
881
882
    return true;
883
}
884
885
?>
886
<?php echo"<?"; ?> xml version="1.0" encoding="UTF-8" <?php echo "?>"; ?>
887
<!DOCTYPE html
888
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
889
       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
890
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en_US" lang="en_US">
891
    <head>
892
        <title>Install StatusNet</title>
893
	<link rel="shortcut icon" href="favicon.ico"/>
894
        <link rel="stylesheet" type="text/css" href="theme/default/css/display.css" media="screen, projection, tv"/>
895
        <!--[if IE]><link rel="stylesheet" type="text/css" href="theme/base/css/ie.css" /><![endif]-->
896
        <!--[if lte IE 6]><link rel="stylesheet" type="text/css" theme/base/css/ie6.css" /><![endif]-->
897
        <!--[if IE]><link rel="stylesheet" type="text/css" href="theme/default/css/ie.css" /><![endif]-->
898
        <script src="js/jquery.min.js"></script>
899
        <script src="js/install.js"></script>
900
    </head>
901
    <body id="install">
902
        <div id="wrap">
903
            <div id="header">
904
                <address id="site_contact" class="vcard">
905
                    <a class="url home bookmark" href=".">
906
                        <img class="logo photo" src="theme/default/logo.png" alt="StatusNet"/>
907
                        <span class="fn org">StatusNet</span>
908
                    </a>
909
                </address>
910
            </div>
911
            <div id="core">
912
                <div id="content">
913
                     <div id="content_inner">
914
                        <h1>Install StatusNet</h1>
915
<?php main(); ?>
916
                   </div>
917
                </div>
918
            </div>
919
        </div>
920
    </body>
921
</html>