From 43c126ecf8b4c7ce23a1d883993f2e62016a320f Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Tue, 21 Jun 2022 11:04:36 +0200 Subject: final adjustments before the release This commit: * adds handling of token expiration, * adds links to Google's Privacy Policy and Terms and * includes some other minor fixes adn adjustments. --- captcha-child-bframe.js | 29 ++++++++++++-- captcha-demo.js | 24 ++++++++---- captcha-parent.js | 101 ++++++++++++++++++++++++++++++++---------------- index.json | 10 ++--- 4 files changed, 114 insertions(+), 50 deletions(-) diff --git a/captcha-child-bframe.js b/captcha-child-bframe.js index 7f595c4..0a5b20d 100644 --- a/captcha-child-bframe.js +++ b/captcha-child-bframe.js @@ -58,6 +58,11 @@ const replacement_markup = ` background-color: white; } + h4 { + margin-bottom: 10px; + margin-top: 10px; + } + #tiles_table, #tiles_body td { border: 1px solid white; padding: 0; @@ -94,6 +99,12 @@ const replacement_markup = ` +
+ Google policies: + privacy + & + terms +

Loading Hacktcha - a libre client for reCAPTCHA. @@ -103,7 +114,7 @@ const replacement_markup = `

Challenge timed out.

- +

@@ -596,6 +607,8 @@ challenge_handlers.doscaptcha = function(api_helper, pmeta) { challenge_handlers.default = challenge_handlers.doscaptcha; +challenge_handlers.null = challenge_handlers.doscaptcha; + async function handle_challenge(api_helper, rresp) { const pmeta = rresp[4]; const challenge_type = rresp[5]; @@ -674,8 +687,16 @@ async function get_final_token(site_key, rc_version, token) { show_main_view("main_timed_out"); - const resume_but = document.getElementById("resume_but"); - await new Promise(cb => resume_but.onclick = cb); + /* + * Resuming after timeout does not work, so we allow the user to reload the + * iframe instead. + * TODO: fix resuming + */ + + /* const resume_but = document.getElementById("resume_but"); */ + /* await new Promise(cb => resume_but.onclick = cb); */ + + return new Promise(() => {}); } else if (e.hacktcha_error === "challenge_blocked") { show_main_view("main_challenge_blocked"); @@ -702,7 +723,7 @@ async function main() { document.close(); for (const but of document.getElementsByClassName("retry_but")) - but.onclick = () => window.postMessage("recreate_frame", origin); + but.onclick = () => window.parent.postMessage("recreate_frame", origin); const final_token = await get_final_token(site_key, rc_version, initial_token); diff --git a/captcha-demo.js b/captcha-demo.js index 28d331d..9c1ad55 100644 --- a/captcha-demo.js +++ b/captcha-demo.js @@ -22,19 +22,27 @@ if (document.getElementsByClassName("recaptcha-success").length > 0) return; - const demo_div = document.getElementById("recaptcha-demo"); + const demo_submit = document.getElementById("recaptcha-demo-submit"); + const demo_div = document.getElementById("recaptcha-demo"); + const token_field = document.createElement("textarea"); const demo_site_key = demo_div.getAttribute("data-sitekey"); + token_field.name = "g-recaptcha-response"; + token_field.style.display = "none"; + while (demo_div.nextElementSibling !== null) demo_div.nextElementSibling.remove(); - const token = await HCHA.run(demo_div, demo_site_key); - - const token_field = document.createElement("textarea"); - demo_div.after(token_field); - token_field.name = "g-recaptcha-response"; - token_field.style.display = "none"; - token_field.value = token; + demo_submit.setAttribute("disabled", ""); + + for await (const token of HCHA.run(demo_div, demo_site_key)) { + if (token === null) { + demo_submit.setAttribute("disabled", ""); + } else { + token_field.value = token; + demo_submit.removeAttribute("disabled"); + } + } })(); diff --git a/captcha-parent.js b/captcha-parent.js index eaef67b..86ab909 100644 --- a/captcha-parent.js +++ b/captcha-parent.js @@ -73,6 +73,18 @@ HCHA._get_rc_version = async function() { return match[1]; } +HCHA._make_default_site_url = function() { + const url_obj = new URL(window.location.href); + let port = url_obj.port; + + if (url_obj.protocol === "http:" && port === "80") + port = ""; + else if (url_obj.protocol === "https:" && port === "") + port = "443"; + + return url_obj.origin + (port && `:${port}`); +} + HCHA._make_anchor_frame_url = function(site_key, site_url, rc_version) { const url = new URL("https://google.com/recaptcha/api2/anchor"); url.search = new URLSearchParams({ @@ -107,56 +119,79 @@ HCHA._spawn_frame = function(container, site_key, site_url, rc_version) { HCHA._make_anchor_frame_url(site_key, site_url, rc_version); } -/* This library's external API consists of the run() function defined below. */ -HCHA.run = async function(container, site_key, site_url, - start_immediately=false) { - site_url ||= window.location.origin; +HCHA._show_msg_and_wait = async function(container, button_text, + paragraph_text=null) { + container.innerHTML = "

"; - const rc_version = await HCHA._get_rc_version(); + HCHA._style_contained_element(container.firstElementChild); - if (!start_immediately) { - container.innerHTML = - "
"; + const paragraph = container.querySelector("p"); - HCHA._style_contained_element(container.firstElementChild); + if (paragraph_text === null) + paragraph.remove(); + else + paragraph.textContent = paragraph_text; - const button = container.querySelector("button"); - await new Promise(cb => button.onclick = e => { - e.preventDefault(); - cb(); - }); - } + const button = container.querySelector("button"); + + button.textContent = button_text; + + await new Promise(cb => button.onclick = e => { + e.preventDefault(); + cb(); + }); +} - HCHA._spawn_frame(container, site_key, site_url, rc_version); +HCHA._show_success_info = async function(container) { + const msg = "CAPTCHA completed successfully. The token shall be valid for 2 minutes."; + await HCHA._show_msg_and_wait(container, "OK", msg); - let event, resolve; + container.innerHTML = ""; +} + +HCHA._main_loop = async function*(container, site_key, site_url) { + const rc_version = await HCHA._get_rc_version(); while (true) { + HCHA._spawn_frame(container, site_key, site_url, rc_version); + + let event, resolve; + const prom = new Promise(cb => resolve = cb); window.addEventListener("message", resolve, {once: true}); event = await prom; - if (event.origin !== "https://google.com") + if (event.origin !== "https://google.com") { console.warn("Received message from unexepected origin!", event); - else if (event.data === "recreate_frame") + } else if (event.data === "recreate_frame") { HCHA._spawn_frame(container, site_key, site_url, rc_version); - else if (typeof event.data !== "string") + } else if (typeof event.data !== "string") { console.error("Received token is not a string!", event); - else - break; + } else { + HCHA._show_success_info(container); + + yield event.data; + + await new Promise(cb => setTimeout(cb, 120000)); + + yield null; + + await HCHA._show_msg_and_wait(container, "Solve reCAPTCHA again", + "Reached token timeout."); + } } +} + +/* This library's external API consists of the run() function defined below. */ +HCHA.run = async function*(container, site_key, site_url, + start_immediately=false) { + site_url = site_url || HCHA._make_default_site_url(); + + if (!start_immediately) + await HCHA._show_msg_and_wait(container, "Start solving reCAPTCHA"); - container.innerHTML = ` -
-

- CAPTCHA completed successfully. The token shall be valid for 2 minutes. -

- -
-` - container.querySelector("button").onclick = () => container.innerHTML = ""; - - return event.data; + for await (const value of HCHA._main_loop(container, site_key, site_url)) + yield value; } diff --git a/index.json b/index.json index fe8d343..99518a9 100644 --- a/index.json +++ b/index.json @@ -5,7 +5,7 @@ { "$schema": "https://hydrilla.koszko.org/schemas/package_source-2.schema.json", - "source_name": "hacktcha-2022.6.20", + "source_name": "hacktcha-2022.6.21", "copyright": [ {"file": "report.spdx"}, {"file": "LICENSES/GPL-3.0-or-later.txt"}, @@ -17,7 +17,7 @@ "type": "mapping_and_resource", "identifier": "hacktcha-anchor", "long_name": "Hacktcha (anchor frame)", - "version": [2022, 6, 20], + "version": [2022, 6, 21], "revision": 1, "description": "Make Google reCAPTCHA solvable without nonfree JavaScript.", "dependencies": [], @@ -32,7 +32,7 @@ "type": "mapping_and_resource", "identifier": "hacktcha-bframe", "long_name": "Hacktcha (bframe frame)", - "version": [2022, 6, 20], + "version": [2022, 6, 21], "revision": 1, "description": "Make Google reCAPTCHA solvable without nonfree JavaScript.", "dependencies": [], @@ -48,7 +48,7 @@ "type": "resource", "identifier": "hacktcha", "long_name": "Hacktcha (main library)", - "version": [2022, 6, 20], + "version": [2022, 6, 21], "revision": 1, "description": "Make Google reCAPTCHA solvable without nonfree JavaScript.", "dependencies": [{"identifier": "cors-bypass"}], @@ -58,7 +58,7 @@ "type": "mapping_and_resource", "identifier": "hacktcha-demo", "long_name": "Hacktcha demo", - "version": [2022, 6, 20], + "version": [2022, 6, 21], "revision": 1, "description": "Solve Google reCAPTCHA online demo using Hacktcha.", "dependencies": [{"identifier": "hacktcha"}], -- cgit v1.2.3