*/ $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', ''); // uid that is able to delete anything * define('UID_SALT', ''); * 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_USER = ''; * $DB_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 "{$match[0]}$you"; } else { pg_free_result($result); return "{$match[0]}"; } } // 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 '
'; while($row = pg_fetch_array($result)) { echo "
"; $time_rel = time_ago_en($row['post_time']); $time_abs = date('Y-m-d H:m (T)', strtotime($row['post_time'])); echo '
'; if((!empty($row['user_id']) && ($row['user_id'] == $_COOKIE['uid'])) || (!empty(ADMIN_ID) && ($_COOKIE['uid'] == ADMIN_ID))) { echo ' '; $show_delete = TRUE; } echo ''; if(!empty($row['email'])) echo "{$row['name']}"; else echo $row['name']; echo ''; if(!empty($row['trip'])) echo "◆{$row['trip']}"; echo " $time_rel "; echo "#{$row['post_id']} "; // Find references $post_id = pg_escape_string($row['post_id']); $ref_query = "SELECT post_id FROM posts WHERE comment LIKE '%>>$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 ">>{$ref['post_id']} "; if(!empty($row['img'])) { $bytesize = bytesize(filesize(UPLOAD_PATH . $row['img'])); echo '
File:'; echo '' . $row['img'] . " ($bytesize)
"; } echo '
'; echo '
'; if(!empty($row['img'])){ echo ''; $thumb_path = ($row['spoiler'] == 't') ? (ASSETS_PATH . 'spoiler.gif') : (THUMB_PATH . preg_replace('/\.(?:png|gif|bmp)$/', '.jpg', $row['img'])); echo "\"{$row['img']}\""; echo ''; } // Process quotes, links, and newlines if(!empty($row['comment'])) { $comment = $row['comment']; $comment = preg_replace('/^>(?!>\d).+/m', '$0', $comment); $comment = preg_replace('/(?:https?|mailto|tel|ftp):.+?(?=\s|,|<|<|$)/m', '$0', $comment); $comment = preg_replace_callback('/>>\s*(\d+)/', 'quote_link', $comment); $comment = str_replace("\n", "
", $comment); $comment = str_replace('trans', 'trans', $comment); echo "
$comment
"; } echo '
'; } pg_free_result($result); if($show_delete) echo '

'; echo '
'; } // 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; } ?> BBS | ピケ.コム
BBS.ピケ.コム

[bottom] [reload] [source] [feed] [feed source] [back]


New Post

(Optional)


(Optional)





(Limit: > 10; ?> KiB)


/>




/>
(Allows deleting your own posts)

(Optional)
(Optional)
(Limit: > 10; ?> KiB)
/>
/> (Allows deleting your own posts)
$err

"; ?>


[top] [reload] [source] [feed] [feed source] [back]

Valid XHTML 1.0 Transitional Valid CSS!