Compare commits

..

5 Commits

15 changed files with 92 additions and 219 deletions

View File

@ -22,8 +22,7 @@
"inc/queue.php",
"inc/polyfill.php",
"inc/error.php",
"inc/functions.php",
"inc/functions/net.php"
"inc/functions.php"
]
},
"license": "Tinyboard + vichan",

View File

@ -311,13 +311,16 @@ class Bans {
$query->execute() or error(db_error($query));
$ban_len = $length > 0 ? preg_replace('/^(\d+) (\w+?)s?$/', '$1-$2', until($length)) : 'permanent';
$ban_board = $ban_board ? "/$ban_board/" : 'all boards';
$ban_ip = filter_var($mask, FILTER_VALIDATE_IP) !== false ? "<a href=\"?/IP/$mask\">$mask</a>" : $mask;
$ban_id = $pdo->lastInsertId();
$ban_reason = $reason ? 'reason: ' . utf8tohtml($reason) : 'no reason';
modLog("Created a new $ban_len ban on $ban_board for $ban_ip (<small># $ban_id </small>) with $ban_reason");
if (isset($mod['id']) && $mod['id'] == $mod_id) {
modLog('Created a new ' .
($length > 0 ? preg_replace('/^(\d+) (\w+?)s?$/', '$1-$2', until($length)) : 'permanent') .
' ban on ' .
($ban_board ? '/' . $ban_board . '/' : 'all boards') .
' for ' .
(filter_var($mask, FILTER_VALIDATE_IP) !== false ? "<a href=\"?/IP/$mask\">$mask</a>" : $mask) .
' (<small>#' . $pdo->lastInsertId() . '</small>)' .
' with ' . ($reason ? 'reason: ' . utf8tohtml($reason) . '' : 'no reason'));
}
rebuildThemes('bans');

View File

@ -172,7 +172,7 @@
// How long should the cookies last (in seconds). Defines how long should moderators should remain logged
// in (0 = browser session).
$config['cookies']['expire'] = 60 * 60 * 24 * 7; // 1 week.
$config['cookies']['expire'] = 60 * 60 * 24 * 30 * 6; // ~6 months
// Make this something long and random for security.
$config['cookies']['salt'] = 'abcdefghijklmnopqrstuvwxyz09123456789!@#$%^&*()';
@ -180,10 +180,6 @@
// Whether or not you can access the mod cookie in JavaScript. Most users should not need to change this.
$config['cookies']['httponly'] = true;
// Do not allow logins via unencrypted HTTP. Should only be changed in testing environments or if you connect to a
// load-balancer without encryption.
$config['cookies']['secure_login_only'] = true;
// Used to salt secure tripcodes ("##trip") and poster IDs (if enabled).
$config['secure_trip_salt'] = ')(*&^%$#@!98765432190zyxwvutsrqponmlkjihgfedcba';
@ -1220,7 +1216,6 @@
// Moderator errors
$config['error']['toomanyunban'] = _('You are only allowed to unban %s users at a time. You tried to unban %u users.');
$config['error']['invalid'] = _('Invalid username and/or password.');
$config['error']['insecure'] = _('Login on insecure connections is disabled.');
$config['error']['notamod'] = _('You are not a mod…');
$config['error']['invalidafter'] = _('Invalid username and/or password. Your user may have been deleted or changed.');
$config['error']['malformed'] = _('Invalid/malformed cookies.');

View File

@ -1,17 +0,0 @@
<?php
namespace Vichan\Functions\Net;
/**
* @return bool Returns if the client-server connection is an HTTPS one.
*/
function is_connection_https(): bool {
return !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off';
}
/**
* @return bool Returns if the client-server connection is an encrypted one (HTTPS or Tor loopback).
*/
function is_connection_secure(): bool {
return is_connection_https() || (!empty($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] === '127.0.0.1');
}

View File

@ -4,21 +4,19 @@
* Copyright (c) 2010-2013 Tinyboard Development Group
*/
use Vichan\Functions\Net;
defined('TINYBOARD') or exit;
// create a hash/salt pair for validate logins
function mkhash($username, $password, $salt = false) {
global $config;
if (!$salt) {
// create some sort of salt for the hash
$salt = substr(base64_encode(sha1(rand() . time(), true) . $config['cookies']['salt']), 0, 15);
$generated_salt = true;
}
// generate hash (method is not important as long as it's strong)
$hash = substr(
base64_encode(
@ -32,13 +30,19 @@ function mkhash($username, $password, $salt = false) {
)
), 0, 20
);
if (isset($generated_salt))
return array($hash, $salt);
else
return $hash;
}
function crypt_password_old($password) {
$salt = generate_salt();
$password = hash('sha256', $salt . sha1($password));
return array($salt, $password);
}
function crypt_password($password) {
global $config;
// `salt` database field is reused as a version value. We don't want it to be 0.
@ -65,16 +69,22 @@ function test_password($password, $salt, $test) {
}
function generate_salt() {
// mcrypt_create_iv() was deprecated in PHP 7.1.0, only use it if we're below that version number.
if (PHP_VERSION_ID < 70100) {
// 128 bits of entropy
return strtr(base64_encode(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM)), '+', '.');
}
// Otherwise, use random_bytes()
return strtr(base64_encode(random_bytes(16)), '+', '.');
}
function login($username, $password) {
global $mod, $config;
$query = prepare("SELECT `id`, `type`, `boards`, `password`, `version` FROM ``mods`` WHERE BINARY `username` = :username");
$query->bindValue(':username', $username);
$query->execute() or error(db_error($query));
if ($user = $query->fetch(PDO::FETCH_ASSOC)) {
list($version, $ok) = test_password($user['password'], $user['version'], $password);
@ -98,7 +108,7 @@ function login($username, $password) {
);
}
}
return false;
}
@ -106,23 +116,20 @@ function setCookies() {
global $mod, $config;
if (!$mod)
error('setCookies() was called for a non-moderator!');
$is_https = Net\is_connection_https();
setcookie($config['cookies']['mod'],
$mod['username'] . // username
':' .
':' .
$mod['hash'][0] . // password
':' .
$mod['hash'][1], // salt
time() + $config['cookies']['expire'], $config['cookies']['jail'] ? $config['cookies']['path'] : '/', null, $is_https, $config['cookies']['httponly']);
time() + $config['cookies']['expire'], $config['cookies']['jail'] ? $config['cookies']['path'] : '/', null, !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off', $config['cookies']['httponly']);
}
function destroyCookies() {
global $config;
$is_https = Net\is_connection_https();
// Delete the cookies
setcookie($config['cookies']['mod'], 'deleted', time() - $config['cookies']['expire'], $config['cookies']['jail']?$config['cookies']['path'] : '/', null, $is_https, true);
setcookie($config['cookies']['mod'], 'deleted', time() - $config['cookies']['expire'], $config['cookies']['jail']?$config['cookies']['path'] : '/', null, !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off', true);
}
function modLog($action, $_board=null) {
@ -139,36 +146,36 @@ function modLog($action, $_board=null) {
else
$query->bindValue(':board', null, PDO::PARAM_NULL);
$query->execute() or error(db_error($query));
if ($config['syslog'])
_syslog(LOG_INFO, '[mod/' . $mod['username'] . ']: ' . $action);
}
function create_pm_header() {
global $mod, $config;
if ($config['cache']['enabled'] && ($header = cache::get('pm_unread_' . $mod['id'])) != false) {
if ($header === true)
return false;
return $header;
}
$query = prepare("SELECT `id` FROM ``pms`` WHERE `to` = :id AND `unread` = 1");
$query->bindValue(':id', $mod['id'], PDO::PARAM_INT);
$query->execute() or error(db_error($query));
if ($pm = $query->fetch(PDO::FETCH_ASSOC))
$header = array('id' => $pm['id'], 'waiting' => $query->rowCount() - 1);
else
$header = true;
if ($config['cache']['enabled'])
cache::set('pm_unread_' . $mod['id'], $header);
if ($header === true)
return false;
return $header;
}
@ -179,7 +186,6 @@ function make_secure_link_token($uri) {
function check_login($prompt = false) {
global $config, $mod;
// Validate session
if (isset($_COOKIE[$config['cookies']['mod']])) {
// Should be username:hash:salt
@ -190,12 +196,12 @@ function check_login($prompt = false) {
if ($prompt) mod_login();
exit;
}
$query = prepare("SELECT `id`, `type`, `boards`, `password` FROM ``mods`` WHERE `username` = :username");
$query->bindValue(':username', $cookie[0]);
$query->execute() or error(db_error($query));
$user = $query->fetch(PDO::FETCH_ASSOC);
// validate password hash
if ($cookie[1] !== mkhash($cookie[0], $user['password'], $cookie[2])) {
// Malformed cookies
@ -203,7 +209,7 @@ function check_login($prompt = false) {
if ($prompt) mod_login();
exit;
}
$mod = array(
'id' => (int)$user['id'],
'type' => (int)$user['type'],

View File

@ -4,11 +4,8 @@
* Copyright (c) 2010-2013 Tinyboard Development Group
*/
use Vichan\Functions\Net;
defined('TINYBOARD') or exit;
function mod_page($title, $template, $args, $subtitle = false) {
global $config, $mod;
@ -38,11 +35,9 @@ function clone_wrapped_with_exist_check($clonefn, $src, $dest) {
function mod_login($redirect = false) {
global $config;
$args = [];
$args = array();
if ($config['cookies']['secure_login_only'] && !Net\is_connection_secure()) {
$args['error'] = $config['error']['insecure'];
} elseif (isset($_POST['login'])) {
if (isset($_POST['login'])) {
// Check if inputs are set and not empty
if (!isset($_POST['username'], $_POST['password']) || $_POST['username'] == '' || $_POST['password'] == '') {
$args['error'] = $config['error']['invalid'];
@ -1345,8 +1340,8 @@ function mod_move($originBoard, $postID) {
if ($targetBoard === $originBoard)
error(_('Target and source board are the same.'));
// link() if leaving a shadow thread behind; else, rename().
$clone = $shadow ? 'link' : 'rename';
// copy() if leaving a shadow thread behind; else, rename().
$clone = $shadow ? 'copy' : 'rename';
// indicate that the post is a thread
$post['op'] = true;
@ -1639,7 +1634,7 @@ function mod_merge($originBoard, $postID) {
$op = $post;
$op['id'] = $newID;
$clone = $shadow ? 'link' : 'rename';
$clone = $shadow ? 'copy' : 'rename';
if ($post['has_file']) {
// copy image

View File

@ -7,6 +7,10 @@
*
* Usage:
* $config['additional_javascript'][] = 'js/jquery.min.js';
* $config['additional_javascript'][] = 'js/style-select.js';
* $config['additional_javascript'][] = 'js/options.js';
* $config['additional_javascript'][] = 'js/options/user-js.js';
* $config['additional_javascript'][] = 'js/options/general.js';
* $config['additional_javascript'][] = 'js/hud-pinning.js';
*/
@ -21,7 +25,7 @@ $(document).ready(function() {
if (window.Options && Options.get_tab('general') && window.jQuery) {
function setHudPinning(pin) {
let style = pin ? '' : 'initial';
let style = pin ? '' : 'absolute';
$('#top-hud').css('position', style);
$('#bottom-hud').css('position', style);
}
@ -42,7 +46,7 @@ $(document).ready(function() {
// Reload on new post: allows it to work with auto-reload.js etc.
$(document).on('new_post', function(e, post) {
setHudPinning(localStorage.hud_pinning !== 'false');
setHudPinning(localStorage.hud_pinning === 'true');
});
// Enforce the setting on loading.

View File

@ -1205,14 +1205,6 @@ span.pln {
min-width: 48px;
}
/* The center tag has been deprecated in HTML 5 */
.center {
display: block;
margin-left: auto;
margin-right: auto;
text-align: center;
}
/* Text and accessibility */
.ltr {
direction: ltr;
@ -1988,3 +1980,4 @@ span.orangeQuote {
float: right;
margin: 0em 1em;
}

View File

@ -27,5 +27,5 @@ CREATE TABLE IF NOT EXISTS ``posts_{{ board }}`` (
KEY `time` (`time`),
KEY `ip` (`ip`),
KEY `list_threads` (`thread`, `sticky`, `bump`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ;
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ;

View File

@ -1,32 +0,0 @@
{% filter remove_whitespace %}
<!doctype html>
<html>
<head>
<meta charset="utf-8">
{% if config.meta_keywords %}<meta name="keywords" content="{{ config.meta_keywords }}">{% endif %}
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes">
<title>{{ theme_config.title }}</title>
{% include 'header.html' %}
</head>
<body class="8chan vichan {% if mod %}is-moderator{% else %}is-not-moderator{% endif %}" data-stylesheet="{% if config.default_stylesheet.1 != '' %}{{ config.default_stylesheet.1 }}{% else %}default{% endif %}">
<div id="top-hud" class="bar top">
{{ board_list.top }}
</div>
<header>
<h1 data-text="{{ theme_config.title }}"> {{ theme_config.title }}</h1>
</header>
<img class="center" img alt="logo" src="{{ config.logo }}" width=15%/>
<div class="center ban">{{ theme_config.description }}</div>
<hr/>
<footer>
<p class="unimportant" style="margin-top:20px;text-align:center;">- <a href="https://github.com/savetheinternet/Tinyboard">Tinyboard</a> +
<a href='https://github.com/vichan-devel/vichan'>vichan</a> +
<br><a href="https://github.com/savetheinternet/Tinyboard">Tinyboard</a> Copyright &copy; 2010-2014 Tinyboard Development Group
<br><a href="https://github.com/vichan-devel/vichan">vichan</a> Copyright &copy; 2012-2016 vichan-devel
</footer>
<div id="bottom-hud" class="bar bottom">
<div class="pages"></div>
</div>
</body>
</html>
{% endfilter %}

View File

@ -1,31 +0,0 @@
<?php
$theme = [
// Theme name.
'name' => 'About',
// Description (you can use Tinyboard markup here).
'description' => 'Extremely basic about page. Enabling board links is recommended for this theme.',
'version' => 'v0.0.1',
// Theme configuration.
'config' => [
[
'title' => 'Site title',
'name' => 'title',
'type' => 'text',
'default' => 'Title goes here'
],
[
'title' => 'Text',
'name' => 'description',
'type' => 'text',
'default' => 'Text goes here'
],
[
'title' => 'File path',
'name' => 'path',
'type' => 'text',
'default' => 'about.html',
'comment' => '(eg. "about.html")'
]
],
'build_function' => 'about_build'
];

View File

@ -1,37 +0,0 @@
<?php
require_once 'info.php';
function about_build(string $action, array $theme_config, $board): void {
$obj = new AboutTheme($theme_config);
$obj->build($action);
}
class AboutTheme {
private $theme_config;
private function render(): string {
global $config;
return Element('themes/about/about.html', [
'theme_config' => $this->theme_config,
'config' => $config,
'board_list' => createBoardlist(),
]);
}
public function __construct(array $theme_config) {
$this->theme_config = $theme_config;
}
public function build(string $action): void {
global $config;
if ($action == 'all') {
$about_page = $this->render();
$home = $config['dir']['home'];
$path = $this->theme_config['path'];
file_write($home . $path, $about_page);
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

View File

@ -13,7 +13,7 @@
{% include 'header.html' %}
</head>
<body class="8chan vichan {% if mod %}is-moderator{% else %}is-not-moderator{% endif %} theme-catalog active-catalog" data-stylesheet="{% if config.default_stylesheet.1 != '' %}{{ config.default_stylesheet.1 }}{% else %}default{% endif %}">
<div id="top-hud" class="top bar topbar">
<div class="top bar topbar">
{{ boardlist.top }}
</div>
<header>
@ -105,7 +105,6 @@
<br><a href="https://github.com/vichan-devel/vichan">vichan</a> Copyright &copy; 2012-2016 vichan-devel
<br><a href="https://github.com/lainchan/lainchan">lainchan</a> Copyright &copy; 2014-2017 lainchan Administration</p>
</footer>
<div id="bottom-hud" class="bar bottom">
<div class="pages"></div>
</div>

View File

@ -14,7 +14,7 @@
{% include 'header.html' %}
</head>
<body class="8chan vichan {% if mod %}is-moderator{% else %}is-not-moderator{% endif %}" data-stylesheet="{% if config.default_stylesheet.1 != '' %}{{ config.default_stylesheet.1 }}{% else %}default{% endif %}">
<div id="top-hud" class="bar top">
<div class="bar top">
{{ boardlist.top }}
</div>
<header>
@ -24,48 +24,48 @@
<center><img alt="logo" src="{{ config.logo }}" width=15%/></center>
<div class="ban">
<h2 id="1">
My question isn't here. What do I do ?
My question isn't here. What do I do ?
</h2>
<p>Make a post in the <a href="/meta/">/meta/</a> board or ask via our <a href="https://matrix.to/#/!nTwQxlnWchxZsgDAgE:matrix.org?via=matrix.org">Matrix Congress chat</a> </p>
<h2 id="2">
What are the rules ?
What are the rules ?
</h2>
<p>Please click <a href="/rules.html">here</a> </p>
<h2 id="3">
I'm new here and <span class="strikethrough">learned politics from memes</span> would like to learn about leftism! Where should I start ?
I'm new here and <span class="strikethrough">learned politics from memes</span> would like to learn about leftism! Where should I start ?
</h2>
<p>There are some beginner lists in <a href="/leftypol/res/285223.html">the reading thread</a>. Meanwhile, consider asking your questions in <a href="/edu/">/edu/</a> or in a relevant /leftypol/ thread, such as <a href="https://leftypol.org/search.php?search=qtddtot&board=leftypol">QTDDTOT</a>.</p>
<h2 id="4">
What is the purpose of leftypol.org ?
What is the purpose of leftypol.org ?
</h2>
<p>The mission of /leftypol/ at leftypol.org is to provide a fun and enjoyable space for the working
masses around the world, as an anonymous community of non-sectarian leftists united in
common cause against the forces of capitalism, fascism, and liberalism. Our goal is to act as a
centre for serious political discourse and less serious informal discussions on various topics
<p>The mission of /leftypol/ at leftypol.org is to provide a fun and enjoyable space for the working
masses around the world, as an anonymous community of non-sectarian leftists united in
common cause against the forces of capitalism, fascism, and liberalism. Our goal is to act as a
centre for serious political discourse and less serious informal discussions on various topics
related to leftist thought.</p>
<p>We aim to be the negation of /pol/; anti-fascist, materialist, better read. Our mission, above all, is
to learn, and help others learn, the philosophical tools necessary to elucidate the interrelated and
<p>We aim to be the negation of /pol/; anti-fascist, materialist, better read. Our mission, above all, is
to learn, and help others learn, the philosophical tools necessary to elucidate the interrelated and
ever-increasingly complex space of self, society, and politics, from the local to the global scale.</p>
<p>An important part of /leftypol/s unique character is our opposition to identity politics. We
believe that conflicts between genders, races, sexualities, and so on are distractions from the
wider class struggle that are intentionally fanned by the ruling class. This does not mean that
oppression based on personal characteristics does not exist, but that it should be approached
from an egalitarian perspective which is not personally accusatory and does not assign victim
and oppressor groups. The rights of individuals to live according to their own wishes is a
<p>An important part of /leftypol/s unique character is our opposition to identity politics. We
believe that conflicts between genders, races, sexualities, and so on are distractions from the
wider class struggle that are intentionally fanned by the ruling class. This does not mean that
oppression based on personal characteristics does not exist, but that it should be approached
from an egalitarian perspective which is not personally accusatory and does not assign victim
and oppressor groups. The rights of individuals to live according to their own wishes is a
fundamental part of leftism.</p>
<p>More broadly, leftism is defined as; “A broad set of ideological
forces which are dedicated to, by one form or another, dismantling the current material way of
things in capitalism and building a new egalitarian system to replace it.” If a point of view or
ideology claims to represent socialism or communism, yet is so divisive and anti-egalitarian that
<p>More broadly, leftism is defined as; “A broad set of ideological
forces which are dedicated to, by one form or another, dismantling the current material way of
things in capitalism and building a new egalitarian system to replace it.” If a point of view or
ideology claims to represent socialism or communism, yet is so divisive and anti-egalitarian that
it fails to offer a reasonable alternative to the current order, it may be considered as non-leftist.</p>
<p>Further information can be found in our <a href="/static/manifesto.pdf">/leftypol/ Manifesto</a>
<h2 id="5">
I don't like the colors. Where can I change my options ?
I don't like the colors. Where can I change my options ?
</h2>
<p>If you are using a normal browser, there should be an [Options] button in the top right corner when viewing a board. The bottom right of the popup is for choosing themes. <span class="spoiler">Try Jungle.</span></p>
<h2 id="6">
How does post formatting work ?
How does post formatting work ?
</h2>
<p> It is standard vichan post formatting. </p>
<p> &gt;text makes it a greentext quote: <span class="quote">&gt;text</span></p>
@ -78,19 +78,19 @@
<p> **text** makes it spoiler: <span class="spoiler">text</span></p>
<p> [code]text[/code] makes it code: <pre class="code lang-">text</pre></p>
<h2 id="7">
Why are my posts being changed / word-filtered ?
Why are my posts being changed / word-filtered ?
</h2>
<p> leftypol has a language enhancer. It's mostly just there to mess with bad-faith posters. /pol/ users often get surprisingly triggered when you don't let them say their favorite slurs...</p>
<h2 id="8">
I don't like the mobile site. Do you have a mobile app ?
I don't like the mobile site. Do you have a mobile app ?
</h2>
<p> We do. There's a thread <a href="/tech/res/4951.html">for it here</a>.</p>
<h2 id="9">
How can I suggest or submit fixes to the site ?
How can I suggest or submit fixes to the site ?
</h2>
<p>There is a /meta/ thread for this, and our <a href="https://git.leftypol.org/leftypol/leftypol">Gitea repo</a>.</p>
<h2 id="10">
I don't trust Tor exit nodes. Do you have an .onion site ?
I don't trust Tor exit nodes. Do you have an .onion site ?
</h2>
<p>Yes, <a href="http://76i2c3hn55fcj5nut3tqlboqqbbh23zvphv7lefk3vftpx6wketlanyd.onion/">here it is</a>.</p>
<h2 id="11">
@ -130,20 +130,16 @@
</h2>
<p>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. </p>
</div>
<hr/>
<footer>
<p class="unimportant" style="margin-top:20px;text-align:center;">- Tinyboard +
<p class="unimportant" style="margin-top:20px;text-align:center;">- Tinyboard +
vichan {{ config.version }} -
<br>Tinyboard Copyright &copy; 2010-2014 Tinyboard Development Group
<br>Tinyboard Copyright &copy; 2010-2014 Tinyboard Development Group
<br>vichan Copyright &copy; 2012-2016 vichan-devel</p>
</footer>
<div id="bottom-hud" class="bar bottom">
<div class="pages"></div>
</div>
<div class="pages"></div>
<script type="text/javascript">{% raw %}
ready();
{% endraw %}</script>