about summary refs log tree commit diff
path: root/pkgs/games/gog/fetch-gog/default.nix
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/games/gog/fetch-gog/default.nix')
-rw-r--r--pkgs/games/gog/fetch-gog/default.nix85
1 files changed, 69 insertions, 16 deletions
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 <QQuickWebEngineProfile>
       #include <QUrlQuery>
 
-      // 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} \