ACSMDownloader

ACSMDownloader Commit Details

Date:2021-05-06 11:10:38 (2 months 21 days ago)
Author:Grégory Soutadé
Commit:f2f7a5c9acc88c1fce65e0d62d30829b13d2f057
Parents: 28f3418c9e0b4295ee824c6c6420b94379752c92
Message:Add activate

Changes:
Cscripts/acsmdownloader.sh → scripts/activate.sh
Asrc/activate.cpp (full)
MMakefile (2 diffs)
MREADME.md (4 diffs)
Minclude/log.h (1 diff)
Minclude/rmsdk/adept.h (5 diffs)
Minclude/rmsdk/dpdev.h (2 diffs)
Minclude/rmsdk/dpdrm.h (4 diffs)
Minclude/rmsdk/dpio.h (1 diff)
Minclude/rmsdk/librmsdk.h (1 diff)
Minclude/rmsdk/uft.h (3 diffs)
Minclude/rmsdk_wrapper.h (16 diffs)
Msrc/acsmdownloader.cpp (6 diffs)

File differences

Makefile
77
88
99
10
10
1111
12
13
14
12
1513
14
15
16
17
18
19
1620
1721
1822
......
2125
2226
2327
24
28
2529
2630
27
31
2832
2933
30
34
3135
3236
3337
34
38
3539
3640
3741
38
42
3943
44
4045
4146
4247
4348
44
49
4550
4651
47
48
52
53
54
55
56
4957
5058
5159
5260
5361
5462
55
56
57
58
59
63
64
65
66
67
6068
6169
62
70
6371
6472
6573
DEBUG ?= 0
NO_BUILD_OPENSSL ?= 0
INSTALL_DIR ?= $(PWD)/deploy
RMSDK_LIBDIR=$(PWD)/lib/rmsdk/$(RMSDK_VERSION)
RMSDK_LIBDIR = $(PWD)/lib/rmsdk/$(RMSDK_VERSION)
OUTPUT ?= output/$(RMSDK_VERSION)
TARGET=$(OUTPUT)/acsmdownloader
SRCS=src/acsmdownloader.cpp
OUTPUT_DIR ?= output/$(RMSDK_VERSION)
TARGET_ACSM = $(OUTPUT_DIR)/acsmdownloader
SRCS_ACSM = src/acsmdownloader.cpp
TARGET_ACTIVATE = $(OUTPUT_DIR)/activate
SRCS_ACTIVATE = src/activate.cpp
TARGETS = $(TARGET_ACSM) $(TARGET_ACTIVATE)
CXXFLAGS += -I./include -I./include/rmsdk/ `pkg-config --cflags Qt5Core Qt5Network` -fPIC -DRMSDK_$(RMSDK_C_VERSION) -Wall
ifeq ($(DEBUG),1)
LDFLAGS=-L. -L$(RMSDK_LIBDIR) -L./lib -lrmsdk -lQt5Core -lQt5Network -lpthread
all: $(TARGET)
all: $(RMSDK_LIBDIR) $(OUTPUT_DIR) $(TARGETS)
clean:
rm -rf $(TARGET)
rm -rf $(TARGETS)
ultraclean:
rm -rf $(TARGET) lib
rm -rf output lib
prepare: $(RMSDK_LIBDIR)
install: $(TARGET)
install: $(TARGETS)
rm -rf $(INSTALL_DIR)
mkdir -p $(INSTALL_DIR)
cp -r $(RMSDK_LIBDIR) $(INSTALL_DIR)/lib
cp $(TARGET) $(INSTALL_DIR)
cp $(TARGETS) $(INSTALL_DIR)
cp scripts/acsmdownloader.sh $(INSTALL_DIR)
cp scripts/activate.sh $(INSTALL_DIR)
$(RMSDK_LIBDIR):
CC=$(CC) NO_BUILD_OPENSSL=$(NO_BUILD_OPENSSL) RMSDK_VERSION=$(RMSDK_VERSION) $(PWD)/scripts/setup.sh
$(OUTPUT):
$(OUTPUT_DIR):
mkdir -p $@
$(TARGET): $(RMSDK_LIBDIR) $(OUTPUT) $(SRCS)
$(CXX) $(CXXFLAGS) $(SRCS) $(LDFLAGS) -o $@
$(TARGET_ACTIVATE): $(SRCS_ACTIVATE)
$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
$(TARGET_ACSM): $(SRCS_ACSM)
$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
help:
@echo ""
@echo "ACSMDownloader Makefile"
@echo ""
@echo "Targets :"
@echo "\tall"
@echo "\tclean \t\tclean target"
@echo "\tultraclean \tclean target and lib directory"
@echo "\tprepare \tdownload libraries from Kobo (done by default)"
@echo "\tinstall \tinstall result"
@echo "\tall \t\tDownload external libraries and build all targets (acsmdownloader and activate)"
@echo "\tclean \t\tClean targets"
@echo "\tultraclean \tClean targets and lib directory"
@echo "\tprepare \tDownload libraries from Kobo (done by default)"
@echo "\tinstall \tInstall result"
@echo ""
@echo "Main environment variables :"
@echo "\tCROSS \t\tthat defines cross compilation prefix (default \"arm-linux-gnueabihf-\")"
@echo "\tCROSS : \tthat defines cross compilation prefix (default \"arm-linux-gnueabihf-\")"
@echo "\tDEBUG : 1|0 \tto enable debug symbols (default 0)"
@echo "\tNO_BUILD_OPENSSL : 1|0 \tto disable OpenSSL build and not use the one from Kobo (default 0)"
@echo "\tINSTALL_DIR : \tdirectory to put everythong needed (default ./deploy)"
README.md
33
44
55
6
6
77
8
8
99
10
10
1111
1212
1313
14
15
1416
1517
1618
......
2022
2123
2224
23
25
2426
25
26
27
28
2729
28
30
2931
3032
3133
......
3436
3537
3638
37
38
39
40
39
4140
42
41
4342
44
43
4544
4645
4746
......
5150
5251
5352
53
5454
5555
5656
ACSMDownloader is a tool to download _epub_ file from Adobe _acsm_ request file.
It was developped to overcome the lack of Adobe Digital Editions (ADE) software on Linux platforms.
Which is ironic because *ALL* eReaders are based on Linux systems (at least Android/Linux).
Which is ironic as *ALL* eReaders are based on Linux systems (at least Android/Linux).
ACSMDownloader is the result of a lot of reverse engineering on Kobo _librmsdk.so_ shared library but it's not included with this package as I cannot distribute it without a licence (that I don't own).
ACSMDownloader is the result of a lot of reverse engineering on Kobo _librmsdk.so_ shared library but it's not included with this package as I cannot distribute it without a licence from Adobe (that I don't own).
**Warning** : It acts like a client for _librmsdk.so_ which has been built for ARMv7 platform and can only be run on these kind of platform ! Until someone find an x64 version (GNU/Linux compliant) of this library, it cannot be run on your computer. A solution is to build a web frontend that run on an ARM server.
**Warning** : It acts like a client for _librmsdk.so_ which has been built for ARMv7 platform and can only be run on these kind of platform ! Until someone find an x64 version (GNU/Linux compliant) of this library, ACSMDownloader cannot be run on your computer. A solution is to build a web frontend that run on an ARM server.
Current RMSDK version is 10.0.4
**Caution** : This tool was not designed for piracy and any request for this will be ignored.
Compilation
Some environment variables can be defined before running _make_ :
* DEBUG : 1|0 to enable debug symbols
* DEBUG : 1|0 to enable debug symbols
* NO_BUILD_OPENSSL : 1 to don't build OpenSSL library (use the one of Kobo or your own)
* CROSS : cross compiler prefix (default arm-linux-gnueabihf-)
* INSTALL_DIR : default (./deploy)
* CROSS : cross compiler prefix (default arm-linux-gnueabihf-)
* INSTALL_DIR : default (./deploy)
First step of compilation is to download all libraries in binary form from Kobo update file. It also downloads OpenSSL and compile, this kind of library must be built for a specific ARM platform (Kobo library may crash).
First step of compilation is to download all libraries in binary form from Kobo update file. It also downloads OpenSSL and compile it because the one from Kobo may crash (compiled with specific target options).
Be careful : Kobo software has been compiled with an old version of Qt Embedded (4.6), but a reference from Qt5 shared libary is made. It implies that some functions may not work (especially QString --> std::string conversion).
Adobe files
-----------
ACSMDownloader requires some generated files present in the eReader. These files are _device.xml_ and _activation.xml_, they're generally stored into _adobe-digital-edition_ directory.
The first one describes eReader characteristics (serial, name, RMSDK version...) while the second contains ADEPT certificates and user information.
Another needed file is _devkey.bin_ as named in Bookeen readers. It's a private AES key (16 bytes length) unique per device, it's used to encrypt some information in _activation.xml_.
ACSMDownloader requires some generated files present in the eReader. These files are _device.xml_, _activation.xml_ and _devicesalt_ (or _devicekey.bin_), they're generally stored into _.adept_ or _adobe-digital-edition_ directory.
While the XML files are easily availables, _devkey.bin_ may require to access root filesystem via ADB/SSH.
The first one describes eReader characteristics (serial, name, RMSDK version...) while the second contains ADEPT certificates and user information, they're easy to find. _devicesalt_/_devkey.bin_ is a base file for ADEPT cryptography and may be harder to find, it may requires root filesystem access via ADB/SSH.
**This tool was not designed, and will never be, for piracy purpose**. ePub downloaded can only be viewed by an eReader that already hosts these private keys/certificates and none of ePub/PDF DRM removal request will be accepted.
In addition to _acsmdownloader_ binary, an util called _activate_ is delivered. It'll create the necessary _xml_ files and link them to your AdobeID account if you don't want to use the ones from your eReader. Be careful as there is a limited number of devices authorized per Adobe account.
* Qt5
Usage
-----
include/log.h
2828
2929
3030
31
31
3232
3333
3434
TRACE
};
#define LOG(lvl, msg) if (lvl <= verbose) {std::cout << msg << std::endl;}
#define LOG(lvl, msg) if (lvl <= verbose) {std::cout << msg << std::endl << std::flush;}
#define LOG_FUNC() LOG(TRACE, __FUNCTION__)
include/rmsdk/adept.h
8282
8383
8484
85
86
85
86
8787
8888
8989
90
90
9191
9292
9393
94
95
96
97
94
95
96
97
9898
99
100
101
102
103
99
100
101
102
103
104104
105
105
106106
107107
108108
......
185185
186186
187187
188
188
189189
190190
191191
......
219219
220220
221221
222
222
223223
224224
225225
......
729729
730730
731731
732
732
733733
734734
735735
......
769769
770770
771771
772
772
773773
774774
775775
776776
777777
778
778
779779
780780
781781
virtual ~ActivationImpl();
virtual void addRef();
virtual void release();
virtual void getUserID();
virtual void getDeviceID();
virtual uft::String getUserID();
virtual uft::String getDeviceID();
virtual void getExpiration();
virtual void getAuthority();
virtual void getUsername();
virtual void hasCredentials();
virtual bool hasCredentials();
};
// class ActivationList: public dp::List<dpdrm::Activation>
// {
// public:
// ActivationList(uft::Vector const&);
class ActivationList: public dp::List<dpdrm::Activation>
{
public:
ActivationList(uft::Vector const&);
// virtual ~ActivationList();
// virtual void addRef();
// virtual void release();
// virtual int length();
// virtual uft::sref<dpdrm::Activation> operator[](unsigned int);
virtual ~ActivationList();
virtual void addRef();
virtual void release();
virtual int length();
virtual dp::ref<dpdrm::Activation> operator[](unsigned int);
// };
};
class ActivationRecord
{
virtual ~DRMProcessorImpl();
virtual void* getOptionalInterface(char const*);
virtual void release();
virtual void getActivations();
virtual dp::list<adept::ActivationImpl> getActivations();
virtual void setUser(dp::String const&);
virtual void setPartition(dpio::Partition*);
virtual void reset();
virtual void reportStateError(uft::String const&);
virtual void reportProcessError(uft::String const&);
virtual void setURLString(uft::String const&);
virtual void getURLString();
virtual uft::String getURLString();
virtual void changeURL(uft::String const&);
void activate();
void* operator new(unsigned int, uft::Value&);
void query(uft::Value const&, void*);
void staticInit();
void toString() const;
dp::String toString() const;
void* s_descriptor;
void findLicenseRequest(uft::Vector const&, uft::String const&, uft::String const&, bool);
void findNode(mdom::Node const&, unsigned int, unsigned int, uft::String, unsigned int, uft::Buffer);
void findNode(mdom::Node const&, unsigned int, unsigned int, unsigned int*, uft::String*, unsigned int, unsigned int*, uft::Buffer*);
void findUserByName(uft::Dict const&, uft::String const&);
uft::sref<adept::User> findUserByName(uft::Dict const&, uft::String const&);
void getActivationService(dpdev::Device*, uft::String const&);
void getBase64EncodedContent(mdom::Node);
uft::String getChildValue(mdom::Node const& node, unsigned int nodeType);
void getLicense(mdom::Node const&, uft::String const&, uft::Vector const&, dpdev::Device*, uft::ErrorHandler*);
void getLicenses(mdom::Node const&, uft::String const&, uft::Vector const&, dpdev::Device*, bool, uft::ErrorHandler*);
void getValidUsers(dpdev::Device*, uft::ErrorHandler*);
uft::Dict getValidUsers(dpdev::Device*, uft::ErrorHandler*);
void hashNode(mdom::Node);
void isDeviceAvailable(dpdev::Device*);
void managePasshash(dpdev::Device*, dp::String const&, dp::Data const&, bool);
include/rmsdk/dpdev.h
1111
1212
1313
14
1415
1516
1617
......
201202
202203
203204
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
228235
229
236
230237
231238
232239
namespace dpio {
class Partition;
class FilesystemPartition;
};
namespace dpdev {
// };
// class UNIXDevice: public dpdev::Device
// {
// public:
// UNIXDevice();
// virtual ~UNIXDevice();
// virtual void* getInterfaceID();
// virtual void getOptionalInterface(char const*);
// virtual void prepareDeviceKey();
// virtual void getProvider();
// virtual void getIndex();
// virtual void getDeviceName();
// virtual void getDeviceType();
// virtual void getFingerprint();
// virtual void getDeviceKey();
// virtual void getActivationRecord();
// virtual void setActivationRecord(dp::Data const&);
// virtual void getPartition(int);
// virtual void getVersionInfo(dp::String const&);
// virtual void isTrusted();
// void addRemovablePartition(dp::String const&, dp::String const&, dp::String const&);
// void removeRemovablePartition(dp::String const&);
// void writeDeviceFile(char const*);
class UNIXDevice: public dpdev::Device
{
public:
UNIXDevice();
virtual ~UNIXDevice();
//virtual void* getInterfaceID();
virtual void* getOptionalInterface(char const*);
virtual void prepareDeviceKey();
virtual dpdev::DeviceProvider* getProvider();
virtual int getIndex();
virtual dp::String getDeviceName();
virtual dp::String getDeviceType();
virtual dp::Data getFingerprint();
virtual dp::Data getDeviceKey();
virtual dp::Data getActivationRecord();
virtual void setActivationRecord(dp::Data const&);
virtual dpio::Partition* getPartition(int);
virtual dp::String getVersionInfo(dp::String const&);
virtual bool isTrusted();
void addRemovablePartition(dp::String const&, dp::String const&, dp::String const&);
void removeRemovablePartition(dp::String const&);
void writeDeviceFile(char const*);
dpio::FilesystemPartition* partition;
unsigned int m1, m2;
void *m3, *m4, *m5, *m6;
void *m7, *m8, *m9, *m10;
void *m11;
// };
};
// class UNIXDeviceProvider: public dpdev::DeviceProvider
// {
include/rmsdk/dpdrm.h
1212
1313
1414
15
1516
1617
1718
......
3536
3637
3738
38
39
39
40
4041
4142
4243
43
44
4445
4546
4647
......
5253
5354
5455
55
56
5657
5758
5859
......
176177
177178
178179
179
180
180
181
181182
182183
183184
namespace adept {
class FulfillmentItemList;
class ActivationImpl;
}
namespace dpdrm {
virtual void* getInterfaceID();
virtual void addRef() = 0;
virtual void release() = 0;
virtual void getUserID() = 0;
virtual void getDeviceID() = 0;
virtual uft::String getUserID() = 0;
virtual uft::String getDeviceID() = 0;
virtual void getExpiration() = 0;
virtual void getAuthority() = 0;
virtual void getUsername() = 0;
virtual void hasCredentials() = 0;
virtual bool hasCredentials() = 0;
};
virtual ~DRMProcessor();
virtual void* getInterfaceID();
virtual void release() = 0;
virtual void getActivations() = 0;
virtual dp::list<adept::ActivationImpl> getActivations() = 0;
virtual void setUser(dp::String const&) = 0;
virtual void setPartition(dpio::Partition*) = 0;
virtual void reset() = 0;
};
// void getAuthenticationCertificate(dp::ref<dpdrm::Activation>);
// void getUserCertificate(dp::ref<dpdrm::Activation>);
void getAuthenticationCertificate(dp::ref<dpdrm::Activation>);
void getUserCertificate(dp::ref<dpdrm::Activation>);
void signChallenge(dp::String const&, dp::String const&);
}
include/rmsdk/dpio.h
144144
145145
146146
147
148
149
150
147
148
149
150
151151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
152
153
154
155
156
157
158
159
160
161
162
163
164
165
168166
169
167
168
169
170170
171171
172172
};
// class FilesystemPartition: public dpio::Partition
// {
// public:
// FilesystemPartition(dpdev::Device*, int, dp::String const&, dp::String const&, dp::String const&, dp::String const&);
class FilesystemPartition: public dpio::Partition
{
public:
FilesystemPartition(dpdev::Device*, int, dp::String const&, dp::String const&, dp::String const&, dp::String const&);
// virtual ~FilesystemPartition();
// virtual void* getInterfaceID();
// virtual void getOptionalInterface(char const*);
// virtual dpdev::Device* getDevice();
// virtual int getIndex();
// virtual dp::String getPartitionName();
// virtual dp::String getPartitionType();
// virtual dp::String getRootURL();
// virtual dp::String getDocumentFolderURL();
// virtual dp::String getTemporaryFolderURL();
// virtual dpio::Stream* readFile(dp::String const&, dpio::StreamClient*, unsigned int);
// virtual void createUniqueFile(dp::String const&, dp::String const&, dp::Callback*);
// virtual void writeFile(dp::String const&, dpio::Stream*, dp::Callback*);
// virtual void removeFile(dp::String const&, dp::Callback*);
// void setIndex(int);
virtual ~FilesystemPartition();
virtual void* getInterfaceID();
virtual void* getOptionalInterface(char const*);
virtual dpdev::Device* getDevice();
virtual int getIndex();
virtual dp::String getPartitionName();
virtual dp::String getPartitionType();
virtual dp::String getRootURL();
virtual dp::String getDocumentFolderURL();
virtual dp::String getTemporaryFolderURL();
virtual dpio::Stream* readFile(dp::String const&, dpio::StreamClient*, unsigned int);
virtual void createUniqueFile(dp::String const&, dp::String const&, dp::Callback*);
virtual void writeFile(dp::String const&, dpio::Stream*, dp::Callback*);
virtual void removeFile(dp::String const&, dp::Callback*);
// };
void setIndex(int);
};
class MessagePipe
{
include/rmsdk/librmsdk.h
2020
2121
2222
23
23
2424
2525
2626
#define WORKFLOW_ACTIVATION 0x10
#define WORKFLOW_FULFILLMENT 0x20
#define WORKFLOW_LOAN_RETURN 0x80
#define WORKFLOW_UPDATE_LOAD 0x100
#define WORKFLOW_UPDATE_LOAN 0x100
#define WORKFLOW_DOWNLOAD 0x200
#define WORKFLOW_JOIN_ACCOUNTS 0x400
#define WORKFLOW_GET_CREDENTIAL_LIST 0x800
include/rmsdk/uft.h
468468
469469
470470
471
471
472472
473473
474474
......
636636
637637
638638
639
639
640640
641641
642642
......
10371037
10381038
10391039
1040
1040
10411041
10421042
10431043
virtual ~ErrorHandler();
virtual void reportStateError(uft::String const&) = 0;
virtual void reportProcessError(uft::String const&) = 0;
virtual void getURLString() = 0;
virtual uft::String getURLString() = 0;
virtual void changeURL(uft::String const&) = 0;
};
virtual ~ParseErrorHandler();
virtual void reportStateError(uft::String const&);
virtual void reportProcessError(uft::String const&);
virtual void getURLString();
virtual uft::String getURLString();
virtual void changeURL(uft::String const&);
virtual void setURLString(uft::String const&);
void trimWhitespace() const;
void uppercase() const;
void utf16length() const;
void utf8() const;
char* utf8() const;
void writableBuffer() const;
void writableBuffer(unsigned int) const;
include/rmsdk_wrapper.h
3636
3737
3838
39
3940
4041
4142
......
4445
4546
4647
47
48
48
49
4950
5051
5152
......
5960
6061
6162
62
63
63
64
65
66
67
6468
6569
6670
......
114118
115119
116120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
117139
118140
119141
......
160182
161183
162184
163
185
164186
165187
166188
......
294316
295317
296318
297
319
320
298321
299322
300323
......
308331
309332
310333
311
334
312335
313336
314337
......
337360
338361
339362
340
363
341364
342365
343366
......
350373
351374
352375
353
354376
355377
356378
......
373395
374396
375397
376
377
378398
379399
380400
......
382402
383403
384404
385
386405
387406
388407
......
392411
393412
394413
395
414
415
396416
397417
418
419
420
421
422
423
398424
399425
400426
......
411437
412438
413439
414
440
415441
416442
443
417444
418445
419446
......
429456
430457
431458
432
459
460
433461
434462
435463
464
436465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
437483
438484
439485
......
509555
510556
511557
558
512559
560
561
562
563
513564
514565
566
515567
516568
517569
......
521573
522574
523575
524
576
525577
526578
527579
#include <QNetworkReply>
#include <QCoreApplication>
#include <QFile>
#include <QDir>
#include <log.h>
class MockDRMProcessorClient: public dpdrm::DRMProcessorClient
{
public:
MockDRMProcessorClient(const char* outputDir, const char* outputFile):
outputDir(outputDir), outputFile(outputFile),
MockDRMProcessorClient(const char* outputDir, const char* outputFile, const char* tmpDir=0):
outputDir(outputDir), outputFile(outputFile), tmpDir(tmpDir),
sourceName(0), targetName(0)
{}
virtual void workflowFinished(unsigned int workflow, dp::String& str)
{
LOG(DEBUG, "Workflow finished " << workflow << " " << str.utf8());
LOG(DEBUG, "Workflow finished " << workflow);
if (str.utf8())
{
LOG(DEBUG, str.utf8());
}
if (workflow & WORKFLOW_FULFILLMENT)
{
dp::list<dpdrm::FulfillmentItem> list = processor->getFulfillmentItems();
LOG(INFO, "Language : " << language.utf8());
}
}
else if (workflow & WORKFLOW_AUTH_SIGN_IN)
{
}
else if (workflow & WORKFLOW_ACTIVATION)
{
QDir dir;
LOG(DEBUG, "Move dir " << tmpDir << "/.adept -> " << outputDir);
if (dir.rename(QString(tmpDir) + "/.adept", QString(outputDir)))
{
LOG(INFO, "Created " << outputDir);
dir.setPath(tmpDir);
dir.removeRecursively();
}
else
{
LOG(ERROR, "Error : Unable to move " << tmpDir << "/.adept into " << outputDir);
}
}
}
virtual void mockclientfn3(void) {LOG_FUNC();}
private:
adept::DRMProcessorImpl* processor;
const char *outputDir, *outputFile;
const char *outputDir, *outputFile, *tmpDir;
char* sourceName;
char* targetName;
/*
class MockDevice : public dpdev::Device, public mdom::DocumentHandler
{
public:
MockDevice(dpdrm::DRMProcessorClient* processorClient, const char* deviceFile, const char* activationFile, const char* devkeyFile) :deviceProvider(0), processorClient(processorClient)
MockDevice(dpdrm::DRMProcessorClient* processorClient, const char* deviceFile, const char* activationFile, const char* devkeyFile) :
deviceProvider(0), processorClient(processorClient), activationFile(activationFile)
{
partition = new MockPartition(this, 0, "root", "Fixed", "/tmp", "/tmp", "/tmp");
unsigned char* deviceXML;
readFile(deviceFile, &deviceXML, 0, true);
wisdom = adept::parseXML((const char*)deviceXML);
mdom::Node node = wisdom->getRoot();
node.walkBranch(this);
virtual void setActivationRecord(const dp::Data& data);
virtual dpio::Partition* getPartition(int index) {LOG_FUNC(); return partition;}
virtual dp::String getVersionInfo(const dp::String& name);
virtual bool isTrusted() {LOG_FUNC();return false;};
virtual bool isTrusted() {LOG_FUNC();return true;};
/* mdom::DocumentHandler */
virtual bool characters(uft::Value const&) {return true;}
virtual bool startElement(mdom::Node const& node, uft::Value const& xmlns, uft::Value const& element, uft::Value const& ns, mdom::NameValueIterator* attributesIterator) {
if (!element.isNull() && element.isString())
{
int i=0;
char* name = element.toString().c_str();
uft::String value = adept::getChildValue(node, 3);
char* attrValue = p1.toString().c_str();
if (attrName && attrValue)
dp::setVersionInfo(attrName, attrValue);
i++;
}
else if (!strcmp(name, "fingerprint"))
{
int res = dp::decodeBase64(value.c_str(), rawFingerprint, sizeof(rawFingerprint));
fingerprint = dp::Data(rawFingerprint, res);
}
i++;
}
return true;
}
{
int _length, fd;
QFile file(filePath);
*res = 0;
if (!length) length = &_length;
if (!file.exists())
{
*length = 0;
return 0;
}
if (addFinalZero)
*res = new unsigned char[file.size()+1];
else
return *length;
}
private:
private:
dpdev::DeviceProvider* deviceProvider;
dpdrm::DRMProcessorClient* processorClient;
const char *activationFile;
unsigned char *activationRecord;
int activationRecordLength;
unsigned char *devkey;
void MockDevice::setActivationRecord( const dp::Data& data ) {
LOG_FUNC();
delete[] activationRecord;
if (activationRecord)
delete[] activationRecord;
activationRecordLength = data.length();
activationRecord = new unsigned char[activationRecordLength];
memcpy(activationRecord, data.data(), activationRecordLength);
LOG(DEBUG, "New activation record :" << std::endl << activationRecord);
if (activationFile)
{
QFile file(activationFile);
bool ret = file.open(QIODevice::WriteOnly|QIODevice::Truncate);
if (!ret)
{
LOG(ERROR, "Unable to open file " << activationFile);
return;
}
else
LOG(WARN, "Write file " << activationFile);
file.write((const char*)data.data(), data.length());
file.close();
}
}
dp::String MockDevice::getVersionInfo( const dp::String& name ) {
LOG(DEBUG, "Reply " << bytes << " bytes");
if (reply->hasRawHeader("Content-Type"))
{
client->propertyReady("Content-Type", reply->rawHeader("Content-Type").constData());
LOG(DEBUG, "Content-Type " << reply->rawHeader("Content-Type").constData());
if (!strcmp(reply->rawHeader("Content-Type").constData(), "application/vnd.adobe.adept+xml"))
LOG(DEBUG, "<<< " << replyData);
}
if (reply->hasRawHeader("Content-Length"))
client->propertyReady("Content-Length", reply->rawHeader("Content-Length").constData());
client->totalLengthReady(bytes);
client->bytesReady(0, dp::Data(replyData, bytes), true);
}
LOG_FUNC();
}
virtual void adjustModifiedStream(unsigned int offset, unsigned int len, void* param0, dpio::StreamClient * client)
virtual void adjustModifiedStream(unsigned int offset, unsigned int len, void* param2, dpio::StreamClient* client)
{
LOG_FUNC();
setStreamClient(client);
scripts/activate.sh
1
2
3
4
#!/bin/bash
export LD_LIBRARY_PATH=$PWD/lib:$LD_LIBRARY_PATH
./activate $@
src/acsmdownloader.cpp
3333
3434
3535
36
36
3737
3838
3939
4040
41
4142
4243
4344
......
110111
111112
112113
113
114
114115
115116
116117
117118
118119
119120
121
122
120123
121124
122125
......
132135
133136
134137
135
138
136139
137140
138141
139
140
142
143
141144
142145
143146
144147
145148
146149
147
150
148151
152
149153
150154
151155
......
154158
155159
156160
157
161
158162
159163
160164
......
207211
208212
209213
210
214
211215
212216
213217
214218
215219
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234220
235221
236222
237
238
223
224
239225
240226
241227
242228
243229
244
230
231
232
233
234
235
236
237
245238
246239
247240
......
252245
253246
254247
255
256
248
249
257250
258251
259252
static const char* deviceFile = "device.xml";
static const char* activationFile = "activation.xml";
static const char* devicekeyFile = 0;
static const char* devicekeyFile = "devicesalt";
static const char* acsmFile = 0;
static const char* outputFile = 0;
static const char* outputDir = 0;
static const char* defaultDirs[] = {
".adept/",
"./adobe-digital-editions/",
"./.adobe-digital-editions/"
};
QCoreApplication* app;
};
static const char* findFile(const char* filename)
static const char* findFile(const char* filename, bool inDefaultDirs=true)
{
QFile file(filename);
if (file.exists())
return strdup(filename);
if (!inDefaultDirs) return 0;
for (int i=0; i<(int)ARRAY_SIZE(defaultDirs); i++)
{
uft::String path = uft::String(defaultDirs[i]) + filename;
{
std::cout << "Download EPUB file from ACSM request file" << std::endl;
std::cout << "Usage: ./acsmdownloader [(-d|--device-file) device.xml] [(-a|--activation-file) activation.xml] [(-O|--output-dir) dir] [(-o|--output-file) output.epub] [(-v|--verbose)] [(-h|--help)] (-k|--device-key-file) devkey.bin (-f|--acsm-file) file.acsm" << std::endl << std::endl;
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;
std::cout << " " << "-d|--device-file" << "\t" << "device.xml file from ereader" << std::endl;
std::cout << " " << "-a|--activation-file" << "\t" << "activation.xml file from ereader" << std::endl;
std::cout << " " << "-k|--device-key-file" << "\t" << "private device key file (eg devkey.bin) from ereader" << std::endl;
std::cout << " " << "-O|--output-dir" << "\t" << "Optional output directory were to put result (default ./)" << std::endl;
std::cout << " " << "-k|--device-key-file" << "\t" << "private device key file (eg devicesalt/devkey.bin) from ereader" << std::endl;
std::cout << " " << "-O|--output-dir" << "\t" << "Optional output directory were to put result (default ./)" << std::endl;
std::cout << " " << "-o|--output-file" << "\t" << "Optional output epub filename (default <title.epub>)" << std::endl;
std::cout << " " << "-f|--acsm-file" << "\t" << "ACSM request file for epub download" << 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;
std::cout << "Device file and activation file are optional. If not set, they are looked into :" << std::endl;
std::cout << "Device file, activation file and device key file are optionals. If not set, they are looked into :" << std::endl;
std::cout << " * Current directory" << std::endl;
std::cout << " * .adept" << std::endl;
std::cout << " * adobe-digital-editions directory" << std::endl;
std::cout << " * .adobe-digital-editions directory" << std::endl;
}
{
int c, ret = -1;
const char** files[] = {&devicekeyFile, &acsmFile};
const char** files[] = {&devicekeyFile, &deviceFile, &activationFile};
while (1) {
int option_index = 0;
}
}
if (!devicekeyFile || !acsmFile)
if (!acsmFile)
{
usage();
return -1;
}
deviceFile = findFile(deviceFile);
if (!deviceFile)
{
deviceFile = (deviceFile)?:(char*)"device.xml";
LOG(ERROR, "Error : " << deviceFile << " not found");
return -1;
}
activationFile = findFile(activationFile);
if (!activationFile)
{
free((void*)deviceFile);
activationFile = (activationFile)?:(char*)"activation.xml";
LOG(ERROR, "Error : " << activationFile << " not found");
return -1;
}
QFile file;
int i;
for (i=0; i<(int)ARRAY_SIZE(files); i++)
{
file.setFileName(*files[i]);
if (!file.exists())
*files[i] = findFile(*files[i]);
if (!*files[i])
{
LOG(ERROR, "Error : " << *files[i] << " doesn't exists");
return -1;
}
}
QFile file(acsmFile);
if (!file.exists())
{
LOG(ERROR, "Error : " << acsmFile << " doesn't exists");
return -1;
}
LOG(INFO, "RMSDK Version " << dp::getVersionInfo("hobbes").utf8());
QCoreApplication app(argc, argv);
ret = app.exec();
free((void*)deviceFile);
free((void*)activationFile);
for (i=0; i<(int)ARRAY_SIZE(files); i++)
free((void*)*files[i]);
return ret;
}
src/activate.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
/*
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 <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 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;
// 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;
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);
// drmprocessor->initSignInWorkflow(workflows,
// dp::String("AdobeID"),
// dp::String("hansored1@mytrashmailer.com"),
// dp::String("Hot542!K"));
LOG(DEBUG, "Start work");
drmprocessor->startWorkflows(workflows);
LOG(DEBUG, "Bye bye");
_this->app->exit(0);
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] [(-v|--verbose)] [(-h|--help)]" << std::endl << std::endl;
std::cout << " " << "-u|--username" << "\t" << "AdobeID username (ie adobe.com email account)" << std::endl;
std::cout << " " << "-p|--password" << "\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 << " " << "-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;
}
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' },
{"verbose", no_argument, 0, 'v' },
{"help", no_argument, 0, 'h' },
{0, 0, 0, 0 }
};
c = getopt_long(argc, argv, "u:p:O:vh",
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 'v':
verbose++;
break;
case 'h':
usage();
return 0;
break;
default:
usage();
return -1;
}
}
if (!username)
{
usage();
return -1;
}
QDir dir;
if (!_outputDir || _outputDir[0] == 0)
{
dir.setPath("./.adept");
outputDir = getcwd(0, 0);
if (dir.exists())
{
LOG(ERROR, "Error, output directory ./.adept already exists");
return -1;
}
}
else
{
// Relative path
if (_outputDir[0] == '.' || _outputDir[0] != '/')
outputDir = realpath(_outputDir, 0);
else
outputDir = strdup(_outputDir);
dir.setPath(outputDir);
if (dir.exists())
{
LOG(ERROR, "Error, output directory " << outputDir << " 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();
}
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();
delete[] outputDir;
return ret;
}

Archive Download the corresponding diff file

Branches