install.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. <?php
  2. // This file is part of GNU social - https://www.gnu.org/software/social
  3. //
  4. // GNU social is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Affero General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // GNU social is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Affero General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Affero General Public License
  15. // along with GNU social. If not, see <http://www.gnu.org/licenses/>.
  16. /**
  17. * Web Installer
  18. *
  19. * @package Installation
  20. * @author Adrian Lang <mail@adrianlang.de>
  21. * @author Brenda Wallace <shiny@cpan.org>
  22. * @author Brett Taylor <brett@webfroot.co.nz>
  23. * @author Brion Vibber <brion@pobox.com>
  24. * @author CiaranG <ciaran@ciarang.com>
  25. * @author Craig Andrews <candrews@integralblue.com>
  26. * @author Eric Helgeson <helfire@Erics-MBP.local>
  27. * @author Evan Prodromou <evan@status.net>
  28. * @author Mikael Nordfeldth <mmn@hethane.se>
  29. * @author Robin Millette <millette@controlyourself.ca>
  30. * @author Sarven Capadisli <csarven@status.net>
  31. * @author Tom Adams <tom@holizz.com>
  32. * @author Zach Copley <zach@status.net>
  33. * @author Diogo Cordeiro <diogo@fc.up.pt>
  34. * @author Kim <kim@aficat.com>
  35. * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
  36. * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
  37. */
  38. define('INSTALLDIR', dirname(__DIR__));
  39. define('PUBLICDIR', INSTALLDIR . DIRECTORY_SEPARATOR . 'public');
  40. require INSTALLDIR . '/lib/installer.php';
  41. /**
  42. * Helper class for building form
  43. */
  44. class Posted
  45. {
  46. /**
  47. * HTML-friendly escaped string for the POST param of given name, or empty.
  48. * @param string $name
  49. * @return string
  50. */
  51. public function value(string $name): string
  52. {
  53. return htmlspecialchars($this->string($name));
  54. }
  55. /**
  56. * The given POST parameter value, forced to a string.
  57. * Missing value will give ''.
  58. *
  59. * @param string $name
  60. * @return string
  61. */
  62. public function string(string $name): string
  63. {
  64. return strval($this->raw($name));
  65. }
  66. /**
  67. * The given POST parameter value, in its original form.
  68. * Magic quotes are stripped, if provided.
  69. * Missing value will give null.
  70. *
  71. * @param string $name
  72. * @return mixed
  73. */
  74. public function raw(string $name)
  75. {
  76. if (isset($_POST[$name])) {
  77. return $this->dequote($_POST[$name]);
  78. } else {
  79. return null;
  80. }
  81. }
  82. /**
  83. * If necessary, strip magic quotes from the given value.
  84. *
  85. * @param mixed $val
  86. * @return mixed
  87. */
  88. public function dequote($val)
  89. {
  90. if (get_magic_quotes_gpc()) {
  91. if (is_string($val)) {
  92. return stripslashes($val);
  93. } elseif (is_array($val)) {
  94. return array_map([$this, 'dequote'], $val);
  95. }
  96. }
  97. return $val;
  98. }
  99. }
  100. /**
  101. * Web-based installer: provides a form and such.
  102. */
  103. class WebInstaller extends Installer
  104. {
  105. /**
  106. * the actual installation.
  107. * If call libraries are present, then install
  108. *
  109. * @return void
  110. */
  111. public function main()
  112. {
  113. if (!$this->checkPrereqs()) {
  114. $this->warning(_('Please fix the above stated problems and refresh this page to continue installing.'));
  115. return;
  116. }
  117. if ($_SERVER['REQUEST_METHOD'] == 'POST') {
  118. $this->handlePost();
  119. } else {
  120. $this->showForm();
  121. }
  122. }
  123. /**
  124. * Web implementation of warning output
  125. * @param string $message
  126. * @param string $submessage
  127. */
  128. public function warning(string $message, string $submessage = '')
  129. {
  130. print "<div class=\"alert alert-danger\" role=\"alert\">$message";
  131. if ($submessage != '') {
  132. print "<hr><p class=\"mb-0\">$submessage</p>\n";
  133. }
  134. print "</div>\n";
  135. }
  136. /**
  137. * Web implementation of status output
  138. * @param string $status
  139. * @param bool $error
  140. */
  141. public function updateStatus(string $status, bool $error = false)
  142. {
  143. print "<div class=\"alert alert-". ($error ? 'danger': 'info' ) ."\" role=\"alert\">";
  144. if ($status != '') {
  145. print "<hr><p class=\"mb-0\">$status</p>\n";
  146. }
  147. print "</div>\n";
  148. }
  149. /**
  150. * Show the web form!
  151. */
  152. public function showForm()
  153. {
  154. global $dbModules;
  155. $post = new Posted();
  156. $dbRadios = '';
  157. $dbtype = $post->raw('dbtype');
  158. foreach (self::$dbModules as $type => $info) {
  159. if (extension_loaded($info['check_module'])) {
  160. if ($dbtype == null || $dbtype == $type) {
  161. $checked = 'checked="checked" ';
  162. $dbtype = $type; // if we didn't have one checked, hit the first
  163. } else {
  164. $checked = '';
  165. }
  166. $dbRadios .= sprintf('<div class="custom-control custom-switch"><input type="radio" name="dbtype" id="dbtype-%1$s" value="%1$s" class="custom-control-input" %2$s/><label class="custom-control-label" for="dbtype-mysql>%3$s</label></div>',
  167. htmlspecialchars($type),
  168. $checked,
  169. htmlspecialchars($info['name'])
  170. );
  171. }
  172. }
  173. $ssl = ['always' => null, 'never' => null];
  174. if (!empty($_SERVER['HTTPS'])) {
  175. $ssl['always'] = 'checked="checked"';
  176. } else {
  177. $ssl['never'] = 'checked="checked"';
  178. }
  179. echo <<<E_O_T
  180. <form method="post" action="install.php" class="form_settings" id="form_install">
  181. <legend>Site settings</legend>
  182. <hr>
  183. <div class="form-group">
  184. <label for="sitename">Site name</label>
  185. <input type="text" id="sitename" name="sitename" class="form-control" value="{$post->value('sitename')}" />
  186. <small class="form-text text-muted">The name of your site</small>
  187. </div>
  188. <div class="form-row">
  189. <div class="col-12 col-sm-6">
  190. <label for="fancy">Fancy URLs</label>
  191. <div class="custom-control custom-switch">
  192. <input type="radio" name="fancy" id="fancy-enable" value="enable" checked='checked' class="custom-control-input" />
  193. <label class="custom-control-label" for="fancy-enable">Enable</label>
  194. </div>
  195. <div class="custom-control custom-switch">
  196. <input type="radio" name="fancy" id="fancy-disable" value="" class="custom-control-input" />
  197. <label class="custom-control-label" for="fancy-disable">Disable</label>
  198. </div>
  199. <small class="form-text text-muted">Enable fancy (pretty) URLs. Auto-detection failed, it depends on Javascript.</small>
  200. </div>
  201. <div class="col-12 col-sm-6">
  202. <label for="ssl">Server SSL</label>
  203. <div class="custom-control custom-switch">
  204. <input type="radio" name="ssl" id="ssl-always" value="always" {$ssl['always']} class="custom-control-input" />
  205. <label class="custom-control-label" for="ssl-always">Enable</label>
  206. </div>
  207. <div class="custom-control custom-switch">
  208. <input type="radio" name="ssl" id="ssl-never" value="never" {$ssl['never']} class="custom-control-input" />
  209. <label class="custom-control-label" for="ssl-never">Disable</label>
  210. </div>
  211. <small class="form-text text-muted">Enabling SSL (https://) requires extra webserver configuration and certificate generation not offered by this installation.</small>
  212. </div>
  213. </div>
  214. <legend>Database settings</legend>
  215. <hr>
  216. <div class="form-group">
  217. <label for="dbtype">Type</label>
  218. {$dbRadios}
  219. <small class="form-text text-muted">Database type</small>
  220. </div>
  221. <div class="form-row">
  222. <div class="col-12 col-sm-6">
  223. <div class="form-group">
  224. <label for="host">Hostname</label>
  225. <input type="text" id="host" name="host" class="form-control" value="{$post->value('host')}" />
  226. <small class="form-text text-muted">Database hostname</small>
  227. </div>
  228. <div class="form-group">
  229. <label for="database">Name</label>
  230. <input type="text" id="database" name="database" class="form-control" value="{$post->value('database')}" />
  231. <small class="form-text text-muted">Database name</small>
  232. </div>
  233. </div>
  234. <div class="col-12 col-sm-6">
  235. <div class="form-group">
  236. <label for="dbusername">DB username</label>
  237. <input type="text" id="dbusername" name="dbusername" class="form-control" value="{$post->value('dbusername')}" />
  238. <small class="form-text text-muted">Database username</small>
  239. </div>
  240. <div class="form-group">
  241. <label for="dbpassword">DB password</label>
  242. <input type="password" id="dbpassword" name="dbpassword" class="form-control" value="{$post->value('dbpassword')}" />
  243. <small class="form-text text-muted">Database password (optional)</small>
  244. </div>
  245. </div>
  246. </div>
  247. <legend>Administrator settings</legend>
  248. <hr>
  249. <div class="form-row">
  250. <div class="col-12 col-sm-6">
  251. <div class="form-group">
  252. <label for="admin_nickname">Administrator nickname</label>
  253. <input type="text" id="admin_nickname" name="admin_nickname" class="form-control" value="{$post->value('admin_nickname')}" />
  254. <small class="form-text text-muted">Nickname for the initial user (administrator)</small>
  255. </div>
  256. <div class="form-group">
  257. <label for="admin_password">Administrator password</label>
  258. <input type="password" id="admin_password" name="admin_password" class="form-control" value="{$post->value('admin_password')}" />
  259. <small class="form-text text-muted">Password for the initial user (administrator)</small>
  260. </div>
  261. </div>
  262. <div class="col-12 col-sm-6">
  263. <div class="form-group">
  264. <label for="admin_password2">Confirm password</label>
  265. <input type="password" id="admin_password2" name="admin_password2" class="form-control" value="{$post->value('admin_password2')}" />
  266. <small class="form-text text-muted">Confirm your password</small>
  267. </div>
  268. <div class="form-group">
  269. <label for="admin_email">Administrator e-mail</label>
  270. <input id="admin_email" name="admin_email" class="form-control" value="{$post->value('admin_email')}" />
  271. <small class="form-text text-muted">Optional email address for the initial user (administrator)</small>
  272. </div>
  273. </div>
  274. </div>
  275. <legend>Site profile</legend>
  276. <hr>
  277. <div class="form-group">
  278. <label for="site_profile">Type of site</label>
  279. <select id="site_profile" class="form-control" name="site_profile">
  280. <option value="community">Community</option>
  281. <option value="public">Public (open registration)</option>
  282. <option value="singleuser">Single User</option>
  283. <option value="private">Private (no federation)</option>
  284. </select>
  285. <small class="form-text text-muted">Initial access settings for your site</small>
  286. </div>
  287. <input type="submit" name="submit" class="btn btn-primary submit" value="Submit" />
  288. </form>
  289. E_O_T;
  290. }
  291. /**
  292. * Handle a POST submission... if we have valid input, start the install!
  293. * Otherwise shows the form along with any error messages.
  294. */
  295. public function handlePost()
  296. {
  297. echo <<<STR
  298. <dl class="system_notice">
  299. <dt>Page notice</dt>
  300. <dd>
  301. <ul>
  302. STR;
  303. $this->validated = $this->prepare();
  304. if ($this->validated) {
  305. $this->doInstall();
  306. }
  307. echo <<<STR
  308. </ul>
  309. </dd>
  310. </dl>
  311. STR;
  312. if (!$this->validated) {
  313. $this->showForm();
  314. }
  315. }
  316. /**
  317. * Read and validate input data.
  318. * May output side effects.
  319. *
  320. * @return bool success
  321. */
  322. public function prepare(): bool
  323. {
  324. $post = new Posted();
  325. $this->host = $post->string('host');
  326. $this->dbtype = $post->string('dbtype');
  327. $this->database = $post->string('database');
  328. $this->username = $post->string('dbusername');
  329. $this->password = $post->string('dbpassword');
  330. $this->sitename = $post->string('sitename');
  331. $this->fancy = (bool)$post->string('fancy');
  332. $this->adminNick = strtolower($post->string('admin_nickname'));
  333. $this->adminPass = $post->string('admin_password');
  334. $adminPass2 = $post->string('admin_password2');
  335. $this->adminEmail = $post->string('admin_email');
  336. $this->siteProfile = $post->string('site_profile');
  337. $this->ssl = $post->string('ssl');
  338. $this->server = $_SERVER['HTTP_HOST'];
  339. $this->path = substr(dirname($_SERVER['PHP_SELF']), 1);
  340. $fail = false;
  341. if (!$this->validateDb()) {
  342. $fail = true;
  343. }
  344. if (!$this->validateAdmin()) {
  345. $fail = true;
  346. }
  347. if ($this->adminPass != $adminPass2) {
  348. $this->updateStatus("Administrator passwords do not match. Did you mistype?", true);
  349. $fail = true;
  350. }
  351. if (!in_array($this->ssl, ['never', 'always'])) {
  352. $this->updateStatus("Bad value for server SSL enabling.");
  353. $fail = true;
  354. }
  355. if (!$this->validateSiteProfile()) {
  356. $fail = true;
  357. }
  358. return !$fail;
  359. }
  360. }
  361. ?>
  362. <!DOCTYPE html>
  363. <html lang="en">
  364. <head>
  365. <meta charset="utf-8">
  366. <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  367. <title>Install GNU social</title>
  368. <link rel="shortcut icon" href="favicon.ico"/>
  369. <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
  370. </head>
  371. <body id="install">
  372. <div class="d-flex flex-column flex-md-row align-items-center p-3 px-md-4 mb-3 bg-white border-bottom shadow-sm">
  373. <img class="logo u-photo" src="theme/neo/logo.png" alt="GNU social"/>
  374. </div>
  375. <div class="pricing-header px-3 py-3 pt-md-5 pb-md-4 mx-auto text-center">
  376. <h1 class="display-4">Install GNU social</h1>
  377. </div>
  378. <div class="container">
  379. <?php
  380. $installer = new WebInstaller();
  381. $installer->main();
  382. ?>
  383. </div>
  384. <div id="footer" class="py-5"></div>
  385. </body>
  386. </html>