ACSMDownloader/src/activate.cpp

383 lines
9.3 KiB
C++

/*
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 <http://www.gnu.org/licenses/>.
*/
#include <sys/random.h>
#include <sys/types.h>
#include <pwd.h>
#include <dlfcn.h>
#include <gnu/lib-names.h>
#include <unistd.h>
#include <getopt.h>
#include <stdlib.h>
#include <termios.h>
#include <iostream>
#include <ostream>
#include <log.h>
#include <QFile>
#include <QCoreApplication>
#include <dp.h>
#include <rmsdk_wrapper.h>
#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; i<RANDOM_NAME_SIZE-1; i++)
randomName[i] = (rand_r(&seed)%('z'-'A'))+'A';
randomName[RANDOM_NAME_SIZE-1] = 0;
LOG(DEBUG, "Random name " << randomName);
}
res->pw_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 <<prompt;
while((ch=getch())!= RETURN)
{
if(ch==BACKSPACE)
{
if(password.length()!=0)
{
if(show_asterisk)
std::cout <<"\b \b";
password.resize(password.length()-1);
}
}
else
{
password+=ch;
if(show_asterisk)
std::cout <<'*';
}
}
std::cout <<std::endl;
return password;
}
class ACSMDownloader
{
public:
ACSMDownloader(QCoreApplication* app):
app(app)
{
}
static void* run(void* param)
{
ACSMDownloader* _this;
LOG_FUNC();
int ret = dp::platformInit(0xFFFFFFFF);
if (ret) {
LOG(ERROR, "Error platform init " << ret);
_this->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;
}