Compare commits

...

9 Commits

7 changed files with 83 additions and 51 deletions

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.
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
@ -92,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>

View File

@ -37,7 +37,7 @@
#define ACS_SERVER "http://adeactivate.adobe.com/adept"
#endif
#define LIBGOUROU_VERSION "0.8.3"
#define LIBGOUROU_VERSION "0.8.6"
namespace gourou
{

View File

@ -120,6 +120,7 @@ namespace gourou
CLIENT_OSSL_ERROR,
CLIENT_CRYPT_ERROR,
CLIENT_DIGEST_ERROR,
CLIENT_HTTP_ERROR
};
enum DRM_REMOVAL_ERROR {

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,6 +468,24 @@ 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);
}
@ -1323,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;
@ -1333,7 +1328,7 @@ namespace gourou
{
EBXHandlerFound = true;
uPDFParser::Object* ebx = *rIt;
ebxVersion = (uPDFParser::Integer*)(*ebx)["V"];
if (ebxVersion->value() != 4)
{
@ -1344,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();
@ -1381,7 +1376,7 @@ namespace gourou
if (object->objectId() == ebxId)
{
// object->deleteKey("Filter");
ebxObjects.push_back(object);
continue;
}
@ -1491,6 +1486,9 @@ namespace gourou
}
}
for(it = ebxObjects.begin(); it != ebxObjects.end(); it++)
parser.removeObject(*it);
uPDFParser::Object& trailer = parser.getTrailer();
trailer.deleteKey("Encrypt");

View File

@ -183,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)";
@ -224,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++;
@ -271,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;

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;

View File

@ -136,6 +136,8 @@ private:
#else
void *legacy, *deflt;
#endif
char cookiejar[64];
};
#endif