", "Message-Id" => "<1234.1488.".$time."@example.vichan.net>", "Newsgroups" => "overchan.test", "Date" => time(), "Subject" => "None", "References" => "<1234.0000.".$time."@example.vichan.net>"],
-[['type' => 'text/plain', 'text' => "hello world, now WITH TWO IMAGES!!!"],
- ['type' => 'image/gif', 'text' => base64_decode("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="), 'name' => "urgif.gif"],
- ['type' => 'image/gif', 'text' => base64_decode("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="), 'name' => "urgif2.gif"]]);
-shoveitup($m0, "<1234.0000.".$time."@example.vichan.net>");
-sleep(1);
-shoveitup($m1, "<1234.1234.".$time."@example.vichan.net>");
-sleep(1);
-shoveitup($m2, "<1234.2137.".$time."@example.vichan.net>");
-shoveitup($m3, "<1234.1488.".$time."@example.vichan.net>");
-
diff --git a/js/post-menu.js b/js/post-menu.js
index 79cfd868..c2155c00 100644
--- a/js/post-menu.js
+++ b/js/post-menu.js
@@ -104,8 +104,10 @@ function buildMenu(e) {
function addButton(post) {
var $ele = $(post);
+ // Use unicode code with ascii variant selector
+ // https://stackoverflow.com/questions/37906969/how-to-prevent-ios-from-converting-ascii-into-emoji
$ele.find('input.delete').after(
- $('', {href: '#', class: 'post-btn', title: 'Post menu'}).text('►')
+ $(' ', {href: '#', class: 'post-btn', title: 'Post menu'}).text('\u{25B6}\u{fe0e}')
);
}
diff --git a/post.php b/post.php
index 6e7c6ab6..37af514f 100644
--- a/post.php
+++ b/post.php
@@ -5,6 +5,7 @@
use Vichan\Context;
use Vichan\Data\ReportQueries;
+use Vichan\Data\Driver\LogDriver;
require_once 'inc/bootstrap.php';
@@ -352,166 +353,6 @@ function db_select_ban_appeals($ban_id)
* Method handling functions
*/
-$dropped_post = false;
-
-function handle_nntpchan()
-{
- global $config;
- if ($_SERVER['REMOTE_ADDR'] != $config['nntpchan']['trusted_peer']) {
- error("NNTPChan: Forbidden. $_SERVER[REMOTE_ADDR] is not a trusted peer");
- }
-
- $_POST = [];
- $_POST['json_response'] = true;
-
- $headers = json_encode($_GET);
-
- if (!isset($_GET['Message-Id'])) {
- if (!isset($_GET['Message-ID'])) {
- error("NNTPChan: No message ID");
- } else {
- $msgid = $_GET['Message-ID'];
- }
- } else {
- $msgid = $_GET['Message-Id'];
- }
-
- $groups = preg_split("/,\s*/", $_GET['Newsgroups']);
- if (count($groups) != 1) {
- error("NNTPChan: Messages can go to only one newsgroup");
- }
- $group = $groups[0];
-
- if (!isset($config['nntpchan']['dispatch'][$group])) {
- error("NNTPChan: We don't synchronize $group");
- }
- $xboard = $config['nntpchan']['dispatch'][$group];
-
- $ref = null;
- if (isset($_GET['References'])) {
- $refs = preg_split("/,\s*/", $_GET['References']);
-
- if (count($refs) > 1) {
- error("NNTPChan: We don't support multiple references");
- }
-
- $ref = $refs[0];
-
- $query = prepare("SELECT `board`,`id` FROM ``nntp_references`` WHERE `message_id` = :ref");
- $query->bindValue(':ref', $ref);
- $query->execute() or error(db_error($query));
-
- $ary = $query->fetchAll(PDO::FETCH_ASSOC);
-
- if (count($ary) == 0) {
- error("NNTPChan: We don't have $ref that $msgid references");
- }
-
- $p_id = $ary[0]['id'];
- $p_board = $ary[0]['board'];
-
- if ($p_board != $xboard) {
- error("NNTPChan: Cross board references not allowed. Tried to reference $p_board on $xboard");
- }
-
- $_POST['thread'] = $p_id;
- }
-
- $date = isset($_GET['Date']) ? strtotime($_GET['Date']) : time();
-
- list($ct) = explode('; ', $_GET['Content-Type']);
-
- $query = prepare("SELECT COUNT(*) AS `c` FROM ``nntp_references`` WHERE `message_id` = :msgid");
- $query->bindValue(":msgid", $msgid);
- $query->execute() or error(db_error($query));
-
- $a = $query->fetch(PDO::FETCH_ASSOC);
- if ($a['c'] > 0) {
- error("NNTPChan: We already have this post. Post discarded.");
- }
-
- if ($ct == 'text/plain') {
- $content = file_get_contents("php://input");
- } elseif ($ct == 'multipart/mixed' || $ct == 'multipart/form-data') {
- _syslog(LOG_INFO, "MM: Files: " . print_r($GLOBALS, true)); // Debug
-
- $content = '';
-
- $newfiles = [];
- foreach ($_FILES['attachment']['error'] as $id => $error) {
- if ($_FILES['attachment']['type'][$id] == 'text/plain') {
- $content .= file_get_contents($_FILES['attachment']['tmp_name'][$id]);
- } elseif ($_FILES['attachment']['type'][$id] == 'message/rfc822') {
- // Signed message, ignore for now
- } else {
- // A real attachment :^)
- $file = [];
- $file['name'] = $_FILES['attachment']['name'][$id];
- $file['type'] = $_FILES['attachment']['type'][$id];
- $file['size'] = $_FILES['attachment']['size'][$id];
- $file['tmp_name'] = $_FILES['attachment']['tmp_name'][$id];
- $file['error'] = $_FILES['attachment']['error'][$id];
-
- $newfiles["file$id"] = $file;
- }
- }
-
- $_FILES = $newfiles;
- } else {
- error("NNTPChan: Wrong mime type: $ct");
- }
-
- $_POST['subject'] = isset($_GET['Subject']) ? ($_GET['Subject'] == 'None' ? '' : $_GET['Subject']) : '';
- $_POST['board'] = $xboard;
-
- if (isset($_GET['From'])) {
- list($name, $mail) = explode(" <", $_GET['From'], 2);
- $mail = preg_replace('/>\s+$/', '', $mail);
-
- $_POST['name'] = $name;
- //$_POST['email'] = $mail;
- $_POST['email'] = '';
- }
-
- if (isset($_GET['X_Sage'])) {
- $_POST['email'] = 'sage';
- }
-
- $content = preg_replace_callback('/>>([0-9a-fA-F]{6,})/', function ($id) use ($xboard) {
- $id = $id[1];
-
- $query = prepare("SELECT `board`,`id` FROM ``nntp_references`` WHERE `message_id_digest` LIKE :rule");
- $idx = $id . "%";
- $query->bindValue(':rule', $idx);
- $query->execute() or error(db_error($query));
-
- $ary = $query->fetchAll(PDO::FETCH_ASSOC);
- if (count($ary) == 0) {
- return ">>>>$id";
- } else {
- $ret = [];
- foreach ($ary as $v) {
- if ($v['board'] != $xboard) {
- $ret[] = ">>>/" . $v['board'] . "/" . $v['id'];
- } else {
- $ret[] = ">>" . $v['id'];
- }
- }
- return implode($ret, ", ");
- }
- }, $content);
-
- $_POST['body'] = $content;
-
- $dropped_post = array(
- 'date' => $date,
- 'board' => $xboard,
- 'msgid' => $msgid,
- 'headers' => $headers,
- 'from_nntp' => true,
- );
-}
-
function handle_delete(Context $ctx)
{
// Delete
@@ -610,8 +451,8 @@ function handle_delete(Context $ctx)
modLog("User at $ip deleted his own post #$id");
}
- _syslog(
- LOG_INFO,
+ $ctx->get(LogDriver::class)->log(
+ LogDriver::INFO,
'Deleted post: ' .
'/' . $board['dir'] . $config['dir']['res'] . sprintf($config['file_page'], $post['thread'] ? $post['thread'] : $id) . ($post['thread'] ? '#' . $id : '')
);
@@ -699,9 +540,7 @@ function handle_report(Context $ctx)
foreach ($report as $id) {
$post = db_select_post_minimal($board['uri'], $id);
if ($post === false) {
- if ($config['syslog']) {
- _syslog(LOG_INFO, "Failed to report non-existing post #{$id} in {$board['dir']}");
- }
+ $ctx->get(LogDriver::class)->log(LogDriver::INFO, "Failed to report non-existing post #{$id} in {$board['dir']}");
error($config['error']['nopost']);
}
@@ -716,13 +555,12 @@ function handle_report(Context $ctx)
error($error);
}
- if ($config['syslog'])
- _syslog(
- LOG_INFO,
- 'Reported post: ' .
- '/' . $board['dir'] . $config['dir']['res'] . link_for($post) . ($post['thread'] ? '#' . $id : '') .
- ' for "' . $reason . '"'
- );
+ $ctx->get(LogDriver::class)->log(
+ LogDriver::INFO,
+ 'Reported post: ' .
+ '/' . $board['dir'] . $config['dir']['res'] . link_for($post) . ($post['thread'] ? '#' . $id : '') .
+ ' for "' . $reason . '"'
+ );
$report_queries->add($_SERVER['REMOTE_ADDR'], $board['uri'], $id, $reason);
@@ -791,9 +629,9 @@ function handle_report(Context $ctx)
function handle_post(Context $ctx)
{
- global $config, $dropped_post, $board, $mod, $pdo;
+ global $config, $board, $mod, $pdo;
- if (!isset($_POST['body'], $_POST['board']) && !$dropped_post) {
+ if (!isset($_POST['body'], $_POST['board'])) {
error($config['error']['bot']);
}
@@ -836,108 +674,104 @@ function handle_post(Context $ctx)
}
- if (!$dropped_post) {
- // Check for CAPTCHA right after opening the board so the "return" link is in there.
- if ($config['captcha']['mode'] !== false) {
- if (!isset($_POST['captcha-response'], $_POST['captcha-form-id'])) {
- error($config['error']['bot']);
- }
-
- $expected_action = $post['op'] ? 'post-thread' : 'post-reply';
- $ret = check_captcha($config['captcha'], $_POST['captcha-form-id'], $post['board'], $_POST['captcha-response'], $_SERVER['REMOTE_ADDR'], $expected_action);
- if (!$ret) {
- error($config['error']['captcha']);
- }
- }
-
- if (isset($config['simple_spam']) && $post['op']) {
- if (!isset($_POST['simple_spam']) || $config['simple_spam']['answer'] != $_POST['simple_spam']) {
- error($config['error']['simple_spam']);
- }
- }
-
- if (isset($config['securimage']) && $config['securimage']) {
- if (!isset($_POST['captcha'])) {
- error($config['error']['securimage']['missing']);
- }
-
- if (empty($_POST['captcha'])) {
- error($config['error']['securimage']['empty']);
- }
-
- if (!db_delete_captcha($_SERVER['REMOTE_ADDR'], $_POST['captcha'])) {
- error($config['error']['securimage']['bad']);
- }
- }
-
- if (
- !(($post['op'] && $_POST['post'] == $config['button_newtopic']) ||
- (!$post['op'] && $_POST['post'] == $config['button_reply']))
- ) {
+ // Check for CAPTCHA right after opening the board so the "return" link is in there.
+ if ($config['captcha']['mode'] !== false) {
+ if (!isset($_POST['captcha-response'], $_POST['captcha-form-id'])) {
error($config['error']['bot']);
}
- // Check the referrer
- if (
- $config['referer_match'] !== false &&
- (!isset($_SERVER['HTTP_REFERER']) || !preg_match($config['referer_match'], rawurldecode($_SERVER['HTTP_REFERER'])))
- ) {
- error($config['error']['referer']);
+ $expected_action = $post['op'] ? 'post-thread' : 'post-reply';
+ $ret = check_captcha($config['captcha'], $_POST['captcha-form-id'], $post['board'], $_POST['captcha-response'], $_SERVER['REMOTE_ADDR'], $expected_action);
+ if (!$ret) {
+ error($config['error']['captcha']);
+ }
+ }
+
+ if (isset($config['simple_spam']) && $post['op']) {
+ if (!isset($_POST['simple_spam']) || $config['simple_spam']['answer'] != $_POST['simple_spam']) {
+ error($config['error']['simple_spam']);
+ }
+ }
+
+ if (isset($config['securimage']) && $config['securimage']) {
+ if (!isset($_POST['captcha'])) {
+ error($config['error']['securimage']['missing']);
}
- checkDNSBL();
-
- // Check if banned
- checkBan($board['uri']);
-
- if ($config['op_require_history'] && $post['op'] && !isIPv6()) {
- $has_any = has_any_history($_SERVER['REMOTE_ADDR'], $_POST['password']);
- if (!$has_any) {
- error($config['error']['opnohistory']);
- }
+ if (empty($_POST['captcha'])) {
+ error($config['error']['securimage']['empty']);
}
- if ($post['mod'] = isset($_POST['mod']) && $_POST['mod']) {
- check_login($ctx, false);
- if (!$mod) {
- // Liar. You're not a mod >:-[
- error($config['error']['notamod']);
- }
+ if (!db_delete_captcha($_SERVER['REMOTE_ADDR'], $_POST['captcha'])) {
+ error($config['error']['securimage']['bad']);
+ }
+ }
- $post['sticky'] = $post['op'] && isset($_POST['sticky']);
- $post['locked'] = $post['op'] && isset($_POST['lock']);
- $post['raw'] = isset($_POST['raw']);
+ if (
+ !(($post['op'] && $_POST['post'] == $config['button_newtopic']) ||
+ (!$post['op'] && $_POST['post'] == $config['button_reply']))
+ ) {
+ error($config['error']['bot']);
+ }
- if ($post['sticky'] && !hasPermission($config['mod']['sticky'], $board['uri'])) {
- error($config['error']['noaccess']);
- }
- if ($post['locked'] && !hasPermission($config['mod']['lock'], $board['uri'])) {
- error($config['error']['noaccess']);
- }
- if ($post['raw'] && !hasPermission($config['mod']['rawhtml'], $board['uri'])) {
- error($config['error']['noaccess']);
- }
+ // Check the referrer
+ if (
+ $config['referer_match'] !== false &&
+ (!isset($_SERVER['HTTP_REFERER']) || !preg_match($config['referer_match'], rawurldecode($_SERVER['HTTP_REFERER'])))
+ ) {
+ error($config['error']['referer']);
+ }
+
+ checkDNSBL();
+
+ // Check if banned
+ checkBan($board['uri']);
+
+ if ($config['op_require_history'] && $post['op'] && !isIPv6()) {
+ $has_any = has_any_history($_SERVER['REMOTE_ADDR'], $_POST['password']);
+ if (!$has_any) {
+ error($config['error']['opnohistory']);
+ }
+ }
+
+ if ($post['mod'] = isset($_POST['mod']) && $_POST['mod']) {
+ check_login($ctx, false);
+ if (!$mod) {
+ // Liar. You're not a mod >:-[
+ error($config['error']['notamod']);
}
- if (!$post['mod'] && $config['spam']['enabled'] == true) {
- $post['antispam_hash'] = checkSpam(
- array(
- $board['uri'],
- isset($post['thread']) ? $post['thread'] : ($config['try_smarter'] && isset($_POST['page']) ? 0 - (int) $_POST['page'] : null)
- )
- );
- //$post['antispam_hash'] = checkSpam();
+ $post['sticky'] = $post['op'] && isset($_POST['sticky']);
+ $post['locked'] = $post['op'] && isset($_POST['lock']);
+ $post['raw'] = isset($_POST['raw']);
- if ($post['antispam_hash'] === true) {
- error($config['error']['spam']);
- }
+ if ($post['sticky'] && !hasPermission($config['mod']['sticky'], $board['uri'])) {
+ error($config['error']['noaccess']);
}
+ if ($post['locked'] && !hasPermission($config['mod']['lock'], $board['uri'])) {
+ error($config['error']['noaccess']);
+ }
+ if ($post['raw'] && !hasPermission($config['mod']['rawhtml'], $board['uri'])) {
+ error($config['error']['noaccess']);
+ }
+ }
- if ($config['robot_enable'] && $config['robot_mute']) {
- checkMute();
+ if (!$post['mod'] && $config['spam']['enabled'] == true) {
+ $post['antispam_hash'] = checkSpam(
+ array(
+ $board['uri'],
+ isset($post['thread']) ? $post['thread'] : ($config['try_smarter'] && isset($_POST['page']) ? 0 - (int) $_POST['page'] : null)
+ )
+ );
+ //$post['antispam_hash'] = checkSpam();
+
+ if ($post['antispam_hash'] === true) {
+ error($config['error']['spam']);
}
- } else {
- $mod = $post['mod'] = false;
+ }
+
+ if ($config['robot_enable'] && $config['robot_mute']) {
+ checkMute();
}
// Check if thread exists.
@@ -1023,40 +857,35 @@ function handle_post(Context $ctx)
$post['password'] = hashPassword($_POST['password']);
$post['has_file'] = (!isset($post['embed']) && (($post['op'] && !isset($post['no_longer_require_an_image_for_op']) && $config['force_image_op']) || count($_FILES) > 0));
- if (!$dropped_post) {
- if (!($post['has_file'] || isset($post['embed'])) || (($post['op'] && $config['force_body_op']) || (!$post['op'] && $config['force_body']))) {
- $stripped_whitespace = preg_replace('/[\s]/u', '', $post['body']);
- if ($stripped_whitespace == '') {
- error($config['error']['tooshort_body']);
- }
- }
-
- if (!$post['op']) {
- // Check if thread is locked
- // but allow mods to post
- if ($thread['locked'] && !hasPermission($config['mod']['postinlocked'], $board['uri'])) {
- error($config['error']['locked']);
- }
-
- $numposts = numPosts($post['thread']);
-
- $replythreshold = isset($thread['cycle']) && $thread['cycle'] ? $numposts['replies'] - 1 : $numposts['replies'];
- $imagethreshold = isset($thread['cycle']) && $thread['cycle'] ? $numposts['images'] - 1 : $numposts['images'];
-
- if ($config['reply_hard_limit'] != 0 && $config['reply_hard_limit'] <= $replythreshold) {
- error($config['error']['reply_hard_limit']);
- }
-
- if ($post['has_file'] && $config['image_hard_limit'] != 0 && $config['image_hard_limit'] <= $imagethreshold) {
- error($config['error']['image_hard_limit']);
- }
- }
- } else {
- if (!$post['op']) {
- $numposts = numPosts($post['thread']);
+ if (!($post['has_file'] || isset($post['embed'])) || (($post['op'] && $config['force_body_op']) || (!$post['op'] && $config['force_body']))) {
+ $stripped_whitespace = preg_replace('/[\s]/u', '', $post['body']);
+ if ($stripped_whitespace == '') {
+ error($config['error']['tooshort_body']);
}
}
+ if (!$post['op']) {
+ // Check if thread is locked
+ // but allow mods to post
+ if ($thread['locked'] && !hasPermission($config['mod']['postinlocked'], $board['uri'])) {
+ error($config['error']['locked']);
+ }
+
+ $numposts = numPosts($post['thread']);
+
+ $replythreshold = isset($thread['cycle']) && $thread['cycle'] ? $numposts['replies'] - 1 : $numposts['replies'];
+ $imagethreshold = isset($thread['cycle']) && $thread['cycle'] ? $numposts['images'] - 1 : $numposts['images'];
+
+ if ($config['reply_hard_limit'] != 0 && $config['reply_hard_limit'] <= $replythreshold) {
+ error($config['error']['reply_hard_limit']);
+ }
+
+ if ($post['has_file'] && $config['image_hard_limit'] != 0 && $config['image_hard_limit'] <= $imagethreshold) {
+ error($config['error']['image_hard_limit']);
+ }
+ }
+
+
if ($post['has_file']) {
// Determine size sanity
$size = 0;
@@ -1162,18 +991,16 @@ function handle_post(Context $ctx)
$post['has_file'] = false;
}
- if (!$dropped_post) {
- // Check for a file
- if ($post['op'] && !isset($post['no_longer_require_an_image_for_op'])) {
- if (!$post['has_file'] && $config['force_image_op']) {
- error($config['error']['noimage']);
- }
+ // Check for a file
+ if ($post['op'] && !isset($post['no_longer_require_an_image_for_op'])) {
+ if (!$post['has_file'] && $config['force_image_op']) {
+ error($config['error']['noimage']);
}
+ }
- // Check for too many files
- if (sizeof($post['files']) > $config['max_images']) {
- error($config['error']['toomanyimages']);
- }
+ // Check for too many files
+ if (sizeof($post['files']) > $config['max_images']) {
+ error($config['error']['toomanyimages']);
}
if ($config['strip_combining_chars']) {
@@ -1183,33 +1010,31 @@ function handle_post(Context $ctx)
$post['body'] = strip_combining_chars($post['body']);
}
- if (!$dropped_post) {
- // Check string lengths
- if (mb_strlen($post['name']) > 35) {
- error(sprintf($config['error']['toolong'], 'name'));
- }
- if (mb_strlen($post['email']) > 40) {
- error(sprintf($config['error']['toolong'], 'email'));
- }
- if (mb_strlen($post['subject']) > 100) {
- error(sprintf($config['error']['toolong'], 'subject'));
- }
- if (!$mod) {
- $body_mb_len = mb_strlen($post['body']);
- $is_op = $post['op'];
+ // Check string lengths
+ if (mb_strlen($post['name']) > 35) {
+ error(sprintf($config['error']['toolong'], 'name'));
+ }
+ if (mb_strlen($post['email']) > 40) {
+ error(sprintf($config['error']['toolong'], 'email'));
+ }
+ if (mb_strlen($post['subject']) > 100) {
+ error(sprintf($config['error']['toolong'], 'subject'));
+ }
+ if (!$mod) {
+ $body_mb_len = mb_strlen($post['body']);
+ $is_op = $post['op'];
- if (($is_op && $config['force_body_op']) || (!$is_op && $config['force_body'])) {
- $min_body = $is_op ? $config['min_body_op'] : $config['min_body'];
+ if (($is_op && $config['force_body_op']) || (!$is_op && $config['force_body'])) {
+ $min_body = $is_op ? $config['min_body_op'] : $config['min_body'];
- if ($body_mb_len < $min_body) {
- error($config['error']['tooshort_body']);
- }
+ if ($body_mb_len < $min_body) {
+ error($config['error']['tooshort_body']);
}
+ }
- $max_body = $is_op ? $config['max_body_op'] : $config['max_body'];
- if ($body_mb_len > $max_body) {
- error($config['error']['toolong_body']);
- }
+ $max_body = $is_op ? $config['max_body_op'] : $config['max_body'];
+ if ($body_mb_len > $max_body) {
+ error($config['error']['toolong_body']);
}
}
@@ -1221,33 +1046,32 @@ function handle_post(Context $ctx)
$post['body'] .= "\n1 ";
}
- if (!$dropped_post)
- if (($config['country_flags'] && !$config['allow_no_country']) || ($config['country_flags'] && $config['allow_no_country'] && !isset($_POST['no_country']))) {
- $gi = geoip_open('inc/lib/geoip/GeoIPv6.dat', GEOIP_STANDARD);
+ if (($config['country_flags'] && !$config['allow_no_country']) || ($config['country_flags'] && $config['allow_no_country'] && !isset($_POST['no_country']))) {
+ $gi = geoip_open('inc/lib/geoip/GeoIPv6.dat', GEOIP_STANDARD);
- function ipv4to6($ip)
- {
- if (strpos($ip, ':') !== false) {
- if (strpos($ip, '.') > 0) {
- $ip = substr($ip, strrpos($ip, ':') + 1);
- } else {
- // Native ipv6.
- return $ip;
- }
+ function ipv4to6($ip)
+ {
+ if (strpos($ip, ':') !== false) {
+ if (strpos($ip, '.') > 0) {
+ $ip = substr($ip, strrpos($ip, ':') + 1);
+ } else {
+ // Native ipv6.
+ return $ip;
}
- $iparr = array_pad(explode('.', $ip), 4, 0);
- $part7 = base_convert(($iparr[0] * 256) + $iparr[1], 10, 16);
- $part8 = base_convert(($iparr[2] * 256) + $iparr[3], 10, 16);
- return '::ffff:' . $part7 . ':' . $part8;
}
+ $iparr = array_pad(explode('.', $ip), 4, 0);
+ $part7 = base_convert(($iparr[0] * 256) + $iparr[1], 10, 16);
+ $part8 = base_convert(($iparr[2] * 256) + $iparr[3], 10, 16);
+ return '::ffff:' . $part7 . ':' . $part8;
+ }
- if ($country_code = geoip_country_code_by_addr_v6($gi, ipv4to6($_SERVER['REMOTE_ADDR']))) {
- if (!in_array(strtolower($country_code), array('eu', 'ap', 'o1', 'a1', 'a2'))) {
- $post['body'] .= "\n" . strtolower($country_code) . " " .
- "\n" . geoip_country_name_by_addr_v6($gi, ipv4to6($_SERVER['REMOTE_ADDR'])) . " ";
- }
+ if ($country_code = geoip_country_code_by_addr_v6($gi, ipv4to6($_SERVER['REMOTE_ADDR']))) {
+ if (!in_array(strtolower($country_code), array('eu', 'ap', 'o1', 'a1', 'a2'))) {
+ $post['body'] .= "\n" . strtolower($country_code) . " " .
+ "\n" . geoip_country_name_by_addr_v6($gi, ipv4to6($_SERVER['REMOTE_ADDR'])) . " ";
}
}
+ }
if ($config['user_flag'] && isset($_POST['user_flag']))
if (!empty($_POST['user_flag'])) {
@@ -1267,11 +1091,9 @@ function handle_post(Context $ctx)
$post['body'] .= "\n" . $_POST['tag'] . " ";
}
- if (!$dropped_post) {
- if ($config['proxy_save'] && isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
- $proxy = preg_replace("/[^0-9a-fA-F.,: ]/", '', $_SERVER['HTTP_X_FORWARDED_FOR']);
- $post['body'] .= "\n" . $proxy . " ";
- }
+ if ($config['proxy_save'] && isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
+ $proxy = preg_replace("/[^0-9a-fA-F.,: ]/", '', $_SERVER['HTTP_X_FORWARDED_FOR']);
+ $post['body'] .= "\n" . $proxy . " ";
}
$post['body_nomarkup'] = $post['body'];
@@ -1313,7 +1135,7 @@ function handle_post(Context $ctx)
}
}
- if (!hasPermission($config['mod']['bypass_filters'], $board['uri']) && !$dropped_post) {
+ if (!hasPermission($config['mod']['bypass_filters'], $board['uri'])) {
require_once 'inc/filters.php';
do_filters($ctx, $post);
}
@@ -1404,13 +1226,13 @@ function handle_post(Context $ctx)
$file['thumbwidth'] = $size[0];
$file['thumbheight'] = $size[1];
} elseif (
- $config['minimum_copy_resize'] &&
+ (($config['strip_exif'] && isset($file['exif_stripped']) && $file['exif_stripped']) || !$config['strip_exif']) &&
$image->size->width <= $config['thumb_width'] &&
$image->size->height <= $config['thumb_height'] &&
$file['extension'] == ($config['thumb_ext'] ? $config['thumb_ext'] : $file['extension'])
) {
// Copy, because there's nothing to resize
- coopy($file['tmp_name'], $file['thumb']);
+ copy($file['tmp_name'], $file['thumb']);
$file['thumbwidth'] = $image->size->width;
$file['thumbheight'] = $image->size->height;
@@ -1449,17 +1271,35 @@ function handle_post(Context $ctx)
}
$image->destroy();
} else {
- if (
- ($file['extension'] == "pdf" && $config['pdf_file_thumbnail']) ||
- ($file['extension'] == "djvu" && $config['djvu_file_thumbnail'])
- ) {
- $path = $file['thumb'];
- $error = shell_exec_error('convert -size ' . $config['thumb_width'] . 'x' . $config['thumb_height'] . ' -thumbnail ' . $config['thumb_width'] . 'x' . $config['thumb_height'] . ' -background white -alpha remove ' .
- escapeshellarg($file['tmp_name'] . '[0]') . ' ' .
- escapeshellarg($file['thumb']));
+ $mime = \mime_content_type($file['tmp_name']);
+ $pdf = $file['extension'] === "pdf" && $config['pdf_file_thumbnail'];
+ $djvu = $file['extension'] === "djvu" && $config['djvu_file_thumbnail'];
+
+ if ($pdf || $djvu) {
+ $e_thumb_path = \escapeshellarg($file['thumb']);
+ $e_file_path = \escapeshellarg($file['tmp_name']);
+ $thumb_width = $config['thumb_width'];
+ $thumb_height = $config['thumb_height'];
+
+ // Generates a PPM image and pipes it directly into convert for resizing + type conversion.
+ if ($pdf && $mime === 'application/pdf') {
+ $error = shell_exec_error("gs -dSAFER -dBATCH -dNOPAUSE -dQUIET \
+ -sDEVICE=ppmraw -r100 -dFirstPage=1 -dLastPage=1 -sOutputFile=- $e_file_path \
+ | convert -thumbnail {$thumb_width}x{$thumb_height} ppm:- $e_thumb_path");
+ } elseif ($djvu && $mime === 'image/vnd.djvu') {
+ $error = shell_exec_error("ddjvu -format=ppm -page 1 $e_file_path \
+ | convert -thumbnail {$thumb_width}x{$thumb_height} ppm:- $e_thumb_path");
+ } else {
+ // Mime check failed.
+ error($config['error']['invalidfile']);
+ }
if ($error) {
- $path = sprintf($config['file_thumb'], isset($config['file_icons'][$file['extension']]) ? $config['file_icons'][$file['extension']] : $config['file_icons']['default']);
+ $log = $ctx->get(LogDriver::class);
+ $log->log(LogDriver::ERROR, 'Could not render thumbnail for PDF/DJVU file, using static fallback.');
+ $path = \sprintf($config['file_thumb'], isset($config['file_icons'][$file['extension']]) ? $config['file_icons'][$file['extension']] : $config['file_icons']['default']);
+ } else {
+ $path = $file['thumb'];
}
$file['thumb'] = basename($file['thumb']);
@@ -1553,35 +1393,6 @@ function handle_post(Context $ctx)
}
}
- if ($config['tesseract_ocr'] && $file['thumb'] != 'file') {
- // Let's OCR it!
- $fname = $file['tmp_name'];
-
- if ($file['height'] > 500 || $file['width'] > 500) {
- $fname = $file['thumb'];
- }
-
- if ($fname == 'spoiler') {
- // We don't have that much CPU time, do we?
- } else {
- $tmpname = __DIR__ . "/tmp/tesseract/" . rand(0, 10000000);
-
- // Preprocess command is an ImageMagick b/w quantization
- $error = shell_exec_error(sprintf($config['tesseract_preprocess_command'], escapeshellarg($fname)) . " | " .
- 'tesseract stdin ' . escapeshellarg($tmpname) . ' ' . $config['tesseract_params']);
- $tmpname .= ".txt";
-
- $value = @file_get_contents($tmpname);
- @unlink($tmpname);
-
- if ($value && trim($value)) {
- // This one has an effect, that the body is appended to a post body. So you can write a correct
- // spamfilter.
- $post['body_nomarkup'] .= "" . htmlspecialchars($value) . " ";
- }
- }
- }
-
if (!isset($dont_copy_file) || !$dont_copy_file) {
if (isset($file['file_tmp'])) {
if (!@rename($file['tmp_name'], $file['file'])) {
@@ -1629,12 +1440,7 @@ function handle_post(Context $ctx)
}
}
- // Do filters again if OCRing
- if ($config['tesseract_ocr'] && !hasPermission($config['mod']['bypass_filters'], $board['uri']) && !$dropped_post) {
- do_filters($ctx, $post);
- }
-
- if (!hasPermission($config['mod']['postunoriginal'], $board['uri']) && $config['robot_enable'] && checkRobot($post['body_nomarkup']) && !$dropped_post) {
+ if (!hasPermission($config['mod']['postunoriginal'], $board['uri']) && $config['robot_enable'] && checkRobot($post['body_nomarkup'])) {
undoImage($post);
if ($config['robot_mute']) {
error(sprintf($config['error']['muted'], mute()));
@@ -1680,41 +1486,6 @@ function handle_post(Context $ctx)
$post['id'] = $id = post($post);
$post['slug'] = slugify($post);
- if ($dropped_post && $dropped_post['from_nntp']) {
- $query = prepare("INSERT INTO ``nntp_references`` (`board`, `id`, `message_id`, `message_id_digest`, `own`, `headers`) VALUES " .
- "(:board , :id , :message_id , :message_id_digest , false, :headers)");
-
- $query->bindValue(':board', $dropped_post['board']);
- $query->bindValue(':id', $id);
- $query->bindValue(':message_id', $dropped_post['msgid']);
- $query->bindValue(':message_id_digest', sha1($dropped_post['msgid']));
- $query->bindValue(':headers', $dropped_post['headers']);
- $query->execute() or error(db_error($query));
- } // ^^^^^ For inbound posts ^^^^^
- elseif ($config['nntpchan']['enabled'] && $config['nntpchan']['group']) {
- // vvvvv For outbound posts vvvvv
-
- require_once('inc/nntpchan/nntpchan.php');
- $msgid = gen_msgid($post['board'], $post['id']);
-
- list($headers, $files) = post2nntp($post, $msgid);
-
- $message = gen_nntp($headers, $files);
-
- $query = prepare("INSERT INTO ``nntp_references`` (`board`, `id`, `message_id`, `message_id_digest`, `own`, `headers`) VALUES " .
- "(:board , :id , :message_id , :message_id_digest , true , :headers)");
-
- $query->bindValue(':board', $post['board']);
- $query->bindValue(':id', $post['id']);
- $query->bindValue(':message_id', $msgid);
- $query->bindValue(':message_id_digest', sha1($msgid));
- $query->bindValue(':headers', json_encode($headers));
- $query->execute() or error(db_error($query));
-
- // Let's broadcast it!
- nntp_publish($message, $msgid);
- }
-
insertFloodPost($post);
// Handle cyclical threads
@@ -1783,10 +1554,10 @@ function handle_post(Context $ctx)
buildThread($post['op'] ? $id : $post['thread']);
- if ($config['syslog']) {
- _syslog(LOG_INFO, 'New post: /' . $board['dir'] . $config['dir']['res'] .
- link_for($post) . (!$post['op'] ? '#' . $id : ''));
- }
+ $ctx->get(LogDriver::class)->log(
+ LogDriver::INFO,
+ 'New post: /' . $board['dir'] . $config['dir']['res'] . link_for($post) . (!$post['op'] ? '#' . $id : '')
+ );
if (!$post['mod']) {
header('X-Associated-Content: "' . $redirect . '"');
@@ -1879,22 +1650,13 @@ function handle_appeal(Context $ctx)
displayBan($ban);
}
-// Is it a post coming from NNTP? Let's extract it and pretend it's a normal post.
-if (isset($_GET['Newsgroups'])) {
- if ($config['nntpchan']['enabled']) {
- handle_nntpchan();
- } else {
- error("NNTPChan: NNTPChan support is disabled");
- }
-}
-
$ctx = Vichan\build_context($config);
if (isset($_POST['delete'])) {
handle_delete($ctx);
} elseif (isset($_POST['report'])) {
handle_report($ctx);
-} elseif (isset($_POST['post']) || $dropped_post) {
+} elseif (isset($_POST['post'])) {
handle_post($ctx);
} elseif (isset($_POST['appeal'])) {
handle_appeal($ctx);
diff --git a/search.php b/search.php
index 0d64ed04..bfd4b022 100644
--- a/search.php
+++ b/search.php
@@ -1,174 +1,178 @@
$boards, 'board' => isset($_GET['board']) ? $_GET['board'] : false, 'search' => isset($_GET['search']) ? str_replace('"', '"', utf8tohtml($_GET['search'])) : false));
+
+if (isset($_GET['search']) && !empty($_GET['search']) && isset($_GET['board']) && in_array($_GET['board'], $boards)) {
+ $phrase = $_GET['search'];
+ $_body = '';
+
+ $query = prepare("SELECT COUNT(*) FROM ``search_queries`` WHERE `ip` = :ip AND `time` > :time");
+ $query->bindValue(':ip', $_SERVER['REMOTE_ADDR']);
+ $query->bindValue(':time', time() - ($queries_per_minutes[1] * 60));
+ $query->execute() or error(db_error($query));
+ if ($query->fetchColumn() > $queries_per_minutes[0])
+ error(_('Wait a while before searching again, please.'));
+
+ $query = prepare("SELECT COUNT(*) FROM ``search_queries`` WHERE `time` > :time");
+ $query->bindValue(':time', time() - ($queries_per_minutes_all[1] * 60));
+ $query->execute() or error(db_error($query));
+ if ($query->fetchColumn() > $queries_per_minutes_all[0])
+ error(_('Wait a while before searching again, please.'));
+
+
+ $query = prepare("INSERT INTO ``search_queries`` VALUES (:ip, :time, :query)");
+ $query->bindValue(':ip', $_SERVER['REMOTE_ADDR']);
+ $query->bindValue(':time', time());
+ $query->bindValue(':query', $phrase);
+ $query->execute() or error(db_error($query));
+
+ _syslog(LOG_NOTICE, 'Searched /' . $_GET['board'] . '/ for "' . $phrase . '"');
+
+ // Cleanup search queries table
+ $query = prepare("DELETE FROM ``search_queries`` WHERE `time` <= :time");
+ $query->bindValue(':time', time() - ($queries_per_minutes_all[1] * 60));
+ $query->execute() or error(db_error($query));
+
+ openBoard($_GET['board']);
+
+ $filters = Array();
+
+ function search_filters($m) {
+ global $filters;
+ $name = $m[2];
+ $value = isset($m[4]) ? $m[4] : $m[3];
+
+ if (!in_array($name, array('id', 'thread', 'subject', 'name'))) {
+ // unknown filter
+ return $m[0];
+ }
+
+ $filters[$name] = $value;
+
+ return $m[1];
}
- $queries_per_minutes = $config['search']['queries_per_minutes'];
- $queries_per_minutes_all = $config['search']['queries_per_minutes_all'];
- $search_limit = $config['search']['search_limit'];
-
- if (isset($config['search']['boards'])) {
- $boards = $config['search']['boards'];
+ $phrase = trim(preg_replace_callback('/(^|\s)(\w+):("(.*)?"|[^\s]*)/', 'search_filters', $phrase));
+
+ if (!preg_match('/[^*^\s]/', $phrase) && empty($filters)) {
+ _syslog(LOG_WARNING, 'Query too broad.');
+ $body .= '(Query too broad.)
';
+ echo Element('page.html', Array(
+ 'config'=>$config,
+ 'title'=>'Search',
+ 'body'=>$body,
+ ));
+ exit;
+ }
+
+ // Escape escape character
+ $phrase = str_replace('!', '!!', $phrase);
+
+ // Remove SQL wildcard
+ $phrase = str_replace('%', '!%', $phrase);
+
+ // Use asterisk as wildcard to suit convention
+ $phrase = str_replace('*', '%', $phrase);
+
+ // Remove `, it's used by table prefix magic
+ $phrase = str_replace('`', '!`', $phrase);
+
+ $like = '';
+ $match = Array();
+
+ // Find exact phrases
+ if (preg_match_all('/"(.+?)"/', $phrase, $m)) {
+ foreach($m[1] as &$quote) {
+ $phrase = str_replace("\"{$quote}\"", '', $phrase);
+ $match[] = $pdo->quote($quote);
+ }
+ }
+
+ $words = explode(' ', $phrase);
+ foreach($words as &$word) {
+ if (empty($word)) {
+ continue;
+ }
+ $match[] = $pdo->quote($word);
+ }
+
+ $like = '';
+ foreach($match as &$phrase) {
+ if (!empty($like)) {
+ $like .= ' AND ';
+ }
+ $phrase = preg_replace('/^\'(.+)\'$/', '\'%$1%\'', $phrase);
+ $like .= '`body` LIKE ' . $phrase . ' ESCAPE \'!\'';
+ }
+
+ foreach($filters as $name => $value) {
+ if (!empty($like)) {
+ $like .= ' AND ';
+ }
+ $like .= '`' . $name . '` = '. $pdo->quote($value);
+ }
+
+ $like = str_replace('%', '%%', $like);
+
+ $query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE " . $like . " ORDER BY `time` DESC LIMIT :limit", $board['uri']));
+ $query->bindValue(':limit', $search_limit, PDO::PARAM_INT);
+ $query->execute() or error(db_error($query));
+
+ if ($query->rowCount() == $search_limit) {
+ _syslog(LOG_WARNING, 'Query too broad.');
+ $body .= '('._('Query too broad.').')
';
+ echo Element('page.html', Array(
+ 'config'=>$config,
+ 'title'=>'Search',
+ 'body'=>$body,
+ ));
+ exit;
+ }
+
+ $temp = '';
+ while ($post = $query->fetch()) {
+ if (!$post['thread']) {
+ $po = new Thread($post);
+ } else {
+ $po = new Post($post);
+ }
+ $temp .= $po->build(true) . ' ';
+ }
+
+ if (!empty($temp))
+ $_body .= ' ' .
+ sprintf(ngettext('%d result in', '%d results in', $query->rowCount()),
+ $query->rowCount()) . ' ' .
+ sprintf($config['board_abbreviation'], $board['uri']) . ' - ' . $board['title'] .
+ ' ' . $temp . ' ';
+
+ $body .= ' ';
+ if (!empty($_body)) {
+ $body .= $_body;
} else {
- $boards = listBoards(TRUE);
+ $body .= '('._('No results.').')
';
}
-
- $body = Element('search_form.html', Array('boards' => $boards, 'board' => isset($_GET['board']) ? $_GET['board'] : false, 'search' => isset($_GET['search']) ? str_replace('"', '"', utf8tohtml($_GET['search'])) : false));
-
- if(isset($_GET['search']) && !empty($_GET['search']) && isset($_GET['board']) && in_array($_GET['board'], $boards)) {
- $phrase = $_GET['search'];
- $_body = '';
-
- $query = prepare("SELECT COUNT(*) FROM ``search_queries`` WHERE `ip` = :ip AND `time` > :time");
- $query->bindValue(':ip', $_SERVER['REMOTE_ADDR']);
- $query->bindValue(':time', time() - ($queries_per_minutes[1] * 60));
- $query->execute() or error(db_error($query));
- if($query->fetchColumn() > $queries_per_minutes[0])
- error(_('Wait a while before searching again, please.'));
-
- $query = prepare("SELECT COUNT(*) FROM ``search_queries`` WHERE `time` > :time");
- $query->bindValue(':time', time() - ($queries_per_minutes_all[1] * 60));
- $query->execute() or error(db_error($query));
- if($query->fetchColumn() > $queries_per_minutes_all[0])
- error(_('Wait a while before searching again, please.'));
-
-
- $query = prepare("INSERT INTO ``search_queries`` VALUES (:ip, :time, :query)");
- $query->bindValue(':ip', $_SERVER['REMOTE_ADDR']);
- $query->bindValue(':time', time());
- $query->bindValue(':query', $phrase);
- $query->execute() or error(db_error($query));
-
- _syslog(LOG_NOTICE, 'Searched /' . $_GET['board'] . '/ for "' . $phrase . '"');
+}
- // Cleanup search queries table
- $query = prepare("DELETE FROM ``search_queries`` WHERE `time` <= :time");
- $query->bindValue(':time', time() - ($queries_per_minutes_all[1] * 60));
- $query->execute() or error(db_error($query));
-
- openBoard($_GET['board']);
-
- $filters = Array();
-
- function search_filters($m) {
- global $filters;
- $name = $m[2];
- $value = isset($m[4]) ? $m[4] : $m[3];
-
- if(!in_array($name, array('id', 'thread', 'subject', 'name'))) {
- // unknown filter
- return $m[0];
- }
-
- $filters[$name] = $value;
-
- return $m[1];
- }
-
- $phrase = trim(preg_replace_callback('/(^|\s)(\w+):("(.*)?"|[^\s]*)/', 'search_filters', $phrase));
-
- if(!preg_match('/[^*^\s]/', $phrase) && empty($filters)) {
- _syslog(LOG_WARNING, 'Query too broad.');
- $body .= '(Query too broad.)
';
- echo Element('page.html', Array(
- 'config'=>$config,
- 'title'=>'Search',
- 'body'=>$body,
- ));
- exit;
- }
-
- // Escape escape character
- $phrase = str_replace('!', '!!', $phrase);
-
- // Remove SQL wildcard
- $phrase = str_replace('%', '!%', $phrase);
-
- // Use asterisk as wildcard to suit convention
- $phrase = str_replace('*', '%', $phrase);
-
- // Remove `, it's used by table prefix magic
- $phrase = str_replace('`', '!`', $phrase);
-
- $like = '';
- $match = Array();
-
- // Find exact phrases
- if(preg_match_all('/"(.+?)"/', $phrase, $m)) {
- foreach($m[1] as &$quote) {
- $phrase = str_replace("\"{$quote}\"", '', $phrase);
- $match[] = $pdo->quote($quote);
- }
- }
-
- $words = explode(' ', $phrase);
- foreach($words as &$word) {
- if(empty($word))
- continue;
- $match[] = $pdo->quote($word);
- }
-
- $like = '';
- foreach($match as &$phrase) {
- if(!empty($like))
- $like .= ' AND ';
- $phrase = preg_replace('/^\'(.+)\'$/', '\'%$1%\'', $phrase);
- $like .= '`body` LIKE ' . $phrase . ' ESCAPE \'!\'';
- }
-
- foreach($filters as $name => $value) {
- if(!empty($like))
- $like .= ' AND ';
- $like .= '`' . $name . '` = '. $pdo->quote($value);
- }
-
- $like = str_replace('%', '%%', $like);
-
- $query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE " . $like . " ORDER BY `time` DESC LIMIT :limit", $board['uri']));
- $query->bindValue(':limit', $search_limit, PDO::PARAM_INT);
- $query->execute() or error(db_error($query));
-
- if($query->rowCount() == $search_limit) {
- _syslog(LOG_WARNING, 'Query too broad.');
- $body .= '('._('Query too broad.').')
';
- echo Element('page.html', Array(
- 'config'=>$config,
- 'title'=>'Search',
- 'body'=>$body,
- ));
- exit;
- }
-
- $temp = '';
- while($post = $query->fetch()) {
- if(!$post['thread']) {
- $po = new Thread($post);
- } else {
- $po = new Post($post);
- }
- $temp .= $po->build(true) . ' ';
- }
-
- if(!empty($temp))
- $_body .= '' .
- sprintf(ngettext('%d result in', '%d results in', $query->rowCount()),
- $query->rowCount()) . ' ' .
- sprintf($config['board_abbreviation'], $board['uri']) . ' - ' . $board['title'] .
- ' ' . $temp . ' ';
-
- $body .= ' ';
- if(!empty($_body))
- $body .= $_body;
- else
- $body .= '('._('No results.').')
';
- }
-
- echo Element('page.html', Array(
- 'config'=>$config,
- 'title'=>_('Search'),
- 'body'=>'' . $body
- ));
+echo Element('page.html', Array(
+ 'config'=>$config,
+ 'title'=>_('Search'),
+ 'body'=>'' . $body
+));
diff --git a/static/banners/1734708324675.jpg b/static/banners/1734708324675.jpg
new file mode 100644
index 00000000..bcb43222
Binary files /dev/null and b/static/banners/1734708324675.jpg differ
diff --git a/static/banners/closeted-dengoid.jpg b/static/banners/closeted-dengoid.jpg
new file mode 100644
index 00000000..91b0f0fc
Binary files /dev/null and b/static/banners/closeted-dengoid.jpg differ
diff --git a/static/banners/just-monika-opt.webp b/static/banners/just-monika-opt.webp
deleted file mode 100644
index 731f985a..00000000
Binary files a/static/banners/just-monika-opt.webp and /dev/null differ
diff --git a/static/banners/thread-on-leftypol.jpg b/static/banners/thread-on-leftypol.jpg
new file mode 100644
index 00000000..df5aadb6
Binary files /dev/null and b/static/banners/thread-on-leftypol.jpg differ
diff --git a/static/flags/420.png b/static/flags/420.png
new file mode 100644
index 00000000..019f2e7f
Binary files /dev/null and b/static/flags/420.png differ
diff --git a/static/flags/rodina_get.png b/static/flags/rodina_get.png
new file mode 100644
index 00000000..a22169ec
Binary files /dev/null and b/static/flags/rodina_get.png differ
diff --git a/static/flags/rodina_lp.png b/static/flags/rodina_lp.png
new file mode 100644
index 00000000..7cdd61a8
Binary files /dev/null and b/static/flags/rodina_lp.png differ
diff --git a/static/flags/stiner.png b/static/flags/stirner.png
similarity index 100%
rename from static/flags/stiner.png
rename to static/flags/stirner.png
diff --git a/static/flags/tania.png b/static/flags/tania.png
new file mode 100644
index 00000000..c711d149
Binary files /dev/null and b/static/flags/tania.png differ
diff --git a/static/leftypol_logo.png b/static/leftypol_logo.png
new file mode 100644
index 00000000..685a16e6
Binary files /dev/null and b/static/leftypol_logo.png differ
diff --git a/stylesheets/dark.css b/stylesheets/dark.css
index 9f3df691..c6d338f2 100644
--- a/stylesheets/dark.css
+++ b/stylesheets/dark.css
@@ -203,6 +203,10 @@ div.boardlist:not(.bottom) {
div.report {
color: #666666;
}
+theme-catalog div.thread:hover {
+ background: #555;
+ border-color: transparent;
+}
/* options.js */
#options_div, #alert_div {
diff --git a/stylesheets/dark_red.css b/stylesheets/dark_red.css
index 4ffe4eeb..71ca26e0 100644
--- a/stylesheets/dark_red.css
+++ b/stylesheets/dark_red.css
@@ -194,6 +194,10 @@ div.boardlist:not(.bottom) {
div.report {
color: #666666;
}
+.theme-catalog div.thread:hover {
+ background: #4f4f4f;
+ border-color: transparent;
+}
#options_div, #alert_div {
background: #333333;
}
diff --git a/stylesheets/dark_spook.css b/stylesheets/dark_spook.css
index 2ca55a20..e667cc01 100644
--- a/stylesheets/dark_spook.css
+++ b/stylesheets/dark_spook.css
@@ -209,6 +209,11 @@ div.boardlist:not(.bottom) {
background-color: #1E1E1E;
}
+.theme-catalog div.thread:hover {
+ background: #555;
+ border-color: transparent;
+}
+
div.report {
color: #666666;
}
diff --git a/stylesheets/style.css b/stylesheets/style.css
index 815e1853..bef69cca 100644
--- a/stylesheets/style.css
+++ b/stylesheets/style.css
@@ -380,6 +380,7 @@ form table tr td div.center {
.file {
float: left;
+ min-width: 100px;
}
.file:not(.multifile) .post-image {
@@ -390,6 +391,10 @@ form table tr td div.center {
float: none;
}
+.file.multifile {
+ margin: 0 10px 0 0;
+}
+
.file.multifile > p {
width: 0px;
min-width: 100%;
@@ -570,6 +575,13 @@ div.post div.body {
white-space: pre-wrap;
}
+div.post div.body:before {
+ content: "";
+ width: 18ch;
+ display: block;
+ overflow: hidden;
+}
+
div.post.reply.highlighted {
background: #D6BAD0;
}
diff --git a/stylesheets/szalet.css b/stylesheets/szalet.css
index a766416e..6feb4939 100644
--- a/stylesheets/szalet.css
+++ b/stylesheets/szalet.css
@@ -206,3 +206,8 @@ div.pages {
border: 0;
background: none !important;
}
+
+.theme-catalog div.thread:hover {
+ background: #583E28;
+ border-color: transparent;
+}
diff --git a/stylesheets/tsuki.css b/stylesheets/tsuki.css
index 523bdb71..9995889d 100644
--- a/stylesheets/tsuki.css
+++ b/stylesheets/tsuki.css
@@ -303,10 +303,6 @@ div.post.reply div.body {
padding-bottom: 0.3em;
}
-div.post.reply.highlighted {
- background: #D6BAD0;
-}
-
div.post.reply div.body a {
color: #D00;
}
@@ -322,6 +318,8 @@ div.post div.body {
div.post.reply {
background: #D6DAF0;
+ border: #555555 1px solid;
+ box-shadow: 4px 4px #555;
margin: 0.2em 4px;
padding: 0.2em 0.3em 0.5em 0.6em;
border-width: 1px;
@@ -692,8 +690,8 @@ pre {
}
.theme-catalog div.thread:hover {
- background: #D6DAF0;
- border-color: #B7C5D9;
+ background: #927C8E;
+ border-color: transparent;
}
.theme-catalog div.grid-size-vsmall img {
@@ -1359,18 +1357,8 @@ a.post_no:hover {
color: #32DD72 !important;
text-decoration: underline overline;
}
-div.post.reply {
- background: #111;
- border: #555555 1px solid;
- box-shadow: 4px 4px #555;
-
- @media (max-width: 48em) {
- border-left-style: none;
- border-right-style: none;
- }
-}
div.post.reply.highlighted {
- background: #555;
+ background: #927C8E;
border: transparent 1px solid;
@media (max-width: 48em) {
diff --git a/templates/rules.html b/templates/rules.html
index 9eaa3c3b..cfb8c8c8 100644
--- a/templates/rules.html
+++ b/templates/rules.html
@@ -62,9 +62,11 @@ Opening posts with liberalism or reactionary topics will be treated with far mor
These examples are low quality posts that are considered, at best, bait, but are better described as spam. Any poster that violates this rule may be subject to a ban, and any post that violates this is subject to deletion at the discretion of moderators, if they feel that the topic may be an avenue for productive discussion.
+15) In order to incentivize creativity and develop a genuine culture, non-original Wojaks, Pepes, or Groypers which are not /leftypol/ original content are considered spam and banned.
+
META:
-15) Volunteers may remove other posts according to their own discretion which they feel do not contribute to the stated mission of /leftypol/, but they should try to adhere to the standards of the community and of their fellow moderators, and to refrain from arbitrary decisions. Where there is disagreement among moderators, the matter will be decided by informal consensus of currently active moderators. If there is still disagreement, the matter should be escalated to a formal vote.
+16) Volunteers may remove other posts according to their own discretion which they feel do not contribute to the stated mission of /leftypol/, but they should try to adhere to the standards of the community and of their fellow moderators, and to refrain from arbitrary decisions. Where there is disagreement among moderators, the matter will be decided by informal consensus of currently active moderators. If there is still disagreement, the matter should be escalated to a formal vote.
-16) Users have the right to question and challenge any bans or post removals, or other moderator actions, which they feel are unfair or do not live up to the spirit of the rules. This may be done in the moderation feedback threads on the various boards, on the /meta/ board, through the ban appeal feature, or in the Leftypol Matrix Congress chat, but comments should be considered and constructive, and should not devolve into polemics against the volunteers. Ultimately, the judgement of the moderation team is final.
+17) Users have the right to question and challenge any bans or post removals, or other moderator actions, which they feel are unfair or do not live up to the spirit of the rules. This may be done in the moderation feedback threads on the various boards, on the /meta/ board, through the ban appeal feature, or in the Leftypol Matrix Congress chat, but comments should be considered and constructive, and should not devolve into polemics against the volunteers. Ultimately, the judgement of the moderation team is final.
diff --git a/templates/themes/categories/frames.html b/templates/themes/categories/frames.html
index 6b2fac38..1c4673cc 100644
--- a/templates/themes/categories/frames.html
+++ b/templates/themes/categories/frames.html
@@ -16,13 +16,13 @@
border-width: 2px;
margin-right: 15px;
}
-
+
.introduction {
grid-column: 2 / 9;
grid-row: 1;
width: 100%;
}
-
+
.content {
grid-column: 2 / 9;
grid-row: 2;
@@ -35,7 +35,7 @@
gap: 20px;
height: 100vh;
}
-
+
.modlog {
width: 50%;
text-align: left;
@@ -69,7 +69,7 @@
li a.system {
font-weight: bold;
}
-
+
@media (max-width:768px) {
body{
display: grid;
@@ -78,7 +78,7 @@
height: 100vh;
width: 100%;
}
-
+
.introduction {
grid-column: 1;
grid-row: 1;
@@ -96,17 +96,18 @@
grid-column: 1;
grid-row: 3;
width: 100%;
+ word-break: break-all;
}
-
+
.modlog {
width: 100%;
text-align: center;
}
-
+
table {
table-layout: fixed;
}
-
+
table.modlog tr th {
white-space: normal;
word-wrap: break-word;
diff --git a/templates/themes/categories/news.html b/templates/themes/categories/news.html
index 484c4eb0..76722e41 100644
--- a/templates/themes/categories/news.html
+++ b/templates/themes/categories/news.html
@@ -11,7 +11,7 @@
.home-description {
margin: 20px auto 0 auto;
text-align: center;
- max-width: 700px;"
+ max-width: 700px;
}
{{ boardlist.top }}
diff --git a/templates/themes/faq/index.html b/templates/themes/faq/index.html
index 6ddf872a..6877edd4 100644
--- a/templates/themes/faq/index.html
+++ b/templates/themes/faq/index.html
@@ -33,7 +33,7 @@
I'm new here and learned politics from memes would like to learn about leftism! Where should I start ?
- There are some beginner lists in the reading thread . Meanwhile, consider asking your questions in /edu/ or in a relevant /leftypol/ thread, such as QTDDTOT .
+ Consider asking your questions in /edu/ or in a relevant /leftypol/ thread, such as QTDDTOT .
What is the purpose of leftypol.org ?
@@ -88,7 +88,7 @@
How can I suggest or submit fixes to the site ?
- There is a /meta/ thread for this, and our Gitlab repo .
+ There is a /meta/ thread for this, and our Forgejo repo .
I don't trust Tor exit nodes. Do you have an .onion site ?
@@ -129,6 +129,10 @@
What are the maximum filesize for attachments ?
Maximum file size in megabytes for attachments to a single post is 80MB (e.g. 5 * 16MB), as most boards support uploading 5 attachments by default. Maximum file size in pixels for images is currently set to 20000 by 20000.
+
+ Can I have an account on your git instance?
+
+ Create the account on Forgejo , then contact the staff via the Tech Team General thread on /meta/ to get your account approved.
diff --git a/tmp/tesseract/.gitkeep b/tmp/tesseract/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/tools/delete-stray-images.php b/tools/delete-stray-images.php
index 36962730..baf955f8 100755
--- a/tools/delete-stray-images.php
+++ b/tools/delete-stray-images.php
@@ -44,20 +44,26 @@ foreach ($boards as $board) {
);
foreach ($stray_src as $src) {
- $stats['deleted']++;
- $stats['size'] += filesize($board['uri'] . "/" . $config['dir']['img'] . $src);
- if (!file_unlink($board['uri'] . "/" . $config['dir']['img'] . $src)) {
- $er = error_get_last();
- die("error: " . $er['message'] . "\n");
+ $p = $board['uri'] . "/" . $config['dir']['img'] . $src;
+ if (file_exists($p)) {
+ $stats['deleted']++;
+ $stats['size'] += filesize($p);
+ if (!file_unlink($p)) {
+ $er = error_get_last();
+ die("error: " . $er['message'] . "\n");
+ }
}
}
foreach ($stray_thumb as $thumb) {
- $stats['deleted']++;
- $stats['size'] += filesize($board['uri'] . "/" . $config['dir']['thumb'] . $thumb);
- if (!file_unlink($board['uri'] . "/" . $config['dir']['thumb'] . $thumb)) {
- $er = error_get_last();
- die("error: " . $er['message'] . "\n");
+ $p = $board['uri'] . "/" . $config['dir']['thumb'] . $thumb;
+ if (file_exists($p)) {
+ $stats['deleted']++;
+ $stats['size'] += filesize($p);
+ if (!file_unlink($p)) {
+ $er = error_get_last();
+ die("error: " . $er['message'] . "\n");
+ }
}
}