diff --git a/flake.nix b/flake.nix index 602f7c4..04a1788 100644 --- a/flake.nix +++ b/flake.nix @@ -15,151 +15,190 @@ }; pugixml-src = { url = "github:zeux/pugixml/latest"; - flake = false; + flake = false; }; }; - outputs = flakes: let - version = "1.3.0"; - self = flakes.self.packages.x86_64-linux; - nixpkgs = flakes.nixpkgs.legacyPackages.x86_64-linux.pkgsStatic; - nixpkgs-dyn = flakes.nixpkgs.legacyPackages.x86_64-linux; - gourou-src = flakes.gourou-src; - updfparser-src = flakes.updfparser-src; - base64-src = flakes.base64-src; - pugixml-src = flakes.pugixml-src; - cxx = "${nixpkgs.stdenv.cc}/bin/x86_64-unknown-linux-musl-g++"; - ar = "${nixpkgs.stdenv.cc.bintools.bintools_bin}/bin/x86_64-unknown-linux-musl-ar"; - obj-flags = "-O2 -static"; - in rec { - packages.x86_64-linux.libzip-static = nixpkgs.libzip.overrideAttrs (prev: { - cmakeFlags = (prev.cmakeFlags or []) ++ [ - "-DBUILD_SHARED_LIBS=OFF" - "-DBUILD_EXAMPLES=OFF" - "-DBUILD_DOC=OFF" - "-DBUILD_TOOLS=OFF" - "-DBUILD_REGRESS=OFF" - ]; - outputs = ["out"]; - }); - packages.x86_64-linux.base64 = derivation { - name = "updfparser"; - system = "x86_64-linux"; - builder = "${nixpkgs.bash}/bin/bash"; - PATH = "${nixpkgs.coreutils}/bin"; - args = ["-c" '' - mkdir -p $out/include/base64 - cp ${base64-src}/Base64.h $out/include/base64/Base64.h - '']; + outputs = flakes: + let + version = "1.3.0"; + self = flakes.self.packages.x86_64-linux; + nixpkgs = flakes.nixpkgs.legacyPackages.x86_64-linux.pkgsStatic; + nixpkgs-dyn = flakes.nixpkgs.legacyPackages.x86_64-linux; + nixpkgs-fmt = flakes.nixpkgs-fmt.defaultPackage.x86_64-linux; + gourou-src = flakes.gourou-src; + updfparser-src = flakes.updfparser-src; + base64-src = flakes.base64-src; + pugixml-src = flakes.pugixml-src; + cxx = "${nixpkgs.stdenv.cc}/bin/x86_64-unknown-linux-musl-g++"; + ar = "${nixpkgs.stdenv.cc.bintools.bintools_bin}/bin/x86_64-unknown-linux-musl-ar"; + obj-flags = "-O2 -static"; + in + rec { + packages.x86_64-linux.libzip-static = nixpkgs.libzip.overrideAttrs (prev: { + cmakeFlags = (prev.cmakeFlags or [ ]) ++ [ + "-DBUILD_SHARED_LIBS=OFF" + "-DBUILD_EXAMPLES=OFF" + "-DBUILD_DOC=OFF" + "-DBUILD_TOOLS=OFF" + "-DBUILD_REGRESS=OFF" + ]; + outputs = [ "out" ]; + }); + packages.x86_64-linux.base64 = derivation { + name = "updfparser"; + system = "x86_64-linux"; + builder = "${nixpkgs.bash}/bin/bash"; + PATH = "${nixpkgs.coreutils}/bin"; + args = [ + "-c" + '' + mkdir -p $out/include/base64 + cp ${base64-src}/Base64.h $out/include/base64/Base64.h + '' + ]; + }; + packages.x86_64-linux.updfparser = derivation { + name = "updfparser"; + system = "x86_64-linux"; + builder = "${nixpkgs.bash}/bin/bash"; + PATH = "${nixpkgs.coreutils}/bin"; + args = [ + "-c" + '' + ${cxx} \ + -c ${updfparser-src}/src/*.cpp \ + -I ${updfparser-src}/include \ + ${obj-flags} + mkdir -p $out/lib + ${ar} crs $out/lib/libupdfparser.a *.o + '' + ]; + }; + packages.x86_64-linux.gourou = derivation { + name = "gourou"; + system = "x86_64-linux"; + builder = "${nixpkgs.bash}/bin/bash"; + PATH = "${nixpkgs.coreutils}/bin"; + args = [ + "-c" + '' + ${cxx} \ + -c \ + ${gourou-src}/src/*.cpp \ + ${pugixml-src}/src/pugixml.cpp \ + -I ${self.base64}/include \ + -I ${gourou-src}/include \ + -I ${pugixml-src}/src \ + -I ${updfparser-src}/include \ + ${obj-flags} + mkdir -p $out/lib $out/debug + ${ar} crs $out/lib/libgourou.a *.o + cp *.o $out/debug + '' + ]; + }; + packages.x86_64-linux.utils-common = derivation { + name = "utils-common"; + system = "x86_64-linux"; + builder = "${nixpkgs.bash}/bin/bash"; + PATH = "${nixpkgs.coreutils}/bin"; + args = [ + "-c" + '' + ${cxx} \ + -c ${gourou-src}/utils/drmprocessorclientimpl.cpp \ + ${gourou-src}/utils/utils_common.cpp \ + -I ${gourou-src}/utils \ + -I ${gourou-src}/include \ + -I ${pugixml-src}/src \ + -I ${nixpkgs.openssl.dev}/include \ + -I ${nixpkgs.curl.dev}/include \ + -I ${nixpkgs.zlib.dev}/include \ + -I ${self.libzip-static}/include \ + ${obj-flags} + mkdir -p $out/lib + ${ar} crs $out/lib/libutils-common.a *.o + '' + ]; + }; + packages.x86_64-linux.knock = derivation { + name = "knock"; + system = "x86_64-linux"; + builder = "${nixpkgs.bash}/bin/bash"; + PATH = "${nixpkgs.coreutils}/bin"; + args = [ + "-c" + '' + mkdir -p $out/bin + ${cxx} \ + -o $out/bin/knock \ + ${./src/knock.cpp} \ + -D KNOCK_VERSION='"${version}"' \ + -Wl,--as-needed -static \ + ${self.utils-common}/lib/libutils-common.a \ + ${self.gourou}/lib/libgourou.a \ + ${self.updfparser}/lib/libupdfparser.a \ + -Wl,--start-group \ + ${self.libzip-static}/lib/libzip.a \ + ${nixpkgs.libnghttp2}/lib/libnghttp2.a \ + ${nixpkgs.libidn2.out}/lib/libidn2.a \ + ${nixpkgs.libunistring}/lib/libunistring.a \ + ${nixpkgs.libssh2}/lib/libssh2.a \ + ${nixpkgs.zstd.out}/lib/libzstd.a \ + ${nixpkgs.zlib}/lib/libz.a \ + ${nixpkgs.openssl.out}/lib/libcrypto.a \ + ${nixpkgs.curl.out}/lib/libcurl.a \ + ${nixpkgs.openssl.out}/lib/libssl.a \ + -static-libgcc -static-libstdc++ \ + -Wl,--end-group \ + -I ${gourou-src}/utils \ + -I ${gourou-src}/include \ + -I ${pugixml-src}/src \ + -I ${nixpkgs.openssl.dev}/include \ + -I ${nixpkgs.curl.dev}/include \ + -I ${nixpkgs.zlib.dev}/include \ + -I ${self.libzip-static}/include + '' + ]; + }; + packages.x86_64-linux.default = self.knock; + packages.x86_64-linux.tests = nixpkgs-dyn.stdenv.mkDerivation { + name = "tests"; + src = ./tests; + buildInputs = [ + (nixpkgs-dyn.python3.withPackages (p: [ + p.beautifulsoup4 + p.requests + ])) + ]; + patchPhase = '' + substituteInPlace tests.py --replace "./result/bin/knock" "${self.knock}/bin/knock" + ''; + installPhase = '' + mkdir -p $out/bin + cp tests.py $out/bin/tests + chmod +x $out/bin/tests + ''; + }; + devShell.x86_64-linux = nixpkgs.mkShell { + packages = [ + # nix formatter + nixpkgs-dyn.nixpkgs-fmt + # python formatter + nixpkgs-dyn.black + # cpp formatter + nixpkgs-dyn.clang-tools + ]; + shellHook = '' + fmt () { + set -ex + nixpkgs-fmt . + black ./tests + clang-format -i --verbose src/*.cpp + set +ex + } + ''; + }; }; - packages.x86_64-linux.updfparser = derivation { - name = "updfparser"; - system = "x86_64-linux"; - builder = "${nixpkgs.bash}/bin/bash"; - PATH = "${nixpkgs.coreutils}/bin"; - args = [ "-c" '' - ${cxx} \ - -c ${updfparser-src}/src/*.cpp \ - -I ${updfparser-src}/include \ - ${obj-flags} - mkdir -p $out/lib - ${ar} crs $out/lib/libupdfparser.a *.o - '' ]; - }; - packages.x86_64-linux.gourou = derivation { - name = "gourou"; - system = "x86_64-linux"; - builder = "${nixpkgs.bash}/bin/bash"; - PATH = "${nixpkgs.coreutils}/bin"; - args = [ "-c" '' - ${cxx} \ - -c \ - ${gourou-src}/src/*.cpp \ - ${pugixml-src}/src/pugixml.cpp \ - -I ${self.base64}/include \ - -I ${gourou-src}/include \ - -I ${pugixml-src}/src \ - -I ${updfparser-src}/include \ - ${obj-flags} - mkdir -p $out/lib $out/debug - ${ar} crs $out/lib/libgourou.a *.o - cp *.o $out/debug - '' ]; - }; - packages.x86_64-linux.utils-common = derivation { - name = "utils-common"; - system = "x86_64-linux"; - builder = "${nixpkgs.bash}/bin/bash"; - PATH = "${nixpkgs.coreutils}/bin"; - args = [ "-c" '' - ${cxx} \ - -c ${gourou-src}/utils/drmprocessorclientimpl.cpp \ - ${gourou-src}/utils/utils_common.cpp \ - -I ${gourou-src}/utils \ - -I ${gourou-src}/include \ - -I ${pugixml-src}/src \ - -I ${nixpkgs.openssl.dev}/include \ - -I ${nixpkgs.curl.dev}/include \ - -I ${nixpkgs.zlib.dev}/include \ - -I ${self.libzip-static}/include \ - ${obj-flags} - mkdir -p $out/lib - ${ar} crs $out/lib/libutils-common.a *.o - '' ]; - }; - packages.x86_64-linux.knock = derivation { - name = "knock"; - system = "x86_64-linux"; - builder = "${nixpkgs.bash}/bin/bash"; - PATH = "${nixpkgs.coreutils}/bin"; - args = [ "-c" '' - mkdir -p $out/bin - ${cxx} \ - -o $out/bin/knock \ - ${./src/knock.cpp} \ - -D KNOCK_VERSION='"${version}"' \ - -Wl,--as-needed -static \ - ${self.utils-common}/lib/libutils-common.a \ - ${self.gourou}/lib/libgourou.a \ - ${self.updfparser}/lib/libupdfparser.a \ - -Wl,--start-group \ - ${self.libzip-static}/lib/libzip.a \ - ${nixpkgs.libnghttp2}/lib/libnghttp2.a \ - ${nixpkgs.libidn2.out}/lib/libidn2.a \ - ${nixpkgs.libunistring}/lib/libunistring.a \ - ${nixpkgs.libssh2}/lib/libssh2.a \ - ${nixpkgs.zstd.out}/lib/libzstd.a \ - ${nixpkgs.zlib}/lib/libz.a \ - ${nixpkgs.openssl.out}/lib/libcrypto.a \ - ${nixpkgs.curl.out}/lib/libcurl.a \ - ${nixpkgs.openssl.out}/lib/libssl.a \ - -static-libgcc -static-libstdc++ \ - -Wl,--end-group \ - -I ${gourou-src}/utils \ - -I ${gourou-src}/include \ - -I ${pugixml-src}/src \ - -I ${nixpkgs.openssl.dev}/include \ - -I ${nixpkgs.curl.dev}/include \ - -I ${nixpkgs.zlib.dev}/include \ - -I ${self.libzip-static}/include - '' ]; - }; - packages.x86_64-linux.default = self.knock; - packages.x86_64-linux.tests = nixpkgs-dyn.stdenv.mkDerivation { - name = "tests"; - src = ./tests; - buildInputs = [ (nixpkgs-dyn.python3.withPackages(p: [ - p.beautifulsoup4 - p.requests - ])) ]; - patchPhase = '' - substituteInPlace tests.py --replace "./result/bin/knock" "${self.knock}/bin/knock" - ''; - installPhase = '' - mkdir -p $out/bin - cp tests.py $out/bin/tests - chmod +x $out/bin/tests - ''; - }; - }; -} \ No newline at end of file +} diff --git a/readme.md b/readme.md index 008414a..585a17c 100644 --- a/readme.md +++ b/readme.md @@ -44,6 +44,14 @@ nix run .#tests -- ./tests/workspace Test books can be found [here](https://www.adobe.com/solutions/ebook/digital-editions/sample-ebook-library.html). +### Formatting + +``` +nix develop +fmt +exit +``` + ## The Name The name comes from the [D&D 5e spell](https://roll20.net/compendium/dnd5e/Knock#content) for freeing locked items: diff --git a/src/knock.cpp b/src/knock.cpp index 014d50d..601fe6a 100644 --- a/src/knock.cpp +++ b/src/knock.cpp @@ -1,119 +1,120 @@ -#include #include "drmprocessorclientimpl.h" -#include "libgourou_common.h" #include "libgourou.h" +#include "libgourou_common.h" +#include #ifndef KNOCK_VERSION - #error KNOCK_VERSION must be defined +#error KNOCK_VERSION must be defined #endif std::string get_data_dir(); void verify_absence(std::string file); void verify_presence(std::string file); -int main(int argc, char** argv) try { +int main(int argc, char **argv) try { - if (argc == 1) { - std::cout - << "info: knock version " << KNOCK_VERSION << ", libgourou version " - << LIBGOUROU_VERSION << "\n" - << "usage: " << argv[0] << " [ACSM]" << "\n" - << "result: converts file ACSM to a plain EPUB/PDF if present, otherwise prints this" - << std::endl; - return EXIT_SUCCESS; - } + if (argc == 1) { + std::cout << "info: knock version " << KNOCK_VERSION + << ", libgourou version " << LIBGOUROU_VERSION << "\n" + << "usage: " << argv[0] << " [ACSM]\n" + << "result: converts file ACSM to a plain EPUB/PDF if present, " + "otherwise prints this" + << std::endl; + return EXIT_SUCCESS; + } - if (argc != 2) { - throw std::invalid_argument("the ACSM file must be passed as an argument"); - } + if (argc != 2) { + throw std::invalid_argument("the ACSM file must be passed as an argument"); + } - const std::string acsm_file = argv[1]; - verify_presence(acsm_file); - const std::string acsm_stem = acsm_file.substr(0, acsm_file.find_last_of(".")); - const std::string drm_file = acsm_stem + ".drm"; - const std::string out_file = acsm_stem + ".out"; - verify_absence(drm_file); - verify_absence(out_file); - const std::string knock_data = get_data_dir(); + const std::string acsm_file = argv[1]; + verify_presence(acsm_file); + const std::string acsm_stem = + acsm_file.substr(0, acsm_file.find_last_of(".")); + const std::string drm_file = acsm_stem + ".drm"; + const std::string out_file = acsm_stem + ".out"; + verify_absence(drm_file); + verify_absence(out_file); + const std::string knock_data = get_data_dir(); - DRMProcessorClientImpl client; - gourou::DRMProcessor* processor = gourou::DRMProcessor::createDRMProcessor( - &client, - false, // don't "always generate a new device" (default) - knock_data - ); + DRMProcessorClientImpl client; + gourou::DRMProcessor *processor = gourou::DRMProcessor::createDRMProcessor( + &client, + false, // don't "always generate a new device" (default) + knock_data); - std::cout << "anonymously signing in..." << std::endl; - processor->signIn("anonymous", ""); - processor->activateDevice(); + std::cout << "anonymously signing in..." << std::endl; + processor->signIn("anonymous", ""); + processor->activateDevice(); - std::cout << "downloading the file from Adobe..." << std::endl; - gourou::FulfillmentItem* item = processor->fulfill(acsm_file); - gourou::DRMProcessor::ITEM_TYPE type = processor->download(item, drm_file); - - std::cout << "removing DRM from the file..." << std::endl; - std::string ext_file; - std::string file_type; - switch (type) { - case gourou::DRMProcessor::ITEM_TYPE::PDF: { - // for pdfs the function moves the pdf while removing drm - processor->removeDRM(drm_file, out_file, type, nullptr, 0); - std::filesystem::remove(drm_file); - ext_file = acsm_stem + ".pdf"; - file_type = "PDF"; - break; - } - case gourou::DRMProcessor::ITEM_TYPE::EPUB: { - // for epubs the drm is removed in-place so in == out - processor->removeDRM(drm_file, drm_file, type, nullptr, 0); - std::filesystem::rename(drm_file, out_file); - ext_file = acsm_stem + ".epub"; - file_type = "EPUB"; - break; - } - } + std::cout << "downloading the file from Adobe..." << std::endl; + gourou::FulfillmentItem *item = processor->fulfill(acsm_file); + gourou::DRMProcessor::ITEM_TYPE type = processor->download(item, drm_file); - if (std::filesystem::exists(ext_file)) { - std::cerr - << "warning: failed to update file extension; " + ext_file + " already exists" - << std::endl; - ext_file = out_file; - } else { - std::filesystem::rename(out_file, ext_file); - } + std::cout << "removing DRM from the file..." << std::endl; + std::string ext_file; + std::string file_type; + switch (type) { + case gourou::DRMProcessor::ITEM_TYPE::PDF: { + // for pdfs the function moves the pdf while removing drm + processor->removeDRM(drm_file, out_file, type, nullptr, 0); + std::filesystem::remove(drm_file); + ext_file = acsm_stem + ".pdf"; + file_type = "PDF"; + break; + } + case gourou::DRMProcessor::ITEM_TYPE::EPUB: { + // for epubs the drm is removed in-place so in == out + processor->removeDRM(drm_file, drm_file, type, nullptr, 0); + std::filesystem::rename(drm_file, out_file); + ext_file = acsm_stem + ".epub"; + file_type = "EPUB"; + break; + } + } - std::cout << file_type + " file generated at " + ext_file << std::endl; + if (std::filesystem::exists(ext_file)) { + std::cerr << "warning: failed to update file extension; " + ext_file + + " already exists" + << std::endl; + ext_file = out_file; + } else { + std::filesystem::rename(out_file, ext_file); + } - return 0; + std::cout << file_type + " file generated at " + ext_file << std::endl; -} catch (const gourou::Exception& e) { - std::cerr << "error:\n" << e.what(); - return EXIT_FAILURE; -} catch (const std::exception& e) { - std::cerr << "error: " << e.what() << std::endl; - return EXIT_FAILURE; + return 0; + +} catch (const gourou::Exception &e) { + std::cerr << "error:\n" << e.what(); + return EXIT_FAILURE; +} catch (const std::exception &e) { + std::cerr << "error: " << e.what() << std::endl; + return EXIT_FAILURE; } std::string get_data_dir() { - char* xdg_data_home = std::getenv("XDG_DATA_HOME"); - std::string knock_data; - if (xdg_data_home != nullptr) { - knock_data = xdg_data_home; - } else { - knock_data = std::string(std::getenv("HOME")) + "/.local/share"; - } - knock_data += "/knock/acsm"; - return knock_data; + char *xdg_data_home = std::getenv("XDG_DATA_HOME"); + std::string knock_data; + if (xdg_data_home != nullptr) { + knock_data = xdg_data_home; + } else { + knock_data = std::string(std::getenv("HOME")) + "/.local/share"; + } + knock_data += "/knock/acsm"; + return knock_data; } void verify_absence(std::string file) { - if (std::filesystem::exists(file)) { - throw std::runtime_error("file " + file + " must be moved out of the way or deleted"); - } + if (std::filesystem::exists(file)) { + throw std::runtime_error("file " + file + + " must be moved out of the way or deleted"); + } } void verify_presence(std::string file) { - if (!std::filesystem::exists(file)) { - throw std::runtime_error("file " + file + " does not exist"); - } + if (!std::filesystem::exists(file)) { + throw std::runtime_error("file " + file + " does not exist"); + } } \ No newline at end of file diff --git a/tests/tests.py b/tests/tests.py index 3c51ef7..60d25a7 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -14,7 +14,7 @@ if not knock.exists(): if len(sys.argv) != 2: print( "error: missing required argument: directory in which to perform the tests", - file=sys.stderr + file=sys.stderr, ) sys.exit() @@ -31,18 +31,18 @@ if workspace.exists(): shutil.rmtree(workspace) workspace.mkdir() -html = requests \ - .get("https://www.adobe.com/solutions/ebook/digital-editions/sample-ebook-library.html") \ - .text -soup = BeautifulSoup(html, 'html.parser') +html = requests.get( + "https://www.adobe.com/solutions/ebook/digital-editions/sample-ebook-library.html" +).text +soup = BeautifulSoup(html, "html.parser") links = [] -for a_tag in soup.find_all('a'): +for a_tag in soup.find_all("a"): if a_tag.string != "Download eBook": continue if not urlparse(a_tag.get("href")).path.endswith(".acsm"): continue - + links.append(a_tag.get("href")) if len(links) >= 10: