Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e05639c09d | ||
|
|
69865e005b | ||
|
|
fd38e84da6 | ||
|
|
92a67312bd | ||
|
|
29d298b373 | ||
|
|
40dcb7a041 | ||
|
|
e0bb1bd4f8 | ||
|
|
9388d82138 | ||
|
|
c19279397f | ||
|
|
bb5349d710 | ||
|
|
9a75213b49 | ||
|
|
e06d20a392 | ||
|
|
a0f6324999 | ||
|
|
c259cbd5a3 |
13
Makefile
13
Makefile
@@ -1,13 +1,10 @@
|
|||||||
LIBDIR ?= /usr/lib
|
PREFIX ?= /usr/local
|
||||||
INCDIR ?= /usr/include
|
LIBDIR ?= /lib
|
||||||
|
INCDIR ?= /include
|
||||||
|
|
||||||
AR ?= $(CROSS)ar
|
AR ?= $(CROSS)ar
|
||||||
CXX ?= $(CROSS)g++
|
CXX ?= $(CROSS)g++
|
||||||
|
|
||||||
ifeq ($(PREFIX),)
|
|
||||||
PREFIX := /usr/local
|
|
||||||
endif
|
|
||||||
|
|
||||||
UPDFPARSERLIB = ./lib/updfparser/libupdfparser.a
|
UPDFPARSERLIB = ./lib/updfparser/libupdfparser.a
|
||||||
|
|
||||||
CXXFLAGS += -Wall -fPIC -I./include -I./usr/include/pugixml -I./lib/updfparser/include
|
CXXFLAGS += -Wall -fPIC -I./include -I./usr/include/pugixml -I./lib/updfparser/include
|
||||||
@@ -86,13 +83,13 @@ libgourou.so: libgourou.so.$(VERSION)
|
|||||||
ln -f -s $^ $@
|
ln -f -s $^ $@
|
||||||
|
|
||||||
build_utils: $(TARGET_LIBRARIES)
|
build_utils: $(TARGET_LIBRARIES)
|
||||||
$(MAKE) -C utils ROOT=$(PWD) CXX=$(CXX) AR=$(AR) DEBUG=$(DEBUG) STATIC_UTILS=$(STATIC_UTILS) DEST_DIR=$(DEST_DIR) PREFIX=$(PREFIX)
|
$(MAKE) -C utils ROOT=$(PWD) CXX=$(CXX) AR=$(AR) DEBUG=$(DEBUG) STATIC_UTILS=$(STATIC_UTILS) DESTDIR=$(DESTDIR) PREFIX=$(PREFIX)
|
||||||
|
|
||||||
install: $(TARGET_LIBRARIES)
|
install: $(TARGET_LIBRARIES)
|
||||||
install -d $(DESTDIR)$(PREFIX)$(LIBDIR)
|
install -d $(DESTDIR)$(PREFIX)$(LIBDIR)
|
||||||
# Use cp to preserver symlinks
|
# Use cp to preserver symlinks
|
||||||
cp --no-dereference $(TARGET_LIBRARIES) $(DESTDIR)$(PREFIX)$(LIBDIR)
|
cp --no-dereference $(TARGET_LIBRARIES) $(DESTDIR)$(PREFIX)$(LIBDIR)
|
||||||
$(MAKE) -C utils ROOT=$(PWD) CXX=$(CXX) AR=$(AR) DEBUG=$(DEBUG) STATIC_UTILS=$(STATIC_UTILS) DEST_DIR=$(DEST_DIR) PREFIX=$(PREFIX) install
|
$(MAKE) -C utils ROOT=$(PWD) CXX=$(CXX) AR=$(AR) DEBUG=$(DEBUG) STATIC_UTILS=$(STATIC_UTILS) DESTDIR=$(DESTDIR) PREFIX=$(PREFIX) install
|
||||||
|
|
||||||
uninstall:
|
uninstall:
|
||||||
cd $(DESTDIR)$(PREFIX)/$(LIBDIR)
|
cd $(DESTDIR)$(PREFIX)/$(LIBDIR)
|
||||||
|
|||||||
@@ -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 :
|
||||||
|
|
||||||
@@ -76,6 +76,8 @@ 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
|
||||||
|
|
||||||
|
|
||||||
@@ -147,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 {
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#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"
|
||||||
@@ -36,7 +37,7 @@
|
|||||||
#define ACS_SERVER "http://adeactivate.adobe.com/adept"
|
#define ACS_SERVER "http://adeactivate.adobe.com/adept"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define LIBGOUROU_VERSION "0.8.1"
|
#define LIBGOUROU_VERSION "0.8.3"
|
||||||
|
|
||||||
namespace gourou
|
namespace gourou
|
||||||
{
|
{
|
||||||
@@ -66,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.
|
||||||
@@ -101,8 +103,9 @@ 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)
|
* @brief Return default ADEPT directory (ie /home/<user>/.config/adept)
|
||||||
@@ -232,6 +235,9 @@ 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);
|
||||||
|
void buildNotifyReq(pugi::xml_document& returnReq, pugi::xml_node& body);
|
||||||
|
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);
|
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 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);
|
||||||
|
|||||||
@@ -120,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 {
|
||||||
@@ -235,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);
|
||||||
|
|
||||||
@@ -249,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)
|
||||||
{
|
{
|
||||||
@@ -266,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
|
||||||
@@ -273,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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -399,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)
|
||||||
@@ -492,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");
|
||||||
@@ -580,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)
|
||||||
@@ -873,7 +872,8 @@ namespace gourou
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void DRMProcessor::returnLoan(const std::string& loanID, const std::string& operatorURL)
|
void DRMProcessor::returnLoan(const std::string& loanID, const std::string& operatorURL,
|
||||||
|
bool notify)
|
||||||
{
|
{
|
||||||
pugi::xml_document returnReq;
|
pugi::xml_document returnReq;
|
||||||
|
|
||||||
@@ -881,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)
|
||||||
@@ -1363,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,5 +1,5 @@
|
|||||||
BINDIR ?= /usr/bin
|
BINDIR ?= /bin
|
||||||
MANDIR ?= /usr/share/man
|
MANDIR ?= /share/man
|
||||||
|
|
||||||
TARGET_BINARIES=acsmdownloader adept_activate adept_remove adept_loan_mgt
|
TARGET_BINARIES=acsmdownloader adept_activate adept_remove adept_loan_mgt
|
||||||
TARGETS=$(TARGET_BINARIES) launcher
|
TARGETS=$(TARGET_BINARIES) launcher
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -190,6 +191,7 @@ static void usage(const char* cmd)
|
|||||||
std::cout << " " << "-f|--acsm-file" << "\t" << "Backward compatibility: 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;
|
||||||
@@ -232,13 +234,14 @@ 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: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;
|
||||||
@@ -276,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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ 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
|
||||||
{
|
{
|
||||||
@@ -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)";
|
||||||
@@ -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))
|
||||||
@@ -342,6 +348,7 @@ static void usage(const char* cmd)
|
|||||||
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;
|
||||||
@@ -375,6 +382,7 @@ int main(int argc, char** argv)
|
|||||||
{"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' },
|
||||||
@@ -402,6 +410,9 @@ int main(int argc, char** argv)
|
|||||||
deleteID = optarg;
|
deleteID = optarg;
|
||||||
actions++;
|
actions++;
|
||||||
break;
|
break;
|
||||||
|
case 'N':
|
||||||
|
notify = false;
|
||||||
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
verbose++;
|
verbose++;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
|||||||
@@ -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())
|
||||||
{
|
{
|
||||||
@@ -291,6 +303,12 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
|
|||||||
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));
|
||||||
|
|
||||||
|
long http_code = 400;
|
||||||
|
curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
|
||||||
|
|
||||||
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user