Pk11 3 years ago committed by GitHub
parent 218258e13b
commit 9c80fd61f6

@ -25,6 +25,8 @@
* For more information, please refer to <http://unlicense.org/>
*/
$start_time = microtime(true);
// Return source code
if(isset($_GET['source'])) {
header("Content-Type: text/plain");
@ -40,7 +42,6 @@
* define('ADMIN_ID', '<SHA1 uid from your cookies>'); // uid that is able to delete anything
* define('UID_SALT', '<some string to salt uids with>');
* define('MAX_POSTS', 50); // Max posts after which old posts will be auto deleted. Only existing posts count, not manually deleted ones.
* define('DISCORD_WEBHOOK', 'https://discord.com/api/webhooks/<webhook id>/<webhook token>'); // A message will be sent to this webhook on post, for easier moderation
*
* // Database constants for PostgreSQL database
* $DB_HOST = 'localhost';
@ -55,6 +56,7 @@
* post_time TIMESTAMPTZ NOT NULL DEFAULT NOW(),
* user_id VARCHAR(40),
* name VARCHAR(256),
* trip VARCHAR(10),
* email VARCHAR(256),
* comment VARCHAR(2048),
* img VARCHAR(256)
@ -81,21 +83,30 @@
&& ($img['size'] > 0) && ($img['size'] <= MAX_FILE_SIZE)) {
if($img['error'] == 0) {
// Move the file to the target upload folder
$name = time();
$target = UPLOAD_PATH . $name . $extensions[$img['type']];
if(move_uploaded_file($img['tmp_name'], $target)) {
$img_name = time();
$ext = $extensions[$img['type']];
$temp = UPLOAD_PATH . "__temp$ext";
$target = UPLOAD_PATH . $img_name . $ext;
if(move_uploaded_file($img['tmp_name'], $temp)) {
$output = null;
$ret = null;
exec("ffmpeg -y -loglevel error -i '$target' -vf 'scale=-1:min(100\,ih)' " . THUMB_PATH . "$name.jpg", $output, $ret);
// Strip EXIF data
exec("ffmpeg -y -loglevel error -i $temp '$target'", $output, $ret);
@unlink($temp);
if($ret != 0)
return 'Unable to upload image, please contact the webmaster.';
// Create thumbnail
exec("ffmpeg -y -loglevel error -i '$target' -filter_complex 'color=#ffccdd[c];[c][0]scale2ref[cs][0s];[cs][0s]overlay=shortest=1[o];[o]scale=min(200\,iw):-1[o];[o]scale=-1:min(100\,ih)' " . THUMB_PATH . "$img_name.jpg", $output, $ret);
if($ret != 0) {
// The new image file move failed, so delete the temporary file and return an error
@unlink($target);
return 'Unable to create thumbnail, please contact the webmaster.';
}
} else {
// The new image file move failed, so delete the temporary file and return an error
@unlink($img['tmp_name']);
return 'Unable to upload image, please contact the webmaster.';
return 'Unable to rename image, please contact the webmaster.';
}
}
} else {
@ -131,16 +142,18 @@
}
}
$trip_name = trip_name($name);
// Add post to database
$query = "INSERT INTO posts (user_id, name, email, comment, img) VALUES ($1, $2, $3, $4, $5)";
$query = "INSERT INTO posts (user_id, name, trip, email, comment, img) VALUES ($1, $2, $3, $4, $5, $6)";
$params = [
empty($uid) ? NULL : $uid,
empty($name) ? 'Anonymous' : htmlspecialchars($name),
empty($trip_name['name']) ? 'Anonymous' : htmlspecialchars($trip_name['name']),
empty($trip_name['trip']) ? NULL : htmlspecialchars($trip_name['trip']),
empty($email) ? NULL : $email,
empty($comment) ? NULL : htmlspecialchars($comment),
empty($target) ? NULL : basename($target)
];
webhook($params[1], $params[3], 'http://' . $_SERVER['SERVER_NAME'] . dirname($_SERVER['PHP_SELF']) . $target); // Send to discord for moderation
pg_query_params($query, $params) or die('Query failed: ' . pg_last_error());
return ""; // Success, no error
@ -160,7 +173,7 @@
// Prints the post list
function show_posts() {
$query = 'SELECT post_id, user_id, name, email, comment, img, TO_CHAR(post_time, \'YYYY-MM-DD HH24:MI (TZ)\') AS post_time FROM posts ORDER BY posts.post_time';
$query = 'SELECT post_id, user_id, name, trip, email, comment, img, post_time FROM posts ORDER BY posts.post_time';
$result = pg_query($query) or die('Query failed: ' . pg_last_error());
// Clean up old posts
@ -176,26 +189,37 @@
$show_delete = FALSE;
echo '<form action="' . $_SERVER['PHP_SELF'] . '#bottom" method="post">';
while($row = pg_fetch_array($result)) {
echo "<fieldset id=\"p{$row['post_id']}\">";
echo "<div id=\"p{$row['post_id']}\" class=\"post\">";
$time_rel = time_ago_en($row['post_time']);
$time_abs = date('Y-m-d H:m (T)', strtotime($row['post_time']));
echo '<legend>';
echo '<p class="posthead">';
if((!empty($row['user_id']) && ($row['user_id'] == $_COOKIE['uid'])) || (!empty(ADMIN_ID) && ($_COOKIE['uid'] == ADMIN_ID))) {
echo '<input type="checkbox" name="delete[]" value="' . $row['post_id'] . '" /> ';
$show_delete = TRUE;
}
echo '<strong class="name">';
if(!empty($row['email']))
echo "<strong><a href=\"mailto:{$row['email']}\">{$row['name']}</a></strong> {$row['post_time']} ";
echo "<a href=\"mailto:{$row['email']}\">{$row['name']}</a>";
else
echo "<strong>{$row['name']}</strong> {$row['post_time']} ";
echo $row['name'];
echo '</strong>';
if(!empty($row['trip']))
echo "<span class=\"trip\">◆{$row['trip']}</span>";
echo " <span title=\"$time_abs\">$time_rel</span> ";
echo "<a href=\"#p{$row['post_id']}\">#{$row['post_id']}</a> ";
// Find references
$post_id = pg_escape_string($row['post_id']);
$ref_query = "SELECT post_id FROM posts WHERE comment LIKE '%&gt;&gt;$post_id%' ORDER BY post_time";
$ref_result = pg_query_params($ref_query, []) or die('Query failed: ' . pg_last_error());
while($ref = pg_fetch_array($ref_result))
echo "<a href=\"#p{$ref['post_id']}\">&gt;&gt;{$ref['post_id']}</a> ";
echo '</legend>';
echo '</p>';
echo '<div class="postbody">';
if(!empty($row['img'])){
echo '<a href="' . UPLOAD_PATH . $row['img'] . '" target="_blank">';
echo '<img src="' . THUMB_PATH . preg_replace('/\.(?:png|gif|bmp)$/', '.jpg', $row['img']) . '" alt="' . $row['img'] . '" />';
@ -205,14 +229,15 @@
// Process quotes, links, and newlines
if(!empty($row['comment'])) {
$comment = $row['comment'];
$comment = preg_replace('/^&gt;(?!&gt;\d).+/m', '<strong>$0</strong>', $comment);
$comment = preg_replace('/^&gt;(?!&gt;\d).+/m', '<strong class="quote">$0</strong>', $comment);
$comment = preg_replace('/(?:https?|mailto|tel|ftp):[^\s]+/m', '<a href="$0">$0</a>', $comment);
$comment = preg_replace_callback('/&gt;&gt;\s*(\d+)/', quote_link, $comment);
$comment = str_replace("\n", "<br />", $comment);
echo "<p>$comment</p>";
echo "<div>$comment</div>";
}
echo '</div>';
echo '</fieldset>';
echo '</div>';
}
pg_free_result($result);
@ -235,54 +260,54 @@
}
}
// Sends a webhook to Discord
function webhook($name, $message, $img) {
if(empty(DISCORD_WEBHOOK))
return;
$data = [
'username' => $name,
'embeds' => [
[
'title' => "New Post",
'url' => 'http://' . $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'] . '#bottom',
'description' => $message,
'image' => [
'url' => $img
]
]
]
];
// https://stackoverflow.com/a/9619947
function time_ago_en($time) {
if(!is_numeric($time))
$time = strtotime($time);
$periods = ['second', 'minute', 'hour', 'day', 'week', 'month', 'year', 'age'];
$lengths = [60, 60, 24, 7, 4.35, 12, 100];
$curl = curl_init(DISCORD_WEBHOOK);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-Type: application/json"));
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
curl_exec($curl);
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
if($status != 204)
die("Error: Sending webhook failed with status $status.");
$now = time();
$difference = $now - $time;
if($difference <= 10 && $difference >= 0)
return $tense = 'just now';
else if($difference > 0)
$tense = 'ago';
else if($difference < 0)
$tense = 'later';
for($j = 0; $difference >= $lengths[$j] && $j < count($lengths)-1; $j++) {
$difference /= $lengths[$j];
}
?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>BBS | ピケ.コム</title>
</head>
<body>
<p>
[<a href="#bottom">bottom</a>]
</p>
$difference = round($difference);
$period = $periods[$j] . ($difference >1 ? 's' :'');
return "{$difference} {$period} {$tense} ";
}
function trip_name($name) {
$split = explode('#', $name);
$name = array_shift($split);
if(empty($split[0]))
return ['name' => $name];
$key = implode('#', $split);
$salt = substr($key . 'H.', 1, 2);
$salt = preg_replace('/[^\.-z]/', '.', $salt);
$salt = strtr($salt, ':;<=>?@[\\]^_`', 'ABCDEFGabcdef');
$trip = crypt($key, $salt);
$trip = substr($trip, -10);
return [
'name' => $name,
'trip' => $trip
];
}
//// code start ////
<?php
// Connect to the database
$dbc = pg_connect("host=$DB_HOST dbname=$DB_NAME user=$DB_USER password=$DB_PASSWORD")
or die('Could not connect: ' . pg_last_error());
@ -296,22 +321,202 @@
$save_cookie = isset($_POST['save_cookie']);
$err = post($name, $email, $comment, $img, $save_cookie);
if($err == "") {
# Redirect to latest post
$query = 'SELECT post_id FROM posts ORDER BY posts.post_time DESC LIMIT 1';
$result = pg_query($query) or die('Query failed: ' . pg_last_error());
$row = pg_fetch_array($result);
if($row) {
pg_close($dbc);
header("Location: {$_SERVER['PHP_SELF']}#p{$row['post_id']}", true, 303);
die();
}
}
} else if($_POST['submit'] == 'Delete' && !empty($_COOKIE['uid'])) {
foreach($_POST['delete'] as $id) {
cleanup($id);
}
}
show_posts();
pg_close($dbc);
$mobile = preg_match('/Mobile|Nintendo DSi/', $_SERVER['HTTP_USER_AGENT']);
if($mobile) {
$header_image = 'header-mobile.gif';
} else {
$header_image = 'header.gif';
}
?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<form enctype="multipart/form-data" method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>#bottom">
<input type="hidden" name="MAX_FILE_SIZE" value="<?php echo MAX_FILE_SIZE; ?>" />
<fieldset id="bottom">
<legend>New Post</legend>
<title>BBS | ピケ.コム</title>
<link rel="alternate" type="application/rss+xml" title="RSS feed" href="/rss.php" />
<style type="text/css">
body {
max-width: 960px;
background-color: #ebc;
background: url(bg2.gif);
background-attachment: fixed;
color: #201;
}
hr {
border: 0;
border-top: 1px solid #534;
}
textarea {
max-width: 100%;
}
input[type=submit],
button {
background-color: #ebc;
border: 2px outset #dab;
}
input[type=submit]:active,
button:active {
background-color: #dab;
border: 2px inset #dab;
color: #201;
}
strong.error, del {
color: red;
}
strong.quote,
strong.name, strong.name > a,
span.trip {
color: green;
}
strong.quote {
font-weight: normal;
}
#newpost {
max-width: 500px;
padding-bottom: 10px;
}
.center {
margin-left: auto;
margin-right: auto;
display: block;
}
.post,
#newpost {
background-color: #fcd;
border: 5px outset #dab;
margin-bottom: 10px;
}
#newpost > h2 {
margin: 0;
}
#newpost input:not([type=submit]),
#newpost textarea {
background-color: #ebc;
border: 2px inset #dab;
}
#footer {
background-color: #ebc;
border: 5px inset #dab;
padding: 10px;
}
#footer p:first-of-type {
margin-top: 0;
}
#footer p:last-of-type {
margin-bottom: 0;
}
.post {
display: table;
padding: 0 10px 10px 0;
}
.posthead {
margin: 0 0 5px 0;
}
.postbody {
padding-left: 10px;
}
.postbody img {
float: left;
margin-right: 10px;
}
</style>
</head>
<body class="center">
<img class="center" src="<?php echo $header_image; ?>" alt="BBS.ピケ.コム" />
<p>
[<a href="#bottom">bottom</a>]
[<a href="<?php echo $_SERVER['PHP_SELF']; ?>">reload</a>]
[<a href="?source">source</a>]
[<a href="rss.php">feed</a>]
[<a href="rss.php?source">feed source</a>]
</p>
<hr />
<form enctype="multipart/form-data" method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>#newpost">
<input type="hidden" name="MAX_FILE_SIZE" value="<?php echo MAX_FILE_SIZE; ?>" />
<div id="newpost" class="center">
<h2>New Post</h2>
<div class="postbody">
<?php if($mobile) { ?>
<label for="name">Name:</label> (Optional)
<br />
<input id="name" name="name" value="<?php if(!empty($err)) echo htmlspecialchars($name); ?>" />
<br />
<br />
<label for="email">Email:</label> (Optional)
<br />
<input id="email" name="email" value="<?php if(!empty($err)) echo htmlspecialchars($email); ?>" />
<br />
<br />
<label for="comment">Comment:</label>
<br />
<textarea id="comment" name="comment" rows="10" cols="27"><?php if(!empty($err)) echo htmlspecialchars($comment); ?></textarea>
<br />
<br />
<label for="img">Image:</label> (Limit: <?php echo MAX_FILE_SIZE >> 10; ?> KiB)
<br />
<input type="file" id="img" name="img" />
<br />
<br />
<label for="save-cookie">Save cookie:</label>
<input type="checkbox" id="save-cookie" name="save_cookie" <?php if($_COOKIE['uid']) echo 'checked'; ?> />
<br />
(Allows deleting your own posts)
<br />
<br />
<input type="submit" value="Post" name="submit" />
<?php } else { ?>
<table>
<tr>
<td><label for="name">Name:</label></td>
@ -338,25 +543,56 @@
<td><input type="submit" value="Post" name="submit" /></td>
</tr>
</table>
<?php } ?>
<?php if(!empty($err)) echo "<br /><strong>$err</strong>"; ?>
</fieldset>
<?php if(!empty($err)) echo "<p><strong class=\"error\">$err</strong></p>"; ?>
</div>
</div>
</form>
<hr />
<?php
show_posts();
pg_close($dbc);
?>
<hr />
<div id="footer">
<p>
Old posts are automatically deleted once there are more than <?php echo MAX_POSTS; ?>, anything inappropriate will be deleted.
Welcome to my silly little bulletin board. It's primarily intended
for personal use transferring images from devices where that is
otherwise difficult, however anyone is free to use it.
Old posts are automatically deleted once there are more than
<?php echo MAX_POSTS; ?>, anything inappropriate will be deleted.
</p>
<p>
Formatting is very simple, just <strong class="quote">&gt;quote</strong>
by starting a line with &gt; and link to other posts with two &gt
and the post number, <del>&gt;&gt;1</del>. No other formatting is
supported besides the automatic conversion of hyperlinks.
You may provide a name and/or email address when posting if you wish
to identify yourself, however it is not required. A tripcode can be
used by writing "Name#Password" in the name field.
</p>
<p>
The year is <?php echo date('Y'); ?>, but no &copy;. This page was generated in <?php printf("%.2f", microtime(true) - $start_time); ?> seconds.
</p>
</div>
<p id="bottom">
[<a href="#top">top</a>]
[<a href="<?php echo $_SERVER['PHP_SELF']; ?>">reload</a>]
[<a href="?source">source</a>]
[<a href="rss.php">feed</a>]
[<a href="rss.php?source">feed source</a>]
</p>
<p>
<a href="//validator.w3.org/check?uri=<?php echo urlencode('http://' . $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF']); ?>" target="_blank">
<img src="//www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0 Transitional" height="31" width="88" />
</a>
<a href="//validator.w3.org/check?uri=<?php echo urlencode('http://' . $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF']); ?>" target="_blank"><img src="//www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0 Transitional" height="31" width="88" /></a>
<a href="https://jigsaw.w3.org/css-validator/check/referer"><img style="border:0;width:88px;height:31px" src="https://jigsaw.w3.org/css-validator/images/vcss" alt="Valid CSS!" /></a>
</p>
</body>
</html>

Loading…
Cancel
Save