]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - install.php
Add a User-Agent fragment blacklist to MobileProfile: sticking iPad on the regular...
[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.4 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
487     echo<<<E_O_T
488         </ul>
489     </dd>
490 </dl>
491 <form method="post" action="install.php" class="form_settings" id="form_install">
492     <fieldset>
493         <fieldset id="settings_site">
494             <legend>Site settings</legend>
495             <ul class="form_data">
496                 <li>
497                     <label for="sitename">Site name</label>
498                     <input type="text" id="sitename" name="sitename" value="{$post->value('sitename')}" />
499                     <p class="form_guide">The name of your site</p>
500                 </li>
501                 <li>
502                     <label for="fancy-enable">Fancy URLs</label>
503                     <input type="radio" name="fancy" id="fancy-enable" value="enable" checked='checked' /> enable<br />
504                     <input type="radio" name="fancy" id="fancy-disable" value="" /> disable<br />
505                     <p class="form_guide" id='fancy-form_guide'>Enable fancy (pretty) URLs. Auto-detection failed, it depends on Javascript.</p>
506                 </li>
507             </ul>
508         </fieldset>
509
510         <fieldset id="settings_db">
511             <legend>Database settings</legend>
512             <ul class="form_data">
513                 <li>
514                     <label for="host">Hostname</label>
515                     <input type="text" id="host" name="host" value="{$post->value('host')}" />
516                     <p class="form_guide">Database hostname</p>
517                 </li>
518                 <li>
519                     <label for="dbtype">Type</label>
520                     $dbRadios
521                     <p class="form_guide">Database type</p>
522                 </li>
523                 <li>
524                     <label for="database">Name</label>
525                     <input type="text" id="database" name="database" value="{$post->value('database')}" />
526                     <p class="form_guide">Database name</p>
527                 </li>
528                 <li>
529                     <label for="dbusername">DB username</label>
530                     <input type="text" id="dbusername" name="dbusername" value="{$post->value('dbusername')}" />
531                     <p class="form_guide">Database username</p>
532                 </li>
533                 <li>
534                     <label for="dbpassword">DB password</label>
535                     <input type="password" id="dbpassword" name="dbpassword" value="{$post->value('dbpassword')}" />
536                     <p class="form_guide">Database password (optional)</p>
537                 </li>
538             </ul>
539         </fieldset>
540
541         <fieldset id="settings_admin">
542             <legend>Administrator settings</legend>
543             <ul class="form_data">
544                 <li>
545                     <label for="admin_nickname">Administrator nickname</label>
546                     <input type="text" id="admin_nickname" name="admin_nickname" value="{$post->value('admin_nickname')}" />
547                     <p class="form_guide">Nickname for the initial StatusNet user (administrator)</p>
548                 </li>
549                 <li>
550                     <label for="admin_password">Administrator password</label>
551                     <input type="password" id="admin_password" name="admin_password" value="{$post->value('admin_password')}" />
552                     <p class="form_guide">Password for the initial StatusNet user (administrator)</p>
553                 </li>
554                 <li>
555                     <label for="admin_password2">Confirm password</label>
556                     <input type="password" id="admin_password2" name="admin_password2" value="{$post->value('admin_password2')}" />
557                 </li>
558                 <li>
559                     <label for="admin_email">Administrator e-mail</label>
560                     <input id="admin_email" name="admin_email" value="{$post->value('admin_email')}" />
561                     <p class="form_guide">Optional email address for the initial StatusNet user (administrator)</p>
562                 </li>
563                 <li>
564                     <label for="admin_updates">Subscribe to announcements</label>
565                     <input type="checkbox" id="admin_updates" name="admin_updates" value="true" checked="checked" />
566                     <p class="form_guide">Release and security feed from <a href="http://update.status.net/">update@status.net</a> (recommended)</p>
567                 </li>
568             </ul>
569         </fieldset>
570         <input type="submit" name="submit" class="submit" value="Submit" />
571     </fieldset>
572 </form>
573
574 E_O_T;
575 }
576
577 function updateStatus($status, $error=false)
578 {
579     echo '<li' . ($error ? ' class="error"': '' ) . ">$status</li>";
580 }
581
582 function handlePost()
583 {
584     $host     = $_POST['host'];
585     $dbtype   = $_POST['dbtype'];
586     $database = $_POST['database'];
587     $username = $_POST['dbusername'];
588     $password = $_POST['dbpassword'];
589     $sitename = $_POST['sitename'];
590     $fancy    = !empty($_POST['fancy']);
591
592     $adminNick = strtolower($_POST['admin_nickname']);
593     $adminPass = $_POST['admin_password'];
594     $adminPass2 = $_POST['admin_password2'];
595     $adminEmail = $_POST['admin_email'];
596     $adminUpdates = $_POST['admin_updates'];
597
598     $server = $_SERVER['HTTP_HOST'];
599     $path = substr(dirname($_SERVER['PHP_SELF']), 1);
600
601     echo <<<STR
602     <dl class="system_notice">
603         <dt>Page notice</dt>
604         <dd>
605             <ul>
606 STR;
607     $fail = false;
608
609     if (empty($host)) {
610         updateStatus("No hostname specified.", true);
611         $fail = true;
612     }
613
614     if (empty($database)) {
615         updateStatus("No database specified.", true);
616         $fail = true;
617     }
618
619     if (empty($username)) {
620         updateStatus("No username specified.", true);
621         $fail = true;
622     }
623
624     if (empty($sitename)) {
625         updateStatus("No sitename specified.", true);
626         $fail = true;
627     }
628
629     if (empty($adminNick)) {
630         updateStatus("No initial StatusNet user nickname specified.", true);
631         $fail = true;
632     }
633     if ($adminNick && !preg_match('/^[0-9a-z]{1,64}$/', $adminNick)) {
634         updateStatus('The user nickname "' . htmlspecialchars($adminNick) .
635                      '" is invalid; should be plain letters and numbers no longer than 64 characters.', true);
636         $fail = true;
637     }
638     // @fixme hardcoded list; should use User::allowed_nickname()
639     // if/when it's safe to have loaded the infrastructure here
640     $blacklist = array('main', 'admin', 'twitter', 'settings', 'rsd.xml', 'favorited', 'featured', 'favoritedrss', 'featuredrss', 'rss', 'getfile', 'api', 'groups', 'group', 'peopletag', 'tag', 'user', 'message', 'conversation', 'bookmarklet', 'notice', 'attachment', 'search', 'index.php', 'doc', 'opensearch', 'robots.txt', 'xd_receiver.html', 'facebook');
641     if (in_array($adminNick, $blacklist)) {
642         updateStatus('The user nickname "' . htmlspecialchars($adminNick) .
643                      '" is reserved.', true);
644         $fail = true;
645     }
646
647     if (empty($adminPass)) {
648         updateStatus("No initial StatusNet user password specified.", true);
649         $fail = true;
650     }
651     
652     if ($adminPass != $adminPass2) {
653         updateStatus("Administrator passwords do not match. Did you mistype?", true);
654         $fail = true;
655     }
656
657     if ($fail) {
658         showForm();
659         return;
660     }
661
662     global $dbModules;
663     $db = call_user_func($dbModules[$dbtype]['installer'], $host, $database, $username, $password);
664
665     if (!$db) {
666         // database connection failed, do not move on to create config file.
667         return false;
668     }
669
670     updateStatus("Writing config file...");
671     $res = writeConf($sitename, $server, $path, $fancy, $db);
672
673     if (!$res) {
674         updateStatus("Can't write config file.", true);
675         showForm();
676         return;
677     }
678
679     // Okay, cross fingers and try to register an initial user
680     if (registerInitialUser($adminNick, $adminPass, $adminEmail, $adminUpdates)) {
681         updateStatus(
682             "An initial user with the administrator role has been created."
683         );
684     } else {
685         updateStatus(
686             "Could not create initial StatusNet user (administrator).",
687             true
688         );
689         showForm();
690         return;
691     }
692
693     /*
694         TODO https needs to be considered
695     */
696     $link = "http://".$server.'/'.$path;
697
698     updateStatus("StatusNet has been installed at $link");
699     updateStatus(
700         "<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>."
701     );
702 }
703
704 function Pgsql_Db_installer($host, $database, $username, $password)
705 {
706     $connstring = "dbname=$database host=$host user=$username";
707
708     //No password would mean trust authentication used.
709     if (!empty($password)) {
710         $connstring .= " password=$password";
711     }
712     updateStatus("Starting installation...");
713     updateStatus("Checking database...");
714     $conn = pg_connect($connstring);
715
716     if ($conn ===false) {
717         updateStatus("Failed to connect to database: $connstring");
718         showForm();
719         return false;
720     }
721
722     //ensure database encoding is UTF8
723     $record = pg_fetch_object(pg_query($conn, 'SHOW server_encoding'));
724     if ($record->server_encoding != 'UTF8') {
725         updateStatus("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
726         showForm();
727         return false;
728     }
729
730     updateStatus("Running database script...");
731     //wrap in transaction;
732     pg_query($conn, 'BEGIN');
733     $res = runDbScript(INSTALLDIR.'/db/statusnet_pg.sql', $conn, 'pgsql');
734
735     if ($res === false) {
736         updateStatus("Can't run database script.", true);
737         showForm();
738         return false;
739     }
740     foreach (array('sms_carrier' => 'SMS carrier',
741                 'notice_source' => 'notice source',
742                 'foreign_services' => 'foreign service')
743           as $scr => $name) {
744         updateStatus(sprintf("Adding %s data to database...", $name));
745         $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn, 'pgsql');
746         if ($res === false) {
747             updateStatus(sprintf("Can't run %d script.", $name), true);
748             showForm();
749             return false;
750         }
751     }
752     pg_query($conn, 'COMMIT');
753
754     if (empty($password)) {
755         $sqlUrl = "pgsql://$username@$host/$database";
756     } else {
757         $sqlUrl = "pgsql://$username:$password@$host/$database";
758     }
759
760     $db = array('type' => 'pgsql', 'database' => $sqlUrl);
761
762     return $db;
763 }
764
765 function Mysql_Db_installer($host, $database, $username, $password)
766 {
767     updateStatus("Starting installation...");
768     updateStatus("Checking database...");
769
770     $conn = mysql_connect($host, $username, $password);
771     if (!$conn) {
772         updateStatus("Can't connect to server '$host' as '$username'.", true);
773         showForm();
774         return false;
775     }
776     updateStatus("Changing to database...");
777     $res = mysql_select_db($database, $conn);
778     if (!$res) {
779         updateStatus("Can't change to database.", true);
780         showForm();
781         return false;
782     }
783     updateStatus("Running database script...");
784     $res = runDbScript(INSTALLDIR.'/db/statusnet.sql', $conn);
785     if ($res === false) {
786         updateStatus("Can't run database script.", true);
787         showForm();
788         return false;
789     }
790     foreach (array('sms_carrier' => 'SMS carrier',
791                 'notice_source' => 'notice source',
792                 'foreign_services' => 'foreign service')
793           as $scr => $name) {
794         updateStatus(sprintf("Adding %s data to database...", $name));
795         $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn);
796         if ($res === false) {
797             updateStatus(sprintf("Can't run %d script.", $name), true);
798             showForm();
799             return false;
800         }
801     }
802
803     $sqlUrl = "mysqli://$username:$password@$host/$database";
804     $db = array('type' => 'mysql', 'database' => $sqlUrl);
805     return $db;
806 }
807
808 function writeConf($sitename, $server, $path, $fancy, $db)
809 {
810     // assemble configuration file in a string
811     $cfg =  "<?php\n".
812             "if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }\n\n".
813
814             // site name
815             "\$config['site']['name'] = '$sitename';\n\n".
816
817             // site location
818             "\$config['site']['server'] = '$server';\n".
819             "\$config['site']['path'] = '$path'; \n\n".
820
821             // checks if fancy URLs are enabled
822             ($fancy ? "\$config['site']['fancy'] = true;\n\n":'').
823
824             // database
825             "\$config['db']['database'] = '{$db['database']}';\n\n".
826             ($db['type'] == 'pgsql' ? "\$config['db']['quote_identifiers'] = true;\n\n":'').
827             "\$config['db']['type'] = '{$db['type']}';\n\n";
828     // write configuration file out to install directory
829     $res = file_put_contents(INSTALLDIR.'/config.php', $cfg);
830
831     return $res;
832 }
833
834 /**
835  * Install schema into the database
836  *
837  * @param string $filename location of database schema file
838  * @param dbconn $conn     connection to database
839  * @param string $type     type of database, currently mysql or pgsql
840  *
841  * @return boolean - indicating success or failure
842  */
843 function runDbScript($filename, $conn, $type = 'mysqli')
844 {
845     $sql = trim(file_get_contents($filename));
846     $stmts = explode(';', $sql);
847     foreach ($stmts as $stmt) {
848         $stmt = trim($stmt);
849         if (!mb_strlen($stmt)) {
850             continue;
851         }
852         // FIXME: use PEAR::DB or PDO instead of our own switch
853         switch ($type) {
854         case 'mysqli':
855             $res = mysql_query($stmt, $conn);
856             if ($res === false) {
857                 $error = mysql_error();
858             }
859             break;
860         case 'pgsql':
861             $res = pg_query($conn, $stmt);
862             if ($res === false) {
863                 $error = pg_last_error();
864             }
865             break;
866         default:
867             updateStatus("runDbScript() error: unknown database type ". $type ." provided.");
868         }
869         if ($res === false) {
870             updateStatus("ERROR ($error) for SQL '$stmt'");
871             return $res;
872         }
873     }
874     return true;
875 }
876
877 function registerInitialUser($nickname, $password, $email, $adminUpdates)
878 {
879     define('STATUSNET', true);
880     define('LACONICA', true); // compatibility
881
882     require_once INSTALLDIR . '/lib/common.php';
883
884     $data = array('nickname' => $nickname,
885                   'password' => $password,
886                   'fullname' => $nickname);
887     if ($email) {
888         $data['email'] = $email;
889     }
890     $user = User::register($data);
891
892     if (empty($user)) {
893         return false;
894     }
895
896     // give initial user carte blanche
897
898     $user->grantRole('owner');
899     $user->grantRole('moderator');
900     $user->grantRole('administrator');
901     
902     // Attempt to do a remote subscribe to update@status.net
903     // Will fail if instance is on a private network.
904
905     if (class_exists('Ostatus_profile') && $adminUpdates) {
906         try {
907             $oprofile = Ostatus_profile::ensureProfile('http://update.status.net/');
908             Subscription::start($user->getProfile(), $oprofile->localProfile());
909             updateStatus("Set up subscription to <a href='http://update.status.net/'>update@status.net</a>.");
910         } catch (Exception $e) {
911             updateStatus("Could not set up subscription to <a href='http://update.status.net/'>update@status.net</a>.");
912         }
913     }
914
915     return true;
916 }
917
918 ?>
919 <?php echo"<?"; ?> xml version="1.0" encoding="UTF-8" <?php echo "?>"; ?>
920 <!DOCTYPE html
921 PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
922        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
923 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en_US" lang="en_US">
924     <head>
925         <title>Install StatusNet</title>
926         <link rel="shortcut icon" href="favicon.ico"/>
927         <link rel="stylesheet" type="text/css" href="theme/default/css/display.css" media="screen, projection, tv"/>
928         <!--[if IE]><link rel="stylesheet" type="text/css" href="theme/base/css/ie.css" /><![endif]-->
929         <!--[if lte IE 6]><link rel="stylesheet" type="text/css" theme/base/css/ie6.css" /><![endif]-->
930         <!--[if IE]><link rel="stylesheet" type="text/css" href="theme/default/css/ie.css" /><![endif]-->
931         <script src="js/jquery.min.js"></script>
932         <script src="js/install.js"></script>
933     </head>
934     <body id="install">
935         <div id="wrap">
936             <div id="header">
937                 <address id="site_contact" class="vcard">
938                     <a class="url home bookmark" href=".">
939                         <img class="logo photo" src="theme/default/logo.png" alt="StatusNet"/>
940                         <span class="fn org">StatusNet</span>
941                     </a>
942                 </address>
943             </div>
944             <div id="core">
945                 <div id="content">
946                      <div id="content_inner">
947                         <h1>Install StatusNet</h1>
948 <?php main(); ?>
949                    </div>
950                 </div>
951             </div>
952         </div>
953     </body>
954 </html>