@@ 1,32 @@
+# Suteba
+*Suteba* (捨場 - dumping ground) is a simple request bin/dump written in PHP.
+
+I needed this functionality for my day job, and
+while I could've used something that's available (and probably better), that wouldn't be much of a learning experience now, would it?
+
+The solution? Make your own.
+
+## Usage
+1. Upload the file to folder/subdomain of your choosing.
+2. Add allowed keys to the file under `$keys`, e.g.:
+```php
+ private $keys = [
+ 'test' => '098f6bcd4621d373cade4e832627b4f6',
+ 'hxii' => '4b58f3d6a1935c14ac594f9ef9dca69e'
+ ];
+```
+3. Send your request to the file, e.g
+`curl -d "test=true&value=suetba%20is%20cool" https://yourdomain/pb.php?key=098f6bcd4621d373cade4e832627b4f6`
+4. If the key matches, you'll get a response with the ID and path of the request, e.g.:
+```
+{
+ "id": "e08b7ecac70c8142ff2c8e76ebd17892",
+ "path": "data/098f6bcd4621d373cade4e832627b4f6/e08b7ecac70c8142ff2c8e76ebd17892.txt"
+}
+```
+5. You can access the request either:
+ 1. Directly using `https://yourdomain.com/data/098f6bcd4621d373cade4e832627b4f6/e08b7ecac70c8142ff2c8e76ebd17892.txt` or
+ 2. You can list all available requests for a key using `https://yourdomain/pb.php?key=098f6bcd4621d373cade4e832627b4f6&action=list`.
+
+## Issues and such
+Found a problem? Report it [here](https://todo.sr.ht/~hxii/suteba).<
\ No newline at end of file
@@ 1,212 @@
+<?php
+/*
+捨場 — Suteba
+https://paulglushak.com/suteba
+*/
+
+define('DUMP_DIR', 'data');
+define('DS', DIRECTORY_SEPARATOR);
+
+class Suteba
+{
+
+ /**
+ * Allowed keys (or buckets)
+ *
+ * @var array keys
+ */
+ private $keys = [
+ 'test' => '098f6bcd4621d373cade4e832627b4f6',
+ 'poato' => 'potato'
+ ];
+
+ public $requestKey, $requestKeyholder, $requestPayload, $requestAction, $requestType,
+ $requestMethod, $requestTime, $requestAgent, $requestSource;
+
+ public function __construct()
+ {
+ if (isset($_GET['key']) && $this->isAllowedToRequest($_GET['key'])) {
+ /* If the key is allowed, save info and handle the request */
+ $this->requestKey = $_GET['key'];
+ $this->requestKeyholder = $this->getKeyholder($this->requestKey);
+ if (isset($_GET['action'])) {
+ $this->requestAction = $_GET['action'];
+ }
+ $this->requestPayload = file_get_contents('php://input');
+ $this->requestMethod = $_SERVER['REQUEST_METHOD'];
+ $this->requestType = $_SERVER['CONTENT_TYPE'];
+ $this->requestTime = date("l jS F \@ g:i:s a", $_SERVER['REQUEST_TIME']);
+ $this->requestAgent = $_SERVER['HTTP_USER_AGENT'];
+ $this->requestSource = $_SERVER['REMOTE_ADDR'];
+ $this->handleRequest($this->requestKey, $this->requestAction);
+ } else {
+ /* If no key, or key not allowed return 401 */
+ $this->response(401);
+ }
+ }
+
+ /**
+ * Is the key allowed?
+ *
+ * @param string $key
+ * @return boolean
+ */
+ private function isAllowedToRequest(string $key)
+ {
+ return in_array($key, array_values($this->keys));
+ }
+
+ /**
+ * Return key "label" or "owner"
+ *
+ * @param string $key
+ * @return void
+ */
+ private function getKeyholder(string $key)
+ {
+ return array_keys($this->keys, $key)[0];
+ }
+
+ /**
+ * Handle the request based on the action. Default is to dump the request.
+ * action=list will return all available requests under a key
+ *
+ * @param string $key
+ * @param string $action
+ * @return void
+ */
+ private function handleRequest(string $key, $action = '')
+ {
+ echo $action;
+ switch ($action) {
+ case 'list':
+ $this->getRequests($key);
+ break;
+ exit;
+
+ case '':
+ $data = $this->prepareRequestDump();
+ $filename = $this->getUUID();
+ $dump = $this->dumpRequest($this->requestKey, $filename, $data);
+ if ($dump) {
+ /* Return request ID and path to it */
+ $message = <<<EOD
+ {
+ "id": "{$dump['id']}",
+ "path": "{$dump['path']}"
+ }
+ EOD;
+ $this->response(200, $message);
+ } else {
+ /* Saving failed */
+ $this->response(500, '{"error": "Error saving request"}');
+ }
+ break;
+ }
+ }
+
+ /**
+ * Prepare data to be saved to TXT file.
+ *
+ * @return string
+ */
+ private function prepareRequestDump()
+ {
+ return (string) <<<EOD
+ Request Key: {$this->requestKey} ({$this->requestKeyholder})
+ Request Method: {$this->requestMethod}
+ Content Type: {$this->requestType}
+ User Agent: {$this->requestAgent}
+ Received From: {$this->requestSource}
+ Recevied At: {$this->requestTime}
+ Request Body:
+ {$this->requestPayload}
+ EOD;
+ }
+
+ /**
+ * Save request locally and return path and ID, or false if saving fails.
+ *
+ * @param string $key
+ * @param string $filename
+ * @param string $data
+ * @return array|bool
+ */
+ private function dumpRequest(string $key, string $filename, string $data)
+ {
+ $path = DUMP_DIR.DS.$key.DS.$filename.'.txt';
+ $dirname = dirname($path);
+ if (!is_dir($dirname)) {
+ mkdir($dirname, 0755, true);
+ }
+ $fh = fopen($path, 'w');
+ if (fwrite($fh, $data)) {
+ fclose($fh);
+ return ['id'=>$filename, 'path'=>$path];
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Generate unique request ID using MD5.
+ *
+ * @return string
+ */
+ private function getUUID()
+ {
+ return (string) md5($this->requestSource . $this->requestTime);
+ }
+
+ /**
+ * Response handler
+ *
+ * @param integer $code
+ * @param string $message
+ * @return string
+ */
+ private function response(int $code, string $message = '')
+ {
+ header('Content-Type: application/json');
+ switch ($code) {
+ case '401':
+ http_response_code(401);
+ echo '{"error": "Not Allowed"}';
+ exit;
+ break;
+
+ case '200':
+ http_response_code(200);
+ echo $message;
+ break;
+
+ default:
+ http_response_code($code);
+ echo $message;
+ break;
+ }
+ }
+
+ /**
+ * Get all requests for a key and echo HTML.
+ *
+ * @param string $key
+ * @return void
+ */
+ private function getRequests(string $key)
+ {
+ echo "<h1>Showing requests for {$key} ($this->requestKeyholder)</h1>";
+ if (is_dir("data/{$key}")) {
+ echo '<table>';
+ $files = scandir("data/{$key}");
+ $files = array_diff($files, array('..', '.'));
+ foreach ($files as $file) {
+ print_r("<tr><td><a href='data/{$key}/{$file}'>{$file}</a></td>");
+ print_r('<td>' . date("F d Y H:i:s", filemtime("data/{$key}/{$file}")) . '</td></tr>');
+ }
+ echo '</table>';
+ }
+ }
+}
+
+$suteba = new Suteba();<
\ No newline at end of file