diff options
-rw-r--r-- | machines/profpatsch/base-workstation.nix | 1 | ||||
-rw-r--r-- | machines/profpatsch/shiki.nix | 8 | ||||
-rw-r--r-- | modules/module-list.nix | 1 | ||||
-rw-r--r-- | modules/services/guix.nix | 106 | ||||
-rw-r--r-- | pkgs/games/gog/fetch-gog/default.nix | 29 | ||||
-rw-r--r-- | pkgs/games/humblebundle/default.nix | 1 | ||||
-rw-r--r-- | pkgs/games/humblebundle/fetch-humble-bundle/default.nix | 89 | ||||
-rw-r--r-- | pkgs/games/humblebundle/fetch-humble-bundle/guard-code.patch | 121 | ||||
-rw-r--r-- | pkgs/games/humblebundle/minimetro.nix | 15 | ||||
-rw-r--r-- | pkgs/profpatsch/default.nix | 14 |
10 files changed, 360 insertions, 25 deletions
diff --git a/machines/profpatsch/base-workstation.nix b/machines/profpatsch/base-workstation.nix index d1a14e70..82dee85d 100644 --- a/machines/profpatsch/base-workstation.nix +++ b/machines/profpatsch/base-workstation.nix @@ -119,6 +119,7 @@ in { # of utmost necessity for me to function basePkgs = [ silver-searcher # file content searcher, > ack > grep + lr # list recursively, ls & find replacement dos2unix # text file conversion manpages # system manpages (not included by default) mkpasswd # UNIX password creator diff --git a/machines/profpatsch/shiki.nix b/machines/profpatsch/shiki.nix index 6bc08b6a..4d66f81e 100644 --- a/machines/profpatsch/shiki.nix +++ b/machines/profpatsch/shiki.nix @@ -162,6 +162,7 @@ in { # myPkgs.fast-init # fast-init of haskell projects gitAndTools.git-annex # version controlled binary file storage gitAndTools.git-dit # decentral issue tracking for git + gitAndTools.git-hub # lightweight GitHub integration # TODO: move to user config go @@ -172,7 +173,7 @@ in { pkgs.vuizvui.profpatsch.nix-http-serve # serve nix builds and rebuild on reloads pkgs.vuizvui.profpatsch.nman # open man pages in temporary nix shell pkgs.vuizvui.profpatsch.warpspeed # trivial http file server - pkgs.vuizvui.profpatsch.nix-gen # generate nix expressions + # pkgs.vuizvui.profpatsch.nix-gen # generate nix expressions pkgs.vuizvui.profpatsch.watch-server # restart server on code change pkgs.vuizvui.profpatsch.until # restart until cmd succeeds myPkgs.execlineb-with-builtins @@ -235,6 +236,8 @@ in { redshift # increases screen warmth at night (so i don’t have to feel cold) # pdfjam is the best CLI pdf modification suite (texlive.combine { inherit (texlive) scheme-small pdfjam; }) + # move script/nix-cache-binary to here + cdb ]; in systemPkgs ++ xPkgs ++ guiPkgs ++ programmingTools ++ documentation @@ -253,6 +256,9 @@ in { LidSwitchIgnoreInhibited=no ''; + # TMP + + vuizvui.services.guix.enable = true; ################### # Graphical System diff --git a/modules/module-list.nix b/modules/module-list.nix index c75b8dae..df539e6e 100644 --- a/modules/module-list.nix +++ b/modules/module-list.nix @@ -11,6 +11,7 @@ ./programs/fish/fasd.nix ./services/postfix ./services/starbound.nix + ./services/guix.nix ./system/iso.nix ./system/kernel/bfq ./system/kernel/rckernel.nix diff --git a/modules/services/guix.nix b/modules/services/guix.nix new file mode 100644 index 00000000..287ac619 --- /dev/null +++ b/modules/services/guix.nix @@ -0,0 +1,106 @@ +# ATTN: this is a WIP service, use at your own risk! +{ config, lib, pkgs, ... }: +# https://www.gnu.org/software/guix/manual/en/html_node/Binary-Installation.html + +let + guixBinaryTar = pkgs.fetchurl { + url = "https://alpha.gnu.org/gnu/guix/guix-binary-0.16.0.x86_64-linux.tar.xz"; + sha256 = "049l0zim30cd0gyly2h3jaw4cshdk78h7xdb9ac173h72i13afbj"; + }; + + #*/ + guixInstallScriptIdempotent = pkgs.writeScript "guix-install.sh" '' + #!/bin/sh + set -euo pipefail + + # extract guix + if ! test -e /gnu; then + echo "INFO: installing guix" + + tmp=$(mktemp -d) + pushd $tmp >/dev/null + export PATH=${pkgs.xz}/bin:$PATH + ${pkgs.gnutar}/bin/tar xf ${guixBinaryTar} + mkdir -p /var + cp -r ./var/guix /var + cp -r ./gnu / + popd >/dev/null + + # XXX + # change the mtime of all compiled guile files, + # because tar in this script somehow changes the mtime + # of extracted files to the current time, and nobody knows + # why. If the sources are newer than the .go files, guile + # will try to recompile everything. + find /gnu/store/ -ipath "*guile*ccache*/*.go" | xargs touch -m + fi + + # install root user profile + if ! test -e /root/.config/guix/current; then + mkdir -p /root/.config/guix + ln -s /var/guix/profiles/per-user/root/current-guix \ + /root/.config/guix/current + fi + + echo INFO: finished installing guix! + ''; + + guixBuildGroup = "guixbuilders"; + + guixBuildUser = id: { + name = "guix-build-user-${toString id}"; + createHome = false; + description = "Guix build user ${toString id}"; + extraGroups = [ guixBuildGroup ]; + isSystemUser = true; + }; + + guixBuildUsers = numberOfUsers: + builtins.listToAttrs + (map (user: { + name = user.name; + value = user; + }) (builtins.genList guixBuildUser numberOfUsers)); +in +{ + options = { + vuizvui.services.guix.enable = + lib.mkEnableOption "the guix daemon and init /gnu/store"; + }; + + config = lib.mkIf config.vuizvui.services.guix.enable { + users.users = guixBuildUsers 10; + users.groups = { "${guixBuildGroup}" = {}; }; + + systemd.services.guix-install = { + serviceConfig = { + ExecStart = guixInstallScriptIdempotent; + Type = "oneshot"; + }; + }; + + systemd.services.guix-daemon = { + serviceConfig = { + ExecStart = "/var/guix/profiles/per-user/root/current-guix/bin/guix-daemon --build-users-group=${guixBuildGroup}"; + Environment = "GUIX_LOCPATH=/var/guix/profiles/per-user/root/guix-profix/lib/locale"; + RemainAfterExit = true; + StandardOutput = "syslog"; + StandardError = "syslog"; + TasksMax = 8192; + }; + wantedBy = [ "multi-user.target" ]; + after = [ "guix-install.service" ]; + wants = [ "guix-install.service" ]; + }; + + environment.shellInit = '' + export GUIX_PROFILE="$HOME/.config/guix/current" + source $GUIX_PROFILE/etc/profile + export GUIX_LOCPATH="${pkgs.glibcLocales}/lib/locale" + export INFOPATH="$GUIX_PROFILE/share/info:$INFOPATH" + + guix archive --authorize < \ + /root/.config/guix/current/share/guix/ci.guix.info.pub + ''; + }; +} diff --git a/pkgs/games/gog/fetch-gog/default.nix b/pkgs/games/gog/fetch-gog/default.nix index d4a35a05..c1d350ce 100644 --- a/pkgs/games/gog/fetch-gog/default.nix +++ b/pkgs/games/gog/fetch-gog/default.nix @@ -147,14 +147,24 @@ let } ''; - in runCommandCC "get-captcha" { - nativeBuildInputs = [ pkgconfig ]; + in stdenv.mkDerivation { + name = "get-captcha"; + + dontUnpack = true; + + nativeBuildInputs = [ pkgconfig (qt5.wrapQtAppsHook or null) ]; buildInputs = [ qt5.qtbase qt5.qtwebengine ]; preferLocalBuild = true; - } '' - g++ $(pkg-config --libs --cflags Qt5WebEngineWidgets Qt5WebEngine) \ - -Wall -std=c++11 -o "$out" ${application} - ''; + + buildPhase = '' + g++ $(pkg-config --libs --cflags Qt5WebEngineWidgets Qt5WebEngine) \ + -Wall -std=c++11 -o get-captcha ${application} + ''; + + installPhase = '' + install -vD get-captcha "$out/bin/get-captcha" + ''; + }; mkPyStr = str: "'${stdenv.lib.escape ["'" "\\"] (toString str)}'"; @@ -178,7 +188,7 @@ let def login(self): browser = mechanicalsoup.StatefulBrowser() response = browser.open(${mkPyStr authURL}) - if "google.com/recaptcha" in response.text: + if "https://www.recaptcha.net/recaptcha" in response.text: token_url = self.login_with_captcha() else: browser.select_form('form[name="login"]') @@ -190,7 +200,7 @@ let if 'code' not in query: sys.stderr.write( - "Unable to login with the provided GOG credentials." + "Unable to login with the provided GOG credentials.\n" ) raise SystemExit(1) @@ -210,7 +220,8 @@ let 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") + sys.stderr.write("Please run " ${mkPyStr getCaptcha} + "/bin/get-captcha now.\n") sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sys.stderr.write("Waiting for connection") i = 0 diff --git a/pkgs/games/humblebundle/default.nix b/pkgs/games/humblebundle/default.nix index 656c06db..8d712eb8 100644 --- a/pkgs/games/humblebundle/default.nix +++ b/pkgs/games/humblebundle/default.nix @@ -24,6 +24,7 @@ let jamestown = callPackage ./jamestown.nix {}; liads = callPackage ./liads.nix {}; megabytepunch = callPackage_i686 ./megabytepunch.nix {}; + minimetro = callPackage ./minimetro.nix {}; opus-magnum = callPackage ./opus-magnum.nix {}; owlboy = callPackage ./owlboy.nix {}; pico-8 = callPackage ./pico-8.nix {}; diff --git a/pkgs/games/humblebundle/fetch-humble-bundle/default.nix b/pkgs/games/humblebundle/fetch-humble-bundle/default.nix index f6f161dc..1340bce8 100644 --- a/pkgs/games/humblebundle/fetch-humble-bundle/default.nix +++ b/pkgs/games/humblebundle/fetch-humble-bundle/default.nix @@ -1,13 +1,20 @@ -{ stdenv, curl, cacert, writeText, fetchFromGitHub, fetchpatch -, python, pythonPackages +{ stdenv, curl, cacert, writeText, writeScript, fetchFromGitHub, fetchpatch +, python, python3, pythonPackages # Dependencies for the captcha solver -, pkgconfig, qt5, runCommandCC +, pkgconfig, qt5 , email, password }: -{ name ? null, machineName, downloadName ? "Download", suffix ? "humblebundle", md5 }: let +{ name ? null +, machineName +, downloadName ? "Download" +, suffix ? "humblebundle" +, md5 +}: + +let cafile = "${cacert}/etc/ssl/certs/ca-bundle.crt"; getCaptcha = let @@ -77,13 +84,34 @@ } ''; - in runCommandCC "get-captcha" { - nativeBuildInputs = [ pkgconfig ]; + in stdenv.mkDerivation { + name = "get-captcha"; + + dontUnpack = true; + + nativeBuildInputs = [ pkgconfig (qt5.wrapQtAppsHook or null) ]; buildInputs = [ qt5.qtbase qt5.qtwebengine ]; preferLocalBuild = true; - } '' - g++ $(pkg-config --libs --cflags Qt5WebEngineWidgets Qt5WebEngine) \ - -Wall -std=c++11 -o "$out" ${application} + + buildPhase = '' + g++ $(pkg-config --libs --cflags Qt5WebEngineWidgets Qt5WebEngine) \ + -Wall -std=c++11 -o get-captcha ${application} + ''; + + installPhase = '' + install -vD get-captcha "$out/bin/get-captcha" + ''; + }; + + getGuard = writeScript "get-guard" '' + #!${python3.interpreter} + import socket + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.bind(('localhost', 18129)) + sock.listen(1) + with sock.accept()[0] as conn: + guard = input("Guard code: ") + conn.sendall(guard.encode()) ''; humbleAPI = pythonPackages.buildPythonPackage rec { @@ -97,6 +125,8 @@ sha256 = "1kcg42nh7sbjabim1pbqx14468pypznjy7fx2bv7dicy0sqd9b8j"; }; + patches = [ ./guard-code.patch ]; + postPatch = '' sed -i -e '/^LOGIN_URL *=/s,/login,/processlogin,' humblebundle/client.py sed -i -e '/self\.supports_canonical.*data.*supports_canonical/d' \ @@ -138,7 +168,8 @@ def login_with_captcha(hb): print >>sys.stderr, "Solving a captcha is required to log in." - print >>sys.stderr, "Please run " ${pyStr (toString getCaptcha)} " now." + print >>sys.stderr, "Please run " ${pyStr (toString getCaptcha)} \ + "/bin/get-captcha now." sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print >>sys.stderr, "Waiting for connection", i = 0 @@ -153,13 +184,41 @@ response = sock.recv(4096) sock.close() print >>sys.stderr, "Captcha solved correctly, logging in." - hb.login(${pyStr email}, ${pyStr password}, recaptcha_response=response) + api_login(hb, recaptcha_response=response) + + def login_with_guard(hb, skip_code): + print >>sys.stderr, "A guard code has been sent to your email address." + print >>sys.stderr, "Please run " ${pyStr (toString getGuard)} " now." + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + print >>sys.stderr, "Waiting for connection", + # XXX: DRY! + i = 0 + while sock.connect_ex(("127.0.0.1", 18129)) != 0: + time.sleep(0.1) + if i % 10 == 0: + sys.stderr.write('.') + sys.stderr.flush() + i += 1 + print >>sys.stderr, " connected." + print >>sys.stderr, "Waiting for guard code..." + response = sock.recv(4096) + sock.close() + print >>sys.stderr, "Guard code supplied, logging in." + api_login(hb, captcha_skip_code=skip_code, guard_code=response) + + def api_login(hb, recaptcha_response=None, + captcha_skip_code=None, guard_code=None): + try: + hb.login(${pyStr email}, ${pyStr password}, + recaptcha_response=recaptcha_response, + captcha_skip_code=captcha_skip_code, guard_code=guard_code) + except humblebundle.exceptions.HumbleCaptchaException: + login_with_captcha(hb) + except humblebundle.exceptions.HumbleGuardRequiredException as e: + login_with_guard(hb, e.captcha_skip_code) hb = humblebundle.HumbleApi() - try: - hb.login(${pyStr email}, ${pyStr password}) - except humblebundle.exceptions.HumbleCaptchaException: - login_with_captcha(hb) + api_login(hb) products = dict(get_products(hb)) dstruct = find_download(products) diff --git a/pkgs/games/humblebundle/fetch-humble-bundle/guard-code.patch b/pkgs/games/humblebundle/fetch-humble-bundle/guard-code.patch new file mode 100644 index 00000000..f17928ae --- /dev/null +++ b/pkgs/games/humblebundle/fetch-humble-bundle/guard-code.patch @@ -0,0 +1,121 @@ +diff --git a/humblebundle/client.py b/humblebundle/client.py +index fbc31c9..44184a1 100644 +--- a/humblebundle/client.py ++++ b/humblebundle/client.py +@@ -75,7 +75,9 @@ class HumbleApi(object): + """ + + @callback +- def login(self, username, password, authy_token=None, recaptcha_challenge=None, recaptcha_response=None, ++ def login(self, username, password, authy_token=None, ++ recaptcha_challenge=None, recaptcha_response=None, ++ guard_code=None, captcha_skip_code=None, + *args, **kwargs): + """ + Login to the Humble Bundle API. The response sets the _simpleauth_sess cookie which is stored in the session +@@ -87,6 +89,8 @@ class HumbleApi(object): + :type authy_token: integer or str + :param str recaptcha_challenge: (optional) The challenge signed by Humble Bundle's public key from reCAPTCHA + :param str recaptcha_response: (optional) The plaintext solved CAPTCHA ++ :param str guard_code: (optional) The guard code sent via email ++ :param str captcha_skip_code: (optional) A token to skip the CAPTCHA + :param list args: (optional) Extra positional args to pass to the request + :param dict kwargs: (optional) Extra keyword args to pass to the request. If a data dict is supplied a key + collision with any of the above params will resolved in favor of the supplied param +@@ -108,7 +112,9 @@ class HumbleApi(object): + 'password': password, + 'authy-token': authy_token, + 'recaptcha_challenge_field': recaptcha_challenge, +- 'recaptcha_response_field': recaptcha_response} ++ 'recaptcha_response_field': recaptcha_response, ++ 'guard': guard_code, ++ 'captcha-skip-code': captcha_skip_code} + kwargs.setdefault('data', {}).update({k: v for k, v in default_data.items() if v is not None}) + + response = self._request('POST', LOGIN_URL, *args, **kwargs) +diff --git a/humblebundle/exceptions.py b/humblebundle/exceptions.py +index 9041219..fe4eeaf 100644 +--- a/humblebundle/exceptions.py ++++ b/humblebundle/exceptions.py +@@ -9,7 +9,7 @@ __copyright__ = "Copyright 2014, Joel Pedraza" + __license__ = "MIT" + + __all__ = ['HumbleException', 'HumbleResponseException', 'HumbleAuthenticationException', 'HumbleCredentialException', +- 'HumbleCaptchaException', 'HumbleTwoFactorException', 'HumbleParseException'] ++ 'HumbleCaptchaException', 'HumbleTwoFactorException', 'HumbleGuardRequiredException', 'HumbleParseException'] + + from requests import RequestException + +@@ -38,6 +38,7 @@ class HumbleAuthenticationException(HumbleResponseException): + def __init__(self, *args, **kwargs): + self.captcha_required = kwargs.pop('captcha_required', None) + self.authy_required = kwargs.pop('authy_required', None) ++ self.captcha_skip_code = kwargs.pop('captcha_skip_code', None) + super(HumbleAuthenticationException, self).__init__(*args, **kwargs) + + +@@ -62,6 +63,13 @@ class HumbleTwoFactorException(HumbleAuthenticationException): + pass + + ++class HumbleGuardRequiredException(HumbleAuthenticationException): ++ """ ++ A guard code is required ++ """ ++ pass ++ ++ + class HumbleParseException(HumbleResponseException): + """ + An error occurred while parsing +diff --git a/humblebundle/handlers.py b/humblebundle/handlers.py +index 36fc6e1..a8acebf 100644 +--- a/humblebundle/handlers.py ++++ b/humblebundle/handlers.py +@@ -64,29 +64,42 @@ def login_handler(client, response): + success = data.get('success', None) + if success is True: + return True ++ if data.get('goto', None) is not None: ++ return True + + captcha_required = data.get('captcha_required') + authy_required = data.get('authy_required') ++ captcha_skip_code = data.get('skip_code', [None])[0] ++ ++ guard = data.get('humble_guard_required', False) ++ if guard: ++ raise HumbleGuardRequiredException('Guard code required', request=response.request, response=response, ++ captcha_required=captcha_required, authy_required=authy_required, ++ captcha_skip_code=captcha_skip_code) + + errors, error_msg = get_errors(data) + if errors: + captcha = errors.get('captcha') + if captcha: + raise HumbleCaptchaException(error_msg, request=response.request, response=response, +- captcha_required=captcha_required, authy_required=authy_required) ++ captcha_required=captcha_required, authy_required=authy_required, ++ captcha_skip_code=captcha_skip_code) + + username = errors.get('username') + if username: + raise HumbleCredentialException(error_msg, request=response.request, response=response, +- captcha_required=captcha_required, authy_required=authy_required) ++ captcha_required=captcha_required, authy_required=authy_required, ++ captcha_skip_code=captcha_skip_code) + + authy_token = errors.get("authy-token") + if authy_token: + raise HumbleTwoFactorException(error_msg, request=response.request, response=response, +- captcha_required=captcha_required, authy_required=authy_required) ++ captcha_required=captcha_required, authy_required=authy_required, ++ captcha_skip_code=captcha_skip_code) + + raise HumbleAuthenticationException(error_msg, request=response.request, response=response, +- captcha_required=captcha_required, authy_required=authy_required) ++ captcha_required=captcha_required, authy_required=authy_required, ++ captcha_skip_code=captcha_skip_code) + + + def gamekeys_handler(client, response): diff --git a/pkgs/games/humblebundle/minimetro.nix b/pkgs/games/humblebundle/minimetro.nix new file mode 100644 index 00000000..a5c48775 --- /dev/null +++ b/pkgs/games/humblebundle/minimetro.nix @@ -0,0 +1,15 @@ +{ buildUnity, fetchHumbleBundle }: + +buildUnity rec { + name = "minimetro"; + version = "39"; + fullName = "Mini Metro"; + saveDir = "Dinosaur Polo Club/Mini Metro"; + + src = fetchHumbleBundle { + name = "MiniMetro-release-39-linux.tar.gz"; + machineName = "minimetro_linux"; + downloadName = "Download"; + md5 = "3e7afefbcc68b6295821394e31f5e48b"; + }; +} diff --git a/pkgs/profpatsch/default.nix b/pkgs/profpatsch/default.nix index 69977fff..dc746c2e 100644 --- a/pkgs/profpatsch/default.nix +++ b/pkgs/profpatsch/default.nix @@ -5,11 +5,25 @@ let # wrapper for execlineb that doesn’t need the execline commands # in PATH to work (making them appear like “builtins”) + # TODO: upstream into nixpkgs + # TODO: the grep could be nicer execlineb-with-builtins = let eldir = "${pkgs.execline}/bin"; in pkgs.writeScriptBin "execlineb" '' #!${eldir}/execlineb -s0 + # appends the execlineb bin dir to PATH if not yet in PATH ${eldir}/define eldir ${eldir} + ''${eldir}/ifelse + { + # since this is nix, we can grep for the execline drv hash in PATH + # to see whether it’s already in there + ''${eldir}/pipeline + { ${pkgs.coreutils}/bin/printenv PATH } + ${pkgs.gnugrep}/bin/grep --quiet "${eldir}" + } + # it’s there already + { ''${eldir}/execlineb $@ } + # not there yet, add it ''${eldir}/importas oldpath PATH ''${eldir}/export PATH "''${eldir}:''${oldpath}" ''${eldir}/execlineb $@ |