Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68bf982b6f | ||
|
|
ef8c2644ca | ||
|
|
e05639c09d | ||
|
|
69865e005b | ||
|
|
fd38e84da6 | ||
|
|
92a67312bd | ||
|
|
29d298b373 | ||
|
|
40dcb7a041 | ||
|
|
e0bb1bd4f8 | ||
|
|
9388d82138 | ||
|
|
c19279397f | ||
|
|
bb5349d710 | ||
|
|
9a75213b49 | ||
|
|
e06d20a392 | ||
|
|
a0f6324999 | ||
|
|
c259cbd5a3 | ||
|
|
46afe771c7 | ||
|
|
cad2189fc2 | ||
|
|
50bc16079f | ||
|
|
1213b34250 | ||
|
|
a66dcb959c | ||
|
|
84b01a5de3 | ||
|
|
be78d24236 | ||
|
|
8aec5be244 | ||
|
|
3a0ab4b438 | ||
|
|
891ed05926 | ||
|
|
fc839e671a | ||
|
|
ab5afa5003 | ||
|
|
937c27fc23 | ||
|
|
34216d1b6e | ||
|
|
ffd2004cbb | ||
|
|
c41dd46ca7 | ||
|
|
e4bd73c03d | ||
|
|
f65e8cd9eb | ||
|
|
24bae89095 | ||
|
|
afab1c0012 | ||
|
|
7878f91cdd |
65
Makefile
65
Makefile
@@ -1,21 +1,34 @@
|
|||||||
|
PREFIX ?= /usr/local
|
||||||
|
LIBDIR ?= /lib
|
||||||
|
INCDIR ?= /include
|
||||||
|
|
||||||
AR ?= $(CROSS)ar
|
AR ?= $(CROSS)ar
|
||||||
CXX ?= $(CROSS)g++
|
CXX ?= $(CROSS)g++
|
||||||
|
|
||||||
UPDFPARSERLIB = ./lib/updfparser/libupdfparser.a
|
UPDFPARSERLIB = ./lib/updfparser/libupdfparser.a
|
||||||
|
|
||||||
CXXFLAGS=-Wall -fPIC -I./include -I./lib/pugixml/src/ -I./lib/updfparser/include
|
CXXFLAGS += -Wall -fPIC -I./include -I./usr/include/pugixml -I./lib/updfparser/include
|
||||||
LDFLAGS = $(UPDFPARSERLIB)
|
LDFLAGS = -lpugixml
|
||||||
|
|
||||||
|
VERSION := $(shell cat include/libgourou.h |grep LIBGOUROU_VERSION|cut -d '"' -f2)
|
||||||
|
|
||||||
BUILD_STATIC ?= 0
|
BUILD_STATIC ?= 0
|
||||||
BUILD_SHARED ?= 1
|
BUILD_SHARED ?= 1
|
||||||
BUILD_UTILS ?= 1
|
BUILD_UTILS ?= 1
|
||||||
|
|
||||||
TARGETS =
|
TARGETS =
|
||||||
|
TARGET_LIBRARIES =
|
||||||
|
ifneq ($(STATIC_UTILS),)
|
||||||
|
BUILD_STATIC=1
|
||||||
|
endif
|
||||||
ifneq ($(BUILD_STATIC), 0)
|
ifneq ($(BUILD_STATIC), 0)
|
||||||
TARGETS += libgourou.a
|
TARGETS += libgourou.a
|
||||||
|
TARGET_LIBRARIES += libgourou.a
|
||||||
|
STATIC_UTILS=1
|
||||||
endif
|
endif
|
||||||
ifneq ($(BUILD_SHARED), 0)
|
ifneq ($(BUILD_SHARED), 0)
|
||||||
TARGETS += libgourou.so
|
TARGETS += libgourou.so
|
||||||
|
TARGET_LIBRARIES += libgourou.so libgourou.so.$(VERSION)
|
||||||
endif
|
endif
|
||||||
ifneq ($(BUILD_UTILS), 0)
|
ifneq ($(BUILD_UTILS), 0)
|
||||||
TARGETS += build_utils
|
TARGETS += build_utils
|
||||||
@@ -33,16 +46,17 @@ CXXFLAGS += -DSTATIC_NONCE=1
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
SRCDIR := src
|
SRCDIR := src
|
||||||
INCDIR := inc
|
|
||||||
BUILDDIR := obj
|
BUILDDIR := obj
|
||||||
TARGETDIR := bin
|
|
||||||
SRCEXT := cpp
|
SRCEXT := cpp
|
||||||
OBJEXT := o
|
OBJEXT := o
|
||||||
|
|
||||||
SOURCES = src/libgourou.cpp src/user.cpp src/device.cpp src/fulfillment_item.cpp src/loan_token.cpp src/bytearray.cpp src/pugixml.cpp
|
SOURCES = src/libgourou.cpp src/user.cpp src/device.cpp src/fulfillment_item.cpp src/loan_token.cpp src/bytearray.cpp
|
||||||
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT)))
|
OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT)))
|
||||||
|
|
||||||
all: lib obj $(TARGETS)
|
all: version lib obj $(TARGETS)
|
||||||
|
|
||||||
|
version:
|
||||||
|
@echo "Building libgourou $(VERSION)"
|
||||||
|
|
||||||
lib:
|
lib:
|
||||||
mkdir lib
|
mkdir lib
|
||||||
@@ -57,21 +71,42 @@ obj:
|
|||||||
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
|
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
|
||||||
$(CXX) $(CXXFLAGS) -c $^ -o $@
|
$(CXX) $(CXXFLAGS) -c $^ -o $@
|
||||||
|
|
||||||
libgourou: libgourou.a libgourou.so
|
libgourou: $(TARGET_LIBRARIES)
|
||||||
|
|
||||||
libgourou.a: $(OBJECTS) $(UPDFPARSERLIB)
|
libgourou.a: $(OBJECTS) $(UPDFPARSERLIB)
|
||||||
$(AR) crs $@ obj/*.o $(UPDFPARSERLIB)
|
$(AR) rcs --thin $@ $^
|
||||||
|
|
||||||
libgourou.so: $(OBJECTS) $(UPDFPARSERLIB)
|
libgourou.so.$(VERSION): $(OBJECTS) $(UPDFPARSERLIB)
|
||||||
$(CXX) obj/*.o $(LDFLAGS) -o $@ -shared
|
$(CXX) $^ -Wl,-soname,$@ $(LDFLAGS) -o $@ -shared
|
||||||
|
|
||||||
build_utils:
|
libgourou.so: libgourou.so.$(VERSION)
|
||||||
make -C utils ROOT=$(PWD) CXX=$(CXX) AR=$(AR) DEBUG=$(DEBUG) STATIC_UTILS=$(STATIC_UTILS)
|
ln -f -s $^ $@
|
||||||
|
|
||||||
|
build_utils: $(TARGET_LIBRARIES)
|
||||||
|
$(MAKE) -C utils ROOT=$(PWD) CXX=$(CXX) AR=$(AR) DEBUG=$(DEBUG) STATIC_UTILS=$(STATIC_UTILS) DESTDIR=$(DESTDIR) PREFIX=$(PREFIX)
|
||||||
|
|
||||||
|
install: $(TARGET_LIBRARIES)
|
||||||
|
install -d $(DESTDIR)$(PREFIX)$(LIBDIR)
|
||||||
|
# Use cp to preserver symlinks
|
||||||
|
cp --no-dereference $(TARGET_LIBRARIES) $(DESTDIR)$(PREFIX)$(LIBDIR)
|
||||||
|
$(MAKE) -C utils ROOT=$(PWD) CXX=$(CXX) AR=$(AR) DEBUG=$(DEBUG) STATIC_UTILS=$(STATIC_UTILS) DESTDIR=$(DESTDIR) PREFIX=$(PREFIX) install
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
cd $(DESTDIR)$(PREFIX)/$(LIBDIR)
|
||||||
|
rm -f $(TARGET_LIBRARIES) libgourou.so.$(VERSION)
|
||||||
|
cd -
|
||||||
|
|
||||||
|
install_headers:
|
||||||
|
install -d $(DESTDIR)$(PREFIX)/$(INCDIR)/libgourou
|
||||||
|
cp --no-dereference include/*.h $(DESTDIR)$(PREFIX)/$(INCDIR)/libgourou
|
||||||
|
|
||||||
|
uninstall_headers:
|
||||||
|
rm -rf $(DESTDIR)$(PREFIX)/$(INCDIR)/libgourou
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf libgourou.a libgourou.so obj
|
rm -rf libgourou.a libgourou.so libgourou.so.$(VERSION)* obj
|
||||||
make -C utils clean
|
$(MAKE) -C utils clean
|
||||||
|
|
||||||
ultraclean: clean
|
ultraclean: clean
|
||||||
rm -rf lib
|
rm -rf lib
|
||||||
make -C utils ultraclean
|
$(MAKE) -C utils ultraclean
|
||||||
|
|||||||
35
README.md
35
README.md
@@ -8,7 +8,7 @@ Architecture
|
|||||||
------------
|
------------
|
||||||
|
|
||||||
Like RMSDK, libgourou has a client/server scheme. All platform specific functions (crypto, network...) has to be implemented in a client class (that derives from DRMProcessorClient) while server implements ADEPT protocol.
|
Like RMSDK, libgourou has a client/server scheme. All platform specific functions (crypto, network...) has to be implemented in a client class (that derives from DRMProcessorClient) while server implements ADEPT protocol.
|
||||||
A reference implementation using Qt, OpenSSL and libzip is provided (in _utils_ directory).
|
A reference implementation using cURL, OpenSSL and libzip is provided (in _utils_ directory).
|
||||||
|
|
||||||
Main fucntions to use from gourou::DRMProcessor are :
|
Main fucntions to use from gourou::DRMProcessor are :
|
||||||
|
|
||||||
@@ -37,12 +37,10 @@ For libgourou :
|
|||||||
|
|
||||||
_externals_ :
|
_externals_ :
|
||||||
|
|
||||||
* None
|
* libpugixml
|
||||||
|
|
||||||
_internals_ :
|
_internals_ :
|
||||||
|
|
||||||
* PugiXML
|
|
||||||
* Base64
|
|
||||||
* uPDFParser
|
* uPDFParser
|
||||||
|
|
||||||
For utils :
|
For utils :
|
||||||
@@ -50,6 +48,7 @@ For utils :
|
|||||||
* libcurl
|
* libcurl
|
||||||
* OpenSSL
|
* OpenSSL
|
||||||
* libzip
|
* libzip
|
||||||
|
* libpugixml
|
||||||
|
|
||||||
|
|
||||||
Internal libraries are automatically fetched and statically compiled during the first run.
|
Internal libraries are automatically fetched and statically compiled during the first run.
|
||||||
@@ -63,7 +62,7 @@ Compilation
|
|||||||
|
|
||||||
Use _make_ command
|
Use _make_ command
|
||||||
|
|
||||||
make [CROSS=XXX] [DEBUG=(0*|1)] [STATIC_UTILS=(0*|1)] [BUILD_UTILS=(0|1*)] [BUILD_STATIC=(0*|1)] [BUILD_SHARED=(0|1*)] [all*|clean|ultraclean|build_utils]
|
make [CROSS=XXX] [DEBUG=(0*|1)] [STATIC_UTILS=(0*|1)] [BUILD_UTILS=(0|1*)] [BUILD_STATIC=(0*|1)] [BUILD_SHARED=(0|1*)] [all*|clean|ultraclean|build_utils|install|uninstall]
|
||||||
|
|
||||||
CROSS can define a cross compiler prefix (ie arm-linux-gnueabihf-)
|
CROSS can define a cross compiler prefix (ie arm-linux-gnueabihf-)
|
||||||
|
|
||||||
@@ -77,44 +76,51 @@ BUILD_STATIC build libgourou.a if 1, nothing if 0, can be combined with BUILD_SH
|
|||||||
|
|
||||||
BUILD_SHARED build libgourou.so if 1, nothing if 0, can be combined with BUILD_STATIC
|
BUILD_SHARED build libgourou.so if 1, nothing if 0, can be combined with BUILD_STATIC
|
||||||
|
|
||||||
|
other variables are DESTDIR and PREFIX to handle destination install directory
|
||||||
|
|
||||||
* Default value
|
* Default value
|
||||||
|
|
||||||
|
|
||||||
Utils
|
Utils
|
||||||
-----
|
-----
|
||||||
|
|
||||||
You can import configuration from your eReader or create a new one with _utils/adept\_activate_ :
|
First, add libgourou.so to your LD_LIBRARY_PATH
|
||||||
|
|
||||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
||||||
|
|
||||||
|
You can optionaly specify your .adept directory
|
||||||
|
|
||||||
|
export ADEPT_DIR=/home/XXX
|
||||||
|
|
||||||
|
Then, use utils as following :
|
||||||
|
|
||||||
|
You can import configuration from your eReader or create a new one with _utils/adept\_activate_ :
|
||||||
|
|
||||||
./utils/adept_activate -u <AdobeID USERNAME>
|
./utils/adept_activate -u <AdobeID USERNAME>
|
||||||
|
|
||||||
Then a _./.adept_ directory is created with all configuration file
|
Then a _/home/<user>/.config/adept_ directory is created with all configuration file
|
||||||
|
|
||||||
To download an ePub/PDF :
|
To download an ePub/PDF :
|
||||||
|
|
||||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
./utils/acsmdownloader <ACSM_FILE>
|
||||||
./utils/acsmdownloader -f <ACSM_FILE>
|
|
||||||
|
|
||||||
To export your private key (for DeDRM software) :
|
To export your private key (for DeDRM software) :
|
||||||
|
|
||||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
|
||||||
./utils/acsmdownloader --export-private-key [-o adobekey_1.der]
|
./utils/acsmdownloader --export-private-key [-o adobekey_1.der]
|
||||||
|
|
||||||
To remove ADEPT DRM :
|
To remove ADEPT DRM :
|
||||||
|
|
||||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
./utils/adept_remove <encryptedFile>
|
||||||
./utils/adept_remove -f <encryptedFile>
|
|
||||||
|
|
||||||
To list loaned books :
|
To list loaned books :
|
||||||
|
|
||||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
|
||||||
./utils/adept_loan_mgt [-l]
|
./utils/adept_loan_mgt [-l]
|
||||||
|
|
||||||
To return a loaned book :
|
To return a loaned book :
|
||||||
|
|
||||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD
|
|
||||||
./utils/adept_loan_mgt -r <id>
|
./utils/adept_loan_mgt -r <id>
|
||||||
|
|
||||||
|
|
||||||
You can get utils full options description with -h or --help switch
|
You can get utils full options description with -h or --help switch
|
||||||
|
|
||||||
|
|
||||||
@@ -143,3 +149,4 @@ Special thanks
|
|||||||
|
|
||||||
* _Jens_ for all test samples and utils testing
|
* _Jens_ for all test samples and utils testing
|
||||||
* _Milian_ for debug & code
|
* _Milian_ for debug & code
|
||||||
|
* _Berwyn H_ for all test samples, feedbacks, patches and kind donation
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace macaron {
|
namespace macaron {
|
||||||
|
|
||||||
@@ -33,7 +34,11 @@ class Base64 {
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
static std::string Encode(const std::string data) {
|
static std::string Encode(const std::string data) {
|
||||||
static constexpr char sEncodingTable[] = {
|
static
|
||||||
|
#if __STDC_VERSION__ >= 201112L
|
||||||
|
constexpr
|
||||||
|
#endif /* __STDC_VERSION__ >= 201112L */
|
||||||
|
char sEncodingTable[] = {
|
||||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||||
@@ -73,7 +78,11 @@ class Base64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::string Decode(const std::string& input, std::string& out) {
|
static std::string Decode(const std::string& input, std::string& out) {
|
||||||
static constexpr unsigned char kDecodingTable[] = {
|
static
|
||||||
|
#if __STDC_VERSION__ >= 201112L
|
||||||
|
constexpr
|
||||||
|
#endif /* __STDC_VERSION__ >= 201112L */
|
||||||
|
unsigned char kDecodingTable[] = {
|
||||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
|
||||||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
|
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
|
||||||
|
|||||||
@@ -27,20 +27,17 @@
|
|||||||
#include "drmprocessorclient.h"
|
#include "drmprocessorclient.h"
|
||||||
|
|
||||||
#include <pugixml.hpp>
|
#include <pugixml.hpp>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifndef HOBBES_DEFAULT_VERSION
|
#ifndef HOBBES_DEFAULT_VERSION
|
||||||
#define HOBBES_DEFAULT_VERSION "10.0.4"
|
#define HOBBES_DEFAULT_VERSION "10.0.4"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DEFAULT_ADEPT_DIR
|
|
||||||
#define DEFAULT_ADEPT_DIR "./.adept"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ACS_SERVER
|
#ifndef ACS_SERVER
|
||||||
#define ACS_SERVER "http://adeactivate.adobe.com/adept"
|
#define ACS_SERVER "http://adeactivate.adobe.com/adept"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define LIBGOUROU_VERSION "0.8"
|
#define LIBGOUROU_VERSION "0.8.5"
|
||||||
|
|
||||||
namespace gourou
|
namespace gourou
|
||||||
{
|
{
|
||||||
@@ -70,10 +67,11 @@ namespace gourou
|
|||||||
* @brief Fulfill ACSM file to server in order to retrieve ePub fulfillment item
|
* @brief Fulfill ACSM file to server in order to retrieve ePub fulfillment item
|
||||||
*
|
*
|
||||||
* @param ACSMFile Path of ACSMFile
|
* @param ACSMFile Path of ACSMFile
|
||||||
|
* @param notify Notify server if requested by response
|
||||||
*
|
*
|
||||||
* @return a FulfillmentItem if all is OK
|
* @return a FulfillmentItem if all is OK
|
||||||
*/
|
*/
|
||||||
FulfillmentItem* fulfill(const std::string& ACSMFile);
|
FulfillmentItem* fulfill(const std::string& ACSMFile, bool notify=true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Once fulfilled, ePub file needs to be downloaded.
|
* @brief Once fulfilled, ePub file needs to be downloaded.
|
||||||
@@ -105,8 +103,14 @@ namespace gourou
|
|||||||
*
|
*
|
||||||
* @param loanID Loan ID received during fulfill
|
* @param loanID Loan ID received during fulfill
|
||||||
* @param operatorURL URL of operator that loans this book
|
* @param operatorURL URL of operator that loans this book
|
||||||
|
* @param notify Notify server if requested by response
|
||||||
*/
|
*/
|
||||||
void returnLoan(const std::string& loanID, const std::string& operatorURL);
|
void returnLoan(const std::string& loanID, const std::string& operatorURL, bool notify=true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return default ADEPT directory (ie /home/<user>/.config/adept)
|
||||||
|
*/
|
||||||
|
static std::string getDefaultAdeptDir(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Create a new ADEPT environment (device.xml, devicesalt and activation.xml).
|
* @brief Create a new ADEPT environment (device.xml, devicesalt and activation.xml).
|
||||||
@@ -118,7 +122,7 @@ namespace gourou
|
|||||||
* @param ACSServer Override main ACS server (default adeactivate.adobe.com)
|
* @param ACSServer Override main ACS server (default adeactivate.adobe.com)
|
||||||
*/
|
*/
|
||||||
static DRMProcessor* createDRMProcessor(DRMProcessorClient* client,
|
static DRMProcessor* createDRMProcessor(DRMProcessorClient* client,
|
||||||
bool randomSerial=false, const std::string& dirName=std::string(DEFAULT_ADEPT_DIR),
|
bool randomSerial=false, std::string dirName=std::string(""),
|
||||||
const std::string& hobbes=std::string(HOBBES_DEFAULT_VERSION),
|
const std::string& hobbes=std::string(HOBBES_DEFAULT_VERSION),
|
||||||
const std::string& ACSServer=ACS_SERVER);
|
const std::string& ACSServer=ACS_SERVER);
|
||||||
|
|
||||||
@@ -231,8 +235,11 @@ namespace gourou
|
|||||||
void buildSignInRequest(pugi::xml_document& signInRequest, const std::string& adobeID, const std::string& adobePassword, const std::string& authenticationCertificate);
|
void buildSignInRequest(pugi::xml_document& signInRequest, const std::string& adobeID, const std::string& adobePassword, const std::string& authenticationCertificate);
|
||||||
void fetchLicenseServiceCertificate(const std::string& licenseURL,
|
void fetchLicenseServiceCertificate(const std::string& licenseURL,
|
||||||
const std::string& operatorURL);
|
const std::string& operatorURL);
|
||||||
std::string encryptedKeyFirstPass(pugi::xml_document& rightsDoc, const std::string& encryptedKey, const std::string& keyType, ITEM_TYPE type);
|
void buildNotifyReq(pugi::xml_document& returnReq, pugi::xml_node& body);
|
||||||
void decryptADEPTKey(const std::string& encryptedKey, unsigned char* decryptedKey);
|
void notifyServer(pugi::xml_node& notifyRoot);
|
||||||
|
void notifyServer(pugi::xml_document& fulfillReply);
|
||||||
|
std::string encryptedKeyFirstPass(pugi::xml_document& rightsDoc, const std::string& encryptedKey, const std::string& keyType);
|
||||||
|
void decryptADEPTKey(pugi::xml_document& rightsDoc, unsigned char* decryptedKey, const unsigned char* encryptionKey=0, unsigned encryptionKeySize=0);
|
||||||
void removeEPubDRM(const std::string& filenameIn, const std::string& filenameOut, const unsigned char* encryptionKey, unsigned encryptionKeySize);
|
void removeEPubDRM(const std::string& filenameIn, const std::string& filenameOut, const unsigned char* encryptionKey, unsigned encryptionKeySize);
|
||||||
void generatePDFObjectKey(int version,
|
void generatePDFObjectKey(int version,
|
||||||
const unsigned char* masterKey, unsigned int masterKeyLength,
|
const unsigned char* masterKey, unsigned int masterKeyLength,
|
||||||
|
|||||||
@@ -64,7 +64,8 @@ namespace gourou
|
|||||||
FF_INVALID_ACSM_FILE,
|
FF_INVALID_ACSM_FILE,
|
||||||
FF_NO_HMAC_IN_ACSM_FILE,
|
FF_NO_HMAC_IN_ACSM_FILE,
|
||||||
FF_NOT_ACTIVATED,
|
FF_NOT_ACTIVATED,
|
||||||
FF_NO_OPERATOR_URL
|
FF_NO_OPERATOR_URL,
|
||||||
|
FF_SERVER_INTERNAL_ERROR
|
||||||
};
|
};
|
||||||
|
|
||||||
enum DOWNLOAD_ERROR {
|
enum DOWNLOAD_ERROR {
|
||||||
@@ -119,6 +120,7 @@ namespace gourou
|
|||||||
CLIENT_OSSL_ERROR,
|
CLIENT_OSSL_ERROR,
|
||||||
CLIENT_CRYPT_ERROR,
|
CLIENT_CRYPT_ERROR,
|
||||||
CLIENT_DIGEST_ERROR,
|
CLIENT_DIGEST_ERROR,
|
||||||
|
CLIENT_HTTP_ERROR
|
||||||
};
|
};
|
||||||
|
|
||||||
enum DRM_REMOVAL_ERROR {
|
enum DRM_REMOVAL_ERROR {
|
||||||
@@ -129,9 +131,20 @@ namespace gourou
|
|||||||
DRM_IN_OUT_EQUALS,
|
DRM_IN_OUT_EQUALS,
|
||||||
DRM_MISSING_PARAMETER,
|
DRM_MISSING_PARAMETER,
|
||||||
DRM_INVALID_KEY_SIZE,
|
DRM_INVALID_KEY_SIZE,
|
||||||
DRM_ERR_ENCRYPTION_KEY_FP
|
DRM_ERR_ENCRYPTION_KEY_FP,
|
||||||
|
DRM_INVALID_USER
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifndef _NOEXCEPT
|
||||||
|
#if __STDC_VERSION__ >= 201112L
|
||||||
|
# define _NOEXCEPT noexcept
|
||||||
|
# define _NOEXCEPT_(x) noexcept(x)
|
||||||
|
#else
|
||||||
|
# define _NOEXCEPT throw()
|
||||||
|
# define _NOEXCEPT_(x)
|
||||||
|
#endif
|
||||||
|
#endif /* !_NOEXCEPT */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic exception class
|
* Generic exception class
|
||||||
*/
|
*/
|
||||||
@@ -157,7 +170,7 @@ namespace gourou
|
|||||||
this->fullmessage = strdup(other.fullmessage);
|
this->fullmessage = strdup(other.fullmessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
~Exception()
|
~Exception() _NOEXCEPT
|
||||||
{
|
{
|
||||||
free(fullmessage);
|
free(fullmessage);
|
||||||
}
|
}
|
||||||
@@ -223,12 +236,7 @@ namespace gourou
|
|||||||
return ltrim(rtrim(s, t), t);
|
return ltrim(rtrim(s, t), t);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static inline pugi::xml_node getNode(const pugi::xml_node& root, const char* tagName, bool throwOnNull=true)
|
||||||
* @brief Extract text node from tag in document
|
|
||||||
* It can throw an exception if tag does not exists
|
|
||||||
* or just return an empty value
|
|
||||||
*/
|
|
||||||
static inline std::string extractTextElem(const pugi::xml_node& root, const char* tagName, bool throwOnNull=true)
|
|
||||||
{
|
{
|
||||||
pugi::xpath_node xpath_node = root.select_node(tagName);
|
pugi::xpath_node xpath_node = root.select_node(tagName);
|
||||||
|
|
||||||
@@ -237,10 +245,23 @@ namespace gourou
|
|||||||
if (throwOnNull)
|
if (throwOnNull)
|
||||||
EXCEPTION(GOUROU_TAG_NOT_FOUND, "Tag " << tagName << " not found");
|
EXCEPTION(GOUROU_TAG_NOT_FOUND, "Tag " << tagName << " not found");
|
||||||
|
|
||||||
return "";
|
return pugi::xml_node();
|
||||||
}
|
}
|
||||||
|
|
||||||
pugi::xml_node node = xpath_node.node().first_child();
|
return xpath_node.node();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Extract text node from tag in document
|
||||||
|
* It can throw an exception if tag does not exists
|
||||||
|
* or just return an empty value
|
||||||
|
*/
|
||||||
|
static inline std::string extractTextElem(const pugi::xml_node& root, const char* tagName, bool throwOnNull=true)
|
||||||
|
{
|
||||||
|
pugi::xml_node node = getNode(root, tagName, throwOnNull);
|
||||||
|
|
||||||
|
node = node.first_child();
|
||||||
|
|
||||||
if (!node)
|
if (!node)
|
||||||
{
|
{
|
||||||
@@ -254,6 +275,30 @@ namespace gourou
|
|||||||
return trim(res);
|
return trim(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set text node of a tag in document
|
||||||
|
* It can throw an exception if tag does not exists
|
||||||
|
*/
|
||||||
|
static inline void setTextElem(const pugi::xml_node& root, const char* tagName,
|
||||||
|
const std::string& value, bool throwOnNull=true)
|
||||||
|
{
|
||||||
|
pugi::xml_node node = getNode(root, tagName, throwOnNull);
|
||||||
|
|
||||||
|
if (!node)
|
||||||
|
{
|
||||||
|
if (throwOnNull)
|
||||||
|
EXCEPTION(GOUROU_TAG_NOT_FOUND, "Text element for tag " << tagName << " not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = node.first_child();
|
||||||
|
|
||||||
|
if (!node)
|
||||||
|
node.append_child(pugi::node_pcdata).set_value(value.c_str());
|
||||||
|
else
|
||||||
|
node.set_value(value.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Extract text attribute from tag in document
|
* @brief Extract text attribute from tag in document
|
||||||
* It can throw an exception if attribute does not exists
|
* It can throw an exception if attribute does not exists
|
||||||
@@ -261,17 +306,9 @@ namespace gourou
|
|||||||
*/
|
*/
|
||||||
static inline std::string extractTextAttribute(const pugi::xml_node& root, const char* tagName, const char* attributeName, bool throwOnNull=true)
|
static inline std::string extractTextAttribute(const pugi::xml_node& root, const char* tagName, const char* attributeName, bool throwOnNull=true)
|
||||||
{
|
{
|
||||||
pugi::xpath_node xpath_node = root.select_node(tagName);
|
pugi::xml_node node = getNode(root, tagName, throwOnNull);
|
||||||
|
|
||||||
if (!xpath_node)
|
pugi::xml_attribute attr = node.attribute(attributeName);
|
||||||
{
|
|
||||||
if (throwOnNull)
|
|
||||||
EXCEPTION(GOUROU_TAG_NOT_FOUND, "Tag " << tagName << " not found");
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
pugi::xml_attribute attr = xpath_node.node().attribute(attributeName);
|
|
||||||
|
|
||||||
if (!attr)
|
if (!attr)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,13 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Pugixml
|
|
||||||
if [ ! -d lib/pugixml ] ; then
|
|
||||||
git clone https://github.com/zeux/pugixml.git lib/pugixml
|
|
||||||
pushd lib/pugixml
|
|
||||||
git checkout latest
|
|
||||||
popd
|
|
||||||
fi
|
|
||||||
|
|
||||||
# uPDFParser
|
# uPDFParser
|
||||||
if [ ! -d lib/updfparser ] ; then
|
if [ ! -d lib/updfparser ] ; then
|
||||||
git clone git://soutade.fr/updfparser.git lib/updfparser
|
git clone git://soutade.fr/updfparser.git lib/updfparser
|
||||||
|
|||||||
@@ -1,17 +1,12 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
if [ ! -d lib/pugixml -o ! -d lib/updfparser ] ; then
|
if [ ! -d lib/updfparser ] ; then
|
||||||
echo "Some libraries are missing"
|
echo "Some libraries are missing"
|
||||||
echo "You must run this script at the top of libgourou working direcotry."
|
echo "You must run this script at the top of libgourou working direcotry."
|
||||||
echo "./lib/setup.sh must be called first (make all)"
|
echo "./lib/setup.sh must be called first (make all)"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Pugixml
|
|
||||||
pushd lib/pugixml
|
|
||||||
git pull origin latest
|
|
||||||
popd
|
|
||||||
|
|
||||||
# uPDFParser
|
# uPDFParser
|
||||||
pushd lib/updfparser
|
pushd lib/updfparser
|
||||||
git pull origin master
|
git pull origin master
|
||||||
|
|||||||
@@ -29,13 +29,23 @@
|
|||||||
#include <libgourou_log.h>
|
#include <libgourou_log.h>
|
||||||
#include <device.h>
|
#include <device.h>
|
||||||
|
|
||||||
// From https://stackoverflow.com/questions/1779715/how-to-get-mac-address-of-your-machine-using-a-c-program/35242525
|
#include <string.h>
|
||||||
|
#if defined(__linux__) || defined(linux) || defined(__linux)
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <string.h>
|
#elif (defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \
|
||||||
|
|| defined(__bsdi__) || defined(__DragonFly__) || defined(__APPLE__))
|
||||||
|
#include <ifaddrs.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <net/if_dl.h>
|
||||||
|
|
||||||
|
#define BSD_HEADERS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__linux__) || defined(linux) || defined(__linux)
|
||||||
|
// From https://stackoverflow.com/questions/1779715/how-to-get-mac-address-of-your-machine-using-a-c-program/35242525
|
||||||
int get_mac_address(unsigned char* mac_address)
|
int get_mac_address(unsigned char* mac_address)
|
||||||
{
|
{
|
||||||
struct ifreq ifr;
|
struct ifreq ifr;
|
||||||
@@ -74,6 +84,43 @@ int get_mac_address(unsigned char* mac_address)
|
|||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
#elif BSD_HEADERS
|
||||||
|
// https://stackoverflow.com/a/3978293
|
||||||
|
int get_mac_address(unsigned char* mac_address, const char* if_name = "en0")
|
||||||
|
{
|
||||||
|
ifaddrs* iflist;
|
||||||
|
int found = 0;
|
||||||
|
if (getifaddrs(&iflist) == 0) {
|
||||||
|
for (ifaddrs* cur = iflist; cur; cur = cur->ifa_next) {
|
||||||
|
if ((cur->ifa_addr->sa_family == AF_LINK) &&
|
||||||
|
(strcmp(cur->ifa_name, if_name) == 0) &&
|
||||||
|
cur->ifa_addr) {
|
||||||
|
sockaddr_dl* sdl = (sockaddr_dl*)cur->ifa_addr;
|
||||||
|
memcpy(mac_address, LLADDR(sdl), sdl->sdl_alen);
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
freeifaddrs(iflist);
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
int get_mac_address(unsigned char* mac_address)
|
||||||
|
{
|
||||||
|
GOUROU_LOG(INFO, "get_mac_address() not implemented for your platform, using a static address");
|
||||||
|
|
||||||
|
mac_address[0] = 0x8D;
|
||||||
|
mac_address[1] = 0x70;
|
||||||
|
mac_address[2] = 0x13;
|
||||||
|
mac_address[3] = 0x8D;
|
||||||
|
mac_address[4] = 0x43;
|
||||||
|
mac_address[5] = 0x27;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif /* defined(__linux__) || defined(linux) || defined(__linux) */
|
||||||
|
|
||||||
|
|
||||||
namespace gourou
|
namespace gourou
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
along with libgourou. If not, see <http://www.gnu.org/licenses/>.
|
along with libgourou. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
#include <fulfillment_item.h>
|
#include <fulfillment_item.h>
|
||||||
#include <libgourou_common.h>
|
#include <libgourou_common.h>
|
||||||
#include "user.h"
|
#include "user.h"
|
||||||
@@ -93,8 +94,12 @@ namespace gourou
|
|||||||
std::string FulfillmentItem::getMetadata(std::string name)
|
std::string FulfillmentItem::getMetadata(std::string name)
|
||||||
{
|
{
|
||||||
// https://stackoverflow.com/questions/313970/how-to-convert-an-instance-of-stdstring-to-lower-case
|
// https://stackoverflow.com/questions/313970/how-to-convert-an-instance-of-stdstring-to-lower-case
|
||||||
|
#if __STDC_VERSION__ >= 201112L
|
||||||
std::transform(name.begin(), name.end(), name.begin(),
|
std::transform(name.begin(), name.end(), name.begin(),
|
||||||
[](unsigned char c){ return std::tolower(c); });
|
[](unsigned char c){ return std::tolower(c); });
|
||||||
|
#else
|
||||||
|
std::transform(name.begin(), name.end(), name.begin(), tolower);
|
||||||
|
#endif
|
||||||
name = std::string("dc:") + name;
|
name = std::string("dc:") + name;
|
||||||
pugi::xpath_node path = metadatas.select_node(name.c_str());
|
pugi::xpath_node path = metadatas.select_node(name.c_str());
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,8 @@
|
|||||||
#include <libgourou_common.h>
|
#include <libgourou_common.h>
|
||||||
#include <libgourou_log.h>
|
#include <libgourou_log.h>
|
||||||
|
|
||||||
|
#define LOCAL_ADEPT_DIR "./.adept"
|
||||||
|
|
||||||
#define ASN_NONE 0x00
|
#define ASN_NONE 0x00
|
||||||
#define ASN_NS_TAG 0x01
|
#define ASN_NS_TAG 0x01
|
||||||
#define ASN_CHILD 0x02
|
#define ASN_CHILD 0x02
|
||||||
@@ -68,11 +70,14 @@ namespace gourou
|
|||||||
if (user) delete user;
|
if (user) delete user;
|
||||||
}
|
}
|
||||||
|
|
||||||
DRMProcessor* DRMProcessor::createDRMProcessor(DRMProcessorClient* client, bool randomSerial, const std::string& dirName,
|
DRMProcessor* DRMProcessor::createDRMProcessor(DRMProcessorClient* client, bool randomSerial, std::string dirName,
|
||||||
const std::string& hobbes, const std::string& ACSServer)
|
const std::string& hobbes, const std::string& ACSServer)
|
||||||
{
|
{
|
||||||
DRMProcessor* processor = new DRMProcessor(client);
|
DRMProcessor* processor = new DRMProcessor(client);
|
||||||
|
|
||||||
|
if (dirName == "")
|
||||||
|
dirName = getDefaultAdeptDir();
|
||||||
|
|
||||||
Device* device = Device::createDevice(processor, dirName, hobbes, randomSerial);
|
Device* device = Device::createDevice(processor, dirName, hobbes, randomSerial);
|
||||||
processor->device = device;
|
processor->device = device;
|
||||||
|
|
||||||
@@ -394,30 +399,6 @@ namespace gourou
|
|||||||
}
|
}
|
||||||
|
|
||||||
doOperatorAuth(operatorURL);
|
doOperatorAuth(operatorURL);
|
||||||
|
|
||||||
// Add new operatorURL to list
|
|
||||||
pugi::xml_document activationDoc;
|
|
||||||
user->readActivation(activationDoc);
|
|
||||||
|
|
||||||
pugi::xml_node root;
|
|
||||||
pugi::xpath_node xpathRes = activationDoc.select_node("//adept:operatorURLList");
|
|
||||||
|
|
||||||
// Create adept:operatorURLList if it doesn't exists
|
|
||||||
if (!xpathRes)
|
|
||||||
{
|
|
||||||
xpathRes = activationDoc.select_node("/activationInfo");
|
|
||||||
root = xpathRes.node();
|
|
||||||
root = root.append_child("adept:operatorURLList");
|
|
||||||
root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;
|
|
||||||
|
|
||||||
appendTextElem(root, "adept:user", user->getUUID());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
root = xpathRes.node();
|
|
||||||
|
|
||||||
appendTextElem(root, "adept:operatorURL", operatorURL);
|
|
||||||
|
|
||||||
user->updateActivationFile(activationDoc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DRMProcessor::buildFulfillRequest(pugi::xml_document& acsmDoc, pugi::xml_document& fulfillReq)
|
void DRMProcessor::buildFulfillRequest(pugi::xml_document& acsmDoc, pugi::xml_document& fulfillReq)
|
||||||
@@ -487,10 +468,28 @@ namespace gourou
|
|||||||
appendTextElem(root, "adept:licenseURL", licenseURL);
|
appendTextElem(root, "adept:licenseURL", licenseURL);
|
||||||
appendTextElem(root, "adept:certificate", certificate);
|
appendTextElem(root, "adept:certificate", certificate);
|
||||||
|
|
||||||
|
// Add new operatorURL to list
|
||||||
|
xpathRes = activationDoc.select_node("//adept:operatorURLList");
|
||||||
|
|
||||||
|
// Create adept:operatorURLList if it doesn't exists
|
||||||
|
if (!xpathRes)
|
||||||
|
{
|
||||||
|
xpathRes = activationDoc.select_node("/activationInfo");
|
||||||
|
root = xpathRes.node();
|
||||||
|
root = root.append_child("adept:operatorURLList");
|
||||||
|
root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;
|
||||||
|
|
||||||
|
appendTextElem(root, "adept:user", user->getUUID());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
root = xpathRes.node();
|
||||||
|
|
||||||
|
appendTextElem(root, "adept:operatorURL", operatorURL);
|
||||||
|
|
||||||
user->updateActivationFile(activationDoc);
|
user->updateActivationFile(activationDoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
FulfillmentItem* DRMProcessor::fulfill(const std::string& ACSMFile)
|
FulfillmentItem* DRMProcessor::fulfill(const std::string& ACSMFile, bool notify)
|
||||||
{
|
{
|
||||||
if (!user->getPKCS12().length())
|
if (!user->getPKCS12().length())
|
||||||
EXCEPTION(FF_NOT_ACTIVATED, "Device not activated");
|
EXCEPTION(FF_NOT_ACTIVATED, "Device not activated");
|
||||||
@@ -500,6 +499,13 @@ namespace gourou
|
|||||||
if (!acsmDoc.load_file(ACSMFile.c_str(), pugi::parse_ws_pcdata_single|pugi::parse_escapes, pugi::encoding_utf8))
|
if (!acsmDoc.load_file(ACSMFile.c_str(), pugi::parse_ws_pcdata_single|pugi::parse_escapes, pugi::encoding_utf8))
|
||||||
EXCEPTION(FF_INVALID_ACSM_FILE, "Invalid ACSM file " << ACSMFile);
|
EXCEPTION(FF_INVALID_ACSM_FILE, "Invalid ACSM file " << ACSMFile);
|
||||||
|
|
||||||
|
// Could be an server internal error
|
||||||
|
pugi::xml_node rootNode = acsmDoc.first_child();
|
||||||
|
if (std::string(rootNode.name()) == "error")
|
||||||
|
{
|
||||||
|
EXCEPTION(FF_SERVER_INTERNAL_ERROR, rootNode.attribute("data").value());
|
||||||
|
}
|
||||||
|
|
||||||
GOUROU_LOG(INFO, "Fulfill " << ACSMFile);
|
GOUROU_LOG(INFO, "Fulfill " << ACSMFile);
|
||||||
|
|
||||||
// Build req file
|
// Build req file
|
||||||
@@ -507,7 +513,7 @@ namespace gourou
|
|||||||
|
|
||||||
buildFulfillRequest(acsmDoc, fulfillReq);
|
buildFulfillRequest(acsmDoc, fulfillReq);
|
||||||
pugi::xpath_node root = fulfillReq.select_node("//adept:fulfill");
|
pugi::xpath_node root = fulfillReq.select_node("//adept:fulfill");
|
||||||
pugi::xml_node rootNode = root.node();
|
rootNode = root.node();
|
||||||
|
|
||||||
// Remove HMAC
|
// Remove HMAC
|
||||||
pugi::xpath_node xpathRes = fulfillReq.select_node("//hmac");
|
pugi::xpath_node xpathRes = fulfillReq.select_node("//hmac");
|
||||||
@@ -568,7 +574,12 @@ namespace gourou
|
|||||||
|
|
||||||
fetchLicenseServiceCertificate(licenseURL, operatorURL);
|
fetchLicenseServiceCertificate(licenseURL, operatorURL);
|
||||||
|
|
||||||
return new FulfillmentItem(fulfillReply, user);
|
FulfillmentItem* item = new FulfillmentItem(fulfillReply, user);
|
||||||
|
|
||||||
|
if (notify)
|
||||||
|
notifyServer(fulfillReply);
|
||||||
|
|
||||||
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
DRMProcessor::ITEM_TYPE DRMProcessor::download(FulfillmentItem* item, std::string path, bool resume)
|
DRMProcessor::ITEM_TYPE DRMProcessor::download(FulfillmentItem* item, std::string path, bool resume)
|
||||||
@@ -845,7 +856,24 @@ namespace gourou
|
|||||||
signNode(root);
|
signNode(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DRMProcessor::returnLoan(const std::string& loanID, const std::string& operatorURL)
|
std::string DRMProcessor::getDefaultAdeptDir(void)
|
||||||
|
{
|
||||||
|
#ifndef DEFAULT_ADEPT_DIR
|
||||||
|
const char* user = getenv("USER");
|
||||||
|
|
||||||
|
if (user && user[0])
|
||||||
|
{
|
||||||
|
return std::string("/home/") + user + std::string("/.config/adept/");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return LOCAL_ADEPT_DIR;
|
||||||
|
#else
|
||||||
|
return DEFAULT_ADEPT_DIR "/";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void DRMProcessor::returnLoan(const std::string& loanID, const std::string& operatorURL,
|
||||||
|
bool notify)
|
||||||
{
|
{
|
||||||
pugi::xml_document returnReq;
|
pugi::xml_document returnReq;
|
||||||
|
|
||||||
@@ -853,7 +881,71 @@ namespace gourou
|
|||||||
|
|
||||||
buildReturnReq(returnReq, loanID, operatorURL);
|
buildReturnReq(returnReq, loanID, operatorURL);
|
||||||
|
|
||||||
sendRequest(returnReq, operatorURL + "/LoanReturn");
|
ByteArray replyData = sendRequest(returnReq, operatorURL + "/LoanReturn");
|
||||||
|
|
||||||
|
pugi::xml_document fulfillReply;
|
||||||
|
|
||||||
|
fulfillReply.load_string((const char*)replyData.data());
|
||||||
|
|
||||||
|
if (notify)
|
||||||
|
notifyServer(fulfillReply);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DRMProcessor::buildNotifyReq(pugi::xml_document& returnReq, pugi::xml_node& body)
|
||||||
|
{
|
||||||
|
pugi::xml_node decl = returnReq.append_child(pugi::node_declaration);
|
||||||
|
decl.append_attribute("version") = "1.0";
|
||||||
|
|
||||||
|
pugi::xml_node root = returnReq.append_child("adept:notification");
|
||||||
|
root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;
|
||||||
|
|
||||||
|
appendTextElem(root, "adept:user", user->getUUID());
|
||||||
|
appendTextElem(root, "adept:device", user->getDeviceUUID());
|
||||||
|
body = root.append_copy(body);
|
||||||
|
body.append_attribute("xmlns") = ADOBE_ADEPT_NS;
|
||||||
|
|
||||||
|
addNonce(root);
|
||||||
|
signNode(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DRMProcessor::notifyServer(pugi::xml_node& notifyRoot)
|
||||||
|
{
|
||||||
|
std::string notifyUrl = extractTextElem(notifyRoot, "//notifyURL", false);
|
||||||
|
pugi::xml_node notifyBody = getNode(notifyRoot, "//body", false);
|
||||||
|
|
||||||
|
if (notifyUrl == "")
|
||||||
|
{
|
||||||
|
GOUROU_LOG(INFO, "No notify URL");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!notifyBody)
|
||||||
|
{
|
||||||
|
GOUROU_LOG(INFO, "No notify body");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pugi::xml_document notifyReq;
|
||||||
|
buildNotifyReq(notifyReq, notifyBody);
|
||||||
|
|
||||||
|
sendRequest(notifyReq, notifyUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DRMProcessor::notifyServer(pugi::xml_document& fulfillReply)
|
||||||
|
{
|
||||||
|
pugi::xpath_node_set notifySet = fulfillReply.select_nodes("//notify");
|
||||||
|
|
||||||
|
if (notifySet.empty())
|
||||||
|
{
|
||||||
|
GOUROU_LOG(DEBUG, "No notify request");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (pugi::xpath_node_set::const_iterator it = notifySet.begin(); it != notifySet.end(); ++it)
|
||||||
|
{
|
||||||
|
pugi::xml_node notifyRoot = it->node();
|
||||||
|
notifyServer(notifyRoot);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteArray DRMProcessor::encryptWithDeviceKey(const unsigned char* data, unsigned int len)
|
ByteArray DRMProcessor::encryptWithDeviceKey(const unsigned char* data, unsigned int len)
|
||||||
@@ -931,45 +1023,33 @@ namespace gourou
|
|||||||
void DRMProcessor::exportPrivateLicenseKey(std::string path)
|
void DRMProcessor::exportPrivateLicenseKey(std::string path)
|
||||||
{
|
{
|
||||||
int fd = open(path.c_str(), O_CREAT|O_TRUNC|O_WRONLY, S_IRWXU);
|
int fd = open(path.c_str(), O_CREAT|O_TRUNC|O_WRONLY, S_IRWXU);
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (fd <= 0)
|
if (fd <= 0)
|
||||||
EXCEPTION(GOUROU_FILE_ERROR, "Unable to open " << path);
|
EXCEPTION(GOUROU_FILE_ERROR, "Unable to open " << path);
|
||||||
|
|
||||||
ByteArray privateLicenseKey = ByteArray::fromBase64(user->getPrivateLicenseKey());
|
ByteArray privateLicenseKey = ByteArray::fromBase64(user->getPrivateLicenseKey());
|
||||||
/* In adobekey.py, we get base64 decoded data [26:] */
|
/* In adobekey.py, we get base64 decoded data [26:] */
|
||||||
write(fd, privateLicenseKey.data()+26, privateLicenseKey.length()-26);
|
ret = write(fd, privateLicenseKey.data()+26, privateLicenseKey.length()-26);
|
||||||
|
|
||||||
close(fd);
|
close(fd);
|
||||||
|
if (ret != (int)(privateLicenseKey.length()-26))
|
||||||
|
{
|
||||||
|
EXCEPTION(gourou::GOUROU_FILE_ERROR, "Error writing " << path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int DRMProcessor::getLogLevel() {return (int)gourou::logLevel;}
|
int DRMProcessor::getLogLevel() {return (int)gourou::logLevel;}
|
||||||
void DRMProcessor::setLogLevel(int logLevel) {gourou::logLevel = (GOUROU_LOG_LEVEL)logLevel;}
|
void DRMProcessor::setLogLevel(int logLevel) {gourou::logLevel = (GOUROU_LOG_LEVEL)logLevel;}
|
||||||
|
|
||||||
void DRMProcessor::decryptADEPTKey(const std::string& encryptedKey, unsigned char* decryptedKey)
|
|
||||||
{
|
|
||||||
if (encryptedKey.size() != 172)
|
|
||||||
EXCEPTION(DRM_INVALID_KEY_SIZE, "Invalid encrypted key size (" << encryptedKey.size() << "). DRM version not supported");
|
|
||||||
|
|
||||||
ByteArray arrayEncryptedKey = ByteArray::fromBase64(encryptedKey);
|
|
||||||
|
|
||||||
std::string privateKeyData = user->getPrivateLicenseKey();
|
|
||||||
ByteArray privateRSAKey = ByteArray::fromBase64(privateKeyData);
|
|
||||||
|
|
||||||
dumpBuffer(gourou::LG_LOG_DEBUG, "To decrypt : ", arrayEncryptedKey.data(), arrayEncryptedKey.length());
|
|
||||||
|
|
||||||
client->RSAPrivateDecrypt(privateRSAKey.data(), privateRSAKey.length(),
|
|
||||||
RSAInterface::RSA_KEY_PKCS8, "",
|
|
||||||
arrayEncryptedKey.data(), arrayEncryptedKey.length(), decryptedKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RSA Key can be over encrypted with AES128-CBC if keyType attribute is set
|
* RSA Key can be over encrypted with AES128-CBC if keyType attribute is set
|
||||||
* For EPUB, Key = SHA256(keyType)[14:22] || SHA256(keyType)[7:13]
|
* remainder = keyType % 16
|
||||||
* For PDF, Key = SHA256(keyType)[6:19] || SHA256(keyType)[3:6]
|
* Key = SHA256(keyType)[remainder*2:remainder*2+(16-remainder)] || SHA256(keyType)[16-remainder:16]
|
||||||
* IV = DeviceID ^ FulfillmentId ^ VoucherId
|
* IV = DeviceID ^ FulfillmentId ^ VoucherId
|
||||||
*
|
*
|
||||||
* @return Base64 encoded decrypted key
|
* @return Base64 encoded decrypted key
|
||||||
*/
|
*/
|
||||||
std::string DRMProcessor::encryptedKeyFirstPass(pugi::xml_document& rightsDoc, const std::string& encryptedKey, const std::string& keyType, ITEM_TYPE type)
|
std::string DRMProcessor::encryptedKeyFirstPass(pugi::xml_document& rightsDoc, const std::string& encryptedKey, const std::string& keyType)
|
||||||
{
|
{
|
||||||
unsigned char digest[32], key[16], iv[16];
|
unsigned char digest[32], key[16], iv[16];
|
||||||
unsigned int dataOutLength;
|
unsigned int dataOutLength;
|
||||||
@@ -979,17 +1059,11 @@ namespace gourou
|
|||||||
|
|
||||||
dumpBuffer(gourou::LG_LOG_DEBUG, "SHA of KeyType : ", digest, sizeof(digest));
|
dumpBuffer(gourou::LG_LOG_DEBUG, "SHA of KeyType : ", digest, sizeof(digest));
|
||||||
|
|
||||||
switch(type)
|
long nonce = std::stol(keyType);
|
||||||
{
|
int remainder = nonce % 16;
|
||||||
case EPUB:
|
|
||||||
memcpy(key, &digest[14], 9);
|
memcpy(key, &digest[remainder*2], 16-remainder);
|
||||||
memcpy(&key[9], &digest[7], 7);
|
memcpy(&key[16-remainder], &digest[remainder], remainder);
|
||||||
break;
|
|
||||||
case PDF:
|
|
||||||
memcpy(key, &digest[6], 13);
|
|
||||||
memcpy(&key[13], &digest[3], 3);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
id = extractTextElem(rightsDoc, "/adept:rights/licenseToken/device");
|
id = extractTextElem(rightsDoc, "/adept:rights/licenseToken/device");
|
||||||
if (id == "")
|
if (id == "")
|
||||||
@@ -1015,11 +1089,10 @@ namespace gourou
|
|||||||
for(unsigned int i=0; i<sizeof(iv); i++)
|
for(unsigned int i=0; i<sizeof(iv); i++)
|
||||||
iv[i] = _deviceId[i] ^ _fulfillmentId[i] ^ _voucherId[i];
|
iv[i] = _deviceId[i] ^ _fulfillmentId[i] ^ _voucherId[i];
|
||||||
|
|
||||||
ByteArray arrayEncryptedKey = ByteArray::fromBase64(encryptedKey);
|
|
||||||
|
|
||||||
dumpBuffer(gourou::LG_LOG_DEBUG, "First pass key : ", key, sizeof(key));
|
dumpBuffer(gourou::LG_LOG_DEBUG, "First pass key : ", key, sizeof(key));
|
||||||
dumpBuffer(gourou::LG_LOG_DEBUG, "First pass IV : ", iv, sizeof(iv));
|
dumpBuffer(gourou::LG_LOG_DEBUG, "First pass IV : ", iv, sizeof(iv));
|
||||||
|
|
||||||
|
ByteArray arrayEncryptedKey = ByteArray::fromBase64(encryptedKey);
|
||||||
unsigned char* clearRSAKey = new unsigned char[arrayEncryptedKey.size()];
|
unsigned char* clearRSAKey = new unsigned char[arrayEncryptedKey.size()];
|
||||||
|
|
||||||
client->decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC,
|
client->decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC,
|
||||||
@@ -1048,6 +1121,57 @@ namespace gourou
|
|||||||
return res.toBase64();
|
return res.toBase64();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DRMProcessor::decryptADEPTKey(pugi::xml_document& rightsDoc, unsigned char* decryptedKey, const unsigned char* encryptionKey, unsigned encryptionKeySize)
|
||||||
|
{
|
||||||
|
unsigned char rsaKey[RSA_KEY_SIZE];
|
||||||
|
|
||||||
|
std::string user = extractTextElem(rightsDoc, "/adept:rights/licenseToken/user");
|
||||||
|
|
||||||
|
if (!encryptionKey)
|
||||||
|
{
|
||||||
|
if (this->user->getUUID() != user)
|
||||||
|
{
|
||||||
|
EXCEPTION(DRM_INVALID_USER, "This book has been downloaded for another user (" << user << ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string encryptedKey = extractTextElem(rightsDoc, "/adept:rights/licenseToken/encryptedKey");
|
||||||
|
std::string keyType = extractTextAttribute(rightsDoc, "/adept:rights/licenseToken/encryptedKey", "keyType", false);
|
||||||
|
|
||||||
|
if (keyType != "")
|
||||||
|
encryptedKey = encryptedKeyFirstPass(rightsDoc, encryptedKey, keyType);
|
||||||
|
|
||||||
|
if (encryptedKey.size() != 172)
|
||||||
|
EXCEPTION(DRM_INVALID_KEY_SIZE, "Invalid encrypted key size (" << encryptedKey.size() << "). DRM version not supported");
|
||||||
|
|
||||||
|
ByteArray arrayEncryptedKey = ByteArray::fromBase64(encryptedKey);
|
||||||
|
|
||||||
|
std::string privateKeyData = this->user->getPrivateLicenseKey();
|
||||||
|
ByteArray privateRSAKey = ByteArray::fromBase64(privateKeyData);
|
||||||
|
|
||||||
|
dumpBuffer(gourou::LG_LOG_DEBUG, "To decrypt : ", arrayEncryptedKey.data(), arrayEncryptedKey.length());
|
||||||
|
|
||||||
|
client->RSAPrivateDecrypt(privateRSAKey.data(), privateRSAKey.length(),
|
||||||
|
RSAInterface::RSA_KEY_PKCS8, "",
|
||||||
|
arrayEncryptedKey.data(), arrayEncryptedKey.length(), rsaKey);
|
||||||
|
|
||||||
|
dumpBuffer(gourou::LG_LOG_DEBUG, "Decrypted : ", rsaKey, sizeof(rsaKey));
|
||||||
|
|
||||||
|
if (rsaKey[0] != 0x00 || rsaKey[1] != 0x02 ||
|
||||||
|
rsaKey[RSA_KEY_SIZE-16-1] != 0x00)
|
||||||
|
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Unable to retrieve encryption key");
|
||||||
|
|
||||||
|
memcpy(decryptedKey, &rsaKey[sizeof(rsaKey)-16], 16);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GOUROU_LOG(DEBUG, "Use provided encryption key");
|
||||||
|
if (encryptionKeySize != 16)
|
||||||
|
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Provided encryption key must be 16 bytes");
|
||||||
|
|
||||||
|
memcpy(decryptedKey, encryptionKey, encryptionKeySize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DRMProcessor::removeEPubDRM(const std::string& filenameIn, const std::string& filenameOut,
|
void DRMProcessor::removeEPubDRM(const std::string& filenameIn, const std::string& filenameOut,
|
||||||
const unsigned char* encryptionKey, unsigned encryptionKeySize)
|
const unsigned char* encryptionKey, unsigned encryptionKeySize)
|
||||||
{
|
{
|
||||||
@@ -1059,32 +1183,9 @@ namespace gourou
|
|||||||
pugi::xml_document rightsDoc;
|
pugi::xml_document rightsDoc;
|
||||||
rightsDoc.load_string((const char*)zipData.data());
|
rightsDoc.load_string((const char*)zipData.data());
|
||||||
|
|
||||||
std::string encryptedKey = extractTextElem(rightsDoc, "/adept:rights/licenseToken/encryptedKey");
|
unsigned char decryptedKey[16];
|
||||||
unsigned char decryptedKey[RSA_KEY_SIZE];
|
|
||||||
|
|
||||||
if (!encryptionKey)
|
decryptADEPTKey(rightsDoc, decryptedKey, encryptionKey, encryptionKeySize);
|
||||||
{
|
|
||||||
std::string keyType = extractTextAttribute(rightsDoc, "/adept:rights/licenseToken/encryptedKey", "keyType", false);
|
|
||||||
|
|
||||||
if (keyType != "")
|
|
||||||
encryptedKey = encryptedKeyFirstPass(rightsDoc, encryptedKey, keyType, EPUB);
|
|
||||||
|
|
||||||
decryptADEPTKey(encryptedKey, decryptedKey);
|
|
||||||
|
|
||||||
dumpBuffer(gourou::LG_LOG_DEBUG, "Decrypted : ", decryptedKey, RSA_KEY_SIZE);
|
|
||||||
|
|
||||||
if (decryptedKey[0] != 0x00 || decryptedKey[1] != 0x02 ||
|
|
||||||
decryptedKey[RSA_KEY_SIZE-16-1] != 0x00)
|
|
||||||
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Unable to retrieve encryption key");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GOUROU_LOG(DEBUG, "Use provided encryption key");
|
|
||||||
if (encryptionKeySize != 16)
|
|
||||||
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Provided encryption key must be 16 bytes");
|
|
||||||
|
|
||||||
memcpy(&decryptedKey[sizeof(decryptedKey)-16], encryptionKey, encryptionKeySize);
|
|
||||||
}
|
|
||||||
|
|
||||||
client->zipReadFile(zipHandler, "META-INF/encryption.xml", zipData);
|
client->zipReadFile(zipHandler, "META-INF/encryption.xml", zipData);
|
||||||
pugi::xml_document encryptionDoc;
|
pugi::xml_document encryptionDoc;
|
||||||
@@ -1123,7 +1224,7 @@ namespace gourou
|
|||||||
unsigned int dataOutLength;
|
unsigned int dataOutLength;
|
||||||
|
|
||||||
client->decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC,
|
client->decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC,
|
||||||
decryptedKey+sizeof(decryptedKey)-16, 16, /* Key */
|
decryptedKey, sizeof(decryptedKey), /* Key */
|
||||||
_data, 16, /* IV */
|
_data, 16, /* IV */
|
||||||
&_data[16], zipData.length()-16,
|
&_data[16], zipData.length()-16,
|
||||||
_clearData, &dataOutLength);
|
_clearData, &dataOutLength);
|
||||||
@@ -1216,7 +1317,7 @@ namespace gourou
|
|||||||
std::vector<uPDFParser::Object*> objects = parser.objects();
|
std::vector<uPDFParser::Object*> objects = parser.objects();
|
||||||
std::vector<uPDFParser::Object*>::iterator it;
|
std::vector<uPDFParser::Object*>::iterator it;
|
||||||
std::vector<uPDFParser::Object*>::reverse_iterator rIt;
|
std::vector<uPDFParser::Object*>::reverse_iterator rIt;
|
||||||
unsigned char decryptedKey[RSA_KEY_SIZE];
|
unsigned char decryptedKey[16];
|
||||||
int ebxId;
|
int ebxId;
|
||||||
|
|
||||||
for(rIt = objects.rbegin(); rIt != objects.rend(); rIt++)
|
for(rIt = objects.rbegin(); rIt != objects.rend(); rIt++)
|
||||||
@@ -1255,31 +1356,7 @@ namespace gourou
|
|||||||
pugi::xml_document rightsDoc;
|
pugi::xml_document rightsDoc;
|
||||||
rightsDoc.load_string((const char*)rightsStr.data());
|
rightsDoc.load_string((const char*)rightsStr.data());
|
||||||
|
|
||||||
std::string encryptedKey = extractTextElem(rightsDoc, "/adept:rights/licenseToken/encryptedKey");
|
decryptADEPTKey(rightsDoc, decryptedKey, encryptionKey, encryptionKeySize);
|
||||||
|
|
||||||
if (!encryptionKey)
|
|
||||||
{
|
|
||||||
std::string keyType = extractTextAttribute(rightsDoc, "/adept:rights/licenseToken/encryptedKey", "keyType", false);
|
|
||||||
|
|
||||||
if (keyType != "")
|
|
||||||
encryptedKey = encryptedKeyFirstPass(rightsDoc, encryptedKey, keyType, PDF);
|
|
||||||
|
|
||||||
decryptADEPTKey(encryptedKey, decryptedKey);
|
|
||||||
|
|
||||||
dumpBuffer(gourou::LG_LOG_DEBUG, "Decrypted : ", decryptedKey, RSA_KEY_SIZE);
|
|
||||||
|
|
||||||
if (decryptedKey[0] != 0x00 || decryptedKey[1] != 0x02 ||
|
|
||||||
decryptedKey[RSA_KEY_SIZE-16-1] != 0x00)
|
|
||||||
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Unable to retrieve encryption key");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GOUROU_LOG(DEBUG, "Use provided encryption key");
|
|
||||||
if (encryptionKeySize != 16)
|
|
||||||
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Provided encryption key must be 16 bytes");
|
|
||||||
|
|
||||||
memcpy(&decryptedKey[sizeof(decryptedKey)-16], encryptionKey, encryptionKeySize);
|
|
||||||
}
|
|
||||||
|
|
||||||
ebxId = ebx->objectId();
|
ebxId = ebx->objectId();
|
||||||
|
|
||||||
@@ -1311,10 +1388,10 @@ namespace gourou
|
|||||||
|
|
||||||
GOUROU_LOG(DEBUG, "Obj " << object->objectId());
|
GOUROU_LOG(DEBUG, "Obj " << object->objectId());
|
||||||
|
|
||||||
unsigned char tmpKey[16];
|
unsigned char tmpKey[sizeof(decryptedKey)];
|
||||||
|
|
||||||
generatePDFObjectKey(ebxVersion->value(),
|
generatePDFObjectKey(ebxVersion->value(),
|
||||||
decryptedKey+sizeof(decryptedKey)-16, 16,
|
decryptedKey, sizeof(decryptedKey),
|
||||||
object->objectId(), object->generationNumber(),
|
object->objectId(), object->generationNumber(),
|
||||||
tmpKey);
|
tmpKey);
|
||||||
|
|
||||||
@@ -1350,6 +1427,30 @@ namespace gourou
|
|||||||
|
|
||||||
delete[] clearData;
|
delete[] clearData;
|
||||||
}
|
}
|
||||||
|
else if (dictData->type() == uPDFParser::DataType::HEXASTRING)
|
||||||
|
{
|
||||||
|
string = ((uPDFParser::HexaString*) dictData)->value();
|
||||||
|
ByteArray hexStr = ByteArray::fromHex(string);
|
||||||
|
|
||||||
|
unsigned char* encryptedData = hexStr.data();
|
||||||
|
unsigned int dataLength = hexStr.size();
|
||||||
|
unsigned char* clearData = new unsigned char[dataLength];
|
||||||
|
unsigned int dataOutLength;
|
||||||
|
|
||||||
|
GOUROU_LOG(DEBUG, "Decrypt hexa string " << dictIt->first << " " << dataLength);
|
||||||
|
|
||||||
|
client->decrypt(CryptoInterface::ALGO_RC4, CryptoInterface::CHAIN_ECB,
|
||||||
|
tmpKey, sizeof(tmpKey), /* Key */
|
||||||
|
NULL, 0, /* IV */
|
||||||
|
encryptedData, dataLength,
|
||||||
|
clearData, &dataOutLength);
|
||||||
|
|
||||||
|
ByteArray clearHexStr = ByteArray(clearData, dataOutLength);
|
||||||
|
decodedStrings[dictIt->first] = new uPDFParser::HexaString(
|
||||||
|
clearHexStr.toHex());
|
||||||
|
|
||||||
|
delete[] clearData;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (dictIt = decodedStrings.begin(); dictIt != decodedStrings.end(); dictIt++)
|
for (dictIt = decodedStrings.begin(); dictIt != decodedStrings.end(); dictIt++)
|
||||||
|
|||||||
@@ -29,24 +29,13 @@ namespace gourou
|
|||||||
if (!node)
|
if (!node)
|
||||||
EXCEPTION(FFI_INVALID_LOAN_TOKEN, "No loanToken element in document");
|
EXCEPTION(FFI_INVALID_LOAN_TOKEN, "No loanToken element in document");
|
||||||
|
|
||||||
node = doc.select_node("/envelope/loanToken/loan").node();
|
node = doc.select_node("/envelope/fulfillmentResult/fulfillment").node();
|
||||||
|
|
||||||
if (node)
|
if (node)
|
||||||
properties["id"] = node.first_child().value();
|
properties["id"] = node.first_child().value();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
node = doc.select_node("/envelope/fulfillmentResult/resourceItemInfo/licenseToken/permissions/display/loan").node();
|
EXCEPTION(FFI_INVALID_LOAN_TOKEN, "No fulfillment element in document");
|
||||||
|
|
||||||
if (node)
|
|
||||||
properties["id"] = node.first_child().value();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
node = doc.select_node("/envelope/fulfillmentResult/resourceItemInfo/licenseToken/permissions/play/loan").node();
|
|
||||||
if (node)
|
|
||||||
properties["id"] = node.first_child().value();
|
|
||||||
else
|
|
||||||
EXCEPTION(FFI_INVALID_LOAN_TOKEN, "No loanToken/loan element in document");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
node = doc.select_node("/envelope/loanToken/operatorURL").node();
|
node = doc.select_node("/envelope/loanToken/operatorURL").node();
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
../lib/pugixml/src/pugixml.cpp
|
|
||||||
@@ -1,10 +1,15 @@
|
|||||||
|
BINDIR ?= /bin
|
||||||
|
MANDIR ?= /share/man
|
||||||
|
|
||||||
TARGETS=acsmdownloader adept_activate adept_remove adept_loan_mgt launcher
|
TARGET_BINARIES=acsmdownloader adept_activate adept_remove adept_loan_mgt
|
||||||
|
TARGETS=$(TARGET_BINARIES) launcher
|
||||||
|
|
||||||
CXXFLAGS=-Wall -fPIC -I$(ROOT)/include -I$(ROOT)/lib/pugixml/src/
|
MAN_PAGES=acsmdownloader adept_activate adept_remove adept_loan_mgt
|
||||||
|
|
||||||
|
CXXFLAGS=-Wall -fPIC -I$(ROOT)/include
|
||||||
|
|
||||||
STATIC_DEP=
|
STATIC_DEP=
|
||||||
LDFLAGS += -L$(ROOT) -lcrypto -lzip -lz -lcurl
|
LDFLAGS += -L$(ROOT) -lcrypto -lzip -lz -lcurl -lpugixml
|
||||||
|
|
||||||
ifneq ($(STATIC_UTILS),)
|
ifneq ($(STATIC_UTILS),)
|
||||||
STATIC_DEP = $(ROOT)/libgourou.a
|
STATIC_DEP = $(ROOT)/libgourou.a
|
||||||
@@ -25,12 +30,25 @@ COMMON_LIB = utils.a
|
|||||||
|
|
||||||
all: $(TARGETS)
|
all: $(TARGETS)
|
||||||
|
|
||||||
${COMMON_LIB}: ${COMMON_DEPS} ${STATIC_DEP}
|
${COMMON_LIB}: $(COMMON_DEPS)
|
||||||
$(CXX) $(CXXFLAGS) ${COMMON_DEPS} $(LDFLAGS) -c
|
$(CXX) $(CXXFLAGS) $(COMMON_DEPS) $(LDFLAGS) -c
|
||||||
$(AR) crs $@ ${COMMON_OBJECTS} $(STATIC_DEP)
|
$(AR) crs $@ $(COMMON_OBJECTS)
|
||||||
|
|
||||||
%: %.cpp ${COMMON_LIB}
|
%: %.cpp $(COMMON_LIB) $(STATIC_DEP)
|
||||||
$(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@
|
$(CXX) $(CXXFLAGS) $^ $(STATIC_DEP) $(LDFLAGS) -o $@
|
||||||
|
|
||||||
|
install: $(TARGET_BINARIES)
|
||||||
|
install -d $(DESTDIR)$(PREFIX)/$(BINDIR)
|
||||||
|
install -m 755 $(TARGET_BINARIES) $(DESTDIR)$(PREFIX)/$(BINDIR)
|
||||||
|
install -d $(DESTDIR)$(PREFIX)/$(MANDIR)/man1
|
||||||
|
install -m 644 man/*.1 $(DESTDIR)$(PREFIX)/$(MANDIR)/man1
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
cd $(DESTDIR)$(PREFIX)/$(BINDIR)
|
||||||
|
rm -f $(TARGET_BINARIES)
|
||||||
|
cd -
|
||||||
|
cd $(DESTDIR)$(PREFIX)/$(MANDIR)/man1
|
||||||
|
rm -f $(addsuffix .1,$(TARGET_BINARIES)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(TARGETS) $(COMMON_LIB)
|
rm -f $(TARGETS) $(COMMON_LIB)
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ static bool exportPrivateKey = false;
|
|||||||
static const char* outputFile = 0;
|
static const char* outputFile = 0;
|
||||||
static const char* outputDir = 0;
|
static const char* outputDir = 0;
|
||||||
static bool resume = false;
|
static bool resume = false;
|
||||||
|
static bool notify = true;
|
||||||
|
|
||||||
|
|
||||||
class ACSMDownloader
|
class ACSMDownloader
|
||||||
@@ -82,7 +83,7 @@ public:
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
gourou::FulfillmentItem* item = processor.fulfill(acsmFile);
|
gourou::FulfillmentItem* item = processor.fulfill(acsmFile, notify);
|
||||||
|
|
||||||
std::string filename;
|
std::string filename;
|
||||||
if (!outputFile)
|
if (!outputFile)
|
||||||
@@ -182,24 +183,31 @@ private:
|
|||||||
|
|
||||||
static void usage(const char* cmd)
|
static void usage(const char* cmd)
|
||||||
{
|
{
|
||||||
std::cout << "Download EPUB file from ACSM request file" << std::endl;
|
std::cout << basename((char*)cmd) << " download EPUB file from ACSM request file" << std::endl << std::endl;
|
||||||
|
std::cout << "Usage: " << basename((char*)cmd) << " [OPTIONS] file.acsm" << std::endl << std::endl;
|
||||||
std::cout << "Usage: " << basename((char*)cmd) << " [(-d|--device-file) device.xml] [(-a|--activation-file) activation.xml] [(-k|--device-key-file) devicesalt] [(-O|--output-dir) dir] [(-o|--output-file) output(.epub|.pdf|.der)] [(-r|--resume)] [(-v|--verbose)] [(-h|--help)] (-f|--acsm-file) file.acsm|(-e|--export-private-key)" << std::endl << std::endl;
|
std::cout << "Global Options:" << 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 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-dir" << "\t" << "Optional output directory were to put result (default ./)" << std::endl;
|
||||||
std::cout << " " << "-o|--output-file" << "\t" << "Optional output filename (default <title.(epub|pdf|der)>)" << std::endl;
|
std::cout << " " << "-o|--output-file" << "\t" << "Optional output filename (default <title.(epub|pdf|der)>)" << std::endl;
|
||||||
std::cout << " " << "-f|--acsm-file" << "\t" << "ACSM request file for epub download" << std::endl;
|
std::cout << " " << "-f|--acsm-file" << "\t" << "Backward compatibility: ACSM request file for epub download" << std::endl;
|
||||||
std::cout << " " << "-e|--export-private-key"<< "\t" << "Export private key in DER format" << std::endl;
|
std::cout << " " << "-e|--export-private-key"<< "\t" << "Export private key in DER format" << std::endl;
|
||||||
std::cout << " " << "-r|--resume" << "\t\t" << "Try to resume download (in case of previous failure)" << std::endl;
|
std::cout << " " << "-r|--resume" << "\t\t" << "Try to resume download (in case of previous failure)" << std::endl;
|
||||||
|
std::cout << " " << "-N|--no-notify" << "\t\t" << "Don't notify server, even if requested" << std::endl;
|
||||||
std::cout << " " << "-v|--verbose" << "\t\t" << "Increase verbosity, can be set multiple times" << std::endl;
|
std::cout << " " << "-v|--verbose" << "\t\t" << "Increase verbosity, can be set multiple times" << std::endl;
|
||||||
std::cout << " " << "-V|--version" << "\t\t" << "Display libgourou version" << std::endl;
|
std::cout << " " << "-V|--version" << "\t\t" << "Display libgourou version" << std::endl;
|
||||||
std::cout << " " << "-h|--help" << "\t\t" << "This help" << std::endl;
|
std::cout << " " << "-h|--help" << "\t\t" << "This help" << std::endl;
|
||||||
|
|
||||||
|
std::cout << "ADEPT Options:" << std::endl;
|
||||||
|
std::cout << " " << "-D|--adept-directory" << "\t" << ".adept directory that must contains device.xml, activation.xml and devicesalt" << 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 devicesalt/devkey.bin) from eReader" << std::endl;
|
||||||
|
|
||||||
std::cout << std::endl;
|
std::cout << 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 << "Environment:" << std::endl;
|
||||||
|
std::cout << "Device file, activation file and device key file are optionals. If not set, they are looked into :" << std::endl << std::endl;
|
||||||
|
std::cout << " * $ADEPT_DIR environment variable" << std::endl;
|
||||||
|
std::cout << " * /home/<user>/.config/adept" << std::endl;
|
||||||
std::cout << " * Current directory" << std::endl;
|
std::cout << " * Current directory" << std::endl;
|
||||||
std::cout << " * .adept" << std::endl;
|
std::cout << " * .adept" << std::endl;
|
||||||
std::cout << " * adobe-digital-editions directory" << std::endl;
|
std::cout << " * adobe-digital-editions directory" << std::endl;
|
||||||
@@ -209,6 +217,7 @@ static void usage(const char* cmd)
|
|||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
int c, ret = -1;
|
int c, ret = -1;
|
||||||
|
std::string _deviceFile, _activationFile, _devicekeyFile;
|
||||||
|
|
||||||
const char** files[] = {&devicekeyFile, &deviceFile, &activationFile};
|
const char** files[] = {&devicekeyFile, &deviceFile, &activationFile};
|
||||||
int verbose = gourou::DRMProcessor::getLogLevel();
|
int verbose = gourou::DRMProcessor::getLogLevel();
|
||||||
@@ -216,6 +225,7 @@ int main(int argc, char** argv)
|
|||||||
while (1) {
|
while (1) {
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
|
{"adept-directory", required_argument, 0, 'D' },
|
||||||
{"device-file", required_argument, 0, 'd' },
|
{"device-file", required_argument, 0, 'd' },
|
||||||
{"activation-file", required_argument, 0, 'a' },
|
{"activation-file", required_argument, 0, 'a' },
|
||||||
{"device-key-file", required_argument, 0, 'k' },
|
{"device-key-file", required_argument, 0, 'k' },
|
||||||
@@ -224,18 +234,27 @@ int main(int argc, char** argv)
|
|||||||
{"acsm-file", required_argument, 0, 'f' },
|
{"acsm-file", required_argument, 0, 'f' },
|
||||||
{"export-private-key",no_argument, 0, 'e' },
|
{"export-private-key",no_argument, 0, 'e' },
|
||||||
{"resume", no_argument, 0, 'r' },
|
{"resume", no_argument, 0, 'r' },
|
||||||
|
{"no-notify", no_argument, 0, 'N' },
|
||||||
{"verbose", no_argument, 0, 'v' },
|
{"verbose", no_argument, 0, 'v' },
|
||||||
{"version", no_argument, 0, 'V' },
|
{"version", no_argument, 0, 'V' },
|
||||||
{"help", no_argument, 0, 'h' },
|
{"help", no_argument, 0, 'h' },
|
||||||
{0, 0, 0, 0 }
|
{0, 0, 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
c = getopt_long(argc, argv, "d:a:k:O:o:f:ervVh",
|
c = getopt_long(argc, argv, "D:d:a:k:O:o:f:erNvVh",
|
||||||
long_options, &option_index);
|
long_options, &option_index);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
case 'D':
|
||||||
|
_deviceFile = std::string(optarg) + "/device.xml";
|
||||||
|
_activationFile = std::string(optarg) + "/activation.xml";
|
||||||
|
_devicekeyFile = std::string(optarg) + "/devicesalt";
|
||||||
|
deviceFile = _deviceFile.c_str();
|
||||||
|
activationFile = _activationFile.c_str();
|
||||||
|
devicekeyFile = _devicekeyFile.c_str();
|
||||||
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
deviceFile = optarg;
|
deviceFile = optarg;
|
||||||
break;
|
break;
|
||||||
@@ -260,6 +279,9 @@ int main(int argc, char** argv)
|
|||||||
case 'r':
|
case 'r':
|
||||||
resume = true;
|
resume = true;
|
||||||
break;
|
break;
|
||||||
|
case 'N':
|
||||||
|
notify = false;
|
||||||
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
verbose++;
|
verbose++;
|
||||||
break;
|
break;
|
||||||
@@ -277,6 +299,9 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
gourou::DRMProcessor::setLogLevel(verbose);
|
gourou::DRMProcessor::setLogLevel(verbose);
|
||||||
|
|
||||||
|
if (optind == argc-1)
|
||||||
|
acsmFile = argv[optind];
|
||||||
|
|
||||||
if ((!acsmFile && !exportPrivateKey) || (outputDir && !outputDir[0]) ||
|
if ((!acsmFile && !exportPrivateKey) || (outputDir && !outputDir[0]) ||
|
||||||
(outputFile && !outputFile[0]))
|
(outputFile && !outputFile[0]))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
@@ -124,10 +125,11 @@ public:
|
|||||||
|
|
||||||
static void usage(const char* cmd)
|
static void usage(const char* cmd)
|
||||||
{
|
{
|
||||||
std::cout << "Create new device files used by ADEPT DRM" << std::endl;
|
std::cout << basename((char*)cmd) << " create new device files used by ADEPT DRM" << std::endl << std::endl;
|
||||||
|
|
||||||
std::cout << "Usage: " << basename((char*)cmd) << " (-a|--anonymous) | ( (-u|--username) username [(-p|--password) password] ) [(-O|--output-dir) dir] [(-r|--random-serial)] [(-v|--verbose)] [(-h|--help)]" << std::endl << std::endl;
|
std::cout << "Usage: " << basename((char*)cmd) << " OPTIONS" << std::endl << std::endl;
|
||||||
|
|
||||||
|
std::cout << "Global Options:" << std::endl;
|
||||||
std::cout << " " << "-a|--anonymous" << "\t" << "Anonymous account, no need for username/password (Use it only with a DRM removal software)" << std::endl;
|
std::cout << " " << "-a|--anonymous" << "\t" << "Anonymous account, no need for username/password (Use it only with a DRM removal software)" << std::endl;
|
||||||
std::cout << " " << "-u|--username" << "\t\t" << "AdobeID username (ie adobe.com email account)" << 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 << " " << "-p|--password" << "\t\t" << "AdobeID password (asked if not set via command line) " << std::endl;
|
||||||
@@ -230,7 +232,7 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
if (!_outputDir || _outputDir[0] == 0)
|
if (!_outputDir || _outputDir[0] == 0)
|
||||||
{
|
{
|
||||||
outputDir = strdup(abspath(DEFAULT_ADEPT_DIR));
|
outputDir = strdup(gourou::DRMProcessor::getDefaultAdeptDir().c_str());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -45,13 +45,14 @@
|
|||||||
|
|
||||||
#define MAX_SIZE_BOOK_NAME 30
|
#define MAX_SIZE_BOOK_NAME 30
|
||||||
|
|
||||||
static char* activationDir = 0;
|
static char* adeptDir = 0;
|
||||||
static const char* deviceFile = "device.xml";
|
static const char* deviceFile = "device.xml";
|
||||||
static const char* activationFile = "activation.xml";
|
static const char* activationFile = "activation.xml";
|
||||||
static const char* devicekeyFile = "devicesalt";
|
static const char* devicekeyFile = "devicesalt";
|
||||||
static bool list = false;
|
static bool list = false;
|
||||||
static const char* returnID = 0;
|
static const char* returnID = 0;
|
||||||
static const char* deleteID = 0;
|
static const char* deleteID = 0;
|
||||||
|
static bool notify = true;
|
||||||
|
|
||||||
struct Loan
|
struct Loan
|
||||||
{
|
{
|
||||||
@@ -106,7 +107,7 @@ private:
|
|||||||
struct Loan* loan;
|
struct Loan* loan;
|
||||||
char * res;
|
char * res;
|
||||||
|
|
||||||
std::string loanDir = std::string(activationDir) + std::string("/") + LOANS_DIR;
|
std::string loanDir = std::string(adeptDir) + std::string("/") + LOANS_DIR;
|
||||||
|
|
||||||
if (!fileExists(loanDir.c_str()))
|
if (!fileExists(loanDir.c_str()))
|
||||||
return;
|
return;
|
||||||
@@ -182,8 +183,13 @@ private:
|
|||||||
loan->bookName = node.first_child().value();
|
loan->bookName = node.first_child().value();
|
||||||
|
|
||||||
struct tm tm;
|
struct tm tm;
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
res = strptime(loan->validity.c_str(), "%Y-%m-%dT%H:%M:%S%z", &tm);
|
||||||
|
#else
|
||||||
res = strptime(loan->validity.c_str(), "%Y-%m-%dT%H:%M:%S%Z", &tm);
|
res = strptime(loan->validity.c_str(), "%Y-%m-%dT%H:%M:%S%Z", &tm);
|
||||||
if (*res == 0)
|
#endif
|
||||||
|
|
||||||
|
if (res != NULL && *res == 0)
|
||||||
{
|
{
|
||||||
if (mktime(&tm) <= time(NULL))
|
if (mktime(&tm) <= time(NULL))
|
||||||
loan->validity = " (Expired)";
|
loan->validity = " (Expired)";
|
||||||
@@ -209,7 +215,7 @@ private:
|
|||||||
{
|
{
|
||||||
if (!loanedBooks.size())
|
if (!loanedBooks.size())
|
||||||
{
|
{
|
||||||
std::cout << "Any book loaned" << std::endl;
|
std::cout << "No books loaned" << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,7 +258,7 @@ private:
|
|||||||
|
|
||||||
std::cout.width (fillExpiration);
|
std::cout.width (fillExpiration);
|
||||||
std::cout << "";
|
std::cout << "";
|
||||||
std::cout << "Exipration";
|
std::cout << "Expiration";
|
||||||
std::cout.width (fillExpiration);
|
std::cout.width (fillExpiration);
|
||||||
std::cout << "" << std::endl;
|
std::cout << "" << std::endl;
|
||||||
|
|
||||||
@@ -296,7 +302,7 @@ private:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
processor.returnLoan(loan->id, loan->operatorURL);
|
processor.returnLoan(loan->id, loan->operatorURL, notify);
|
||||||
|
|
||||||
deleteID = returnID;
|
deleteID = returnID;
|
||||||
if (deleteLoan(false))
|
if (deleteLoan(false))
|
||||||
@@ -334,20 +340,27 @@ private:
|
|||||||
|
|
||||||
static void usage(const char* cmd)
|
static void usage(const char* cmd)
|
||||||
{
|
{
|
||||||
std::cout << "Manage loaned books" << std::endl;
|
std::cout << basename((char*)cmd) << " manage loaned books" << std::endl << std::endl;
|
||||||
|
|
||||||
std::cout << "Usage: " << basename((char*)cmd) << " [(-d|--activation-dir) dir] (-l|--list)|(-D|--delete loanID)|(-R|--delete loanID) [(-v|--verbose)] [(-h|--help)]" << std::endl << std::endl;
|
std::cout << "Usage: " << basename((char*)cmd) << " [OPTIONS]" << std::endl << std::endl;
|
||||||
|
|
||||||
std::cout << " " << "-d|--activation-dir" << "\t" << "Directory of device.xml/activation.xml and device key" << std::endl;
|
std::cout << "Global Options:" << std::endl;
|
||||||
std::cout << " " << "-l|--list" << "\t\t" << "List all loaned books" << std::endl;
|
std::cout << " " << "-l|--list" << "\t\t" << "List all loaned books" << std::endl;
|
||||||
std::cout << " " << "-r|--return" << "\t\t" << "Return a loaned book" << std::endl;
|
std::cout << " " << "-r|--return" << "\t\t" << "Return a loaned book" << std::endl;
|
||||||
std::cout << " " << "-D|--delete" << "\t\t" << "Delete a loan entry without returning it" << std::endl;
|
std::cout << " " << "-d|--delete" << "\t\t" << "Delete a loan entry without returning it" << std::endl;
|
||||||
|
std::cout << " " << "-N|--no-notify" << "\t\t" << "Don't notify server, even if requested" << std::endl;
|
||||||
std::cout << " " << "-v|--verbose" << "\t\t" << "Increase verbosity, can be set multiple times" << std::endl;
|
std::cout << " " << "-v|--verbose" << "\t\t" << "Increase verbosity, can be set multiple times" << std::endl;
|
||||||
std::cout << " " << "-V|--version" << "\t\t" << "Display libgourou version" << std::endl;
|
std::cout << " " << "-V|--version" << "\t\t" << "Display libgourou version" << std::endl;
|
||||||
std::cout << " " << "-h|--help" << "\t\t" << "This help" << std::endl;
|
std::cout << " " << "-h|--help" << "\t\t" << "This help" << std::endl;
|
||||||
|
|
||||||
|
std::cout << "ADEPT Options:" << std::endl;
|
||||||
|
std::cout << " " << "-D|--adept-directory" << "\t" << ".adept directory that must contains device.xml, activation.xml and devicesalt" << std::endl;
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
std::cout << "Activation directory is optional. If not set, it's looked into :" << std::endl;
|
|
||||||
|
std::cout << "Environment:" << std::endl;
|
||||||
|
std::cout << "ADEPT directory is optional. If not set, it's looked into :" << std::endl;
|
||||||
|
std::cout << " * $ADEPT_DIR environment variable" << std::endl;
|
||||||
|
std::cout << " * /home/<user>/.config/adept" << std::endl;
|
||||||
std::cout << " * Current directory" << std::endl;
|
std::cout << " * Current directory" << std::endl;
|
||||||
std::cout << " * .adept" << std::endl;
|
std::cout << " * .adept" << std::endl;
|
||||||
std::cout << " * adobe-digital-editions directory" << std::endl;
|
std::cout << " * adobe-digital-editions directory" << std::endl;
|
||||||
@@ -365,10 +378,11 @@ int main(int argc, char** argv)
|
|||||||
while (1) {
|
while (1) {
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{"activation-dir", required_argument, 0, 'd' },
|
{"adept-directory", required_argument, 0, 'D' },
|
||||||
{"list", no_argument, 0, 'l' },
|
{"list", no_argument, 0, 'l' },
|
||||||
{"return", no_argument, 0, 'r' },
|
{"return", no_argument, 0, 'r' },
|
||||||
{"delete", no_argument, 0, 'D' },
|
{"delete", no_argument, 0, 'd' },
|
||||||
|
{"no-notify", no_argument, 0, 'N' },
|
||||||
{"verbose", no_argument, 0, 'v' },
|
{"verbose", no_argument, 0, 'v' },
|
||||||
{"version", no_argument, 0, 'V' },
|
{"version", no_argument, 0, 'V' },
|
||||||
{"help", no_argument, 0, 'h' },
|
{"help", no_argument, 0, 'h' },
|
||||||
@@ -381,8 +395,8 @@ int main(int argc, char** argv)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'd':
|
case 'D':
|
||||||
activationDir = optarg;
|
adeptDir = optarg;
|
||||||
break;
|
break;
|
||||||
case 'l':
|
case 'l':
|
||||||
list = true;
|
list = true;
|
||||||
@@ -392,10 +406,13 @@ int main(int argc, char** argv)
|
|||||||
returnID = optarg;
|
returnID = optarg;
|
||||||
actions++;
|
actions++;
|
||||||
break;
|
break;
|
||||||
case 'D':
|
case 'd':
|
||||||
deleteID = optarg;
|
deleteID = optarg;
|
||||||
actions++;
|
actions++;
|
||||||
break;
|
break;
|
||||||
|
case 'N':
|
||||||
|
notify = false;
|
||||||
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
verbose++;
|
verbose++;
|
||||||
break;
|
break;
|
||||||
@@ -432,9 +449,9 @@ int main(int argc, char** argv)
|
|||||||
{
|
{
|
||||||
orig = *files[i];
|
orig = *files[i];
|
||||||
|
|
||||||
if (activationDir)
|
if (adeptDir)
|
||||||
{
|
{
|
||||||
std::string path = std::string(activationDir) + std::string("/") + orig;
|
std::string path = std::string(adeptDir) + std::string("/") + orig;
|
||||||
filename = strdup(path.c_str());
|
filename = strdup(path.c_str());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -450,17 +467,17 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
if (hasErrors)
|
if (hasErrors)
|
||||||
{
|
{
|
||||||
// In case of activation dir was provided by user
|
// In case of adept dir was provided by user
|
||||||
activationDir = 0;
|
adeptDir = 0;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activationDir)
|
if (adeptDir)
|
||||||
activationDir = strdup(activationDir); // For below free
|
adeptDir = strdup(adeptDir); // For below free
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
activationDir = strdup(deviceFile);
|
adeptDir = strdup(deviceFile);
|
||||||
activationDir = dirname(activationDir);
|
adeptDir = dirname(adeptDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = loanMGT.run();
|
ret = loanMGT.run();
|
||||||
@@ -472,8 +489,8 @@ end:
|
|||||||
free((void*)*files[i]);
|
free((void*)*files[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activationDir)
|
if (adeptDir)
|
||||||
free(activationDir);
|
free(adeptDir);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
@@ -141,22 +142,30 @@ public:
|
|||||||
|
|
||||||
static void usage(const char* cmd)
|
static void usage(const char* cmd)
|
||||||
{
|
{
|
||||||
std::cout << "Remove ADEPT DRM (from Adobe) of EPUB/PDF file" << std::endl;
|
std::cout << basename((char*)cmd) << " remove ADEPT DRM (from Adobe) of EPUB/PDF file" << std::endl << std::endl;
|
||||||
|
|
||||||
std::cout << "Usage: " << basename((char*)cmd) << " [(-d|--device-file) device.xml] [(-a|--activation-file) activation.xml] [(-k|--device-key-file) devicesalt] [(-O|--output-dir) dir] [(-o|--output-file) output(.epub|.pdf|.der)] [(-v|--verbose)] [(-h|--help)] (-f|--input-file) file(.epub|pdf)" << std::endl << std::endl;
|
std::cout << "Usage: " << basename((char*)cmd) << " [OPTIONS] file(.epub|pdf)" << std::endl << std::endl;
|
||||||
|
|
||||||
std::cout << " " << "-d|--device-file" << "\t" << "device.xml file from eReader" << std::endl;
|
std::cout << "Global Options:" << 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 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-dir" << "\t" << "Optional output directory were to put result (default ./)" << std::endl;
|
||||||
std::cout << " " << "-o|--output-file" << "\t" << "Optional output filename (default inplace DRM removal>)" << std::endl;
|
std::cout << " " << "-o|--output-file" << "\t" << "Optional output filename (default inplace DRM removal>)" << std::endl;
|
||||||
std::cout << " " << "-f|--input-file" << "\t" << "EPUB/PDF file to process" << std::endl;
|
std::cout << " " << "-f|--input-file" << "\t" << "Backward compatibility: EPUB/PDF file to process" << std::endl;
|
||||||
std::cout << " " << "-v|--verbose" << "\t\t" << "Increase verbosity, can be set multiple times" << std::endl;
|
std::cout << " " << "-v|--verbose" << "\t\t" << "Increase verbosity, can be set multiple times" << std::endl;
|
||||||
std::cout << " " << "-V|--version" << "\t\t" << "Display libgourou version" << std::endl;
|
std::cout << " " << "-V|--version" << "\t\t" << "Display libgourou version" << std::endl;
|
||||||
std::cout << " " << "-h|--help" << "\t\t" << "This help" << std::endl;
|
std::cout << " " << "-h|--help" << "\t\t" << "This help" << std::endl;
|
||||||
|
|
||||||
|
std::cout << "ADEPT Options:" << std::endl;
|
||||||
|
std::cout << " " << "-D|--adept-directory" << "\t" << ".adept directory that must contains device.xml, activation.xml and devicesalt" << 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 devicesalt/devkey.bin) from eReader" << std::endl;
|
||||||
|
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
std::cout << "Environment:" << 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 << "Device file, activation file and device key file are optionals. If not set, they are looked into :" << std::endl;
|
||||||
|
std::cout << " * $ADEPT_DIR environment variable" << std::endl;
|
||||||
|
std::cout << " * /home/<user>/.config/adept" << std::endl;
|
||||||
std::cout << " * Current directory" << std::endl;
|
std::cout << " * Current directory" << std::endl;
|
||||||
std::cout << " * .adept" << std::endl;
|
std::cout << " * .adept" << std::endl;
|
||||||
std::cout << " * adobe-digital-editions directory" << std::endl;
|
std::cout << " * adobe-digital-editions directory" << std::endl;
|
||||||
@@ -169,10 +178,12 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
const char** files[] = {&devicekeyFile, &deviceFile, &activationFile};
|
const char** files[] = {&devicekeyFile, &deviceFile, &activationFile};
|
||||||
int verbose = gourou::DRMProcessor::getLogLevel();
|
int verbose = gourou::DRMProcessor::getLogLevel();
|
||||||
|
std::string _deviceFile, _activationFile, _devicekeyFile;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
|
{"adept-directory", required_argument, 0, 'D' },
|
||||||
{"device-file", required_argument, 0, 'd' },
|
{"device-file", required_argument, 0, 'd' },
|
||||||
{"activation-file", required_argument, 0, 'a' },
|
{"activation-file", required_argument, 0, 'a' },
|
||||||
{"device-key-file", required_argument, 0, 'k' },
|
{"device-key-file", required_argument, 0, 'k' },
|
||||||
@@ -186,12 +197,20 @@ int main(int argc, char** argv)
|
|||||||
{0, 0, 0, 0 }
|
{0, 0, 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
c = getopt_long(argc, argv, "d:a:k:O:o:f:K:vVh",
|
c = getopt_long(argc, argv, "D:d:a:k:O:o:f:K:vVh",
|
||||||
long_options, &option_index);
|
long_options, &option_index);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
|
case 'D':
|
||||||
|
_deviceFile = std::string(optarg) + "/device.xml";
|
||||||
|
_activationFile = std::string(optarg) + "/activation.xml";
|
||||||
|
_devicekeyFile = std::string(optarg) + "/devicesalt";
|
||||||
|
deviceFile = _deviceFile.c_str();
|
||||||
|
activationFile = _activationFile.c_str();
|
||||||
|
devicekeyFile = _devicekeyFile.c_str();
|
||||||
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
deviceFile = optarg;
|
deviceFile = optarg;
|
||||||
break;
|
break;
|
||||||
@@ -230,6 +249,9 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
gourou::DRMProcessor::setLogLevel(verbose);
|
gourou::DRMProcessor::setLogLevel(verbose);
|
||||||
|
|
||||||
|
if (optind == argc-1)
|
||||||
|
inputFile = argv[optind];
|
||||||
|
|
||||||
if (!inputFile || (outputDir && !outputDir[0]) ||
|
if (!inputFile || (outputDir && !outputDir[0]) ||
|
||||||
(outputFile && !outputFile[0]))
|
(outputFile && !outputFile[0]))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <locale>
|
#include <locale>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#define OPENSSL_NO_DEPRECATED 1
|
#define OPENSSL_NO_DEPRECATED 1
|
||||||
|
|
||||||
@@ -60,6 +61,14 @@ DRMProcessorClientImpl::DRMProcessorClientImpl():
|
|||||||
if (!deflt)
|
if (!deflt)
|
||||||
EXCEPTION(gourou::CLIENT_OSSL_ERROR, "Error, OpenSSL default provider not available");
|
EXCEPTION(gourou::CLIENT_OSSL_ERROR, "Error, OpenSSL default provider not available");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
strcpy(cookiejar, "C:\\temp\\libgourou_cookie_jar_XXXXXX");
|
||||||
|
#else
|
||||||
|
strcpy(cookiejar, "/tmp/libgourou_cookie_jar_XXXXXX");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mkstemp(cookiejar);
|
||||||
}
|
}
|
||||||
|
|
||||||
DRMProcessorClientImpl::~DRMProcessorClientImpl()
|
DRMProcessorClientImpl::~DRMProcessorClientImpl()
|
||||||
@@ -71,6 +80,8 @@ DRMProcessorClientImpl::~DRMProcessorClientImpl()
|
|||||||
if (deflt)
|
if (deflt)
|
||||||
OSSL_PROVIDER_unload(deflt);
|
OSSL_PROVIDER_unload(deflt);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
unlink(cookiejar);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Digest interface */
|
/* Digest interface */
|
||||||
@@ -227,6 +238,7 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
|
|||||||
}
|
}
|
||||||
|
|
||||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookiejar);
|
||||||
|
|
||||||
if (POSTData.size())
|
if (POSTData.size())
|
||||||
{
|
{
|
||||||
@@ -286,11 +298,18 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
|
|||||||
}
|
}
|
||||||
|
|
||||||
curl_slist_free_all(list);
|
curl_slist_free_all(list);
|
||||||
|
|
||||||
|
long http_code = 400;
|
||||||
|
curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
|
||||||
|
|
||||||
curl_easy_cleanup(curl);
|
curl_easy_cleanup(curl);
|
||||||
|
|
||||||
if (res != CURLE_OK)
|
if (res != CURLE_OK)
|
||||||
EXCEPTION(gourou::CLIENT_NETWORK_ERROR, "Error " << curl_easy_strerror(res));
|
EXCEPTION(gourou::CLIENT_NETWORK_ERROR, "Error " << curl_easy_strerror(res));
|
||||||
|
|
||||||
|
if (http_code >= 400)
|
||||||
|
EXCEPTION(gourou::CLIENT_HTTP_ERROR, "HTTP Error code " << http_code);
|
||||||
|
|
||||||
if ((downloadedBytes >= DISPLAY_THRESHOLD || replyData.size() >= DISPLAY_THRESHOLD) &&
|
if ((downloadedBytes >= DISPLAY_THRESHOLD || replyData.size() >= DISPLAY_THRESHOLD) &&
|
||||||
gourou::logLevel >= gourou::LG_LOG_WARN)
|
gourou::logLevel >= gourou::LG_LOG_WARN)
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
|
|||||||
@@ -136,6 +136,8 @@ private:
|
|||||||
#else
|
#else
|
||||||
void *legacy, *deflt;
|
void *legacy, *deflt;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
char cookiejar[64];
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
61
utils/man/acsmdownloader.1
Normal file
61
utils/man/acsmdownloader.1
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||||
|
.TH ACSMDOWNLOADER "1" "January 2023" "acsmdownloader download EPUB file from ACSM request file" "User Commands"
|
||||||
|
.SH NAME
|
||||||
|
acsmdownloader \- download EPUB file from ACSM request file
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B acsmdownloader
|
||||||
|
[\fI\,OPTIONS\/\fR] \fI\,file.acsm\/\fR
|
||||||
|
.SH DESCRIPTION
|
||||||
|
Download EPUB file from ACSM request file
|
||||||
|
.SS "Global Options:"
|
||||||
|
.TP
|
||||||
|
\fB\-O\fR|\-\-output\-dir
|
||||||
|
Optional output directory were to put result (default ./)
|
||||||
|
.TP
|
||||||
|
\fB\-o\fR|\-\-output\-file
|
||||||
|
Optional output filename (default <title.(epub|pdf|der)>)
|
||||||
|
.TP
|
||||||
|
\fB\-f\fR|\-\-acsm\-file
|
||||||
|
Backward compatibility: ACSM request file for epub download
|
||||||
|
.TP
|
||||||
|
\fB\-e\fR|\-\-export\-private\-key
|
||||||
|
Export private key in DER format
|
||||||
|
.TP
|
||||||
|
\fB\-r\fR|\-\-resume
|
||||||
|
Try to resume download (in case of previous failure)
|
||||||
|
.TP
|
||||||
|
\fB\-v\fR|\-\-verbose
|
||||||
|
Increase verbosity, can be set multiple times
|
||||||
|
.TP
|
||||||
|
\fB\-V\fR|\-\-version
|
||||||
|
Display libgourou version
|
||||||
|
.TP
|
||||||
|
\fB\-h\fR|\-\-help
|
||||||
|
This help
|
||||||
|
.SS "ADEPT Options:"
|
||||||
|
.TP
|
||||||
|
\fB\-D\fR|\-\-adept\-directory
|
||||||
|
\&.adept directory that must contains device.xml, activation.xml and devicesalt
|
||||||
|
.TP
|
||||||
|
\fB\-d\fR|\-\-device\-file
|
||||||
|
device.xml file from eReader
|
||||||
|
.TP
|
||||||
|
\fB\-a\fR|\-\-activation\-file
|
||||||
|
activation.xml file from eReader
|
||||||
|
.TP
|
||||||
|
\fB\-k\fR|\-\-device\-key\-file
|
||||||
|
private device key file (eg devicesalt/devkey.bin) from eReader
|
||||||
|
.SH ENVIRONMENT
|
||||||
|
Device file, activation file and device key file are optionals. If not set, they are looked into :
|
||||||
|
.IP
|
||||||
|
* $ADEPT_DIR environment variable
|
||||||
|
.IP
|
||||||
|
* /home/<user>/.config/adept
|
||||||
|
.IP
|
||||||
|
* Current directory
|
||||||
|
.IP
|
||||||
|
* .adept
|
||||||
|
.IP
|
||||||
|
* adobe\-digital\-editions directory
|
||||||
|
.IP
|
||||||
|
* .adobe\-digital\-editions directory
|
||||||
67
utils/man/adept_activate.1
Normal file
67
utils/man/adept_activate.1
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||||
|
.TH ADEPT_ACTIVATE "1" "January 2023" "adept_activate create new device files used by ADEPT DRM" "User Commands"
|
||||||
|
.SH NAME
|
||||||
|
adept_activate create new device files used by ADEPT DRM
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B adept_activate
|
||||||
|
\fI\,OPTIONS\/\fR
|
||||||
|
.SH DESCRIPTION
|
||||||
|
Create new device files used by ADEPT DRM
|
||||||
|
.SS "Global Options:"
|
||||||
|
.TP
|
||||||
|
\fB\-a\fR|\-\-anonymous
|
||||||
|
Anonymous account, no need for username/password (Use it only with a DRM removal software)
|
||||||
|
.TP
|
||||||
|
\fB\-u\fR|\-\-username
|
||||||
|
AdobeID username (ie adobe.com email account)
|
||||||
|
.TP
|
||||||
|
\fB\-p\fR|\-\-password
|
||||||
|
AdobeID password (asked if not set via command line)
|
||||||
|
.TP
|
||||||
|
\fB\-O\fR|\-\-output\-dir
|
||||||
|
Optional output directory were to put result (default ./.adept). This directory must not already exists
|
||||||
|
.TP
|
||||||
|
\fB\-H\fR|\-\-hobbes\-version
|
||||||
|
Force RMSDK version to a specific value (default: version of current librmsdk)
|
||||||
|
.TP
|
||||||
|
\fB\-r\fR|\-\-random\-serial
|
||||||
|
Generate a random device serial (if not set, it will be dependent of your current configuration)
|
||||||
|
.TP
|
||||||
|
\fB\-v\fR|\-\-verbose
|
||||||
|
Increase verbosity, can be set multiple times
|
||||||
|
.TP
|
||||||
|
\fB\-V\fR|\-\-version
|
||||||
|
Display libgourou version
|
||||||
|
.TP
|
||||||
|
\fB\-h\fR|\-\-help
|
||||||
|
This help
|
||||||
|
.PP
|
||||||
|
Usage: adept_activate OPTIONS
|
||||||
|
.SS "Global Options:"
|
||||||
|
.TP
|
||||||
|
\fB\-a\fR|\-\-anonymous
|
||||||
|
Anonymous account, no need for username/password (Use it only with a DRM removal software)
|
||||||
|
.TP
|
||||||
|
\fB\-u\fR|\-\-username
|
||||||
|
AdobeID username (ie adobe.com email account)
|
||||||
|
.TP
|
||||||
|
\fB\-p\fR|\-\-password
|
||||||
|
AdobeID password (asked if not set via command line)
|
||||||
|
.TP
|
||||||
|
\fB\-O\fR|\-\-output\-dir
|
||||||
|
Optional output directory were to put result (default ./.adept). This directory must not already exists
|
||||||
|
.TP
|
||||||
|
\fB\-H\fR|\-\-hobbes\-version
|
||||||
|
Force RMSDK version to a specific value (default: version of current librmsdk)
|
||||||
|
.TP
|
||||||
|
\fB\-r\fR|\-\-random\-serial
|
||||||
|
Generate a random device serial (if not set, it will be dependent of your current configuration)
|
||||||
|
.TP
|
||||||
|
\fB\-v\fR|\-\-verbose
|
||||||
|
Increase verbosity, can be set multiple times
|
||||||
|
.TP
|
||||||
|
\fB\-V\fR|\-\-version
|
||||||
|
Display libgourou version
|
||||||
|
.TP
|
||||||
|
\fB\-h\fR|\-\-help
|
||||||
|
This help
|
||||||
46
utils/man/adept_loan_mgt.1
Normal file
46
utils/man/adept_loan_mgt.1
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||||
|
.TH ADEPT_LOAN_MGT "1" "January 2023" "adept_loan_mgt manage loaned books" "User Commands"
|
||||||
|
.SH NAME
|
||||||
|
adept_loan_mgt manage loaned books
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B adept_loan_mgt
|
||||||
|
[\fI\,OPTIONS\/\fR]
|
||||||
|
.SH DESCRIPTION
|
||||||
|
Manage ADEPT loaned books
|
||||||
|
.SS "Global Options:"
|
||||||
|
.TP
|
||||||
|
\fB\-l\fR|\-\-list
|
||||||
|
List all loaned books
|
||||||
|
.TP
|
||||||
|
\fB\-r\fR|\-\-return
|
||||||
|
Return a loaned book
|
||||||
|
.TP
|
||||||
|
\fB\-d\fR|\-\-delete
|
||||||
|
Delete a loan entry without returning it
|
||||||
|
.TP
|
||||||
|
\fB\-v\fR|\-\-verbose
|
||||||
|
Increase verbosity, can be set multiple times
|
||||||
|
.TP
|
||||||
|
\fB\-V\fR|\-\-version
|
||||||
|
Display libgourou version
|
||||||
|
.TP
|
||||||
|
\fB\-h\fR|\-\-help
|
||||||
|
This help
|
||||||
|
.SS "ADEPT Options:"
|
||||||
|
.TP
|
||||||
|
\fB\-D\fR|\-\-adept\-directory
|
||||||
|
\&.adept directory that must contains device.xml, activation.xml and devicesalt
|
||||||
|
.SH ENVIRONMENT
|
||||||
|
.SS "ADEPT directory is optional. If not set, it's looked into :"
|
||||||
|
.IP
|
||||||
|
* $ADEPT_DIR environment variable
|
||||||
|
.IP
|
||||||
|
* /home/<user>/.config/adept
|
||||||
|
.IP
|
||||||
|
* Current directory
|
||||||
|
.IP
|
||||||
|
* .adept
|
||||||
|
.IP
|
||||||
|
* adobe\-digital\-editions directory
|
||||||
|
.IP
|
||||||
|
* .adobe\-digital\-editions directory
|
||||||
55
utils/man/adept_remove.1
Normal file
55
utils/man/adept_remove.1
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||||
|
.TH ADEPT_REMOVE "1" "January 2023" "adept_remove remove ADEPT DRM (from Adobe) of EPUB/PDF file" "User Commands"
|
||||||
|
.SH NAME
|
||||||
|
adept_remove remove ADEPT DRM (from Adobe) of EPUB/PDF file
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B adept_remove
|
||||||
|
[\fI\,OPTIONS\/\fR] \fI\,file(.epub|pdf)\/\fR
|
||||||
|
.SH DESCRIPTION
|
||||||
|
Remove ADEPT DRM (from Adobe) of EPUB/PDF file
|
||||||
|
.SS "Global Options:"
|
||||||
|
.TP
|
||||||
|
\fB\-O\fR|\-\-output\-dir
|
||||||
|
Optional output directory were to put result (default ./)
|
||||||
|
.TP
|
||||||
|
\fB\-o\fR|\-\-output\-file
|
||||||
|
Optional output filename (default inplace DRM removal>)
|
||||||
|
.TP
|
||||||
|
\fB\-f\fR|\-\-input\-file
|
||||||
|
Backward compatibility: EPUB/PDF file to process
|
||||||
|
.TP
|
||||||
|
\fB\-v\fR|\-\-verbose
|
||||||
|
Increase verbosity, can be set multiple times
|
||||||
|
.TP
|
||||||
|
\fB\-V\fR|\-\-version
|
||||||
|
Display libgourou version
|
||||||
|
.TP
|
||||||
|
\fB\-h\fR|\-\-help
|
||||||
|
This help
|
||||||
|
.SS "ADEPT Options:"
|
||||||
|
.TP
|
||||||
|
\fB\-D\fR|\-\-adept\-directory
|
||||||
|
\&.adept directory that must contains device.xml, activation.xml and devicesalt
|
||||||
|
.TP
|
||||||
|
\fB\-d\fR|\-\-device\-file
|
||||||
|
device.xml file from eReader
|
||||||
|
.TP
|
||||||
|
\fB\-a\fR|\-\-activation\-file
|
||||||
|
activation.xml file from eReader
|
||||||
|
.TP
|
||||||
|
\fB\-k\fR|\-\-device\-key\-file
|
||||||
|
private device key file (eg devicesalt/devkey.bin) from eReader
|
||||||
|
.SH ENVIRONMENT
|
||||||
|
.SS "Device file, activation file and device key file are optionals. If not set, they are looked into :"
|
||||||
|
.IP
|
||||||
|
* $ADEPT_DIR environment variable
|
||||||
|
.IP
|
||||||
|
* /home/<user>/.config/adept
|
||||||
|
.IP
|
||||||
|
* Current directory
|
||||||
|
.IP
|
||||||
|
* .adept
|
||||||
|
.IP
|
||||||
|
* adobe\-digital\-editions directory
|
||||||
|
.IP
|
||||||
|
* .adobe\-digital\-editions directory
|
||||||
@@ -61,6 +61,20 @@ bool fileExists(const char* filename)
|
|||||||
|
|
||||||
const char* findFile(const char* filename, bool inDefaultDirs)
|
const char* findFile(const char* filename, bool inDefaultDirs)
|
||||||
{
|
{
|
||||||
|
std::string path;
|
||||||
|
|
||||||
|
const char* adeptDir = getenv("ADEPT_DIR");
|
||||||
|
if (adeptDir && adeptDir[0])
|
||||||
|
{
|
||||||
|
path = adeptDir + std::string("/") + filename;
|
||||||
|
if (fileExists(path.c_str()))
|
||||||
|
return strdup(path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
path = gourou::DRMProcessor::getDefaultAdeptDir() + filename;
|
||||||
|
if (fileExists(path.c_str()))
|
||||||
|
return strdup(path.c_str());
|
||||||
|
|
||||||
if (fileExists(filename))
|
if (fileExists(filename))
|
||||||
return strdup(filename);
|
return strdup(filename);
|
||||||
|
|
||||||
@@ -68,7 +82,7 @@ const char* findFile(const char* filename, bool inDefaultDirs)
|
|||||||
|
|
||||||
for (int i=0; i<(int)ARRAY_SIZE(defaultDirs); i++)
|
for (int i=0; i<(int)ARRAY_SIZE(defaultDirs); i++)
|
||||||
{
|
{
|
||||||
std::string path = std::string(defaultDirs[i]) + filename;
|
path = std::string(defaultDirs[i]) + filename;
|
||||||
if (fileExists(path.c_str()))
|
if (fileExists(path.c_str()))
|
||||||
return strdup(path.c_str());
|
return strdup(path.c_str());
|
||||||
}
|
}
|
||||||
@@ -98,8 +112,8 @@ void mkpath(const char *dir)
|
|||||||
|
|
||||||
void fileCopy(const char* in, const char* out)
|
void fileCopy(const char* in, const char* out)
|
||||||
{
|
{
|
||||||
char buffer[4096];
|
char buffer[4096], *_buffer;
|
||||||
int ret, fdIn, fdOut;
|
int ret, ret2, fdIn, fdOut;
|
||||||
|
|
||||||
fdIn = open(in, O_RDONLY);
|
fdIn = open(in, O_RDONLY);
|
||||||
|
|
||||||
@@ -119,7 +133,20 @@ void fileCopy(const char* in, const char* out)
|
|||||||
ret = ::read(fdIn, buffer, sizeof(buffer));
|
ret = ::read(fdIn, buffer, sizeof(buffer));
|
||||||
if (ret <= 0)
|
if (ret <= 0)
|
||||||
break;
|
break;
|
||||||
::write(fdOut, buffer, ret);
|
do
|
||||||
|
{
|
||||||
|
_buffer = buffer;
|
||||||
|
ret2 = ::write(fdOut, _buffer, ret);
|
||||||
|
if (ret2 >= 0)
|
||||||
|
{
|
||||||
|
ret -= ret2;
|
||||||
|
_buffer += ret2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EXCEPTION(gourou::CLIENT_FILE_ERROR, "Error writing " << out);
|
||||||
|
}
|
||||||
|
} while (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
close (fdIn);
|
close (fdIn);
|
||||||
|
|||||||
Reference in New Issue
Block a user