1 | /*␊ |
2 | Copyright 2021 Grégory Soutadé␊ |
3 | ␊ |
4 | This is a free software: you can redistribute it and/or modify␊ |
5 | it under the terms of the GNU General Public License as published by␊ |
6 | the Free Software Foundation, either version 3 of the License, or␊ |
7 | (at your option) any later version.␊ |
8 | ␊ |
9 | It is distributed in the hope that it will be useful,␊ |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of␊ |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the␊ |
12 | GNU General Public License for more details.␊ |
13 | ␊ |
14 | You should have received a copy of the GNU General Public License␊ |
15 | along with it. If not, see <http://www.gnu.org/licenses/>.␊ |
16 | */␊ |
17 | ␊ |
18 | #include <unistd.h>␊ |
19 | #include <getopt.h>␊ |
20 | ␊ |
21 | #include <iostream>␊ |
22 | #include <log.h>␊ |
23 | ␊ |
24 | #include <QFile>␊ |
25 | #include <QCoreApplication>␊ |
26 | ␊ |
27 | #include <dp.h>␊ |
28 | #include <rmsdk_wrapper.h>␊ |
29 | ␊ |
30 | #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))␊ |
31 | ␊ |
32 | int verbose = INFO;␊ |
33 | ␊ |
34 | static const char* deviceFile = "device.xml";␊ |
35 | static const char* activationFile = "activation.xml";␊ |
36 | static const char* devicekeyFile = "devicesalt";␊ |
37 | static const char* acsmFile = 0;␊ |
38 | static const char* outputFile = 0;␊ |
39 | static const char* outputDir = 0;␊ |
40 | static const char* defaultDirs[] = {␊ |
41 | ".adept/",␊ |
42 | "./adobe-digital-editions/",␊ |
43 | "./.adobe-digital-editions/"␊ |
44 | };␊ |
45 | ␊ |
46 | class ACSMDownloader␊ |
47 | {␊ |
48 | public:␊ |
49 | ACSMDownloader(QCoreApplication* app):␊ |
50 | ␉app(app)␊ |
51 | {␊ |
52 | ␊ |
53 | }␊ |
54 | ␊ |
55 | static void* run(void* param)␊ |
56 | {␊ |
57 | ␉ACSMDownloader* _this;␊ |
58 | ␊ |
59 | ␉LOG_FUNC();␊ |
60 | ␊ |
61 | ␉int ret = dp::platformInit(0xFFFFFFFF);␊ |
62 | ␊ |
63 | ␉if (ret) {␊ |
64 | ␉ LOG(ERROR, "Error platform init " << ret);␊ |
65 | ␉ _this->app->exit(ret);␊ |
66 | ␉ return 0;␊ |
67 | ␉}␊ |
68 | ␊ |
69 | ␉dp::cryptRegisterOpenSSL();␊ |
70 | ␉dp::documentRegisterEPUB();␊ |
71 | ␉dp::documentRegisterPDF();␊ |
72 | ␊ |
73 | ␉LOG(DEBUG, "Create Adobe objects");␊ |
74 | ␉␊ |
75 | ␉try␊ |
76 | ␉{␊ |
77 | ␉ MockDRMProcessorClient processorClient(outputDir, outputFile);␊ |
78 | ␉ MockDevice device(&processorClient, deviceFile, activationFile, devicekeyFile);␊ |
79 | ␉ MockProvider provider(&device);␊ |
80 | ␉ device.setProvider(&provider);␊ |
81 | ␉ dpdev::DeviceProvider::addProvider(&provider);␊ |
82 | ␊ |
83 | ␉ MockNetProvider netProvider;␊ |
84 | ␉ dpnet::NetProvider::setProvider(&netProvider);␊ |
85 | ␊ |
86 | ␉ adept::DRMProviderImpl* _prov = rmsdk::getProvider();␊ |
87 | ␊ |
88 | ␉ LOG(DEBUG, "Create DRM Processor");␊ |
89 | ␉ adept::DRMProcessorImpl* drmprocessor = _prov->createDRMProcessor(&processorClient, &device);␊ |
90 | ␊ |
91 | ␉ processorClient.setProcessor(drmprocessor);␊ |
92 | ␊ |
93 | ␉ unsigned char* buffer;␊ |
94 | ␉ int buffer_size;␊ |
95 | ␉ MockDevice::readFile(acsmFile, &buffer, &buffer_size, true);␊ |
96 | ␊ |
97 | ␉ LOG(DEBUG, "Init workflow ");␊ |
98 | ␊ |
99 | ␉ drmprocessor->initWorkflows(WORKFLOW_AUTH_SIGN_IN|WORKFLOW_FULFILLMENT|WORKFLOW_DOWNLOAD|WORKFLOW_NOTIFICATION, dp::Data(buffer, buffer_size));␊ |
100 | ␊ |
101 | ␉ LOG(DEBUG, "Start work");␊ |
102 | ␉ drmprocessor->startWorkflows(WORKFLOW_AUTH_SIGN_IN|WORKFLOW_FULFILLMENT|WORKFLOW_DOWNLOAD|WORKFLOW_NOTIFICATION);␊ |
103 | ␉ delete[] buffer;␊ |
104 | ␉␊ |
105 | ␉ LOG(DEBUG, "Bye bye");␊ |
106 | ␊ |
107 | ␉ ret = (int)processorClient.getErrors();␊ |
108 | ␉}␊ |
109 | ␉catch(std::exception& e)␊ |
110 | ␉{␊ |
111 | ␉ LOG(ERROR, e.what());␊ |
112 | ␉ ret = 1;␊ |
113 | ␉}␊ |
114 | ␊ |
115 | ␉_this->app->exit(ret);␊ |
116 | ␉␊ |
117 | ␉return 0;␊ |
118 | }␊ |
119 | ␊ |
120 | private:␊ |
121 | QCoreApplication* app;␊ |
122 | };␉ ␊ |
123 | ␊ |
124 | static const char* findFile(const char* filename, bool inDefaultDirs=true)␊ |
125 | {␊ |
126 | QFile file(filename);␊ |
127 | ␊ |
128 | if (file.exists())␊ |
129 | ␉return strdup(filename);␊ |
130 | ␊ |
131 | if (!inDefaultDirs) return 0;␊ |
132 | ␊ |
133 | for (int i=0; i<(int)ARRAY_SIZE(defaultDirs); i++)␊ |
134 | {␊ |
135 | ␉uft::String path = uft::String(defaultDirs[i]) + filename;␊ |
136 | ␉file.setFileName(path.c_str());␊ |
137 | ␉if (file.exists())␊ |
138 | ␉ return strdup(path.c_str());␊ |
139 | }␊ |
140 | ␊ |
141 | return 0;␊ |
142 | }␊ |
143 | ␊ |
144 | static void usage(void)␊ |
145 | {␊ |
146 | std::cout << "Download EPUB file from ACSM request file" << std::endl;␊ |
147 | ␊ |
148 | std::cout << "Usage: ./acsmdownloader [(-d|--device-file) device.xml] [(-a|--activation-file) activation.xml] [(-s|--device-key-file) devicesalt] [(-O|--output-dir) dir] [(-o|--output-file) output.epub] [(-v|--verbose)] [(-h|--help)] (-f|--acsm-file) file.acsm" << std::endl << std::endl;␊ |
149 | ␊ |
150 | std::cout << " " << "-d|--device-file" << "\t" << "device.xml file from eReader" << std::endl;␊ |
151 | std::cout << " " << "-a|--activation-file" << "\t" << "activation.xml file from eReader" << std::endl;␊ |
152 | std::cout << " " << "-k|--device-key-file" << "\t" << "private device key file (eg devicesalt/devkey.bin) from eReader" << std::endl;␊ |
153 | std::cout << " " << "-O|--output-dir" << "\t" << "Optional output directory were to put result (default ./)" << std::endl;␊ |
154 | std::cout << " " << "-o|--output-file" << "\t" << "Optional output epub filename (default <title.epub>)" << std::endl;␊ |
155 | std::cout << " " << "-f|--acsm-file" << "\t" << "ACSM request file for epub download" << std::endl;␊ |
156 | std::cout << " " << "-v|--verbose" << "\t\t" << "Increase verbosity, can be set multiple times" << std::endl;␊ |
157 | std::cout << " " << "-h|--help" << "\t\t" << "This help" << std::endl;␊ |
158 | ␊ |
159 | std::cout << std::endl;␊ |
160 | std::cout << "Device file, activation file and device key file are optionals. If not set, they are looked into :" << std::endl;␊ |
161 | std::cout << " * Current directory" << std::endl;␊ |
162 | std::cout << " * .adept" << std::endl;␊ |
163 | std::cout << " * adobe-digital-editions directory" << std::endl;␊ |
164 | std::cout << " * .adobe-digital-editions directory" << std::endl;␊ |
165 | }␊ |
166 | ␊ |
167 | int main(int argc, char** argv)␊ |
168 | {␊ |
169 | int c, ret = -1;␊ |
170 | ␊ |
171 | const char** files[] = {&devicekeyFile, &deviceFile, &activationFile};␊ |
172 | ␊ |
173 | while (1) {␊ |
174 | ␉int option_index = 0;␊ |
175 | ␉static struct option long_options[] = {␊ |
176 | ␉ {"device-file", required_argument, 0, 'd' },␊ |
177 | ␉ {"activation-file", required_argument, 0, 'a' },␊ |
178 | ␉ {"device-key-file", required_argument, 0, 'k' },␊ |
179 | ␉ {"output-dir", required_argument, 0, 'O' },␊ |
180 | ␉ {"output-file", required_argument, 0, 'o' },␊ |
181 | ␉ {"acsm-file", required_argument, 0, 'f' },␊ |
182 | ␉ {"verbose", no_argument, 0, 'v' },␊ |
183 | ␉ {"help", no_argument, 0, 'h' },␊ |
184 | ␉ {0, 0, 0, 0 }␊ |
185 | ␉};␊ |
186 | ␊ |
187 | ␉c = getopt_long(argc, argv, "d:a:k:O:o:f:vh",␊ |
188 | long_options, &option_index);␊ |
189 | ␉if (c == -1)␊ |
190 | ␉ break;␊ |
191 | ␊ |
192 | ␉switch (c) {␊ |
193 | ␉case 'd':␊ |
194 | ␉ deviceFile = optarg;␊ |
195 | ␉ break;␊ |
196 | ␉case 'a':␊ |
197 | ␉ activationFile = optarg;␊ |
198 | ␉ break;␊ |
199 | ␉case 'k':␊ |
200 | ␉ devicekeyFile = optarg;␊ |
201 | ␉ break;␊ |
202 | ␉case 'f':␊ |
203 | ␉ acsmFile = optarg;␊ |
204 | ␉ break;␊ |
205 | ␉case 'O':␊ |
206 | ␉ outputDir = optarg;␊ |
207 | ␉ break;␊ |
208 | ␉case 'o':␊ |
209 | ␉ outputFile = optarg;␊ |
210 | ␉ break;␊ |
211 | ␉case 'v':␊ |
212 | ␉ verbose++;␊ |
213 | ␉ break;␊ |
214 | ␉case 'h':␊ |
215 | ␉ usage();␊ |
216 | ␉ return 0;␊ |
217 | ␉ break;␊ |
218 | ␉default:␊ |
219 | ␉ usage();␊ |
220 | ␉ return -1;␊ |
221 | ␉}␊ |
222 | }␊ |
223 | ␊ |
224 | if (!acsmFile || (outputDir && !outputDir[0]) ||␊ |
225 | ␉(outputFile && !outputFile[0]))␊ |
226 | {␊ |
227 | ␉usage();␊ |
228 | ␉return -1;␊ |
229 | }␊ |
230 | ␊ |
231 | int i;␊ |
232 | for (i=0; i<(int)ARRAY_SIZE(files); i++)␊ |
233 | {␊ |
234 | ␉*files[i] = findFile(*files[i]);␊ |
235 | ␉if (!*files[i])␊ |
236 | ␉{␊ |
237 | ␉ LOG(ERROR, "Error : " << *files[i] << " doesn't exists");␊ |
238 | ␉ return -1;␊ |
239 | ␉}␊ |
240 | }␊ |
241 | ␊ |
242 | QFile file(acsmFile);␊ |
243 | if (!file.exists())␊ |
244 | {␊ |
245 | ␉LOG(ERROR, "Error : " << acsmFile << " doesn't exists");␊ |
246 | ␉return -1;␊ |
247 | }␊ |
248 | ␊ |
249 | LOG(INFO, "RMSDK Version " << dp::getVersionInfo("hobbes").utf8());␊ |
250 | ␊ |
251 | QCoreApplication app(argc, argv);␊ |
252 | ␊ |
253 | ACSMDownloader downloader(&app);␊ |
254 | pthread_t thread;␊ |
255 | pthread_create(&thread, NULL, ACSMDownloader::run, (void*)&downloader);␊ |
256 | ␊ |
257 | ret = app.exec();␊ |
258 | ␊ |
259 | for (i=0; i<(int)ARRAY_SIZE(files); i++)␊ |
260 | ␉free((void*)*files[i]);␊ |
261 | ␊ |
262 | return ret;␊ |
263 | }␊ |