diff --git a/db/captchas.js b/db/captchas.js index a8086a4d..37efabf8 100644 --- a/db/captchas.js +++ b/db/captchas.js @@ -30,7 +30,7 @@ module.exports = { { $sample: { size: 1 } } - ]).toArray().then(res => res[0]); + ]).toArray(); }, deleteAll: () => { diff --git a/gulp/res/css/style.css b/gulp/res/css/style.css index 7e464513..f9ee6247 100644 --- a/gulp/res/css/style.css +++ b/gulp/res/css/style.css @@ -1,3 +1,7 @@ +table, img, .label, .pages, .catalog-tile, .post-container, .stickynav, .toggle-summary, .postform-style, input[type="button"], input[type="file"], input[type="number"], input[type="password"], input[type="range"], input[type="submit"], input[type="text"], select, textarea { + border-radius: 2px!important; +} + body { font-family: arial, helvetica, sans-serif; font-size: 10pt; @@ -488,6 +492,10 @@ td, th { align-items: center; } +.f1 { + flex: 1; +} + .stickynav { bottom: 5px; right: 5px; @@ -1069,7 +1077,7 @@ iframe.captcha, iframe.bypass { } iframe.bypass { - height: var(--bypass-height); /*probs needs bigger for google captcha*/ + height: var(--bypass-height); width: 450px; } @@ -1301,6 +1309,10 @@ row.wrap.sb .col { max-width: 150px; } + iframe.bypass { + height: 100vh; + } + .file-thumb { max-width: 128px; max-height: 128px; diff --git a/helpers/captcha/generators/grid.js b/helpers/captcha/generators/grid.js index 81a35e8f..f8b1c3bd 100644 --- a/helpers/captcha/generators/grid.js +++ b/helpers/captcha/generators/grid.js @@ -7,12 +7,15 @@ const gm = require('gm').subClass({ imageMagick: true }) , randomRange = async (min, max) => { if (max <= min) return min; const mod = max - min + 1; - const div = (((0xffffffff - (mod-1)) / mod) | 0) + 1; - let g + const div = Math.ceil(Math.log2(mod)); + const numberBytes = Math.ceil(div / 4); + const mask = (1 << div) - 1; + let rand; do { - g = (await randomBytes(4)).readUInt32LE(); - } while (g > div * mod - 1); - return ((g / div) | 0) + min; + rand = (await randomBytes(numberBytes)).readUIntBE(0, numberBytes); + rand = rand & mask; + } while (rand > mod); + return rand + min; } , padding = 30 , width = captchaOptions.grid.imageSize+padding @@ -44,8 +47,8 @@ module.exports = async () => { , divEnd = (div*(i+1)); const originx = await randomRange(divStart, divEnd) , originy = await randomRange(0,height); - const destx = await randomRange(Math.max(captchaOptions.distortion,originx-captchaOptions.distortion),Math.min(width-captchaOptions.distortion,originx+captchaOptions.distortion)) - , desty = await randomRange(Math.max(captchaOptions.distortion,originy-captchaOptions.distortion*2),Math.min(height-captchaOptions.distortion,originy+captchaOptions.distortion*2)); + const destx = await randomRange(Math.max(captchaOptions.distortion,originx-captchaOptions.distortion),Math.min(width-captchaOptions.distortion,originx+captchaOptions.distortion)).catch(e => console.error(e)) + , desty = await randomRange(Math.max(captchaOptions.distortion,originy-captchaOptions.distortion*2),Math.min(height-captchaOptions.distortion,originy+captchaOptions.distortion*2)).catch(e => console.error(e)); distorts.push([ {x:originx,y:originy}, {x:destx,y:desty} @@ -53,39 +56,41 @@ module.exports = async () => { } } - return new Promise(async(resolve, reject) => { - const captcha = gm(width,height,'#ffffff') - .fill('#000000') - .font(__dirname+'/../font.ttf'); + const captcha = gm(width,height,'#ffffff') + .fill('#000000') + .font(__dirname+'/../font.ttf'); - const spaceSize = (width-padding)/gridSize; - for(let j = 0; j < gridSize; j++) { - let cxOffset = await randomRange(0, spaceSize*1.5); - for(let i = 0; i < gridSize; i++) { - const index = (j*gridSize)+i; - const cyOffset = await randomRange(0, captchaOptions.grid.iconYOffset); - const charIndex = await randomRange(0, ones.length-1); - const character = (boolArray[index] ? ones : zeros)[charIndex]; - captcha.fontSize((await randomRange(20,30))) - captcha.drawText( - spaceSize*(i)+cxOffset, - spaceSize*(j+1)+cyOffset, - character - ); - } + const spaceSize = (width-padding)/gridSize; + for(let j = 0; j < gridSize; j++) { + let cxOffset = await randomRange(0, spaceSize*1.5); + for(let i = 0; i < gridSize; i++) { + const index = (j*gridSize)+i; + const cyOffset = await randomRange(0, captchaOptions.grid.iconYOffset); + const charIndex = await randomRange(0, ones.length-1); + const character = (boolArray[index] ? ones : zeros)[charIndex]; + captcha.fontSize((await randomRange(20,30))) + captcha.drawText( + spaceSize*(i)+cxOffset, + spaceSize*(j+1)+cyOffset, + character + ); } + } - if (captchaOptions.distortion > 0) { - captcha.distort(distorts, 'Shepards'); - } + if (captchaOptions.distortion > 0) { + captcha.distort(distorts, 'Shepards'); + } + + captcha + .edge(25); + return new Promise((resolve, reject) => { captcha - .edge(25) .write(`${uploadDirectory}/captcha/${captchaId}.jpg`, (err) => { if (err) { return reject(err); } - return resolve({ id: captchaId }); + return resolve({ captchaId }); }); }); diff --git a/helpers/captcha/generators/text.js b/helpers/captcha/generators/text.js index c7b6d6c8..e039866b 100644 --- a/helpers/captcha/generators/text.js +++ b/helpers/captcha/generators/text.js @@ -31,16 +31,19 @@ const gm = require('gm').subClass({ imageMagick: true }) , minVal = parseInt('1000000', 36) , maxVal = parseInt('1zzzzzz', 36); -const randomRange = async (min, max) => { +const randomRange = async (min, max) => { if (max <= min) return min; const mod = max - min + 1; - const div = (((0xffffffff - (mod-1)) / mod) | 0) + 1; - let g + const div = Math.ceil(Math.log2(mod)); + const numberBytes = Math.ceil(div / 4); + const mask = (1 << div) - 1; + let rand; do { - g = (await randomBytes(4)).readUInt32LE(); - } while (g > div * mod - 1); - return ((g / div) | 0) + min; -}; + rand = (await randomBytes(numberBytes)).readUIntBE(0, numberBytes); + rand = rand & mask; + } while (rand > mod); + return rand + min; +} module.exports = async () => { // generate between 1000000 and 1zzzzzz and not 0 and zzzzzz, so toString @@ -86,7 +89,7 @@ module.exports = async () => { if (err) { return reject(err); } - return resolve({ id: captchaId }); + return resolve({ captchaId }); }); }); diff --git a/models/pages/captcha.js b/models/pages/captcha.js index 7bbea1d8..23d20ef2 100644 --- a/models/pages/captcha.js +++ b/models/pages/captcha.js @@ -21,15 +21,16 @@ module.exports = async (req, res, next) => { } } let id; - if ((await Captchas.db.estimatedDocumentCount()) >= captchaOptions.generateLimit) { + const captchaCount = await Captchas.db.estimatedDocumentCount(); + if (captchaCount >= captchaOptions.generateLimit) { //TODOs: round robin sample? store in redis? only sample random with longer than x expiry? - const randomCaptcha = await Captchas.randomSample(); - id = randomCaptcha._id; + const captchaSample = await Captchas.randomSample(); + const randomCaptcha = captchaSample[0]; + captchaId = randomCaptcha._id; maxAge = Math.abs((randomCaptcha.expireAt.getTime()+maxAge) - Date.now()); //abs in case mongo hasn't pruned, and will not be too big since it can't be too far away from pruning anyway } else { - ({ id } = await generateCaptcha()); + ({ captchaId } = await generateCaptcha()); } - captchaId = id; } catch (err) { return next(err); } diff --git a/views/mixins/modal.pug b/views/mixins/modal.pug index 41774346..dcff1785 100644 --- a/views/mixins/modal.pug +++ b/views/mixins/modal.pug @@ -18,7 +18,7 @@ mixin modal(data) each error in data.errors li #{error} if data.frame - .row + .row.f1 iframe.bypass#modalframe(src=data.frame frameborder='0' scrolling='no') else if data.link .row