Compare commits

...

24 Commits

Author SHA1 Message Date
soutade eab41809a0 Compilation with static OpenSSL3 2024-09-19 08:43:23 +02:00
soutade 204500117d Typo fix 2024-09-19 08:41:15 +02:00
soutade ce2cf4192a Update README.md 2024-08-31 08:27:53 +02:00
soutade 68bf48df27 Update README.md 2024-08-27 21:48:39 +02:00
soutade 81faf1f9be adept_loan_mgt: manage cases were name is empty 2024-04-15 07:37:28 +02:00
nicokosi f60abf04d8 Fix typos in README.md 2024-04-11 06:05:23 +02:00
soutade 0d77cf55e1 Update version 2024-03-28 21:58:07 +01:00
soutade 86a79cc381 Remove whole EBX objects for PDF when removing DRM 2024-03-28 21:54:23 +01:00
soutade 68bf982b6f Fix use after free in DRMProcessorClientImpl::sendHTTPRequest() 2024-01-24 19:13:22 +01:00
soutade ef8c2644ca Update version 2024-01-16 11:09:31 +01:00
soutade e05639c09d Support HTTP error codes != 200 (exception) and cookies in drmprocessorclientimpl 2024-01-06 09:25:11 +01:00
soutade 69865e005b Register operatorURL only when certificate from operator is fetched 2024-01-06 09:23:03 +01:00
soutade fd38e84da6 Fix for Android for adept_loan_mgt.cpp (strptime format) 2024-01-06 09:22:11 +01:00
soutade 92a67312bd Update README.md 2023-09-06 21:21:43 +02:00
soutade 29d298b373 Update libgourou version 2023-09-06 21:17:06 +02:00
soutade 40dcb7a041 Add --no-notify option to utils 2023-09-06 21:17:06 +02:00
soutade e0bb1bd4f8 Add notify server feature 2023-09-06 21:17:06 +02:00
soutade 9388d82138 Loan ID must be Fullfilment ID, not <loan> value(s) 2023-09-04 18:28:47 +02:00
soutade c19279397f Update README.md 2023-08-09 20:58:06 +02:00
soutade bb5349d710 Update version 2023-08-08 20:14:23 +02:00
soutade 9a75213b49 Fix misuse of DESTDIR and PREFIX in Makefile 2023-08-08 20:13:03 +02:00
soutade e06d20a392 DRM removal: Forgot to decrypt HexaString objects 2023-08-05 14:43:48 +02:00
soutade a0f6324999 Try to fix GCC 13 compilation errors 2023-05-03 21:15:31 +02:00
soutade c259cbd5a3 Add missing libgen.h in utils for basename() call 2023-03-28 20:32:05 +02:00
16 changed files with 288 additions and 104 deletions
+5 -8
View File
@@ -1,13 +1,10 @@
LIBDIR ?= /usr/lib
INCDIR ?= /usr/include
PREFIX ?= /usr/local
LIBDIR ?= /lib
INCDIR ?= /include
AR ?= $(CROSS)ar
CXX ?= $(CROSS)g++
ifeq ($(PREFIX),)
PREFIX := /usr/local
endif
UPDFPARSERLIB = ./lib/updfparser/libupdfparser.a
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 $^ $@
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 -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) 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:
cd $(DESTDIR)$(PREFIX)/$(LIBDIR)
+36 -19
View File
@@ -1,16 +1,16 @@
Introduction
------------
libgourou is a free implementation of Adobe's ADEPT protocol used to add DRM on ePub/PDF files. It overcome the lacks of Adobe support for Linux platforms.
libgourou is a free implementation of Adobe's ADEPT protocol used to add DRM on ePub/PDF files. It overcomes the lack of Adobe support for Linux platforms.
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.
A reference implementation using Qt, OpenSSL and libzip is provided (in _utils_ directory).
Like RMSDK, libgourou has a client/server scheme. All platform specific functions (crypto, network...) have to be implemented in a client class (that derives from DRMProcessorClient) while server implements ADEPT protocol.
A reference implementation using cURL, OpenSSL and libzip is provided (in _utils_ directory).
Main fucntions to use from gourou::DRMProcessor are :
Main functions to use from gourou::DRMProcessor are:
* Get an ePub from an ACSM file : _fulfill()_ and _download()_
* Create a new device : _createDRMProcessor()_
@@ -18,32 +18,32 @@ Main fucntions to use from gourou::DRMProcessor are :
* Remove DRM : _removeDRM()_
* Return loaned book : _returnLoan()_
You can import configuration from (at least) :
You can import configuration from (at least):
* Kobo device : .adept/device.xml, .adept/devicesalt and .adept/activation.xml
* Bookeen device : .adobe-digital-editions/device.xml, root/devkey.bin and .adobe-digital-editions/activation.xml
Or create a new one. Be careful : there is a limited number of devices that can be created bye one account.
Or create a new one. Be careful: there is a limited number of devices that can be created by one account.
ePub are encrypted using a shared key : one account / multiple devices, so you can create and register a device into your computer and read downloaded (and encrypted) ePub file with your eReader configured using the same AdobeID account.
ePub are encrypted using a shared key: one account / multiple devices, so you can create and register a device into your computer and read downloaded (and encrypted) ePub file with your eReader configured using the same AdobeID account.
For those who wants to remove DRM without adept_remove, you can export your private key and import it within [Calibre](https://calibre-ebook.com/) an its DeDRM plugin.
For those who want to remove DRM without adept_remove, you can export your private key and import it within [Calibre](https://calibre-ebook.com/) an its DeDRM plugin.
Dependencies
------------
For libgourou :
For libgourou:
_externals_ :
* libpugixml
_internals_ :
_internals_:
* uPDFParser
For utils :
For utils:
* libcurl
* OpenSSL
@@ -52,7 +52,7 @@ For utils :
Internal libraries are automatically fetched and statically compiled during the first run.
When you update libgourou's repository, **don't forget to update internal libraries** with :
When you update libgourou's repository, **don't forget to update internal libraries** with:
make update_lib
@@ -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
other variables are DESTDIR and PREFIX to handle destination install directory
* Default value
@@ -90,31 +92,31 @@ You can optionaly specify your .adept directory
export ADEPT_DIR=/home/XXX
Then, use utils as following :
Then, use utils as following:
You can import configuration from your eReader or create a new one with _utils/adept\_activate_ :
You can import configuration from your eReader or create a new one with _utils/adept\_activate_:
./utils/adept_activate -u <AdobeID USERNAME>
Then a _/home/<user>/.config/adept_ directory is created with all configuration file
To download an ePub/PDF :
To download an ePub/PDF:
./utils/acsmdownloader <ACSM_FILE>
To export your private key (for DeDRM software) :
To export your private key (for DeDRM software):
./utils/acsmdownloader --export-private-key [-o adobekey_1.der]
To remove ADEPT DRM :
To remove ADEPT DRM:
./utils/adept_remove <encryptedFile>
To list loaned books :
To list loaned books:
./utils/adept_loan_mgt [-l]
To return a loaned book :
To return a loaned book:
./utils/adept_loan_mgt -r <id>
@@ -147,3 +149,18 @@ Special thanks
* _Jens_ for all test samples and utils testing
* _Milian_ for debug & code
* _Berwyn H_ for all test samples, feedbacks, patches and kind donation
Donation
--------
https://www.paypal.com/donate/?hosted_button_id=JD3U6XMZCPHKN
Donators
--------
* _Berwyn H_
* _bwitt_
* _Ismail_
+1
View File
@@ -26,6 +26,7 @@
*/
#include <string>
#include <stdint.h>
namespace macaron {
+10 -4
View File
@@ -27,6 +27,7 @@
#include "drmprocessorclient.h"
#include <pugixml.hpp>
#include <stdint.h>
#ifndef HOBBES_DEFAULT_VERSION
#define HOBBES_DEFAULT_VERSION "10.0.4"
@@ -36,7 +37,7 @@
#define ACS_SERVER "http://adeactivate.adobe.com/adept"
#endif
#define LIBGOUROU_VERSION "0.8.1"
#define LIBGOUROU_VERSION "0.8.6"
namespace gourou
{
@@ -66,10 +67,11 @@ namespace gourou
* @brief Fulfill ACSM file to server in order to retrieve ePub fulfillment item
*
* @param ACSMFile Path of ACSMFile
* @param notify Notify server if requested by response
*
* @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.
@@ -101,8 +103,9 @@ namespace gourou
*
* @param loanID Loan ID received during fulfill
* @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)
@@ -155,7 +158,7 @@ namespace gourou
* @brief Send HTTP POST request to URL with document as POSTData
*/
ByteArray sendRequest(const pugi::xml_document& document, const std::string& url);
/**
* @brief In place encrypt data with private device key
*/
@@ -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 fetchLicenseServiceCertificate(const std::string& licenseURL,
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);
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);
+43 -18
View File
@@ -120,6 +120,7 @@ namespace gourou
CLIENT_OSSL_ERROR,
CLIENT_CRYPT_ERROR,
CLIENT_DIGEST_ERROR,
CLIENT_HTTP_ERROR
};
enum DRM_REMOVAL_ERROR {
@@ -235,12 +236,7 @@ namespace gourou
return ltrim(rtrim(s, t), t);
}
/**
* @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)
static inline pugi::xml_node getNode(const pugi::xml_node& root, const char* tagName, bool throwOnNull=true)
{
pugi::xpath_node xpath_node = root.select_node(tagName);
@@ -249,10 +245,23 @@ namespace gourou
if (throwOnNull)
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)
{
@@ -266,6 +275,30 @@ namespace gourou
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
* 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)
{
pugi::xpath_node xpath_node = root.select_node(tagName);
pugi::xml_node node = getNode(root, tagName, throwOnNull);
if (!xpath_node)
{
if (throwOnNull)
EXCEPTION(GOUROU_TAG_NOT_FOUND, "Tag " << tagName << " not found");
return "";
}
pugi::xml_attribute attr = xpath_node.node().attribute(attributeName);
pugi::xml_attribute attr = node.attribute(attributeName);
if (!attr)
{
+9
View File
@@ -7,3 +7,12 @@ if [ ! -d lib/updfparser ] ; then
make BUILD_STATIC=1 BUILD_SHARED=0
popd
fi
# OpenSSL legacy
if [ ! -d lib/openssl ] ; then
git clone --branch openssl-3.3 https://github.com/openssl/openssl.git lib/openssl
pushd lib/openssl
./Configure disable-apps disable-shared enable-legacy
make -j4
popd
fi
+1 -1
View File
@@ -3,7 +3,7 @@
if [ ! -d lib/updfparser ] ; then
echo "Some libraries are missing"
echo "You must run this script at the top of libgourou working direcotry."
echo "./lib/setup.sh must be called first (make all)"
echo "./scripts/setup.sh must be called first (make all)"
exit 1
fi
+124 -32
View File
@@ -398,31 +398,7 @@ namespace gourou
}
}
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);
doOperatorAuth(operatorURL);
}
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: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);
}
FulfillmentItem* DRMProcessor::fulfill(const std::string& ACSMFile)
FulfillmentItem* DRMProcessor::fulfill(const std::string& ACSMFile, bool notify)
{
if (!user->getPKCS12().length())
EXCEPTION(FF_NOT_ACTIVATED, "Device not activated");
@@ -580,7 +574,12 @@ namespace gourou
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)
@@ -873,7 +872,8 @@ namespace gourou
#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;
@@ -881,9 +881,73 @@ namespace gourou
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)
{
const unsigned char* deviceKey = device->getDeviceKey();
@@ -1253,6 +1317,7 @@ namespace gourou
std::vector<uPDFParser::Object*> objects = parser.objects();
std::vector<uPDFParser::Object*>::iterator it;
std::vector<uPDFParser::Object*>::reverse_iterator rIt;
std::vector<uPDFParser::Object*> ebxObjects;
unsigned char decryptedKey[16];
int ebxId;
@@ -1263,7 +1328,7 @@ namespace gourou
{
EBXHandlerFound = true;
uPDFParser::Object* ebx = *rIt;
ebxVersion = (uPDFParser::Integer*)(*ebx)["V"];
if (ebxVersion->value() != 4)
{
@@ -1274,7 +1339,7 @@ namespace gourou
{
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "No ADEPT_LICENSE found");
}
uPDFParser::String* licenseObject = (uPDFParser::String*)(*ebx)["ADEPT_LICENSE"];
std::string value = licenseObject->value();
@@ -1311,7 +1376,7 @@ namespace gourou
if (object->objectId() == ebxId)
{
// object->deleteKey("Filter");
ebxObjects.push_back(object);
continue;
}
@@ -1363,6 +1428,30 @@ namespace gourou
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++)
@@ -1397,6 +1486,9 @@ namespace gourou
}
}
for(it = ebxObjects.begin(); it != ebxObjects.end(); it++)
parser.removeObject(*it);
uPDFParser::Object& trailer = parser.getTrailer();
trailer.deleteKey("Encrypt");
+2 -13
View File
@@ -29,24 +29,13 @@ namespace gourou
if (!node)
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)
properties["id"] = node.first_child().value();
else
{
node = doc.select_node("/envelope/fulfillmentResult/resourceItemInfo/licenseToken/permissions/display/loan").node();
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");
}
EXCEPTION(FFI_INVALID_LOAN_TOKEN, "No fulfillment element in document");
}
node = doc.select_node("/envelope/loanToken/operatorURL").node();
+3 -2
View File
@@ -1,5 +1,5 @@
BINDIR ?= /usr/bin
MANDIR ?= /usr/share/man
BINDIR ?= /bin
MANDIR ?= /share/man
TARGET_BINARIES=acsmdownloader adept_activate adept_remove adept_loan_mgt
TARGETS=$(TARGET_BINARIES) launcher
@@ -10,6 +10,7 @@ CXXFLAGS=-Wall -fPIC -I$(ROOT)/include
STATIC_DEP=
LDFLAGS += -L$(ROOT) -lcrypto -lzip -lz -lcurl -lpugixml
LDFLAGS += -L$(ROOT) $(ROOT)/lib/openssl/libcrypto.a -lzip -lz -lcurl -lpugixml
ifneq ($(STATIC_UTILS),)
STATIC_DEP = $(ROOT)/libgourou.a
+8 -2
View File
@@ -46,6 +46,7 @@ static bool exportPrivateKey = false;
static const char* outputFile = 0;
static const char* outputDir = 0;
static bool resume = false;
static bool notify = true;
class ACSMDownloader
@@ -82,7 +83,7 @@ public:
}
else
{
gourou::FulfillmentItem* item = processor.fulfill(acsmFile);
gourou::FulfillmentItem* item = processor.fulfill(acsmFile, notify);
std::string filename;
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 << " " << "-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 << " " << "-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|--version" << "\t\t" << "Display libgourou version" << 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' },
{"export-private-key",no_argument, 0, 'e' },
{"resume", no_argument, 0, 'r' },
{"no-notify", no_argument, 0, 'N' },
{"verbose", no_argument, 0, 'v' },
{"version", no_argument, 0, 'V' },
{"help", no_argument, 0, 'h' },
{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);
if (c == -1)
break;
@@ -276,6 +279,9 @@ int main(int argc, char** argv)
case 'r':
resume = true;
break;
case 'N':
notify = false;
break;
case 'v':
verbose++;
break;
+1
View File
@@ -31,6 +31,7 @@
#include <termios.h>
#include <string.h>
#include <limits.h>
#include <libgen.h>
#include <iostream>
#include <ostream>
+22 -4
View File
@@ -52,6 +52,7 @@ static const char* devicekeyFile = "devicesalt";
static bool list = false;
static const char* returnID = 0;
static const char* deleteID = 0;
static bool notify = true;
struct Loan
{
@@ -182,8 +183,13 @@ private:
loan->bookName = node.first_child().value();
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);
if (*res == 0)
#endif
if (res != NULL && *res == 0)
{
if (mktime(&tm) <= time(NULL))
loan->validity = " (Expired)";
@@ -223,7 +229,12 @@ private:
maxSizeBookName = loan->bookName.size();
}
if (maxSizeBookName > MAX_SIZE_BOOK_NAME)
/* Manage empty names */
if (maxSizeBookName == 0)
maxSizeBookName = sizeof("No name ")-1;
else if (maxSizeBookName < 4)
maxSizeBookName = 4;
else if (maxSizeBookName > MAX_SIZE_BOOK_NAME)
maxSizeBookName = MAX_SIZE_BOOK_NAME;
else if ((maxSizeBookName % 2))
maxSizeBookName++;
@@ -270,7 +281,9 @@ private:
std::cout << kv.first;
std::cout << " ";
if (loan->bookName.size() > MAX_SIZE_BOOK_NAME)
if (loan->bookName.size() == 0)
bookName = std::string("No name ");
else if (loan->bookName.size() > MAX_SIZE_BOOK_NAME)
bookName = std::string(loan->bookName.c_str(), MAX_SIZE_BOOK_NAME);
else
bookName = loan->bookName;
@@ -296,7 +309,7 @@ private:
return;
}
processor.returnLoan(loan->id, loan->operatorURL);
processor.returnLoan(loan->id, loan->operatorURL, notify);
deleteID = returnID;
if (deleteLoan(false))
@@ -342,6 +355,7 @@ static void usage(const char* cmd)
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 << " " << "-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|--version" << "\t\t" << "Display libgourou version" << std::endl;
std::cout << " " << "-h|--help" << "\t\t" << "This help" << std::endl;
@@ -375,6 +389,7 @@ int main(int argc, char** argv)
{"list", no_argument, 0, 'l' },
{"return", no_argument, 0, 'r' },
{"delete", no_argument, 0, 'd' },
{"no-notify", no_argument, 0, 'N' },
{"verbose", no_argument, 0, 'v' },
{"version", no_argument, 0, 'V' },
{"help", no_argument, 0, 'h' },
@@ -402,6 +417,9 @@ int main(int argc, char** argv)
deleteID = optarg;
actions++;
break;
case 'N':
notify = false;
break;
case 'v':
verbose++;
break;
+1
View File
@@ -27,6 +27,7 @@
*/
#include <getopt.h>
#include <libgen.h>
#include <iostream>
+20 -1
View File
@@ -30,6 +30,7 @@
#include <algorithm>
#include <cctype>
#include <locale>
#include <stdlib.h>
#define OPENSSL_NO_DEPRECATED 1
@@ -60,6 +61,14 @@ DRMProcessorClientImpl::DRMProcessorClientImpl():
if (!deflt)
EXCEPTION(gourou::CLIENT_OSSL_ERROR, "Error, OpenSSL default provider not available");
#endif
#ifdef WIN32
strcpy(cookiejar, "C:\\temp\\libgourou_cookie_jar_XXXXXX");
#else
strcpy(cookiejar, "/tmp/libgourou_cookie_jar_XXXXXX");
#endif
mkstemp(cookiejar);
}
DRMProcessorClientImpl::~DRMProcessorClientImpl()
@@ -71,6 +80,8 @@ DRMProcessorClientImpl::~DRMProcessorClientImpl()
if (deflt)
OSSL_PROVIDER_unload(deflt);
#endif
unlink(cookiejar);
}
/* 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_COOKIEJAR, cookiejar);
if (POSTData.size())
{
@@ -286,11 +298,18 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
}
curl_slist_free_all(list);
long http_code = 400;
curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
curl_easy_cleanup(curl);
if (res != CURLE_OK)
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) &&
gourou::logLevel >= gourou::LG_LOG_WARN)
std::cout << std::endl;
+2
View File
@@ -136,6 +136,8 @@ private:
#else
void *legacy, *deflt;
#endif
char cookiejar[64];
};
#endif