Add adept_remove util
This commit is contained in:
		| @@ -15,6 +15,7 @@ Main fucntions to use from gourou::DRMProcessor are : | ||||
|   * Get an ePub from an ACSM file : _fulfill()_ and _download()_ | ||||
|   * Create a new device : _createDRMProcessor()_ | ||||
|   * Register a new device : _signIn()_ and _activateDevice()_ | ||||
|   * Remove DRM : _removeDRM()_ | ||||
|  | ||||
|  | ||||
| You can import configuration from (at least) : | ||||
| @@ -26,7 +27,7 @@ Or create a new one. Be careful : there is a limited number of devices that can | ||||
|  | ||||
| ePub are encrypted using a shared key : one account / multiple devices, so you can create and register a device into your computer and read downloaded (and encrypted) ePub file with your eReader configured using the same AdobeID account. | ||||
|  | ||||
| For those who wants to remove DRM, you can export your private key and import it within [Calibre](https://calibre-ebook.com/) an its DeDRM plugin. | ||||
| For those who wants to remove DRM without adept_remove, you can export your private key and import it within [Calibre](https://calibre-ebook.com/) an its DeDRM plugin. | ||||
|  | ||||
|  | ||||
| Dependencies | ||||
| @@ -65,6 +66,7 @@ BUILD_SHARED build libgourou.so if 1, nothing if 0, can be combined with BUILD_S | ||||
|  | ||||
| * Default value | ||||
|  | ||||
|  | ||||
| Utils | ||||
| ----- | ||||
|  | ||||
| @@ -85,6 +87,11 @@ To export your private key : | ||||
|     export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD | ||||
|     ./utils/acsmdownloader --export-private-key [-o adobekey_1.der] | ||||
|  | ||||
| To remove ADEPT DRM : | ||||
|  | ||||
|     export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD | ||||
|     ./utils/adept_remove -f <encryptedFile> | ||||
|  | ||||
|  | ||||
| Copyright | ||||
| --------- | ||||
|   | ||||
| @@ -41,12 +41,10 @@ To export your private key : | ||||
|     export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD | ||||
|     ./acsmdownloader --export-private-key [-o adobekey_1.der] | ||||
|  | ||||
| To remove ADEPT DRM : | ||||
|  | ||||
| Sources | ||||
| ------- | ||||
|  | ||||
| http://indefero.soutade.fr/p/libgourou | ||||
|  | ||||
|     export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD | ||||
|     ./adept_remove -f <encryptedFile> | ||||
|  | ||||
|  | ||||
| Copyright | ||||
| @@ -69,4 +67,3 @@ Special thanks | ||||
| -------------- | ||||
|  | ||||
|   * _Jens_ for all test samples and utils testing | ||||
|    | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
|  | ||||
| TARGETS=acsmdownloader adept_activate | ||||
| TARGETS=acsmdownloader adept_activate adept_remove | ||||
|  | ||||
| CXXFLAGS=-Wall `pkg-config --cflags Qt5Core Qt5Network` -fPIC -I$(ROOT)/include -I$(ROOT)/lib/pugixml/src/ | ||||
|  | ||||
| @@ -18,12 +18,17 @@ else | ||||
| CXXFLAGS += -O2 | ||||
| endif | ||||
|  | ||||
| COMMON_DEPS = drmprocessorclientimpl.cpp $(STATIC_DEP) | ||||
|  | ||||
| all: $(TARGETS) | ||||
|  | ||||
| acsmdownloader: drmprocessorclientimpl.cpp acsmdownloader.cpp $(STATIC_DEP) | ||||
| acsmdownloader: acsmdownloader.cpp $(COMMON_DEPS) | ||||
| 	$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@ | ||||
|  | ||||
| adept_activate: drmprocessorclientimpl.cpp adept_activate.cpp $(STATIC_DEP)  | ||||
| adept_activate: adept_activate.cpp $(COMMON_DEPS) | ||||
| 	$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@ | ||||
|  | ||||
| adept_remove: adept_remove.cpp $(COMMON_DEPS) | ||||
| 	$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@ | ||||
|  | ||||
| clean: | ||||
|   | ||||
							
								
								
									
										304
									
								
								utils/adept_remove.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										304
									
								
								utils/adept_remove.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,304 @@ | ||||
| /* | ||||
|   Copyright (c) 2021, Grégory Soutadé | ||||
|  | ||||
|   All rights reserved. | ||||
|   Redistribution and use in source and binary forms, with or without | ||||
|   modification, are permitted provided that the following conditions are met: | ||||
|    | ||||
|   * Redistributions of source code must retain the above copyright | ||||
|     notice, this list of conditions and the following disclaimer. | ||||
|   * Redistributions in binary form must reproduce the above copyright | ||||
|     notice, this list of conditions and the following disclaimer in the | ||||
|     documentation and/or other materials provided with the distribution. | ||||
|   * Neither the name of the copyright holder nor the | ||||
|     names of its contributors may be used to endorse or promote products | ||||
|     derived from this software without specific prior written permission. | ||||
|    | ||||
|   THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY | ||||
|   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||
|   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
|   DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY | ||||
|   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||||
|   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||
|   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||||
|   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
|   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||
|   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
| */ | ||||
|  | ||||
| #include <unistd.h> | ||||
| #include <getopt.h> | ||||
|  | ||||
| #include <iostream> | ||||
|  | ||||
| #include <QFile> | ||||
| #include <QDir> | ||||
| #include <QCoreApplication> | ||||
| #include <QRunnable> | ||||
| #include <QThreadPool> | ||||
| #include <QTemporaryFile> | ||||
|  | ||||
| #include <libgourou.h> | ||||
| #include <libgourou_common.h> | ||||
| #include "drmprocessorclientimpl.h" | ||||
|  | ||||
| #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) | ||||
|  | ||||
| static const char* deviceFile     = "device.xml"; | ||||
| static const char* activationFile = "activation.xml"; | ||||
| static const char* devicekeyFile  = "devicesalt"; | ||||
| static const char* inputFile      = 0; | ||||
| static const char* outputFile     = 0; | ||||
| static const char* outputDir      = 0; | ||||
| static const char* defaultDirs[]  = { | ||||
|     ".adept/", | ||||
|     "./adobe-digital-editions/", | ||||
|     "./.adobe-digital-editions/" | ||||
| }; | ||||
|  | ||||
| static inline bool endsWith(const std::string& s, const std::string& suffix) | ||||
| { | ||||
|     return s.rfind(suffix) == std::abs((int)(s.size()-suffix.size())); | ||||
| } | ||||
|  | ||||
| class ADEPTRemove: public QRunnable | ||||
| { | ||||
| public: | ||||
|     ADEPTRemove(QCoreApplication* app): | ||||
| 	app(app) | ||||
|     { | ||||
| 	setAutoDelete(false); | ||||
|     } | ||||
|     | ||||
|     void run() | ||||
|     { | ||||
| 	int ret = 0; | ||||
| 	try | ||||
| 	{ | ||||
| 	    gourou::DRMProcessor::ITEM_TYPE type; | ||||
| 	    DRMProcessorClientImpl client; | ||||
| 	    gourou::DRMProcessor processor(&client, deviceFile, activationFile, devicekeyFile); | ||||
| 	     | ||||
| 	    std::string filename; | ||||
| 	    if (!outputFile) | ||||
| 		filename = std::string(inputFile); | ||||
| 	    else | ||||
| 		filename = outputFile; | ||||
| 	     | ||||
| 	    if (outputDir) | ||||
| 	    { | ||||
| 		QDir dir(outputDir); | ||||
| 		if (!dir.exists(outputDir)) | ||||
| 		    dir.mkpath(outputDir); | ||||
|  | ||||
| 		filename = std::string(outputDir) + "/" + filename; | ||||
| 	    } | ||||
|  | ||||
| 	    if (endsWith(filename, ".epub")) | ||||
| 		type = gourou::DRMProcessor::ITEM_TYPE::EPUB; | ||||
| 	    else if (endsWith(filename, ".pdf")) | ||||
| 		type = gourou::DRMProcessor::ITEM_TYPE::PDF; | ||||
| 	    else | ||||
| 	    { | ||||
| 		EXCEPTION(gourou::DRM_FORMAT_NOT_SUPPORTED, "Unsupported file format of " << filename); | ||||
| 	    } | ||||
| 	     | ||||
| 	    if (inputFile != filename) | ||||
| 	    { | ||||
| 		QFile::remove(filename.c_str()); | ||||
| 		if (!QFile::copy(inputFile, filename.c_str())) | ||||
| 		{ | ||||
| 		    EXCEPTION(gourou::DRM_FILE_ERROR, "Unable to copy " << inputFile << " into " << filename); | ||||
| 		} | ||||
| 		processor.removeDRM(inputFile, filename, type); | ||||
| 		std::cout << "DRM removed into new file " << filename << std::endl; | ||||
| 	    } | ||||
| 	    else | ||||
| 	    { | ||||
| 		// Use temp file for PDF | ||||
| 		if (type == gourou::DRMProcessor::ITEM_TYPE::PDF) | ||||
| 		{ | ||||
| 		    QTemporaryFile tempFile; | ||||
| 		    tempFile.open(); | ||||
| 		    tempFile.setAutoRemove(false); // In case of failure | ||||
| 		    processor.removeDRM(inputFile, tempFile.fileName().toStdString(), type); | ||||
| 		    /* Original file must be removed before doing a copy... */ | ||||
| 		    QFile origFile(inputFile); | ||||
| 		    origFile.remove(); | ||||
| 		    if (!QFile::copy(tempFile.fileName(), filename.c_str())) | ||||
| 		    { | ||||
| 			EXCEPTION(gourou::DRM_FILE_ERROR, "Unable to copy " << tempFile.fileName().toStdString() << " into " << filename); | ||||
| 		    } | ||||
| 		    tempFile.setAutoRemove(true); | ||||
| 		} | ||||
| 		else | ||||
| 		    processor.removeDRM(inputFile, filename, type); | ||||
| 		std::cout << "DRM removed from " << filename << std::endl; | ||||
| 	    } | ||||
| 	} catch(std::exception& e) | ||||
| 	{ | ||||
| 	    std::cout << e.what() << std::endl; | ||||
| 	    ret = 1; | ||||
| 	} | ||||
|  | ||||
| 	this->app->exit(ret); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     QCoreApplication* app; | ||||
| };	       | ||||
|  | ||||
| static const char* findFile(const char* filename, bool inDefaultDirs=true) | ||||
| { | ||||
|     QFile file(filename); | ||||
|  | ||||
|     if (file.exists()) | ||||
| 	return strdup(filename); | ||||
|  | ||||
|     if (!inDefaultDirs) return 0; | ||||
|      | ||||
|     for (int i=0; i<(int)ARRAY_SIZE(defaultDirs); i++) | ||||
|     { | ||||
| 	QString path = QString(defaultDirs[i]) + QString(filename); | ||||
| 	file.setFileName(path); | ||||
| 	if (file.exists()) | ||||
| 	    return strdup(path.toStdString().c_str()); | ||||
|     } | ||||
|      | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void version(void) | ||||
| { | ||||
|     std::cout << "Current libgourou version : " << gourou::DRMProcessor::VERSION << std::endl ; | ||||
| } | ||||
|  | ||||
| static void usage(const char* cmd) | ||||
| { | ||||
|     std::cout << "Remove ADEPT DRM (from Adobe) of EPUB/PDF file" << std::endl; | ||||
|      | ||||
|     std::cout << "Usage: " << cmd << " [(-d|--device-file) device.xml] [(-a|--activation-file) activation.xml] [(-k|--device-key-file) devicesalt] [(-O|--output-dir) dir] [(-o|--output-file) output(.epub|.pdf|.der)] [(-v|--verbose)] [(-h|--help)] (-f|--input-file) file(.epub|pdf)" << std::endl << std::endl; | ||||
|      | ||||
|     std::cout << "  " << "-d|--device-file"     << "\t"   << "device.xml file from eReader" << std::endl; | ||||
|     std::cout << "  " << "-a|--activation-file" << "\t"   << "activation.xml file from eReader" << std::endl; | ||||
|     std::cout << "  " << "-k|--device-key-file" << "\t"   << "private device key file (eg devicesalt/devkey.bin) from eReader" << 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 << "  " << "-f|--input-file"      << "\t"   << "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; | ||||
|     std::cout << "  " << "-h|--help"            << "\t\t" << "This help" << std::endl; | ||||
|  | ||||
|     std::cout << std::endl; | ||||
|     std::cout << "Device file, activation file and device key file are optionals. If not set, they are looked into :" << std::endl; | ||||
|     std::cout << "  * Current directory" << std::endl; | ||||
|     std::cout << "  * .adept" << std::endl; | ||||
|     std::cout << "  * adobe-digital-editions directory" << std::endl; | ||||
|     std::cout << "  * .adobe-digital-editions directory" << std::endl; | ||||
| } | ||||
|  | ||||
| int main(int argc, char** argv) | ||||
| { | ||||
|     int c, ret = -1; | ||||
|  | ||||
|     const char** files[] = {&devicekeyFile, &deviceFile, &activationFile}; | ||||
|     int verbose = gourou::DRMProcessor::getLogLevel(); | ||||
|  | ||||
|     while (1) { | ||||
| 	int option_index = 0; | ||||
| 	static struct option long_options[] = { | ||||
| 	    {"device-file",      required_argument, 0,  'd' }, | ||||
| 	    {"activation-file",  required_argument, 0,  'a' }, | ||||
| 	    {"device-key-file",  required_argument, 0,  'k' }, | ||||
| 	    {"output-dir",       required_argument, 0,  'O' }, | ||||
| 	    {"output-file",      required_argument, 0,  'o' }, | ||||
| 	    {"input-file",       required_argument, 0,  'f' }, | ||||
| 	    {"export-private-key",no_argument,      0,  'e' }, | ||||
| 	    {"verbose",          no_argument,       0,  'v' }, | ||||
| 	    {"version",          no_argument,       0,  'V' }, | ||||
| 	    {"help",             no_argument,       0,  'h' }, | ||||
| 	    {0,                  0,                 0,  0 } | ||||
| 	}; | ||||
|  | ||||
| 	c = getopt_long(argc, argv, "d:a:k:O:o:f:evVh", | ||||
|                         long_options, &option_index); | ||||
| 	if (c == -1) | ||||
| 	    break; | ||||
|  | ||||
| 	switch (c) { | ||||
| 	case 'd': | ||||
| 	    deviceFile = optarg; | ||||
| 	    break; | ||||
| 	case 'a': | ||||
| 	    activationFile = optarg; | ||||
| 	    break; | ||||
| 	case 'k': | ||||
| 	    devicekeyFile = optarg; | ||||
| 	    break; | ||||
| 	case 'f': | ||||
| 	    inputFile = optarg; | ||||
| 	    break; | ||||
| 	case 'O': | ||||
| 	    outputDir = optarg; | ||||
| 	    break; | ||||
| 	case 'o': | ||||
| 	    outputFile = optarg; | ||||
| 	    break; | ||||
| 	case 'v': | ||||
| 	    verbose++; | ||||
| 	    break; | ||||
| 	case 'V': | ||||
| 	    version(); | ||||
| 	    return 0; | ||||
| 	case 'h': | ||||
| 	    usage(argv[0]); | ||||
| 	    return 0; | ||||
| 	default: | ||||
| 	    usage(argv[0]); | ||||
| 	    return -1; | ||||
| 	} | ||||
|     } | ||||
|     | ||||
|     gourou::DRMProcessor::setLogLevel(verbose); | ||||
|  | ||||
|     if (!inputFile || (outputDir && !outputDir[0]) || | ||||
| 	(outputFile && !outputFile[0])) | ||||
|     { | ||||
| 	usage(argv[0]); | ||||
| 	return -1; | ||||
|     } | ||||
|  | ||||
|     QCoreApplication app(argc, argv); | ||||
|     ADEPTRemove remover(&app); | ||||
|  | ||||
|     int i; | ||||
|     bool hasErrors = false; | ||||
|     const char* orig; | ||||
|     for (i=0; i<(int)ARRAY_SIZE(files); i++) | ||||
|     { | ||||
| 	orig = *files[i]; | ||||
| 	*files[i] = findFile(*files[i]); | ||||
| 	if (!*files[i]) | ||||
| 	{ | ||||
| 	    std::cout << "Error : " << orig << " doesn't exists, did you activate your device ?" << std::endl; | ||||
| 	    ret = -1; | ||||
| 	    hasErrors = true; | ||||
| 	} | ||||
|     } | ||||
|  | ||||
|     if (hasErrors) | ||||
| 	goto end; | ||||
|         | ||||
|     QThreadPool::globalInstance()->start(&remover); | ||||
|  | ||||
|     ret = app.exec(); | ||||
|  | ||||
| end: | ||||
|     for (i=0; i<(int)ARRAY_SIZE(files); i++) | ||||
|     { | ||||
| 	if (*files[i]) | ||||
| 	    free((void*)*files[i]); | ||||
|     } | ||||
|  | ||||
|     return ret; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user