From 76cab1866712105bf03f47cebdf568c0cc674a09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Soutad=C3=A9?= Date: Sat, 31 Jan 2026 20:31:44 +0100 Subject: [PATCH 1/4] Delete all referenced objects deleted by libgourou during PDF DRM removal --- src/libgourou.cpp | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/libgourou.cpp b/src/libgourou.cpp index d9b15ce..6607afd 100644 --- a/src/libgourou.cpp +++ b/src/libgourou.cpp @@ -1342,7 +1342,7 @@ namespace gourou uPDFParser::Integer* ebxVersion; std::vector objects = parser.objects(); - std::vector::iterator it; + std::vector::iterator it, ebxIt; std::vector::reverse_iterator rIt; std::vector ebxObjects; unsigned char decryptedKey[16]; @@ -1513,10 +1513,34 @@ namespace gourou } } + /* Delete objects that reference EBX objects, except in trailer */ + for(it = objects.begin(); it != objects.end(); it++) + { + uPDFParser::Object* object = *it; + + if (object->hasKey("Encrypt") && (*object)["Encrypt"]->type() == uPDFParser::DataType::REFERENCE) + { + uPDFParser::Reference* encrypt = (uPDFParser::Reference*)(*object)["Encrypt"]; + + /* Delete EBX objects */ + for(ebxIt = ebxObjects.begin(); ebxIt != ebxObjects.end(); ebxIt++) + { + if (encrypt->value() == (*ebxIt)->objectId()) + { + GOUROU_LOG(ERROR, "Delete stream id " << object->objectId()); + + parser.removeObject(object); + break; + } + } + } + } + + /* Delete EBX objects */ for(it = ebxObjects.begin(); it != ebxObjects.end(); it++) parser.removeObject(*it); - uPDFParser::Object& trailer = parser.getTrailer(); + uPDFParser::Object& trailer = parser.getTrailer(); trailer.deleteKey("Encrypt"); parser.write(filenameOut); From b44b9889664ca5a9aaa7c5da9a726eaa02714108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Soutad=C3=A9?= Date: Sun, 1 Feb 2026 08:56:27 +0100 Subject: [PATCH 2/4] Fix bug in utils with -o and -O options --- src/libgourou.cpp | 2 +- utils/acsmdownloader.cpp | 46 +++++++++++++++++++++------------------- utils/adept_activate.cpp | 4 ++-- utils/adept_loan_mgt.cpp | 2 +- utils/adept_remove.cpp | 29 ++++++++++++++----------- utils/launcher.cpp | 2 +- utils/utils_common.cpp | 22 +++++++++++++------ utils/utils_common.h | 9 ++++++-- 8 files changed, 69 insertions(+), 47 deletions(-) diff --git a/src/libgourou.cpp b/src/libgourou.cpp index 6607afd..e34b676 100644 --- a/src/libgourou.cpp +++ b/src/libgourou.cpp @@ -525,7 +525,7 @@ namespace gourou time_t expirationTime = parseDateTime(expiration.c_str(), "%Y-%m-%dT%H:%M:%S"); if (time(NULL) > expirationTime) - GOUROU_LOG(WARN, "WARNING: ACSM file expired (" << expiration << "). It may not work"); + GOUROU_LOG(WARN, "WARNING: ACSM file expired (" << expiration << "), It may not work."); } // Build req file diff --git a/utils/acsmdownloader.cpp b/utils/acsmdownloader.cpp index 554a1a9..cc9a5d9 100644 --- a/utils/acsmdownloader.cpp +++ b/utils/acsmdownloader.cpp @@ -64,19 +64,18 @@ public: if (exportPrivateKey) { std::string filename; - if (!outputFile) - filename = std::string("Adobe_PrivateLicenseKey--") + user->getUsername() + ".der"; - else + if (outputFile) filename = outputFile; - - if (outputDir) + else { - if (!fileExists(outputDir)) - mkpath(outputDir); - - filename = std::string(outputDir) + "/" + filename; + filename = std::string("Adobe_PrivateLicenseKey--") + user->getUsername() + ".der"; + + if (outputDir) + filename = std::string(outputDir) + "/" + filename; } + createPath(filename.c_str()); + processor.exportPrivateLicenseKey(filename); std::cout << "Private license key exported to " << filename << std::endl; @@ -86,7 +85,9 @@ public: gourou::FulfillmentItem* item = processor.fulfill(acsmFile, notify); std::string filename; - if (!outputFile) + if (outputFile) + filename = outputFile; + else { filename = item->getMetadata("title"); if (filename == "") @@ -96,18 +97,13 @@ public: // Remove invalid characters std::replace(filename.begin(), filename.end(), '/', '_'); } - } - else - filename = outputFile; - - if (outputDir) - { - if (!fileExists(outputDir)) - mkpath(outputDir); - filename = std::string(outputDir) + "/" + filename; + if (outputDir) + filename = std::string(outputDir) + "/" + filename; } + createPath(filename.c_str()); + gourou::DRMProcessor::ITEM_TYPE type = processor.download(item, filename, resume); if (!outputFile) @@ -186,8 +182,8 @@ static void usage(const char* cmd) std::cout << basename((char*)cmd) << " download EPUB file from ACSM request file" << std::endl << std::endl; std::cout << "Usage: " << basename((char*)cmd) << " [OPTIONS] file.acsm" << std::endl << std::endl; std::cout << "Global Options:" << std::endl; - std::cout << " " << "-O|--output-dir" << "\t" << "Optional output directory were to put result (default ./)" << std::endl; - std::cout << " " << "-o|--output-file" << "\t" << "Optional output filename (default )" << std::endl; + std::cout << " " << "-O|--output-dir" << "\t" << "Optional output directory were to put result (default ./) (not compatible with -o)" << std::endl; + std::cout << " " << "-o|--output-file" << "\t" << "Optional output filename (default ) (not compatible with -O)" << std::endl; std::cout << " " << "-f|--acsm-file" << "\t" << "Backward compatibility: ACSM request file for epub download" << std::endl; std::cout << " " << "-e|--export-private-key"<< "\t" << "Export private key in DER format" << std::endl; std::cout << " " << "-r|--resume" << "\t\t" << "Try to resume download (in case of previous failure)" << std::endl; @@ -309,6 +305,12 @@ int main(int argc, char** argv) return -1; } + if (outputDir && outputFile) + { + std::cout << "Error : you cannot use both -o and -O" << std::endl; + return -1; + } + ACSMDownloader downloader; int i; @@ -339,7 +341,7 @@ int main(int argc, char** argv) } else { - if (!fileExists(acsmFile)) + if (!pathExists(acsmFile)) { std::cout << "Error : " << acsmFile << " doesn't exists" << std::endl; ret = -1; diff --git a/utils/adept_activate.cpp b/utils/adept_activate.cpp index b224de5..b12b617 100644 --- a/utils/adept_activate.cpp +++ b/utils/adept_activate.cpp @@ -240,7 +240,7 @@ int main(int argc, char** argv) if (_outputDir[0] == '.' || _outputDir[0] != '/') { // realpath doesn't works if file/dir doesn't exists - if (fileExists(_outputDir)) + if (pathExists(_outputDir)) outputDir = strdup(realpath(_outputDir, 0)); else outputDir = strdup(abspath(_outputDir)); @@ -250,7 +250,7 @@ int main(int argc, char** argv) } std::string pass; - if (fileExists(outputDir)) + if (pathExists(outputDir)) { int key; diff --git a/utils/adept_loan_mgt.cpp b/utils/adept_loan_mgt.cpp index fd060ab..ab46d98 100644 --- a/utils/adept_loan_mgt.cpp +++ b/utils/adept_loan_mgt.cpp @@ -109,7 +109,7 @@ private: std::string loanDir = std::string(adeptDir) + std::string("/") + LOANS_DIR; - if (!fileExists(loanDir.c_str())) + if (!pathExists(loanDir.c_str())) return; dp = opendir (loanDir.c_str()); diff --git a/utils/adept_remove.cpp b/utils/adept_remove.cpp index c678f3d..47112ab 100644 --- a/utils/adept_remove.cpp +++ b/utils/adept_remove.cpp @@ -81,19 +81,16 @@ public: gourou::DRMProcessor processor(&client, deviceFile, activationFile, devicekeyFile); std::string filename; - if (!outputFile) - filename = std::string(inputFile); - else + if (outputFile) filename = outputFile; - - if (outputDir) + else { - if (!fileExists(outputDir)) - mkpath(outputDir); + filename = std::string(inputFile); - filename = std::string(outputDir) + "/" + filename; + if (outputDir) + filename = std::string(outputDir) + "/" + filename; } - + if (endsWith(filename, ".epub")) type = gourou::DRMProcessor::ITEM_TYPE::EPUB; else if (endsWith(filename, ".pdf")) @@ -102,7 +99,9 @@ public: { EXCEPTION(gourou::DRM_FORMAT_NOT_SUPPORTED, "Unsupported file format of " << filename); } - + + createPath(filename.c_str()); + if (inputFile != filename) { unlink(filename.c_str()); @@ -147,8 +146,8 @@ static void usage(const char* cmd) std::cout << "Usage: " << basename((char*)cmd) << " [OPTIONS] file(.epub|pdf)" << std::endl << std::endl; std::cout << "Global Options:" << std::endl; - std::cout << " " << "-O|--output-dir" << "\t" << "Optional output directory were to put result (default ./)" << std::endl; - std::cout << " " << "-o|--output-file" << "\t" << "Optional output filename (default inplace DRM removal>)" << std::endl; + std::cout << " " << "-O|--output-dir" << "\t" << "Optional output directory were to put result (default ./) (not compatible with -o)" << std::endl; + std::cout << " " << "-o|--output-file" << "\t" << "Optional output filename (default inplace DRM removal>) (not compatible with -O)" << std::endl; std::cout << " " << "-f|--input-file" << "\t" << "Backward compatibility: EPUB/PDF file to process" << std::endl; std::cout << " " << "-v|--verbose" << "\t\t" << "Increase verbosity, can be set multiple times" << std::endl; std::cout << " " << "-V|--version" << "\t\t" << "Display libgourou version" << std::endl; @@ -259,6 +258,12 @@ int main(int argc, char** argv) return -1; } + if (outputDir && outputFile) + { + std::cout << "Error : you cannot use both -o and -O" << std::endl; + return -1; + } + ADEPTRemove remover; int i; diff --git a/utils/launcher.cpp b/utils/launcher.cpp index 6eb2bcf..6b29f78 100644 --- a/utils/launcher.cpp +++ b/utils/launcher.cpp @@ -23,7 +23,7 @@ int main(int argc, char** argv) fullPath = std::string(mountPoint) + util; - if (std::string(util) == "launcher" || !fileExists(fullPath.c_str())) + if (std::string(util) == "launcher" || !pathExists(fullPath.c_str())) fullPath = std::string(mountPoint) + DEFAULT_UTIL; free(argv0); diff --git a/utils/utils_common.cpp b/utils/utils_common.cpp index 383af5f..61fce6d 100644 --- a/utils/utils_common.cpp +++ b/utils/utils_common.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include @@ -51,10 +52,10 @@ void version(void) std::cout << "Current libgourou version : " << gourou::DRMProcessor::VERSION << std::endl ; } -bool fileExists(const char* filename) +bool pathExists(const char* path) { struct stat _stat; - int ret = stat(filename, &_stat); + int ret = stat(path, &_stat); return (ret == 0); } @@ -67,15 +68,15 @@ const char* findFile(const char* filename, bool inDefaultDirs) if (adeptDir && adeptDir[0]) { path = adeptDir + std::string("/") + filename; - if (fileExists(path.c_str())) + if (pathExists(path.c_str())) return strdup(path.c_str()); } path = gourou::DRMProcessor::getDefaultAdeptDir() + filename; - if (fileExists(path.c_str())) + if (pathExists(path.c_str())) return strdup(path.c_str()); - if (fileExists(filename)) + if (pathExists(filename)) return strdup(filename); if (!inDefaultDirs) return 0; @@ -83,7 +84,7 @@ const char* findFile(const char* filename, bool inDefaultDirs) for (int i=0; i<(int)ARRAY_SIZE(defaultDirs); i++) { path = std::string(defaultDirs[i]) + filename; - if (fileExists(path.c_str())) + if (pathExists(path.c_str())) return strdup(path.c_str()); } @@ -152,3 +153,12 @@ void fileCopy(const char* in, const char* out) close (fdIn); close (fdOut); } + +void createPath(const char* filename) +{ + char* basepath = strdup(filename); + char* outputDir = dirname(basepath); + if (outputDir && !pathExists(outputDir)) + mkpath(outputDir); + free(basepath); +} diff --git a/utils/utils_common.h b/utils/utils_common.h index 9289209..519b8aa 100644 --- a/utils/utils_common.h +++ b/utils/utils_common.h @@ -50,9 +50,9 @@ void version(void); const char* findFile(const char* filename, bool inDefaultDirs=true); /** - * @brief Does the file (or directory exists) + * @brief Does the file (or directory) exists */ -bool fileExists(const char* filename); +bool pathExists(const char* path); /** * @brief Recursively created dir @@ -64,4 +64,9 @@ void mkpath(const char *dir); */ void fileCopy(const char* in, const char* out); +/** + * @brief Create intermediate directories if it does not exists + */ +void createPath(const char* filename); + #endif From 72cb22ad2af0e8636858f72e826f80a3fb1808f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Soutad=C3=A9?= Date: Tue, 17 Feb 2026 20:01:58 +0100 Subject: [PATCH 3/4] Refresh curl download percent only when updated --- utils/drmprocessorclientimpl.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/utils/drmprocessorclientimpl.cpp b/utils/drmprocessorclientimpl.cpp index 02ef555..2512fb8 100644 --- a/utils/drmprocessorclientimpl.cpp +++ b/utils/drmprocessorclientimpl.cpp @@ -147,6 +147,7 @@ void DRMProcessorClientImpl::randBytes(unsigned char* bytesOut, unsigned int len #define HTTP_REQ_MAX_RETRY 5 #define DISPLAY_THRESHOLD 10*1024 // Threshold to display download progression static unsigned downloadedBytes; +static int lastPercent = -1; static int downloadProgress(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) @@ -158,7 +159,11 @@ static int downloadProgress(void *clientp, curl_off_t dltotal, curl_off_t dlnow, if (dltotal) percent = (dlnow * 100) / dltotal; - std::cout << "\rDownload " << percent << "%" << std::flush; + if (lastPercent != percent) + { + std::cout << "\rDownload " << percent << "%" << std::flush; + lastPercent = percent; + } } return 0; @@ -279,6 +284,7 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, downloadProgress); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); + lastPercent = -1; for (int i=0; i Date: Sun, 8 Mar 2026 14:09:41 -0700 Subject: [PATCH 4/4] Use -fmacro-prefix-map for utils The __FILE__ macro is used in the EXCEPTION macro to report the file name alongside error messages. This macro reports the file name exactly as it is passed to the compiler. For most source files in libgourou this is a nice relative path such as "src/libgourou.cpp". However, for EXCEPTION instances in libgourou_common.h, the compiler flag is "-I$(ROOT)/include", where $(ROOT) is an absolute path passed from the higher Makefile. This results in an absolute path to the build directory being hardcoded into the utils shared library and binaries, and reported in error messages. Besides being less readable than the more common relative paths, this triggers warnings from packaging tools that detect inadvertent references to temporary build directories that end up in compiled binaries and might indicate a bug. There is a GCC feature -fmacro-prefix-map which allows to perform substition on the values that are used for __FILE__. Use that option to strip out the absolute path component, without changing any functionality. The feature was added to GCC on 2018-01-18 and released in GCC 8.1.0. https://github.com/gcc-mirror/gcc/commit/7365279fca30371b07e49bfa83a23ddc44cc3860 --- utils/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/Makefile b/utils/Makefile index ba85377..2b4bfda 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -6,7 +6,7 @@ TARGETS=$(TARGET_BINARIES) launcher MAN_PAGES=acsmdownloader adept_activate adept_remove adept_loan_mgt -CXXFLAGS=-Wall -fPIC -I$(ROOT)/include -fdata-sections -ffunction-sections +CXXFLAGS=-Wall -fPIC -I$(ROOT)/include -fmacro-prefix-map=$(ROOT)/= -fdata-sections -ffunction-sections STATIC_DEP= # LDFLAGS += -Wl,--gc-sections