Prevent delpass bruteforcing
This commit is contained in:
parent
9822874d3d
commit
b16b6a7362
|
@ -0,0 +1,4 @@
|
|||
ALTER TABLE `posts`
|
||||
ADD COLUMN `attempts` TINYINT(3) UNSIGNED NULL DEFAULT '0' AFTER `by_new_user`;
|
||||
|
||||
UPDATE `posts` SET `attempts`=0 WHERE 1;
|
156
board.php
156
board.php
|
@ -83,45 +83,47 @@ function notify($room, $data=array()) {
|
|||
}
|
||||
|
||||
class PolymorphicReporter {
|
||||
function __construct($itemtype, $id, $is_ajax) {
|
||||
$this->itemtype = $itemtype;
|
||||
$this->id = $id;
|
||||
$this->is_ajax = $is_ajax;
|
||||
}
|
||||
function __construct($itemtype, $id, $is_ajax) {
|
||||
$this->itemtype = $itemtype;
|
||||
$this->id = $id;
|
||||
$this->is_ajax = $is_ajax;
|
||||
}
|
||||
|
||||
function fail($msg="") {
|
||||
if ($this->is_ajax) {
|
||||
$this->success = false;
|
||||
$this->message = $msg;
|
||||
}
|
||||
else
|
||||
echo $this->itemtype . ' #' . $this->id . ': ' . $msg . '<br />';
|
||||
}
|
||||
function fail($msg="", $special_error=false) {
|
||||
$this->special_error = $special_error;
|
||||
if ($this->is_ajax) {
|
||||
$this->success = false;
|
||||
$this->message = $msg;
|
||||
}
|
||||
else
|
||||
echo $this->itemtype . ' #' . $this->id . ': ' . $msg . '<br />';
|
||||
}
|
||||
|
||||
function succ($msg="") {
|
||||
if ($this->is_ajax) {
|
||||
$this->success = true;
|
||||
$this->message = $msg;
|
||||
}
|
||||
else
|
||||
echo $this->itemtype . ' #' . $this->id . ': ' . $msg . '<br />';
|
||||
}
|
||||
function succ($msg="") {
|
||||
if ($this->is_ajax) {
|
||||
$this->success = true;
|
||||
$this->message = $msg;
|
||||
}
|
||||
else
|
||||
echo $this->itemtype . ' #' . $this->id . ': ' . $msg . '<br />';
|
||||
}
|
||||
|
||||
function report() {
|
||||
return array(
|
||||
'id' => $this->id,
|
||||
'itemtype' => $this->itemtype,
|
||||
'action' => $this->action,
|
||||
'success' => $this->success,
|
||||
'message' => $this->message
|
||||
);
|
||||
}
|
||||
function report() {
|
||||
return array(
|
||||
'id' => $this->id,
|
||||
'itemtype' => $this->itemtype,
|
||||
'action' => $this->action,
|
||||
'success' => $this->success,
|
||||
'message' => $this->message,
|
||||
'special_error' => $this->special_error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function error_redirect($url, $message) {
|
||||
if ($_POST['AJAX']) {
|
||||
exit(json_encode(array(
|
||||
'error' => $message
|
||||
'error' => $message
|
||||
)));
|
||||
}
|
||||
else {
|
||||
|
@ -159,8 +161,8 @@ $posting_class = new Posting();
|
|||
$ban_result = $bans_class->BanCheck($posting_class->user_id, $board_class->board['name']);
|
||||
if ($ban_result && is_array($ban_result) && $_POST['AJAX']) {
|
||||
exit(json_encode(array(
|
||||
'error' => _gettext('YOU ARE BANNED'),
|
||||
'error_type' => 'ban'
|
||||
'error' => _gettext('YOU ARE BANNED'),
|
||||
'error_type' => 'ban'
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -357,9 +359,9 @@ if (isset($_POST['makepost'])) { // A more evident way to identify post action,
|
|||
// First array is the converted form of the japanese characters meaning sage, second meaning age
|
||||
$ords_email = unistr_to_ords($post_email);
|
||||
if (strtolower($_POST['em']) != 'sage' && $ords_email != array(19979, 12370) && strtolower($_POST['em']) != 'age' && $ords_email != array(19978, 12370) && $_POST['em'] != 'return' && $_POST['em'] != 'noko') {
|
||||
$post['email_save'] = true;
|
||||
$post['email_save'] = true;
|
||||
} else {
|
||||
$post['email_save'] = false;
|
||||
$post['email_save'] = false;
|
||||
}
|
||||
$post['subject'] = mb_substr($post_subject, 0, KU_MAXSUBJLENGTH);
|
||||
$post['message'] = $post_message;
|
||||
|
@ -368,13 +370,13 @@ if (isset($_POST['makepost'])) { // A more evident way to identify post action,
|
|||
|
||||
// I never knew this weird shit exists in Kusaba
|
||||
if ($thread_replyto != '0') {
|
||||
if ($post['message'] == '' && KU_NOMESSAGEREPLY != '') {
|
||||
$post['message'] = KU_NOMESSAGEREPLY;
|
||||
}
|
||||
if ($post['message'] == '' && KU_NOMESSAGEREPLY != '') {
|
||||
$post['message'] = KU_NOMESSAGEREPLY;
|
||||
}
|
||||
} else {
|
||||
if ($post['message'] == '' && KU_NOMESSAGETHREAD != '') {
|
||||
$post['message'] = KU_NOMESSAGETHREAD;
|
||||
}
|
||||
if ($post['message'] == '' && KU_NOMESSAGETHREAD != '') {
|
||||
$post['message'] = KU_NOMESSAGETHREAD;
|
||||
}
|
||||
}
|
||||
|
||||
// Emoji registration
|
||||
|
@ -407,7 +409,7 @@ if (isset($_POST['makepost'])) { // A more evident way to identify post action,
|
|||
}
|
||||
// Reparse post
|
||||
if ($any_new)
|
||||
$post['message'] = $parse_class->Smileys($post['message']);
|
||||
$post['message'] = $parse_class->Smileys($post['message']);
|
||||
}
|
||||
// ← Emoji registration
|
||||
|
||||
|
@ -434,14 +436,14 @@ if (isset($_POST['makepost'])) { // A more evident way to identify post action,
|
|||
}
|
||||
|
||||
if ($user_authority > 0 && $user_authority != 3) {
|
||||
$modpost_message = 'Modposted #<a href="' . KU_BOARDSFOLDER . $board_class->board['name'] . '/res/';
|
||||
if ($post_isreply) {
|
||||
$modpost_message .= $thread_replyto;
|
||||
} else {
|
||||
$modpost_message .= $post_id;
|
||||
}
|
||||
$modpost_message .= '.html#' . $post_id . '">' . $post_id . '</a> in /'.$_POST['board'].'/ with flags: ' . $flags . '.';
|
||||
management_addlogentry($modpost_message, 1, md5_decrypt($_POST['modpassword'], KU_RANDOMSEED));
|
||||
$modpost_message = 'Modposted #<a href="' . KU_BOARDSFOLDER . $board_class->board['name'] . '/res/';
|
||||
if ($post_isreply) {
|
||||
$modpost_message .= $thread_replyto;
|
||||
} else {
|
||||
$modpost_message .= $post_id;
|
||||
}
|
||||
$modpost_message .= '.html#' . $post_id . '">' . $post_id . '</a> in /'.$_POST['board'].'/ with flags: ' . $flags . '.';
|
||||
management_addlogentry($modpost_message, 1, md5_decrypt($_POST['modpassword'], KU_RANDOMSEED));
|
||||
}
|
||||
|
||||
// Give persistent cookie
|
||||
|
@ -449,11 +451,11 @@ if (isset($_POST['makepost'])) { // A more evident way to identify post action,
|
|||
setcookie('I0_persistent_id', $posting_class->user_id, time() + 31556926, '/'/*, KU_DOMAIN*/);
|
||||
|
||||
if ($post['name_save'] && isset($_POST['name'])) {
|
||||
setcookie('name', $_POST['name'], time() + 31556926, '/', KU_DOMAIN);
|
||||
setcookie('name', $_POST['name'], time() + 31556926, '/', KU_DOMAIN);
|
||||
}
|
||||
|
||||
if ($post['email_save']) {
|
||||
setcookie('email', $post['email'], time() + 31556926, '/', KU_DOMAIN);
|
||||
setcookie('email', $post['email'], time() + 31556926, '/', KU_DOMAIN);
|
||||
}
|
||||
|
||||
setcookie('postpassword', $_POST['postpassword'], time() + 31556926, '/');
|
||||
|
@ -525,6 +527,7 @@ elseif (
|
|||
$pages_to_regenerate = array(); // single pages to regenerate
|
||||
$page_from = false;
|
||||
$page_to = false;
|
||||
$captcha_ok = false;
|
||||
|
||||
// Check rights
|
||||
$pass = (isset($_POST['postpassword']) && $_POST['postpassword']!="") ? $_POST['postpassword'] : null;
|
||||
|
@ -575,10 +578,32 @@ elseif (
|
|||
$isop = false;
|
||||
$pwd_ref_post = $post_class;
|
||||
}
|
||||
if ($pass) {
|
||||
// Determine if post is locked due to many access attempts
|
||||
$locked = !$ismod && $post_class->CheckAccessLocked();
|
||||
$unlocked = !$locked;
|
||||
if ($locked) {
|
||||
if ($captcha_ok === false) {
|
||||
if ($_POST['captcha']) {
|
||||
$captcha_ok = $posting_class->CheckCaptcha(true);
|
||||
if (!$captcha_ok) {
|
||||
$post_action->fail(_gettext('Incorrect captcha entered.'), 'captchalocked');
|
||||
}
|
||||
else {
|
||||
$unlocked = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$post_action->fail(_gettext('Insert captcha.'), 'captchalocked');
|
||||
}
|
||||
}
|
||||
else {
|
||||
$unlocked = $captcha_ok;
|
||||
}
|
||||
}
|
||||
if ($pass && $unlocked) {
|
||||
$passtype = $pwd_ref_post->post['password'] [0];
|
||||
if ($passtype == '+') { // modern hash with salt: +md5(password+postid+boardid+randomseed)
|
||||
$pass_for_this_post = '+'.md5($pass . $pwd_ref_post->post['id'] . $board_class->board['id'] . KU_RANDOMSEED);
|
||||
$pass_for_this_post = '+'.md5($pass . $pwd_ref_post->post['id'] . $board_class->board['id'] . KU_RANDOMSEED);
|
||||
}
|
||||
elseif ($passtype == '-') { // modern hash w/o salt: -md5(password+randomseed)
|
||||
if (!$passmd5_new)
|
||||
|
@ -591,7 +616,7 @@ elseif (
|
|||
$pass_for_this_post = $passmd5_old;
|
||||
}
|
||||
}
|
||||
$granted = ($ismod || ($pass && $pass_for_this_post == $pwd_ref_post->post['password']));
|
||||
$granted = $unlocked && ($ismod || ($pass && $pass_for_this_post == $pwd_ref_post->post['password']));
|
||||
if ($granted) {
|
||||
$thread_id = $post_class->post['parentid'] != '0' ? $post_class->post['parentid'] : $post_class->post['id'];
|
||||
$room_id = $board_class->board['name'].':'.$thread_id;
|
||||
|
@ -611,6 +636,9 @@ elseif (
|
|||
'by_mod' => $ismod,
|
||||
'by_op' => $isop
|
||||
);
|
||||
if ($locked) {
|
||||
$post_class->Unlock();
|
||||
}
|
||||
if (! in_array($thread_id, $threads_to_regenerate)) {
|
||||
$threads_to_regenerate []= $thread_id;
|
||||
}
|
||||
|
@ -631,7 +659,7 @@ elseif (
|
|||
if (isset($_POST['deletepost'])) {
|
||||
$post_action->action = 'delete';
|
||||
$isownpost = !$ismod && !$isop;
|
||||
$delres = $post_class->Delete(false, $isownpost && I0_ERASE_DELETED);
|
||||
$delres = $post_class->Delete(false, $isownpost && I0_ERASE_DELETED, $ismod);
|
||||
if ($delres) {
|
||||
if ($delres !== 'already_deleted') { // Skip the unneeded rebuild if the post is already deleted
|
||||
if (! isset($notifications_del[$room_id]))
|
||||
|
@ -721,7 +749,7 @@ elseif (
|
|||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
elseif ($post_action->special_error !== 'captchalocked') {
|
||||
$post_action->fail(_gettext('Incorrect password.'));
|
||||
}
|
||||
}
|
||||
|
@ -783,8 +811,8 @@ elseif (
|
|||
}
|
||||
if ($_POST['AJAX'])
|
||||
exit(json_encode(array(
|
||||
'action' => 'multi_post_action',
|
||||
'data' => $items_affected
|
||||
'action' => 'multi_post_action',
|
||||
'data' => $items_affected
|
||||
)));
|
||||
else
|
||||
do_redirect(KU_BOARDSPATH . '/' . $board_class->board['name'] . '/');
|
||||
|
@ -818,10 +846,10 @@ if( $_POST['redirecttothread'] == 1 || $_POST['em'] == 'return' || $_POST['em']
|
|||
|
||||
if ($_POST['AJAX']) {
|
||||
exit(json_encode(array(
|
||||
'error' => false,
|
||||
'action' => 'post',
|
||||
'thread_replyto' => $thread_replyto,
|
||||
'post_id' => $post_id,
|
||||
'board' => $board_class->board['name']
|
||||
'error' => false,
|
||||
'action' => 'post',
|
||||
'thread_replyto' => $thread_replyto,
|
||||
'post_id' => $post_id,
|
||||
'board' => $board_class->board['name']
|
||||
)));
|
||||
}
|
||||
|
|
|
@ -99,6 +99,7 @@ if (!$cache_loaded) {
|
|||
|
||||
$cf['I0_DELPASS_SALTING'] = true; // Whether or not the delpass should be hashed to prevent poster identification
|
||||
$cf['I0_ERASE_DELETED'] = false; // Whether or not the contents of posts deleted by user should be erased
|
||||
$cf['I0_MAX_ACCESS_ATTEMPTS'] = 3; // How many attempts at deleting a post are allowed before it gets locked with catpcha
|
||||
|
||||
$cf['I0_DETECT_SOSACH'] = false; // Detect pictures from particular website
|
||||
|
||||
|
|
|
@ -2810,6 +2810,22 @@ figure .post-menu {
|
|||
.post-menu li .icon {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.menu-captcha {
|
||||
display: -webkit-box;
|
||||
display: flex;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-box-direction: normal;
|
||||
flex-direction: column;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
.menu-captcha .captchawrap, .menu-captcha input {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.menu-captcha input {
|
||||
margin-bottom: 2px;
|
||||
width: 100%;
|
||||
}
|
||||
.select-multiple .multidel {
|
||||
display: initial;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,11 @@
|
|||
<tr><td>
|
||||
<input placeholder="{t}Password{/t}" type="password" name="postpassword" size="8" class="make-me-readonly"/>
|
||||
</td></tr>
|
||||
<noscript><tr><td><details>
|
||||
<summary>{t}Captcha{/t}</summary>
|
||||
<iframe class="captchawrap" src="{%KU_BOARDSFOLDER}nojscaptcha.php" frameborder="0" width="150" height="32" style="vertical-align: middle;"></iframe><br>
|
||||
<input type="text" name="captcha" placeholder="{t}Captcha{/t}" style="margin-top:4px" accesskey="c" style="vertical-align: middle" autocomplete="off">
|
||||
</details></td></tr></noscript>
|
||||
<tr><td>
|
||||
<input name="deletepost" value="{t}Delete post{/t}" type="submit" class="styled-button bad-button" />{if $board.opmod}<label for="opmod">(<input type="checkbox" id="opmod" name="opdelete" value="1">{t}as OP{/t})</label>
|
||||
{/if}
|
||||
|
@ -16,7 +21,6 @@
|
|||
<input name="cancel_timer" value="{t}Cancel timer{/t}" type="submit" class="styled-button" />
|
||||
</td></tr>
|
||||
</tbody></table>
|
||||
|
||||
</form>
|
||||
|
||||
<script type="text/javascript"><!--
|
||||
|
|
|
@ -70,7 +70,7 @@
|
|||
<td class="postblock"></td>
|
||||
<td><nobr class="captcharow">
|
||||
<input type="text" name="captcha" placeholder="{t}Captcha{/t}" size="28" accesskey="c" style="vertical-align: middle" autocomplete="off">
|
||||
<script>document.write('<div class="captchawrap cw-initial" title="{t}Refresh captcha{/t}"><div class="captcha-show msg">{t}Show captcha{/t}</div><img class="captchaimage" valign="middle" border="0" alt="Captcha image"><div class="rotting-indicator"></div><div class="rotten-msg msg">{t}Captcha has expired{t}.</div></div>')</script>
|
||||
<script>document.write('<div class="captchawrap cw-initial" title="{t}Refresh captcha{/t}"><div class="captcha-show msg">{t}Show captcha{/t}</div><img class="captchaimage" valign="middle" border="0" alt="{t}Captcha image{/t}"><div class="rotting-indicator"></div><div class="rotten-msg msg">{t}Captcha has expired{t}.</div></div>')</script>
|
||||
<noscript><iframe class="captchawrap" src="{%KU_BOARDSFOLDER}nojscaptcha.php" frameborder="0" width="150" height="32" style="vertical-align: middle;"></iframe></noscript>
|
||||
</nobr></td>
|
||||
</tr>
|
||||
|
@ -159,7 +159,7 @@
|
|||
{t}Password{/t}
|
||||
</td>
|
||||
<td>
|
||||
<input class="make-me-readonly" type="password" placeholder="{t}Password{/t}" name="postpassword" size="8" accesskey="p" /><div><span>{t}(for post and file deletion){/t}</span></div>
|
||||
<input class="make-me-readonly" type="password" placeholder="{t}Password{/t}" name="postpassword" size="28" accesskey="p" /><div><span>{t}(for post and file deletion){/t}</span></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="ttl-row">
|
||||
|
|
|
@ -973,7 +973,37 @@ class Post extends Board {
|
|||
`country` = '',
|
||||
`password` = ''";
|
||||
|
||||
function Delete($allow_archive = false, $erase = false) {
|
||||
function CheckAccessLocked() {
|
||||
global $tc_db;
|
||||
|
||||
$attempts = $tc_db->GetOne("SELECT `attempts`
|
||||
FROM `".KU_DBPREFIX."posts`
|
||||
WHERE
|
||||
`id` = ".$this->post['id']." AND
|
||||
`boardid` = ".$this->board['id']);
|
||||
if ((int)$attempts >= I0_MAX_ACCESS_ATTEMPTS) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
$tc_db->Execute("UPDATE `".KU_DBPREFIX."posts`
|
||||
SET `attempts` = `attempts`+1
|
||||
WHERE
|
||||
`id` = ".$this->post['id']." AND
|
||||
`boardid` = ".$this->board['id']);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function Unlock() {
|
||||
global $tc_db;
|
||||
$tc_db->Execute("UPDATE `".KU_DBPREFIX."posts`
|
||||
SET `attempts` = 0
|
||||
WHERE
|
||||
`id` = ".$this->post['id']." AND
|
||||
`boardid` = ".$this->board['id']);
|
||||
}
|
||||
|
||||
function Delete($allow_archive = false, $erase = false) {
|
||||
global $tc_db;
|
||||
if ($this->post['IS_DELETED'])
|
||||
return 'already_deleted';
|
||||
|
|
|
@ -208,22 +208,26 @@ class Posting {
|
|||
}
|
||||
}
|
||||
|
||||
function CheckCaptcha() {
|
||||
function CheckCaptcha($for_access=false) {
|
||||
global $board_class;
|
||||
mb_internal_encoding("UTF-8");
|
||||
/* If the board has captcha's enabled... */
|
||||
if ($board_class->board['enablecaptcha'] == 1) {
|
||||
if ($for_access || $board_class->board['enablecaptcha'] == 1) {
|
||||
$code = $_SESSION['security_code'];
|
||||
unset($_SESSION['security_code']);
|
||||
$submit_time = time();
|
||||
if($submit_time - $_SESSION['captchatime'] > KU_CAPTCHALIFE) {
|
||||
if ($for_access) return false;
|
||||
exitWithErrorPage(_gettext('Captcha has expired.'));
|
||||
}
|
||||
/* Check if they entered the correct code. If not... */
|
||||
if ($_SESSION['security_code'] != mb_strtoupper($_POST['captcha']) || empty($_SESSION['security_code'])) {
|
||||
if ($code != mb_strtoupper($_POST['captcha']) || empty($code)) {
|
||||
if ($for_access) return false;
|
||||
/* Kill the script, stopping the posting process */
|
||||
exitWithErrorPage(_gettext('Incorrect captcha entered.'));
|
||||
}
|
||||
}
|
||||
unset($_SESSION['security_code']);
|
||||
if ($for_access) return true;
|
||||
}
|
||||
|
||||
function CheckRecaptcha() { //just backup
|
||||
|
|
|
@ -2148,3 +2148,6 @@ msgstr "Отменить таймер"
|
|||
|
||||
msgid "as OP"
|
||||
msgstr "как ОП"
|
||||
|
||||
msgid "Insert captcha."
|
||||
msgstr "Введите капчу."
|
||||
|
|
|
@ -125,7 +125,12 @@ var _messages = {
|
|||
cancelTimer: 'Cancel timer',
|
||||
saved:'saved from deletion',
|
||||
savedMulti: 'saved from deletion',
|
||||
password: 'Password'
|
||||
password: 'Password',
|
||||
captcha: 'Captcha',
|
||||
captchaImage: 'Captcha image',
|
||||
refreshCaptcha: 'Refresh captcha',
|
||||
showCaptcha: 'Show captcha',
|
||||
captchaExpired: 'Captcha has expired.'
|
||||
},
|
||||
ru: {
|
||||
noLocalStorage: "localStorage не поддерживается браузером",
|
||||
|
@ -245,7 +250,12 @@ var _messages = {
|
|||
cancelTimer: 'Отменить таймер',
|
||||
saved:'спасен от удаления',
|
||||
savedMulti: 'спасены от удаления',
|
||||
password: 'Пароль'
|
||||
password: 'Пароль',
|
||||
captcha: 'Captcha',
|
||||
captchaImage: 'Captcha image',
|
||||
refreshCaptcha: 'Обновить капчу',
|
||||
showCaptcha: 'Показать капчу',
|
||||
captchaExpired: 'Капча протухла.'
|
||||
}
|
||||
}
|
||||
var _l = (typeof locale !== 'undefined' && _messages.hasOwnProperty(locale)) ? _messages[locale] : _messages.ru;
|
||||
|
@ -526,10 +536,12 @@ function highlight(id, offTimeout=5000) {
|
|||
return true
|
||||
}
|
||||
|
||||
const password_length = 20;
|
||||
|
||||
function get_password(name) {
|
||||
let pass = getCookie(name);
|
||||
if(pass) return pass;
|
||||
pass = randomString(8)
|
||||
pass = randomString(password_length)
|
||||
Cookie(name, pass, 365);
|
||||
return(pass);
|
||||
}
|
||||
|
@ -1064,6 +1076,7 @@ function clonePostForm(anchorID, preview) {
|
|||
newForm.dataset.anchorID = anchorID
|
||||
newForm.classList.remove('main-reply-form')
|
||||
newForm.querySelector('.simplified-send-row .primary span').innerText = _l.reply
|
||||
newForm.querySelector('input[name="postpassword"]').value = get_password('postpassword')
|
||||
newForm.querySelector('input.primary').value = _l.reply
|
||||
let blotter = newForm.querySelector('.blotter-row')
|
||||
if (preview)
|
||||
|
@ -1291,9 +1304,9 @@ function popupMessage(content, delay=1000) {
|
|||
}
|
||||
|
||||
var Captcha = {
|
||||
init: function() {
|
||||
init: function(forceEnable=false) {
|
||||
let captchaImage = document.querySelector('.captchaimage')
|
||||
this.enabled = !!captchaImage
|
||||
this.enabled = forceEnable || !!captchaImage
|
||||
if (!this.enabled) return;
|
||||
injector.inject('captcha-rotting',
|
||||
`.cw-running .rotting-indicator {
|
||||
|
@ -1304,16 +1317,25 @@ var Captcha = {
|
|||
.cw-running .rotten-msg {
|
||||
-webkit-animation-delay: ${captchaTimeout}s;
|
||||
animation-delay: ${captchaTimeout}s;}`)
|
||||
captchaImage.onload = this.onImageLoad.bind(this)
|
||||
;['animationend', 'webkitAnimationEnd', 'msAnimationEnd'].forEach(evType => {
|
||||
captchaImage.addEventListener(evType, this.onAnimationEnd.bind(this))
|
||||
})
|
||||
if (captchaImage) {
|
||||
this.addImgLoadListener(captchaImage)
|
||||
}
|
||||
},
|
||||
initForm: function(form) {
|
||||
form.querySelector('.captchawrap').onclick = this.onClick.bind(this)
|
||||
;['click', 'focus'].forEach(evt => {
|
||||
form.querySelector('input[name=captcha]').addEventListener(evt, this.onFieldClick.bind(this))
|
||||
})
|
||||
let captchaImage = form.querySelector('.captchaimage')
|
||||
if (captchaImage) {
|
||||
this.addImgLoadListener(captchaImage)
|
||||
}
|
||||
},
|
||||
addImgLoadListener: function(captchaImage) {
|
||||
captchaImage.onload = this.onImageLoad.bind(this)
|
||||
;['animationend', 'webkitAnimationEnd', 'msAnimationEnd'].forEach(evType => {
|
||||
captchaImage.addEventListener(evType, this.onAnimationEnd.bind(this))
|
||||
})
|
||||
},
|
||||
_state: 'init',
|
||||
get state() {
|
||||
|
@ -1961,6 +1983,32 @@ function makeIcon(i, classes="", bare=false) {
|
|||
${bare ? '' : '</svg>'}`
|
||||
}
|
||||
|
||||
function addCaptchaToMenu($menu) {
|
||||
if ($menu.find('.captchawrap').length) {
|
||||
Captcha.state = 'init';
|
||||
return;
|
||||
}
|
||||
if (!Captcha.enabled) {
|
||||
Captcha.init('only-access')
|
||||
}
|
||||
$menu.prepend(`<li class="menu-captcha">
|
||||
<div class="captchawrap cw-initial captchaimage-invisible" title="${_l.refreshCaptcha}">
|
||||
<div class="captcha-show msg">${_l.showCaptcha}</div>
|
||||
<img class="captchaimage" valign="middle" border="0" alt="${_l.captchaImage}">
|
||||
<div class="rotting-indicator"></div>
|
||||
<div class="rotten-msg msg">${_l.captchaExpired}</div>
|
||||
</div>
|
||||
<input type="text" name="captcha" placeholder="${_l.captcha}" autocomplete="off">
|
||||
</li>`)
|
||||
Captcha.initForm($menu[0])
|
||||
$menu.find('input[name=captcha]').keydown(ev => {
|
||||
if (ev.key == 'Enter') {
|
||||
ev.preventDefault()
|
||||
ev.stopPropagation()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function expandimg(postnum, imgurl, thumburl, imgw, imgh, thumbw, thumbh) {
|
||||
let element = document.getElementById("thumb" + postnum);
|
||||
if (element == null) return false;
|
||||
|
@ -2632,6 +2680,16 @@ function readyset() {
|
|||
, $pass = $menu.find('.menu-password')
|
||||
$this.addClass('spin-around')
|
||||
let fd = new FormData()
|
||||
if ($menu.find('.menu-captcha').length) {
|
||||
let $c = $menu.find('input[name=captcha]')
|
||||
if (!$c.val()) {
|
||||
$c.focus()
|
||||
pups.warn(_l.enterCaptcha)
|
||||
$this.removeClass('spin-around')
|
||||
return;
|
||||
}
|
||||
fd.append('captcha', $c.val())
|
||||
}
|
||||
if (isFile)
|
||||
fd.append('delete-file[]', menu.__menuProps.fileid)
|
||||
else
|
||||
|
@ -2655,7 +2713,18 @@ function readyset() {
|
|||
$menu.prepend(`<li class="menu-password">${makeIcon('password')}
|
||||
<input value="${get_password("postpassword")}" class="make-me-readonly" type="password" placeholder="${_l.password}" readonly onfocus="this.readOnly=false"></li>`)
|
||||
}
|
||||
$menu.find('.menu-password input').select()
|
||||
if (result.special_error && result.special_error == 'captchalocked') {
|
||||
addCaptchaToMenu($menu)
|
||||
}
|
||||
else {
|
||||
$menu.find('.menu-password input').select()
|
||||
.keydown(ev => {
|
||||
if (ev.key == 'Enter') {
|
||||
ev.preventDefault()
|
||||
ev.stopPropagation()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
else if (isFile)
|
||||
$('.file-menu').hide()
|
||||
|
@ -2691,6 +2760,16 @@ function readyset() {
|
|||
, $postnode = $this.parents('.postnode')
|
||||
$this.addClass('spin-around')
|
||||
let fd = new FormData()
|
||||
if ($menu.find('.menu-captcha').length) {
|
||||
let $c = $menu.find('input[name=captcha]')
|
||||
if (!$c.val()) {
|
||||
$c.focus()
|
||||
pups.warn(_l.enterCaptcha)
|
||||
$this.removeClass('spin-around')
|
||||
return;
|
||||
}
|
||||
fd.append('captcha', $c.val())
|
||||
}
|
||||
fd.append('post[]', $postnode.data('id'))
|
||||
fd.append('board', $postnode.data('board'))
|
||||
fd.append('modsave', $this.hasClass('menu-delete-mod'))
|
||||
|
@ -2701,11 +2780,26 @@ function readyset() {
|
|||
Ajax.cancelTimer(fd, errors => {
|
||||
$this.removeClass('spin-around')
|
||||
if (errors.length) {
|
||||
let result = errors[0]
|
||||
if (!$pass.length) {
|
||||
$menu.prepend(`<li class="menu-password">${makeIcon('password')}
|
||||
<input value="${get_password("postpassword")}" class="make-me-readonly" type="password" placeholder="${_l.password}" readonly onfocus="this.readOnly=false"></li>`)
|
||||
}
|
||||
$menu.find('.menu-password input').select()
|
||||
if (result.special_error && result.special_error == 'captchalocked') {
|
||||
addCaptchaToMenu($menu)
|
||||
}
|
||||
else {
|
||||
$menu.find('.menu-password input').select()
|
||||
.keydown(ev => {
|
||||
if (ev.key == 'Enter') {
|
||||
ev.preventDefault()
|
||||
ev.stopPropagation()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
else {
|
||||
$menu.find('.menu-password, .menu-captcha').remove()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -3953,7 +4047,7 @@ function initForm(form) {
|
|||
form.querySelector('textarea').id = areaID
|
||||
form.querySelector('.uib-tx').dataset.target = areaID
|
||||
// captcha
|
||||
if (Captcha.enabled)
|
||||
if (Captcha.enabled && Captcha.enabled!=='only-access')
|
||||
Captcha.initForm(form)
|
||||
// readonly stuff
|
||||
form.querySelectorAll('.make-me-readonly').forEach(ro => {
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue