From 570ad837472b600f3dfcca108786fd852f207ee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Soutad=C3=A9?= Date: Sun, 3 Apr 2022 09:32:06 +0200 Subject: [PATCH] Manage loan tokens --- Makefile | 2 +- include/fulfillment_item.h | 12 +++++- include/libgourou.h | 11 +++++- include/libgourou_common.h | 6 ++- include/loan_token.h | 54 ++++++++++++++++++++++++++ src/fulfillment_item.cpp | 26 ++++++++++++- src/libgourou.cpp | 27 +++++++++++++ src/loan_token.cpp | 77 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 207 insertions(+), 8 deletions(-) create mode 100644 include/loan_token.h create mode 100644 src/loan_token.cpp diff --git a/Makefile b/Makefile index e4db7ae..2ce770e 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ TARGETDIR := bin SRCEXT := cpp OBJEXT := o -SOURCES = src/libgourou.cpp src/user.cpp src/device.cpp src/fulfillment_item.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 src/pugixml.cpp OBJECTS := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.$(SRCEXT)=.$(OBJEXT))) all: lib obj $(TARGETS) diff --git a/include/fulfillment_item.h b/include/fulfillment_item.h index 0cfcae0..100bcdf 100644 --- a/include/fulfillment_item.h +++ b/include/fulfillment_item.h @@ -20,7 +20,7 @@ #ifndef _FULFILLMENT_ITEM_H_ #define _FULFILLMENT_ITEM_H_ -#include "bytearray.h" +#include "loan_token.h" #include @@ -42,6 +42,8 @@ namespace gourou */ FulfillmentItem(pugi::xml_document& doc, User* user); + ~FulfillmentItem(); + /** * @brief Return metadata value from ACSM metadata section * @@ -64,13 +66,19 @@ namespace gourou */ std::string getResource(); + /** + * @brief Return loan token if there is one + */ + LoanToken* getLoanToken(); + private: pugi::xml_document fulfillDoc; pugi::xml_node metadatas; pugi::xml_document rights; std::string downloadURL; std::string resource; - + LoanToken* loanToken; + void buildRights(const pugi::xml_node& licenseToken, User* user); }; } diff --git a/include/libgourou.h b/include/libgourou.h index 88d8c63..625dc2e 100644 --- a/include/libgourou.h +++ b/include/libgourou.h @@ -40,7 +40,7 @@ #define ACS_SERVER "http://adeactivate.adobe.com/adept" #endif -#define LIBGOUROU_VERSION "0.6" +#define LIBGOUROU_VERSION "0.7" namespace gourou { @@ -100,6 +100,14 @@ namespace gourou */ void activateDevice(); + /** + * @brief Return loaned book to server + * + * @param loanID Loan ID received during fulfill + * @param operatorURL URL of operator that loans this book + */ + void returnLoan(const std::string& loanID, const std::string& operatorURL); + /** * @brief Create a new ADEPT environment (device.xml, devicesalt and activation.xml). * @@ -218,6 +226,7 @@ namespace gourou void operatorAuth(std::string operatorURL); void buildFulfillRequest(pugi::xml_document& acsmDoc, pugi::xml_document& fulfillReq); void buildActivateReq(pugi::xml_document& activateReq); + void buildReturnReq(pugi::xml_document& returnReq, const std::string& loanID, const std::string& operatorURL); ByteArray sendFulfillRequest(const pugi::xml_document& document, const std::string& url); void buildSignInRequest(pugi::xml_document& signInRequest, const std::string& adobeID, const std::string& adobePassword, const std::string& authenticationCertificate); void fetchLicenseServiceCertificate(const std::string& licenseURL, diff --git a/include/libgourou_common.h b/include/libgourou_common.h index 298ec1f..f876f60 100644 --- a/include/libgourou_common.h +++ b/include/libgourou_common.h @@ -55,7 +55,8 @@ namespace gourou GOUROU_INVALID_CLIENT, GOUROU_TAG_NOT_FOUND, GOUROU_ADEPT_ERROR, - GOUROU_FILE_ERROR + GOUROU_FILE_ERROR, + GOUROU_INVALID_PROPERTY }; enum FULFILL_ERROR { @@ -96,7 +97,8 @@ namespace gourou }; enum FULFILL_ITEM_ERROR { - FFI_INVALID_FULFILLMENT_DATA = 0x4000 + FFI_INVALID_FULFILLMENT_DATA = 0x4000, + FFI_INVALID_LOAN_TOKEN }; enum CLIENT_ERROR { diff --git a/include/loan_token.h b/include/loan_token.h new file mode 100644 index 0000000..945f68d --- /dev/null +++ b/include/loan_token.h @@ -0,0 +1,54 @@ +/* + Copyright 2022 Grégory Soutadé + + This file is part of libgourou. + + libgourou is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + libgourou is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with libgourou. If not, see . +*/ + +#ifndef _LOAN_TOKEN_H_ +#define _LOAN_TOKEN_H_ + +#include + +#include + +namespace gourou +{ + /** + * @brief This class is a container for a fulfillment object + */ + class LoanToken + { + public: + /** + * @brief Main constructor. Not to be called by user + * + * @param doc Fulfill reply + */ + LoanToken(pugi::xml_document& doc); + + /** + * @brief Get a property (id, operatorURL, validity) + */ + std::string getProperty(const std::string& property, const std::string& _default=std::string("")); + std::string operator[](const std::string& property); + + private: + std::map properties; + }; +} + + +#endif diff --git a/src/fulfillment_item.cpp b/src/fulfillment_item.cpp index 679ac68..924c63f 100644 --- a/src/fulfillment_item.cpp +++ b/src/fulfillment_item.cpp @@ -24,7 +24,7 @@ namespace gourou { FulfillmentItem::FulfillmentItem(pugi::xml_document& doc, User* user) - : fulfillDoc() + : fulfillDoc(), loanToken(0) { fulfillDoc.reset(doc); /* We must keep a copy */ metadatas = fulfillDoc.select_node("//metadata").node(); @@ -50,8 +50,25 @@ namespace gourou EXCEPTION(FFI_INVALID_FULFILLMENT_DATA, "Any license token in document"); buildRights(licenseToken, user); + + node = doc.select_node("/envelope/fulfillmentResult/returnable").node(); + try + { + if (node && node.first_child().value() == std::string("true")) + loanToken = new LoanToken(doc); + } + catch(std::exception& e) + { + GOUROU_LOG(ERROR, "Book is returnable, but contains invalid loan token"); + GOUROU_LOG(ERROR, e.what()); + } } - + + FulfillmentItem::~FulfillmentItem() + { + if (loanToken) delete loanToken; + } + void FulfillmentItem::buildRights(const pugi::xml_node& licenseToken, User* user) { pugi::xml_node decl = rights.append_child(pugi::node_declaration); @@ -103,4 +120,9 @@ namespace gourou { return resource; } + + LoanToken* FulfillmentItem::getLoanToken() + { + return loanToken; + } } diff --git a/src/libgourou.cpp b/src/libgourou.cpp index b77bea8..d0f22ae 100644 --- a/src/libgourou.cpp +++ b/src/libgourou.cpp @@ -837,6 +837,33 @@ namespace gourou user->updateActivationFile(activationDoc); } + void DRMProcessor::buildReturnReq(pugi::xml_document& returnReq, const std::string& loanID, const std::string& operatorURL) + { + pugi::xml_node decl = returnReq.append_child(pugi::node_declaration); + decl.append_attribute("version") = "1.0"; + + pugi::xml_node root = returnReq.append_child("adept:loanReturn"); + root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS; + + appendTextElem(root, "adept:user", user->getUUID()); + appendTextElem(root, "adept:device", user->getDeviceUUID()); + appendTextElem(root, "adept:loan", loanID); + + addNonce(root); + signNode(root); + } + + void DRMProcessor::returnLoan(const std::string& loanID, const std::string& operatorURL) + { + pugi::xml_document returnReq; + + GOUROU_LOG(INFO, "Return loan " << loanID); + + buildReturnReq(returnReq, loanID, operatorURL); + + sendRequest(returnReq, operatorURL + "/LoanReturn"); + } + ByteArray DRMProcessor::encryptWithDeviceKey(const unsigned char* data, unsigned int len) { const unsigned char* deviceKey = device->getDeviceKey(); diff --git a/src/loan_token.cpp b/src/loan_token.cpp new file mode 100644 index 0000000..1d10086 --- /dev/null +++ b/src/loan_token.cpp @@ -0,0 +1,77 @@ +/* + Copyright 2022 Grégory Soutadé + + This file is part of libgourou. + + libgourou is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + libgourou is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with libgourou. If not, see . +*/ + +#include "libgourou_common.h" +#include "loan_token.h" + +namespace gourou +{ + LoanToken::LoanToken(pugi::xml_document& doc) + { + pugi::xml_node node = doc.select_node("/envelope/loanToken").node(); + + if (!node) + EXCEPTION(FFI_INVALID_LOAN_TOKEN, "No loanToken element in document"); + + node = doc.select_node("/envelope/loanToken/loan").node(); + + if (!node) + EXCEPTION(FFI_INVALID_LOAN_TOKEN, "No loanToken/loan element in document"); + + properties["id"] = node.first_child().value(); + + node = doc.select_node("/envelope/loanToken/operatorURL").node(); + + if (!node) + EXCEPTION(FFI_INVALID_LOAN_TOKEN, "No loanToken/operatorURL element in document"); + + properties["operatorURL"] = node.first_child().value(); + + node = doc.select_node("/envelope/fulfillmentResult/resourceItemInfo/licenseToken/permissions/display/until").node(); + + if (node) + properties["validity"] = node.first_child().value(); + else + { + node = doc.select_node("/envelope/fulfillmentResult/resourceItemInfo/licenseToken/permissions/play/until").node(); + if (node) + properties["validity"] = node.first_child().value(); + else + EXCEPTION(FFI_INVALID_LOAN_TOKEN, "No loanToken/operatorURL element in document"); + } + } + + std::string LoanToken::getProperty(const std::string& property, const std::string& _default) + { + if (properties.find(property) == properties.end()) + { + if (_default == "") + EXCEPTION(GOUROU_INVALID_PROPERTY, "Invalid property " << property); + + return _default; + } + + return properties[property]; + } + + std::string LoanToken::operator[](const std::string& property) + { + return getProperty(property); + } +}