Add a lot of work
This commit is contained in:
parent
827ae15917
commit
cd74f9a167
10 changed files with 320 additions and 1 deletions
2
.gitignore
vendored
Executable file
2
.gitignore
vendored
Executable file
|
@ -0,0 +1,2 @@
|
||||||
|
css/
|
||||||
|
meowboard.db
|
0
LICENSE
Normal file → Executable file
0
LICENSE
Normal file → Executable file
5
README.md
Normal file → Executable file
5
README.md
Normal file → Executable file
|
@ -1,3 +1,6 @@
|
||||||
# meowboard
|
# meowboard
|
||||||
|
|
||||||
Basic image gallery in PHP.
|
Simple multi-user portfolio image board script in PHP.
|
||||||
|
|
||||||
|
## Setup considerations
|
||||||
|
If you're going to use meowboard, you probably will want to either make your own user interface (at present, you will have to modify `include/templates.php` to do this) or download [Zimit framework](https://firezenk.github.io/zimit/) into `css/`. Zimit is not included with this software for licensing purposes. meowboard is developed with PHP 8.0 but might work with PHP 7.
|
||||||
|
|
123
include/db.php
Executable file
123
include/db.php
Executable file
|
@ -0,0 +1,123 @@
|
||||||
|
<?php
|
||||||
|
if(!file_exists("meowboard.db")) {
|
||||||
|
die("Database not found.");
|
||||||
|
}
|
||||||
|
class Store extends SQLite3
|
||||||
|
{
|
||||||
|
function __construct()
|
||||||
|
{
|
||||||
|
$this->open('meowboard.db');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = new Store();
|
||||||
|
$sitenameStatement = $db->prepare("SELECT * FROM settings WHERE key = 'sitename'");
|
||||||
|
$result = $sitenameStatement->execute();
|
||||||
|
$sitename = $result->fetchArray()[1];
|
||||||
|
|
||||||
|
function saltString($string) {
|
||||||
|
global $db;
|
||||||
|
|
||||||
|
$saltStatement = $db->prepare("SELECT * FROM settings WHERE key = 'salt'");
|
||||||
|
$result = $saltStatement->execute();
|
||||||
|
$salt = $result->fetchArray()[1];
|
||||||
|
|
||||||
|
return hash("sha512", $string . $salt);
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyPassword($username, $password) {
|
||||||
|
global $db;
|
||||||
|
|
||||||
|
$password = hash("sha512", $password);
|
||||||
|
|
||||||
|
$grabUser = $db->prepare("SELECT * FROM users WHERE username = ?");
|
||||||
|
$grabUser->bindParam(1, $username, SQLITE3_TEXT);
|
||||||
|
$result = $grabUser->execute();
|
||||||
|
$resultArray = $result->fetchArray();
|
||||||
|
|
||||||
|
$storedPassword = $resultArray[1];
|
||||||
|
$pepper = $resultArray[2];
|
||||||
|
|
||||||
|
$passwordFinal = hash("sha512", saltString($password) . $pepper);
|
||||||
|
if ($passwordFinal != $storedPassword) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function issueSessionToken($username) {
|
||||||
|
global $db;
|
||||||
|
|
||||||
|
$token = bin2hex(random_bytes(256));
|
||||||
|
$tokenStore = saltString($token); // We store this value and give the user the unhashed token.
|
||||||
|
$expiry = time() + 2_419_200_000; // 28 days.
|
||||||
|
|
||||||
|
$insertTokenStatement = $db->prepare("INSERT INTO tokens (hash, username, expiry) VALUES (?, ?, ?)");
|
||||||
|
$insertTokenStatement->bindParam(1, $tokenStore, SQLITE3_TEXT);
|
||||||
|
$insertTokenStatement->bindParam(2, $username, SQLITE3_TEXT);
|
||||||
|
$insertTokenStatement->bindParam(3, $expiry, SQLITE3_INTEGER);
|
||||||
|
$result = $insertTokenStatement->execute();
|
||||||
|
|
||||||
|
return $token;
|
||||||
|
}
|
||||||
|
|
||||||
|
function flushSessionTokens() {
|
||||||
|
global $db;
|
||||||
|
|
||||||
|
$flushStatement = $db->prepare("DELETE FROM tokens WHERE expiry < ?");
|
||||||
|
$flushStatement->bindParam(1, time(), SQLITE3_INTEGER);
|
||||||
|
$flushStatement->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
function purgeSessionTokens() {
|
||||||
|
global $db;
|
||||||
|
|
||||||
|
$db->execute("DELETE FROM tokens");
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteSessionToken($token) {
|
||||||
|
global $db;
|
||||||
|
$tokenStore = saltString($token);
|
||||||
|
|
||||||
|
$deleteStatement = $db->prepare("DELETE FROM tokens WHERE hash = ?");
|
||||||
|
$deleteStatement->bindParam(1, $tokenStore, SQLITE3_TEXT);
|
||||||
|
$deleteStatement->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkSessionToken($token) {
|
||||||
|
global $db;
|
||||||
|
flushSessionTokens();
|
||||||
|
|
||||||
|
$tokenStore = saltString($token);
|
||||||
|
|
||||||
|
$getTokenStatement = $db->prepare("SELECT hash FROM tokens WHERE hash = ?");
|
||||||
|
$getTokenStatement->bindParam(1, $tokenStore, SQLITE3_TEXT);
|
||||||
|
$result = $getTokenStatement->execute();
|
||||||
|
$tokenInDB = $result->fetchArray()[0];
|
||||||
|
|
||||||
|
if ($tokenInDB == $tokenStore) { return 1; }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tokenToUsername($token) {
|
||||||
|
global $db;
|
||||||
|
|
||||||
|
$tokenStore = saltString($token);
|
||||||
|
$getTokenStatement = $db->prepare("SELECT username FROM tokens WHERE hash = ?");
|
||||||
|
$getTokenStatement->bindParam(1, $tokenStore, SQLITE3_TEXT);
|
||||||
|
$result = $getTokenStatement->execute();
|
||||||
|
$username = $result->fetchArray()[0];
|
||||||
|
|
||||||
|
return $username;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAdmin($username) {
|
||||||
|
global $db;
|
||||||
|
|
||||||
|
$getTokenStatement = $db->prepare("SELECT admin FROM users WHERE username = ?");
|
||||||
|
$getTokenStatement->bindParam(1, $username, SQLITE3_TEXT);
|
||||||
|
$result = $getTokenStatement->execute();
|
||||||
|
return $result->fetchArray()[0];
|
||||||
|
}
|
||||||
|
?>
|
41
include/templates.php
Executable file
41
include/templates.php
Executable file
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
if(!isset($sitename)) { $sitename = "meowboard"; }
|
||||||
|
$headerGuest = '<!DOCTYPE html><html><head><meta charset="utf-8"><title>' . $sitename . '</title><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="stylesheet" type="text/css" href="css/zimit.min.css"><link rel="stylesheet" type="text/css" href="css/custom.css"></head><body><header><nav><a class="site">' . $sitename . '</a><ul id="menu"><li><a href="login.php" id="menu">login</a></li></ul></nav></header><br/><main>';
|
||||||
|
$headerNoButtons = '<!DOCTYPE html><html><head><meta charset="utf-8"><title>' . $sitename . '</title><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="stylesheet" type="text/css" href="css/zimit.min.css"><link rel="stylesheet" type="text/css" href="css/custom.css"></head><body><header><nav><a class="site">' . $sitename . '</a></nav></header><br/><main>';
|
||||||
|
$headerLoggedIn = '<!DOCTYPE html><html><head><meta charset="utf-8"><title>' . $sitename . '</title><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="stylesheet" type="text/css" href="css/zimit.min.css"><link rel="stylesheet" type="text/css" href="css/custom.css"></head><body><header><nav><a class="site">' . $sitename . '</a><ul id="menu"><li><a href="upload.php" id="menu">upload</a></li><li><a href="settings.php" id="menu">settings</a></li><li><a href="logout.php" id="menu">logout</a></li></ul></nav></header><br/><main>';
|
||||||
|
$headerAdmin = '<!DOCTYPE html><html><head><meta charset="utf-8"><title>' . $sitename . '</title><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="stylesheet" type="text/css" href="css/zimit.min.css"><link rel="stylesheet" type="text/css" href="css/custom.css"></head><body><header><nav><a class="site">' . $sitename . '</a><ul id="menu"><li><a href="upload.php" id="menu">upload</a></li><li><a href="settings.php" id="menu">settings</a></li><li><a href="admin.php" id="menu">admin</a></li><li><a href="logout.php" id="menu">logout</a></li></ul></nav></header><br/><main>';
|
||||||
|
$footer = '</main><footer>bla bla copyright content and like uh meow</footer></body></html>';
|
||||||
|
function dispImage($imgurl, $tags) {
|
||||||
|
$imgstart = '<div class="thumbnail" style="padding:10px;"><img src="' . $imgurl . '"/><br/><div class="details" style="font-size:20px;">';
|
||||||
|
$imgend = '</div></div>';
|
||||||
|
echo "$imgstart";
|
||||||
|
for($x = 0; $x < count($tags); $x++) {
|
||||||
|
$tagblock = '<tag color="black">' . $tags[$x] . '</tag>';
|
||||||
|
echo "$tagblock";
|
||||||
|
}
|
||||||
|
echo "$imgend";
|
||||||
|
}
|
||||||
|
|
||||||
|
function showHeader($hideButtons = 0) {
|
||||||
|
// PHP is so quirky!!! I love scoping :sparkling-heart:
|
||||||
|
global $headerNoButtons;
|
||||||
|
global $headerLoggedIn;
|
||||||
|
global $headerGuest;
|
||||||
|
global $headerAdmin;
|
||||||
|
global $db;
|
||||||
|
|
||||||
|
if ($hideButtons == 1) { echo $headerNoButtons; return; }
|
||||||
|
if (!empty($db) && isset($_COOKIE["meowboardSession"])) {
|
||||||
|
if (isAdmin(tokenToUsername($_COOKIE["meowboardSession"])) == 1) {
|
||||||
|
echo $headerAdmin;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
echo $headerLoggedIn;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isset($_COOKIE["meowboardSession"])) { echo $headerLoggedIn; return; }
|
||||||
|
echo $headerGuest;
|
||||||
|
}
|
||||||
|
?>
|
14
index.php
Executable file
14
index.php
Executable file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
include 'include/db.php';
|
||||||
|
include 'include/templates.php';
|
||||||
|
|
||||||
|
if (isset($_COOKIE["meowboardSession"])) {
|
||||||
|
if (checkSessionToken($_COOKIE["meowboardSession"]) == 0) {
|
||||||
|
setcookie("meowboardSession", "", 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
showHeader();
|
||||||
|
dispImage("https://media.discordapp.net/attachments/823599010166210610/963104145164886077/image0-4.gif", array("meow","witch"));
|
||||||
|
echo $footer;
|
||||||
|
?>
|
87
install.php
Executable file
87
install.php
Executable file
|
@ -0,0 +1,87 @@
|
||||||
|
<?php
|
||||||
|
$sitename = "meowboard";
|
||||||
|
include 'include/templates.php';
|
||||||
|
|
||||||
|
if (file_exists("meowboard.db")) {
|
||||||
|
die("Meowboard is already installed. If you are a webmaster, you may want to delete this file.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||||
|
$username = $_POST['username'];
|
||||||
|
if (empty($_POST['password'])) {
|
||||||
|
die("No admin password.");
|
||||||
|
}
|
||||||
|
// The password is hashed before being entered into the database for security reasons.
|
||||||
|
// It is then hashed with the salt and pepper.
|
||||||
|
$password = hash("sha512", $_POST['password']);
|
||||||
|
$sitename = $_POST['sitename'];
|
||||||
|
|
||||||
|
if(empty($sitename)) {
|
||||||
|
die("No site name.");
|
||||||
|
}
|
||||||
|
if(empty($username)) {
|
||||||
|
die("No admin username.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$salt = random_bytes(128);
|
||||||
|
$saltHex = bin2hex($salt);
|
||||||
|
$pepper = random_bytes(64);
|
||||||
|
$pepperHex = bin2hex($pepper);
|
||||||
|
|
||||||
|
echo $saltHex;
|
||||||
|
echo "<br>";
|
||||||
|
echo $pepperHex;
|
||||||
|
echo "<br>";
|
||||||
|
|
||||||
|
$passwordFinal = hash("sha512", hash("sha512", $password . $saltHex) . $pepperHex);
|
||||||
|
|
||||||
|
class Store extends SQLite3
|
||||||
|
{
|
||||||
|
function __construct()
|
||||||
|
{
|
||||||
|
$this->open('meowboard.db');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This creates the database file on the system automatically.
|
||||||
|
$db = new Store();
|
||||||
|
|
||||||
|
// Initialise tables in the database.
|
||||||
|
$db->exec('CREATE TABLE users(username TEXT PRIMARY KEY UNIQUE NOT NULL, password TEXT NOT NULL, pepper TEXT NOT NULL, admin INTEGER DEFAULT 0)');
|
||||||
|
$db->exec('CREATE TABLE images(id INTEGER PRIMARY KEY AUTOINCREMENT, location TEXT NOT NULL, uploader TEXT NOT NULL, tags TEXT)');
|
||||||
|
$db->exec('CREATE TABLE settings(key TEXT PRIMARY KEY UNIQUE NOT NULL, value TEXT DEFAULT NULL)');
|
||||||
|
$db->exec('CREATE TABLE tokens(hash TEXT PRIMARY KEY NOT NULL, username TEXT NOT NULL, expiry INTEGER)');
|
||||||
|
|
||||||
|
// Add the admin user to the database.
|
||||||
|
$insert_user_query = $db->prepare('INSERT INTO users (username, password, pepper, admin) VALUES (?, ?, ?, 1)');
|
||||||
|
$insert_user_query->bindParam(1, $username, SQLITE3_TEXT);
|
||||||
|
$insert_user_query->bindParam(2, $passwordFinal, SQLITE3_TEXT);
|
||||||
|
$insert_user_query->bindParam(3, $pepperHex, SQLITE3_TEXT);
|
||||||
|
$result = $insert_user_query->execute();
|
||||||
|
|
||||||
|
// Add the salt into the database otherwise it will be impossible to login.
|
||||||
|
// Also add site name in.
|
||||||
|
$insert_salt_query = $db->prepare('INSERT INTO settings (key, value) VALUES ("salt", ?)');
|
||||||
|
$insert_salt_query->bindParam(1, $saltHex, SQLITE3_TEXT);
|
||||||
|
$result = $insert_salt_query->execute();
|
||||||
|
|
||||||
|
$insert_sitename_query = $db->prepare('INSERT INTO settings (key, value) VALUES ("sitename", ?)');
|
||||||
|
$insert_sitename_query->bindParam(1, $sitename, SQLITE3_TEXT);
|
||||||
|
$result = $insert_sitename_query->execute();
|
||||||
|
|
||||||
|
echo "Meowboard has been installed and is ready to use!";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
showHeader(1);
|
||||||
|
|
||||||
|
echo '<h3>Install meowboard</h3>';
|
||||||
|
echo '<h2>Admin account credentials</h2>';
|
||||||
|
echo '<form method="post"><label for="username">Username</label><br/> <input type="text" id="username" name="username"/> <br/>';
|
||||||
|
echo '<label for="password">Password</label><br/> <input type="password" id="password" name="password" /> <br/>';
|
||||||
|
echo '<h2>Site settings</h2>';
|
||||||
|
echo '<label for="sitename">Site name</label><br/> <input type="text" id="sitename" name="sitename"/> <br/>';
|
||||||
|
echo '<br/><button>install</button></form>';
|
||||||
|
|
||||||
|
echo $footer;
|
||||||
|
}
|
||||||
|
?>
|
27
login.php
Executable file
27
login.php
Executable file
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
include 'include/db.php';
|
||||||
|
include 'include/templates.php';
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||||
|
if (isset($_COOKIE["meowboardSession"])) { die('<meta http-equiv="refresh" content="0; url=index.php">'); }
|
||||||
|
|
||||||
|
$username = $_POST['username'];
|
||||||
|
$password = $_POST['password'];
|
||||||
|
|
||||||
|
if (empty($username) || empty($password)) { die('<meta http-equiv="refresh" content="0">'); }
|
||||||
|
if (verifyPassword($username, $password) == 0) { die('<meta http-equiv="refresh" content="0">'); }
|
||||||
|
|
||||||
|
$token = issueSessionToken($username);
|
||||||
|
|
||||||
|
setcookie("meowboardSession", $token, time() + 2_419_200_000);
|
||||||
|
|
||||||
|
echo '<meta http-equiv="refresh" content="0; url=index.php">';
|
||||||
|
} else {
|
||||||
|
showHeader();
|
||||||
|
echo "<h3>Login</h3>";
|
||||||
|
echo '<form method="post"><label for="username">Username</label><br/> <input type="text" id="username" name="username"/> <br/>';
|
||||||
|
echo '<label for="password">Password</label><br/> <input type="password" id="password" name="password" /> <br/>';
|
||||||
|
echo '<br/><button>login</button></form>';
|
||||||
|
echo $footer;
|
||||||
|
}
|
||||||
|
?>
|
7
logout.php
Normal file
7
logout.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
include 'include/db.php';
|
||||||
|
|
||||||
|
if (isset($_COOKIE["meowboardSession"])) { deleteSessionToken($_COOKIE["meowboardSession"]); }
|
||||||
|
setcookie("meowboardSession", "", 1);
|
||||||
|
echo '<meta http-equiv="refresh" content="0; url=index.php">';
|
||||||
|
?>
|
15
upload.php
Normal file
15
upload.php
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
include 'include/db.php';
|
||||||
|
include 'include/templates.php';
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] == "POST") { }
|
||||||
|
else {
|
||||||
|
showHeader();
|
||||||
|
echo '<h3>Upload</h3><form action="upload.php" method="post" enctype="multipart/form-data">
|
||||||
|
<input type="file" name="fileToUpload" id="fileToUpload">
|
||||||
|
<label for="tags">Tags</label><br/><input type="text" id="tags" name="tags">
|
||||||
|
<br/><button>submit</button>
|
||||||
|
</form>';
|
||||||
|
echo $footer;
|
||||||
|
}
|
||||||
|
?>
|
Loading…
Reference in a new issue