From: Christian Weiske Date: Thu, 14 May 2020 05:09:02 +0000 (+0200) Subject: "Push to my OUYA" support X-Git-Tag: v2.0.0~4 X-Git-Url: https://git.cweiske.de/stouyapi.git/commitdiff_plain/4d2b9288d5403294fe6541358341986910e43c36 "Push to my OUYA" support --- diff --git a/.gitignore b/.gitignore index cd66da5..4827cbd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /config.php +/data/push-to-my-ouya.sqlite3 /README.html www/api/v1/apps/ www/api/v1/details-data/ diff --git a/README.rst b/README.rst index 91d490f..d06a382 100644 --- a/README.rst +++ b/README.rst @@ -36,6 +36,7 @@ Apache setup Virtual host configuration:: Script PUT /empty-json.php + Script DELETE /api/v1/queued_downloads_delete.php ``mod_actions`` need to be enabled for apache 2.4. diff --git a/bin/build-html.php b/bin/build-html.php index b4561e6..f7f9154 100755 --- a/bin/build-html.php +++ b/bin/build-html.php @@ -7,6 +7,13 @@ */ require_once __DIR__ . '/functions.php'; +//default configuration values +$GLOBALS['pushToMyOuyaUrl'] = '../push-to-my-ouya.php'; +$cfgFile = __DIR__ . '/../config.php'; +if (file_exists($cfgFile)) { + include $cfgFile; +} + $wwwDir = __DIR__ . '/../www/'; $discoverDir = __DIR__ . '/../www/api/v1/discover-data/'; $wwwDiscoverDir = $wwwDir . 'discover/'; @@ -111,6 +118,8 @@ function renderGameFile($gameDataFile) ) ); $apkDownloadUrl = $downloadJson->app->downloadLink; + $pushUrl = $GLOBALS['pushToMyOuyaUrl'] + . '?game=' . urlencode($json->apk->package); $navLinks = []; foreach ($json->genres as $genreTitle) { diff --git a/config.php.dist b/config.php.dist index 8c23bd2..5087c22 100644 --- a/config.php.dist +++ b/config.php.dist @@ -11,3 +11,4 @@ $GLOBALS['packagelists']["cweiske's picks"] = [ 'com.cosmos.babyloniantwins', 'com.inverseblue.skyriders', ]; +$GLOBALS['pushToMyOuyaUrl'] = '../push-to-my-ouya.php'; diff --git a/data/templates/game.tpl.php b/data/templates/game.tpl.php index 36a1d09..531fb28 100644 --- a/data/templates/game.tpl.php +++ b/data/templates/game.tpl.php @@ -75,6 +75,15 @@ version->publishedAt) ?>

+
+
+ +
+
+ + + + + diff --git a/src/push-to-my-ouya-helpers.php b/src/push-to-my-ouya-helpers.php new file mode 100644 index 0000000..a020988 --- /dev/null +++ b/src/push-to-my-ouya-helpers.php @@ -0,0 +1,39 @@ + + */ +$dbFile = __DIR__ . '/../../../data/push-to-my-ouya.sqlite3'; +$apiGameDir = __DIR__ . '/details-data/'; + +require_once __DIR__ . '/../../../src/push-to-my-ouya-helpers.php'; + +$ip = $_SERVER['REMOTE_ADDR']; +if ($ip == '' || strpos($ip, ':') !== false) { + //empty or IPv6 + header('Content-type: application/json'); + echo file_get_contents('queued_downloads'); + exit(1); +} +$ip = mapIp($ip); + +try { + $db = new SQLite3($dbFile, SQLITE3_OPEN_READONLY); +} catch (Exception $e) { + //db file not found + header('Content-type: application/json'); + echo file_get_contents('queued_downloads'); + exit(1); +} + +$res = $db->query( + 'SELECT * FROM pushes' + . ' WHERE ip = \'' . SQLite3::escapeString($ip) . '\'' +); +$queue = []; +while ($row = $res->fetchArray(SQLITE3_ASSOC)) { + $apiGameFile = $apiGameDir . $row['game'] . '.json'; + if (!file_exists($apiGameFile)) { + //game deleted? + continue; + } + $json = json_decode(file_get_contents($apiGameFile)); + $queue[] = [ + 'versionUuid' => '', + 'title' => $json->title, + 'source' => 'gamer', + 'uuid' => $row['game'], + ]; +} + +header('Content-type: application/json'); +echo json_encode(['queue' => $queue]) . "\n"; +?> diff --git a/www/api/v1/queued_downloads_delete.php b/www/api/v1/queued_downloads_delete.php new file mode 100644 index 0000000..3861f93 --- /dev/null +++ b/www/api/v1/queued_downloads_delete.php @@ -0,0 +1,53 @@ + + */ +$dbFile = __DIR__ . '/../../../data/push-to-my-ouya.sqlite3'; +$apiGameDir = __DIR__ . '/details-data/'; + +require_once __DIR__ . '/../../../src/push-to-my-ouya-helpers.php'; + +$ip = $_SERVER['REMOTE_ADDR']; +if ($ip == '' || strpos($ip, ':') !== false) { + //empty or IPv6 + header('HTTP/1.0 204 No Content'); + exit(1); +} +$ip = mapIp($ip); + +$game = $_GET['game']; +$cleanGame = preg_replace('#[^a-zA-Z0-9.]#', '', $game); +if ($game != $cleanGame || $game == '') { + header('HTTP/1.0 400 Bad Request'); + header('Content-type: text/plain'); + echo 'Invalid game' . "\n"; + exit(1); +} + +try { + $db = new SQLite3($dbFile, SQLITE3_OPEN_READWRITE); +} catch (Exception $e) { + //db file not found + header('HTTP/1.0 204 No Content'); + exit(1); +} + +$rowId = $db->querySingle( + 'SELECT id FROM pushes' + . ' WHERE ip = \'' . SQLite3::escapeString($ip) . '\'' + . ' AND game =\'' . SQLite3::escapeString($game) . '\'' +); +if ($rowId === null) { + header('HTTP/1.0 404 Not Found'); + header('Content-type: text/plain'); + echo 'Game not queued' . "\n"; + exit(1); +} + +$db->exec('DELETE FROM pushes WHERE id = ' . intval($rowId)); +header('HTTP/1.0 204 No Content'); +?> diff --git a/www/ouya-game.css b/www/ouya-game.css index b9886e7..ce23fdd 100644 --- a/www/ouya-game.css +++ b/www/ouya-game.css @@ -97,10 +97,20 @@ nav { .buttons h2 { display: none; } +.buttons { + display: flex; + justify-content: space-between; +} .buttons a { font-size: 1.5rem; color: #CCC; } +button.push-to-my-ouya { + cursor: pointer; + border: none; + padding: 0; + background-color: transparent; +} nav { text-align: center; @@ -156,3 +166,31 @@ nav a { .average-5:before { content: "★★★★★"; } + + +.popup { + position: fixed; + top: 2rem; + right: 2rem; + width: 20rem; + padding: 1rem; + background-color: black; + border: 1px solid #AAA; + border-radius: 0.5rem; +} +.popup a.close { + color: white; + font-size: 2rem; + text-decoration: none; + position: absolute; + top: 0; + right: 0.5rem; +} +.popup a.close:hover { + color: #fc4422; +} +.popup strong { + display: block; + color: #fc4422; + margin-bottom: 0.5rem; +} diff --git a/www/push-to-my-ouya.php b/www/push-to-my-ouya.php new file mode 100644 index 0000000..fd39043 --- /dev/null +++ b/www/push-to-my-ouya.php @@ -0,0 +1,145 @@ + + */ +$dbFile = __DIR__ . '/../data/push-to-my-ouya.sqlite3'; +$apiGameDir = __DIR__ . '/api/v1/details-data/'; + +require_once __DIR__ . '/../src/push-to-my-ouya-helpers.php'; + +//support different ipv4-only domain +header('Access-Control-Allow-Origin: *'); + +if ($_SERVER['REQUEST_METHOD'] != 'POST') { + header('HTTP/1.0 400 Bad Request'); + header('Content-type: text/plain'); + echo 'POST only, please' . "\n"; + exit(1); +} + +if (!isset($_GET['game'])) { + header('HTTP/1.0 400 Bad Request'); + header('Content-type: text/plain'); + echo '"game" parameter missing' . "\n"; + exit(1); +} + +$game = $_GET['game']; +$cleanGame = preg_replace('#[^a-zA-Z0-9.]#', '', $game); +if ($game != $cleanGame) { + header('HTTP/1.0 400 Bad Request'); + header('Content-type: text/plain'); + echo 'Invalid game' . "\n"; + exit(1); +} + +$apiGameFile = $apiGameDir . $game . '.json'; +if (!file_exists($apiGameFile)) { + header('HTTP/1.0 404 Not Found'); + header('Content-type: text/plain'); + echo 'Game does not exist' . "\n"; + exit(1); +} + +$ip = $_SERVER['REMOTE_ADDR']; +if ($ip == '') { + header('HTTP/1.0 400 Bad Request'); + header('Content-type: text/plain'); + echo 'Cannot detect your IP address' . "\n"; + exit(1); +} +if (strpos($ip, ':') !== false) { + header('HTTP/1.0 400 Bad Request'); + header('Content-type: text/plain'); + echo 'Sorry, IPv6 is not supported' . "\n"; + echo 'This here only works if the OUYA and your PC have the same IP address,' + . "\n"; + echo 'and this is definitely not the case when using IPv6' . "\n"; + exit(1); +} +$ip = mapIp($ip); + +try { + $db = new SQLite3($dbFile, SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE); +} catch (Exception $e) { + header('HTTP/1.0 500 Internal server error'); + header('Content-type: text/plain'); + echo 'Cannot open database' . "\n"; + echo $e->getMessage() . "\n"; + exit(2); +} + +$res = $db->querySingle( + 'SELECT name FROM sqlite_master WHERE type = "table" AND name = "pushes"' +); +if ($res === null) { + //table does not exist yet + $db->exec( + <<exec( + 'DELETE FROM pushes' + . ' WHERE created_at < \'' . gmdate('Y-m-d H:i:s', time() - 86400) . '\'' +); + +//check if this IP already pushed this game +$numThisGame = $db->querySingle( + 'SELECT COUNT(*) FROM pushes' + . ' WHERE ip = \'' . SQLite3::escapeString($ip) . '\'' + . ' AND game = \'' . SQLite3::escapeString($game) . '\'' +); +if ($numThisGame >= 1) { + header('HTTP/1.0 400 Bad Request'); + header('Content-type: text/plain'); + echo 'Already pushed.' . "\n"; + exit(1); +} + +//check number of pushes for this IP +$numPushes = $db->querySingle( + 'SELECT COUNT(*) FROM pushes' + . ' WHERE ip = \'' . SQLite3::escapeString($ip) . '\'' +); +if ($numPushes >= 30) { + header('HTTP/1.0 400 Bad Request'); + header('Content-type: text/plain'); + echo 'Too many pushes. Come back tomorrow.' . "\n"; + exit(1); +} + +//store the push +$stmt = $db->prepare('INSERT INTO pushes (game, ip) VALUES(:game, :ip)'); +$stmt->bindValue(':game', $game); +$stmt->bindValue(':ip', $ip); +$res = $stmt->execute(); +if ($res === false) { + header('HTTP/1.0 500 Internal server error'); + header('Content-type: text/plain'); + echo 'Cannot store push' . "\n"; + exit(3); +} +$res->finalize(); + +header('HTTP/1.0 200 OK'); +header('Content-type: text/plain'); +echo 'Push accepted' . "\n"; +exit(3); +?> diff --git a/www/push-to-my-ouya.png b/www/push-to-my-ouya.png new file mode 100644 index 0000000..50bd0e2 Binary files /dev/null and b/www/push-to-my-ouya.png differ