Libgourou

Libgourou Git Source Tree

Root/utils/adept_remove.cpp

1/*
2 Copyright (c) 2021, Grégory Soutadé
3
4 All rights reserved.
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8 * Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 * Neither the name of the copyright holder nor the
14 names of its contributors may be used to endorse or promote products
15 derived from this software without specific prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
18 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
21 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27*/
28
29#include <getopt.h>
30#include <libgen.h>
31
32#include <iostream>
33
34#include <libgourou.h>
35#include <libgourou_common.h>
36
37#include "drmprocessorclientimpl.h"
38#include "utils_common.h"
39
40static const char* deviceFile = "device.xml";
41static const char* activationFile = "activation.xml";
42static const char* devicekeyFile = "devicesalt";
43static const char* inputFile = 0;
44static const char* outputFile = 0;
45static const char* outputDir = 0;
46
47static char* encryptionKeyUser = 0;
48static unsigned char* encryptionKey = 0;
49static unsigned encryptionKeySize = 0;
50
51static inline unsigned char htoi(unsigned char c)
52{
53 if (c >= '0' && c <= '9')
54c -= '0';
55 else if (c >= 'a' && c <= 'f')
56c -= 'a' - 10;
57 else if (c >= 'A' && c <= 'F')
58c -= 'A' - 10;
59 else
60EXCEPTION(gourou::USER_INVALID_INPUT, "Invalid character " << c << " in encryption key");
61
62 return c;
63}
64
65static inline bool endsWith(const std::string& s, const std::string& suffix)
66{
67 return s.rfind(suffix) == std::abs((int)(s.size()-suffix.size()));
68}
69
70class ADEPTRemove
71{
72public:
73
74 int run()
75 {
76int ret = 0;
77try
78{
79 gourou::DRMProcessor::ITEM_TYPE type;
80 DRMProcessorClientImpl client;
81 gourou::DRMProcessor processor(&client, deviceFile, activationFile, devicekeyFile);
82
83 std::string filename;
84 if (!outputFile)
85filename = std::string(inputFile);
86 else
87filename = outputFile;
88
89 if (outputDir)
90 {
91if (!fileExists(outputDir))
92 mkpath(outputDir);
93
94filename = std::string(outputDir) + "/" + filename;
95 }
96
97 if (endsWith(filename, ".epub"))
98type = gourou::DRMProcessor::ITEM_TYPE::EPUB;
99 else if (endsWith(filename, ".pdf"))
100type = gourou::DRMProcessor::ITEM_TYPE::PDF;
101 else
102 {
103EXCEPTION(gourou::DRM_FORMAT_NOT_SUPPORTED, "Unsupported file format of " << filename);
104 }
105
106 if (inputFile != filename)
107 {
108unlink(filename.c_str());
109fileCopy(inputFile, filename.c_str());
110processor.removeDRM(inputFile, filename, type, encryptionKey, encryptionKeySize);
111std::cout << "DRM removed into new file " << filename << std::endl;
112 }
113 else
114 {
115// Use temp file for PDF
116if (type == gourou::DRMProcessor::ITEM_TYPE::PDF)
117{
118 std::string tempFile = filename + ".tmp";
119 /* Be sure there is not already a temp file */
120 unlink(tempFile.c_str());
121 processor.removeDRM(filename, tempFile, type, encryptionKey, encryptionKeySize);
122 /* Original file must be removed before doing a copy... */
123 unlink(filename.c_str());
124 if (rename(tempFile.c_str(), filename.c_str()))
125 {
126EXCEPTION(gourou::DRM_FILE_ERROR, "Unable to copy " << tempFile << " into " << filename);
127 }
128}
129else
130 processor.removeDRM(inputFile, filename, type, encryptionKey, encryptionKeySize);
131std::cout << "DRM removed from " << filename << std::endl;
132 }
133} catch(std::exception& e)
134{
135 std::cout << e.what() << std::endl;
136 ret = 1;
137}
138
139return ret;
140 }
141};
142
143static void usage(const char* cmd)
144{
145 std::cout << basename((char*)cmd) << " remove ADEPT DRM (from Adobe) of EPUB/PDF file" << std::endl << std::endl;
146
147 std::cout << "Usage: " << basename((char*)cmd) << " [OPTIONS] file(.epub|pdf)" << std::endl << std::endl;
148
149 std::cout << "Global Options:" << std::endl;
150 std::cout << " " << "-O|--output-dir" << "\t" << "Optional output directory were to put result (default ./)" << std::endl;
151 std::cout << " " << "-o|--output-file" << "\t" << "Optional output filename (default inplace DRM removal>)" << std::endl;
152 std::cout << " " << "-f|--input-file" << "\t" << "Backward compatibility: EPUB/PDF file to process" << std::endl;
153 std::cout << " " << "-v|--verbose" << "\t\t" << "Increase verbosity, can be set multiple times" << std::endl;
154 std::cout << " " << "-V|--version" << "\t\t" << "Display libgourou version" << std::endl;
155 std::cout << " " << "-h|--help" << "\t\t" << "This help" << std::endl;
156
157 std::cout << "ADEPT Options:" << std::endl;
158 std::cout << " " << "-D|--adept-directory" << "\t" << ".adept directory that must contains device.xml, activation.xml and devicesalt" << std::endl;
159 std::cout << " " << "-d|--device-file" << "\t" << "device.xml file from eReader" << std::endl;
160 std::cout << " " << "-a|--activation-file" << "\t" << "activation.xml file from eReader" << std::endl;
161 std::cout << " " << "-k|--device-key-file" << "\t" << "private device key file (eg devicesalt/devkey.bin) from eReader" << std::endl;
162
163 std::cout << std::endl;
164
165 std::cout << "Environment:" << std::endl;
166 std::cout << "Device file, activation file and device key file are optionals. If not set, they are looked into :" << std::endl;
167 std::cout << " * $ADEPT_DIR environment variable" << std::endl;
168 std::cout << " * /home/<user>/.config/adept" << std::endl;
169 std::cout << " * Current directory" << std::endl;
170 std::cout << " * .adept" << std::endl;
171 std::cout << " * adobe-digital-editions directory" << std::endl;
172 std::cout << " * .adobe-digital-editions directory" << std::endl;
173}
174
175int main(int argc, char** argv)
176{
177 int c, ret = -1;
178
179 const char** files[] = {&devicekeyFile, &deviceFile, &activationFile};
180 int verbose = gourou::DRMProcessor::getLogLevel();
181 std::string _deviceFile, _activationFile, _devicekeyFile;
182
183 while (1) {
184int option_index = 0;
185static struct option long_options[] = {
186 {"adept-directory", required_argument, 0, 'D' },
187 {"device-file", required_argument, 0, 'd' },
188 {"activation-file", required_argument, 0, 'a' },
189 {"device-key-file", required_argument, 0, 'k' },
190 {"output-dir", required_argument, 0, 'O' },
191 {"output-file", required_argument, 0, 'o' },
192 {"input-file", required_argument, 0, 'f' },
193 {"encryption-key", required_argument, 0, 'K' }, // Private option
194 {"verbose", no_argument, 0, 'v' },
195 {"version", no_argument, 0, 'V' },
196 {"help", no_argument, 0, 'h' },
197 {0, 0, 0, 0 }
198};
199
200c = getopt_long(argc, argv, "D:d:a:k:O:o:f:K:vVh",
201 long_options, &option_index);
202if (c == -1)
203 break;
204
205switch (c) {
206case 'D':
207 _deviceFile = std::string(optarg) + "/device.xml";
208 _activationFile = std::string(optarg) + "/activation.xml";
209 _devicekeyFile = std::string(optarg) + "/devicesalt";
210 deviceFile = _deviceFile.c_str();
211 activationFile = _activationFile.c_str();
212 devicekeyFile = _devicekeyFile.c_str();
213 break;
214case 'd':
215 deviceFile = optarg;
216 break;
217case 'a':
218 activationFile = optarg;
219 break;
220case 'k':
221 devicekeyFile = optarg;
222 break;
223case 'f':
224 inputFile = optarg;
225 break;
226case 'O':
227 outputDir = optarg;
228 break;
229case 'o':
230 outputFile = optarg;
231 break;
232case 'K':
233 encryptionKeyUser = optarg;
234 break;
235case 'v':
236 verbose++;
237 break;
238case 'V':
239 version();
240 return 0;
241case 'h':
242 usage(argv[0]);
243 return 0;
244default:
245 usage(argv[0]);
246 return -1;
247}
248 }
249
250 gourou::DRMProcessor::setLogLevel(verbose);
251
252 if (optind == argc-1)
253inputFile = argv[optind];
254
255 if (!inputFile || (outputDir && !outputDir[0]) ||
256(outputFile && !outputFile[0]))
257 {
258usage(argv[0]);
259return -1;
260 }
261
262 ADEPTRemove remover;
263
264 int i;
265 bool hasErrors = false;
266 const char* orig;
267 for (i=0; i<(int)ARRAY_SIZE(files); i++)
268 {
269orig = *files[i];
270*files[i] = findFile(*files[i]);
271if (!*files[i])
272{
273 std::cout << "Error : " << orig << " doesn't exists, did you activate your device ?" << std::endl;
274 ret = -1;
275 hasErrors = true;
276}
277 }
278
279 if (encryptionKeyUser)
280 {
281int size = std::string(encryptionKeyUser).size();
282if ((size % 2))
283{
284 std::cout << "Error : Encryption key must be odd length" << std::endl;
285 goto end;
286}
287
288if (encryptionKeyUser[0] == '0' && encryptionKeyUser[1] == 'x')
289{
290 encryptionKeyUser += 2;
291 size -= 2;
292}
293
294encryptionKey = new unsigned char[size/2];
295
296for(i=0; i<size; i+=2)
297{
298 encryptionKey[i/2] = htoi(encryptionKeyUser[i]) << 4;
299 encryptionKey[i/2] |= htoi(encryptionKeyUser[i+1]);
300}
301
302encryptionKeySize = size/2;
303 }
304
305 if (hasErrors)
306goto end;
307
308 ret = remover.run();
309
310end:
311 for (i=0; i<(int)ARRAY_SIZE(files); i++)
312 {
313if (*files[i])
314 free((void*)*files[i]);
315 }
316
317 if (encryptionKey)
318free(encryptionKey);
319
320 return ret;
321}

Archive Download this file