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 <unistd.h>␊ |
30 | #include <getopt.h>␊ |
31 | #include <termios.h>␊ |
32 | #include <string.h>␊ |
33 | #include <limits.h>␊ |
34 | #include <libgen.h>␊ |
35 | ␊ |
36 | #include <iostream>␊ |
37 | #include <ostream>␊ |
38 | ␊ |
39 | #include <libgourou.h>␊ |
40 | #include "drmprocessorclientimpl.h"␊ |
41 | #include "utils_common.h"␊ |
42 | ␊ |
43 | static const char* username = 0;␊ |
44 | static const char* password = 0;␊ |
45 | static const char* outputDir = 0;␊ |
46 | static const char* hobbesVersion = HOBBES_DEFAULT_VERSION;␊ |
47 | static bool randomSerial = false;␊ |
48 | ␊ |
49 | // From http://www.cplusplus.com/articles/E6vU7k9E/␊ |
50 | static int getch() {␊ |
51 | int ch;␊ |
52 | struct termios t_old, t_new;␊ |
53 | ␊ |
54 | tcgetattr(STDIN_FILENO, &t_old);␊ |
55 | t_new = t_old;␊ |
56 | t_new.c_lflag &= ~(ICANON | ECHO);␊ |
57 | tcsetattr(STDIN_FILENO, TCSANOW, &t_new);␊ |
58 | ␊ |
59 | ch = getchar();␊ |
60 | ␊ |
61 | tcsetattr(STDIN_FILENO, TCSANOW, &t_old);␊ |
62 | return ch;␊ |
63 | }␊ |
64 | ␊ |
65 | static std::string getpass(const char *prompt, bool show_asterisk=false)␊ |
66 | {␊ |
67 | const char BACKSPACE=127;␊ |
68 | const char RETURN=10;␊ |
69 | ␊ |
70 | std::string password;␊ |
71 | unsigned char ch=0;␊ |
72 | ␊ |
73 | std::cout <<prompt;␊ |
74 | ␊ |
75 | while((ch=getch())!= RETURN)␊ |
76 | {␊ |
77 | ␉if(ch==BACKSPACE)␊ |
78 | {␊ |
79 | if(password.length()!=0)␊ |
80 | {␊ |
81 | if(show_asterisk)␊ |
82 | std::cout <<"\b \b";␊ |
83 | password.resize(password.length()-1);␊ |
84 | }␊ |
85 | }␊ |
86 | else␊ |
87 | {␊ |
88 | password+=ch;␊ |
89 | if(show_asterisk)␊ |
90 | std::cout <<'*';␊ |
91 | }␊ |
92 | }␊ |
93 | std::cout <<std::endl;␊ |
94 | return password;␊ |
95 | }␊ |
96 | ␊ |
97 | ␊ |
98 | class ADEPTActivate␊ |
99 | {␊ |
100 | public: ␊ |
101 | ␊ |
102 | int run()␊ |
103 | {␊ |
104 | ␉int ret = 0;␊ |
105 | ␉try␊ |
106 | ␉{␊ |
107 | ␉ DRMProcessorClientImpl client;␊ |
108 | ␉ gourou::DRMProcessor* processor = gourou::DRMProcessor::createDRMProcessor(␊ |
109 | ␉␉&client, randomSerial, outputDir, hobbesVersion);␊ |
110 | ␊ |
111 | ␉ processor->signIn(username, password);␊ |
112 | ␉ processor->activateDevice();␊ |
113 | ␊ |
114 | ␉ std::cout << username << " fully signed and device activated in " << outputDir << std::endl;␊ |
115 | ␉} catch(std::exception& e)␊ |
116 | ␉{␊ |
117 | ␉ std::cout << e.what() << std::endl;␊ |
118 | ␉ ret = 1;␊ |
119 | ␉}␊ |
120 | ␊ |
121 | ␉return ret;␊ |
122 | }␊ |
123 | };␉ ␊ |
124 | ␊ |
125 | ␊ |
126 | static void usage(const char* cmd)␊ |
127 | {␊ |
128 | std::cout << basename((char*)cmd) << " create new device files used by ADEPT DRM" << std::endl << std::endl;␊ |
129 | ␊ |
130 | std::cout << "Usage: " << basename((char*)cmd) << " OPTIONS" << std::endl << std::endl;␊ |
131 | ␊ |
132 | std::cout << "Global Options:" << std::endl;␊ |
133 | std::cout << " " << "-a|--anonymous" << "\t" << "Anonymous account, no need for username/password (Use it only with a DRM removal software)" << std::endl;␊ |
134 | std::cout << " " << "-u|--username" << "\t\t" << "AdobeID username (ie adobe.com email account)" << std::endl;␊ |
135 | std::cout << " " << "-p|--password" << "\t\t" << "AdobeID password (asked if not set via command line) " << std::endl;␊ |
136 | std::cout << " " << "-O|--output-dir" << "\t" << "Optional output directory were to put result (default ./.adept). This directory must not already exists" << std::endl;␊ |
137 | std::cout << " " << "-H|--hobbes-version" << "\t"<< "Force RMSDK version to a specific value (default: version of current librmsdk)" << std::endl;␊ |
138 | std::cout << " " << "-r|--random-serial" << "\t"<< "Generate a random device serial (if not set, it will be dependent of your current configuration)" << std::endl;␊ |
139 | std::cout << " " << "-v|--verbose" << "\t\t" << "Increase verbosity, can be set multiple times" << std::endl;␊ |
140 | std::cout << " " << "-V|--version" << "\t\t" << "Display libgourou version" << std::endl;␊ |
141 | std::cout << " " << "-h|--help" << "\t\t" << "This help" << std::endl;␊ |
142 | ␊ |
143 | std::cout << std::endl;␊ |
144 | }␊ |
145 | ␊ |
146 | static const char* abspath(const char* filename)␊ |
147 | {␊ |
148 | const char* root = getcwd(0, PATH_MAX);␊ |
149 | std::string fullPath = std::string(root) + std::string("/") + filename;␊ |
150 | const char* res = strdup(fullPath.c_str());␊ |
151 | ␊ |
152 | free((void*)root);␊ |
153 | ␊ |
154 | return res;␊ |
155 | }␊ |
156 | ␊ |
157 | int main(int argc, char** argv)␊ |
158 | {␊ |
159 | int c, ret = -1;␊ |
160 | const char* _outputDir = outputDir;␊ |
161 | int verbose = gourou::DRMProcessor::getLogLevel();␊ |
162 | bool anonymous = false;␊ |
163 | ␊ |
164 | while (1) {␊ |
165 | ␉int option_index = 0;␊ |
166 | ␉static struct option long_options[] = {␊ |
167 | ␉ {"anonymous", no_argument , 0, 'a' },␊ |
168 | ␉ {"username", required_argument, 0, 'u' },␊ |
169 | ␉ {"password", required_argument, 0, 'p' },␊ |
170 | ␉ {"output-dir", required_argument, 0, 'O' },␊ |
171 | ␉ {"hobbes-version",required_argument, 0, 'H' },␊ |
172 | ␉ {"random-serial", no_argument, 0, 'r' },␊ |
173 | ␉ {"verbose", no_argument, 0, 'v' },␊ |
174 | ␉ {"version", no_argument, 0, 'V' },␊ |
175 | ␉ {"help", no_argument, 0, 'h' },␊ |
176 | ␉ {0, 0, 0, 0 }␊ |
177 | ␉};␊ |
178 | ␊ |
179 | ␉c = getopt_long(argc, argv, "au:p:O:H:rvVh",␊ |
180 | long_options, &option_index);␊ |
181 | ␉if (c == -1)␊ |
182 | ␉ break;␊ |
183 | ␊ |
184 | ␉switch (c) {␊ |
185 | ␉case 'a':␊ |
186 | ␉ anonymous = true;␊ |
187 | ␉ break;␊ |
188 | ␉case 'u':␊ |
189 | ␉ username = optarg;␊ |
190 | ␉ break;␊ |
191 | ␉case 'p':␊ |
192 | ␉ password = optarg;␊ |
193 | ␉ break;␊ |
194 | ␉case 'O':␊ |
195 | ␉ _outputDir = optarg;␊ |
196 | ␉ break;␊ |
197 | ␉case 'H':␊ |
198 | ␉ hobbesVersion = optarg;␊ |
199 | ␉ break;␊ |
200 | ␉case 'v':␊ |
201 | ␉ verbose++;␊ |
202 | ␉ break;␊ |
203 | ␉case 'V':␊ |
204 | ␉ version();␊ |
205 | ␉ return 0;␊ |
206 | ␉case 'h':␊ |
207 | ␉ usage(argv[0]);␊ |
208 | ␉ return 0;␊ |
209 | ␉case 'r':␊ |
210 | ␉ randomSerial = true;␊ |
211 | ␉ break;␊ |
212 | ␉default:␊ |
213 | ␉ usage(argv[0]);␊ |
214 | ␉ return -1;␊ |
215 | ␉}␊ |
216 | }␊ |
217 | ␊ |
218 | gourou::DRMProcessor::setLogLevel(verbose);␊ |
219 | ␊ |
220 | if ((!username && !anonymous) ||␊ |
221 | ␉(username && anonymous))␊ |
222 | {␊ |
223 | ␉usage(argv[0]);␊ |
224 | ␉return -1;␊ |
225 | }␊ |
226 | ␊ |
227 | if (anonymous)␊ |
228 | {␊ |
229 | ␉username = "anonymous";␊ |
230 | ␉password = "";␊ |
231 | }␊ |
232 | ␊ |
233 | if (!_outputDir || _outputDir[0] == 0)␊ |
234 | {␊ |
235 | ␉outputDir = strdup(gourou::DRMProcessor::getDefaultAdeptDir().c_str());␊ |
236 | }␊ |
237 | else␊ |
238 | {␊ |
239 | ␉// Relative path␊ |
240 | ␉if (_outputDir[0] == '.' || _outputDir[0] != '/')␊ |
241 | ␉{␊ |
242 | ␉ // realpath doesn't works if file/dir doesn't exists␊ |
243 | ␉ if (fileExists(_outputDir))␊ |
244 | ␉␉outputDir = strdup(realpath(_outputDir, 0));␊ |
245 | ␉ else␊ |
246 | ␉␉outputDir = strdup(abspath(_outputDir));␊ |
247 | ␉}␊ |
248 | ␉else␊ |
249 | ␉ outputDir = strdup(_outputDir);␊ |
250 | }␊ |
251 | ␊ |
252 | std::string pass;␊ |
253 | if (fileExists(outputDir))␊ |
254 | {␊ |
255 | ␉int key;␊ |
256 | ␉␊ |
257 | ␉while (true)␊ |
258 | ␉{␊ |
259 | ␉ std::cout << "!! Warning !! : " << outputDir << " already exists." << std::endl;␊ |
260 | ␉ std::cout << "All your data will be overwrite. Would you like to continue ? [y/N] " << std::flush ;␊ |
261 | ␉ key = getchar();␊ |
262 | ␉ if (key == 'n' || key == 'N' || key == '\n' || key == '\r')␊ |
263 | ␉␉goto end;␊ |
264 | ␉ if (key == 'y' || key == 'Y')␊ |
265 | ␉␉break;␊ |
266 | ␉}␊ |
267 | ␊ |
268 | ␉// Clean STDIN buf␊ |
269 | ␉while ((key = getchar()) != '\n')␊ |
270 | ␉ ;␊ |
271 | }␊ |
272 | ␊ |
273 | if (!password)␊ |
274 | {␊ |
275 | ␉char prompt[128];␊ |
276 | ␉std::snprintf(prompt, sizeof(prompt), "Enter password for <%s> : ", username);␊ |
277 | ␉pass = getpass((const char*)prompt, false);␊ |
278 | ␉password = pass.c_str();␊ |
279 | }␊ |
280 | ␊ |
281 | ADEPTActivate activate;␊ |
282 | ␊ |
283 | ret = activate.run();␊ |
284 | ␊ |
285 | end: ␊ |
286 | free((void*)outputDir);␊ |
287 | return ret;␊ |
288 | }␊ |