Merge pull request 'Remove all nntpchan support' (#120) from nntpchan-remove into config

Reviewed-on: leftypol/leftypol#120
This commit is contained in:
Zankaria 2025-04-23 15:07:29 -05:00
commit 2cac548b4d
4 changed files with 166 additions and 607 deletions

View file

@ -1896,45 +1896,6 @@
// Example: Adding the pre-markup post body to the API as "com_nomarkup".
// $config['api']['extra_fields'] = array('body_nomarkup' => 'com_nomarkup');
/*
* ==================
* NNTPChan settings
* ==================
*/
/*
* Please keep in mind that NNTPChan support in vichan isn't finished yet / is in an experimental
* state. Please join #nntpchan on Rizon in order to peer with someone.
*/
$config['nntpchan'] = array();
// Enable NNTPChan integration
$config['nntpchan']['enabled'] = false;
// NNTP server
$config['nntpchan']['server'] = "localhost:1119";
// Global dispatch array. Add your boards to it to enable them. Please make
// sure that this setting is set in a global context.
$config['nntpchan']['dispatch'] = array(); // 'overchan.test' => 'test'
// Trusted peer - an IP address of your NNTPChan instance. This peer will have
// increased capabilities, eg.: will evade spamfilter.
$config['nntpchan']['trusted_peer'] = '127.0.0.1';
// Salt for message ID generation. Keep it long and secure.
$config['nntpchan']['salt'] = 'change_me+please';
// A local message ID domain. Make sure to change it.
$config['nntpchan']['domain'] = 'example.vichan.net';
// An NNTPChan group name.
// Please set this setting in your board/config.php, not globally.
$config['nntpchan']['group'] = false; // eg. 'overchan.test'
/*
* ====================
* Other/uncategorized

View file

@ -1,152 +0,0 @@
<?php
/*
* Copyright (c) 2016 vichan-devel
*/
defined('TINYBOARD') or exit;
function gen_msgid($board, $id) {
global $config;
$b = preg_replace("/[^0-9a-zA-Z$]/", 'x', $board);
$salt = sha1($board . "|" . $id . "|" . $config['nntpchan']['salt']);
$salt = substr($salt, 0, 7);
$salt = base_convert($salt, 16, 36);
return "<$b.$id.$salt@".$config['nntpchan']['domain'].">";
}
function gen_nntp($headers, $files) {
if (count($files) == 0) {
}
else if (count($files) == 1 && $files[0]['type'] == 'text/plain') {
$content = $files[0]['text'] . "\r\n";
$headers['Content-Type'] = "text/plain; charset=UTF-8";
}
else {
$boundary = sha1($headers['Message-Id']);
$content = "";
$headers['Content-Type'] = "multipart/mixed; boundary=$boundary";
foreach ($files as $file) {
$content .= "--$boundary\r\n";
if (isset($file['name'])) {
$file['name'] = preg_replace('/[\r\n\0"]/', '', $file['name']);
$content .= "Content-Disposition: form-data; filename=\"$file[name]\"; name=\"attachment\"\r\n";
}
$type = explode('/', $file['type'])[0];
if ($type == 'text') {
$file['type'] .= '; charset=UTF-8';
}
$content .= "Content-Type: $file[type]\r\n";
if ($type != 'text' && $type != 'message') {
$file['text'] = base64_encode($file['text']);
$content .= "Content-Transfer-Encoding: base64\r\n";
}
$content .= "\r\n";
$content .= $file['text'];
$content .= "\r\n";
}
$content .= "--$boundary--\r\n";
$headers['Mime-Version'] = '1.0';
}
//$headers['Content-Length'] = strlen($content);
$headers['Date'] = date('r', $headers['Date']);
$out = "";
foreach ($headers as $id => $val) {
$val = str_replace("\n", "\n\t", $val);
$out .= "$id: $val\r\n";
}
$out .= "\r\n";
$out .= $content;
return $out;
}
function nntp_publish($msg, $id) {
global $config;
$server = $config["nntpchan"]["server"];
$s = fsockopen("tcp://$server");
fgets($s);
fputs($s, "MODE STREAM\r\n");
fgets($s);
fputs($s, "TAKETHIS $id\r\n");
fputs($s, $msg);
fputs($s, "\r\n.\r\n");
fgets($s);
fputs($s, "QUIT\r\n");
fclose($s);
}
function post2nntp($post, $msgid) {
global $config;
$headers = array();
$files = array();
$headers['Message-Id'] = $msgid;
$headers['Newsgroups'] = $config['nntpchan']['group'];
$headers['Date'] = time();
$headers['Subject'] = $post['subject'] ? $post['subject'] : "None";
$headers['From'] = $post['name'] . " <poster@" . $config['nntpchan']['domain'] . ">";
if ($post['email'] == 'sage') {
$headers['X-Sage'] = true;
}
if (!$post['op']) {
// Get muh parent
$query = prepare("SELECT `message_id` FROM ``nntp_references`` WHERE `board` = :board AND `id` = :id");
$query->bindValue(':board', $post['board']);
$query->bindValue(':id', $post['thread']);
$query->execute() or error(db_error($query));
if ($result = $query->fetch(PDO::FETCH_ASSOC)) {
$headers['References'] = $result['message_id'];
}
else {
return false; // We don't have OP. Discarding.
}
}
// Let's parse the body a bit.
$body = trim($post['body_nomarkup']);
$body = preg_replace('/\r?\n/', "\r\n", $body);
$body = preg_replace_callback('@>>(>/([a-zA-Z0-9_+-]+)/)?([0-9]+)@', function($o) use ($post) {
if ($o[1]) {
$board = $o[2];
}
else {
$board = $post['board'];
}
$id = $o[3];
$query = prepare("SELECT `message_id_digest` FROM ``nntp_references`` WHERE `board` = :board AND `id` = :id");
$query->bindValue(':board', $board);
$query->bindValue(':id', $id);
$query->execute() or error(db_error($query));
if ($result = $query->fetch(PDO::FETCH_ASSOC)) {
return ">>".substr($result['message_id_digest'], 0, 18);
}
else {
return $o[0]; // Should send URL imo
}
}, $body);
$body = preg_replace('/>>>>([0-9a-fA-F])+/', '>>\1', $body);
$files[] = array('type' => 'text/plain', 'text' => $body);
foreach ($post['files'] as $id => $file) {
$fc = array();
$fc['type'] = $file['type'];
$fc['text'] = file_get_contents($file['file_path']);
$fc['name'] = $file['name'];
$files[] = $fc;
}
return array($headers, $files);
}

View file

@ -1,30 +0,0 @@
<?php
define('TINYBOARD', 'fuck yeah');
require_once('nntpchan.php');
die();
$time = time();
echo "\n@@@@ Thread:\n";
echo $m0 = gennntp(["From" => "czaks <marcin@6irc.net>", "Message-Id" => "<1234.0000.".$time."@example.vichan.net>", "Newsgroups" => "overchan.test", "Date" => time(), "Subject" => "None"],
[['type' => 'text/plain', 'text' => "THIS IS A NEW TEST THREAD"]]);
echo "\n@@@@ Single msg:\n";
echo $m1 = gennntp(["From" => "czaks <marcin@6irc.net>", "Message-Id" => "<1234.1234.".$time."@example.vichan.net>", "Newsgroups" => "overchan.test", "Date" => time(), "Subject" => "None", "References" => "<1234.0000.".$time."@example.vichan.net>"],
[['type' => 'text/plain', 'text' => "hello world, with no image :("]]);
echo "\n@@@@ Single msg and pseudoimage:\n";
echo $m2 = gennntp(["From" => "czaks <marcin@6irc.net>", "Message-Id" => "<1234.2137.".$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 an image!"],
['type' => 'image/gif', 'text' => base64_decode("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="), 'name' => "urgif.gif"]]);
echo "\n@@@@ Single msg and two pseudoimages:\n";
echo $m3 = gennntp(["From" => "czaks <marcin@6irc.net>", "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>");

232
post.php
View file

@ -353,166 +353,6 @@ function db_select_ban_appeals($ban_id)
* Method handling functions
*/
$dropped_post = false;
function handle_nntpchan(Context $ctx)
{
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') {
$ctx->get(LogDriver::class)->log(LogDriver::DEBUG, 'MM: Files: ' . print_r($GLOBALS, true));
$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
@ -789,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']);
}
@ -834,7 +674,6 @@ 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'])) {
@ -934,9 +773,6 @@ function handle_post(Context $ctx)
if ($config['robot_enable'] && $config['robot_mute']) {
checkMute();
}
} else {
$mod = $post['mod'] = false;
}
// Check if thread exists.
if (!$post['op']) {
@ -1021,7 +857,6 @@ 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 == '') {
@ -1049,11 +884,7 @@ function handle_post(Context $ctx)
error($config['error']['image_hard_limit']);
}
}
} else {
if (!$post['op']) {
$numposts = numPosts($post['thread']);
}
}
if ($post['has_file']) {
// Determine size sanity
@ -1160,7 +991,6 @@ 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']) {
@ -1172,7 +1002,6 @@ function handle_post(Context $ctx)
if (sizeof($post['files']) > $config['max_images']) {
error($config['error']['toomanyimages']);
}
}
if ($config['strip_combining_chars']) {
$post['name'] = strip_combining_chars($post['name']);
@ -1181,7 +1010,6 @@ 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'));
@ -1209,7 +1037,6 @@ function handle_post(Context $ctx)
error($config['error']['toolong_body']);
}
}
}
wordfilters($post['body']);
@ -1219,7 +1046,6 @@ function handle_post(Context $ctx)
$post['body'] .= "\n<tinyboard raw html>1</tinyboard>";
}
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);
@ -1265,12 +1091,10 @@ function handle_post(Context $ctx)
$post['body'] .= "\n<tinyboard tag>" . $_POST['tag'] . "</tinyboard>";
}
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<tinyboard proxy>" . $proxy . "</tinyboard>";
}
}
$post['body_nomarkup'] = $post['body'];
$post['tracked_cites'] = markup($post['body'], true);
@ -1311,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);
}
@ -1616,7 +1440,7 @@ function handle_post(Context $ctx)
}
}
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()));
@ -1662,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
@ -1863,20 +1652,11 @@ function handle_appeal(Context $ctx)
$ctx = Vichan\build_context($config);
// 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($ctx);
} else {
error("NNTPChan: NNTPChan support is disabled");
}
}
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);