diff --git a/boards.php b/boards.php
index f22cb524..de7f15e8 100644
--- a/boards.php
+++ b/boards.php
@@ -10,10 +10,17 @@ if (php_sapi_name() == 'fpm-fcgi' && !$admin) {
error('Cannot be run directly.');
}
$boards = listBoards();
-
+$all_tags = array();
$total_posts_hour = 0;
$total_posts = 0;
+function to_tag($str) {
+ $str = trim($str);
+ $str = strtolower($str);
+ $str = str_replace(['_', ' '], '-', $str);
+ return $str;
+}
+
foreach ($boards as $i => $board) {
//$query = prepare(sprintf("SELECT (SELECT MAX(id) from ``posts_%s``) AS max, (SELECT MAX(id) FROM ``posts_%s`` WHERE FROM_UNIXTIME(time) < DATE_SUB(NOW(), INTERVAL 1 HOUR)) AS oldmax, (SELECT MAX(id) from ``posts_%s``) AS max_d, (SELECT MAX(id) FROM ``posts_%s`` WHERE FROM_UNIXTIME(time) < DATE_SUB(NOW(), INTERVAL 1 DAY)) AS oldmax_d, (SELECT count(id) FROM ``posts_%s``) AS count;", $board['uri'], $board['uri'], $board['uri'], $board['uri'], $board['uri']));
@@ -28,6 +35,23 @@ SELECT MAX(id) max, (SELECT COUNT(*) FROM ``posts_%s`` WHERE FROM_UNIXTIME(time)
$query->execute() or error(db_error($query));
$r = $query->fetch(PDO::FETCH_ASSOC);
+ $tquery = prepare("SELECT `tag` FROM ``board_tags`` WHERE `uri` = :uri");
+ $tquery->execute([":uri" => $board['uri']]) or error(db_error($tquery));
+ $r2 = $tquery->fetchAll(PDO::FETCH_ASSOC);
+
+ $tags = array();
+ if ($r2) {
+ foreach ($r2 as $ii => $t) {
+ $tag=to_tag($t['tag']);
+ $tags[] = $tag;
+ if (!isset($all_tags[$tag])) {
+ $all_tags[$tag] = (int)$r['uniq_ip'];
+ } else {
+ $all_tags[$tag] += $r['uniq_ip'];
+ }
+ }
+ }
+
$pph = $r['pph'];
$ppd = $r['ppd'];
@@ -38,6 +62,7 @@ SELECT MAX(id) max, (SELECT COUNT(*) FROM ``posts_%s`` WHERE FROM_UNIXTIME(time)
$boards[$i]['ppd'] = $ppd;
$boards[$i]['max'] = $r['max'];
$boards[$i]['uniq_ip'] = $r['uniq_ip'];
+ $boards[$i]['tags'] = $tags;
}
usort($boards,
@@ -86,14 +111,18 @@ foreach ($boards as $i => &$board) {
$n_boards = sizeof($boards);
$t_boards = $hidden_boards_total + $n_boards;
+$boards = array_values($boards);
+arsort($all_tags);
+
$config['additional_javascript'] = array('js/jquery.min.js', 'js/jquery.tablesorter.min.js');
-$body = Element("8chan/boards.html", array("config" => $config, "n_boards" => $n_boards, "t_boards" => $t_boards, "hidden_boards_total" => $hidden_boards_total, "total_posts" => $total_posts, "total_posts_hour" => $total_posts_hour, "boards" => $boards, "last_update" => date('r'), "uptime_p" => shell_exec('uptime -p')));
+$body = Element("8chan/boards-tags.html", array("config" => $config, "n_boards" => $n_boards, "t_boards" => $t_boards, "hidden_boards_total" => $hidden_boards_total, "total_posts" => $total_posts, "total_posts_hour" => $total_posts_hour, "boards" => $boards, "last_update" => date('r'), "uptime_p" => shell_exec('uptime -p'), 'tags' => $all_tags));
$html = Element("page.html", array("config" => $config, "body" => $body, "title" => "Boards on ∞chan"));
if ($admin) {
echo $html;
} else {
file_write("boards.json", json_encode($boards));
+ file_write("tags.json", json_encode($all_tags));
foreach ($boards as $i => $b) {
if (in_array($b['uri'], $config['no_top_bar_boards'])) {
unset($boards[$i]);
diff --git a/claim.php b/claim.php
index 4c733180..a07ccadf 100644
--- a/claim.php
+++ b/claim.php
@@ -16,7 +16,7 @@ function last_activity($board) {
$last_activity_date->setTimestamp($row['time']);
- $query = query("SELECT id, username FROM mods WHERE boards = '$board'");
+ $query = query("SELECT id, username FROM mods WHERE boards = '$board' AND type = 20");
$mods = $query->fetchAll();
if ($mods) {
diff --git a/expire.php b/expire.php
index e24b69cf..8ca0789f 100644
--- a/expire.php
+++ b/expire.php
@@ -8,8 +8,8 @@ $protected = array('burgers', 'cow', 'wilno', 'cute', 'yoga');
$q = query("SELECT uri FROM boards");
$boards = $q->fetchAll(PDO::FETCH_COLUMN);
$now = new DateTime();
-$ago = (new DateTime)->sub(new DateInterval('P3D'));
-$mod_ago = (new DateTime)->sub(new DateInterval('P7D'));
+$ago = (new DateTime)->sub(new DateInterval('P7D'));
+$mod_ago = (new DateTime)->sub(new DateInterval('P14D'));
// Find out the last activity for our board
$delete = array();
diff --git a/inc/8chan-mod-pages.php b/inc/8chan-mod-pages.php
index dfd51658..86d91ef8 100644
--- a/inc/8chan-mod-pages.php
+++ b/inc/8chan-mod-pages.php
@@ -20,6 +20,8 @@
$config['mod']['noticeboard_post'] = GLOBALVOLUNTEER;
$config['mod']['search'] = GLOBALVOLUNTEER;
$config['mod']['clean_global'] = GLOBALVOLUNTEER;
+ $config['mod']['view_notes'] = GLOBALVOLUNTEER;
+ $config['mod']['create_notes'] = GLOBALVOLUNTEER;
$config['mod']['debug_recent'] = ADMIN;
$config['mod']['debug_antispam'] = ADMIN;
$config['mod']['noticeboard_post'] = ADMIN;
@@ -29,6 +31,7 @@
$config['mod']['edit_flags'] = MOD;
$config['mod']['edit_settings'] = MOD;
$config['mod']['edit_volunteers'] = MOD;
+ $config['mod']['edit_tags'] = MOD;
$config['mod']['clean'] = BOARDVOLUNTEER;
// new perms
@@ -51,6 +54,55 @@
$config['mod']['view_ban'] = BOARDVOLUNTEER;
$config['mod']['reassign_board'] = ADMIN;
+ $config['mod']['custom_pages']['/tags/(\%b)'] = function ($b) {
+ global $board, $config;
+
+ if (!openBoard($b))
+ error("Could not open board!");
+
+ if (!hasPermission($config['mod']['edit_tags'], $b))
+ error($config['error']['noaccess']);
+
+ if (isset($_POST['tags'])) {
+ if (sizeof($_POST['tags']) > 5)
+ error(_('Too many tags.'));
+
+ $delete = prepare('DELETE FROM ``board_tags`` WHERE uri = :uri');
+ $delete->bindValue(':uri', $b);
+ $delete->execute();
+
+ foreach ($_POST['tags'] as $i => $tag) {
+ if ($tag) {
+ if (strlen($tag) > 255)
+ continue;
+
+ $insert = prepare('INSERT INTO ``board_tags``(uri, tag) VALUES (:uri, :tag)');
+ $insert->bindValue(':uri', $b);
+ $insert->bindValue(':tag', utf8tohtml($tag));
+ $insert->execute();
+ }
+ }
+
+ $update = prepare('UPDATE ``boards`` SET sfw = :sfw WHERE uri = :uri');
+ $update->bindValue(':uri', $b);
+ $update->bindValue(':sfw', isset($_POST['sfw']));
+ $update->execute();
+ }
+ $query = prepare('SELECT * FROM ``board_tags`` WHERE uri = :uri');
+ $query->bindValue(':uri', $b);
+ $query->execute();
+
+ $tags = $query->fetchAll();
+
+ $query = prepare('SELECT `sfw` FROM ``boards`` WHERE uri = :uri');
+ $query->bindValue(':uri', $b);
+ $query->execute();
+
+ $sfw = $query->fetchColumn();
+
+ mod_page(_('Edit tags'), 'mod/tags.html', array('board'=>$board,'token'=>make_secure_link_token('reassign/'.$board['uri']), 'tags'=>$tags, 'sfw'=>$sfw));
+ };
+
$config['mod']['custom_pages']['/reassign/(\%b)'] = function($b) {
global $board, $config;
@@ -435,10 +487,39 @@ $locale
$add_to_config
EOT;
+ // Clean up our CSS...no more expression() or off-site URLs.
+ $clean_css = preg_replace('/expression\s*\(/', '', $_POST['css']);
+
+ // URL matcher from SO:
+ $match_urls = '(?xi)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?«»“”‘’]))';
+
+ $matched = array();
+
+ preg_match_all("#$match_urls#im", $clean_css, $matched);
+
+ $allowed_urls = array('https://i.imgur.com/', 'https://media.8chan.co/', 'https://a.pomf.se/', 'https://fonts.googleapis.com/', 'http://8ch.net/');
+ $error = false;
+
+ if (isset($matched[0])) {
+ foreach ($matched[0] as $i => $v) {
+ $error = true;
+ foreach ($allowed_urls as $ii => $url) {
+ if (strpos($v, $url) === 0) {
+ $error = false;
+ break;
+ }
+ }
+ }
+ }
+
+ if ($error) {
+ error(_('Off-site links are not allowed in board stylesheets!'));
+ }
+
$query = query('SELECT `uri`, `title`, `subtitle` FROM ``boards`` WHERE `8archive` = TRUE');
file_write('8archive.json', json_encode($query->fetchAll(PDO::FETCH_ASSOC)));
file_write($b.'/config.php', $config_file);
- file_write('stylesheets/board/'.$b.'.css', $_POST['css']);
+ file_write('stylesheets/board/'.$b.'.css', $clean_css);
file_write($b.'/rules.html', Element('page.html', array('title'=>'Rules', 'subtitle'=>'', 'config'=>$config, 'body'=>'
'.purify($_POST['rules']).'
')));
file_write($b.'/rules.txt', $_POST['rules']);
@@ -446,7 +527,7 @@ EOT;
// Faster than openBoard and bypasses cache...we're trusting the PHP output
// to be safe enough to run with every request, we can eval it here.
- eval(preg_replace('/^\<\?php$/m', '', $config_file));
+ eval(str_replace('flags.php', "$b/flags.php", preg_replace('/^\<\?php$/m', '', $config_file)));
// be smarter about rebuilds...only some changes really require us to rebuild all threads
if ($_config['captcha']['enabled'] != $config['captcha']['enabled']
diff --git a/inc/bans.php b/inc/bans.php
index 14b7f939..215ff279 100644
--- a/inc/bans.php
+++ b/inc/bans.php
@@ -218,14 +218,8 @@ class Bans {
}
unset($ban['type']);
if ($filter_ips || ($board_access !== false && !in_array($ban['board'], $board_access))) {
- @list($ban['mask'], $subnet) = explode("/", $ban['mask']);
- $ban['mask'] = preg_split("/[\.:]/", $ban['mask']);
- $ban['mask'] = array_slice($ban['mask'], 0, 2);
- $ban['mask'] = implode(".", $ban['mask']);
- $ban['mask'] .= ".x.x";
- if (isset ($subnet)) {
- $ban['mask'] .= "/$subnet";
- }
+ $ban['mask'] = @less_ip($ban['mask']);
+
$ban['masked'] = true;
}
diff --git a/inc/config.php b/inc/config.php
index 89eaac69..b54d0e0f 100644
--- a/inc/config.php
+++ b/inc/config.php
@@ -753,7 +753,6 @@
// Allowed image file extensions.
$config['allowed_ext'][] = 'jpg';
$config['allowed_ext'][] = 'jpeg';
- $config['allowed_ext'][] = 'bmp';
$config['allowed_ext'][] = 'gif';
$config['allowed_ext'][] = 'png';
// $config['allowed_ext'][] = 'svg';
@@ -885,7 +884,7 @@
$config['thread_subject_in_title'] = false;
// Additional lines added to the footer of all pages.
- $config['footer'][] = _('All trademarks, copyrights, comments, and images on this page are owned by and are the responsibility of their respective parties.');
+ // $config['footer'][] = _('All trademarks, copyrights, comments, and images on this page are owned by and are the responsibility of their respective parties.');
// Characters used to generate a random password (with Javascript).
$config['genpassword_chars'] = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+';
diff --git a/inc/functions.php b/inc/functions.php
index f02abd3d..2d505173 100755
--- a/inc/functions.php
+++ b/inc/functions.php
@@ -29,14 +29,16 @@ mb_internal_encoding('UTF-8');
loadConfig();
function init_locale($locale, $error='error') {
- if (_setlocale(LC_ALL, $locale) === false) {
- $error('The specified locale (' . $locale . ') does not exist on your platform!');
- }
+ if ($locale === 'en')
+ $locale = 'en_US.utf8';
+
if (extension_loaded('gettext')) {
+ setlocale(LC_ALL, $locale);
bindtextdomain('tinyboard', './inc/locale');
bind_textdomain_codeset('tinyboard', 'UTF-8');
textdomain('tinyboard');
} else {
+ _setlocale(LC_ALL, $locale);
_bindtextdomain('tinyboard', './inc/locale');
_bind_textdomain_codeset('tinyboard', 'UTF-8');
_textdomain('tinyboard');
@@ -680,7 +682,7 @@ function listBoards($just_uri = false, $indexed_only = false) {
return $boards;
if (!$just_uri) {
- $query = query("SELECT ``boards``.`uri` uri, ``boards``.`title` title, ``boards``.`subtitle` subtitle, ``board_create``.`time` time, ``boards``.`indexed` indexed FROM ``boards``" . ( $indexed_only ? " WHERE `indexed` = 1 " : "" ) . "LEFT JOIN ``board_create`` ON ``boards``.`uri` = ``board_create``.`uri` ORDER BY ``boards``.`uri`") or error(db_error());
+ $query = query("SELECT ``boards``.`uri` uri, ``boards``.`title` title, ``boards``.`subtitle` subtitle, ``board_create``.`time` time, ``boards``.`indexed` indexed, ``boards``.`sfw` sfw FROM ``boards``" . ( $indexed_only ? " WHERE `indexed` = 1 " : "" ) . "LEFT JOIN ``board_create`` ON ``boards``.`uri` = ``board_create``.`uri` ORDER BY ``boards``.`uri`") or error(db_error());
$boards = $query->fetchAll(PDO::FETCH_ASSOC);
} else {
$boards = array();
@@ -1099,12 +1101,6 @@ function deletePost($id, $error_if_doesnt_exist=true, $rebuild_after=true) {
$antispam_query->bindValue(':board', $board['uri']);
$antispam_query->bindValue(':thread', $post['id']);
$antispam_query->execute() or error(db_error($antispam_query));
-
- cache::delete("thread_index_{$board['uri']}_{$post['id']}");
- cache::delete("thread_index_display_{$board['uri']}_{$post['id']}");
- cache::delete("thread_{$board['uri']}_{$post['id']}");
- cache::delete("thread_50_{$board['uri']}_{$post['id']}");
- cache::delete("catalog_{$board['uri']}");
} elseif ($query->rowCount() == 1) {
// Rebuild thread
$rebuild = &$post['thread'];
@@ -1196,62 +1192,57 @@ function index($page, $mod=false) {
return false;
$threads = array();
-
+
while ($th = $query->fetch(PDO::FETCH_ASSOC)) {
- if (($config['cache']['enabled'] && !($thread = cache::get("thread_index_display_{$board['uri']}_{$th['id']}")) || $mod)) {
- $thread = new Thread($th, $mod ? '?/' : $config['root'], $mod);
+ $thread = new Thread($th, $mod ? '?/' : $config['root'], $mod);
- if ($config['cache']['enabled']) {
- $cached = cache::get("thread_index_{$board['uri']}_{$th['id']}");
- if (isset($cached['replies'], $cached['omitted'])) {
- $replies = $cached['replies'];
- $omitted = $cached['omitted'];
- } else {
- unset($cached);
- }
+ if ($config['cache']['enabled']) {
+ $cached = cache::get("thread_index_{$board['uri']}_{$th['id']}");
+ if (isset($cached['replies'], $cached['omitted'])) {
+ $replies = $cached['replies'];
+ $omitted = $cached['omitted'];
+ } else {
+ unset($cached);
}
- if (!isset($cached)) {
- $posts = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE `thread` = :id ORDER BY `id` DESC LIMIT :limit", $board['uri']));
- $posts->bindValue(':id', $th['id']);
- $posts->bindValue(':limit', ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']), PDO::PARAM_INT);
- $posts->execute() or error(db_error($posts));
-
- $replies = array_reverse($posts->fetchAll(PDO::FETCH_ASSOC));
-
- if (count($replies) == ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview'])) {
- $count = numPosts($th['id']);
- $omitted = array('post_count' => $count['replies'], 'image_count' => $count['images']);
- } else {
- $omitted = false;
- }
-
- if ($config['cache']['enabled'])
- cache::set("thread_index_{$board['uri']}_{$th['id']}", array(
- 'replies' => $replies,
- 'omitted' => $omitted,
- ));
- }
-
- $num_images = 0;
- foreach ($replies as $po) {
- if ($po['num_files'])
- $num_images+=$po['num_files'];
-
- $thread->add(new Post($po, $mod ? '?/' : $config['root'], $mod));
- }
-
- $thread->images = $num_images;
- $thread->replies = isset($omitted['post_count']) ? $omitted['post_count'] : count($replies);
-
- if ($omitted) {
- $thread->omitted = $omitted['post_count'] - ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']);
- $thread->omitted_images = $omitted['image_count'] - $num_images;
- }
-
- if ($config['cache']['enabled'] && !$mod)
- cache::set("thread_index_display_{$board['uri']}_{$th['id']}", $thread);
-
}
+ if (!isset($cached)) {
+ $posts = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE `thread` = :id ORDER BY `id` DESC LIMIT :limit", $board['uri']));
+ $posts->bindValue(':id', $th['id']);
+ $posts->bindValue(':limit', ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']), PDO::PARAM_INT);
+ $posts->execute() or error(db_error($posts));
+
+ $replies = array_reverse($posts->fetchAll(PDO::FETCH_ASSOC));
+
+ if (count($replies) == ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview'])) {
+ $count = numPosts($th['id']);
+ $omitted = array('post_count' => $count['replies'], 'image_count' => $count['images']);
+ } else {
+ $omitted = false;
+ }
+
+ if ($config['cache']['enabled'])
+ cache::set("thread_index_{$board['uri']}_{$th['id']}", array(
+ 'replies' => $replies,
+ 'omitted' => $omitted,
+ ));
+ }
+
+ $num_images = 0;
+ foreach ($replies as $po) {
+ if ($po['num_files'])
+ $num_images+=$po['num_files'];
+
+ $thread->add(new Post($po, $mod ? '?/' : $config['root'], $mod));
+ }
+
+ $thread->images = $num_images;
+ $thread->replies = isset($omitted['post_count']) ? $omitted['post_count'] : count($replies);
+
+ if ($omitted) {
+ $thread->omitted = $omitted['post_count'] - ($th['sticky'] ? $config['threads_preview_sticky'] : $config['threads_preview']);
+ $thread->omitted_images = $omitted['image_count'] - $num_images;
+ }
+
$threads[] = $thread;
$body .= $thread->build(true);
}
@@ -1469,10 +1460,6 @@ function checkMute() {
function buildIndex() {
global $board, $config, $build_pages;
- if ($config['use_read_php']) {
- cache::delete("catalog_{$board['uri']}");
- return;
- }
$pages = getPages();
if (!$config['try_smarter'])
@@ -1550,17 +1537,6 @@ function buildIndex() {
function buildJavascript() {
global $config;
- if ($config['cache']['enabled']) {
- if (false === strpos($config['file_script'], '/')) {
- $cache_name = 'main_js';
- } else {
- $b = explode('/', $config['file_script'])[0];
- $cache_name = "board_{$b}_js";
- }
-
- cache::delete($cache_name);
- }
-
$script = Element('main.js', array(
'config' => $config,
));
@@ -1582,14 +1558,7 @@ function buildJavascript() {
$script = JSMin::minify($script);
}
- if ($config['cache']['enabled'])
- cache::set($cache_name, $script);
-
- if (!$config['use_read_php']) {
- file_write($config['file_script'], $script);
- } else {
- return $script;
- }
+ file_write($config['file_script'], $script);
}
function checkDNSBL() {
@@ -2023,35 +1992,27 @@ function strip_combining_chars($str) {
function buildThread($id, $return = false, $mod = false) {
global $board, $config, $build_pages;
- if (!$return && $config['use_read_php']) {
- cache::delete("thread_index_{$board['uri']}_{$id}");
- cache::delete("thread_50_{$board['uri']}_{$id}");
- cache::delete("thread_index_display_{$board['uri']}_{$id}");
- cache::delete("thread_{$board['uri']}_{$id}");
- cache::delete("catalog_{$board['uri']}");
- return;
- }
-
$id = round($id);
if (event('build-thread', $id))
return;
- if (!($thread = cache::get("thread_{$board['uri']}_{$id}")) || $mod) {
- unset($thread);
- $query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id ORDER BY `thread`,`id`", $board['uri']));
- $query->bindValue(':id', $id, PDO::PARAM_INT);
- $query->execute() or error(db_error($query));
+ if ($config['cache']['enabled'] && !$mod) {
+ // Clear cache
+ cache::delete("thread_index_{$board['uri']}_{$id}");
+ cache::delete("thread_{$board['uri']}_{$id}");
+ }
- while ($post = $query->fetch(PDO::FETCH_ASSOC)) {
- if (!isset($thread)) {
- $thread = new Thread($post, $mod ? '?/' : $config['root'], $mod);
- } else {
- $thread->add(new Post($post, $mod ? '?/' : $config['root'], $mod));
- }
+ $query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id ORDER BY `thread`,`id`", $board['uri']));
+ $query->bindValue(':id', $id, PDO::PARAM_INT);
+ $query->execute() or error(db_error($query));
+
+ while ($post = $query->fetch(PDO::FETCH_ASSOC)) {
+ if (!isset($thread)) {
+ $thread = new Thread($post, $mod ? '?/' : $config['root'], $mod);
+ } else {
+ $thread->add(new Post($post, $mod ? '?/' : $config['root'], $mod));
}
-
- if (isset($thread)) cache::set("thread_{$board['uri']}_{$id}", $thread);
}
// Check if any posts were found
@@ -2079,7 +2040,7 @@ function buildThread($id, $return = false, $mod = false) {
$build_pages[] = thread_find_page($id);
// json api
- if ($config['api']['enabled'] && !$config['use_read_php']) {
+ if ($config['api']['enabled']) {
$api = new Api();
$json = json_encode($api->translateThread($thread));
$jsonFilename = $board['dir'] . $config['dir']['res'] . $id . '.json';
@@ -2105,8 +2066,7 @@ function buildThread50($id, $return = false, $mod = false, $thread = null, $anti
if ($antibot)
$antibot->reset();
- if (!$thread && ($config['cache']['enabled'] && !($thread = cache::get("thread_50_{$board['uri']}_{$id}")))) {
- unset($thread);
+ if (!$thread) {
$query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id ORDER BY `thread`,`id` DESC LIMIT :limit", $board['uri']));
$query->bindValue(':id', $id, PDO::PARAM_INT);
$query->bindValue(':limit', $config['noko50_count']+1, PDO::PARAM_INT);
@@ -2143,8 +2103,6 @@ function buildThread50($id, $return = false, $mod = false, $thread = null, $anti
}
$thread->posts = array_reverse($thread->posts);
- if ($config['cache']['enabled'])
- cache::set("thread_50_{$board['uri']}_{$id}", $thread);
} else {
$allPosts = $thread->posts;
@@ -2471,6 +2429,8 @@ function diceRoller($post) {
}
function less_ip($ip) {
+ global $config;
+
$ipv6 = (strstr($ip, ':') !== false);
$has_range = (strstr($ip, '/') !== false);
@@ -2490,7 +2450,15 @@ function less_ip($ip) {
}
$final = inet_ntop($in_addr & $mask);
- return str_replace(array(':0', '.0'), array(':x', '.x'), $final) . (isset($range) ? '/'.$range : '');
+ $masked = str_replace(array(':0', '.0'), array(':x', '.x'), $final);
+
+ if ($config['hash_masked_ip']) {
+ $masked = substr(sha1(sha1($masked) . $config['secure_trip_salt']), 0, 10);
+ }
+
+ $masked .= (isset($range) ? '/'.$range : '');
+
+ return $masked;
}
function less_hostmask($hostmask) {
diff --git a/inc/instance-config.php b/inc/instance-config.php
index 9c442fef..2c183707 100644
--- a/inc/instance-config.php
+++ b/inc/instance-config.php
@@ -138,6 +138,7 @@
$config['additional_javascript'][] = 'js/comment-toolbar.js';
$config['additional_javascript'][] = 'js/catalog-search.js';
$config['additional_javascript'][] = 'js/thread-stats.js';
+ $config['additional_javascript'][] = 'js/quote-selection.js';
//$config['font_awesome_css'] = '/netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css';
@@ -153,12 +154,13 @@
$config['boards'] = array(array('' => '/', '' => '/boards.html', '' => '/faq.html', '' => '/random.php', '' => '/create.php', '' => '/bans.html', '' => '/search.php', '' => '/mod.php', '' => 'https://qchat.rizon.net/?channels=#8chan'), array('b', 'meta'), array(''=>'https://twitter.com/infinitechan'));
//$config['boards'] = array(array('' => '/', '' => '/boards.html', '' => '/faq.html', '' => '/random.php', '' => '/create.php', '' => '/search.php', '' => '/mod.php', '' => 'https://qchat.rizon.net/?channels=#8chan'), array('b', 'meta', 'int'), array('v', 'a', 'tg', 'fit', 'pol', 'tech', 'mu', 'co', 'sp', 'boards'), array(''=>'https://twitter.com/infinitechan'));
+ $config['footer'][] = 'All posts on 8chan.co are the responsibility of the individual poster and not the administration of 8chan.co, pursuant to 47 U.S.C. § 230.';
+ $config['footer'][] = 'We have not been served any secret court orders and are not under any gag orders.';
$config['footer'][] = 'Contribute to 8chan.co development at github';
- $config['footer'][] = 'To make a DMCA request or report illegal content, please email admin@8chan.co or use the "Global Report" functionality on every page.';
+ $config['footer'][] = 'To make a DMCA request or report illegal content, please email admin@8chan.co.';
$config['search']['enable'] = true;
-//$config['debug'] = true;
$config['syslog'] = true;
$config['wordfilters'][] = array('\rule', ''); // 'true' means it's a regular expression
@@ -192,6 +194,7 @@
);
$config['gzip_static'] = false;
+$config['hash_masked_ip'] = true;
// 8chan specific mod pages
require '8chan-mod-pages.php';
diff --git a/install.sql b/install.sql
index 9e15396a..9a03736f 100644
--- a/install.sql
+++ b/install.sql
@@ -67,6 +67,8 @@ CREATE TABLE IF NOT EXISTS `boards` (
`subtitle` tinytext,
`indexed` boolean default true,
`public_bans` boolean default true,
+ `8archive` boolean default false,
+ `sfw` boolean default false,
PRIMARY KEY (`uri`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
@@ -316,6 +318,19 @@ CREATE TABLE `post_clean` (
UNIQUE KEY `clean_id_UNIQUE` (`clean_id`)
);
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `board_tags`
+--
+
+CREATE TABLE `board_tags` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `uri` varchar(30) DEFAULT NULL,
+ `tag` varchar(255) DEFAULT NULL,
+ PRIMARY KEY (`id`)
+);
+
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
diff --git a/post.php b/post.php
index 50c5c8bd..1a7c5da2 100644
--- a/post.php
+++ b/post.php
@@ -49,7 +49,7 @@ if (isset($_POST['delete'])) {
// Check if deletion enabled
if (!$config['allow_delete'])
- error(_('Post deletion is not allowed!'));
+ error(_('Users are not allowed to delete their own posts on this board.'));
if (empty($delete))
error($config['error']['nodelete']);
diff --git a/read.php b/read.php
deleted file mode 100644
index 64fee794..00000000
--- a/read.php
+++ /dev/null
@@ -1,183 +0,0 @@
- 'view_board',
- '/(\%b)/(\d+)\.json' => 'view_api_index',
- '/(\%b)/catalog\.json' => 'view_api_catalog',
- '/(\%b)/threads\.json' => 'view_api_threads',
- '/(\%b)/main\.js' => 'view_js',
- '/main\.js' => 'view_js',
- '/(\%b)/catalog(\.html)?' => 'view_catalog',
- '/(\%b)/' . preg_quote($config['file_index'], '!') => 'view_board',
- '/(\%b)/' . str_replace('%d', '(\d+)', preg_quote($config['file_page'], '!')) => 'view_board',
- '/(\%b)/' . preg_quote($config['dir']['res'], '!') .
- str_replace('%d', '(\d+)', '%d\+50(\.html)?') => 'view_thread50',
- '/(\%b)/' . preg_quote($config['dir']['res'], '!') .
- str_replace('%d', '(\d+)', '%d(\.html)?') => 'view_thread',
-);
-
-$new_pages = array();
-foreach ($pages as $key => $callback) {
- if (is_string($callback) && preg_match('/^secure /', $callback))
- $key .= '(/(?P[a-f0-9]{8}))?';
- $key = str_replace('\%b', '?P' . sprintf(substr($config['board_path'], 0, -1), $config['board_regex']), $key);
- $new_pages[@$key[0] == '!' ? $key : '!^' . $key . '(?:&[^&=]+=[^&]*)*$!u'] = $callback;
-}
-$pages = $new_pages;
-
-function view_thread50($boardName, $thread) {
- global $config, $mod;
-
- if (!openBoard($boardName)) {
- include '404.php';
- return;
- }
-
- $page = buildThread50($thread, true, false);
- echo $page;
-}
-
-function view_thread($boardName, $thread) {
- global $config, $mod;
-
- if (!openBoard($boardName)) {
- include '404.php';
- return;
- }
-
- $page = buildThread($thread, true, false);
- echo $page;
-}
-
-function view_api_index($boardName, $page) {
- global $config, $board;
-
- $api = new Api();
-
- if (!openBoard($boardName)) {
- include '404.php';
- return;
- }
-
- $content = index($page+1);
-
- if (!$content)
- return;
-
- if ($config['api']['enabled']) {
- $threads = $content['threads'];
- $json = json_encode($api->translatePage($threads));
- header('Content-Type: text/json');
-
- echo $json;
- }
-}
-
-function APICatalog($boardName, $gen_threads = false) {
- global $config;
- if (!openBoard($boardName)) {
- include '404.php';
- return;
- }
-
- header('Content-Type: text/json');
-
- $catalog = array();
- $api = new Api();
-
- for ($page = 1; $page <= $config['max_pages']; $page++) {
- $content = index($page);
-
- if (!$content)
- break;
-
- $catalog[$page-1] = $content['threads'];
- }
-
- echo json_encode($api->translateCatalog($catalog, $gen_threads));
-}
-
-function view_api_catalog($boardName) {
- APICatalog($boardName, false);
-}
-
-function view_api_threads($boardName) {
- APICatalog($boardName, true);
-}
-
-function view_board($boardName, $page_no = 1) {
- global $config, $mod;
-
- if (!openBoard($boardName)) {
- include '404.php';
- return;
- }
-
- if (!$page = index($page_no, $mod)) {
- error($config['error']['404']);
- }
-
- $page['pages'] = getPages(false);
- $page['pages'][$page_no-1]['selected'] = true;
- $page['btn'] = getPageButtons($page['pages'], false);
- $page['mod'] = false;
- $page['config'] = $config;
-
- echo Element('index.html', $page);
-}
-
-function view_js($boardName = false) {
- global $config;
-
- if ($boardName && !openBoard($boardName))
- error($config['error']['noboard']);
-
- if (!$boardName) {
- $cache_name = 'main_js';
- } else {
- $cache_name = "board_{$boardName}_js";
- }
-
- if (!($script = cache::get($cache_name))) {
- $script = buildJavascript();
- }
-
- echo $script;
-}
-
-function view_catalog($boardName) {
- global $board, $config;
- $_theme = 'catalog';
-
- $theme = loadThemeConfig($_theme);
-
- if (!openBoard($boardName)) {
- include '404.php';
- return;
- }
-
- require_once $config['dir']['themes'] . '/' . $_theme . '/theme.php';
-
- $catalog = $theme['build_function']('read_php', themeSettings($_theme), $board['uri']);
- echo $catalog;
-}
-
-$found = false;
-foreach ($pages as $uri => $handler) {
- if (preg_match($uri, $query, $matches)) {
- $matches = array_slice($matches, 2);
- if (is_callable($handler)) {
- $found = true;
- call_user_func_array($handler, $matches);
- }
- }
-}
-
-if (!$found)
- include '404.php';
diff --git a/stylesheets/style.css b/stylesheets/style.css
index bebd4813..4a038483 100644
--- a/stylesheets/style.css
+++ b/stylesheets/style.css
@@ -819,7 +819,7 @@ pre {
margin-top: 1em;
}
-.new-threads {
+.new-threads, .board-settings {
text-align: center;
}
diff --git a/templates/8chan/boards-tags.html b/templates/8chan/boards-tags.html
new file mode 100644
index 00000000..7ac4bcc3
--- /dev/null
+++ b/templates/8chan/boards-tags.html
@@ -0,0 +1,157 @@
+
+
+{% trans %}There are currently {{n_boards}} boards + {{hidden_boards_total}} unindexed boards = {{t_boards}} total boards. Site-wide, {{total_posts_hour}} posts have been made in the last hour, with {{total_posts}} being made on all active boards since October 23, 2013.{% endtrans %}
+
+
+
+
+
+ B |
+ {% trans %}Board{% endtrans %} |
+ {% trans %}Title{% endtrans %} |
+ {% trans %}PPH{% endtrans %} |
+ {% trans %}Total posts{% endtrans %} |
+ {% trans %}Active users{% endtrans %} |
+ {% trans %}Tags{% endtrans %} |
+
+{% for board in boards %}
+
+ {{ board.img|raw }} {% if board['sfw'] %} {% else %} {% endif %} |
+ |
+ {{ board['title'] }} |
+ {{board['pph']}} |
+ {{board['max']}} |
+ {{board['uniq_ip']}} |
+ {% for tag in board.tags %}{{ tag }} {% endfor %} |
+{% endfor %}
+
+Page last updated: {{last_update}}
+{{uptime_p}} without interruption (read)
+
+
diff --git a/templates/8chan/create.html b/templates/8chan/create.html
index f99fa727..91ed9262 100644
--- a/templates/8chan/create.html
+++ b/templates/8chan/create.html
@@ -1,3 +1,4 @@
+Did you know? Many boards with popular names have been abandoned by their owners and can be claimed. See here for a full list.