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