Skip to content

Instantly share code, notes, and snippets.

@Epicpkmn11
Last active October 24, 2023 16:39
Show Gist options
  • Save Epicpkmn11/ad08ee01b4e2548846dbf54f1e60ae9e to your computer and use it in GitHub Desktop.
Save Epicpkmn11/ad08ee01b4e2548846dbf54f1e60ae9e to your computer and use it in GitHub Desktop.
bbs.php - crappy php bbs because I was bored lol http://bbs.xn--rck9c.xn--tckwe/index.php
<?php
/* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* For more information, please refer to <http://unlicense.org/>
*/
$start_time = microtime(true);
# $captcha_str = substr(strtr(sha1($start_time), "1234567890ABCDEF", "abdeghjkmnpqtuwx"), 0, 6);
$captcha_str = 'i\'m not a robot';
// Return source code
if(isset($_GET['source'])) {
header("Content-Type: text/plain");
die(file_get_contents(basename($_SERVER['PHP_SELF'])));
}
/* vars.php should contain the following, but with your variables as needed:
*
* // Application constants
* define('UPLOAD_PATH', 'image/'); // Folder to save images to
* define('THUMB_PATH', 'thumb/'); // Folder to save thumbnails to
* define('ASSETS_PATH', 'assets/'); // Folder to store assets (header images, etc) in
* define('MAX_FILE_SIZE', 8 << 20); // Maximum image size, 8 MiB
* 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.
*
* // Database constants for PostgreSQL database
* $DB_HOST = 'localhost';
* $DB_NAME = '<db name>';
* $DB_USER = '<db role name>';
* $DB_PASSWORD = '<db role password>';
*
* You also need to make the following table:
*
* CREATE TABLE posts (
* post_id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
* 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),
* thumb_width INT,
* thumb_height INT,
* spoiler BOOL
* );
*
* CREATE TABLE access
* count INT,
* lock BOOL PRIMARY KEY DEFAULT TRUE,
* CONSTRAINT lock_unique CHECK(lock)
* );
*
* INSERT INTO access VALUES (0);
*
* Finally, you will also need ffmpeg and exiftool installed for
* metadata stripping and thumbnail conversion.
*/
require_once('vars.php');
//// Functions: ////
// Adds a post to the database
function post($name, $email, $comment, $img, $spoiler, $save_cookie, $captcha, $captcha_answer) {
$extensions = [
'image/bmp' => '.bmp',
'image/gif' => '.gif',
'image/jpeg' => '.jpg',
'image/pjpeg' => '.jpg',
'image/png' => '.png'
];
if($save_cookie) {
$uid = $_COOKIE['uid'];
if(empty($uid)) {
$uid = sha1(time() . $img['tmp_name'] . $_SERVER['REMOTE_ADDR'] . UID_SALT);
setcookie('uid', $uid, 0x7FFFFFFF);
$_COOKIE['uid'] = $uid; // so that the checkbox is checked
}
setcookie('name', $name, 0x7FFFFFFF);
$_COOKIE['name'] = $name;
setcookie('email', $email, 0x7FFFFFFF);
$_COOKIE['email'] = $email;
}
if($captcha != $captcha_answer || empty($captcha_answer)) {
if(!empty($img['tmp_name']))
@unlink($img['tmp_name']);
return 'Invalid CAPTCHA answer, copy the text on the left into the box';
}
// Validate and move the uploaded image file, if necessary
if($img['error'] != UPLOAD_ERR_OK && $img['error'] != UPLOAD_ERR_NO_FILE) {
if(!empty($img['tmp_name']))
@unlink($img['tmp_name']);
return 'Invalid image, error ' . $img['error'];
}
if(!empty($img['tmp_name'])) {
if((($img['type'] == 'image/gif') || ($img['type'] == 'image/jpeg') || ($img['type'] == 'image/pjpeg')
|| ($img['type'] == 'image/png') || ($img['type'] == 'image/bmp'))
&& ($img['size'] > 0) && ($img['size'] <= MAX_FILE_SIZE)) {
if($img['error'] == 0) {
// Move the file to the target upload folder
$img_name = time();
$ext = $extensions[$img['type']];
$target = UPLOAD_PATH . $img_name . $ext;
if(move_uploaded_file($img['tmp_name'], $target)) {
$output = null;
$ret = null;
if($img['type'] != 'image/bmp') {
// Strip EXIF data
exec("exiftool -overwrite_original -all:all= -tagsfromfile @ -exif:Orientation '$target'", $output, $ret);
if($ret != 0)
return 'Unable to upload image, please contact the webmaster.';
}
if($spoiler) {
$thumb_size = getimagesize(ASSETS_PATH . "spoiler.gif");
} else {
// 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)' -frames:v 1 " . THUMB_PATH . "$img_name.jpg", $output, $ret);
if($ret != 0) {
@unlink($target);
return 'Unable to create thumbnail, please contact the webmaster.';
}
$thumb_size = getimagesize(THUMB_PATH . "$img_name.jpg");
}
} else {
// The new image file move failed, so delete the temporary file and return an error
@unlink($img['tmp_name']);
return 'Unable to rename image, please contact the webmaster.';
}
}
} else {
// The image is not valid, so delete the temporary file and return an error
@unlink($img['tmp_name']);
return 'Your image must be a PNG, GIF, JPEG, or BMP image file no greater than ' . (MAX_FILE_SIZE >> 10) . ' KiB.';
}
}
if(strlen($comment) > 2048)
return 'Comment must be 2048 or fewer characters';
if(empty($comment) && empty($target))
return 'You must include an image and/or a comment';
// Check email
if(!empty($email)) {
if(!preg_match('/^[a-zA-Z0-9][a-zA-Z0-9\._\-&!?=#+]*@/', $email)) {
return 'Invalid email address';
} else {
$domain = preg_replace('/^[a-zA-Z0-9][a-zA-Z0-9\._\-&!?=#+]*@/', '', $email);
if(!checkdnsrr($domain))
return 'Invalid email address';
}
}
$trip_name = trip_name($name);
// Add post to database
$query = 'INSERT INTO posts (user_id, name, trip, email, comment, img, thumb_width, thumb_height, spoiler) ' .
'VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)';
$params = [
empty($uid) ? NULL : $uid,
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),
$thumb_size[0],
$thumb_size[1],
$spoiler ? 'T' : 'F'
];
pg_query_params($query, $params) or die('Query failed: ' . pg_last_error());
return ""; // Success, no error
}
// Regex callback, makes >>quotes into links
function quote_link($match) {
$query = "SELECT user_id FROM posts WHERE post_id=$1";
$result = pg_query_params($query, [$match[1]]) or die('Query failed: ' . pg_last_error());
$row_count = pg_num_rows($result);
if($row_count > 0) {
$row = pg_fetch_array($result);
$you = (!empty($row['user_id']) && $row['user_id'] == $_COOKIE['uid']) ? ' (You)' : '';
pg_free_result($result);
return "<a href=\"#p{$match[1]}\">{$match[0]}$you</a>";
} else {
pg_free_result($result);
return "<del>{$match[0]}</del>";
}
}
// Prints the post list
function show_posts() {
$query = 'SELECT post_id, user_id, name, trip, email, comment, img, thumb_width, thumb_height, spoiler, post_time FROM posts ORDER BY posts.post_time';
$result = pg_query($query) or die('Query failed: ' . pg_last_error());
// Clean up old posts
$row_count = pg_num_rows($result);
if($row_count > MAX_POSTS) {
for($i = 0; $i < $row_count - MAX_POSTS; $i++) {
$row = pg_fetch_array($result);
cleanup($row['post_id']);
}
}
// Print posts
$show_delete = FALSE;
echo '<form action="' . $_SERVER['PHP_SELF'] . '#bottom" method="post">';
while($row = pg_fetch_array($result)) {
echo "<div><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 '<div 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 "<a href=\"mailto:{$row['email']}\">{$row['name']}</a>";
else
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> ";
if(!empty($row['img'])) {
$bytesize = bytesize(filesize(UPLOAD_PATH . $row['img']));
echo '<div>File:';
echo '<a href="' . UPLOAD_PATH . $row['img'] . '" target="_blank">' . $row['img'] . "</a> ($bytesize)</div>";
}
echo '</div>';
echo '<div class="postbody clearfix">';
if(!empty($row['img'])){
echo '<a href="' . UPLOAD_PATH . $row['img'] . '" target="_blank">';
$thumb_path = ($row['spoiler'] == 't') ? (ASSETS_PATH . 'spoiler.gif') : (THUMB_PATH . preg_replace('/\.(?:png|gif|bmp)$/', '.jpg', $row['img']));
echo "<img src=\"$thumb_path\" alt=\"{$row['img']}\" width=\"{$row['thumb_width']}\" height=\"{$row['thumb_height']}\" />";
echo '</a>';
}
// Process quotes, links, and newlines
if(!empty($row['comment'])) {
$comment = $row['comment'];
$comment = preg_replace('/^&gt;(?!&gt;\d).+/m', '<strong class="quote">$0</strong>', $comment);
$comment = preg_replace('/(?:https?|mailto|tel|ftp):.+?(?=\s|,|&lt;|<|$)/m', '<a href="$0" target="_blank">$0</a>', $comment);
$comment = preg_replace_callback('/&gt;&gt;\s*(\d+)/', 'quote_link', $comment);
$comment = str_replace("\n", "<br />", $comment);
echo "<div>$comment</div>";
}
echo '</div></div></div>';
}
pg_free_result($result);
if($show_delete)
echo '<p><input type="submit" name="submit" value="Delete" /></p>';
echo '</form>';
}
// Removes a post from the database and its image
function cleanup($id, $force = FALSE) {
$query = "SELECT user_id, img FROM posts WHERE post_id=$1";
$result = pg_query_params($query, [$id]) or die('Query failed: ' . pg_last_error());
$row = pg_fetch_array($result);
pg_free_result($result);
if($force || $row['user_id'] == $_COOKIE['uid'] || (!empty(ADMIN_ID) && ($_COOKIE['uid'] == ADMIN_ID))) {
unlink(UPLOAD_PATH . $row['img']);
$query = "DELETE FROM posts WHERE post_id=$1";
pg_query_params($query, [$id]) or die('Query failed: ' . pg_last_error());
}
}
// 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];
$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];
}
$difference = round($difference);
$period = $periods[$j] . ($difference >1 ? 's' :'');
return "{$difference} {$period} {$tense} ";
}
// Pretty size for bytes
function bytesize($bytes) {
if($bytes == 1) {
return "1 Byte";
} else if($bytes < (1 << 10)) {
return "{$bytes} Bytes";
} else if($bytes < (1 << 20)) {
return round($bytes / (1 << 10), 0) . " KiB";
} else if($bytes < (1 << 30)) {
return round($bytes / (1 << 20), 0) . " MiB";
} else {
return round($bytes / (1 << 30), 0) . " GiB";
}
}
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
];
}
function header_image($mobile) {
$path = ASSETS_PATH . 'header';
if($mobile)
$path .= '/mobile';
$files = glob("$path/*.gif");
if(empty($files))
return '';
return $files[array_rand($files)];
}
//// code start ////
// 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());
$access_count = 0;
{
pg_query('UPDATE access SET count=count+1;') or die('Query failed ' . pg_last_error());
$res = pg_query('SELECT count FROM access') or die('Query failed: ' . pg_last_error());
$access_count = pg_fetch_array($res)['count'];
}
if($_POST['submit'] == 'Post') {
// Grab the data from the POST
$name = trim($_POST['name']);
$email = trim($_POST['email']);
$comment = trim($_POST['comment']);
$img = $_FILES['img'];
$spoiler = isset($_POST['spoiler']) && !empty($_FILES['img']['tmp_name']);
$save_cookie = isset($_POST['save_cookie']);
$captcha = trim($_POST['captcha']);
$captcha_answer = trim($_POST['captcha_answer']);
$err = post($name, $email, $comment, $img, $spoiler, $save_cookie, $captcha, $captcha_answer);
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);
}
} else {
$name = $_GET['name'];
$email = $_GET['email'];
$comment = $_GET['comment'];
}
$mobile = preg_match('/Mobile|Nintendo DSi/', $_SERVER['HTTP_USER_AGENT']);
if($mobile) {
$headerW = 192;
$headerH = 50;
} else {
$headerW = 384;
$headerH = 100;
}
?>
<!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" />
<meta name="theme-color" content="#DBA9B9" />
<title>BBS | ピケ.コム</title>
<link rel="alternate" type="application/rss+xml" title="RSS feed" href="/rss.php" />
<link href="<?php echo ASSETS_PATH . 'style.css'; ?>" rel="stylesheet" />
</head>
<body class="center">
<center>
<img class="center" src="<?php echo header_image($mobile); ?>" alt="BBS.ピケ.コム" width="<?php echo $headerW; ?>" height="<?php echo $headerH; ?>" />
</center>
<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 echo htmlspecialchars($name ? : $_COOKIE['name']); ?>" />
<br />
<br />
<label for="email">Email:</label> (Optional)
<br />
<input id="email" name="email" value="<?php echo htmlspecialchars($email ? : $_COOKIE['email']); ?>" />
<br />
<br />
<label for="comment">Comment:</label>
<br />
<textarea id="comment" name="comment" rows="10" cols="27"><?php 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="spoiler">Spoiler:</label>
<input type="checkbox" id="spoiler" name="spoiler" <?php if($spoiler) echo 'checked'; ?> />
<br />
<br />
<label for="captcha">CAPTCHA:</label></td>
<br />
<code><?php echo $captcha_str; ?></code>
<input id="captcha" name="captcha" />
<input type="hidden" name="captcha_answer" value="<?php echo $captcha_str; ?>" />
<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>
<td><input id="name" name="name" value="<?php echo htmlspecialchars($name ? : $_COOKIE['name']); ?>" /> (Optional)</td>
</tr>
<tr>
<td><label for="email">Email:</label></td>
<td><input id="email" name="email" value="<?php echo htmlspecialchars($email ? : $_COOKIE['email']); ?>" /> (Optional)</td>
</tr>
<tr>
<td><label for="comment">Comment:</label></td>
<td><textarea id="comment" name="comment" rows="10" cols="40"><?php echo htmlspecialchars($comment); ?></textarea></td>
</tr>
<tr>
<td><label for="img">Image:</label></td>
<td><input type="file" id="img" name="img" /> (Limit: <?php echo MAX_FILE_SIZE >> 10; ?> KiB)</td>
</tr>
<tr>
<td><label for="spoiler">Spoiler:</label></td>
<td><input type="checkbox" id="spoiler" name="spoiler" <?php if($spoiler) echo 'checked'; ?> /></td>
</tr>
<tr>
<td><label for="captcha">CAPTCHA:</label></td>
<td>
<code><?php echo $captcha_str; ?></code>
<input id="captcha" name="captcha" />
<input type="hidden" name="captcha_answer" value="<?php echo $captcha_str; ?>" />
</td>
</tr>
<tr>
<td><label for="save-cookie">Save cookie:</label></td>
<td><input type="checkbox" id="save-cookie" name="save_cookie" <?php if($_COOKIE['uid']) echo 'checked'; ?> /> (Allows deleting your own posts)</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="Post" name="submit" /></td>
</tr>
</table>
<?php } ?>
<?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>
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 URLs being automatically made into 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, note that only
the first 8 characters are used.
</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 and has been accessed <?php echo $access_count; ?> times.
</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="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>
<?php
/* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* For more information, please refer to <http://unlicense.org/>
*/
// Return source code
if(isset($_GET['source'])) {
header("Content-Type: text/plain");
die(file_get_contents(basename($_SERVER['PHP_SELF'])));
}
// See bbs.php for a description of vars.php
require_once('vars.php');
// Regex callback, makes >>quotes into links
function quote_link($match) {
$query = "SELECT post_id FROM posts WHERE post_id=$1";
$result = pg_query_params($query, [$match[1]]) or die('Query failed: ' . pg_last_error());
$row_count = pg_num_rows($result);
pg_free_result($result);
if($row_count > 0)
return "<a href=\"#p{$match[1]}\">{$match[0]}</a>";
else
return "<del>{$match[0]}</del>";
}
$dbc = pg_connect("host=$DB_HOST dbname=$DB_NAME user=$DB_USER password=$DB_PASSWORD")
or die('Could not connect: ' . pg_last_error());
// Get posts
$posts = [];
$query = "SELECT post_id, name, trip, email, comment, img, spoiler, TO_CHAR(post_time, 'Dy, DD Mon YYYY HH24:MI:SS TZHTZM') AS ts "
. "FROM posts ORDER BY posts.post_time DESC LIMIT 10";
$result = pg_query($query) or die('Query failed: ' . pg_last_error());
while($row = pg_fetch_array($result)) {
// Process quotes, links, and newlines
$comment = NULL;
if(!empty($row['comment'])) {
$comment = $row['comment'];
$comment = preg_replace('/^&gt;(?!&gt;\d).+/m', '<strong>$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);
}
$name = $row['name'];
if(!empty($row['trip']))
$name .= '◆' . $row['trip'];
$comment = htmlspecialchars("<p>$comment</p>");
$img = ($row['spoiler'] == 't') ? (ASSETS_PATH . 'spoiler.gif') : (THUMB_PATH . preg_replace('/\.(?:png|gif|bmp)$/', '.jpg', $row['img']));
$posts[] = [
'title' => "New post by $name",
'link' => $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['SERVER_NAME'] . '/#p' . $row['post_id'],
'description' => $comment,
'author' => $name,
'pubDate' => $row['ts'],
'guid' => 'p' . $row['post_id'],
'img' => empty($row['img']) ? NULL : [
'url' => $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['SERVER_NAME'] . '/' . $img,
'length' => filesize($img),
'type' => mime_content_type($img)
]
];
}
pg_close($dbc);
header("Content-Type: application/rss+xml");
?>
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
<title>BBS | ピケ.コム</title>
<link>http://db.universal-team.net</link>
<atom:link href="http://bbs.xn--rck9c.xn--tckwe/rss.php" rel="self" type="application/rss+xml" />
<description>dumb PHP BBS because I was bored</description>
<lastBuildDate><?php echo $posts[0]['pubDate']; ?></lastBuildDate>
<language>en-US</language>
<?php foreach($posts as $post) { ?>
<item>
<title><?php echo $post['title']; ?></title>
<link><?php echo $post['link']; ?></link>
<description><?php echo $post['description']; ?></description>
<author><?php echo $post['author']; ?></author>
<pubDate><?php echo $post['pubDate']; ?></pubDate>
<guid isPermaLink="false"><?php echo $post['guid']; ?></guid>
<?php if(!empty($post['img'])) { ?>
<enclosure url="<?php echo $post['img']['url']; ?>" length="<?php echo $post['img']['length']; ?>" type="<?php echo $post['img']['type']; ?>"></enclosure>
<?php } ?>
</item>
<?php } ?>
</channel>
</rss>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment