]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - install.php
Merge branch 'testing' of git@gitorious.org:statusnet/mainline into testing
[quix0rs-gnu-social.git] / install.php
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>