1
0
mirror of https://github.com/vichan-devel/vichan.git synced 2025-01-24 15:12:18 +01:00

Merge pull request #555 from OpenIB/surface-area

Lets lower the surface area
This commit is contained in:
Ron 2017-04-09 16:13:36 +09:00 committed by GitHub
commit fd35e5e4a3
10 changed files with 18 additions and 866 deletions

62
404.php
View File

@ -1,62 +0,0 @@
<?php
require_once "inc/functions.php";
header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found");
global $config;
$dir = "static/404/";
if (!is_dir($dir))
mkdir($dir);
if ($config['cache']['enabled']) {
$files = cache::get('notfound_files');
}
if (!isset($files) or !$files) {
$files = array_diff(scandir($dir), array('..', '.'));
if ($config['cache']['enabled']) {
cache::set('notfound_files', $files);
}
}
if (count($files) == 0) {
$errorimage = false;
} else {
$errorimage = $files[array_rand($files)];
}
if (preg_match('!'.$config['board_regex'].'/'.$config['dir']['res'].'\d+\.html!u', $_SERVER['REQUEST_URI'])) {
$return_link = '<a href="../index.html">[ Return ]</a>';
} else {
$return_link = '';
}
$ad = Element("ad_top.html", array());
$page = <<<EOT
<div style="text-align:center">$ad</div>
<div class="ban">
<p style="text-align:center"><img src="/static/404/{$errorimage}" style="width:100%"></p>
<p style="text-align:center"><a href="/index.html">[ Home ]</a>{$return_link}</p>
</div>
<script type="text/javascript">
if (localStorage.favorites) {
var faves = JSON.parse(localStorage.favorites);
$.each(faves, function(k, v) {
if ((window.location.pathname === '/' + v + '/') || (window.location.pathname === '/' + v)) {
faves.splice(k, 1);
localStorage.favorites = JSON.stringify(faves);
alert('As /' + v + '/ no longer exists, it has been removed from your favorites.');
}
})
}
</script>
EOT;
echo Element("page.html", array("config" => $config, "body" => $errorimage ? $page : "", "title" => "404 Not Found"));

View File

@ -1,51 +1,42 @@
infinity OpenIB
======================================================== ========================================================
Notice
------------
As of April 9, 2017, Infinity is longer be maintained. Development of this project will continue on a security-focused fork located here: https://github.com/OpenIB/OpenIB
A message about infinity GitHub repo
------------
Given that Fredrick Brennan is no longer sole administrator of 8chan, maintenance of this repository has passed to N.T. Technology, Inc (NTTEC). NTTEC has decided to split the repo into two branches:
* `master`: The historical infinity repository as it was in November of 2015.
* `public-site`: A repository containing some patches over master of changes that have been made to infinity since then by either Mr. Brennan or other developers on behalf of NTTEC. Going forward all activity will be on this branch.
About About
------------ ------------
infinity is a fork of vichan, with the difference that infinity is geared towards allowing users to create their own boards. A running instance is at [8ch.net](https://8ch.net/) (new! a user of the software wrote to me that they created a Polish version: [8ch.pl](http://8ch.pl/)) OpenIB is a fork of Infinity which is a fork of vichan. OpenIB will be a security fork focused on user security. Infinity offered us board creation ontop of vichan. Now OpenIB will be refactoring Infinity and making the imageboard ecosystem safer for users. A running instance is at [8ch.net](https://8ch.net/)
Most things (other than installation) that apply to upstream vichan also apply to infinity. See their readme for a detailed FAQ: https://github.com/vichan-devel/vichan/blob/master/README.md Due to being a recent fork, we have not yet deviated much from Infinity.
If you are not interested in letting your users make their own boards, install vichan instead of infinity. As of now, most things (other than installation) that apply to upstream vichan also apply to OpenIB. See their readme for a detailed FAQ: https://github.com/vichan-devel/vichan/blob/master/README.md
**Much like Arch Linux, infinity should be considered ``rolling release''. Unlike upstream vichan, we have no install.php. Database schema and templates are changed often and it is on you to read the Git log before updating!** If you are not interested in letting your users make their own boards, install vichan instead of OpenIB.
**Much like Arch Linux, OpenIB should be considered ``rolling release''. Unlike upstream vichan, we have no install.php. Database schema and templates are changed often and it is on you to read the Git log before updating!**
Installation Installation
------------ ------------
Basic requirements: Basic requirements:
A computer running a Unix or Unix-like OS(infinity has been specifically tested with and is known to work under Ubuntu 14.x), Apache, MySQL, and PHP A computer running a Unix or Unix-like OS (OpenIB has been specifically tested with and is known to work under FreeBSD 10.3), Apache, MySQL, and PHP
* Make sure Apache has read/write access to the directory infinity resides in. * Make sure Apache has read/write access to the directory OpenIB resides in.
* `install.php` is not maintained. Don't use it. * `install.php` is not maintained. Don't use it.
* As of February 22, 2015, you need the [DirectIO module (dio.so)](http://php.net/manual/en/ref.dio.php). This is for compatibility with NFS. * As of February 22, 2015, you need the [DirectIO module (dio.so)](http://php.net/manual/en/ref.dio.php). This is for compatibility with NFS.
Step 1. Create infinity's database from the included install.sql file. Enter mysql and create an empty database named 'infinity'. Then cd into the infinity base directory and run: Step 1. Create OpenIB's database from the included install.sql file. Enter mysql and create an empty database named 'openib'. Then cd into the openib base directory and run:
``` ```
mysql -uroot -p infinity < install.sql mysql -uroot -p openib < install.sql
echo '+ <a href="https://github.com/ctrlcctrlv/infinity">infinity</a> '`git rev-parse HEAD|head -c 10` > .installed echo '+ <a href="https://github.com/OpenIB/OpenIB">OpenIB</a> '`git rev-parse HEAD|head -c 10` > .installed
``` ```
Step 2. /inc/secrets.php does not exist by default, but infinity needs it in order to function. To fix this, cd into /inc/ and run: Step 2. /inc/secrets.php does not exist by default, but OpenIB needs it in order to function. To fix this, cd into /inc/ and run:
``` ```
sudo cp secrets.example.php secrets.php sudo cp secrets.example.php secrets.php
``` ```
Now open secrets.php and edit the $config['db'] settings to point to the 'infinity' MySQL database you created in Step 1. 'user' and 'password' refer to your MySQL login credentials. It should look something like this when you're finished: Now open secrets.php and edit the $config['db'] settings to point to the 'openib' MySQL database you created in Step 1. 'user' and 'password' refer to your MySQL login credentials. It should look something like this when you're finished:
``` ```
$config['db']['server'] = 'localhost'; $config['db']['server'] = 'localhost';
$config['db']['database'] = 'infinity'; $config['db']['database'] = 'openib';
$config['db']['prefix'] = ''; $config['db']['prefix'] = '';
$config['db']['user'] = 'root'; $config['db']['user'] = 'root';
$config['db']['password'] = 'password'; $config['db']['password'] = 'password';
@ -53,12 +44,12 @@ Now open secrets.php and edit the $config['db'] settings to point to the 'infini
$config['cache']['enabled'] = 'apc'; $config['cache']['enabled'] = 'apc';
``` ```
Step 3.(Optional) By default, infinity will ignore any changes you make to the template files until you log into mod.php, go to Rebuild, and select Flush Cache. You may find this inconvenient. To make infinity automatically accept your changes to the template files, set $config['twig_cache']. Step 3.(Optional) By default, OpenIB will ignore any changes you make to the template files until you log into mod.php, go to Rebuild, and select Flush Cache. You may find this inconvenient. To make OpenIB automatically accept your changes to the template files, set $config['twig_cache'].
Step 4. Infinity can function in a *very* barebones fashion after the first two steps, but you should probably install these additional packages if you want to seriously run it and/or contribute to it. ffmpeg may fail to install under certain versions of Ubuntu. If it does, remove it from this script and install it via an alternate method. Make sure to run the below as root: Step 4. OpenIB can function in a *very* barebones fashion after the first two steps, but you should probably install these additional packages if you want to seriously run it and/or contribute to it. Make sure to run the below as root:
``` ```
apt-get install graphicsmagick gifsicle php5-fpm mysql-client php5-mysql php5-cli php-pear php5-apcu php5-dev; add-apt-repository ppa:jon-severinsson/ffmpeg; add-apt-repository ppa:nginx/stable; apt-get update; apt-get install nginx ffmpeg; pear install Net_DNS2; pecl install "channel://pecl.php.net/dio-0.0.7" pkg add graphicxmagick gifsicle nginx mysql56-server php56 php56-mysql ffmpeg pear
``` ```
Page Generation Page Generation

View File

@ -1,123 +0,0 @@
<?php
/*
include "inc/functions.php";
include "inc/mod/auth.php";
function last_activity($board) {
// last post
$query = prepare(sprintf("SELECT MAX(time) AS time FROM posts_%s", $board));
$query->execute();
$row = $query->fetch();
$ago = (new DateTime)->sub(new DateInterval('P1W'));
$mod_ago = (new DateTime)->sub(new DateInterval('P2W'));
$last_activity_date = new DateTime();
$last_mod_date = new DateTime();
$last_activity_date->setTimestamp($row['time']);
$query = query("SELECT id, username FROM mods WHERE boards = '$board'");
$mods = $query->fetchAll();
if ($mods) {
$mod = $mods[0]['id'];
$query = query("SELECT MAX(time) AS time FROM modlogs WHERE `mod` = $mod");
$a = $query->fetchAll(PDO::FETCH_COLUMN);
if ($a[0]) {
$last_mod_date->setTimestamp($a[0]);
if (!$row['time'])
$last_activity_date->setTimestamp($a[0]);
} else {// no one ever logged in, try board creation time
$query = query("SELECT UNIX_TIMESTAMP(time) AS time FROM board_create WHERE uri = '$board'");
$crt = $query->fetchAll(PDO::FETCH_COLUMN);
$last_activity_date->setTimestamp($crt[0]);
$last_mod_date = false;
}
}
if ($mods and ($last_activity_date < $ago or ($last_mod_date and $last_mod_date < $mod_ago))) {
return array($last_activity_date, $last_mod_date, $mods);
}
else {
return false;
}
}
// Find out the last activity for our board
if (!isset($_GET['claim'])) {
$title = "Boards that need new owners";
$q = query("SELECT uri FROM boards");
$body = '<p>The following boards have been abandoned by their owners or have less than one post in a seventy-two hour period. Think you can do a better job? Claim one! Note: You can only reclaim one board per IP per 72 hours. Choose carefully!</p><table class="modlog" style="width:auto"><thead><tr><th>Board</th><th>Last activity</th><th>Last mod login</th><th>Reclaim</th></thead></tr><tbody>';
$boards = $q->fetchAll(PDO::FETCH_COLUMN);
$delete = array();
foreach($boards as $board) {
$last_activity = last_activity($board);
if ($last_activity) {
list($last_activity_date, $last_mod_date, $mods) = $last_activity;
$delete[] = array('board' => $board, 'last_activity' => $last_activity_date, 'last_mod' => $last_mod_date);
$last_mod_f = $last_mod_date ? $last_mod_date->format('Y-m-d H:i:s') : '<em>never</em>';
$body .= "<tr><td><a href='/{$board}/'>/{$board}/</a></td><td>{$last_activity_date->format('Y-m-d H:i:s')}</td><td>{$last_mod_f}</td><td><form style='margin-bottom:0!important'><input type='hidden' name='claim' value='{$board}'><input type='submit' value='Claim!'></form></td></tr>";
}
}
$body .= "</table>";
}
else {
$query = prepare('SELECT `last` FROM ``claim`` WHERE `ip` = :ip');
$query->bindValue(':ip', $_SERVER['REMOTE_ADDR']);
$query->execute();
$r_last = $query->fetch(PDO::FETCH_ASSOC);
$last = new DateTime($r_last['last'], new DateTimeZone('UTC'));
$ago = (new DateTime('',new DateTimeZone('UTC')))->sub(new DateInterval('P3D'));
if ($last > $ago and $r_last) {
error('You already claimed a board today');
}
openBoard($_GET['claim']);
$title = "Claiming board /{$board['uri']}/";
$last_activity = last_activity($board['uri']);
if ($last_activity) {
list($last_activity_date, $last_mod_date, $mods) = $last_activity;
$query = prepare('INSERT INTO ``claim``(`ip`, `last`) VALUES(:ip, NOW()) ON DUPLICATE KEY UPDATE `last`=NOW()');
$query->bindValue(':ip', $_SERVER['REMOTE_ADDR']);
$query->execute();
$password = base64_encode(openssl_random_pseudo_bytes(9));
$salt = generate_salt();
$hashed = hash('sha256', $salt . sha1($password));
$query = prepare('UPDATE ``mods`` SET `password` = :hashed, `salt` = :salt WHERE BINARY username = :mod');
$query->bindValue(':hashed', $hashed);
$query->bindValue(':salt', $salt);
$query->bindValue(':mod', $mods[0]['username']);
$query->execute();
query(sprintf("UPDATE posts_%s SET ip = '127.0.0.1'", $board['uri']));
$query = prepare("DELETE FROM bans WHERE board = :board");
$query->bindValue(":board", $board['uri']);
$query->execute();
_syslog(LOG_NOTICE, "Board claimed: {$board['uri']}");
$body = "<p>Please read the following instructions carefully:</p>
<p>The username of the admin of this board is <strong>{$mods[0]['username']}</strong>. Please write this down, you will need it to log in. It cannot be changed.</p>
<p>A new password has been generated for this board. It is <tt>{$password}</tt> . Please write this down, you will need it to log in. <em>If you forget your password, it cannot be changed. You must wait to reclaim the board again! Do not lose it.</em></p>
<p>The URL you use to manage your board is <a href='/mod.php'>https://8ch.net/mod.php</a>. Log in using the details above. Note: The board can still be claimed by another user until you log in for the first time or if it still meets the inactivity criteria, so post something!</p>
<p>
";
} else {
error('Board active or does not exist, cannot be reclaimed.');
}
}
$config['default_stylesheet'] = array('Yotsuba B', $config['stylesheets']['Yotsuba B']);
echo Element("page.html", array("config" => $config, "body" => $body, "title" => $title));
*/

View File

@ -1,61 +0,0 @@
<?php
include 'inc/functions.php';
if (php_sapi_name() == 'fpm-fcgi') {
error('Cannot be run directly.');
}
function last_activity($board) {
// last post
$query = prepare(sprintf("SELECT MAX(time) AS time FROM posts_%s", $board));
$query->execute();
$row = $query->fetch();
$ago = (new DateTime)->sub(new DateInterval('P1W'));
$mod_ago = (new DateTime)->sub(new DateInterval('P2W'));
$last_activity_date = new DateTime();
$last_mod_date = new DateTime();
$last_activity_date->setTimestamp($row['time']);
$query = query("SELECT id, username FROM mods WHERE boards = '$board' AND type = 20");
$mods = $query->fetchAll();
if ($mods) {
$mod = $mods[0]['id'];
$query = query("SELECT MAX(time) AS time FROM modlogs WHERE `mod` = $mod");
$a = $query->fetchAll(PDO::FETCH_COLUMN);
if ($a[0]) {
$last_mod_date->setTimestamp($a[0]);
if (!$row['time'])
$last_activity_date->setTimestamp($a[0]);
} else {// no one ever logged in, try board creation time
$query = query("SELECT UNIX_TIMESTAMP(time) AS time FROM board_create WHERE uri = '$board'");
$crt = $query->fetchAll(PDO::FETCH_COLUMN);
if ($crt) $last_activity_date->setTimestamp($crt[0]);
$last_mod_date = false;
}
}
if (($last_activity_date < $ago or ($last_mod_date and $last_mod_date < $mod_ago))) {
return array($last_activity_date, $last_mod_date, $mods);
}
else {
return false;
}
}
$q = query("SELECT uri FROM boards");
$boards = $q->fetchAll(PDO::FETCH_COLUMN);
$delete = array();
foreach($boards as $board) {
$last_activity = last_activity($board);
if ($last_activity) {
list($last_activity_date, $last_mod_date, $mods) = $last_activity;
$last_mod_f = $last_mod_date ? $last_mod_date->format('Y-m-d H:i:s') : '<em>never</em>';
$last_activity_f = $last_activity_date ? $last_activity_date->format('Y-m-d H:i:s') : '<em>never</em>';
$delete[] = array('board' => $board, 'last_activity_date' => $last_activity_f, 'last_mod' => $last_mod_date, 'last_mod_f' => $last_mod_f);
}
}
$body = Element("8chan/claim.html", array("config" => $config, "delete" => $delete));
file_write("claim.html", Element("page.html", array("config" => $config, "body" => $body, "title" => _("Claim"), "subtitle" => _("Take deserted boards back from their owners"))));

View File

@ -1,152 +0,0 @@
<?php
include "inc/functions.php";
if (!(php_sapi_name() == "cli")) {
die('nope');
}
$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('P14D'));
$mod_ago = (new DateTime)->sub(new DateInterval('P7D'));
// Find out the last activity for our board
$delete = array();
foreach($boards as $board) {
if (in_array($board, $protected)) {
continue;
}
// last post
$query = prepare(sprintf("SELECT MAX(time) AS time FROM posts_%s", $board));
$query->execute();
$row = $query->fetch();
//count posts
$query = prepare(sprintf("SELECT COUNT(id) AS count FROM posts_%s", $board));
$query->execute();
$count = $query->fetch();
$last_activity_date = new DateTime();
$last_mod_date = new DateTime();
$last_activity_date->setTimestamp($row['time']);
$query = query("SELECT id, username FROM mods WHERE boards = '$board'");
$mods = $query->fetchAll();
if ($mods) {
$mod = $mods[0]['id'];
$query = query("SELECT MAX(time) AS time FROM modlogs WHERE `mod` = BINARY $mod");
$a = $query->fetchAll(PDO::FETCH_COLUMN);
if ($a[0]) {
$last_mod_date->setTimestamp($a[0]);
if (!$row['time'])
$last_activity_date->setTimestamp($a[0]);
} else {// no one ever logged in, try board creation time
$query = query("SELECT UNIX_TIMESTAMP(time) AS time FROM board_create WHERE uri = '$board'");
$crt = $query->fetchAll(PDO::FETCH_COLUMN);
if (!empty($crt)) $last_activity_date->setTimestamp($crt[0]);
$last_mod_date = false;
}
}
if (($last_activity_date < $ago or ($last_mod_date and $last_mod_date < $mod_ago)) and (int)$count['count'] < 5 and isset($mods[0]['id'])) {
#if (($last_activity_date < $ago or ($last_mod_date and $last_mod_date < $mod_ago)) and isset($mods[0]['id'])) {
echo $board, ' ', $last_activity_date->format('Y-m-d H:i:s'), ' ', ($last_mod_date ? $last_mod_date->format('Y-m-d H:i:s') : 'false'), ' ', $count['count'], ' ', $mod, "\r\n";
$delete[] = array('board' => $board, 'last_activity' => $last_activity_date, 'last_mod' => $last_mod_date, 'mod' => isset($mods[0]['username']) ? $mods[0]['username'] : false, 'count' => $count['count']);
}
}
if ($argc > 1) {
$f = fopen('rip.txt', 'a');
fwrite($f, "--\r\n".date('c')."\r\n");
foreach($delete as $i => $d){
$s = "RIP /".$d['board']."/, created by ".($d['mod']?$d['mod']:'?')." and last active on ".$d['last_activity']->format('Y-m-d H:i:s.').($d['last_mod'] ? ' Mod last active on ' . $d['last_mod']->format('Y-m-d H:i:s.') : ' Mod never active.') . " Number of posts: {$d['count']}." . "\r\n";
echo $s;
fwrite($f, $s);
if (!openBoard($d['board'])) continue;;
$query = prepare('DELETE FROM ``boards`` WHERE `uri` = :uri');
$query->bindValue(':uri', $board['uri']);
$query->execute() or error(db_error($query));
if ($config['cache']['enabled']) {
cache::delete('board_' . $board['uri']);
cache::delete('all_boards');
}
// Delete posting table
$query = query(sprintf('DROP TABLE IF EXISTS ``posts_%s``', $board['uri'])) or error(db_error());
// Clear reports
$query = prepare('DELETE FROM ``reports`` WHERE `board` = :id');
$query->bindValue(':id', $board['uri'], PDO::PARAM_INT);
$query->execute() or error(db_error($query));
// Delete from table
$query = prepare('DELETE FROM ``boards`` WHERE `uri` = :uri');
$query->bindValue(':uri', $board['uri'], PDO::PARAM_INT);
$query->execute() or error(db_error($query));
$query = prepare("SELECT `board`, `post` FROM ``cites`` WHERE `target_board` = :board ORDER BY `board`");
$query->bindValue(':board', $board['uri']);
$query->execute() or error(db_error($query));
while ($cite = $query->fetch(PDO::FETCH_ASSOC)) {
if ($board['uri'] != $cite['board']) {
if (!isset($tmp_board))
$tmp_board = $board;
openBoard($cite['board']);
rebuildPost($cite['post']);
}
}
if (isset($tmp_board))
$board = $tmp_board;
$query = prepare('DELETE FROM ``cites`` WHERE `board` = :board OR `target_board` = :board');
$query->bindValue(':board', $board['uri']);
$query->execute() or error(db_error($query));
$query = prepare('DELETE FROM ``antispam`` WHERE `board` = :board');
$query->bindValue(':board', $board['uri']);
$query->execute() or error(db_error($query));
// Remove board from users/permissions table
$query = query('SELECT `id`,`boards` FROM ``mods``') or error(db_error());
while ($user = $query->fetch(PDO::FETCH_ASSOC)) {
$user_boards = explode(',', $user['boards']);
if (in_array($board['uri'], $user_boards)) {
unset($user_boards[array_search($board['uri'], $user_boards)]);
$_query = prepare('UPDATE ``mods`` SET `boards` = :boards WHERE `id` = :id');
$_query->bindValue(':boards', implode(',', $user_boards));
$_query->bindValue(':id', $user['id']);
$_query->execute() or error(db_error($_query));
}
}
// Delete entire board directory
exec('rm -rf ' . $board['uri'] . '/');
rrmdir('static/banners/' . $board['uri']);
file_unlink("stylesheets/board/{$board['uri']}.css");
// HAAAAAX
if($config['dir']['img_root'] != '')
rrmdir($config['dir']['img_root'] . $board['uri']);
if ($config['cache']['enabled']) cache::delete('board_' . $board['uri']);
_syslog(LOG_NOTICE, "Board deleted: {$board['uri']}");
if ($d['mod']) {
$query = prepare('DELETE FROM ``mods`` WHERE `username` = BINARY :id');
$query->bindValue(':id', $d['mod']);
$query->execute() or error(db_error($query));
}
}
fclose($f);
}
cache::delete('all_boards_uri');
cache::delete('all_boards');
rebuildThemes('boards');
$query = query('DELETE FROM board_create WHERE uri NOT IN (SELECT uri FROM boards);') or error(db_error());

181
faq.php
View File

@ -1,181 +0,0 @@
<?php
include "inc/functions.php";
$body = <<<EOT
<style>img{max-width:100%}</style>
<div class="ban">
<h2>What is 8chan?</h2>
<p>8ch.net is a site running 'infinity', which is an open source software that allows anyone to create and manage their own anonymous imageboard without any programming or webhosting experience for free.</p>
<h2>What is an imageboard?</h2>
<p>An imageboard is a type of internet forum which lets users post text and images anonymously (without name) about any topic, or no topic at all. Unlike forums, imageboards do not hold old content permamently, and old threads are pruned as new ones are created.</p>
<p>The imageboard format holds several advantages against traditional forums:</p>
<ol>
<li>There is no registration process, which allows for anyone to post what they like without having to jump through hoops.</li>
<li>Users do not have names and thus feel no reason to build up an identity or reputation for themselves. Post are judged based on their content rather than who made them.</li>
<li>sharing images and multimedia content is as easy as saving and uploading it to the site.</li>
</ol>
<h2>How is 8chan run?</h2>
<p>8chan is a service that hosts a large selection of imageboards to browse. These boards are maintained by their respective board owners, who are not affiliated with the 8chan global staff.</p>
<p>The 8chan global staff are responsible for maintaining the site as a whole and protecting it from spam and illegal content. The administration is NOT responsible for enforcing any rules outside of the global rule. Any complaints about the content or management of a board should be addressed towards the owner of the board, unless it violates the law of the United States of America, or global policy.</p>
<h2>How do I post a new thread?</h2>
<p>Navigate to the board you would like to post on, fill out the post form, and click "New Thread". On many boards, you are required to upload an image, but if you do not have one you can also draw a picture by clicking "Oekaki". No boards require you to fill out a name or email address.</p>
<img src="/static/faq/new-thread.png" alt="New thread FAQ">
<h2>How do I comment on a thread?</h2>
<p>On 8chan, threads are ordered by newest to oldest and have no "score" like on other websites. There are no upvotes or "Like" buttons. This allows even unpopular opinions to rise to the top. The replies in threads are ordered oldest to newest and similarly have no score.</p>
<p>To reply to a thread, click [Reply] on any thread on a board's index.</p>
<img src="/static/faq/new-reply.png" alt="New thread FAQ">
<p>Fill out your reply. Only the "Comment" field is required for replies on all boards.</p>
<img src="/static/faq/new-reply2.png" alt="New thread FAQ">
<p>Your reply will be highlighted on the page.</p>
<img src="/static/faq/new-reply3.png" alt="New thread FAQ">
<h2>How do I reply to another poster?</h2>
<p>Click the number of their post. A reply box will open automatically prepopulated with the post number you're replying to. Write your post under their post number. Similarly, when people reply to you, it will show (You) after the post number as a hint.</p>
<img src="/static/faq/new-reply4.png" alt="New thread FAQ">
<h2>Are there any global rules regarding content?</h2>
<p>Only one:</p>
<ul>
<li>Do not post, request, or link to any content illegal in the United States of America. Do not create boards with the sole purpose of posting or spreading such content.</li>
</ul>
<p>Other than that, you are free to institute whatever rules you want on your board.</p>
<p><a href="/obscenity.html">More information about US obscenity laws and how they relate to 8chan boards</a></p>
<p><a href="/dost.html">More information about the Dost test</a></p>
<p><strong>TL;DR: 8chan considers all nude images of children to be child porn and they will be deleted and the posting address banned if viable.</strong></p>
<p><a href="/personhood.html">Just who is this 8chan person anyway?</a></p>
<h2>How do I add more volunteers?</h2>
<p>You may do this in your board settings, click on "Edit board volunteers".
<h2>How do I manage my board?</h2>
<p>Go to <a href="/mod.php">the volunteer panel</a> and click on the board link for your board.</p>
<h2>How do I contact the admin?</h2>
<p>The admin can be reached at <tt>admin at 8chan dot co</tt>.</p>
<h2>What's your privacy policy?</h2>
<p>Find it <a href="/privacy.pdf">here</a>.</p>
<h2>Help! My board has been deleted!</h2>
<p>As of November 13th, 2014, board expiration no longer occurs.</p>
<p>You still may lose access to your board, however, if you fail to log in for two weeks or it receives no posts for a week. See <a href="/claim.html">here</a> for a list of boards that are available for reclaiming.</p>
<h2>How do I post as a volunteer on my board?</h2>
<p>Make sure you are using the volunteer interface to view your board. The URL of your browser should be <a href="/mod.php?/yourboard"><tt>https://8ch.net/mod.php?/yourboard</tt></a>.</p>
<p>If you are the owner of the board, put "## Board Owner" in the name field. If someone else is the owner and you are just assisting them, put "## Board Volunteer" in the name field. Write your post and click "Reply". It will appear with your capcode.</p>
<h2>Help! The owner of X board is doing something I don't like! Can I have X board?</h2>
<p>If they aren't doing anything illegal, I can't help you. I don't dictate how board owners should manage their boards <a href="http://8archive.moe/meta/thread/18555/#18555">outside of a few conditions</a>:</p>
<ol>
<li>the board owner nukes the board either by deleting all the posts or banning so much IP space hardly anyone can post</li>
<li>the board owner implements CSS which makes posting impossible or very difficult, and someone wants to use the board name for something else</li>
<li>the board owner allows illegal content to be posted, or states in the rules that the global rule doesn't apply</li>
</ol>
<p>If they are, email me.</p>
<h2>Can you add some new feature?</h2>
<p>Open a <a href="https://github.com/ctrlcctrlv/8chan/issues">Github issue</a>. Better yet, write it yourself and open a pull request.
<h2>What is "sage"?</h2>
<p>Posters may reply to threads without bumping them to the top of the index by putting "sage" in the email field.</p>
<h2>What is a tripcode?</h2>
<p>Most posts on 8chan are made anonymously, but this is not the only way to post. The name field can be used <em>four</em> ways to establish identity:</p>
<ol>
<li>By simply writing a name in the box. This is insecure as any other poster can write the same name.</li>
<li>By writing a # character and then a password. Putting #example in the name field would become !KtW6XcghiY. This is reasonably secure, but with increasing GPU speeds these tripcodes can be cracked in a few days by a dedicated attacker.</li>
<li>By writing two # characters and then a password. Putting ##example in the name field would become !!Dz.MSNRw9M. This is quite secure, but it relies on a secret salt on the server so the code will not work on sites other than 8chan.</li>
<li>Board owners and volunteers can enter the special codes "## Board Owner" and "## Board Volunteer" which become <em>capcodes</em> that display after the name. The 8chan administrator can type "## Admin" which becomes <span class="capcode" title="This post was written by the global 8chan administrator."> <i class="fa fa-wheelchair" style="color:blue;"></i> <span style="color:red">8chan Administrator</span></span>.</li>
</ol>
<p>Please note, many boards on 8chan have an option set called "Forced anonymity" which causes the name field to not work. This is because many users (and therefore board owners) do not like tripcode users.</p>
<h2>How do I format my text?</h2>
<ul>
<li>**spoiler** or [spoiler]spoiler[/spoiler] -&gt; spoiler</li>
<li>''italics'' -&gt; <em>italics</em></li>
<li>'''bold''' -&gt; <strong>bold</strong></li>
<li>__underline__ -&gt; <u>underline</u></li>
<li>==heading== -&gt; <span class='heading'>heading</span> (must be on own line)</li>
<li>~~strikethrough~~ -&gt; <s>strikethrough</s></li>
<li>[aa] tags for ASCII/JIS art (escape formatting)</li>
<li>[code] tags if enabled by board owner</li>
<li>$$ and \( \) LaTeX tags if enabled by board owner</li>
</ul>
<h2>How are featured boards chosen?</h2>
<p>Top twenty-five boards excluding /meta/, /b/, /operate/, /boards/ and /n/.</p>
<h2>Who owns boards like /b/, /n/ and /operate/?</h2>
<p>No one, so they are <em>de facto</em> managed by the administration.</p>
<h2>Why does <a href="/banned">https://8ch.net/banned</a> say that I'm banned? I can still use the boards?</h2>
<p>8chan is centered around user created boards. That's a board with CSS that makes it look like the ban page, not an official page. You've been tricked. 8chan has no official ban check page.</p>
<h2>Where's the mobile app?</h2>
<p>There is no official mobile app, however there is an unofficial Android app at <a href="https://github.com/wingy/Exodus/releases">wingy/Exodus</a>.</p>
<p>I don't provide support for this app, ask the developer of it if you have a problem with it.</p>
<h2>Where's the archive?</h2>
<p><s>There isn't one yet and there will never be an official archive.</s></p>
<p>Given that archives are inevitable and will be created anyway via <a href="https://archive.today">archive.today</a>, Google cache, and anyone who installs Asagi, I'm softening my stance on this. Currently, 8archive.moe provides our archive, and I may set up an official one. <strong>All archives officially partnered with us will be opt-in by our board owners, not opt-out. Archives who archive boards that have not opted in will be considered pirate archives, and legal action may be taken.</strong></p>
<h2>Can I have a list of all API endpoints for getting raw data from 8chan?</h2>
<p>
Assuming the /b/ board, they are as follows:</p>
<ul>
<li><a href="https://8ch.net/b/index.rss">https://8ch.net/b/index.rss</a> - RSS formatted index so that you can watch smaller boards and get updates when they get new posts using a feed reader like Thunderbird or Feedly.</li>
<li><a href="https://8ch.net/b/0.json">https://8ch.net/b/0.json</a> - Index of all threads on page 0 of /b/.</li>
<li><a href="https://8ch.net/b/res/1.json">https://8ch.net/b/res/1.json</a> - All replies of thread 1 on /b/.</li>
<li><a href="https://8ch.net/b/threads.json">https://8ch.net/b/threads.json</a> - Thread index of all 15 pages of /b/.</li>
</ul>
<p>There are also endpoints for getting information about 8chan's boards:</p>
<ul>
<li><a href="https://8ch.net/boards.json">https://8ch.net/boards.json</a> - Boards on 8chan (warning, 1MB+)</a></li>
<li><a href="https://8ch.net/settings.php?board=b">https://8ch.net/settings.php?board=b</a> - Board settings of /b/ (JSON format)</li>
</ul>
<p>Just read the data to get an idea of what is exposed and under what attribute names. It should be self explanatory.</p>
<p><strong>Endpoints not listed here, like post.php, catalog.json or boards-top20.json are subject to change or removal at any time!</strong></p>
<h2>I would like to contribute a translation in my language.</h2>
<p>Great! See <a href="/translation.html">this page</a> for more information.</p>
<h2>Are there any publicly available statistics?</h2>
<p>Yes, take a look at <a href="http://stats.4ch.net/8chan/">http://stats.4ch.net/8chan/</a>.
<h2>I got an email from an @8chan.co email address, is that you?</h2>
<p>8chan.co uses <a href="https://cock.li">cock.li</a> to manage our domain's email. cock.li allows anyone to create an email account @8chan.co.</p>
<p>That said, we have quite a few official 8chan.co email addresses. They are:</p>
<ul>
<li>admin at 8chan dot co</li>
<li>dmca at 8chan dot co</li>
<li>claim at 8chan dot co</li>
</ul>
<h2>I would like to send you an encrypted message.</h2>
<p>The current admin contact private key can always be found at <a href="https://8ch.net/pubkey.txt">https://8ch.net/pubkey.txt</a>.</p>
<p>The current key fingerprint is <tt>6F12 EC72 A82A BCA3 5235 063A 10DD C983 901A A183</tt>.</p>
<h2>How do I donate?</h2>
<p>Donations can be sent to 1NpQaXqmCBji6gfX8UgaQEmEstvVY7U32C (Bitcoin) or LgNczzSm64C3BmaXyFVQnM3PvcmSd196f6 (Litecoin).</p>
<p>I am also a big fan of Monero (XMR). You can send XMR to our <a href="http://openalias.org">OpenAlias</a> in the simplewallet client, or simply send to 49dBJhGhYFxJEfydS6hH6GRyg1W4cDgupdNVtw7j1WtcUY7xPXwNLw6fUVay644viaCcEhMFG1Z7SjjxRXEFDdNWJdvH9kS.</p>
<h2>Are you really a cripple?</h2>
<p>Yes.</p>
<img src="/static/Mamoru.jpg" alt="Mamoru" style="width:128px">
</div>
EOT;
echo Element("page.html", array("config" => $config, "body" => $body, "title" => "FAQ"));

View File

@ -1266,16 +1266,6 @@
// Try not to build pages when we shouldn't have to. // Try not to build pages when we shouldn't have to.
$config['try_smarter'] = true; $config['try_smarter'] = true;
// EXPERIMENTAL: Defer static HTML building to a moment, when a given file is actually accessed.
// Warning: This option won't run out of the box. You need to tell your webserver, that a file
// for serving 403 and 404 pages is /smart_build.php. Also, you need to turn off indexes.
$config['smart_build'] = false;
// Smart build related: when a file doesn't exist, where should we redirect?
$config['page_404'] = '/404.html';
// Smart build related: extra entrypoints.
$config['smart_build_entrypoints'] = array();
/* /*
* ==================== * ====================

View File

@ -1,13 +0,0 @@
<?php
/*include 'inc/functions.php';
$query = query('SELECT np.* FROM newsplus np INNER JOIN `posts_n` p ON np.thread=p.id WHERE np.dead IS FALSE ORDER BY p.bump DESC');
if ($query) {
$newsplus = $query->fetchAll(PDO::FETCH_ASSOC);
} else {
$newsplus = false;
}
$index = Element("8chan/index.html", array("config" => $config, "newsplus" => $newsplus));
file_write('index.html', $index);
echo $index;*/

View File

@ -1,200 +0,0 @@
<?php
require_once("inc/functions.php");
if (!$config['smart_build']) {
die('You need to enable $config["smart_build"]');
}
$config['smart_build'] = false; // Let's disable it, so we can build the page for real
function after_open_board() { global $config;
$config['smart_build'] = false;
};
function sb_board($b, $page = 1) { global $config, $build_pages; $page = (int)$page;
if ($page < 1) return false;
if (!openBoard($b)) return false;
if ($page > $config['max_pages']) return false;
$config['try_smarter'] = true;
$build_pages = array($page);
buildIndex("skip");
return true;
}
function sb_api_board($b, $page = 0) { $page = (int)$page;
return sb_board($b, $page + 1);
}
function sb_thread($b, $thread, $slugcheck = false) { global $config; $thread = (int)$thread;
if ($thread < 1) return false;
if (!preg_match('/^'.$config['board_regex'].'$/u', $b)) return false;
if (Cache::get("thread_exists_".$b."_".$thread) == "no") return false;
$query = prepare(sprintf("SELECT MAX(`id`) AS `max` FROM ``posts_%s``", $b));
if (!$query->execute()) return false;
$s = $query->fetch(PDO::FETCH_ASSOC);
$max = $s['max'];
if ($thread > $max) return false;
$query = prepare(sprintf("SELECT `id` FROM ``posts_%s`` WHERE `id` = :id AND `thread` IS NULL", $b));
$query->bindValue(':id', $thread);
if (!$query->execute() || !$query->fetch(PDO::FETCH_ASSOC) ) {
Cache::set("thread_exists_".$b."_".$thread, "no");
return false;
}
if ($slugcheck == 50) { // Should we really generate +50 page? Maybe there are not enough posts anyway
global $request;
$r = str_replace("+50", "", $request);
$r = substr($r, 1); // Cut the slash
if (file_exists($r)) return false;
}
if (!openBoard($b)) return false;
buildThread($thread);
return true;
}
function sb_thread_slugcheck50($b, $thread) {
return sb_thread($b, $thread, 50);
}
function sb_api($b) { global $config;
if (!openBoard($b)) return false;
$config['try_smarter'] = true;
$build_pages = array(-1);
buildIndex();
return true;
}
function sb_ukko() {
rebuildTheme("ukko", "post-thread");
return true;
}
function sb_catalog($b) {
if (!openBoard($b)) return false;
rebuildTheme("catalog", "post-thread", $b);
return true;
}
function sb_recent() {
rebuildTheme("recent", "post-thread");
return true;
}
function sb_sitemap() {
rebuildTheme("sitemap", "all");
return true;
}
$entrypoints = array();
$entrypoints['/%b/'] = 'sb_board';
$entrypoints['/%b/'.$config['file_index']] = 'sb_board';
$entrypoints['/%b/'.$config['file_page']] = 'sb_board';
$entrypoints['/%b/%d.json'] = 'sb_api_board';
if ($config['api']['enabled']) {
$entrypoints['/%b/threads.json'] = 'sb_api';
$entrypoints['/%b/catalog.json'] = 'sb_api';
}
$entrypoints['/%b/'.$config['dir']['res'].$config['file_page']] = 'sb_thread';
$entrypoints['/%b/'.$config['dir']['res'].$config['file_page50']] = 'sb_thread_slugcheck50';
if ($config['api']['enabled']) {
$entrypoints['/%b/'.$config['dir']['res'].'%d.json'] = 'sb_thread';
}
$entrypoints['/*/'] = 'sb_ukko';
$entrypoints['/*/index.html'] = 'sb_ukko';
$entrypoints['/recent.html'] = 'sb_recent';
$entrypoints['/%b/catalog.html'] = 'sb_catalog';
$entrypoints['/sitemap.xml'] = 'sb_sitemap';
$reached = false;
$request = $_SERVER['REQUEST_URI'];
list($request) = explode('?', $request);
foreach ($entrypoints as $id => $fun) {
$id = '@^' . preg_quote($id, '@') . '$@u';
$id = str_replace('%b', '('.$config['board_regex'].')', $id);
$id = str_replace('%d', '([0-9]+)', $id);
$id = str_replace('%s', '[a-zA-Z0-9-]+', $id);
$matches = null;
if (preg_match ($id, $request, $matches)) {
array_shift($matches);
$reached = call_user_func_array($fun, $matches);
break;
}
}
function die_404() { global $config;
if (!$config['page_404']) {
header("HTTP/1.1 404 Not Found");
header("Status: 404 Not Found");
echo "<h1>404 Not Found</h1><p>Page doesn't exist<hr><address>vichan</address>";
}
elseif (is_callable($config['page_404'])) {
$config['page_404']();
}
else {
header("Location: ".$config['page_404']);
}
header("X-Accel-Expires: 120");
die();
}
if ($reached) {
if ($request[strlen($request)-1] == '/') {
$request .= 'index.html';
}
$request = '.'.$request;
if (!file_exists($request)) {
die_404();
}
header("HTTP/1.1 200 OK");
header("Status: 200 OK");
if (preg_match('/\.json$/', $request)) {
header("Content-Type", "application/json");
}
elseif (preg_match('/\.js$/', $request)) {
header("Content-Type", "text/javascript; charset=utf-8");
}
elseif (preg_match('/\.xml$/', $request)) {
header("Content-Type", "application/xml");
}
else {
header("Content-Type", "text/html; charset=utf-8");
}
header("Cache-Control: public, nocache, no-cache, max-age=0, must-revalidate");
header("Expires: Fri, 22 Feb 1991 06:00:00 GMT");
header("Last-Modified: ".date('r', filemtime($request)));
//if (isset ($_SERVER['HTTP_ACCEPT_ENCODING']) && preg_match('/gzip/', $_SERVER['HTTP_ACCEPT_ENCODING']) && file_exists($request.".gz")) {
// header("Content-Encoding: gzip");
// $file = fopen($request.".gz", 'r');
//}
//else {
$file = fopen($request, 'r');
//}
fpassthru($file);
fclose($file);
}
else {
die_404();
}

View File

@ -1,37 +0,0 @@
<?php
include "inc/functions.php";
$body = <<<EOT
<DIv class="ban">
<p>Thank you for your interest in contributing a translation to infinity. This page will teach you how.</p>
<p><em>Historical note: infinity is based on a project called vichan (pronunced 6chan) which is in turn based on an older, abandoned project called Tinyboard. Vichan uses a service called Transifex to translate their files. In earlier versions of infinity, I decided to just keep using the vichan files because the only substantial source of new strings was the homepage and the Board configuration page, neither of which were displayed to board users so the existing set of translations worked. However, as time went on and more scripts and features were contributed, strings became out of sync. I originally intended to create 8chan a Transifex account, <s>but Transifex charges for something as simple as bulk imports</s>, so we will use this slightly more complicated process instead. Further, despite how much their charismatic CEO tried to sugarcoat it, the Transifex company <a href="https://github.com/transifex/transifex/issues/206#issuecomment-15243207">abandoned their open source repository</a> and became proprietary software, and then <s>immediately put limits on imports/exports</s>. <a href="https://www.gnu.org/philosophy/who-does-that-server-really-serve.html">Please see this page from the Free Software Foundation for more about the philosophy behind this and the dangers of trusting SaaS with your data. Who does that server really serve?</a></em></p>
<p>Some of my criticism of Transifex was not accurate, I apologize. You are free to either use Transifex or follow the steps below. <a href="https://www.transifex.com/projects/p/infinity/">Here's our Transifex team page</a></p>
<p>infinity uses gettext files for translation. This is what allows us to have boards in many languages on the same site, such as <a href="/argentina/">/argentina/ in Spanish</a>, <a href="/deutsch/">/deutsch/ in German</a> and <a href="/japan2/">/japan2/ in Japanese</a>. gettext files have the .po file extension. You can edit PO files by hand, but I highly recommend using POEdit. It is very easy to make syntax errors without POEdit or similar software.</p>
<ol>
<li>Install <a href="http://poedit.net/">POEdit</a>. POEdit is free software available for Mac, Linux and Windows.</li>
<li>Find your translation file. Go to <a href="https://github.com/ctrlcctrlv/infinity/">our Github project</a>, and in the files list click "inc", then "locale". You will see a list of languages. It's usually self explanatory which code is for which language, but if you're not sure you can check <a href="https://www.gnu.org/software/gettext/manual/html_node/Usual-Language-Codes.html#Usual-Language-Codes">the GNU project's list of usual language codes</a> and search for your language.</li>
</ol>
<p><strong>If your language is listed and you want to update the translation:</strong></p>
<ol>
<li>Download the .po files. <tt>tinyboard.po</tt> contains the strings generated by PHP, like "Comment", "Name", et cetera. <tt>javascript.po</tt> contains the strings generated by JavaScript, like all the fields under [Options]. You can translate one or both. For example, <a href="https://github.com/ctrlcctrlv/infinity/blob/master/inc/locale/fr_FR/LC_MESSAGES/tinyboard.po">here is the French translation of tinyboard.po</a>. To download it, click "Raw" and then save the file to your computer.</li>
<li>Click "Edit a translation" in POEdit. Navigate to the file you downloaded, fill in the translation boxes and save your file.</li>
</ol>
<p><strong>If your language is not listed and you want to add a translation for it:</strong></p>
<ol>
<li>Download the en <em>English</em> .po file. (Tip: If, for example, you want to create a new Spanish dialect translation when Spanish (Spain) already exists, download es_ES and use that as the template.)</li>
<li>Click "Create a new translation" in POEdit and select the po file you downloaded as the template file.</li>
<li>Select your language from the dropdown when prompted.</li>
</ol>
<p><em>Tip: If you would like to attribute your translation to you, you can change your Name and Email in Preferences.</em></p>
<p><em>Another tip: You might find that a string you want to translate is not in the files. Don't panic, I accidentally forget to put strings in {% trans %} tags and _() gettext function all the time so gettext doesn't catch them. Just email me and tell me where I forgot and I'll add it and update tinyboard.po/javascript.po. Some strings I don't want to add for legal reasons. Those are the ones at the bottom including the copyright notice.</em></p>
<p>Once you are done translating, save your .po file in POEdit and send it to admin@8chan.co, or, if you know how, open a pull request on Github with your translated file. Make sure to put the language you translated to in the subject of your email. Thanks in advance for your contribution!</p>
</div>
EOT;
echo Element("page.html", array("config" => $config, "body" => $body, "title" => "Translation tutorial"));