Initial commit

This commit is contained in:
2023-11-25 20:09:22 +01:00
commit 797205e7c5
10 changed files with 284 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
credentials.*
!*.sample.*

2
README.md Normal file
View File

@ -0,0 +1,2 @@
# Elwig REST API

138
www/clients.php Normal file
View File

@ -0,0 +1,138 @@
<?php
require "format.inc";
$format = get_fmt();
$cred_file_name = 'credentials.txt';
$clients = [];
$names = [];
$passwords = [];
foreach (scandir('.clients/') as $file) {
if ($file === '.' || $file === '..') continue;
array_push($clients, $file);
$content = file_get_contents(".clients/$file/$cred_file_name");
if ($content) {
$creds = explode(":", explode("\n", $content)[0]);
$names[$file] = $creds[0];
$passwords[$file] = $creds[1];
}
}
$path = $_SERVER['PATH_INFO'];
if ($path == '') {
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
header('Status: 405');
header('Allow: GET');
if ($format === 'text') {
header('Content-Type: text/plain; charset=UTF-8');
echo "405 Method Not Allowed :(\n";
} else if ($format === 'json') {
header('Content-Type: application/json; charset=UTF-8');
echo "{\"status\": \"error\", \"errors\": [{\"message\": \"Method not allowed\"}]}\n";
} else {
header('Content-Type: text/html; charset=UTF-8');
header('Content-Length: 0');
}
exit();
}
if ($format === 'text' || $format === 'html') {
header('Content-Type: text/plain; charset=UTF-8');
foreach ($clients as $c)
echo "$c\n";
} else if ($format === 'json') {
header('Content-Type: application/json; charset=UTF-8');
echo "{\"status\": \"success\", \"data\": [";
$first = true;
foreach ($clients as $c) {
if (!$first) echo ",";
echo "\n {\"name\": \"$c\"}";
$first = false;
}
echo "\n]}\n";
} else if ($format === 'html') {
header('Content-Type: text/html; charset=UTF-8');
// TODO
}
exit();
}
foreach ($clients as $c) {
if ($path !== "/$c" && !str_starts_with($path, "/$c/"))
continue;
header('Content-Type: text/plain; charset=UTF-8');
if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']) || $_SERVER['PHP_AUTH_USER'] !== $names[$c] || $_SERVER['PHP_AUTH_PW'] !== $passwords[$c]) {
header('Status: 401');
header('WWW-Authenticate: Basic realm="Elwig"');
exit("401 Unauthorized :(\n");
} elseif ($path === "/$c") {
header("Location: $c/");
header('Status: 303');
exit("303 See Other :)\n");
} elseif ($path === "/$c/") {
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
header("Status: 405");
header("Allow: GET");
exit("405 Method Not Allowed :(\n");
}
system("ls -Al .clients/$c/");
exit();
}
$file = substr($path, strlen("/$c/"));
$path = ".clients/$c/$file";
if (str_contains($file, '/')) {
header("Status: 400");
exit("400 Bad Request :(\n");
} elseif ($_SERVER['REQUEST_METHOD'] === 'GET') {
$size = filesize($path);
if ($size === false) {
header("Status: 404");
exit("404 Not Found :(\n");
}
$type = mime_content_type($path);
header("Content-Type: $type");
header("Content-Disposition: attachment; filename=\"$file\"");
header("Content-Length: $size");
readfile($path);
} elseif ($_SERVER['REQUEST_METHOD'] === 'PUT') {
$putdata = fopen('php://input', 'r');
$fp = fopen($path, 'wb');
if ($fp === false) {
header("Status: 500");
exit("500 Internal Server Error :(\n");
}
while ($data = fread($putdata, 4096))
fwrite($fp, $data);
fclose($fp);
fclose($putdata);
header("Status: 201");
exit("201 Created :)\n");
} elseif ($_SERVER['REQUEST_METHOD'] === 'DELETE') {
if (unlink($path) === false) {
header("Status: 500");
exit("500 Internal Server Error :(\n");
}
exit("200 OK :)\n");
} else {
header("Status: 405");
header("Allow: GET, PUT, DELETE");
exit("405 Method Not Allowed :(\n");
}
exit();
}
header("Status: 404");
if ($format === 'text') {
header('Content-Type: text/plain; charset=UTF-8');
echo "404 Not Found :(\n";
} else if ($format === 'json') {
header('Content-Type: application/json; charset=UTF-8');
echo "{\"status\": \"error\", \"errors\": [{\"message\": \"Not found\"}]}\n";
} else {
header('Content-Type: text/html; charset=UTF-8');
header('Content-Length: 0');
}
exit();

View File

@ -0,0 +1,7 @@
<?php
global $CREDENTIALS;
$CREDENTIALS = [
'name' => [
'username' => 'password',
],
];

69
www/files/index.php Normal file
View File

@ -0,0 +1,69 @@
<?php
require "../format.inc";
if ($_SERVER['PATH_INFO'] !== '') {
header('Status: 404');
header('Content-Length: 0');
exit();
}
$files = [];
foreach (scandir('.') as $file) {
if ($file === '.' || $file === '..' || str_ends_with($file, ".php")) continue;
$files[$file] = [filesize($file), filemtime($file), filectime($file)];
}
$format = get_fmt();
if ($format === 'json') {
header('Content-Type: application/json; charset=UTF-8');
echo "{\"status\": \"success\", \"data\": [\n";
$first = true;
foreach ($files as $name => [$size, $mtime, $ctime]) {
if (!$first) echo ",\n";
$p1 = strrpos($name, '-') + 1;
$p2 = strrpos($name, '.');
$vers = substr($name, $p1, $p2 - $p1);
$url = "https://www.necronda.net/elwig/files/$name";
$mod = date(DATE_ATOM, $mtime);
$cre = date(DATE_ATOM, $ctime);
echo " {\"version\": \"$vers\", \"name\": \"$name\", \"url\": \"$url\", \"size\": $size, \"created\": \"$cre\", \"modified\": \"$mod\"}";
$first = false;
}
echo "\n]}\n";
} else if ($format === 'text') {
header('Content-Type: text/plain; charset=UTF-8');
foreach ($files as $name => [$size, $mtime, $ctime]) {
echo "$name\t" . number_format($size / 1024 / 1024, 1) . " MB\n";
}
} else if ($format === 'html') {
header('Content-Type: text/html; charset=UTF-8');
?>
<!DOCTYPE html>
<html lang="de">
<head>
<title>Downloads - Elwig - Elektronische Winzergenossenschaftsverwaltung</title>
<meta charset="UTF-8"/>
<link rel="icon" sizes="16x16 20x20 24x24 30x30 32x32 36x36 40x40 48x48 60x60 64x64 72x72 80x80 96x96 128x128 256x256" href="../res/elwig.ico"/>
<link rel="stylesheet" href="../res/style.css"/>
</head>
<body>
<h1>Downloads</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Größe</th>
</tr>
</thead>
<tbody>
<?php
foreach ($files as $name => [$size, $mtime, $ctime]) {
echo " <tr><td><a href='$name'>$name</a></td><td>" . number_format($size / 1024 / 1024, 1) . " MB</td></tr>\n";
}
?>
</tbody>
</table>
<p><a href="?format=json">JSON-Format</a></p>
</body>
</html>
<?php }

46
www/format.inc Normal file
View File

@ -0,0 +1,46 @@
<?php
function get_fmt(): string {
$fmt = _get_fmt();
if ($fmt === 'ascii') {
header('Status: 303');
header('Location: ?format=text');
exit();
} else if ($fmt !== 'json' && $fmt !== 'html' && $fmt !== 'text') {
header('Status: 300');
header('Content-Type: text/html; charset=UTF-8');
echo "<!DOCTYPE html><html><head></head><body>\n<a href='?format=html'>HTML</a><br/>\n<a href='?format=json'>JSON</a><br/>\n<a href='?format=text'>Text</a>\n</body></html>\n";
exit();
}
return $fmt;
}
function _get_fmt(): string {
if (!empty($_GET['format'])) return $_GET['format'];
$fmts = [];
foreach (explode(',', $_SERVER['HTTP_ACCEPT']) as $acc) {
$acc = explode(';', trim($acc));
$q = 1;
if (sizeof($acc) > 1) {
$qv = trim($acc[1]);
if (str_starts_with($qv, 'q=')) {
$q = (double)substr($qv, 2);
}
}
$fmts[trim($acc[0])] = $q;
}
arsort($fmts, SORT_NUMERIC);
array_filter($fmts, function($k) {
return str_contains($k, '/json') || $k === 'text/html' || $k === 'text/plain' || str_contains($k, 'text/*');
});
$type = sizeof($fmts) > 0 ? array_key_first($fmts) : null;
if (str_contains($type, '/json')) {
return 'json';
} else if ($type === 'text/html') {
return 'html';
} else {
return 'text';
}
}

15
www/index.html Normal file
View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="de">
<head>
<title>Elwig - Elektronische Winzergenossenschaftsverwaltung</title>
<meta charset="UTF-8"/>
<link rel="icon" sizes="16x16 20x20 24x24 30x30 32x32 36x36 40x40 48x48 60x60 64x64 72x72 80x80 96x96 128x128 256x256" href="res/elwig.ico"/>
<link rel="stylesheet" href="res/style.css"/>
</head>
<body>
<h1>Elwig</h1>
<h2>Elektronische Winzergenossenschaftsverwaltung</h2>
<a href="https://git.necronda.net/winzer">Source</a>
<a href="files/">Downloads</a>
</body>
</html>

BIN
www/res/elwig.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

BIN
www/res/elwig.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

5
www/res/style.css Normal file
View File

@ -0,0 +1,5 @@
html {
font-family: 'Arial', sans-serif;
}