install.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. <?php
  2. /* GNUkebox -- a free software server for recording your listening habits
  3. Copyright (C) 2009, 2015 Free Software Foundation, Inc
  4. This program 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. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Affero General Public License for more details.
  12. You should have received a copy of the GNU Affero General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. require_once(__DIR__ . '/vendor/autoload.php');
  16. require_once('version.php');
  17. require_once('utils/get_absolute_url.php');
  18. if (file_exists('config.php')) {
  19. die('A configuration file already exists. Please delete <i>config.php</i> if you wish to reinstall.');
  20. }
  21. if (isset($_POST['install'])) {
  22. //Get the database connection string
  23. $dbms = $_POST['dbms'];
  24. if ($dbms == 'sqlite') {
  25. $filename = urlencode($_POST['filename']);
  26. $connect_string = 'sqlite://' . $filename;
  27. } else {
  28. $connect_string = $dbms . '://' . $_POST['username'] . ':' . $_POST['password'] . '@' . $_POST['hostname'] . ':' . $_POST['port'] . '/' . $_POST['dbname'];
  29. }
  30. $adodb_connect_string = str_replace('pgsql:', 'postgres:', $connect_string);
  31. try {
  32. $adodb =& NewADOConnection($adodb_connect_string);
  33. } catch (Exception $e) {
  34. var_dump($e);
  35. adodb_backtrace($e->gettrace());
  36. die("Database connection failure\n");
  37. }
  38. //Create tables
  39. $stage_one_queries = array(
  40. 'CREATE TABLE Places(
  41. location_uri VARCHAR(255) unique,
  42. latitude FLOAT,
  43. longitude FLOAT,
  44. country CHAR(2))',
  45. 'CREATE TABLE Countries (
  46. country varchar(2) PRIMARY KEY,
  47. country_name varchar(200),
  48. wikipedia_en varchar(120));',
  49. 'CREATE TABLE Users (
  50. uniqueid SERIAL PRIMARY KEY,
  51. username VARCHAR(64) unique,
  52. password VARCHAR(32) NOT NULL,
  53. email VARCHAR(255),
  54. fullname VARCHAR(255),
  55. bio TEXT,
  56. homepage VARCHAR(255),
  57. location VARCHAR(255),
  58. userlevel INTEGER DEFAULT 0,
  59. anticommercial INTEGER DEFAULT 0,
  60. webid_uri VARCHAR(255),
  61. avatar_uri VARCHAR(255),
  62. openid_uri VARCHAR(100),
  63. active INTEGER DEFAULT 0,
  64. public_export INTEGER DEFAULT 0,
  65. location_uri VARCHAR(255) REFERENCES Places(location_uri),
  66. laconica_profile VARCHAR(255),
  67. created INTEGER DEFAULT 0,
  68. modified INTEGER DEFAULT 0,
  69. journal_rss VARCHAR(255),
  70. receive_emails INTEGER DEFAULT 1)',
  71. 'CREATE TABLE Groups (
  72. id SERIAL PRIMARY KEY,
  73. groupname VARCHAR(64),
  74. owner INTEGER REFERENCES Users(uniqueid),
  75. fullname VARCHAR(255),
  76. bio TEXT,
  77. homepage VARCHAR(255),
  78. created INTEGER NOT NULL,
  79. modified INTEGER,
  80. avatar_uri VARCHAR(255),
  81. grouptype INTEGER)',
  82. 'CREATE TABLE Group_Members (
  83. grp INTEGER REFERENCES Groups(id),
  84. member INTEGER REFERENCES Users(uniqueid),
  85. joined INTEGER NOT NULL,
  86. PRIMARY KEY (grp, member))',
  87. # TODO: REMOVE
  88. 'CREATE TABLE AccountActivation(
  89. username VARCHAR(64),
  90. authcode VARCHAR(32),
  91. expires INTEGER)',
  92. 'CREATE TABLE Auth (
  93. token VARCHAR(32) PRIMARY KEY,
  94. sk VARCHAR(32),
  95. expires INTEGER,
  96. username VARCHAR(64) REFERENCES Users(username))',
  97. 'CREATE TABLE Artist(
  98. id SERIAL PRIMARY KEY,
  99. name VARCHAR(255) unique,
  100. mbid VARCHAR(36),
  101. imbid INTEGER,
  102. streamable INTEGER,
  103. bio_published INTEGER,
  104. bio_content TEXT,
  105. bio_summary TEXT,
  106. image_small VARCHAR(255),
  107. image_medium VARCHAR(255),
  108. image_large VARCHAR(255),
  109. homepage VARCHAR(255),
  110. hashtag VARCHAR(255),
  111. origin VARCHAR(255) REFERENCES Places(location_uri),
  112. flattr_uid VARCHAR(255))',
  113. 'CREATE TABLE Album(
  114. id SERIAL PRIMARY KEY,
  115. name VARCHAR(255),
  116. artist_name VARCHAR(255) REFERENCES Artist(name),
  117. mbid VARCHAR(36),
  118. image VARCHAR(255),
  119. artwork_license VARCHAR(255),
  120. releasedate INTEGER,
  121. albumurl VARCHAR(255),
  122. downloadurl VARCHAR(255))',
  123. 'CREATE TABLE Similar_Artist(
  124. name_a VARCHAR(255) REFERENCES Artist(name),
  125. name_b VARCHAR(255) REFERENCES Artist(name),
  126. PRIMARY KEY(name_a, name_b))'
  127. );
  128. $stage_two_queries_mysql = array(
  129. 'CREATE TABLE Track(
  130. id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  131. name VARCHAR(255),
  132. artist_name VARCHAR(255) REFERENCES Artist(name),
  133. album_name VARCHAR(255),
  134. mbid VARCHAR(36),
  135. duration INTEGER,
  136. streamable INTEGER DEFAULT 0,
  137. license VARCHAR(255),
  138. downloadurl VARCHAR(255),
  139. streamurl VARCHAR(255),
  140. otherid VARCHAR(16))'
  141. );
  142. $stage_two_queries_other = array(
  143. 'CREATE SEQUENCE track_id_seq;',
  144. 'CREATE TABLE Track(
  145. id INTEGER NOT NULL DEFAULT nextval(\'track_id_seq\'::regclass) PRIMARY KEY,
  146. name VARCHAR(255),
  147. artist_name VARCHAR(255) REFERENCES Artist(name),
  148. album_name VARCHAR(255),
  149. mbid VARCHAR(36),
  150. duration INTEGER,
  151. streamable INTEGER DEFAULT 0,
  152. license VARCHAR(255),
  153. downloadurl VARCHAR(255),
  154. streamurl VARCHAR(255),
  155. otherid VARCHAR(16))'
  156. );
  157. $stage_three_queries = array(
  158. 'CREATE TABLE Scrobbles(
  159. userid INTEGER REFERENCES Users(uniqueid),
  160. track VARCHAR(255),
  161. album VARCHAR(255),
  162. artist VARCHAR(255) REFERENCES Artist(name),
  163. time INTEGER,
  164. mbid VARCHAR(36),
  165. source VARCHAR(6),
  166. rating CHAR(1),
  167. length INTEGER,
  168. stid INTEGER)',
  169. 'CREATE TABLE Scrobble_Sessions(
  170. userid INTEGER REFERENCES Users(uniqueid),
  171. sessionid VARCHAR(32) PRIMARY KEY,
  172. client CHAR(3),
  173. api_key VARCHAR(32),
  174. expires INTEGER)',
  175. 'CREATE TABLE Now_Playing(
  176. sessionid VARCHAR(32) PRIMARY KEY REFERENCES Scrobble_Sessions(sessionid) ON DELETE CASCADE,
  177. track VARCHAR(255),
  178. artist VARCHAR(255),
  179. album VARCHAR(255),
  180. mbid VARCHAR(36),
  181. expires INTEGER)',
  182. # TODO: Delete
  183. 'CREATE TABLE Invitation_Request(
  184. email VARCHAR(255) PRIMARY KEY,
  185. time INTEGER)',
  186. 'CREATE TABLE Invitations(
  187. inviter VARCHAR(64) REFERENCES Users(username),
  188. invitee VARCHAR(64) REFERENCES Users(username),
  189. code VARCHAR(32),
  190. PRIMARY KEY(inviter, invitee, code))',
  191. 'CREATE TABLE ClientCodes(
  192. code CHAR(3),
  193. name VARCHAR(32),
  194. url VARCHAR(256),
  195. free CHAR(1),
  196. PRIMARY KEY(code))',
  197. 'CREATE TABLE Tags(
  198. tag VARCHAR(64),
  199. artist VARCHAR(255) REFERENCES Artist(name),
  200. album VARCHAR(255),
  201. track VARCHAR(255),
  202. userid INTEGER REFERENCES Users(uniqueid),
  203. UNIQUE(tag, artist, album, track, userid))',
  204. 'CREATE TABLE Manages(
  205. userid INTEGER REFERENCES Users(uniqueid),
  206. artist VARCHAR(255) REFERENCES Artist(name),
  207. authorised INTEGER)',
  208. 'CREATE TABLE Error(
  209. id SERIAL PRIMARY KEY,
  210. msg TEXT,
  211. data TEXT,
  212. time INTEGER)',
  213. 'CREATE TABLE Recovery_Request(
  214. username VARCHAR(64),
  215. email VARCHAR(255),
  216. code VARCHAR(32),
  217. expires INTEGER,
  218. PRIMARY KEY(username))',
  219. 'CREATE TABLE Radio_Sessions(
  220. username VARCHAR(64),
  221. session VARCHAR(32),
  222. url VARCHAR(255),
  223. expires INTEGER NOT NULL DEFAULT 0,
  224. PRIMARY KEY(session))',
  225. //Table for delete profile requests
  226. 'CREATE TABLE Delete_Request (
  227. code VARCHAR(300),
  228. expires INTEGER,
  229. username VARCHAR(64) REFERENCES Users(username),
  230. PRIMARY KEY(code))',
  231. 'CREATE TABLE Scrobble_Track(
  232. id SERIAL PRIMARY KEY,
  233. artist VARCHAR(255) NOT NULL,
  234. album VARCHAR(255),
  235. name VARCHAR(255) NOT NULL,
  236. mbid VARCHAR(36),
  237. track INTEGER NOT NULL)',
  238. 'CREATE VIEW Free_Scrobbles AS
  239. SELECT s.userid, s.track, s.artist, s.time, s.mbid, s.album, s.source, s.rating, s.length
  240. FROM Scrobbles s
  241. JOIN Scrobble_Track st ON s.stid = st.id
  242. JOIN Track t ON st.track = t.id
  243. WHERE t.streamable = 1',
  244. 'CREATE TABLE Banned_Tracks (
  245. userid INTEGER REFERENCES Users(uniqueid) ON DELETE CASCADE,
  246. track varchar(255),
  247. artist varchar(255),
  248. time INTEGER,
  249. UNIQUE(userid, track, artist))',
  250. 'CREATE TABLE Loved_Tracks (
  251. userid INTEGER REFERENCES Users(uniqueid) ON DELETE CASCADE,
  252. track varchar(255),
  253. artist varchar(255),
  254. time varchar(255),
  255. UNIQUE(userid, track, artist))',
  256. 'CREATE TABLE Service_Connections (
  257. userid INTEGER REFERENCES Users(uniqueid) ON DELETE CASCADE,
  258. webservice_url VARCHAR(255),
  259. remote_key VARCHAR(255),
  260. remote_username VARCHAR(255),
  261. forward INTEGER DEFAULT 1)',
  262. 'CREATE TABLE User_Relationships (
  263. uid1 INTEGER REFERENCES Users(uniqueid) ON DELETE CASCADE,
  264. uid2 INTEGER REFERENCES Users(uniqueid) ON DELETE CASCADE,
  265. established INTEGER NOT NULL,
  266. PRIMARY KEY (uid1, uid2))',
  267. 'CREATE TABLE Relationship_Flags (
  268. flag VARCHAR(12),
  269. PRIMARY KEY (flag))',
  270. 'CREATE TABLE User_Relationship_Flags (
  271. uid1 INTEGER,
  272. uid2 INTEGER,
  273. flag VARCHAR(12) REFERENCES Relationship_Flags(flag),
  274. PRIMARY KEY (uid1, uid2, flag),
  275. FOREIGN KEY (uid1, uid2) REFERENCES User_Relationships (uid1, uid2))',
  276. 'INSERT INTO Relationship_Flags VALUES (\'contact\')',
  277. 'INSERT INTO Relationship_Flags VALUES (\'acquaintance\')',
  278. 'INSERT INTO Relationship_Flags VALUES (\'friend\')',
  279. 'INSERT INTO Relationship_Flags VALUES (\'met\')',
  280. 'INSERT INTO Relationship_Flags VALUES (\'co-worker\')',
  281. 'INSERT INTO Relationship_Flags VALUES (\'colleague\')',
  282. 'INSERT INTO Relationship_Flags VALUES (\'co-resident\')',
  283. 'INSERT INTO Relationship_Flags VALUES (\'neighbor\')',
  284. 'INSERT INTO Relationship_Flags VALUES (\'child\')',
  285. 'INSERT INTO Relationship_Flags VALUES (\'parent\')',
  286. 'INSERT INTO Relationship_Flags VALUES (\'sibling\')',
  287. 'INSERT INTO Relationship_Flags VALUES (\'spouse\')',
  288. 'INSERT INTO Relationship_Flags VALUES (\'kin\')',
  289. 'INSERT INTO Relationship_Flags VALUES (\'muse\')',
  290. 'INSERT INTO Relationship_Flags VALUES (\'crush\')',
  291. 'INSERT INTO Relationship_Flags VALUES (\'date\')',
  292. 'INSERT INTO Relationship_Flags VALUES (\'sweetheart\')',
  293. 'CREATE TABLE User_Stats (
  294. userid INTEGER REFERENCES Users(uniqueid) ON DELETE CASCADE,
  295. scrobble_count INTEGER NOT NULL,
  296. PRIMARY KEY (userid))',
  297. 'CREATE TABLE Domain_Blacklist (
  298. domain TEXT,
  299. expires INTEGER)'
  300. );
  301. foreach ($stage_one_queries as $query) {
  302. try {
  303. $adodb->Execute($query);
  304. } catch (Exception $e) {
  305. die('Database Error: ' . $adodb->ErrorMsg());
  306. }
  307. }
  308. if (strtolower(substr($dbms, 0, 5)) == 'mysql') {
  309. foreach ($stage_two_queries_mysql as $query) {
  310. try {
  311. $adodb->Execute($query);
  312. } catch (Exception $e) {
  313. die('Database Error: ' . $adodb->ErrorMsg());
  314. }
  315. }
  316. } else {
  317. foreach ($stage_two_queries_other as $query) {
  318. try {
  319. $adodb->Execute($query);
  320. } catch (Exception $e) {
  321. die('Database Error: ' . $adodb->ErrorMsg());
  322. }
  323. }
  324. }
  325. foreach ($stage_three_queries as $query) {
  326. try {
  327. $adodb->Execute($query);
  328. } catch (Exception $e) {
  329. die('Database Error: ' . $adodb->ErrorMsg());
  330. }
  331. }
  332. $adodb->Execute("CREATE INDEX scrobbles_time_idx ON Scrobbles(time)");
  333. $adodb->Execute("CREATE INDEX scrobbles_userid_time_idx ON Scrobbles(userid, time)");
  334. $adodb->Execute("CREATE INDEX scrobbles_track_idx on Scrobbles(track)");
  335. $adodb->Execute("CREATE INDEX scrobble_track_name_idx ON Scrobble_Track(name)");
  336. $adodb->Execute("CREATE INDEX track_streamable_idx on Track(streamable);");
  337. $adodb->Execute("CREATE INDEX track_name_idx ON Track(name)");
  338. $adodb->Execute("CREATE INDEX album_name_idx ON Album(name)");
  339. $adodb->Execute("CREATE INDEX artist_name_idx ON Artist(name)");
  340. if(strtolower(substr($dbms, 0, 5)) == 'pgsql') {
  341. // MySQL doesn't support the use of lower() to create case-insensitive indexes
  342. $adodb->Execute("CREATE INDEX album_lower_artistname_idx ON Album(lower(artist_name))");
  343. $adodb->Execute("CREATE INDEX track_lower_artist_idx ON Track(lower(artist_name))");
  344. $adodb->Execute("CREATE INDEX track_lower_name_idx ON Track(lower(name))");
  345. $adodb->Execute("CREATE INDEX scrobbles_lower_artist_idx on Scrobbles(lower(artist))");
  346. $adodb->Execute("CREATE INDEX scrobbles_lower_track_idx on Scrobbles(lower(track))");
  347. $adodb->Execute("CREATE INDEX groups_lower_groupname_idx ON Groups(lower(groupname))");
  348. // PostgreSQL stored functions
  349. $adodb->Execute("CREATE OR REPLACE LANGUAGE plpgsql;");
  350. $adodb->Execute("CREATE FUNCTION update_user_stats_scrobble_count() RETURNS TRIGGER AS $$
  351. DECLARE s_count int;
  352. BEGIN
  353. UPDATE User_Stats SET scrobble_count = scrobble_count + 1 WHERE userid = NEW.userid;
  354. IF found THEN
  355. RETURN NULL;
  356. END IF;
  357. BEGIN
  358. -- userid not in User_Stats table, get current scrobble count from Scrobbles
  359. -- and insert userid into User_Stats
  360. SELECT COUNT(userid) into s_count FROM Scrobbles WHERE userid = NEW.userid;
  361. INSERT INTO User_Stats(userid, scrobble_count) VALUES(NEW.userid, s_count);
  362. RETURN NULL;
  363. END;
  364. END;
  365. $$ LANGUAGE plpgsql;");
  366. $adodb->Execute("CREATE TRIGGER update_user_stats_scrobble_count
  367. AFTER INSERT ON Scrobbles
  368. FOR EACH ROW EXECUTE PROCEDURE update_user_stats_scrobble_count();");
  369. } elseif (substr($dbms, 0, 5) == 'mysql') {
  370. $adodb->Execute("CREATE PROCEDURE update_user_stats_scrobble_count(uid INT)
  371. main: BEGIN
  372. DECLARE s_count INT;
  373. UPDATE User_Stats SET scrobble_count = (scrobble_count + 1) WHERE userid = uid;
  374. IF ROW_COUNT() > 0 THEN
  375. LEAVE main;
  376. END IF;
  377. SELECT COUNT(userid) INTO s_count FROM Scrobbles WHERE userid = uid;
  378. INSERT INTO User_Stats(userid, scrobble_count) VALUES(uid, s_count);
  379. END main;");
  380. $adodb->Execute("CREATE TRIGGER update_user_stats_scrobble_count
  381. AFTER INSERT ON Scrobbles
  382. FOR EACH ROW CALL update_user_stats_scrobble_count(NEW.userid);");
  383. }
  384. $adodb->Close();
  385. $submissions_server = $_POST['submissions'];
  386. $install_path = dirname(__FILE__) . '/';
  387. //Write out the configuration
  388. $config = "<?php\n \$config_version = " . $version .";\n \$connect_string = '" . $connect_string . "';\n \$submissions_server = '" . $submissions_server . "';\n \$install_path = '" . $install_path . "';\n \$adodb_connect_string = '" . $adodb_connect_string . "';\n\n require_once(__DIR__ . '/vendor/autoload.php');\n";
  389. $conf_file = fopen('config.php', 'w');
  390. $result = fwrite($conf_file, $config);
  391. fclose($conf_file);
  392. if (!$result) {
  393. $print_config = str_replace('<', '&lt;', $config);
  394. die('Unable to write to file \'<i>config.php</i>\'. Please create this file and copy the following in to it: <br /><pre>' . $print_config . '</pre>');
  395. }
  396. die('Configuration completed successfully!');
  397. }
  398. ?>
  399. <html>
  400. <head>
  401. <title>GNUkebox Installer</title>
  402. <script type='text/javascript'>
  403. function showSqlite() {
  404. document.getElementById("sqlite").style.visibility = "visible";
  405. document.getElementById("networkdbms").style.visibility = "hidden";
  406. }
  407. function showNetworkDBMS() {
  408. document.getElementById("sqlite").style.visibility = "hidden";
  409. document.getElementById("networkdbms").style.visibility = "visible";
  410. }
  411. </script>
  412. </head>
  413. <body onload="showSqlite()">
  414. <h1>GNUkebox Installer</h1>
  415. <form method="post">
  416. <h2>Database</h2>
  417. Database Management System: <br />
  418. <input type="radio" name="dbms" value="sqlite" onclick='showSqlite()' checked>SQLite (use an absolute path)</input><br />
  419. <input type="radio" name="dbms" value="mysql" onclick='showNetworkDBMS()'>MySQL</input><br />
  420. <input type="radio" name="dbms" value="pgsql" onclick='showNetworkDBMS()'>PostgreSQL</input><br />
  421. <br />
  422. <div id="sqlite">
  423. Filename: <input type="text" name="filename" /><br />
  424. </div>
  425. <div id="networkdbms">
  426. Hostname: <input type="text" name="hostname" /><br />
  427. Port: <input type="text" name="port" /><br />
  428. Database: <input type="text" name="dbname" /><br />
  429. Username: <input type="text" name="username" /><br />
  430. Password: <input type="password" name="password" /><br />
  431. </div>
  432. <br />
  433. <h2>Servers</h2>
  434. Submissions Server URL: <input type="text" name="submissions" value="<?php echo getAbsoluteURL(); ?>" /><br />
  435. <br />
  436. <input type="submit" value="Install" name="install" />
  437. </form>
  438. <br />
  439. <div align="center"><a href="http://docs.jurg.no/gnufm_install.txt">Help</a></div>
  440. </body>
  441. </html>