diff --git a/inc/8chan-functions.php b/inc/8chan-functions.php index 5a50ec74..ef289616 100644 --- a/inc/8chan-functions.php +++ b/inc/8chan-functions.php @@ -54,3 +54,23 @@ function human_time_diff( $from, $to = '' ) { return $since; } + +function is_billion_laughs($arr1, $arr2) { + $arr = array(); + foreach ($arr1 as $k => $v) { + $arr[$v] = $arr2[$k]; + } + + for ($i = 0; $i <= sizeof($arr); $i++) { + $cur = array_slice($arr, $i, 1); + $pst = array_slice($arr, 0, $i); + if (!$cur) continue; + $kk = array_keys($cur)[0]; + $vv = array_values($cur)[0]; + foreach ($pst as $k => $v) { + if (str_replace($kk, $vv, $v) != $v) + return true; + } + } + return false; +} diff --git a/inc/8chan-mod-pages-functions.php b/inc/8chan-mod-pages-functions.php new file mode 100644 index 00000000..eef96790 --- /dev/null +++ b/inc/8chan-mod-pages-functions.php @@ -0,0 +1,680 @@ + 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('tags/'.$board['uri']), 'tags'=>$tags, 'sfw'=>$sfw)); + } + + function mod_8_reassign($b) { + global $board, $config; + + if (!openBoard($b)) + error("Could not open board!"); + + if (!hasPermission($config['mod']['reassign_board'], $b)) + error($config['error']['noaccess']); + + $query = query("SELECT id, username FROM mods WHERE boards = '$b' AND type = 20"); + $mods = $query->fetchAll(); + + if (!$mods) { + error('No mods?'); + } + + $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(); + + $body = "Thanks for your interest in this board. Kindly find the username and password below. You can login at https://8ch.net/mod.php.
Username: {$mods[0]['username']}
Password: {$password}
Thanks for using 8chan!"; + + modLog("Reassigned board /$b/"); + + mod_page(_('Edit reassign'), 'blank.html', array('board'=>$board,'token'=>make_secure_link_token('reassign/'.$board['uri']),'body'=>$body)); + } + + function mod_8_volunteers($b) { + global $board, $config, $pdo; + if (!hasPermission($config['mod']['edit_volunteers'], $b)) + error($config['error']['noaccess']); + + if (!openBoard($b)) + error("Could not open board!"); + + if (isset($_POST['username'], $_POST['password'])) { + $query = prepare('SELECT * FROM ``mods`` WHERE type = 19 AND boards = :board'); + $query->bindValue(':board', $b); + $query->execute() or error(db_error($query)); + $count = $query->rowCount(); + $query = prepare('SELECT `username` FROM ``mods``'); + $query->execute() or error(db_error($query)); + $volunteers = $query->fetchAll(PDO::FETCH_ASSOC); + + if ($_POST['username'] == '') + error(sprintf($config['error']['required'], 'username')); + if ($_POST['password'] == '') + error(sprintf($config['error']['required'], 'password')); + if (!preg_match('/^[a-zA-Z0-9._]{1,30}$/', $_POST['username'])) + error(_('Invalid username')); + + if ($count > 10) { + error(_('Too many board volunteers!')); + } + + foreach ($volunteers as $i => $v) { + if (strtolower($_POST['username']) == strtolower($v['username'])) { + error(_('Refusing to create a volunteer with the same username as an existing one.')); + } + } + + $salt = generate_salt(); + $password = hash('sha256', $salt . sha1($_POST['password'])); + + $query = prepare('INSERT INTO ``mods`` VALUES (NULL, :username, :password, :salt, 19, :board)'); + $query->bindValue(':username', $_POST['username']); + $query->bindValue(':password', $password); + $query->bindValue(':salt', $salt); + $query->bindValue(':board', $b); + $query->execute() or error(db_error($query)); + + $userID = $pdo->lastInsertId(); + + + modLog('Created a new volunteer: ' . utf8tohtml($_POST['username']) . ' (#' . $userID . ')'); + } + + if (isset($_POST['delete'])){ + foreach ($_POST['delete'] as $i => $d){ + $query = prepare('SELECT * FROM ``mods`` WHERE id = :id'); + $query->bindValue(':id', $d); + $query->execute() or error(db_error($query)); + + $result = $query->fetch(PDO::FETCH_ASSOC); + + if (!$result) { + error(_('Volunteer does not exist!')); + } + + if ($result['boards'] != $b || $result['type'] != BOARDVOLUNTEER) { + error($config['error']['noaccess']); + } + + $query = prepare('DELETE FROM ``mods`` WHERE id = :id'); + $query->bindValue(':id', $d); + $query->execute() or error(db_error($query)); + } + } + + $query = prepare('SELECT * FROM ``mods`` WHERE type = 19 AND boards = :board'); + $query->bindValue(':board', $b); + $query->execute() or error(db_error($query)); + $volunteers = $query->fetchAll(PDO::FETCH_ASSOC); + + mod_page(_('Edit volunteers'), 'mod/volunteers.html', array('board'=>$board,'token'=>make_secure_link_token('volunteers/'.$board['uri']),'volunteers'=>$volunteers)); + + } + + function mod_8_flags($b) { + global $config, $mod, $board; + require_once 'inc/image.php'; + if (!hasPermission($config['mod']['edit_flags'], $b)) + error($config['error']['noaccess']); + + if (!openBoard($b)) + error("Could not open board!"); + + if (file_exists("$b/flags.ser")) + $config['user_flags'] = unserialize(file_get_contents("$b/flags.ser")); + + $dir = 'static/custom-flags/'.$b; + + if (!is_dir($dir)){ + mkdir($dir, 0777, true); + } + + function handle_file($id = false, $description, $b, $dir) { + global $config; + + if (!isset($description) and $description) + error(_('You must enter a flag description!')); + + if (strlen($description) > 255) + error(_('Flag description too long!')); + + if ($id) { + $f = 'flag-'.$id; + } else { + $f = 'file'; + $id = time() . substr(microtime(), 2, 3); + } + + $upload = $_FILES[$f]['tmp_name']; + $banners = array_diff(scandir($dir), array('..', '.')); + + if (!is_readable($upload)) + error($config['error']['nomove']); + + $extension = strtolower(mb_substr($_FILES[$f]['name'], mb_strrpos($_FILES[$f]['name'], '.') + 1)); + + if ($extension != 'png') { + error(_('Flags must be in PNG format.')); + } + + if (filesize($upload) > 48000){ + error(_('File too large!')); + } + + if (!$size = @getimagesize($upload)) { + error($config['error']['invalidimg']); + } + + if ($size[0] > 20 or $size[0] < 11 or $size[1] > 16 or $size[1] < 11){ + error(_('Image wrong size!')); + } + if (sizeof($banners) > 256) { + error(_('Too many flags.')); + } + + copy($upload, "$dir/$id.$extension"); + purge("$dir/$id.$extension"); + $config['user_flags'][$id] = utf8tohtml($description); + file_write($b.'/flags.ser', serialize($config['user_flags'])); + } + + // Handle a new flag, if any. + if (isset($_FILES['file'])){ + handle_file(false, $_POST['description'], $b, $dir); + } + + // Handle edits to existing flags. + foreach ($_FILES as $k => $a) { + if (empty($_FILES[$k]['tmp_name'])) continue; + + if (preg_match('/^flag-(\d+)$/', $k, $matches)) { + $id = (int)$matches[1]; + if (!isset($_POST['description-'.$id])) continue; + + if (isset($config['user_flags'][$id])) { + handle_file($id, $_POST['description-'.$id], $b, $dir); + } + } + } + + // Description just changed, flag not edited. + foreach ($_POST as $k => $v) { + if (!preg_match('/^description-(\d+)$/', $k, $matches)) continue; + $id = (int)$matches[1]; + if (!isset($_POST['description-'.$id])) continue; + + $description = $_POST['description-'.$id]; + + if (strlen($description) > 255) + error(_('Flag description too long!')); + $config['user_flags'][$id] = utf8tohtml($description); + file_write($b.'/flags.ser', serialize($config['user_flags'])); + } + + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $flags = << $d){ + if (!preg_match('/[0-9+]/', $d)){ + error('Nice try.'); + } + unlink("$dir/$d.png"); + $id = explode('.', $d)[0]; + unset($config['user_flags'][$id]); + file_write($b.'/flags.ser', serialize($config['user_flags'])); + } + } + + if (isset($_POST['alphabetize'])) { + asort($config['user_flags'], SORT_NATURAL | SORT_FLAG_CASE); + file_write($b.'/flags.ser', serialize($config['user_flags'])); + } + + $banners = array_diff(scandir($dir), array('..', '.')); + mod_page(_('Edit flags'), 'mod/flags.html', array('board'=>$board,'banners'=>$banners,'token'=>make_secure_link_token('banners/'.$board['uri']))); + } + + function mod_8_banners($b) { + global $config, $mod, $board; + require_once 'inc/image.php'; + + if (!hasPermission($config['mod']['edit_banners'], $b)) + error($config['error']['noaccess']); + + if (!openBoard($b)) + error("Could not open board!"); + + $dir = 'static/banners/'.$b; + + if (!is_dir($dir)){ + mkdir($dir, 0777, true); + } + + + if (isset($_FILES['file'])){ + $upload = $_FILES['file']['tmp_name']; + $banners = array_diff(scandir($dir), array('..', '.')); + + if (!is_readable($upload)) + error($config['error']['nomove']); + + $id = time() . substr(microtime(), 2, 3); + $extension = strtolower(mb_substr($_FILES['file']['name'], mb_strrpos($_FILES['file']['name'], '.') + 1)); + + if (!in_array($extension, array('jpg','jpeg','png','gif'))){ + error('Not an image extension.'); + } + + if (filesize($upload) > 512000){ + error('File too large!'); + } + + if (!$size = @getimagesize($upload)) { + error($config['error']['invalidimg']); + } + + if ($size[0] != 300 or $size[1] != 100){ + error('Image wrong size!'); + } + if (sizeof($banners) >= 50) { + error('Too many banners.'); + } + + copy($upload, "$dir/$id.$extension"); + } + + if (isset($_POST['delete'])){ + foreach ($_POST['delete'] as $i => $d){ + if (!preg_match('/[0-9+]\.(png|jpeg|jpg|gif)/', $d)){ + error('Nice try.'); + } + unlink("$dir/$d"); + } + } + + $banners = array_diff(scandir($dir), array('..', '.')); + mod_page(_('Edit banners'), 'mod/banners.html', array('board'=>$board,'banners'=>$banners,'token'=>make_secure_link_token('banners/'.$board['uri']))); + + } + + function mod_8_settings($b) { + global $config, $mod; + + //if ($b === 'infinity' && $mod['type'] !== ADMIN) + // error('Settings temporarily disabled for this board.'); + + if (!in_array($b, $mod['boards']) and $mod['boards'][0] != '*') + error($config['error']['noaccess']); + + if (!hasPermission($config['mod']['edit_settings'], $b)) + error($config['error']['noaccess']); + + if (!openBoard($b)) + error("Could not open board!"); + + $possible_languages = array_diff(scandir('inc/locale/'), array('..', '.', '.tx', 'README.md')); + + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $title = $_POST['title']; + $subtitle = $_POST['subtitle']; + $country_flags = isset($_POST['country_flags']) ? 'true' : 'false'; + $field_disable_name = isset($_POST['field_disable_name']) ? 'true' : 'false'; + $enable_embedding = isset($_POST['enable_embedding']) ? 'true' : 'false'; + $force_image_op = isset($_POST['force_image_op']) ? 'true' : 'false'; + $disable_images = isset($_POST['disable_images']) ? 'true' : 'false'; + $poster_ids = isset($_POST['poster_ids']) ? 'true' : 'false'; + $show_sages = isset($_POST['show_sages']) ? 'true' : 'false'; + $auto_unicode = isset($_POST['auto_unicode']) ? 'true' : 'false'; + $strip_combining_chars = isset($_POST['strip_combining_chars']) ? 'true' : 'false'; + $allow_roll = isset($_POST['allow_roll']) ? 'true' : 'false'; + $image_reject_repost = isset($_POST['image_reject_repost']) ? 'true' : 'false'; + $image_reject_repost_in_thread = isset($_POST['image_reject_repost_in_thread']) ? 'true' : 'false'; + $early_404 = isset($_POST['early_404']) ? 'true' : 'false'; + $allow_delete = isset($_POST['allow_delete']) ? 'true' : 'false'; + $allow_flash = isset($_POST['allow_flash']) ? '$config[\'allowed_ext_files\'][] = \'swf\';' : ''; + $allow_pdf = isset($_POST['allow_pdf']) ? '$config[\'allowed_ext_files\'][] = \'pdf\';' : ''; + $code_tags = isset($_POST['code_tags']) ? '$config[\'additional_javascript\'][] = \'js/code_tags/run_prettify.js\';$config[\'markup\'][] = array("/\[code\](.+?)\[\/code\]/ms", "
\$1
");' : ''; + $katex = isset($_POST['katex']) ? '$config[\'katex\'] = true;$config[\'additional_javascript\'][] = \'js/katex/katex.min.js\'; $config[\'markup\'][] = array("/\[tex\](.+?)\[\/tex\]/ms", "\$1"); $config[\'additional_javascript\'][] = \'js/katex-enable.js\';' : ''; + $user_flags = isset($_POST['user_flags']) ? "if (file_exists('$b/flags.php')) { include 'flags.php'; }\n" : ''; + $captcha = isset($_POST['captcha']) ? 'true' : 'false'; + $force_subject_op = isset($_POST['force_subject_op']) ? 'true' : 'false'; + $force_flag = isset($_POST['force_flag']) ? 'true' : 'false'; + $tor_posting = isset($_POST['tor_posting']) ? 'true' : 'false'; + $new_thread_capt = isset($_POST['new_thread_capt']) ? 'true' : 'false'; + $oekaki = isset($_POST['oekaki']) ? 'true' : 'false'; + + if ($_POST['locale'] !== 'en' && in_array($_POST['locale'], $possible_languages)) { + $locale = "\$config['locale'] = '{$_POST['locale']}.UTF-8';"; + } else { + $locale = ''; + } + + if (isset($_POST['max_images']) && (int)$_POST['max_images'] && (int)$_POST['max_images'] <= 5) { + $_POST['max_images'] = (int)$_POST['max_images']; + $multiimage = "\$config['max_images'] = {$_POST['max_images']}; + \$config['additional_javascript'][] = 'js/multi-image.js';"; + } else { + $multiimage = ''; + } + + $anonymous = base64_encode($_POST['anonymous']); + $blotter = base64_encode(purify_html(html_entity_decode($_POST['blotter']))); + $add_to_config = @file_get_contents($b.'/extra_config.php'); + $replace = ''; + + if (isset($_POST['replace'])) { + if (sizeof($_POST['replace']) > 200 || sizeof($_POST['with']) > 200) { + error(_('Sorry, max 200 wordfilters allowed.')); + } + if (count($_POST['replace']) == count($_POST['with'])) { + foreach ($_POST['replace'] as $i => $r ) { + if ($r !== '') { + $w = $_POST['with'][$i]; + + if (strlen($w) > 255) { + error(sprintf(_('Sorry, %s is too long. Max replacement is 255 characters'), utf8tohtml($w))); + } + + $replace .= '$config[\'wordfilters\'][] = array(base64_decode(\'' . base64_encode($r) . '\'), base64_decode(\'' . base64_encode($w) . '\'));'; + } + } + } + if (is_billion_laughs($_POST['replace'], $_POST['with'])) { + error(_('Wordfilters may not wordfilter previous wordfilters. For example, if a filters to bb and b filters to cc, that is not allowed.')); + } + } + + if (isset($_POST['hour_max_threads']) && in_array($_POST['hour_max_threads'], ['10', '25', '50', '100'])) { + $hour_max_threads = $_POST['hour_max_threads']; + } else { + $hour_max_threads = 'false'; + } + + if (isset($_POST['max_pages'])) { + $mp = (int)$_POST['max_pages']; + if ($mp > 25 || $mp < 2) { + $max_pages = 15; + } else { + $max_pages = $mp; + } + } else { + $max_pages = 15; + } + + if (isset($_POST['reply_limit'])) { + $rl = (int)$_POST['reply_limit']; + if ($rl > 750 || $rl < 250 || $rl % 25) { + $reply_limit = 250; + } else { + $reply_limit = $rl; + } + } else { + $reply_limit = 250; + } + + if (isset($_POST['max_newlines'])) { + $mn = (int)$_POST['max_newlines']; + if ($mn < 20 || $mn > 300) { + $max_newlines = 0; + } else { + $max_newlines = $mn; + } + } else { + $max_newlines = $mn; + } + + if (!(strlen($title) < 40)) + error('Invalid title'); + if (!(strlen($subtitle) < 200)) + error('Invalid subtitle'); + + $query = prepare('UPDATE ``boards`` SET `title` = :title, `subtitle` = :subtitle, `indexed` = :indexed, `public_bans` = :public_bans, `public_logs` = :public_logs, `8archive` = :8archive WHERE `uri` = :uri'); + $query->bindValue(':title', $title); + $query->bindValue(':subtitle', $subtitle); + $query->bindValue(':uri', $b); + $query->bindValue(':indexed', !isset($_POST['meta_noindex'])); + $query->bindValue(':public_bans', isset($_POST['public_bans'])); + $query->bindValue(':public_logs', (int)$_POST['public_logs']); + $query->bindValue(':8archive', isset($_POST['8archive'])); + $query->execute() or error(db_error($query)); + + $config_file = <<fetchAll(PDO::FETCH_ASSOC))); + file_write($b.'/config.php', $config_file); + file_write('stylesheets/board/'.$b.'.css', $clean_css); + + $_config = $config; + unset($config['wordfilters']); + + // 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(str_replace('flags.php', "$b/flags.php", preg_replace('/^\<\?php$/m', '', $config_file))); + // czaks: maybe reconsider using it, now that config is cached? + + // Clean the cache + if ($config['cache']['enabled']) { + cache::delete('board_' . $board['uri']); + cache::delete('all_boards'); + + cache::delete('config_' . $board['uri']); + cache::delete('events_' . $board['uri']); + + unlink('tmp/cache/locale_' . $board['uri']); + } + + // be smarter about rebuilds...only some changes really require us to rebuild all threads + if ($_config['captcha']['enabled'] != $config['captcha']['enabled'] + || $_config['new_thread_capt'] != $config['new_thread_capt'] /*New thread captcha - if toggling "enable captcha" requires this, toggling new thread capt does too, I guess.*/ + || $_config['captcha']['extra'] != $config['captcha']['extra'] + || $_config['blotter'] != $config['blotter'] + || $_config['field_disable_name'] != $config['field_disable_name'] + || $_config['show_sages'] != (isset($config['show_sages']) && $config['show_sages'])) { + buildIndex(); + $query = query(sprintf("SELECT `id` FROM ``posts_%s`` WHERE `thread` IS NULL", $b)) or error(db_error()); + while ($post = $query->fetch(PDO::FETCH_ASSOC)) { + buildThread($post['id']); + } + } + + modLog('Edited board settings', $b); + } + + $query = prepare('SELECT * FROM boards WHERE uri = :board'); + $query->bindValue(':board', $b); + $query->execute() or error(db_error($query)); + $board = $query->fetchAll()[0]; + + $css = @file_get_contents('stylesheets/board/' . $board['uri'] . '.css'); + + mod_page(_('Board configuration'), 'mod/settings.html', array('board'=>$board, 'css'=>prettify_textarea($css), 'token'=>make_secure_link_token('settings/'.$board['uri']), 'languages'=>$possible_languages,'allowed_urls'=>$config['allowed_offsite_urls'])); + } diff --git a/inc/8chan-mod-pages.php b/inc/8chan-mod-pages.php index 135a1c46..7894be58 100644 --- a/inc/8chan-mod-pages.php +++ b/inc/8chan-mod-pages.php @@ -1,26 +1,4 @@ $v) { - $arr[$v] = $arr2[$k]; - } - - for ($i = 0; $i <= sizeof($arr); $i++) { - $cur = array_slice($arr, $i, 1); - $pst = array_slice($arr, 0, $i); - if (!$cur) continue; - $kk = array_keys($cur)[0]; - $vv = array_values($cur)[0]; - foreach ($pst as $k => $v) { - if (str_replace($kk, $vv, $v) != $v) - return true; - } - } - return false; - } - } - $config['mod']['show_ip'] = GLOBALVOLUNTEER; $config['mod']['show_ip_less'] = BOARDVOLUNTEER; $config['mod']['manageusers'] = GLOBALVOLUNTEER; @@ -67,636 +45,10 @@ $config['mod']['shadow_capcode'] = 'Global Volunteer'; - $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('tags/'.$board['uri']), 'tags'=>$tags, 'sfw'=>$sfw)); - }; - - $config['mod']['custom_pages']['/reassign/(\%b)'] = function($b) { - global $board, $config; - - if (!openBoard($b)) - error("Could not open board!"); - - if (!hasPermission($config['mod']['reassign_board'], $b)) - error($config['error']['noaccess']); - - $query = query("SELECT id, username FROM mods WHERE boards = '$b' AND type = 20"); - $mods = $query->fetchAll(); - - if (!$mods) { - error('No mods?'); - } - - $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(); - - $body = "Thanks for your interest in this board. Kindly find the username and password below. You can login at https://8ch.net/mod.php.
Username: {$mods[0]['username']}
Password: {$password}
Thanks for using 8chan!"; - - modLog("Reassigned board /$b/"); - - mod_page(_('Edit reassign'), 'blank.html', array('board'=>$board,'token'=>make_secure_link_token('reassign/'.$board['uri']),'body'=>$body)); - }; - - $config['mod']['custom_pages']['/volunteers/(\%b)'] = function($b) { - global $board, $config, $pdo; - if (!hasPermission($config['mod']['edit_volunteers'], $b)) - error($config['error']['noaccess']); - - if (!openBoard($b)) - error("Could not open board!"); - - if (isset($_POST['username'], $_POST['password'])) { - $query = prepare('SELECT * FROM ``mods`` WHERE type = 19 AND boards = :board'); - $query->bindValue(':board', $b); - $query->execute() or error(db_error($query)); - $count = $query->rowCount(); - $query = prepare('SELECT `username` FROM ``mods``'); - $query->execute() or error(db_error($query)); - $volunteers = $query->fetchAll(PDO::FETCH_ASSOC); - - if ($_POST['username'] == '') - error(sprintf($config['error']['required'], 'username')); - if ($_POST['password'] == '') - error(sprintf($config['error']['required'], 'password')); - if (!preg_match('/^[a-zA-Z0-9._]{1,30}$/', $_POST['username'])) - error(_('Invalid username')); - - if ($count > 10) { - error(_('Too many board volunteers!')); - } - - foreach ($volunteers as $i => $v) { - if (strtolower($_POST['username']) == strtolower($v['username'])) { - error(_('Refusing to create a volunteer with the same username as an existing one.')); - } - } - - $salt = generate_salt(); - $password = hash('sha256', $salt . sha1($_POST['password'])); - - $query = prepare('INSERT INTO ``mods`` VALUES (NULL, :username, :password, :salt, 19, :board)'); - $query->bindValue(':username', $_POST['username']); - $query->bindValue(':password', $password); - $query->bindValue(':salt', $salt); - $query->bindValue(':board', $b); - $query->execute() or error(db_error($query)); - - $userID = $pdo->lastInsertId(); - - - modLog('Created a new volunteer: ' . utf8tohtml($_POST['username']) . ' (#' . $userID . ')'); - } - - if (isset($_POST['delete'])){ - foreach ($_POST['delete'] as $i => $d){ - $query = prepare('SELECT * FROM ``mods`` WHERE id = :id'); - $query->bindValue(':id', $d); - $query->execute() or error(db_error($query)); - - $result = $query->fetch(PDO::FETCH_ASSOC); - - if (!$result) { - error(_('Volunteer does not exist!')); - } - - if ($result['boards'] != $b || $result['type'] != BOARDVOLUNTEER) { - error($config['error']['noaccess']); - } - - $query = prepare('DELETE FROM ``mods`` WHERE id = :id'); - $query->bindValue(':id', $d); - $query->execute() or error(db_error($query)); - } - } - - $query = prepare('SELECT * FROM ``mods`` WHERE type = 19 AND boards = :board'); - $query->bindValue(':board', $b); - $query->execute() or error(db_error($query)); - $volunteers = $query->fetchAll(PDO::FETCH_ASSOC); - - mod_page(_('Edit volunteers'), 'mod/volunteers.html', array('board'=>$board,'token'=>make_secure_link_token('volunteers/'.$board['uri']),'volunteers'=>$volunteers)); - - }; - - $config['mod']['custom_pages']['/flags/(\%b)'] = function($b) { - global $config, $mod, $board; - require_once 'inc/image.php'; - if (!hasPermission($config['mod']['edit_flags'], $b)) - error($config['error']['noaccess']); - - if (!openBoard($b)) - error("Could not open board!"); - - if (file_exists("$b/flags.ser")) - $config['user_flags'] = unserialize(file_get_contents("$b/flags.ser")); - - $dir = 'static/custom-flags/'.$b; - - if (!is_dir($dir)){ - mkdir($dir, 0777, true); - } - - function handle_file($id = false, $description, $b, $dir) { - global $config; - - if (!isset($description) and $description) - error(_('You must enter a flag description!')); - - if (strlen($description) > 255) - error(_('Flag description too long!')); - - if ($id) { - $f = 'flag-'.$id; - } else { - $f = 'file'; - $id = time() . substr(microtime(), 2, 3); - } - - $upload = $_FILES[$f]['tmp_name']; - $banners = array_diff(scandir($dir), array('..', '.')); - - if (!is_readable($upload)) - error($config['error']['nomove']); - - $extension = strtolower(mb_substr($_FILES[$f]['name'], mb_strrpos($_FILES[$f]['name'], '.') + 1)); - - if ($extension != 'png') { - error(_('Flags must be in PNG format.')); - } - - if (filesize($upload) > 48000){ - error(_('File too large!')); - } - - if (!$size = @getimagesize($upload)) { - error($config['error']['invalidimg']); - } - - if ($size[0] > 20 or $size[0] < 11 or $size[1] > 16 or $size[1] < 11){ - error(_('Image wrong size!')); - } - if (sizeof($banners) > 256) { - error(_('Too many flags.')); - } - - copy($upload, "$dir/$id.$extension"); - purge("$dir/$id.$extension"); - $config['user_flags'][$id] = utf8tohtml($description); - file_write($b.'/flags.ser', serialize($config['user_flags'])); - } - - // Handle a new flag, if any. - if (isset($_FILES['file'])){ - handle_file(false, $_POST['description'], $b, $dir); - } - - // Handle edits to existing flags. - foreach ($_FILES as $k => $a) { - if (empty($_FILES[$k]['tmp_name'])) continue; - - if (preg_match('/^flag-(\d+)$/', $k, $matches)) { - $id = (int)$matches[1]; - if (!isset($_POST['description-'.$id])) continue; - - if (isset($config['user_flags'][$id])) { - handle_file($id, $_POST['description-'.$id], $b, $dir); - } - } - } - - // Description just changed, flag not edited. - foreach ($_POST as $k => $v) { - if (!preg_match('/^description-(\d+)$/', $k, $matches)) continue; - $id = (int)$matches[1]; - if (!isset($_POST['description-'.$id])) continue; - - $description = $_POST['description-'.$id]; - - if (strlen($description) > 255) - error(_('Flag description too long!')); - $config['user_flags'][$id] = utf8tohtml($description); - file_write($b.'/flags.ser', serialize($config['user_flags'])); - } - - if ($_SERVER['REQUEST_METHOD'] === 'POST') { - $flags = << $d){ - if (!preg_match('/[0-9+]/', $d)){ - error('Nice try.'); - } - unlink("$dir/$d.png"); - $id = explode('.', $d)[0]; - unset($config['user_flags'][$id]); - file_write($b.'/flags.ser', serialize($config['user_flags'])); - } - } - - if (isset($_POST['alphabetize'])) { - asort($config['user_flags'], SORT_NATURAL | SORT_FLAG_CASE); - file_write($b.'/flags.ser', serialize($config['user_flags'])); - } - - $banners = array_diff(scandir($dir), array('..', '.')); - mod_page(_('Edit flags'), 'mod/flags.html', array('board'=>$board,'banners'=>$banners,'token'=>make_secure_link_token('banners/'.$board['uri']))); - }; - - $config['mod']['custom_pages']['/banners/(\%b)'] = function($b) { - global $config, $mod, $board; - require_once 'inc/image.php'; - - if (!hasPermission($config['mod']['edit_banners'], $b)) - error($config['error']['noaccess']); - - if (!openBoard($b)) - error("Could not open board!"); - - $dir = 'static/banners/'.$b; - - if (!is_dir($dir)){ - mkdir($dir, 0777, true); - } - - - if (isset($_FILES['file'])){ - $upload = $_FILES['file']['tmp_name']; - $banners = array_diff(scandir($dir), array('..', '.')); - - if (!is_readable($upload)) - error($config['error']['nomove']); - - $id = time() . substr(microtime(), 2, 3); - $extension = strtolower(mb_substr($_FILES['file']['name'], mb_strrpos($_FILES['file']['name'], '.') + 1)); - - if (!in_array($extension, array('jpg','jpeg','png','gif'))){ - error('Not an image extension.'); - } - - if (filesize($upload) > 512000){ - error('File too large!'); - } - - if (!$size = @getimagesize($upload)) { - error($config['error']['invalidimg']); - } - - if ($size[0] != 300 or $size[1] != 100){ - error('Image wrong size!'); - } - if (sizeof($banners) >= 50) { - error('Too many banners.'); - } - - copy($upload, "$dir/$id.$extension"); - } - - if (isset($_POST['delete'])){ - foreach ($_POST['delete'] as $i => $d){ - if (!preg_match('/[0-9+]\.(png|jpeg|jpg|gif)/', $d)){ - error('Nice try.'); - } - unlink("$dir/$d"); - } - } - - $banners = array_diff(scandir($dir), array('..', '.')); - mod_page(_('Edit banners'), 'mod/banners.html', array('board'=>$board,'banners'=>$banners,'token'=>make_secure_link_token('banners/'.$board['uri']))); - - }; - - $config['mod']['custom_pages']['/settings/(\%b)'] = function($b) { - global $config, $mod; - - //if ($b === 'infinity' && $mod['type'] !== ADMIN) - // error('Settings temporarily disabled for this board.'); - - if (!in_array($b, $mod['boards']) and $mod['boards'][0] != '*') - error($config['error']['noaccess']); - - if (!hasPermission($config['mod']['edit_settings'], $b)) - error($config['error']['noaccess']); - - if (!openBoard($b)) - error("Could not open board!"); - - $possible_languages = array_diff(scandir('inc/locale/'), array('..', '.', '.tx', 'README.md')); - - if ($_SERVER['REQUEST_METHOD'] == 'POST') { - $title = $_POST['title']; - $subtitle = $_POST['subtitle']; - $country_flags = isset($_POST['country_flags']) ? 'true' : 'false'; - $field_disable_name = isset($_POST['field_disable_name']) ? 'true' : 'false'; - $enable_embedding = isset($_POST['enable_embedding']) ? 'true' : 'false'; - $force_image_op = isset($_POST['force_image_op']) ? 'true' : 'false'; - $disable_images = isset($_POST['disable_images']) ? 'true' : 'false'; - $poster_ids = isset($_POST['poster_ids']) ? 'true' : 'false'; - $show_sages = isset($_POST['show_sages']) ? 'true' : 'false'; - $auto_unicode = isset($_POST['auto_unicode']) ? 'true' : 'false'; - $strip_combining_chars = isset($_POST['strip_combining_chars']) ? 'true' : 'false'; - $allow_roll = isset($_POST['allow_roll']) ? 'true' : 'false'; - $image_reject_repost = isset($_POST['image_reject_repost']) ? 'true' : 'false'; - $image_reject_repost_in_thread = isset($_POST['image_reject_repost_in_thread']) ? 'true' : 'false'; - $early_404 = isset($_POST['early_404']) ? 'true' : 'false'; - $allow_delete = isset($_POST['allow_delete']) ? 'true' : 'false'; - $allow_flash = isset($_POST['allow_flash']) ? '$config[\'allowed_ext_files\'][] = \'swf\';' : ''; - $allow_pdf = isset($_POST['allow_pdf']) ? '$config[\'allowed_ext_files\'][] = \'pdf\';' : ''; - $code_tags = isset($_POST['code_tags']) ? '$config[\'additional_javascript\'][] = \'js/code_tags/run_prettify.js\';$config[\'markup\'][] = array("/\[code\](.+?)\[\/code\]/ms", "
\$1
");' : ''; - $katex = isset($_POST['katex']) ? '$config[\'katex\'] = true;$config[\'additional_javascript\'][] = \'js/katex/katex.min.js\'; $config[\'markup\'][] = array("/\[tex\](.+?)\[\/tex\]/ms", "\$1"); $config[\'additional_javascript\'][] = \'js/katex-enable.js\';' : ''; - $user_flags = isset($_POST['user_flags']) ? "if (file_exists('$b/flags.php')) { include 'flags.php'; }\n" : ''; - $captcha = isset($_POST['captcha']) ? 'true' : 'false'; - $force_subject_op = isset($_POST['force_subject_op']) ? 'true' : 'false'; - $force_flag = isset($_POST['force_flag']) ? 'true' : 'false'; - $tor_posting = isset($_POST['tor_posting']) ? 'true' : 'false'; - $new_thread_capt = isset($_POST['new_thread_capt']) ? 'true' : 'false'; - $oekaki = isset($_POST['oekaki']) ? 'true' : 'false'; - - if ($_POST['locale'] !== 'en' && in_array($_POST['locale'], $possible_languages)) { - $locale = "\$config['locale'] = '{$_POST['locale']}.UTF-8';"; - } else { - $locale = ''; - } - - if (isset($_POST['max_images']) && (int)$_POST['max_images'] && (int)$_POST['max_images'] <= 5) { - $_POST['max_images'] = (int)$_POST['max_images']; - $multiimage = "\$config['max_images'] = {$_POST['max_images']}; - \$config['additional_javascript'][] = 'js/multi-image.js';"; - } else { - $multiimage = ''; - } - - $anonymous = base64_encode($_POST['anonymous']); - $blotter = base64_encode(purify_html(html_entity_decode($_POST['blotter']))); - $add_to_config = @file_get_contents($b.'/extra_config.php'); - $replace = ''; - - if (isset($_POST['replace'])) { - if (sizeof($_POST['replace']) > 200 || sizeof($_POST['with']) > 200) { - error(_('Sorry, max 200 wordfilters allowed.')); - } - if (count($_POST['replace']) == count($_POST['with'])) { - foreach ($_POST['replace'] as $i => $r ) { - if ($r !== '') { - $w = $_POST['with'][$i]; - - if (strlen($w) > 255) { - error(sprintf(_('Sorry, %s is too long. Max replacement is 255 characters'), utf8tohtml($w))); - } - - $replace .= '$config[\'wordfilters\'][] = array(base64_decode(\'' . base64_encode($r) . '\'), base64_decode(\'' . base64_encode($w) . '\'));'; - } - } - } - if (is_billion_laughs($_POST['replace'], $_POST['with'])) { - error(_('Wordfilters may not wordfilter previous wordfilters. For example, if a filters to bb and b filters to cc, that is not allowed.')); - } - } - - if (isset($_POST['hour_max_threads']) && in_array($_POST['hour_max_threads'], ['10', '25', '50', '100'])) { - $hour_max_threads = $_POST['hour_max_threads']; - } else { - $hour_max_threads = 'false'; - } - - if (isset($_POST['max_pages'])) { - $mp = (int)$_POST['max_pages']; - if ($mp > 25 || $mp < 2) { - $max_pages = 15; - } else { - $max_pages = $mp; - } - } else { - $max_pages = 15; - } - - if (isset($_POST['reply_limit'])) { - $rl = (int)$_POST['reply_limit']; - if ($rl > 750 || $rl < 250 || $rl % 25) { - $reply_limit = 250; - } else { - $reply_limit = $rl; - } - } else { - $reply_limit = 250; - } - - if (isset($_POST['max_newlines'])) { - $mn = (int)$_POST['max_newlines']; - if ($mn < 20 || $mn > 300) { - $max_newlines = 0; - } else { - $max_newlines = $mn; - } - } else { - $max_newlines = $mn; - } - - if (!(strlen($title) < 40)) - error('Invalid title'); - if (!(strlen($subtitle) < 200)) - error('Invalid subtitle'); - - $query = prepare('UPDATE ``boards`` SET `title` = :title, `subtitle` = :subtitle, `indexed` = :indexed, `public_bans` = :public_bans, `public_logs` = :public_logs, `8archive` = :8archive WHERE `uri` = :uri'); - $query->bindValue(':title', $title); - $query->bindValue(':subtitle', $subtitle); - $query->bindValue(':uri', $b); - $query->bindValue(':indexed', !isset($_POST['meta_noindex'])); - $query->bindValue(':public_bans', isset($_POST['public_bans'])); - $query->bindValue(':public_logs', (int)$_POST['public_logs']); - $query->bindValue(':8archive', isset($_POST['8archive'])); - $query->execute() or error(db_error($query)); - - $config_file = <<fetchAll(PDO::FETCH_ASSOC))); - file_write($b.'/config.php', $config_file); - file_write('stylesheets/board/'.$b.'.css', $clean_css); - - $_config = $config; - unset($config['wordfilters']); - - // 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(str_replace('flags.php', "$b/flags.php", preg_replace('/^\<\?php$/m', '', $config_file))); - // czaks: maybe reconsider using it, now that config is cached? - - // Clean the cache - if ($config['cache']['enabled']) { - cache::delete('board_' . $board['uri']); - cache::delete('all_boards'); - - cache::delete('config_' . $board['uri']); - cache::delete('events_' . $board['uri']); - - unlink('tmp/cache/locale_' . $board['uri']); - } - - // be smarter about rebuilds...only some changes really require us to rebuild all threads - if ($_config['captcha']['enabled'] != $config['captcha']['enabled'] - || $_config['new_thread_capt'] != $config['new_thread_capt'] /*New thread captcha - if toggling "enable captcha" requires this, toggling new thread capt does too, I guess.*/ - || $_config['captcha']['extra'] != $config['captcha']['extra'] - || $_config['blotter'] != $config['blotter'] - || $_config['field_disable_name'] != $config['field_disable_name'] - || $_config['show_sages'] != (isset($config['show_sages']) && $config['show_sages'])) { - buildIndex(); - $query = query(sprintf("SELECT `id` FROM ``posts_%s`` WHERE `thread` IS NULL", $b)) or error(db_error()); - while ($post = $query->fetch(PDO::FETCH_ASSOC)) { - buildThread($post['id']); - } - } - - modLog('Edited board settings', $b); - } - - $query = prepare('SELECT * FROM boards WHERE uri = :board'); - $query->bindValue(':board', $b); - $query->execute() or error(db_error($query)); - $board = $query->fetchAll()[0]; - - $css = @file_get_contents('stylesheets/board/' . $board['uri'] . '.css'); - - mod_page(_('Board configuration'), 'mod/settings.html', array('board'=>$board, 'css'=>prettify_textarea($css), 'token'=>make_secure_link_token('settings/'.$board['uri']), 'languages'=>$possible_languages,'allowed_urls'=>$config['allowed_offsite_urls'])); - }; + // Mod pages code now resides in 8chan-mod-pages-functions.php file + $config['mod']['custom_pages']['/tags/(\%b)'] = '8_tags'; + $config['mod']['custom_pages']['/reassign/(\%b)'] = '8_reassign'; + $config['mod']['custom_pages']['/volunteers/(\%b)'] = '8_volunteers'; + $config['mod']['custom_pages']['/flags/(\%b)'] = '8_flags'; + $config['mod']['custom_pages']['/banners/(\%b)'] = '8_banners'; + $config['mod']['custom_pages']['/settings/(\%b)'] = '8_settings'; diff --git a/inc/instance-config.php b/inc/instance-config.php index bc8d63da..152bb9d4 100644 --- a/inc/instance-config.php +++ b/inc/instance-config.php @@ -8,7 +8,7 @@ * You can copy values from config.php (defaults) and paste them here. */ require_once "lib/htmlpurifier-4.6.0/library/HTMLPurifier.auto.php"; - require_once "8chan-functions.php"; + require_once "instance-functions.php"; // Note - you may want to change some of these in secrets.php instead of here // See the secrets.example.php file @@ -177,19 +177,7 @@ $config['hour_max_threads'] = 10; $config['filters'][] = array( 'condition' => array( - 'custom' => function($post) { - global $config, $board; - if (!$config['hour_max_threads']) return false; - - if ($post['op']) { - $query = prepare(sprintf('SELECT COUNT(*) AS `count` FROM ``posts_%s`` WHERE `thread` IS NULL AND FROM_UNIXTIME(`time`) > DATE_SUB(NOW(), INTERVAL 1 HOUR);', $board['uri'])); - $query->bindValue(':ip', $_SERVER['REMOTE_ADDR']); - $query->execute() or error(db_error($query)); - $r = $query->fetch(PDO::FETCH_ASSOC); - - return ($r['count'] > $config['hour_max_threads']); - } - } + 'custom' => 'test_posts_per_hour' ), 'action' => 'reject', 'message' => 'On this board, to prevent raids the number of threads that can be created per hour is limited. Please try again later, or post in an existing thread.' @@ -212,6 +200,7 @@ $config['enable_antibot'] = false; $config['spam']['unicode'] = false; $config['twig_cache'] = false; $config['report_captcha'] = true; + // 8chan specific mod pages require '8chan-mod-pages.php'; diff --git a/inc/instance-functions.php b/inc/instance-functions.php new file mode 100644 index 00000000..f291714f --- /dev/null +++ b/inc/instance-functions.php @@ -0,0 +1,17 @@ + DATE_SUB(NOW(), INTERVAL 1 HOUR);', $board[$ + $query->bindValue(':ip', $_SERVER['REMOTE_ADDR']); + $query->execute() or error(db_error($query)); + $r = $query->fetch(PDO::FETCH_ASSOC); + + return ($r['count'] > $config['hour_max_threads']); + } +}