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