/* Copyright 2021 Grégory Soutadé This is a free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with it. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define RANDOM_NAME_SIZE 64 #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) int verbose = INFO; static const char* username = 0; static const char* password = 0; static const char* outputDir = 0; static const char* hobbesVersion = 0; static bool randomSerial = false; static char randomName[RANDOM_NAME_SIZE]; /* Overload libc getpwuid function (used to generate device serial) */ struct passwd *getpwuid(uid_t uid) { struct passwd *res; void *handle; struct passwd* (*_getpwuid)(uid_t uid); handle = dlopen(LIBC_SO, RTLD_LAZY); if (!handle) { LOG(ERROR, "Unable to open libc (" << dlerror() << ")"); return 0; } // Original getpwuid() _getpwuid = (struct passwd *(*)(uid_t uid)) dlsym(handle, "getpwuid"); if (!_getpwuid) { LOG(ERROR, "getpwuid() not found in libc (" << dlerror() << ")"); return 0; } res = _getpwuid(uid); LOG(DEBUG, "Original name " << res->pw_name); if (res && randomSerial) { // Already generated if (randomName[0] == 0) { unsigned int seed; LOG(WARN, "Generate random name"); getrandom((void*)&seed, sizeof(seed), GRND_RANDOM); rand_r(&seed); rand_r(&seed); rand_r(&seed); for(int i=0; ipw_name = randomName; } dlclose(handle); return res; } // From http://www.cplusplus.com/articles/E6vU7k9E/ static int getch() { int ch; struct termios t_old, t_new; tcgetattr(STDIN_FILENO, &t_old); t_new = t_old; t_new.c_lflag &= ~(ICANON | ECHO); tcsetattr(STDIN_FILENO, TCSANOW, &t_new); ch = getchar(); tcsetattr(STDIN_FILENO, TCSANOW, &t_old); return ch; } static std::string getpass(const char *prompt, bool show_asterisk=false) { const char BACKSPACE=127; const char RETURN=10; std::string password; unsigned char ch=0; std::cout <app->exit(ret); return (void*)ret; } dp::cryptRegisterOpenSSL(); dp::documentRegisterEPUB(); dp::documentRegisterPDF(); LOG(DEBUG, "Create Adobe objects"); char tmpActivate [] = "/tmp/activateXXXXXX"; char* tmpDir = mkdtemp(tmpActivate); setenv((char*)"HOME", tmpDir, 1); LOG(DEBUG, tmpDir << " created"); /* Will create a new dir at /tmp/activateXXXXXX/.adept */ dpdev::UNIXDevice uDevice; uDevice.getFingerprint(); uft::String root(tmpDir); uft::String tmpDevice = root + "/.adept/device.xml"; uft::String tmpActivation = root + "/.adept/activation.xml"; uft::String tmpDevicekey = root + "/.adept/devicesalt"; MockDRMProcessorClient processorClient(outputDir, 0, tmpDir); MockDevice device(&processorClient, tmpDevice.utf8(), tmpActivation.utf8(), tmpDevicekey.utf8()); MockProvider provider(&device); device.setProvider(&provider); dpdev::DeviceProvider::addProvider(&provider); MockNetProvider netProvider; dpnet::NetProvider::setProvider(&netProvider); adept::DRMProviderImpl* _prov = rmsdk::getProvider(); LOG(DEBUG, "Create DRM Processor"); adept::DRMProcessorImpl* drmprocessor = _prov->createDRMProcessor(&processorClient, &device); processorClient.setProcessor(drmprocessor); LOG(DEBUG, "Init workflow "); unsigned int workflows = WORKFLOW_AUTH_SIGN_IN|WORKFLOW_ACTIVATION; drmprocessor->initSignInWorkflow(workflows, dp::String("AdobeID"), username, password); LOG(DEBUG, "Start work"); drmprocessor->startWorkflows(workflows); LOG(DEBUG, "Bye bye"); _this->app->exit(processorClient.getErrors()); return 0; } private: QCoreApplication* app; }; static void usage(void) { std::cout << "Create new device files used by ADEPT DRM" << std::endl; std::cout << "Usage: ./activate (-u|--username) username [(-p|--password) password] [(-O|--output-dir) dir] [(-r|--random-serial)] [(-v|--verbose)] [(-h|--help)]" << std::endl << std::endl; std::cout << " " << "-u|--username" << "\t\t" << "AdobeID username (ie adobe.com email account)" << std::endl; std::cout << " " << "-p|--password" << "\t\t" << "AdobeID password (asked if not set via command line) " << std::endl; std::cout << " " << "-O|--output-dir" << "\t" << "Optional output directory were to put result (default ./.adept). This directory must not already exists" << std::endl; std::cout << " " << "-H|--hobbes-version" << "\t"<< "Force RMSDK version to a specific value (default: version of current librmsdk)" << std::endl; std::cout << " " << "-r|--random-serial" << "\t"<< "Generate a random device serial (if not set, it will be dependent of your current configuration)" << std::endl; std::cout << " " << "-v|--verbose" << "\t\t" << "Increase verbosity, can be set multiple times" << std::endl; std::cout << " " << "-h|--help" << "\t\t" << "This help" << std::endl; std::cout << std::endl; } static const char* abspath(const char* filename) { const char* root = getcwd(0, PATH_MAX); uft::StringBuffer fullPath(root); fullPath.append("/"); fullPath.append(filename); const char* res = strdup(fullPath.utf8()); free((void*)root); return res; } int main(int argc, char** argv) { int c, ret = -1; const char* _outputDir = outputDir; while (1) { int option_index = 0; static struct option long_options[] = { {"username", required_argument, 0, 'u' }, {"password", required_argument, 0, 'p' }, {"output-dir", required_argument, 0, 'O' }, {"hibbes-version",required_argument, 0, 'H' }, {"random-serial", no_argument, 0, 'r' }, {"verbose", no_argument, 0, 'v' }, {"help", no_argument, 0, 'h' }, {0, 0, 0, 0 } }; c = getopt_long(argc, argv, "u:p:O:H:rvh", long_options, &option_index); if (c == -1) break; switch (c) { case 'u': username = optarg; break; case 'p': password = optarg; break; case 'O': _outputDir = optarg; break; case 'H': hobbesVersion = optarg; break; case 'v': verbose++; break; case 'h': usage(); return 0; break; case 'r': randomSerial = true; break; default: usage(); return -1; } } if (!username) { usage(); return -1; } if (!_outputDir || _outputDir[0] == 0) { outputDir = abspath(".adept"); } else { // Relative path if (_outputDir[0] == '.' || _outputDir[0] != '/') { QFile file(_outputDir); // realpath doesn't works if file/dir doesn't exists if (file.exists()) outputDir = realpath(_outputDir, 0); else outputDir = abspath(_outputDir); } else outputDir = strdup(_outputDir); } QDir dir; dir.setPath(outputDir); if (dir.exists()) { LOG(ERROR, "Error, output directory ./.adept already exists"); return -1; } if (!password) { char prompt[128]; std::snprintf(prompt, sizeof(prompt), "Enter password for <%s> : ", username); std::string pass = getpass((const char*)prompt, false); password = pass.c_str(); } if (hobbesVersion) { dp::setVersionInfo("hobbes", hobbesVersion); LOG(INFO, "RMSDK Version forced to " << dp::getVersionInfo("hobbes").utf8()); } else { LOG(INFO, "RMSDK Version " << dp::getVersionInfo("hobbes").utf8()); } QCoreApplication app(argc, argv); ACSMDownloader downloader(&app); pthread_t thread; pthread_create(&thread, NULL, ACSMDownloader::run, (void*)&downloader); ret = app.exec(); free((void*)outputDir); return ret; }