From 5136f67286519753b9696b4ac46a0e7afd0efe43 Mon Sep 17 00:00:00 2001 From: aszlig Date: Thu, 22 Feb 2018 05:09:09 +0100 Subject: fetch-gog: Try to login without captcha On some occasions a captcha isn't needed for login (not sure exactly when this is the case), so we really should just log in without it instead of bailing out with an error. Signed-off-by: aszlig --- pkgs/games/gog/fetch-gog/default.nix | 85 ++++++++-- pkgs/games/gog/fetch-gog/hexify-char.nix | 258 +++++++++++++++++++++++++++++++ 2 files changed, 327 insertions(+), 16 deletions(-) create mode 100644 pkgs/games/gog/fetch-gog/hexify-char.nix (limited to 'pkgs/games/gog') diff --git a/pkgs/games/gog/fetch-gog/default.nix b/pkgs/games/gog/fetch-gog/default.nix index 3389eb60..e16f9e87 100644 --- a/pkgs/games/gog/fetch-gog/default.nix +++ b/pkgs/games/gog/fetch-gog/default.nix @@ -9,6 +9,33 @@ }: let + # Taken from lgogdownloader (https://github.com/Sude-/lgogdownloader): + clientId = "46899977096215655"; + clientSecret = "9d85c43b1482497dbbce61f6e4aa173a" + + "433796eeae2ca8c5f6129f2dc4de46d9"; + redirectUri = "https://embed.gog.com/on_login_success?origin=client"; + + urlencode = url: query: let + urlquote = isQstring: val: let + extraSafeChars = lib.optionalString (!isQstring) "/:"; + safeChars = lib.lowerChars ++ lib.upperChars + ++ lib.stringToCharacters ("0123456789_.-" + extraSafeChars); + charList = lib.stringToCharacters val; + hexify = chr: "%${import ./hexify-char.nix chr}"; + quoteChar = chr: if lib.elem chr safeChars then chr else hexify chr; + in lib.concatMapStrings quoteChar charList; + mkKeyVal = key: val: "${urlquote true key}=${urlquote true val}"; + qstring = lib.concatStringsSep "&" (lib.mapAttrsToList mkKeyVal query); + in urlquote false url + lib.optionalString (query != {}) "?${qstring}"; + + authURL = urlencode "https://auth.gog.com/auth" { + client_id = clientId; + redirect_uri = redirectUri; + response_type = "code"; + layout = "default"; + brand = "gog"; + }; + getCaptcha = let mkCString = val: let escaped = lib.replaceStrings ["\"" "\\" "\n"] ["\\\"" "\\\\" "\\n"] val; @@ -44,12 +71,9 @@ let #include #include - // Taken from lgogdownloader (https://github.com/Sude-/lgogdownloader): - static QString clientId = "46899977096215655"; - static QString clientSecret = - "9d85c43b1482497dbbce61f6e4aa173a433796eeae2ca8c5f6129f2dc4de46d9"; - static QString redirectUri = - "https://embed.gog.com/on_login_success?origin=client"; + static QString clientId = ${mkCString clientId}; + static QString clientSecret = ${mkCString clientSecret}; + static QString redirectUri = ${mkCString redirectUri}; static QUrl getAuthUrl() { QUrl url("https://auth.gog.com/auth"); @@ -136,13 +160,48 @@ let fetcher = writeText "fetch-gog.py" '' import sys, socket, time from urllib.request import urlopen, Request + from urllib.parse import urlsplit, urlencode, parse_qs from urllib.error import HTTPError from json import loads + import mechanicalsoup from tabulate import tabulate class GogFetcher: def __init__(self, product_id, download_type, download_name): + self.product_id = product_id + self.download_type = download_type + self.download_name = download_name + self.login() + + def login(self): + browser = mechanicalsoup.StatefulBrowser() + response = browser.open(${mkPyStr authURL}) + if "google.com/recaptcha" in response.text: + token_url = self.login_with_captcha() + else: + browser.select_form('form[name="login"]') + browser['login[username]'] = ${mkPyStr email} + browser['login[password]'] = ${mkPyStr password} + browser.submit_selected() + + auth_code = parse_qs(urlsplit(browser.get_url()).query)['code'] + + token_url = "https://auth.gog.com/token?" + urlencode({ + 'client_id': ${mkPyStr clientId}, + 'client_secret': ${mkPyStr clientSecret}, + 'grant_type': 'authorization_code', + 'code': auth_code, + 'redirect_uri': ${mkPyStr redirectUri} + }) + + response = urlopen( + token_url, cafile=${mkPyStr "${cacert}/etc/ssl/certs/ca-bundle.crt"} + ) + + self.access_token = loads(response.read())['access_token'] + + def login_with_captcha(self): sys.stderr.write("Solving a captcha is required to log in.\n") sys.stderr.write("Please run " ${mkPyStr getCaptcha} " now.\n") sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -159,15 +218,7 @@ let token_url = sock.recv(4096) sock.close() sys.stderr.write("Captcha solved correctly, logging in.\n") - response = urlopen( - token_url.decode(), - cafile=${mkPyStr "${cacert}/etc/ssl/certs/ca-bundle.crt"} - ) - - self.product_id = product_id - self.download_type = download_type - self.download_name = download_name - self.access_token = loads(response.read())['access_token'] + return token_url.decode() def request(self, url): headers = {"Authorization": "Bearer " + self.access_token} @@ -227,7 +278,9 @@ in stdenv.mkDerivation { outputHashAlgo = "sha256"; outputHash = sha256; - nativeBuildInputs = [ curl python3Packages.tabulate ]; + nativeBuildInputs = [ + curl python3Packages.tabulate python3Packages.MechanicalSoup + ]; buildCommand = '' url="$(${python3Packages.python.interpreter} ${fetcher} \ diff --git a/pkgs/games/gog/fetch-gog/hexify-char.nix b/pkgs/games/gog/fetch-gog/hexify-char.nix new file mode 100644 index 00000000..78ae9159 --- /dev/null +++ b/pkgs/games/gog/fetch-gog/hexify-char.nix @@ -0,0 +1,258 @@ +val: + +if val == "" then "01" +else if val == "" then "02" +else if val == "" then "03" +else if val == "" then "04" +else if val == "" then "05" +else if val == "" then "06" +else if val == "" then "07" +else if val == "" then "08" +else if val == "\t" then "09" +else if val == "\n" then "0a" +else if val == " " then "0b" +else if val == " " then "0c" +else if val == "\r" then "0d" +else if val == "" then "0e" +else if val == "" then "0f" +else if val == "" then "10" +else if val == "" then "11" +else if val == "" then "12" +else if val == "" then "13" +else if val == "" then "14" +else if val == "" then "15" +else if val == "" then "16" +else if val == "" then "17" +else if val == "" then "18" +else if val == "" then "19" +else if val == "" then "1a" +else if val == "" then "1b" +else if val == "" then "1c" +else if val == "" then "1d" +else if val == "" then "1e" +else if val == "" then "1f" +else if val == " " then "20" +else if val == "!" then "21" +else if val == "\"" then "22" +else if val == "#" then "23" +else if val == "$" then "24" +else if val == "%" then "25" +else if val == "&" then "26" +else if val == "'" then "27" +else if val == "(" then "28" +else if val == ")" then "29" +else if val == "*" then "2a" +else if val == "+" then "2b" +else if val == "," then "2c" +else if val == "-" then "2d" +else if val == "." then "2e" +else if val == "/" then "2f" +else if val == "0" then "30" +else if val == "1" then "31" +else if val == "2" then "32" +else if val == "3" then "33" +else if val == "4" then "34" +else if val == "5" then "35" +else if val == "6" then "36" +else if val == "7" then "37" +else if val == "8" then "38" +else if val == "9" then "39" +else if val == ":" then "3a" +else if val == ";" then "3b" +else if val == "<" then "3c" +else if val == "=" then "3d" +else if val == ">" then "3e" +else if val == "?" then "3f" +else if val == "@" then "40" +else if val == "A" then "41" +else if val == "B" then "42" +else if val == "C" then "43" +else if val == "D" then "44" +else if val == "E" then "45" +else if val == "F" then "46" +else if val == "G" then "47" +else if val == "H" then "48" +else if val == "I" then "49" +else if val == "J" then "4a" +else if val == "K" then "4b" +else if val == "L" then "4c" +else if val == "M" then "4d" +else if val == "N" then "4e" +else if val == "O" then "4f" +else if val == "P" then "50" +else if val == "Q" then "51" +else if val == "R" then "52" +else if val == "S" then "53" +else if val == "T" then "54" +else if val == "U" then "55" +else if val == "V" then "56" +else if val == "W" then "57" +else if val == "X" then "58" +else if val == "Y" then "59" +else if val == "Z" then "5a" +else if val == "[" then "5b" +else if val == "\\" then "5c" +else if val == "]" then "5d" +else if val == "^" then "5e" +else if val == "_" then "5f" +else if val == "`" then "60" +else if val == "a" then "61" +else if val == "b" then "62" +else if val == "c" then "63" +else if val == "d" then "64" +else if val == "e" then "65" +else if val == "f" then "66" +else if val == "g" then "67" +else if val == "h" then "68" +else if val == "i" then "69" +else if val == "j" then "6a" +else if val == "k" then "6b" +else if val == "l" then "6c" +else if val == "m" then "6d" +else if val == "n" then "6e" +else if val == "o" then "6f" +else if val == "p" then "70" +else if val == "q" then "71" +else if val == "r" then "72" +else if val == "s" then "73" +else if val == "t" then "74" +else if val == "u" then "75" +else if val == "v" then "76" +else if val == "w" then "77" +else if val == "x" then "78" +else if val == "y" then "79" +else if val == "z" then "7a" +else if val == "{" then "7b" +else if val == "|" then "7c" +else if val == "}" then "7d" +else if val == "~" then "7e" +else if val == "" then "7f" +else if val == "" then "80" +else if val == "" then "81" +else if val == "" then "82" +else if val == "" then "83" +else if val == "" then "84" +else if val == "" then "85" +else if val == "" then "86" +else if val == "" then "87" +else if val == "" then "88" +else if val == "" then "89" +else if val == "" then "8a" +else if val == "" then "8b" +else if val == "" then "8c" +else if val == "" then "8d" +else if val == "" then "8e" +else if val == "" then "8f" +else if val == "" then "90" +else if val == "" then "91" +else if val == "" then "92" +else if val == "" then "93" +else if val == "" then "94" +else if val == "" then "95" +else if val == "" then "96" +else if val == "" then "97" +else if val == "" then "98" +else if val == "" then "99" +else if val == "" then "9a" +else if val == "" then "9b" +else if val == "" then "9c" +else if val == "" then "9d" +else if val == "" then "9e" +else if val == "" then "9f" +else if val == "" then "a0" +else if val == "" then "a1" +else if val == "" then "a2" +else if val == "" then "a3" +else if val == "" then "a4" +else if val == "" then "a5" +else if val == "" then "a6" +else if val == "" then "a7" +else if val == "" then "a8" +else if val == "" then "a9" +else if val == "" then "aa" +else if val == "" then "ab" +else if val == "" then "ac" +else if val == "" then "ad" +else if val == "" then "ae" +else if val == "" then "af" +else if val == "" then "b0" +else if val == "" then "b1" +else if val == "" then "b2" +else if val == "" then "b3" +else if val == "" then "b4" +else if val == "" then "b5" +else if val == "" then "b6" +else if val == "" then "b7" +else if val == "" then "b8" +else if val == "" then "b9" +else if val == "" then "ba" +else if val == "" then "bb" +else if val == "" then "bc" +else if val == "" then "bd" +else if val == "" then "be" +else if val == "" then "bf" +else if val == "" then "c0" +else if val == "" then "c1" +else if val == "" then "c2" +else if val == "" then "c3" +else if val == "" then "c4" +else if val == "" then "c5" +else if val == "" then "c6" +else if val == "" then "c7" +else if val == "" then "c8" +else if val == "" then "c9" +else if val == "" then "ca" +else if val == "" then "cb" +else if val == "" then "cc" +else if val == "" then "cd" +else if val == "" then "ce" +else if val == "" then "cf" +else if val == "" then "d0" +else if val == "" then "d1" +else if val == "" then "d2" +else if val == "" then "d3" +else if val == "" then "d4" +else if val == "" then "d5" +else if val == "" then "d6" +else if val == "" then "d7" +else if val == "" then "d8" +else if val == "" then "d9" +else if val == "" then "da" +else if val == "" then "db" +else if val == "" then "dc" +else if val == "" then "dd" +else if val == "" then "de" +else if val == "" then "df" +else if val == "" then "e0" +else if val == "" then "e1" +else if val == "" then "e2" +else if val == "" then "e3" +else if val == "" then "e4" +else if val == "" then "e5" +else if val == "" then "e6" +else if val == "" then "e7" +else if val == "" then "e8" +else if val == "" then "e9" +else if val == "" then "ea" +else if val == "" then "eb" +else if val == "" then "ec" +else if val == "" then "ed" +else if val == "" then "ee" +else if val == "" then "ef" +else if val == "" then "f0" +else if val == "" then "f1" +else if val == "" then "f2" +else if val == "" then "f3" +else if val == "" then "f4" +else if val == "" then "f5" +else if val == "" then "f6" +else if val == "" then "f7" +else if val == "" then "f8" +else if val == "" then "f9" +else if val == "" then "fa" +else if val == "" then "fb" +else if val == "" then "fc" +else if val == "" then "fd" +else if val == "" then "fe" +else if val == "" then "ff" +else throw "Invalid character '${val}'." -- cgit 1.4.1