]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - install.php
Revert "CentOS 5.4 still bogus on a stock install." - bad debug lines crept in
[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     // Look for known library bugs
305     $str = "abcdefghijklmnopqrstuvwxyz";
306     $replaced = preg_replace('/[\p{Cc}\p{Cs}]/u', '*', $str);
307     if ($str != $replaced) {
308         printf('<p class="error">PHP is linked to a version of the PCRE library ' .
309                'that does not support Unicode properties. ' .
310                'If you are running Red Hat Enterprise Linux / ' .
311                'CentOS 5.3 or earlier, see <a href="' .
312                'http://status.net/wiki/Red_Hat_Enterprise_Linux#PCRE_library' .
313                '">our documentation page</a> on fixing this.</p>');
314         $pass = false;
315     }
316
317     $reqs = array('gd', 'curl',
318                   'xmlwriter', 'mbstring', 'xml', 'dom', 'simplexml');
319
320     foreach ($reqs as $req) {
321         if (!checkExtension($req)) {
322             printf('<p class="error">Cannot load required extension: <code>%s</code></p>', $req);
323             $pass = false;
324         }
325     }
326     // Make sure we have at least one database module available
327     global $dbModules;
328     $missingExtensions = array();
329     foreach ($dbModules as $type => $info) {
330         if (!checkExtension($info['check_module'])) {
331             $missingExtensions[] = $info['check_module'];
332         }
333     }
334
335     if (count($missingExtensions) == count($dbModules)) {
336         $req = implode(', ', $missingExtensions);
337         printf('<p class="error">Cannot find mysql or pgsql extension. You need one or the other.');
338         $pass = false;
339     }
340
341     if (!is_writable(INSTALLDIR)) {
342         printf('<p class="error">Cannot write config file to: <code>%s</code></p>', INSTALLDIR);
343         printf('<p>On your server, try this command: <code>chmod a+w %s</code>', INSTALLDIR);
344         $pass = false;
345     }
346
347     // Check the subdirs used for file uploads
348     $fileSubdirs = array('avatar', 'background', 'file');
349     foreach ($fileSubdirs as $fileSubdir) {
350         $fileFullPath = INSTALLDIR."/$fileSubdir/";
351         if (!is_writable($fileFullPath)) {
352             printf('<p class="error">Cannot write to %s directory: <code>%s</code></p>', $fileSubdir, $fileFullPath);
353             printf('<p>On your server, try this command: <code>chmod a+w %s</code></p>', $fileFullPath);
354             $pass = false;
355         }
356     }
357
358     return $pass;
359 }
360
361 /**
362  * Checks if a php extension is both installed and loaded
363  *
364  * @param string $name of extension to check
365  *
366  * @return boolean whether extension is installed and loaded
367  */
368 function checkExtension($name)
369 {
370     if (extension_loaded($name)) {
371         return true;
372     } elseif (function_exists('dl') && ini_get('enable_dl') && !ini_get('safe_mode')) {
373         // dl will throw a fatal error if it's disabled or we're in safe mode.
374         // More fun, it may not even exist under some SAPIs in 5.3.0 or later...
375         $soname = $name . '.' . PHP_SHLIB_SUFFIX;
376         if (PHP_SHLIB_SUFFIX == 'dll') {
377             $soname = "php_" . $soname;
378         }
379         return @dl($soname);
380     } else {
381         return false;
382     }
383 }
384
385 /**
386  * Show list of libraries
387  *
388  * @return void
389  */
390 function showLibs()
391 {
392     global $external_libraries;
393     $present_libraries=array();
394     $absent_libraries=array();
395     foreach ($external_libraries as $external_library) {
396         if (haveExternalLibrary($external_library)) {
397             $present_libraries[]=$external_library;
398         } else {
399             $absent_libraries[]=$external_library;
400         }
401     }
402     echo<<<E_O_T
403     <div class="instructions">
404         <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
405         libraries instead, as they tend to provide security updates faster, and may offer improved performance.</p>
406         <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>
407         <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>
408         <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>
409     </div>
410     <h2>Absent Libraries</h2>
411     <ul id="absent_libraries">
412 E_O_T;
413     foreach ($absent_libraries as $library) {
414         echo '<li>';
415         if (isset($library['url'])) {
416             echo '<a href="'.$library['url'].'">'.htmlentities($library['name']).'</a>';
417         } else {
418             echo htmlentities($library['name']);
419         }
420         echo '<ul>';
421         if (isset($library['deb'])) {
422             echo '<li class="deb package">deb: <a href="apt:' . urlencode($library['deb']) . '">' . htmlentities($library['deb']) . '</a></li>';
423         }
424         if (isset($library['rpm'])) {
425             echo '<li class="rpm package">rpm: ' . htmlentities($library['rpm']) . '</li>';
426         }
427         if (isset($library['pear'])) {
428             echo '<li class="pear package">pear: ' . htmlentities($library['pear']) . '</li>';
429         }
430         echo '</ul>';
431     }
432     echo<<<E_O_T
433     </ul>
434     <h2>Installed Libraries</h2>
435     <ul id="present_libraries">
436 E_O_T;
437     foreach ($present_libraries as $library) {
438         echo '<li>';
439         if (isset($library['url'])) {
440             echo '<a href="'.$library['url'].'">'.htmlentities($library['name']).'</a>';
441         } else {
442             echo htmlentities($library['name']);
443         }
444         echo '</li>';
445     }
446     echo<<<E_O_T
447     </ul>
448 E_O_T;
449 }
450
451 /**
452  * Helper class for building form
453  */
454 class Posted {
455     function value($name)
456     {
457         if (isset($_POST[$name])) {
458             return htmlspecialchars(strval($_POST[$name]));
459         } else {
460             return '';
461         }
462     }
463 }
464
465 function showForm()
466 {
467     global $dbModules;
468     $post = new Posted();
469     $dbRadios = '';
470     if (isset($_POST['dbtype'])) {
471         $dbtype = $_POST['dbtype'];
472     } else {
473         $dbtype = null;
474     }
475     foreach ($dbModules as $type => $info) {
476         if (checkExtension($info['check_module'])) {
477             if ($dbtype == null || $dbtype == $type) {
478                 $checked = 'checked="checked" ';
479                 $dbtype = $type; // if we didn't have one checked, hit the first
480             } else {
481                 $checked = '';
482             }
483             $dbRadios .= "<input type=\"radio\" name=\"dbtype\" id=\"dbtype-$type\" value=\"$type\" $checked/> $info[name]<br />\n";
484         }
485     }
486     echo<<<E_O_T
487         </ul>
488     </dd>
489 </dl>
490 <form method="post" action="install.php" class="form_settings" id="form_install">
491     <fieldset>
492         <fieldset id="settings_site">
493             <legend>Site settings</legend>
494             <ul class="form_data">
495                 <li>
496                     <label for="sitename">Site name</label>
497                     <input type="text" id="sitename" name="sitename" value="{$post->value('sitename')}" />
498                     <p class="form_guide">The name of your site</p>
499                 </li>
500                 <li>
501                     <label for="fancy-enable">Fancy URLs</label>
502                     <input type="radio" name="fancy" id="fancy-enable" value="enable" checked='checked' /> enable<br />
503                     <input type="radio" name="fancy" id="fancy-disable" value="" /> disable<br />
504                     <p class="form_guide" id='fancy-form_guide'>Enable fancy (pretty) URLs. Auto-detection failed, it depends on Javascript.</p>
505                 </li>
506             </ul>
507         </fieldset>
508
509         <fieldset id="settings_db">
510             <legend>Database settings</legend>
511             <ul class="form_data">
512                 <li>
513                     <label for="host">Hostname</label>
514                     <input type="text" id="host" name="host" value="{$post->value('host')}" />
515                     <p class="form_guide">Database hostname</p>
516                 </li>
517                 <li>
518                     <label for="dbtype">Type</label>
519                     $dbRadios
520                     <p class="form_guide">Database type</p>
521                 </li>
522                 <li>
523                     <label for="database">Name</label>
524                     <input type="text" id="database" name="database" value="{$post->value('database')}" />
525                     <p class="form_guide">Database name</p>
526                 </li>
527                 <li>
528                     <label for="dbusername">DB username</label>
529                     <input type="text" id="dbusername" name="dbusername" value="{$post->value('dbusername')}" />
530                     <p class="form_guide">Database username</p>
531                 </li>
532                 <li>
533                     <label for="dbpassword">DB password</label>
534                     <input type="password" id="dbpassword" name="dbpassword" value="{$post->value('dbpassword')}" />
535                     <p class="form_guide">Database password (optional)</p>
536                 </li>
537             </ul>
538         </fieldset>
539
540         <fieldset id="settings_admin">
541             <legend>Administrator settings</legend>
542             <ul class="form_data">
543                 <li>
544                     <label for="admin_nickname">Administrator nickname</label>
545                     <input type="text" id="admin_nickname" name="admin_nickname" value="{$post->value('admin_nickname')}" />
546                     <p class="form_guide">Nickname for the initial StatusNet user (administrator)</p>
547                 </li>
548                 <li>
549                     <label for="admin_password">Administrator password</label>
550                     <input type="password" id="admin_password" name="admin_password" value="{$post->value('admin_password')}" />
551                     <p class="form_guide">Password for the initial StatusNet user (administrator)</p>
552                 </li>
553                 <li>
554                     <label for="admin_password2">Confirm password</label>
555                     <input type="password" id="admin_password2" name="admin_password2" value="{$post->value('admin_password2')}" />
556                 </li>
557                 <li>
558                     <label for="admin_email">Administrator e-mail</label>
559                     <input id="admin_email" name="admin_email" value="{$post->value('admin_email')}" />
560                     <p class="form_guide">Optional email address for the initial StatusNet user (administrator)</p>
561                 </li>
562             </ul>
563         </fieldset>
564         <input type="submit" name="submit" class="submit" value="Submit" />
565     </fieldset>
566 </form>
567
568 E_O_T;
569 }
570
571 function updateStatus($status, $error=false)
572 {
573     echo '<li' . ($error ? ' class="error"': '' ) . ">$status</li>";
574 }
575
576 function handlePost()
577 {
578     $host     = $_POST['host'];
579     $dbtype   = $_POST['dbtype'];
580     $database = $_POST['database'];
581     $username = $_POST['dbusername'];
582     $password = $_POST['dbpassword'];
583     $sitename = $_POST['sitename'];
584     $fancy    = !empty($_POST['fancy']);
585
586     $adminNick = $_POST['admin_nickname'];
587     $adminPass = $_POST['admin_password'];
588     $adminPass2 = $_POST['admin_password2'];
589     $adminEmail = $_POST['admin_email'];
590
591     $server = $_SERVER['HTTP_HOST'];
592     $path = substr(dirname($_SERVER['PHP_SELF']), 1);
593
594     echo <<<STR
595     <dl class="system_notice">
596         <dt>Page notice</dt>
597         <dd>
598             <ul>
599 STR;
600     $fail = false;
601
602     if (empty($host)) {
603         updateStatus("No hostname specified.", true);
604         $fail = true;
605     }
606
607     if (empty($database)) {
608         updateStatus("No database specified.", true);
609         $fail = true;
610     }
611
612     if (empty($username)) {
613         updateStatus("No username specified.", true);
614         $fail = true;
615     }
616
617     if (empty($sitename)) {
618         updateStatus("No sitename specified.", true);
619         $fail = true;
620     }
621
622     if (empty($adminNick)) {
623         updateStatus("No initial StatusNet user nickname specified.", true);
624         $fail = true;
625     }
626
627     if (empty($adminPass)) {
628         updateStatus("No initial StatusNet user password specified.", true);
629         $fail = true;
630     }
631     
632     if ($adminPass != $adminPass2) {
633         updateStatus("Administrator passwords do not match. Did you mistype?", true);
634         $fail = true;
635     }
636
637     if ($fail) {
638         showForm();
639         return;
640     }
641
642     global $dbModules;
643     $db = call_user_func($dbModules[$dbtype]['installer'], $host, $database, $username, $password);
644
645     if (!$db) {
646         // database connection failed, do not move on to create config file.
647         return false;
648     }
649
650     updateStatus("Writing config file...");
651     $res = writeConf($sitename, $server, $path, $fancy, $db);
652
653     if (!$res) {
654         updateStatus("Can't write config file.", true);
655         showForm();
656         return;
657     }
658
659     // Okay, cross fingers and try to register an initial user
660     if (registerInitialUser($adminNick, $adminPass, $adminEmail)) {
661         updateStatus(
662             "An initial user with the administrator role has been created."
663         );
664     } else {
665         updateStatus(
666             "Could not create initial StatusNet user (administrator).",
667             true
668         );
669         showForm();
670         return;
671     }
672
673     /*
674         TODO https needs to be considered
675     */
676     $link = "http://".$server.'/'.$path;
677
678     updateStatus("StatusNet has been installed at $link");
679     updateStatus(
680         "<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>."
681     );
682 }
683
684 function Pgsql_Db_installer($host, $database, $username, $password)
685 {
686     $connstring = "dbname=$database host=$host user=$username";
687
688     //No password would mean trust authentication used.
689     if (!empty($password)) {
690         $connstring .= " password=$password";
691     }
692     updateStatus("Starting installation...");
693     updateStatus("Checking database...");
694     $conn = pg_connect($connstring);
695
696     if ($conn ===false) {
697         updateStatus("Failed to connect to database: $connstring");
698         showForm();
699         return false;
700     }
701
702     //ensure database encoding is UTF8
703     $record = pg_fetch_object(pg_query($conn, 'SHOW server_encoding'));
704     if ($record->server_encoding != 'UTF8') {
705         updateStatus("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
706         showForm();
707         return false;
708     }
709
710     updateStatus("Running database script...");
711     //wrap in transaction;
712     pg_query($conn, 'BEGIN');
713     $res = runDbScript(INSTALLDIR.'/db/statusnet_pg.sql', $conn, 'pgsql');
714
715     if ($res === false) {
716         updateStatus("Can't run database script.", true);
717         showForm();
718         return false;
719     }
720     foreach (array('sms_carrier' => 'SMS carrier',
721                 'notice_source' => 'notice source',
722                 'foreign_services' => 'foreign service')
723           as $scr => $name) {
724         updateStatus(sprintf("Adding %s data to database...", $name));
725         $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn, 'pgsql');
726         if ($res === false) {
727             updateStatus(sprintf("Can't run %d script.", $name), true);
728             showForm();
729             return false;
730         }
731     }
732     pg_query($conn, 'COMMIT');
733
734     if (empty($password)) {
735         $sqlUrl = "pgsql://$username@$host/$database";
736     } else {
737         $sqlUrl = "pgsql://$username:$password@$host/$database";
738     }
739
740     $db = array('type' => 'pgsql', 'database' => $sqlUrl);
741
742     return $db;
743 }
744
745 function Mysql_Db_installer($host, $database, $username, $password)
746 {
747     updateStatus("Starting installation...");
748     updateStatus("Checking database...");
749
750     $conn = mysql_connect($host, $username, $password);
751     if (!$conn) {
752         updateStatus("Can't connect to server '$host' as '$username'.", true);
753         showForm();
754         return false;
755     }
756     updateStatus("Changing to database...");
757     $res = mysql_select_db($database, $conn);
758     if (!$res) {
759         updateStatus("Can't change to database.", true);
760         showForm();
761         return false;
762     }
763     updateStatus("Running database script...");
764     $res = runDbScript(INSTALLDIR.'/db/statusnet.sql', $conn);
765     if ($res === false) {
766         updateStatus("Can't run database script.", true);
767         showForm();
768         return false;
769     }
770     foreach (array('sms_carrier' => 'SMS carrier',
771                 'notice_source' => 'notice source',
772                 'foreign_services' => 'foreign service')
773           as $scr => $name) {
774         updateStatus(sprintf("Adding %s data to database...", $name));
775         $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn);
776         if ($res === false) {
777             updateStatus(sprintf("Can't run %d script.", $name), true);
778             showForm();
779             return false;
780         }
781     }
782
783     $sqlUrl = "mysqli://$username:$password@$host/$database";
784     $db = array('type' => 'mysql', 'database' => $sqlUrl);
785     return $db;
786 }
787
788 function writeConf($sitename, $server, $path, $fancy, $db)
789 {
790     // assemble configuration file in a string
791     $cfg =  "<?php\n".
792             "if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }\n\n".
793
794             // site name
795             "\$config['site']['name'] = '$sitename';\n\n".
796
797             // site location
798             "\$config['site']['server'] = '$server';\n".
799             "\$config['site']['path'] = '$path'; \n\n".
800
801             // checks if fancy URLs are enabled
802             ($fancy ? "\$config['site']['fancy'] = true;\n\n":'').
803
804             // database
805             "\$config['db']['database'] = '{$db['database']}';\n\n".
806             ($db['type'] == 'pgsql' ? "\$config['db']['quote_identifiers'] = true;\n\n":'').
807             "\$config['db']['type'] = '{$db['type']}';\n\n";
808     // write configuration file out to install directory
809     $res = file_put_contents(INSTALLDIR.'/config.php', $cfg);
810
811     return $res;
812 }
813
814 /**
815  * Install schema into the database
816  *
817  * @param string $filename location of database schema file
818  * @param dbconn $conn     connection to database
819  * @param string $type     type of database, currently mysql or pgsql
820  *
821  * @return boolean - indicating success or failure
822  */
823 function runDbScript($filename, $conn, $type = 'mysqli')
824 {
825     $sql = trim(file_get_contents($filename));
826     $stmts = explode(';', $sql);
827     foreach ($stmts as $stmt) {
828         $stmt = trim($stmt);
829         if (!mb_strlen($stmt)) {
830             continue;
831         }
832         // FIXME: use PEAR::DB or PDO instead of our own switch
833         switch ($type) {
834         case 'mysqli':
835             $res = mysql_query($stmt, $conn);
836             if ($res === false) {
837                 $error = mysql_error();
838             }
839             break;
840         case 'pgsql':
841             $res = pg_query($conn, $stmt);
842             if ($res === false) {
843                 $error = pg_last_error();
844             }
845             break;
846         default:
847             updateStatus("runDbScript() error: unknown database type ". $type ." provided.");
848         }
849         if ($res === false) {
850             updateStatus("ERROR ($error) for SQL '$stmt'");
851             return $res;
852         }
853     }
854     return true;
855 }
856
857 function registerInitialUser($nickname, $password, $email)
858 {
859     define('STATUSNET', true);
860     define('LACONICA', true); // compatibility
861
862     require_once INSTALLDIR . '/lib/common.php';
863
864     $data = array('nickname' => $nickname,
865                   'password' => $password,
866                   'fullname' => $nickname);
867     if ($email) {
868         $data['email'] = $email;
869     }
870     $user = User::register($data);
871
872     if (empty($user)) {
873         return false;
874     }
875
876     // give initial user carte blanche
877
878     $user->grantRole('owner');
879     $user->grantRole('moderator');
880     $user->grantRole('administrator');
881     
882     // Attempt to do a remote subscribe to update@status.net
883     // Will fail if instance is on a private network.
884
885     if (class_exists('Ostatus_profile')) {
886         try {
887             $oprofile = Ostatus_profile::ensureProfile('http://update.status.net/');
888             Subscription::start($user->getProfile(), $oprofile->localProfile());
889             updateStatus("Set up subscription to <a href='http://update.status.net/'>update@status.net</a>.");
890         } catch (Exception $e) {
891             updateStatus("Could not set up subscription to <a href='http://update.status.net/'>update@status.net</a>.");
892         }
893     }
894
895     return true;
896 }
897
898 ?>
899 <?php echo"<?"; ?> xml version="1.0" encoding="UTF-8" <?php echo "?>"; ?>
900 <!DOCTYPE html
901 PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
902        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
903 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en_US" lang="en_US">
904     <head>
905         <title>Install StatusNet</title>
906         <link rel="shortcut icon" href="favicon.ico"/>
907         <link rel="stylesheet" type="text/css" href="theme/default/css/display.css" media="screen, projection, tv"/>
908         <!--[if IE]><link rel="stylesheet" type="text/css" href="theme/base/css/ie.css" /><![endif]-->
909         <!--[if lte IE 6]><link rel="stylesheet" type="text/css" theme/base/css/ie6.css" /><![endif]-->
910         <!--[if IE]><link rel="stylesheet" type="text/css" href="theme/default/css/ie.css" /><![endif]-->
911         <script src="js/jquery.min.js"></script>
912         <script src="js/install.js"></script>
913     </head>
914     <body id="install">
915         <div id="wrap">
916             <div id="header">
917                 <address id="site_contact" class="vcard">
918                     <a class="url home bookmark" href=".">
919                         <img class="logo photo" src="theme/default/logo.png" alt="StatusNet"/>
920                         <span class="fn org">StatusNet</span>
921                     </a>
922                 </address>
923             </div>
924             <div id="core">
925                 <div id="content">
926                      <div id="content_inner">
927                         <h1>Install StatusNet</h1>
928 <?php main(); ?>
929                    </div>
930                 </div>
931             </div>
932         </div>
933     </body>
934 </html>