Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d3112bc38 | ||
|
|
e149af9e17 | ||
|
|
1221b2a95a | ||
|
|
2ce6142596 | ||
|
|
0f475423c0 | ||
|
|
9b946a62b4 | ||
|
|
432eb6f6cb | ||
|
|
85b65f8d61 | ||
|
|
25f5049ab9 |
4
Makefile
4
Makefile
@@ -53,8 +53,8 @@ $(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
|
|||||||
|
|
||||||
libgourou: libgourou.a libgourou.so
|
libgourou: libgourou.a libgourou.so
|
||||||
|
|
||||||
libgourou.a: $(OBJECTS)
|
libgourou.a: $(OBJECTS) $(UPDFPARSERLIB)
|
||||||
$(AR) crs $@ obj/*.o ./lib/updfparser/obj/*.o
|
$(AR) crs $@ obj/*.o $(UPDFPARSERLIB)
|
||||||
|
|
||||||
libgourou.so: $(OBJECTS) $(UPDFPARSERLIB)
|
libgourou.so: $(OBJECTS) $(UPDFPARSERLIB)
|
||||||
$(CXX) obj/*.o $(LDFLAGS) -o $@ -shared
|
$(CXX) obj/*.o $(LDFLAGS) -o $@ -shared
|
||||||
|
|||||||
@@ -39,8 +39,7 @@ For libgourou :
|
|||||||
|
|
||||||
For utils :
|
For utils :
|
||||||
|
|
||||||
* QT5Core
|
* libcurl
|
||||||
* QT5Network
|
|
||||||
* OpenSSL
|
* OpenSSL
|
||||||
* libzip
|
* libzip
|
||||||
|
|
||||||
|
|||||||
@@ -98,10 +98,11 @@ namespace gourou
|
|||||||
* @param POSTData POST data if needed, if not set, a GET request is done
|
* @param POSTData POST data if needed, if not set, a GET request is done
|
||||||
* @param contentType Optional content type of POST Data
|
* @param contentType Optional content type of POST Data
|
||||||
* @param responseHeaders Optional Response headers of HTTP request
|
* @param responseHeaders Optional Response headers of HTTP request
|
||||||
|
* @param fd Optional file descriptor to write request result
|
||||||
*
|
*
|
||||||
* @return data of HTTP response
|
* @return data of HTTP response
|
||||||
*/
|
*/
|
||||||
virtual std::string sendHTTPRequest(const std::string& URL, const std::string& POSTData=std::string(""), const std::string& contentType=std::string(""), std::map<std::string, std::string>* responseHeaders=0) = 0;
|
virtual std::string sendHTTPRequest(const std::string& URL, const std::string& POSTData=std::string(""), const std::string& contentType=std::string(""), std::map<std::string, std::string>* responseHeaders=0, int fd=0) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RSAInterface
|
class RSAInterface
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
#define ACS_SERVER "http://adeactivate.adobe.com/adept"
|
#define ACS_SERVER "http://adeactivate.adobe.com/adept"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define LIBGOUROU_VERSION "0.5.2"
|
#define LIBGOUROU_VERSION "0.6"
|
||||||
|
|
||||||
namespace gourou
|
namespace gourou
|
||||||
{
|
{
|
||||||
@@ -134,10 +134,11 @@ namespace gourou
|
|||||||
* @param POSTData POST data if needed, if not set, a GET request is done
|
* @param POSTData POST data if needed, if not set, a GET request is done
|
||||||
* @param contentType Optional content type of POST Data
|
* @param contentType Optional content type of POST Data
|
||||||
* @param responseHeaders Optional Response headers of HTTP request
|
* @param responseHeaders Optional Response headers of HTTP request
|
||||||
|
* @param fd Optional File descriptor to write received data
|
||||||
*
|
*
|
||||||
* @return data of HTTP response
|
* @return data of HTTP response
|
||||||
*/
|
*/
|
||||||
ByteArray sendRequest(const std::string& URL, const std::string& POSTData=std::string(), const char* contentType=0, std::map<std::string, std::string>* responseHeaders=0);
|
ByteArray sendRequest(const std::string& URL, const std::string& POSTData=std::string(), const char* contentType=0, std::map<std::string, std::string>* responseHeaders=0, int fd=0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Send HTTP POST request to URL with document as POSTData
|
* @brief Send HTTP POST request to URL with document as POSTData
|
||||||
@@ -185,10 +186,16 @@ namespace gourou
|
|||||||
DRMProcessorClient* getClient() { return client; }
|
DRMProcessorClient* getClient() { return client; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Remove ADEPT DRM.
|
* @brief Remove ADEPT DRM
|
||||||
* Warning: for PDF format, filenameIn must be different than filenameOut
|
* Warning: for PDF format, filenameIn must be different than filenameOut
|
||||||
|
*
|
||||||
|
* @param filenameIn Input file (with ADEPT DRM)
|
||||||
|
* @param filenameOut Output file (without ADEPT DRM)
|
||||||
|
* @param type Type of file (ePub or PDF)
|
||||||
|
* @param encryptionKey Optional encryption key, do not try to decrypt the one inside input file
|
||||||
|
* @param encryptionKeySize Size of encryption key (if provided)
|
||||||
*/
|
*/
|
||||||
void removeDRM(const std::string& filenameIn, const std::string& filenameOut, ITEM_TYPE type);
|
void removeDRM(const std::string& filenameIn, const std::string& filenameOut, ITEM_TYPE type, const unsigned char* encryptionKey=0, unsigned encryptionKeySize=0);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
gourou::DRMProcessorClient* client;
|
gourou::DRMProcessorClient* client;
|
||||||
@@ -214,12 +221,12 @@ namespace gourou
|
|||||||
void fetchLicenseServiceCertificate(const std::string& licenseURL,
|
void fetchLicenseServiceCertificate(const std::string& licenseURL,
|
||||||
const std::string& operatorURL);
|
const std::string& operatorURL);
|
||||||
void decryptADEPTKey(const std::string& encryptedKey, unsigned char* decryptedKey);
|
void decryptADEPTKey(const std::string& encryptedKey, unsigned char* decryptedKey);
|
||||||
void removeEPubDRM(const std::string& filenameIn, const std::string& filenameOut);
|
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,
|
||||||
int objectId, int objectGenerationNumber,
|
int objectId, int objectGenerationNumber,
|
||||||
unsigned char* keyOut);
|
unsigned char* keyOut);
|
||||||
void removePDFDRM(const std::string& filenameIn, const std::string& filenameOut);
|
void removePDFDRM(const std::string& filenameIn, const std::string& filenameOut, const unsigned char* encryptionKey, unsigned encryptionKeySize);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ namespace gourou
|
|||||||
USER_INVALID_ACTIVATION_FILE,
|
USER_INVALID_ACTIVATION_FILE,
|
||||||
USER_NO_AUTHENTICATION_URL,
|
USER_NO_AUTHENTICATION_URL,
|
||||||
USER_NO_PROPERTY,
|
USER_NO_PROPERTY,
|
||||||
|
USER_INVALID_INPUT,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum FULFILL_ITEM_ERROR {
|
enum FULFILL_ITEM_ERROR {
|
||||||
@@ -110,7 +111,8 @@ namespace gourou
|
|||||||
CLIENT_ZIP_ERROR,
|
CLIENT_ZIP_ERROR,
|
||||||
CLIENT_GENERIC_EXCEPTION,
|
CLIENT_GENERIC_EXCEPTION,
|
||||||
CLIENT_NETWORK_ERROR,
|
CLIENT_NETWORK_ERROR,
|
||||||
CLIENT_INVALID_PKCS8
|
CLIENT_INVALID_PKCS8,
|
||||||
|
CLIENT_FILE_ERROR
|
||||||
};
|
};
|
||||||
|
|
||||||
enum DRM_REMOVAL_ERROR {
|
enum DRM_REMOVAL_ERROR {
|
||||||
@@ -285,15 +287,27 @@ namespace gourou
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Write data in a file. If it already exists, it's truncated
|
* @brief Open a file descriptor on path. If it already exists, it's truncated
|
||||||
|
*
|
||||||
|
* @return Created fd, must be closed
|
||||||
*/
|
*/
|
||||||
static inline void writeFile(std::string path, const unsigned char* data, unsigned int length)
|
static inline int createNewFile(std::string path)
|
||||||
{
|
{
|
||||||
int fd = open(path.c_str(), O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
|
int fd = open(path.c_str(), O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
|
||||||
|
|
||||||
if (fd <= 0)
|
if (fd <= 0)
|
||||||
EXCEPTION(GOUROU_FILE_ERROR, "Unable to create " << path);
|
EXCEPTION(GOUROU_FILE_ERROR, "Unable to create " << path);
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Write data in a file. If it already exists, it's truncated
|
||||||
|
*/
|
||||||
|
static inline void writeFile(std::string path, const unsigned char* data, unsigned int length)
|
||||||
|
{
|
||||||
|
int fd = createNewFile(path);
|
||||||
|
|
||||||
if (write(fd, data, length) != length)
|
if (write(fd, data, length) != length)
|
||||||
EXCEPTION(GOUROU_FILE_ERROR, "Write error for file " << path);
|
EXCEPTION(GOUROU_FILE_ERROR, "Write error for file " << path);
|
||||||
|
|
||||||
|
|||||||
@@ -300,11 +300,13 @@ namespace gourou
|
|||||||
appendTextElem(root, "adept:expiration", buffer);
|
appendTextElem(root, "adept:expiration", buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteArray DRMProcessor::sendRequest(const std::string& URL, const std::string& POSTdata, const char* contentType, std::map<std::string, std::string>* responseHeaders)
|
ByteArray DRMProcessor::sendRequest(const std::string& URL, const std::string& POSTdata, const char* contentType, std::map<std::string, std::string>* responseHeaders, int fd)
|
||||||
{
|
{
|
||||||
if (contentType == 0)
|
if (contentType == 0)
|
||||||
contentType = "";
|
contentType = "";
|
||||||
std::string reply = client->sendHTTPRequest(URL, POSTdata, contentType, responseHeaders);
|
std::string reply = client->sendHTTPRequest(URL, POSTdata, contentType, responseHeaders, fd);
|
||||||
|
|
||||||
|
if (fd) return ByteArray();
|
||||||
|
|
||||||
pugi::xml_document replyDoc;
|
pugi::xml_document replyDoc;
|
||||||
replyDoc.load_buffer(reply.c_str(), reply.length());
|
replyDoc.load_buffer(reply.c_str(), reply.length());
|
||||||
@@ -590,9 +592,11 @@ namespace gourou
|
|||||||
|
|
||||||
std::map<std::string, std::string> headers;
|
std::map<std::string, std::string> headers;
|
||||||
|
|
||||||
ByteArray replyData = sendRequest(item->getDownloadURL(), "", 0, &headers);
|
int fd = createNewFile(path);
|
||||||
|
|
||||||
writeFile(path, replyData);
|
sendRequest(item->getDownloadURL(), "", 0, &headers, fd);
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
|
||||||
GOUROU_LOG(INFO, "Download into " << path);
|
GOUROU_LOG(INFO, "Download into " << path);
|
||||||
|
|
||||||
@@ -667,7 +671,10 @@ namespace gourou
|
|||||||
pugi::xml_node signIn = signInRequest.append_child("adept:signIn");
|
pugi::xml_node signIn = signInRequest.append_child("adept:signIn");
|
||||||
signIn.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;
|
signIn.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS;
|
||||||
std::string loginMethod = user->getLoginMethod();
|
std::string loginMethod = user->getLoginMethod();
|
||||||
if (loginMethod.size())
|
|
||||||
|
if (adobeID == "anonymous")
|
||||||
|
signIn.append_attribute("method") = "anonymous";
|
||||||
|
else if (loginMethod.size())
|
||||||
signIn.append_attribute("method") = loginMethod.c_str();
|
signIn.append_attribute("method") = loginMethod.c_str();
|
||||||
else
|
else
|
||||||
signIn.append_attribute("method") = "AdobeID";
|
signIn.append_attribute("method") = "AdobeID";
|
||||||
@@ -949,7 +956,8 @@ namespace gourou
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
ByteArray zipData;
|
ByteArray zipData;
|
||||||
bool removeEncryptionXML = true;
|
bool removeEncryptionXML = true;
|
||||||
@@ -962,7 +970,16 @@ namespace gourou
|
|||||||
std::string encryptedKey = extractTextElem(rightsDoc, "/adept:rights/licenseToken/encryptedKey");
|
std::string encryptedKey = extractTextElem(rightsDoc, "/adept:rights/licenseToken/encryptedKey");
|
||||||
unsigned char decryptedKey[RSA_KEY_SIZE];
|
unsigned char decryptedKey[RSA_KEY_SIZE];
|
||||||
|
|
||||||
|
if (!encryptionKey)
|
||||||
decryptADEPTKey(encryptedKey, decryptedKey);
|
decryptADEPTKey(encryptedKey, decryptedKey);
|
||||||
|
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;
|
||||||
@@ -1001,7 +1018,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+RSA_KEY_SIZE-16, 16, /* Key */
|
decryptedKey+sizeof(decryptedKey)-16, 16, /* Key */
|
||||||
_data, 16, /* IV */
|
_data, 16, /* IV */
|
||||||
&_data[16], zipData.length()-16,
|
&_data[16], zipData.length()-16,
|
||||||
_clearData, &dataOutLength);
|
_clearData, &dataOutLength);
|
||||||
@@ -1068,7 +1085,8 @@ namespace gourou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DRMProcessor::removePDFDRM(const std::string& filenameIn, const std::string& filenameOut)
|
void DRMProcessor::removePDFDRM(const std::string& filenameIn, const std::string& filenameOut,
|
||||||
|
const unsigned char* encryptionKey, unsigned encryptionKeySize)
|
||||||
{
|
{
|
||||||
uPDFParser::Parser parser;
|
uPDFParser::Parser parser;
|
||||||
bool EBXHandlerFound = false;
|
bool EBXHandlerFound = false;
|
||||||
@@ -1091,16 +1109,18 @@ namespace gourou
|
|||||||
|
|
||||||
uPDFParser::Integer* ebxVersion;
|
uPDFParser::Integer* ebxVersion;
|
||||||
std::vector<uPDFParser::Object*> objects = parser.objects();
|
std::vector<uPDFParser::Object*> objects = parser.objects();
|
||||||
std::vector<uPDFParser::Object*>::reverse_iterator it;
|
std::vector<uPDFParser::Object*>::iterator it;
|
||||||
|
std::vector<uPDFParser::Object*>::reverse_iterator rIt;
|
||||||
unsigned char decryptedKey[RSA_KEY_SIZE];
|
unsigned char decryptedKey[RSA_KEY_SIZE];
|
||||||
|
int ebxId;
|
||||||
|
|
||||||
for(it = objects.rbegin(); it != objects.rend(); it++)
|
for(rIt = objects.rbegin(); rIt != objects.rend(); rIt++)
|
||||||
{
|
{
|
||||||
// Update EBX_HANDLER with rights
|
// Update EBX_HANDLER with rights
|
||||||
if ((*it)->hasKey("Filter") && (**it)["Filter"]->str() == "/EBX_HANDLER")
|
if ((*rIt)->hasKey("Filter") && (**rIt)["Filter"]->str() == "/EBX_HANDLER")
|
||||||
{
|
{
|
||||||
EBXHandlerFound = true;
|
EBXHandlerFound = true;
|
||||||
uPDFParser::Object* ebx = *it;
|
uPDFParser::Object* ebx = *rIt;
|
||||||
|
|
||||||
ebxVersion = (uPDFParser::Integer*)(*ebx)["V"];
|
ebxVersion = (uPDFParser::Integer*)(*ebx)["V"];
|
||||||
if (ebxVersion->value() != 4)
|
if (ebxVersion->value() != 4)
|
||||||
@@ -1115,7 +1135,15 @@ namespace gourou
|
|||||||
|
|
||||||
uPDFParser::String* licenseObject = (uPDFParser::String*)(*ebx)["ADEPT_LICENSE"];
|
uPDFParser::String* licenseObject = (uPDFParser::String*)(*ebx)["ADEPT_LICENSE"];
|
||||||
|
|
||||||
ByteArray zippedData = ByteArray::fromBase64(licenseObject->value());
|
std::string value = licenseObject->value();
|
||||||
|
// Pad with '='
|
||||||
|
while ((value.size() % 4))
|
||||||
|
value += "=";
|
||||||
|
ByteArray zippedData = ByteArray::fromBase64(value);
|
||||||
|
|
||||||
|
if (zippedData.size() == 0)
|
||||||
|
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Invalid ADEPT_LICENSE");
|
||||||
|
|
||||||
ByteArray rightsStr;
|
ByteArray rightsStr;
|
||||||
client->inflate(zippedData, rightsStr);
|
client->inflate(zippedData, rightsStr);
|
||||||
|
|
||||||
@@ -1124,7 +1152,19 @@ namespace gourou
|
|||||||
|
|
||||||
std::string encryptedKey = extractTextElem(rightsDoc, "/adept:rights/licenseToken/encryptedKey");
|
std::string encryptedKey = extractTextElem(rightsDoc, "/adept:rights/licenseToken/encryptedKey");
|
||||||
|
|
||||||
|
if (!encryptionKey)
|
||||||
decryptADEPTKey(encryptedKey, decryptedKey);
|
decryptADEPTKey(encryptedKey, decryptedKey);
|
||||||
|
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();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1134,28 +1174,29 @@ namespace gourou
|
|||||||
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "EBX_HANDLER not found");
|
EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "EBX_HANDLER not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uPDFParser::XRefValue> xrefTable = parser.xrefTable();
|
for(it = objects.begin(); it != objects.end(); it++)
|
||||||
std::vector<uPDFParser::XRefValue>::iterator xrefIt;
|
|
||||||
|
|
||||||
for(xrefIt = xrefTable.begin(); xrefIt != xrefTable.end(); xrefIt++)
|
|
||||||
{
|
{
|
||||||
GOUROU_LOG(DEBUG, "XREF obj " << (*xrefIt).objectId() << " used " << (*xrefIt).used());
|
uPDFParser::Object* object = *it;
|
||||||
|
|
||||||
if (!(*xrefIt).used())
|
if (object->objectId() == ebxId)
|
||||||
continue;
|
|
||||||
|
|
||||||
uPDFParser::Object* object = (*xrefIt).object();
|
|
||||||
|
|
||||||
if (!object)
|
|
||||||
{
|
{
|
||||||
GOUROU_LOG(DEBUG, "No object");
|
// object->deleteKey("Filter");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Should not decrypt XRef stream
|
||||||
|
if (object->hasKey("Type") && (*object)["Type"]->str() == "/XRef")
|
||||||
|
{
|
||||||
|
GOUROU_LOG(DEBUG, "XRef stream at " << object->offset());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
GOUROU_LOG(DEBUG, "Obj " << object->objectId());
|
||||||
|
|
||||||
unsigned char tmpKey[16];
|
unsigned char tmpKey[16];
|
||||||
|
|
||||||
generatePDFObjectKey(ebxVersion->value(),
|
generatePDFObjectKey(ebxVersion->value(),
|
||||||
decryptedKey+RSA_KEY_SIZE-16, 16,
|
decryptedKey+sizeof(decryptedKey)-16, 16,
|
||||||
object->objectId(), object->generationNumber(),
|
object->objectId(), object->generationNumber(),
|
||||||
tmpKey);
|
tmpKey);
|
||||||
|
|
||||||
@@ -1197,7 +1238,7 @@ namespace gourou
|
|||||||
dictionary.replace(dictIt->first, dictIt->second);
|
dictionary.replace(dictIt->first, dictIt->second);
|
||||||
|
|
||||||
std::vector<uPDFParser::DataType*>::iterator datasIt;
|
std::vector<uPDFParser::DataType*>::iterator datasIt;
|
||||||
std::vector<uPDFParser::DataType*>& datas = (*xrefIt).object()->data();
|
std::vector<uPDFParser::DataType*>& datas = object->data();
|
||||||
uPDFParser::Stream* stream;
|
uPDFParser::Stream* stream;
|
||||||
|
|
||||||
for (datasIt = datas.begin(); datasIt != datas.end(); datasIt++)
|
for (datasIt = datas.begin(); datasIt != datas.end(); datasIt++)
|
||||||
@@ -1220,6 +1261,8 @@ namespace gourou
|
|||||||
clearData, &dataOutLength);
|
clearData, &dataOutLength);
|
||||||
|
|
||||||
stream->setData(clearData, dataOutLength, true);
|
stream->setData(clearData, dataOutLength, true);
|
||||||
|
if (dataOutLength != dataLength)
|
||||||
|
GOUROU_LOG(DEBUG, "New size " << dataOutLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1230,11 +1273,11 @@ namespace gourou
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DRMProcessor::removeDRM(const std::string& filenameIn, const std::string& filenameOut,
|
void DRMProcessor::removeDRM(const std::string& filenameIn, const std::string& filenameOut,
|
||||||
ITEM_TYPE type)
|
ITEM_TYPE type, const unsigned char* encryptionKey, unsigned encryptionKeySize)
|
||||||
{
|
{
|
||||||
if (type == PDF)
|
if (type == PDF)
|
||||||
removePDFDRM(filenameIn, filenameOut);
|
removePDFDRM(filenameIn, filenameOut, encryptionKey, encryptionKeySize);
|
||||||
else
|
else
|
||||||
removeEPubDRM(filenameIn, filenameOut);
|
removeEPubDRM(filenameIn, filenameOut, encryptionKey, encryptionKeySize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
|
||||||
TARGETS=acsmdownloader adept_activate adept_remove
|
TARGETS=acsmdownloader adept_activate adept_remove
|
||||||
|
|
||||||
CXXFLAGS=-Wall `pkg-config --cflags Qt5Core Qt5Network` -fPIC -I$(ROOT)/include -I$(ROOT)/lib/pugixml/src/
|
CXXFLAGS=-Wall -fPIC -I$(ROOT)/include -I$(ROOT)/lib/pugixml/src/
|
||||||
|
|
||||||
STATIC_DEP=
|
STATIC_DEP=
|
||||||
LDFLAGS=`pkg-config --libs Qt5Core Qt5Network` -L$(ROOT) -lcrypto -lzip -lz
|
LDFLAGS=-L$(ROOT) -lcrypto -lzip -lz -lcurl
|
||||||
|
|
||||||
ifneq ($(STATIC_UTILS),)
|
ifneq ($(STATIC_UTILS),)
|
||||||
STATIC_DEP = $(ROOT)/libgourou.a
|
STATIC_DEP = $(ROOT)/libgourou.a
|
||||||
@@ -18,7 +18,7 @@ else
|
|||||||
CXXFLAGS += -O2
|
CXXFLAGS += -O2
|
||||||
endif
|
endif
|
||||||
|
|
||||||
COMMON_DEPS = drmprocessorclientimpl.cpp $(STATIC_DEP)
|
COMMON_DEPS = drmprocessorclientimpl.cpp utils_common.cpp $(STATIC_DEP)
|
||||||
|
|
||||||
all: $(TARGETS)
|
all: $(TARGETS)
|
||||||
|
|
||||||
|
|||||||
@@ -26,21 +26,14 @@
|
|||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <getopt.h>
|
||||||
#include <getopt.h>
|
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <algorithm>
|
||||||
#include <QFile>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QCoreApplication>
|
|
||||||
#include <QRunnable>
|
|
||||||
#include <QThreadPool>
|
|
||||||
|
|
||||||
#include <libgourou.h>
|
#include <libgourou.h>
|
||||||
#include "drmprocessorclientimpl.h"
|
#include "drmprocessorclientimpl.h"
|
||||||
|
#include "utils_common.h"
|
||||||
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[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";
|
||||||
@@ -49,23 +42,13 @@ static const char* acsmFile = 0;
|
|||||||
static bool exportPrivateKey = false;
|
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 const char* defaultDirs[] = {
|
|
||||||
".adept/",
|
|
||||||
"./adobe-digital-editions/",
|
|
||||||
"./.adobe-digital-editions/"
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class ACSMDownloader: public QRunnable
|
class ACSMDownloader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ACSMDownloader(QCoreApplication* app):
|
|
||||||
app(app)
|
|
||||||
{
|
|
||||||
setAutoDelete(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void run()
|
int run()
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
try
|
try
|
||||||
@@ -84,9 +67,8 @@ public:
|
|||||||
|
|
||||||
if (outputDir)
|
if (outputDir)
|
||||||
{
|
{
|
||||||
QDir dir(outputDir);
|
if (!fileExists(outputDir))
|
||||||
if (!dir.exists(outputDir))
|
mkpath(outputDir);
|
||||||
dir.mkpath(outputDir);
|
|
||||||
|
|
||||||
filename = std::string(outputDir) + "/" + filename;
|
filename = std::string(outputDir) + "/" + filename;
|
||||||
}
|
}
|
||||||
@@ -116,9 +98,8 @@ public:
|
|||||||
|
|
||||||
if (outputDir)
|
if (outputDir)
|
||||||
{
|
{
|
||||||
QDir dir(outputDir);
|
if (!fileExists(outputDir))
|
||||||
if (!dir.exists(outputDir))
|
mkpath(outputDir);
|
||||||
dir.mkpath(outputDir);
|
|
||||||
|
|
||||||
filename = std::string(outputDir) + "/" + filename;
|
filename = std::string(outputDir) + "/" + filename;
|
||||||
}
|
}
|
||||||
@@ -132,8 +113,7 @@ public:
|
|||||||
finalName += ".pdf";
|
finalName += ".pdf";
|
||||||
else
|
else
|
||||||
finalName += ".epub";
|
finalName += ".epub";
|
||||||
QDir dir;
|
rename(filename.c_str(), finalName.c_str());
|
||||||
dir.rename(filename.c_str(), finalName.c_str());
|
|
||||||
filename = finalName;
|
filename = finalName;
|
||||||
}
|
}
|
||||||
std::cout << "Created " << filename << std::endl;
|
std::cout << "Created " << filename << std::endl;
|
||||||
@@ -144,37 +124,10 @@ public:
|
|||||||
ret = 1;
|
ret = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->app->exit(ret);
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
QCoreApplication* app;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char* findFile(const char* filename, bool inDefaultDirs=true)
|
|
||||||
{
|
|
||||||
QFile file(filename);
|
|
||||||
|
|
||||||
if (file.exists())
|
|
||||||
return strdup(filename);
|
|
||||||
|
|
||||||
if (!inDefaultDirs) return 0;
|
|
||||||
|
|
||||||
for (int i=0; i<(int)ARRAY_SIZE(defaultDirs); i++)
|
|
||||||
{
|
|
||||||
QString path = QString(defaultDirs[i]) + QString(filename);
|
|
||||||
file.setFileName(path);
|
|
||||||
if (file.exists())
|
|
||||||
return strdup(path.toStdString().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void version(void)
|
|
||||||
{
|
|
||||||
std::cout << "Current libgourou version : " << gourou::DRMProcessor::VERSION << std::endl ;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void usage(const char* cmd)
|
static void usage(const char* cmd)
|
||||||
{
|
{
|
||||||
@@ -275,8 +228,7 @@ int main(int argc, char** argv)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
QCoreApplication app(argc, argv);
|
ACSMDownloader downloader;
|
||||||
ACSMDownloader downloader(&app);
|
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
bool hasErrors = false;
|
bool hasErrors = false;
|
||||||
@@ -306,8 +258,7 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
QFile file(acsmFile);
|
if (!fileExists(acsmFile))
|
||||||
if (!file.exists())
|
|
||||||
{
|
{
|
||||||
std::cout << "Error : " << acsmFile << " doesn't exists" << std::endl;
|
std::cout << "Error : " << acsmFile << " doesn't exists" << std::endl;
|
||||||
ret = -1;
|
ret = -1;
|
||||||
@@ -315,9 +266,7 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QThreadPool::globalInstance()->start(&downloader);
|
ret = downloader.run();
|
||||||
|
|
||||||
ret = app.exec();
|
|
||||||
|
|
||||||
end:
|
end:
|
||||||
for (i=0; i<(int)ARRAY_SIZE(files); i++)
|
for (i=0; i<(int)ARRAY_SIZE(files); i++)
|
||||||
|
|||||||
@@ -28,22 +28,16 @@
|
|||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
|
||||||
#include <QFile>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QCoreApplication>
|
|
||||||
#include <QRunnable>
|
|
||||||
#include <QThreadPool>
|
|
||||||
|
|
||||||
#include <libgourou.h>
|
#include <libgourou.h>
|
||||||
#include "drmprocessorclientimpl.h"
|
#include "drmprocessorclientimpl.h"
|
||||||
|
#include "utils_common.h"
|
||||||
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
|
|
||||||
|
|
||||||
static const char* username = 0;
|
static const char* username = 0;
|
||||||
static const char* password = 0;
|
static const char* password = 0;
|
||||||
@@ -100,16 +94,11 @@ static std::string getpass(const char *prompt, bool show_asterisk=false)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class ADEPTActivate: public QRunnable
|
class ADEPTActivate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ADEPTActivate(QCoreApplication* app):
|
|
||||||
app(app)
|
|
||||||
{
|
|
||||||
setAutoDelete(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void run()
|
int run()
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
try
|
try
|
||||||
@@ -128,17 +117,10 @@ public:
|
|||||||
ret = 1;
|
ret = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->app->exit(ret);
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
QCoreApplication* app;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void version(void)
|
|
||||||
{
|
|
||||||
std::cout << "Current libgourou version : " << gourou::DRMProcessor::VERSION << std::endl ;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void usage(const char* cmd)
|
static void usage(const char* cmd)
|
||||||
{
|
{
|
||||||
@@ -162,8 +144,8 @@ static void usage(const char* cmd)
|
|||||||
static const char* abspath(const char* filename)
|
static const char* abspath(const char* filename)
|
||||||
{
|
{
|
||||||
const char* root = getcwd(0, PATH_MAX);
|
const char* root = getcwd(0, PATH_MAX);
|
||||||
QString fullPath = QString(root) + QString("/") + QString(filename);
|
std::string fullPath = std::string(root) + std::string("/") + filename;
|
||||||
const char* res = strdup(fullPath.toStdString().c_str());
|
const char* res = strdup(fullPath.c_str());
|
||||||
|
|
||||||
free((void*)root);
|
free((void*)root);
|
||||||
|
|
||||||
@@ -255,9 +237,8 @@ int main(int argc, char** argv)
|
|||||||
// Relative path
|
// Relative path
|
||||||
if (_outputDir[0] == '.' || _outputDir[0] != '/')
|
if (_outputDir[0] == '.' || _outputDir[0] != '/')
|
||||||
{
|
{
|
||||||
QFile file(_outputDir);
|
|
||||||
// realpath doesn't works if file/dir doesn't exists
|
// realpath doesn't works if file/dir doesn't exists
|
||||||
if (file.exists())
|
if (fileExists(_outputDir))
|
||||||
outputDir = strdup(realpath(_outputDir, 0));
|
outputDir = strdup(realpath(_outputDir, 0));
|
||||||
else
|
else
|
||||||
outputDir = strdup(abspath(_outputDir));
|
outputDir = strdup(abspath(_outputDir));
|
||||||
@@ -266,10 +247,8 @@ int main(int argc, char** argv)
|
|||||||
outputDir = strdup(_outputDir);
|
outputDir = strdup(_outputDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
QCoreApplication app(argc, argv);
|
std::string pass;
|
||||||
|
if (fileExists(outputDir))
|
||||||
QFile file(outputDir);
|
|
||||||
if (file.exists())
|
|
||||||
{
|
{
|
||||||
int key;
|
int key;
|
||||||
|
|
||||||
@@ -289,7 +268,6 @@ int main(int argc, char** argv)
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string pass;
|
|
||||||
if (!password)
|
if (!password)
|
||||||
{
|
{
|
||||||
char prompt[128];
|
char prompt[128];
|
||||||
@@ -298,13 +276,11 @@ int main(int argc, char** argv)
|
|||||||
password = pass.c_str();
|
password = pass.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
ADEPTActivate activate(&app);
|
ADEPTActivate activate;
|
||||||
QThreadPool::globalInstance()->start(&activate);
|
|
||||||
|
|
||||||
ret = app.exec();
|
ret = activate.run();
|
||||||
|
|
||||||
end:
|
end:
|
||||||
|
|
||||||
free((void*)outputDir);
|
free((void*)outputDir);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,23 +26,15 @@
|
|||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <QFile>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QCoreApplication>
|
|
||||||
#include <QRunnable>
|
|
||||||
#include <QThreadPool>
|
|
||||||
#include <QTemporaryFile>
|
|
||||||
|
|
||||||
#include <libgourou.h>
|
#include <libgourou.h>
|
||||||
#include <libgourou_common.h>
|
#include <libgourou_common.h>
|
||||||
#include "drmprocessorclientimpl.h"
|
|
||||||
|
|
||||||
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
|
#include "drmprocessorclientimpl.h"
|
||||||
|
#include "utils_common.h"
|
||||||
|
|
||||||
static const char* deviceFile = "device.xml";
|
static const char* deviceFile = "device.xml";
|
||||||
static const char* activationFile = "activation.xml";
|
static const char* activationFile = "activation.xml";
|
||||||
@@ -50,27 +42,35 @@ static const char* devicekeyFile = "devicesalt";
|
|||||||
static const char* inputFile = 0;
|
static const char* inputFile = 0;
|
||||||
static const char* outputFile = 0;
|
static const char* outputFile = 0;
|
||||||
static const char* outputDir = 0;
|
static const char* outputDir = 0;
|
||||||
static const char* defaultDirs[] = {
|
|
||||||
".adept/",
|
static char* encryptionKeyUser = 0;
|
||||||
"./adobe-digital-editions/",
|
static unsigned char* encryptionKey = 0;
|
||||||
"./.adobe-digital-editions/"
|
static unsigned encryptionKeySize = 0;
|
||||||
};
|
|
||||||
|
static inline unsigned char htoi(unsigned char c)
|
||||||
|
{
|
||||||
|
if (c >= '0' && c <= '9')
|
||||||
|
c -= '0';
|
||||||
|
else if (c >= 'a' && c <= 'f')
|
||||||
|
c -= 'a' - 10;
|
||||||
|
else if (c >= 'A' && c <= 'F')
|
||||||
|
c -= 'A' - 10;
|
||||||
|
else
|
||||||
|
EXCEPTION(gourou::USER_INVALID_INPUT, "Invalid character " << c << " in encryption key");
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool endsWith(const std::string& s, const std::string& suffix)
|
static inline bool endsWith(const std::string& s, const std::string& suffix)
|
||||||
{
|
{
|
||||||
return s.rfind(suffix) == std::abs((int)(s.size()-suffix.size()));
|
return s.rfind(suffix) == std::abs((int)(s.size()-suffix.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
class ADEPTRemove: public QRunnable
|
class ADEPTRemove
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ADEPTRemove(QCoreApplication* app):
|
|
||||||
app(app)
|
|
||||||
{
|
|
||||||
setAutoDelete(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void run()
|
int run()
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
try
|
try
|
||||||
@@ -87,9 +87,8 @@ public:
|
|||||||
|
|
||||||
if (outputDir)
|
if (outputDir)
|
||||||
{
|
{
|
||||||
QDir dir(outputDir);
|
if (!fileExists(outputDir))
|
||||||
if (!dir.exists(outputDir))
|
mkpath(outputDir);
|
||||||
dir.mkpath(outputDir);
|
|
||||||
|
|
||||||
filename = std::string(outputDir) + "/" + filename;
|
filename = std::string(outputDir) + "/" + filename;
|
||||||
}
|
}
|
||||||
@@ -105,12 +104,9 @@ public:
|
|||||||
|
|
||||||
if (inputFile != filename)
|
if (inputFile != filename)
|
||||||
{
|
{
|
||||||
QFile::remove(filename.c_str());
|
unlink(filename.c_str());
|
||||||
if (!QFile::copy(inputFile, filename.c_str()))
|
fileCopy(inputFile, filename.c_str());
|
||||||
{
|
processor.removeDRM(inputFile, filename, type, encryptionKey, encryptionKeySize);
|
||||||
EXCEPTION(gourou::DRM_FILE_ERROR, "Unable to copy " << inputFile << " into " << filename);
|
|
||||||
}
|
|
||||||
processor.removeDRM(inputFile, filename, type);
|
|
||||||
std::cout << "DRM removed into new file " << filename << std::endl;
|
std::cout << "DRM removed into new file " << filename << std::endl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -118,21 +114,19 @@ public:
|
|||||||
// Use temp file for PDF
|
// Use temp file for PDF
|
||||||
if (type == gourou::DRMProcessor::ITEM_TYPE::PDF)
|
if (type == gourou::DRMProcessor::ITEM_TYPE::PDF)
|
||||||
{
|
{
|
||||||
QTemporaryFile tempFile;
|
char* tempFile = tempnam("/tmp", NULL);
|
||||||
tempFile.open();
|
processor.removeDRM(inputFile, tempFile, type, encryptionKey, encryptionKeySize);
|
||||||
tempFile.setAutoRemove(false); // In case of failure
|
|
||||||
processor.removeDRM(inputFile, tempFile.fileName().toStdString(), type);
|
|
||||||
/* Original file must be removed before doing a copy... */
|
/* Original file must be removed before doing a copy... */
|
||||||
QFile origFile(inputFile);
|
unlink(inputFile);
|
||||||
origFile.remove();
|
if (!rename(tempFile, filename.c_str()))
|
||||||
if (!QFile::copy(tempFile.fileName(), filename.c_str()))
|
|
||||||
{
|
{
|
||||||
EXCEPTION(gourou::DRM_FILE_ERROR, "Unable to copy " << tempFile.fileName().toStdString() << " into " << filename);
|
free(tempFile);
|
||||||
|
EXCEPTION(gourou::DRM_FILE_ERROR, "Unable to copy " << tempFile << " into " << filename);
|
||||||
}
|
}
|
||||||
tempFile.setAutoRemove(true);
|
free(tempFile);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
processor.removeDRM(inputFile, filename, type);
|
processor.removeDRM(inputFile, filename, type, encryptionKey, encryptionKeySize);
|
||||||
std::cout << "DRM removed from " << filename << std::endl;
|
std::cout << "DRM removed from " << filename << std::endl;
|
||||||
}
|
}
|
||||||
} catch(std::exception& e)
|
} catch(std::exception& e)
|
||||||
@@ -141,38 +135,10 @@ public:
|
|||||||
ret = 1;
|
ret = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->app->exit(ret);
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
QCoreApplication* app;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char* findFile(const char* filename, bool inDefaultDirs=true)
|
|
||||||
{
|
|
||||||
QFile file(filename);
|
|
||||||
|
|
||||||
if (file.exists())
|
|
||||||
return strdup(filename);
|
|
||||||
|
|
||||||
if (!inDefaultDirs) return 0;
|
|
||||||
|
|
||||||
for (int i=0; i<(int)ARRAY_SIZE(defaultDirs); i++)
|
|
||||||
{
|
|
||||||
QString path = QString(defaultDirs[i]) + QString(filename);
|
|
||||||
file.setFileName(path);
|
|
||||||
if (file.exists())
|
|
||||||
return strdup(path.toStdString().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void version(void)
|
|
||||||
{
|
|
||||||
std::cout << "Current libgourou version : " << gourou::DRMProcessor::VERSION << std::endl ;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 << "Remove ADEPT DRM (from Adobe) of EPUB/PDF file" << std::endl;
|
||||||
@@ -213,14 +179,14 @@ int main(int argc, char** argv)
|
|||||||
{"output-dir", required_argument, 0, 'O' },
|
{"output-dir", required_argument, 0, 'O' },
|
||||||
{"output-file", required_argument, 0, 'o' },
|
{"output-file", required_argument, 0, 'o' },
|
||||||
{"input-file", required_argument, 0, 'f' },
|
{"input-file", required_argument, 0, 'f' },
|
||||||
{"export-private-key",no_argument, 0, 'e' },
|
{"encryption-key", required_argument, 0, 'K' }, // Private option
|
||||||
{"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:evVh",
|
c = getopt_long(argc, argv, "d:a:k:O:o:f:K:vVh",
|
||||||
long_options, &option_index);
|
long_options, &option_index);
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
@@ -244,6 +210,9 @@ int main(int argc, char** argv)
|
|||||||
case 'o':
|
case 'o':
|
||||||
outputFile = optarg;
|
outputFile = optarg;
|
||||||
break;
|
break;
|
||||||
|
case 'K':
|
||||||
|
encryptionKeyUser = optarg;
|
||||||
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
verbose++;
|
verbose++;
|
||||||
break;
|
break;
|
||||||
@@ -268,8 +237,7 @@ int main(int argc, char** argv)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
QCoreApplication app(argc, argv);
|
ADEPTRemove remover;
|
||||||
ADEPTRemove remover(&app);
|
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
bool hasErrors = false;
|
bool hasErrors = false;
|
||||||
@@ -286,12 +254,36 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (encryptionKeyUser)
|
||||||
|
{
|
||||||
|
int size = std::string(encryptionKeyUser).size();
|
||||||
|
if ((size % 2))
|
||||||
|
{
|
||||||
|
std::cout << "Error : Encryption key must be odd length" << std::endl;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encryptionKeyUser[0] == '0' && encryptionKeyUser[1] == 'x')
|
||||||
|
{
|
||||||
|
encryptionKeyUser += 2;
|
||||||
|
size -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptionKey = new unsigned char[size/2];
|
||||||
|
|
||||||
|
for(i=0; i<size; i+=2)
|
||||||
|
{
|
||||||
|
encryptionKey[i/2] = htoi(encryptionKeyUser[i]) << 4;
|
||||||
|
encryptionKey[i/2] |= htoi(encryptionKeyUser[i+1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptionKeySize = size/2;
|
||||||
|
}
|
||||||
|
|
||||||
if (hasErrors)
|
if (hasErrors)
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
QThreadPool::globalInstance()->start(&remover);
|
ret = remover.run();
|
||||||
|
|
||||||
ret = app.exec();
|
|
||||||
|
|
||||||
end:
|
end:
|
||||||
for (i=0; i<(int)ARRAY_SIZE(files); i++)
|
for (i=0; i<(int)ARRAY_SIZE(files); i++)
|
||||||
@@ -300,5 +292,8 @@ end:
|
|||||||
free((void*)*files[i]);
|
free((void*)*files[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (encryptionKey)
|
||||||
|
free(encryptionKey);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,17 +25,18 @@
|
|||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
#include <bytearray.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
#include <openssl/rand.h>
|
#include <openssl/rand.h>
|
||||||
#include <openssl/pkcs12.h>
|
#include <openssl/pkcs12.h>
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <curl/curl.h>
|
||||||
#include <QNetworkReply>
|
|
||||||
#include <QNetworkRequest>
|
|
||||||
#include <QNetworkAccessManager>
|
|
||||||
#include <QFile>
|
|
||||||
|
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#include <zip.h>
|
#include <zip.h>
|
||||||
@@ -44,6 +45,27 @@
|
|||||||
#include <libgourou_log.h>
|
#include <libgourou_log.h>
|
||||||
#include "drmprocessorclientimpl.h"
|
#include "drmprocessorclientimpl.h"
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/216823/how-to-trim-a-stdstring
|
||||||
|
// trim from start (in place)
|
||||||
|
static inline void ltrim(std::string &s) {
|
||||||
|
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
|
||||||
|
return !std::isspace(ch);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim from end (in place)
|
||||||
|
static inline void rtrim(std::string &s) {
|
||||||
|
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
|
||||||
|
return !std::isspace(ch);
|
||||||
|
}).base(), s.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// trim from both ends (in place)
|
||||||
|
static inline void trim(std::string &s) {
|
||||||
|
ltrim(s);
|
||||||
|
rtrim(s);
|
||||||
|
}
|
||||||
|
|
||||||
/* Digest interface */
|
/* Digest interface */
|
||||||
void* DRMProcessorClientImpl::createDigest(const std::string& digestName)
|
void* DRMProcessorClientImpl::createDigest(const std::string& digestName)
|
||||||
{
|
{
|
||||||
@@ -88,25 +110,78 @@ void DRMProcessorClientImpl::randBytes(unsigned char* bytesOut, unsigned int len
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* HTTP interface */
|
/* HTTP interface */
|
||||||
|
#define HTTP_REQ_MAX_RETRY 5
|
||||||
#define DISPLAY_THRESHOLD 10*1024 // Threshold to display download progression
|
#define DISPLAY_THRESHOLD 10*1024 // Threshold to display download progression
|
||||||
|
static unsigned downloadedBytes;
|
||||||
|
|
||||||
static void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
|
static int downloadProgress(void *clientp, curl_off_t dltotal, curl_off_t dlnow,
|
||||||
// For "big" files only
|
curl_off_t ultotal, curl_off_t ulnow)
|
||||||
if (bytesTotal >= DISPLAY_THRESHOLD && gourou::logLevel >= gourou::WARN)
|
{
|
||||||
|
// For "big" files only
|
||||||
|
if (dltotal >= DISPLAY_THRESHOLD && gourou::logLevel >= gourou::WARN)
|
||||||
{
|
{
|
||||||
int percent = 0;
|
int percent = 0;
|
||||||
if (bytesTotal)
|
if (dltotal)
|
||||||
percent = (bytesReceived * 100) / bytesTotal;
|
percent = (dlnow * 100) / dltotal;
|
||||||
|
|
||||||
std::cout << "\rDownload " << percent << "%" << std::flush;
|
std::cout << "\rDownload " << percent << "%" << std::flush;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, const std::string& POSTData, const std::string& contentType, std::map<std::string, std::string>* responseHeaders)
|
static size_t curlRead(void *data, size_t size, size_t nmemb, void *userp)
|
||||||
{
|
{
|
||||||
QNetworkRequest request(QUrl(URL.c_str()));
|
gourou::ByteArray* replyData = (gourou::ByteArray*) userp;
|
||||||
QNetworkAccessManager networkManager;
|
|
||||||
QByteArray replyData;
|
replyData->append((unsigned char*)data, size*nmemb);
|
||||||
|
|
||||||
|
return size*nmemb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t curlReadFd(void *data, size_t size, size_t nmemb, void *userp)
|
||||||
|
{
|
||||||
|
int fd = *(int*) userp;
|
||||||
|
|
||||||
|
size_t res = write(fd, data, size*nmemb);
|
||||||
|
|
||||||
|
downloadedBytes += res;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t curlHeaders(char *buffer, size_t size, size_t nitems, void *userdata)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string>* responseHeaders = (std::map<std::string, std::string>*)userdata;
|
||||||
|
std::string::size_type pos = 0;
|
||||||
|
std::string buf(buffer, size*nitems);
|
||||||
|
|
||||||
|
pos = buf.find(":", pos);
|
||||||
|
|
||||||
|
if (pos != std::string::npos)
|
||||||
|
{
|
||||||
|
std::string key = std::string(buffer, pos);
|
||||||
|
std::string value = std::string(&buffer[pos+1], (size*nitems)-(pos+1));
|
||||||
|
|
||||||
|
trim(key);
|
||||||
|
trim(value);
|
||||||
|
|
||||||
|
(*responseHeaders)[key] = value;
|
||||||
|
|
||||||
|
if (gourou::logLevel >= gourou::DEBUG)
|
||||||
|
std::cout << key << " : " << value << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size*nitems;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, const std::string& POSTData, const std::string& contentType, std::map<std::string, std::string>* responseHeaders, int fd)
|
||||||
|
{
|
||||||
|
gourou::ByteArray replyData;
|
||||||
|
std::map<std::string, std::string> localHeaders;
|
||||||
|
|
||||||
|
if (!responseHeaders)
|
||||||
|
responseHeaders = &localHeaders;
|
||||||
|
|
||||||
GOUROU_LOG(gourou::INFO, "Send request to " << URL);
|
GOUROU_LOG(gourou::INFO, "Send request to " << URL);
|
||||||
if (POSTData.size())
|
if (POSTData.size())
|
||||||
@@ -114,54 +189,99 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons
|
|||||||
GOUROU_LOG(gourou::DEBUG, "<<< " << std::endl << POSTData);
|
GOUROU_LOG(gourou::DEBUG, "<<< " << std::endl << POSTData);
|
||||||
}
|
}
|
||||||
|
|
||||||
request.setRawHeader("Accept", "*/*");
|
unsigned prevDownloadedBytes;
|
||||||
request.setRawHeader("User-Agent", "book2png");
|
downloadedBytes = 0;
|
||||||
if (contentType.size())
|
CURL *curl = curl_easy_init();
|
||||||
request.setRawHeader("Content-Type", contentType.c_str());
|
CURLcode res;
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, URL.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, "book2png");
|
||||||
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
|
||||||
|
|
||||||
QNetworkReply* reply;
|
|
||||||
|
struct curl_slist *list = NULL;
|
||||||
|
list = curl_slist_append(list, "Accept: */*");
|
||||||
|
std::string _contentType;
|
||||||
|
if (contentType.size())
|
||||||
|
{
|
||||||
|
_contentType = "Content-Type: " + contentType;
|
||||||
|
list = curl_slist_append(list, _contentType.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
|
||||||
|
|
||||||
if (POSTData.size())
|
if (POSTData.size())
|
||||||
reply = networkManager.post(request, POSTData.c_str());
|
|
||||||
else
|
|
||||||
reply = networkManager.get(request);
|
|
||||||
|
|
||||||
QEventLoop loop;
|
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
|
||||||
// Handled just below
|
|
||||||
QObject::connect(reply, &QNetworkReply::errorOccurred, &loop, &QEventLoop::quit);
|
|
||||||
QObject::connect(reply, &QNetworkReply::downloadProgress, &loop, downloadProgress);
|
|
||||||
|
|
||||||
loop.exec();
|
|
||||||
|
|
||||||
QByteArray location = reply->rawHeader("Location");
|
|
||||||
if (location.size() != 0)
|
|
||||||
{
|
{
|
||||||
GOUROU_LOG(gourou::DEBUG, "New location");
|
curl_easy_setopt(curl, CURLOPT_POST, 1L);
|
||||||
return sendHTTPRequest(location.constData(), POSTData, contentType, responseHeaders);
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, POSTData.size());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, POSTData.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reply->error() != QNetworkReply::NoError)
|
if (fd)
|
||||||
EXCEPTION(gourou::CLIENT_NETWORK_ERROR, "Error " << reply->errorString().toStdString());
|
{
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlReadFd);
|
||||||
QList<QByteArray> headers = reply->rawHeaderList();
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&fd);
|
||||||
for (int i = 0; i < headers.size(); ++i) {
|
}
|
||||||
if (gourou::logLevel >= gourou::DEBUG)
|
else
|
||||||
std::cout << headers[i].constData() << " : " << reply->rawHeader(headers[i]).constData() << std::endl;
|
{
|
||||||
if (responseHeaders)
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlRead);
|
||||||
(*responseHeaders)[headers[i].constData()] = reply->rawHeader(headers[i]).constData();
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&replyData);
|
||||||
}
|
}
|
||||||
|
|
||||||
replyData = reply->readAll();
|
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curlHeaders);
|
||||||
if (replyData.size() >= DISPLAY_THRESHOLD && gourou::logLevel >= gourou::WARN)
|
curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void*)responseHeaders);
|
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, downloadProgress);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
|
||||||
|
|
||||||
|
for (int i=0; i<HTTP_REQ_MAX_RETRY; i++)
|
||||||
|
{
|
||||||
|
prevDownloadedBytes = downloadedBytes;
|
||||||
|
if (downloadedBytes)
|
||||||
|
curl_easy_setopt(curl, CURLOPT_RESUME_FROM, downloadedBytes);
|
||||||
|
|
||||||
|
res = curl_easy_perform(curl);
|
||||||
|
|
||||||
|
// Connexion failed, wait & retry
|
||||||
|
if (res == CURLE_COULDNT_CONNECT)
|
||||||
|
{
|
||||||
|
GOUROU_LOG(gourou::WARN, "Connection failed, attempt " << (i+1) << "/" << HTTP_REQ_MAX_RETRY);
|
||||||
|
}
|
||||||
|
// Transfer failed but some data has been received
|
||||||
|
// --> try again without incrementing tries
|
||||||
|
else if (res == CURLE_RECV_ERROR)
|
||||||
|
{
|
||||||
|
if (prevDownloadedBytes != downloadedBytes)
|
||||||
|
{
|
||||||
|
GOUROU_LOG(gourou::WARN, "Connection broken, but data received, try again");
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
GOUROU_LOG(gourou::WARN, "Connection broken and no data received, attempt " << (i+1) << "/" << HTTP_REQ_MAX_RETRY);
|
||||||
|
}
|
||||||
|
// Other error --> fail
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Wait a little bit (250ms * i)
|
||||||
|
usleep((250 * 1000) * (i+1));
|
||||||
|
}
|
||||||
|
|
||||||
|
curl_slist_free_all(list);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
|
||||||
|
if (res != CURLE_OK)
|
||||||
|
EXCEPTION(gourou::CLIENT_NETWORK_ERROR, "Error " << curl_easy_strerror(res));
|
||||||
|
|
||||||
|
if ((downloadedBytes >= DISPLAY_THRESHOLD || replyData.size() >= DISPLAY_THRESHOLD) &&
|
||||||
|
gourou::logLevel >= gourou::WARN)
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
if (reply->rawHeader("Content-Type") == "application/vnd.adobe.adept+xml")
|
|
||||||
|
if ((*responseHeaders)["Content-Type"] == "application/vnd.adobe.adept+xml")
|
||||||
{
|
{
|
||||||
GOUROU_LOG(gourou::DEBUG, ">>> " << std::endl << replyData.data());
|
GOUROU_LOG(gourou::DEBUG, ">>> " << std::endl << replyData.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::string(replyData.data(), replyData.length());
|
return std::string((char*)replyData.data(), replyData.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DRMProcessorClientImpl::RSAPrivateEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength,
|
void DRMProcessorClientImpl::RSAPrivateEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength,
|
||||||
@@ -532,7 +652,7 @@ void DRMProcessorClientImpl::inflate(gourou::ByteArray& data, gourou::ByteArray&
|
|||||||
{
|
{
|
||||||
// Real error
|
// Real error
|
||||||
if (ret == Z_BUF_ERROR && infstream.avail_out == (uInt)dataSize)
|
if (ret == Z_BUF_ERROR && infstream.avail_out == (uInt)dataSize)
|
||||||
EXCEPTION(gourou::CLIENT_ZIP_ERROR, "Inflate error, code " << zError(ret) << ", msg " << infstream.msg);
|
break;
|
||||||
|
|
||||||
result.append(buffer, dataSize-infstream.avail_out);
|
result.append(buffer, dataSize-infstream.avail_out);
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public:
|
|||||||
virtual void randBytes(unsigned char* bytesOut, unsigned int length);
|
virtual void randBytes(unsigned char* bytesOut, unsigned int length);
|
||||||
|
|
||||||
/* HTTP interface */
|
/* HTTP interface */
|
||||||
virtual std::string sendHTTPRequest(const std::string& URL, const std::string& POSTData=std::string(""), const std::string& contentType=std::string(""), std::map<std::string, std::string>* responseHeaders=0);
|
virtual std::string sendHTTPRequest(const std::string& URL, const std::string& POSTData=std::string(""), const std::string& contentType=std::string(""), std::map<std::string, std::string>* responseHeaders=0, int fd=0);
|
||||||
|
|
||||||
virtual void RSAPrivateEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength,
|
virtual void RSAPrivateEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength,
|
||||||
const RSA_KEY_TYPE keyType, const std::string& password,
|
const RSA_KEY_TYPE keyType, const std::string& password,
|
||||||
|
|||||||
127
utils/utils_common.cpp
Normal file
127
utils/utils_common.cpp
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2022, Grégory Soutadé
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the copyright holder nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||||
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <libgourou.h>
|
||||||
|
#include <libgourou_common.h>
|
||||||
|
#include "utils_common.h"
|
||||||
|
|
||||||
|
static const char* defaultDirs[] = {
|
||||||
|
".adept/",
|
||||||
|
"./adobe-digital-editions/",
|
||||||
|
"./.adobe-digital-editions/"
|
||||||
|
};
|
||||||
|
|
||||||
|
void version(void)
|
||||||
|
{
|
||||||
|
std::cout << "Current libgourou version : " << gourou::DRMProcessor::VERSION << std::endl ;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fileExists(const char* filename)
|
||||||
|
{
|
||||||
|
struct stat _stat;
|
||||||
|
int ret = stat(filename, &_stat);
|
||||||
|
|
||||||
|
return (ret == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* findFile(const char* filename, bool inDefaultDirs)
|
||||||
|
{
|
||||||
|
if (fileExists(filename))
|
||||||
|
return strdup(filename);
|
||||||
|
|
||||||
|
if (!inDefaultDirs) return 0;
|
||||||
|
|
||||||
|
for (int i=0; i<(int)ARRAY_SIZE(defaultDirs); i++)
|
||||||
|
{
|
||||||
|
std::string path = std::string(defaultDirs[i]) + filename;
|
||||||
|
if (fileExists(path.c_str()))
|
||||||
|
return strdup(path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/2336242/recursive-mkdir-system-call-on-unix
|
||||||
|
void mkpath(const char *dir)
|
||||||
|
{
|
||||||
|
char tmp[PATH_MAX];
|
||||||
|
char *p = NULL;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
snprintf(tmp, sizeof(tmp),"%s",dir);
|
||||||
|
len = strlen(tmp);
|
||||||
|
if (tmp[len - 1] == '/')
|
||||||
|
tmp[len - 1] = 0;
|
||||||
|
for (p = tmp + 1; *p; p++)
|
||||||
|
if (*p == '/') {
|
||||||
|
*p = 0;
|
||||||
|
mkdir(tmp, S_IRWXU);
|
||||||
|
*p = '/';
|
||||||
|
}
|
||||||
|
mkdir(tmp, S_IRWXU);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fileCopy(const char* in, const char* out)
|
||||||
|
{
|
||||||
|
char buffer[4096];
|
||||||
|
int ret, fdIn, fdOut;
|
||||||
|
|
||||||
|
fdIn = open(in, O_RDONLY);
|
||||||
|
|
||||||
|
if (!fdIn)
|
||||||
|
EXCEPTION(gourou::CLIENT_FILE_ERROR, "Unable to open " << in);
|
||||||
|
|
||||||
|
fdOut = gourou::createNewFile(out);
|
||||||
|
|
||||||
|
if (!fdOut)
|
||||||
|
{
|
||||||
|
close (fdIn);
|
||||||
|
EXCEPTION(gourou::CLIENT_FILE_ERROR, "Unable to open " << out);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
ret = ::read(fdIn, buffer, sizeof(buffer));
|
||||||
|
if (ret <= 0)
|
||||||
|
break;
|
||||||
|
::write(fdOut, buffer, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
close (fdIn);
|
||||||
|
close (fdOut);
|
||||||
|
}
|
||||||
64
utils/utils_common.h
Normal file
64
utils/utils_common.h
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2022, Grégory Soutadé
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the copyright holder nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||||
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _UTILS_COMMON_H_
|
||||||
|
#define _UTILS_COMMON_H_
|
||||||
|
|
||||||
|
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Display libgourou version
|
||||||
|
*/
|
||||||
|
void version(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find a given filename in current directory and/or in default directories
|
||||||
|
*
|
||||||
|
* @param filename Filename to search
|
||||||
|
* @param inDefaultDirs Search is default directories or not
|
||||||
|
*
|
||||||
|
* @return A copy of full path
|
||||||
|
*/
|
||||||
|
const char* findFile(const char* filename, bool inDefaultDirs=true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Does the file (or directory exists)
|
||||||
|
*/
|
||||||
|
bool fileExists(const char* filename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Recursively created dir
|
||||||
|
*/
|
||||||
|
void mkpath(const char *dir);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copy file in into file out
|
||||||
|
*/
|
||||||
|
void fileCopy(const char* in, const char* out);
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user