diff options
author | Wojtek Kosior <koszko@koszko.org> | 2022-05-26 14:55:16 +0200 |
---|---|---|
committer | Wojtek Kosior <koszko@koszko.org> | 2022-05-26 14:55:16 +0200 |
commit | 8bdb22d2ca4545ebc6bc39eb3f2447cdbcafd324 (patch) | |
tree | 3944c446971ca6b900ab072122fdcdee580db733 /captcha-parent.js | |
download | hacktcha-8bdb22d2ca4545ebc6bc39eb3f2447cdbcafd324.tar.gz hacktcha-8bdb22d2ca4545ebc6bc39eb3f2447cdbcafd324.zip |
initial commit
Diffstat (limited to 'captcha-parent.js')
-rw-r--r-- | captcha-parent.js | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/captcha-parent.js b/captcha-parent.js new file mode 100644 index 0000000..eaef67b --- /dev/null +++ b/captcha-parent.js @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later + * + * Part of Hacktcha, a free/libre front-end for reCAPTCHA for use with Haketilo. + * + * Copyright (C) 2022 Wojtek Kosior + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * As additional permission under GNU GPL version 3 section 7, you + * may distribute forms of that code without the copy of the GNU + * GPL normally required by section 4, provided you include this + * license notice and, in case of non-source distribution, a URL + * through which recipients can access the Corresponding Source. + * If you modify file(s) with this exception, you may extend this + * exception to your version of the file(s), but you are not + * obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + * + * As a special exception to the GPL, any HTML file which merely + * makes function calls to this code, and for that purpose + * includes it by reference shall be deemed a separate work for + * copyright law purposes. If you modify this code, you may extend + * this exception to your version of the code, but you are not + * obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * + * I, Wojtek Kosior, thereby promise not to sue for violation of this file's + * license. Although I request that you do not make use of this code in a + * proprietary program, I am not going to enforce this in court. + */ + +/* Load CORS bypass library first. */ + +"use strict"; + +var HCHA = {}; + +/* First, define some helper functions under the "HCHA" napesmace. */ + +/* + * reCAPTCHA uses a less common variant of base64 which we produce by slightly + * modifying the output of JavaScript's native base64 encoding routine. + */ +HCHA._rc_base64 = function(data) { + return btoa(data) + .replaceAll("+", "-") + .replaceAll("/", "_") + .replaceAll("=", "."); +} + +HCHA._get_rc_version = async function() { + const url = "https://google.com/recaptcha/api.js"; + const resp = await HKT.fetch(url); + const text = await resp.text(); + + const reg = /https:\/\/www.gstatic.com\/recaptcha\/releases\/([^/]*)/; + const match = reg.exec(text); + if (!match) + throw new Error(`URL '${url}' returned invalid response.`); + + return match[1]; +} + +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({ + "ar": "1", + "k": site_key, + "co": HCHA._rc_base64(site_url), + "hl": "en", + "v": rc_version, + "size": "normal", + "sa": "action" + }); + + return url; +} + +HCHA._style_contained_element = function(elem) { + elem.style.width = "302px"; + + if (elem.tagName === "IFRAME") { + elem.style.borderStyle = "none"; + elem.style.height = "422px"; + } else { + elem.style.textAlign = "center"; + elem.style.padding = "10px"; + } +} + +HCHA._spawn_frame = function(container, site_key, site_url, rc_version) { + container.innerHTML = '<iframe scrolling="no" frameborder="0"></iframe>'; + HCHA._style_contained_element(container.firstElementChild); + container.firstElementChild.src = + 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; + + const rc_version = await HCHA._get_rc_version(); + + if (!start_immediately) { + container.innerHTML = + "<div><button>Start solving reCAPTCHA</button></div>"; + + HCHA._style_contained_element(container.firstElementChild); + + const button = container.querySelector("button"); + await new Promise(cb => button.onclick = e => { + e.preventDefault(); + cb(); + }); + } + + HCHA._spawn_frame(container, site_key, site_url, rc_version); + + let event, resolve; + + while (true) { + const prom = new Promise(cb => resolve = cb); + + window.addEventListener("message", resolve, {once: true}); + + event = await prom; + + if (event.origin !== "https://google.com") + console.warn("Received message from unexepected origin!", event); + else if (event.data === "recreate_frame") + HCHA._spawn_frame(container, site_key, site_url, rc_version); + else if (typeof event.data !== "string") + console.error("Received token is not a string!", event); + else + break; + } + + container.innerHTML = ` +<div style="width: 302px; border-style: none; padding: 10px"> + <p> + CAPTCHA completed successfully. The token shall be valid for 2 minutes. + </p> + <button>OK</button> +</div> +` + container.querySelector("button").onclick = () => container.innerHTML = ""; + + return event.data; +} |