From 8f0341d0bdadb7b8281c4dba89ac8175fa9858a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Soutad=C3=A9?= Date: Sun, 18 Jan 2026 15:44:20 +0100 Subject: [PATCH] Some code formating --- include/Base64.h | 174 +-- include/bytearray.h | 238 ++-- include/device.h | 80 +- include/drmprocessorclient.h | 644 ++++----- include/fulfillment_item.h | 76 +- include/libgourou.h | 348 ++--- include/libgourou_common.h | 500 +++---- include/libgourou_log.h | 10 +- include/loan_token.h | 24 +- include/user.h | 122 +- src/bytearray.cpp | 290 ++-- src/device.cpp | 310 ++-- src/fulfillment_item.cpp | 142 +- src/libgourou.cpp | 2260 +++++++++++++++--------------- src/loan_token.cpp | 66 +- src/user.cpp | 282 ++-- utils/Makefile | 5 +- utils/acsmdownloader.cpp | 420 +++--- utils/adept_activate.cpp | 272 ++-- utils/adept_loan_mgt.cpp | 608 ++++---- utils/adept_remove.cpp | 352 ++--- utils/drmprocessorclientimpl.cpp | 558 ++++---- utils/drmprocessorclientimpl.h | 94 +- utils/launcher.cpp | 8 +- utils/utils_common.cpp | 80 +- utils/utils_common.h | 14 +- 26 files changed, 3989 insertions(+), 3988 deletions(-) diff --git a/include/Base64.h b/include/Base64.h index 548b9f1..68f0341 100644 --- a/include/Base64.h +++ b/include/Base64.h @@ -30,104 +30,104 @@ namespace macaron { -class Base64 { - public: + class Base64 { + public: - static std::string Encode(const std::string data) { - static + static std::string Encode(const std::string data) { + static #if __STDC_VERSION__ >= 201112L - constexpr + constexpr #endif /* __STDC_VERSION__ >= 201112L */ - char sEncodingTable[] = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', - 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', - 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', - 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', - 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', '0', '1', '2', '3', - '4', '5', '6', '7', '8', '9', '+', '/' - }; + char sEncodingTable[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/' + }; - size_t in_len = data.size(); - size_t out_len = 4 * ((in_len + 2) / 3); - std::string ret(out_len, '\0'); - size_t i; - char *p = const_cast(ret.c_str()); + size_t in_len = data.size(); + size_t out_len = 4 * ((in_len + 2) / 3); + std::string ret(out_len, '\0'); + size_t i; + char *p = const_cast(ret.c_str()); - for (i = 0; i < in_len - 2; i += 3) { - *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; - *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)]; - *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2) | ((int) (data[i + 2] & 0xC0) >> 6)]; - *p++ = sEncodingTable[data[i + 2] & 0x3F]; - } - if (i < in_len) { - *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; - if (i == (in_len - 1)) { - *p++ = sEncodingTable[((data[i] & 0x3) << 4)]; - *p++ = '='; - } - else { - *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)]; - *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2)]; - } - *p++ = '='; - } + for (i = 0; i < in_len - 2; i += 3) { + *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; + *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)]; + *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2) | ((int) (data[i + 2] & 0xC0) >> 6)]; + *p++ = sEncodingTable[data[i + 2] & 0x3F]; + } + if (i < in_len) { + *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; + if (i == (in_len - 1)) { + *p++ = sEncodingTable[((data[i] & 0x3) << 4)]; + *p++ = '='; + } + else { + *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int) (data[i + 1] & 0xF0) >> 4)]; + *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2)]; + } + *p++ = '='; + } - return ret; - } + return ret; + } - static std::string Decode(const std::string& input, std::string& out) { - static + static std::string Decode(const std::string& input, std::string& out) { + static #if __STDC_VERSION__ >= 201112L - constexpr + constexpr #endif /* __STDC_VERSION__ >= 201112L */ - unsigned char kDecodingTable[] = { - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, - 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, - 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 + unsigned char kDecodingTable[] = { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, + 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, + 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 + }; + + size_t in_len = input.size(); + if (in_len % 4 != 0) return "Input data size is not a multiple of 4"; + + size_t out_len = in_len / 4 * 3; + if (input[in_len - 1] == '=') out_len--; + if (input[in_len - 2] == '=') out_len--; + + out.resize(out_len); + + for (size_t i = 0, j = 0; i < in_len;) { + uint32_t a = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + uint32_t b = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + uint32_t c = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + uint32_t d = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; + + uint32_t triple = (a << 3 * 6) + (b << 2 * 6) + (c << 1 * 6) + (d << 0 * 6); + + if (j < out_len) out[j++] = (triple >> 2 * 8) & 0xFF; + if (j < out_len) out[j++] = (triple >> 1 * 8) & 0xFF; + if (j < out_len) out[j++] = (triple >> 0 * 8) & 0xFF; + } + + return ""; + } + }; - size_t in_len = input.size(); - if (in_len % 4 != 0) return "Input data size is not a multiple of 4"; - - size_t out_len = in_len / 4 * 3; - if (input[in_len - 1] == '=') out_len--; - if (input[in_len - 2] == '=') out_len--; - - out.resize(out_len); - - for (size_t i = 0, j = 0; i < in_len;) { - uint32_t a = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; - uint32_t b = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; - uint32_t c = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; - uint32_t d = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; - - uint32_t triple = (a << 3 * 6) + (b << 2 * 6) + (c << 1 * 6) + (d << 0 * 6); - - if (j < out_len) out[j++] = (triple >> 2 * 8) & 0xFF; - if (j < out_len) out[j++] = (triple >> 1 * 8) & 0xFF; - if (j < out_len) out[j++] = (triple >> 0 * 8) & 0xFF; - } - - return ""; - } - -}; - } #endif /* _MACARON_BASE64_H_ */ diff --git a/include/bytearray.h b/include/bytearray.h index 8fb7680..25c6aa1 100644 --- a/include/bytearray.h +++ b/include/bytearray.h @@ -38,149 +38,149 @@ namespace gourou { public: - /** - * @brief Create an empty byte array - * - * @param useMalloc If true, use malloc() instead of new[] for allocation - */ - ByteArray(bool useMalloc=false); + /** + * @brief Create an empty byte array + * + * @param useMalloc If true, use malloc() instead of new[] for allocation + */ + ByteArray(bool useMalloc=false); - /** - * @brief Create an empty byte array of length bytes - * - * @param length Length of data - * @param useMalloc If true, use malloc() instead of new[] for allocation - */ - ByteArray(unsigned int length, bool useMalloc=false); + /** + * @brief Create an empty byte array of length bytes + * + * @param length Length of data + * @param useMalloc If true, use malloc() instead of new[] for allocation + */ + ByteArray(unsigned int length, bool useMalloc=false); - /** - * @brief Initialize ByteArray with a copy of data - * - * @param data Data to be copied - * @param length Length of data - */ - ByteArray(const unsigned char* data, unsigned int length); + /** + * @brief Initialize ByteArray with a copy of data + * + * @param data Data to be copied + * @param length Length of data + */ + ByteArray(const unsigned char* data, unsigned int length); - /** - * @brief Initialize ByteArray with a copy of data - * - * @param data Data to be copied - * @param length Optional length of data. If length == -1, it use strlen(data) as length - */ - ByteArray(const char* data, int length=-1); + /** + * @brief Initialize ByteArray with a copy of data + * + * @param data Data to be copied + * @param length Optional length of data. If length == -1, it use strlen(data) as length + */ + ByteArray(const char* data, int length=-1); - /** - * @brief Initialize ByteArray with a copy of str - * - * @param str Use internal data of str - */ - ByteArray(const std::string& str); + /** + * @brief Initialize ByteArray with a copy of str + * + * @param str Use internal data of str + */ + ByteArray(const std::string& str); - ByteArray(const ByteArray& other); - ~ByteArray(); + ByteArray(const ByteArray& other); + ~ByteArray(); - /** - * @brief Encode "other" data into base64 and put it into a ByteArray - */ - static ByteArray fromBase64(const ByteArray& other); + /** + * @brief Encode "other" data into base64 and put it into a ByteArray + */ + static ByteArray fromBase64(const ByteArray& other); - /** - * @brief Encode data into base64 and put it into a ByteArray - * - * @param data Data to be encoded - * @param length Optional length of data. If length == -1, it use strlen(data) as length - */ - static ByteArray fromBase64(const char* data, int length=-1); + /** + * @brief Encode data into base64 and put it into a ByteArray + * + * @param data Data to be encoded + * @param length Optional length of data. If length == -1, it use strlen(data) as length + */ + static ByteArray fromBase64(const char* data, int length=-1); - /** - * @brief Encode str into base64 and put it into a ByteArray - * - * @param str Use internal data of str - */ - static ByteArray fromBase64(const std::string& str); + /** + * @brief Encode str into base64 and put it into a ByteArray + * + * @param str Use internal data of str + */ + static ByteArray fromBase64(const std::string& str); - /** - * @brief Return a string with base64 encoded internal data - */ - std::string toBase64(); + /** + * @brief Return a string with base64 encoded internal data + */ + std::string toBase64(); - /** - * @brief Convert hex string into bytes - * - * @param str Hex string - */ - static ByteArray fromHex(const std::string& str); + /** + * @brief Convert hex string into bytes + * + * @param str Hex string + */ + static ByteArray fromHex(const std::string& str); - /** - * @brief Return a string with human readable hex encoded internal data - */ - std::string toHex(); + /** + * @brief Return a string with human readable hex encoded internal data + */ + std::string toHex(); - /** - * @brief Append a byte to internal data - */ - void append(unsigned char c); + /** + * @brief Append a byte to internal data + */ + void append(unsigned char c); - /** - * @brief Append data to internal data - */ - void append(const unsigned char* data, unsigned int length); + /** + * @brief Append data to internal data + */ + void append(const unsigned char* data, unsigned int length); - /** - * @brief Append str to internal data - */ - void append(const char* str); + /** + * @brief Append str to internal data + */ + void append(const char* str); - /** - * @brief Append str to internal data - */ - void append(const std::string& str); + /** + * @brief Append str to internal data + */ + void append(const std::string& str); - /** - * @brief Get internal data. Must not be freed - */ - unsigned char* data() {return _data;} + /** + * @brief Get internal data. Must not be freed + */ + unsigned char* data() {return _data;} - /** - * @brief Get internal data and increment internal reference counter. - * Must bot be freed - */ - unsigned char* takeShadowData() {addRef() ; return _data;} + /** + * @brief Get internal data and increment internal reference counter. + * Must bot be freed + */ + unsigned char* takeShadowData() {addRef() ; return _data;} - /** - * @brief Release shadow data. It can now be freed by ByteArray - */ - void releaseShadowData() {delRef();} + /** + * @brief Release shadow data. It can now be freed by ByteArray + */ + void releaseShadowData() {delRef();} - /** - * @brief Get internal data length - */ - unsigned int length() const {return _length;} + /** + * @brief Get internal data length + */ + unsigned int length() const {return _length;} - /** - * @brief Get internal data length - */ - unsigned int size() const {return length();} + /** + * @brief Get internal data length + */ + unsigned int size() const {return length();} - /** - * @brief Increase or decrease internal buffer - * @param length New length of internal buffer - * @param keepData If true copy old data on new buffer, if false, - * create a new buffer with random data - */ - void resize(unsigned int length, bool keepData=true); + /** + * @brief Increase or decrease internal buffer + * @param length New length of internal buffer + * @param keepData If true copy old data on new buffer, if false, + * create a new buffer with random data + */ + void resize(unsigned int length, bool keepData=true); + + ByteArray& operator=(const ByteArray& other); - ByteArray& operator=(const ByteArray& other); - private: - void initData(const unsigned char* data, unsigned int length); - void addRef(); - void delRef(); + void initData(const unsigned char* data, unsigned int length); + void addRef(); + void delRef(); - bool _useMalloc; - unsigned char* _data; - unsigned int _length; - static std::map refCounter; + bool _useMalloc; + unsigned char* _data; + unsigned int _length; + static std::map refCounter; }; } #endif diff --git a/include/device.h b/include/device.h index 2e01b58..3a88abe 100644 --- a/include/device.h +++ b/include/device.h @@ -30,54 +30,54 @@ namespace gourou class Device { public: - static const int DEVICE_KEY_SIZE = 16; - static const int DEVICE_SERIAL_LEN = 10; + static const int DEVICE_KEY_SIZE = 16; + static const int DEVICE_SERIAL_LEN = 10; - /** - * @brief Main Device constructor - * - * @param processor Instance of DRMProcessor - * @param deviceFile Path of device.xml - * @param deviceKeyFile Path of devicesalt - */ - Device(DRMProcessor* processor, const std::string& deviceFile, const std::string& deviceKeyFile); + /** + * @brief Main Device constructor + * + * @param processor Instance of DRMProcessor + * @param deviceFile Path of device.xml + * @param deviceKeyFile Path of devicesalt + */ + Device(DRMProcessor* processor, const std::string& deviceFile, const std::string& deviceKeyFile); - /** - * @brief Return value of devicesalt file (DEVICE_KEY_SIZE len) - */ - const unsigned char* getDeviceKey(); + /** + * @brief Return value of devicesalt file (DEVICE_KEY_SIZE len) + */ + const unsigned char* getDeviceKey(); - /** - * @brief Get one value of device.xml (deviceClass, deviceSerial, deviceName, deviceType, hobbes, clientOS, clientLocale) - */ - std::string getProperty(const std::string& property, const std::string& _default=std::string("")); - std::string operator[](const std::string& property); + /** + * @brief Get one value of device.xml (deviceClass, deviceSerial, deviceName, deviceType, hobbes, clientOS, clientLocale) + */ + std::string getProperty(const std::string& property, const std::string& _default=std::string("")); + std::string operator[](const std::string& property); + + /** + * @brief Create device.xml and devicesalt files when they did not exists + * + * @param processor Instance of DRMProcessor + * @param dirName Directory where to put files (.adept) + * @param hobbes Hobbes (client version) to set + * @param randomSerial Create a random serial (new device each time) or not (serial computed from machine specs) + */ + static Device* createDevice(DRMProcessor* processor, const std::string& dirName, const std::string& hobbes, bool randomSerial); - /** - * @brief Create device.xml and devicesalt files when they did not exists - * - * @param processor Instance of DRMProcessor - * @param dirName Directory where to put files (.adept) - * @param hobbes Hobbes (client version) to set - * @param randomSerial Create a random serial (new device each time) or not (serial computed from machine specs) - */ - static Device* createDevice(DRMProcessor* processor, const std::string& dirName, const std::string& hobbes, bool randomSerial); - private: - DRMProcessor* processor; + DRMProcessor* processor; std::string deviceFile; std::string deviceKeyFile; - unsigned char deviceKey[DEVICE_KEY_SIZE]; - std::map properties; + unsigned char deviceKey[DEVICE_KEY_SIZE]; + std::map properties; - Device(DRMProcessor* processor); - - std::string makeFingerprint(const std::string& serial); - std::string makeSerial(bool random); - void parseDeviceFile(); - void parseDeviceKeyFile(); - void createDeviceFile(const std::string& hobbes, bool randomSerial); - void createDeviceKeyFile(); + Device(DRMProcessor* processor); + + std::string makeFingerprint(const std::string& serial); + std::string makeSerial(bool random); + void parseDeviceFile(); + void parseDeviceKeyFile(); + void createDeviceFile(const std::string& hobbes, bool randomSerial); + void createDeviceKeyFile(); }; } diff --git a/include/drmprocessorclient.h b/include/drmprocessorclient.h index 8d24ed7..352e2f7 100644 --- a/include/drmprocessorclient.h +++ b/include/drmprocessorclient.h @@ -34,376 +34,376 @@ namespace gourou class DigestInterface { public: - /** - * @brief Create a digest handler - * - * @param digestName Digest name to instanciate - */ - virtual void* createDigest(const std::string& digestName) = 0; + /** + * @brief Create a digest handler + * + * @param digestName Digest name to instanciate + */ + virtual void* createDigest(const std::string& digestName) = 0; - /** - * @brief Update digest engine with new data - * - * @param handler Digest handler - * @param data Data to digest - * @param length Length of data - */ - virtual void digestUpdate(void* handler, unsigned char* data, unsigned int length) = 0; + /** + * @brief Update digest engine with new data + * + * @param handler Digest handler + * @param data Data to digest + * @param length Length of data + */ + virtual void digestUpdate(void* handler, unsigned char* data, unsigned int length) = 0; - /** - * @brief Finalize digest with remained buffered data and destroy handler - * - * @param handler Digest handler - * @param digestOut Digest result (buffer must be pre allocated with right size) - */ - virtual void digestFinalize(void* handler, unsigned char* digestOut) = 0; + /** + * @brief Finalize digest with remained buffered data and destroy handler + * + * @param handler Digest handler + * @param digestOut Digest result (buffer must be pre allocated with right size) + */ + virtual void digestFinalize(void* handler, unsigned char* digestOut) = 0; - /** - * @brief Global digest function - * - * @param digestName Digest name to instanciate - * @param data Data to digest - * @param length Length of data - * @param digestOut Digest result (buffer must be pre allocated with right size) - */ - virtual void digest(const std::string& digestName, unsigned char* data, unsigned int length, unsigned char* digestOut) = 0; + /** + * @brief Global digest function + * + * @param digestName Digest name to instanciate + * @param data Data to digest + * @param length Length of data + * @param digestOut Digest result (buffer must be pre allocated with right size) + */ + virtual void digest(const std::string& digestName, unsigned char* data, unsigned int length, unsigned char* digestOut) = 0; }; class RandomInterface { public: - /** - * @brief Generate random bytes - * - * @param bytesOut Buffer to fill with random bytes - * @param length Length of bytesOut - */ - virtual void randBytes(unsigned char* bytesOut, unsigned int length) = 0; + /** + * @brief Generate random bytes + * + * @param bytesOut Buffer to fill with random bytes + * @param length Length of bytesOut + */ + virtual void randBytes(unsigned char* bytesOut, unsigned int length) = 0; }; class HTTPInterface { public: - - /** - * @brief Send HTTP (GET or POST) request - * - * @param URL HTTP URL - * @param POSTData POST data if needed, if not set, a GET request is done - * @param contentType Optional content type of POST Data - * @param responseHeaders Optional Response headers of HTTP request - * @param fd Optional file descriptor to write request result - * @param resume false if target file should be truncated, true to try resume download (works only in combination with a valid fd) - * - * @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* responseHeaders=0, int fd=0, bool resume=false) = 0; + + /** + * @brief Send HTTP (GET or POST) request + * + * @param URL HTTP URL + * @param POSTData POST data if needed, if not set, a GET request is done + * @param contentType Optional content type of POST Data + * @param responseHeaders Optional Response headers of HTTP request + * @param fd Optional file descriptor to write request result + * @param resume false if target file should be truncated, true to try resume download (works only in combination with a valid fd) + * + * @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* responseHeaders=0, int fd=0, bool resume=false) = 0; }; class RSAInterface { public: - enum RSA_KEY_TYPE { - RSA_KEY_PKCS12 = 0, - RSA_KEY_PKCS8, - RSA_KEY_X509 - }; + enum RSA_KEY_TYPE { + RSA_KEY_PKCS12 = 0, + RSA_KEY_PKCS8, + RSA_KEY_X509 + }; - /** - * @brief Encrypt data with RSA private key. Data is padded using PKCS1.5 - * - * @param RSAKey RSA key in binary form - * @param RSAKeyLength RSA key length - * @param keyType Key type - * @param password Optional password for RSA PKCS12 certificate - * @param data Data to encrypt - * @param dataLength Data length - * @param res Encryption result (pre allocated buffer) - */ - virtual void RSAPrivateEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength, - const RSA_KEY_TYPE keyType, const std::string& password, - const unsigned char* data, unsigned dataLength, - unsigned char* res) = 0; - - /** - * @brief Decrypt data with RSA private key. Data is padded using PKCS1.5 - * - * @param RSAKey RSA key in binary form - * @param RSAKeyLength RSA key length - * @param keyType Key type - * @param password Optional password for RSA PKCS12 certificate - * @param data Data to encrypt - * @param dataLength Data length - * @param res Encryption result (pre allocated buffer) - */ - virtual void RSAPrivateDecrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength, - const RSA_KEY_TYPE keyType, const std::string& password, - const unsigned char* data, unsigned dataLength, - unsigned char* res) = 0; + /** + * @brief Encrypt data with RSA private key. Data is padded using PKCS1.5 + * + * @param RSAKey RSA key in binary form + * @param RSAKeyLength RSA key length + * @param keyType Key type + * @param password Optional password for RSA PKCS12 certificate + * @param data Data to encrypt + * @param dataLength Data length + * @param res Encryption result (pre allocated buffer) + */ + virtual void RSAPrivateEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength, + const RSA_KEY_TYPE keyType, const std::string& password, + const unsigned char* data, unsigned dataLength, + unsigned char* res) = 0; - /** - * @brief Encrypt data with RSA public key. Data is padded using PKCS1.5 - * - * @param RSAKey RSA key in binary form - * @param RSAKeyLength RSA key length - * @param keyType Key type - * @param password Optional password for RSA PKCS12 certificate - * @param data Data to encrypt - * @param dataLength Data length - * @param res Encryption result (pre allocated buffer) - */ - virtual void RSAPublicEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength, - const RSA_KEY_TYPE keyType, - const unsigned char* data, unsigned dataLength, - unsigned char* res) = 0; + /** + * @brief Decrypt data with RSA private key. Data is padded using PKCS1.5 + * + * @param RSAKey RSA key in binary form + * @param RSAKeyLength RSA key length + * @param keyType Key type + * @param password Optional password for RSA PKCS12 certificate + * @param data Data to encrypt + * @param dataLength Data length + * @param res Encryption result (pre allocated buffer) + */ + virtual void RSAPrivateDecrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength, + const RSA_KEY_TYPE keyType, const std::string& password, + const unsigned char* data, unsigned dataLength, + unsigned char* res) = 0; - /** - * @brief Generate RSA key. Expnonent is fixed (65537 / 0x10001) - * - * @param keyLengthBits Length of key (in bits) to generate - * - * @return generatedKey - */ - virtual void* generateRSAKey(int keyLengthBits) = 0; + /** + * @brief Encrypt data with RSA public key. Data is padded using PKCS1.5 + * + * @param RSAKey RSA key in binary form + * @param RSAKeyLength RSA key length + * @param keyType Key type + * @param password Optional password for RSA PKCS12 certificate + * @param data Data to encrypt + * @param dataLength Data length + * @param res Encryption result (pre allocated buffer) + */ + virtual void RSAPublicEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength, + const RSA_KEY_TYPE keyType, + const unsigned char* data, unsigned dataLength, + unsigned char* res) = 0; - /** - * @brief Destroy key previously generated - * - * @param handler Key to destroy - */ - virtual void destroyRSAHandler(void* handler) = 0; + /** + * @brief Generate RSA key. Expnonent is fixed (65537 / 0x10001) + * + * @param keyLengthBits Length of key (in bits) to generate + * + * @return generatedKey + */ + virtual void* generateRSAKey(int keyLengthBits) = 0; - /** - * @brief Extract public key (big number) from RSA handler - * - * @param handler RSA handler (generated key) - * @param keyOut Pre allocated buffer (if *keyOut != 0). If *keyOut is 0, memory is internally allocated (must be freed) - * @param keyOutLength Length of result - */ - virtual void extractRSAPublicKey(void* handler, unsigned char** keyOut, unsigned int* keyOutLength) = 0; + /** + * @brief Destroy key previously generated + * + * @param handler Key to destroy + */ + virtual void destroyRSAHandler(void* handler) = 0; - /** - * @brief Extract private key (big number) from RSA handler - * - * @param handler RSA handler (generated key) - * @param keyOut Pre allocated buffer (if *keyOut != 0). If *keyOut is 0, memory is internally allocated (must be freed) - * @param keyOutLength Length of result - */ - virtual void extractRSAPrivateKey(void* handler, unsigned char** keyOut, unsigned int* keyOutLength) = 0; + /** + * @brief Extract public key (big number) from RSA handler + * + * @param handler RSA handler (generated key) + * @param keyOut Pre allocated buffer (if *keyOut != 0). If *keyOut is 0, memory is internally allocated (must be freed) + * @param keyOutLength Length of result + */ + virtual void extractRSAPublicKey(void* handler, unsigned char** keyOut, unsigned int* keyOutLength) = 0; - /** - * @brief Extract certificate from PKCS12 blob - * - * @param RSAKey RSA key in binary form - * @param RSAKeyLength RSA key length - * @param keyType Key type - * @param password Optional password for RSA PKCS12 certificate - * @param certOut Result certificate - * @param certOutLength Result certificate length - */ - virtual void extractCertificate(const unsigned char* RSAKey, unsigned int RSAKeyLength, - const RSA_KEY_TYPE keyType, const std::string& password, - unsigned char** certOut, unsigned int* certOutLength) = 0; + /** + * @brief Extract private key (big number) from RSA handler + * + * @param handler RSA handler (generated key) + * @param keyOut Pre allocated buffer (if *keyOut != 0). If *keyOut is 0, memory is internally allocated (must be freed) + * @param keyOutLength Length of result + */ + virtual void extractRSAPrivateKey(void* handler, unsigned char** keyOut, unsigned int* keyOutLength) = 0; + + /** + * @brief Extract certificate from PKCS12 blob + * + * @param RSAKey RSA key in binary form + * @param RSAKeyLength RSA key length + * @param keyType Key type + * @param password Optional password for RSA PKCS12 certificate + * @param certOut Result certificate + * @param certOutLength Result certificate length + */ + virtual void extractCertificate(const unsigned char* RSAKey, unsigned int RSAKeyLength, + const RSA_KEY_TYPE keyType, const std::string& password, + unsigned char** certOut, unsigned int* certOutLength) = 0; }; class CryptoInterface { public: - enum CRYPTO_ALGO { - ALGO_AES=0, - ALGO_RC4 - }; + enum CRYPTO_ALGO { + ALGO_AES=0, + ALGO_RC4 + }; - enum CHAINING_MODE { - CHAIN_ECB=0, - CHAIN_CBC - }; - - /** - * @brief Do encryption. If length of data is not multiple of block size, PKCS#5 padding is done - * - * @param algo Algorithm to use - * @param chaining Chaining mode - * @param key AES key - * @param keyLength AES key length - * @param iv IV key - * @param ivLength IV key length - * @param dataIn Data to encrypt - * @param dataInLength Data length - * @param dataOut Encrypted data - * @param dataOutLength Length of encrypted data - */ - virtual void encrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining, - const unsigned char* key, unsigned int keyLength, - const unsigned char* iv, unsigned int ivLength, - const unsigned char* dataIn, unsigned int dataInLength, - unsigned char* dataOut, unsigned int* dataOutLength) = 0; + enum CHAINING_MODE { + CHAIN_ECB=0, + CHAIN_CBC + }; - /** - * @brief Init encryption - * - * @param chaining Chaining mode - * @param key Key - * @param keyLength Key length - * @param iv Optional IV key - * @param ivLength Optional IV key length - * - * @return AES handler - */ - virtual void* encryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining, - const unsigned char* key, unsigned int keyLength, - const unsigned char* iv=0, unsigned int ivLength=0) = 0; + /** + * @brief Do encryption. If length of data is not multiple of block size, PKCS#5 padding is done + * + * @param algo Algorithm to use + * @param chaining Chaining mode + * @param key AES key + * @param keyLength AES key length + * @param iv IV key + * @param ivLength IV key length + * @param dataIn Data to encrypt + * @param dataInLength Data length + * @param dataOut Encrypted data + * @param dataOutLength Length of encrypted data + */ + virtual void encrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining, + const unsigned char* key, unsigned int keyLength, + const unsigned char* iv, unsigned int ivLength, + const unsigned char* dataIn, unsigned int dataInLength, + unsigned char* dataOut, unsigned int* dataOutLength) = 0; - /** - * @brief Encrypt data - * - * @param handler Crypto handler - * @param dataIn Data to encrypt - * @param dataInLength Data length - * @param dataOut Encrypted data - * @param dataOutLength Length of encrypted data - */ - virtual void encryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength, - unsigned char* dataOut, unsigned int* dataOutLength) = 0; + /** + * @brief Init encryption + * + * @param chaining Chaining mode + * @param key Key + * @param keyLength Key length + * @param iv Optional IV key + * @param ivLength Optional IV key length + * + * @return AES handler + */ + virtual void* encryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining, + const unsigned char* key, unsigned int keyLength, + const unsigned char* iv=0, unsigned int ivLength=0) = 0; - /** - * @brief Finalize encryption (pad and encrypt last block if needed) - * Destroy handler at the end - * - * @param handler Crypto handler - * @param dataOut Last block of encrypted data - * @param dataOutLength Length of encrypted data - */ - virtual void encryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength) = 0; + /** + * @brief Encrypt data + * + * @param handler Crypto handler + * @param dataIn Data to encrypt + * @param dataInLength Data length + * @param dataOut Encrypted data + * @param dataOutLength Length of encrypted data + */ + virtual void encryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength, + unsigned char* dataOut, unsigned int* dataOutLength) = 0; - /** - * @brief Do decryption. If length of data is not multiple of block size, PKCS#5 padding is done - * - * @param algo Algorithm to use - * @param chaining Chaining mode - * @param key AES key - * @param keyLength AES key length - * @param iv IV key - * @param ivLength IV key length - * @param dataIn Data to encrypt - * @param dataInLength Data length - * @param dataOut Encrypted data - * @param dataOutLength Length of encrypted data - */ - virtual void decrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining, - const unsigned char* key, unsigned int keyLength, - const unsigned char* iv, unsigned int ivLength, - const unsigned char* dataIn, unsigned int dataInLength, - unsigned char* dataOut, unsigned int* dataOutLength) = 0; + /** + * @brief Finalize encryption (pad and encrypt last block if needed) + * Destroy handler at the end + * + * @param handler Crypto handler + * @param dataOut Last block of encrypted data + * @param dataOutLength Length of encrypted data + */ + virtual void encryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength) = 0; - /** - * @brief Init decryption - * - * @param chaining Chaining mode - * @param key Key - * @param keyLength Key length - * @param iv IV key - * @param ivLength IV key length - * - * @return AES handler - */ - virtual void* decryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining, - const unsigned char* key, unsigned int keyLength, - const unsigned char* iv=0, unsigned int ivLength=0) = 0; + /** + * @brief Do decryption. If length of data is not multiple of block size, PKCS#5 padding is done + * + * @param algo Algorithm to use + * @param chaining Chaining mode + * @param key AES key + * @param keyLength AES key length + * @param iv IV key + * @param ivLength IV key length + * @param dataIn Data to encrypt + * @param dataInLength Data length + * @param dataOut Encrypted data + * @param dataOutLength Length of encrypted data + */ + virtual void decrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining, + const unsigned char* key, unsigned int keyLength, + const unsigned char* iv, unsigned int ivLength, + const unsigned char* dataIn, unsigned int dataInLength, + unsigned char* dataOut, unsigned int* dataOutLength) = 0; - /** - * @brief Decrypt data - * - * @param handler Crypto handler - * @param dataIn Data to decrypt - * @param dataInLength Data length - * @param dataOut Decrypted data - * @param dataOutLength Length of decrypted data - */ - virtual void decryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength, - unsigned char* dataOut, unsigned int* dataOutLength) = 0; - /** - * @brief Finalize decryption (decrypt last block and remove padding if it is set). - * Destroy handler at the end - * - * @param handler Crypto handler - * @param dataOut Last block decrypted data - * @param dataOutLength Length of decrypted data - */ - virtual void decryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength) = 0; + /** + * @brief Init decryption + * + * @param chaining Chaining mode + * @param key Key + * @param keyLength Key length + * @param iv IV key + * @param ivLength IV key length + * + * @return AES handler + */ + virtual void* decryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining, + const unsigned char* key, unsigned int keyLength, + const unsigned char* iv=0, unsigned int ivLength=0) = 0; + + /** + * @brief Decrypt data + * + * @param handler Crypto handler + * @param dataIn Data to decrypt + * @param dataInLength Data length + * @param dataOut Decrypted data + * @param dataOutLength Length of decrypted data + */ + virtual void decryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength, + unsigned char* dataOut, unsigned int* dataOutLength) = 0; + /** + * @brief Finalize decryption (decrypt last block and remove padding if it is set). + * Destroy handler at the end + * + * @param handler Crypto handler + * @param dataOut Last block decrypted data + * @param dataOutLength Length of decrypted data + */ + virtual void decryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength) = 0; }; class ZIPInterface { public: - /** - * @brief Open a zip file and return an handler - * - * @param path Path of zip file - * - * @return ZIP file handler - */ - virtual void* zipOpen(const std::string& path) = 0; - - /** - * @brief Read zip internal file - * - * @param handler ZIP file handler - * @param path Internal path inside zip file - * @param result Result buffer - * @param decompress If false, don't decompress read data - */ - virtual void zipReadFile(void* handler, const std::string& path, ByteArray& result, bool decompress=true) = 0; - - /** - * @brief Write zip internal file - * - * @param handler ZIP file handler - * @param path Internal path inside zip file - * @param content File content - */ - virtual void zipWriteFile(void* handler, const std::string& path, ByteArray& content) = 0; + /** + * @brief Open a zip file and return an handler + * + * @param path Path of zip file + * + * @return ZIP file handler + */ + virtual void* zipOpen(const std::string& path) = 0; - /** - * @brief Delete zip internal file - * - * @param handler ZIP file handler - * @param path Internal path inside zip file - */ - virtual void zipDeleteFile(void* handler, const std::string& path) = 0; + /** + * @brief Read zip internal file + * + * @param handler ZIP file handler + * @param path Internal path inside zip file + * @param result Result buffer + * @param decompress If false, don't decompress read data + */ + virtual void zipReadFile(void* handler, const std::string& path, ByteArray& result, bool decompress=true) = 0; - /** - * @brief Close ZIP file handler - * - * @param handler ZIP file handler - */ - virtual void zipClose(void* handler) = 0; + /** + * @brief Write zip internal file + * + * @param handler ZIP file handler + * @param path Internal path inside zip file + * @param content File content + */ + virtual void zipWriteFile(void* handler, const std::string& path, ByteArray& content) = 0; - /** - * @brief Inflate algorithm - * - * @param data Data to inflate - * @param result Zipped data - * @param wbits Window bits value for libz - */ - virtual void inflate(gourou::ByteArray& data, gourou::ByteArray& result, - int wbits=-15) = 0; - - /** - * @brief Deflate algorithm - * - * @param data Data to deflate - * @param result Unzipped data - * @param wbits Window bits value for libz - * @param compressionLevel Compression level for libz - */ - virtual void deflate(gourou::ByteArray& data, gourou::ByteArray& result, - int wbits=-15, int compressionLevel=8) = 0; + /** + * @brief Delete zip internal file + * + * @param handler ZIP file handler + * @param path Internal path inside zip file + */ + virtual void zipDeleteFile(void* handler, const std::string& path) = 0; + + /** + * @brief Close ZIP file handler + * + * @param handler ZIP file handler + */ + virtual void zipClose(void* handler) = 0; + + /** + * @brief Inflate algorithm + * + * @param data Data to inflate + * @param result Zipped data + * @param wbits Window bits value for libz + */ + virtual void inflate(gourou::ByteArray& data, gourou::ByteArray& result, + int wbits=-15) = 0; + + /** + * @brief Deflate algorithm + * + * @param data Data to deflate + * @param result Unzipped data + * @param wbits Window bits value for libz + * @param compressionLevel Compression level for libz + */ + virtual void deflate(gourou::ByteArray& data, gourou::ByteArray& result, + int wbits=-15, int compressionLevel=8) = 0; }; - + class DRMProcessorClient: public DigestInterface, public RandomInterface, public HTTPInterface, \ - public RSAInterface, public CryptoInterface, public ZIPInterface + public RSAInterface, public CryptoInterface, public ZIPInterface {}; } #endif diff --git a/include/fulfillment_item.h b/include/fulfillment_item.h index 100bcdf..32d2fe8 100644 --- a/include/fulfillment_item.h +++ b/include/fulfillment_item.h @@ -34,52 +34,52 @@ namespace gourou class FulfillmentItem { public: - /** - * @brief Main constructor. Not to be called by user - * - * @param doc Fulfill reply - * @param user User pointer - */ - FulfillmentItem(pugi::xml_document& doc, User* user); + /** + * @brief Main constructor. Not to be called by user + * + * @param doc Fulfill reply + * @param user User pointer + */ + FulfillmentItem(pugi::xml_document& doc, User* user); - ~FulfillmentItem(); + ~FulfillmentItem(); - /** - * @brief Return metadata value from ACSM metadata section - * - * @param name Name of key to return - */ - std::string getMetadata(std::string name); + /** + * @brief Return metadata value from ACSM metadata section + * + * @param name Name of key to return + */ + std::string getMetadata(std::string name); - /** - * @brief Return rights generated by ACS server (XML format) - */ - std::string getRights(); + /** + * @brief Return rights generated by ACS server (XML format) + */ + std::string getRights(); - /** - * @brief Return epub download URL - */ - std::string getDownloadURL(); + /** + * @brief Return epub download URL + */ + std::string getDownloadURL(); - /** - * @brief Return resource value - */ - std::string getResource(); + /** + * @brief Return resource value + */ + std::string getResource(); - /** - * @brief Return loan token if there is one - */ - LoanToken* getLoanToken(); + /** + * @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); + 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 cdd3cf4..921937b 100644 --- a/include/libgourou.h +++ b/include/libgourou.h @@ -48,204 +48,204 @@ namespace gourou { public: - static const std::string VERSION; + static const std::string VERSION; - enum ITEM_TYPE { EPUB=0, PDF }; - /** - * @brief Main constructor. To be used once all is configured (user has signedIn, device is activated) - * - * @param client Client processor - * @param deviceFile Path of device.xml - * @param activationFile Path of activation.xml - * @param deviceKeyFile Path of devicesalt - */ + enum ITEM_TYPE { EPUB=0, PDF }; + /** + * @brief Main constructor. To be used once all is configured (user has signedIn, device is activated) + * + * @param client Client processor + * @param deviceFile Path of device.xml + * @param activationFile Path of activation.xml + * @param deviceKeyFile Path of devicesalt + */ DRMProcessor(DRMProcessorClient* client, const std::string& deviceFile, const std::string& activationFile, const std::string& deviceKeyFile); - - ~DRMProcessor(); - /** - * @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, bool notify=true); + ~DRMProcessor(); - /** - * @brief Once fulfilled, ePub file needs to be downloaded. - * During this operation, DRM information is added into downloaded file - * - * @param item Item from fulfill() method - * @param path Output file path - * @param resume false if target file should be truncated, true to try resume download - * - * @return Type of downloaded item - */ - ITEM_TYPE download(FulfillmentItem* item, std::string path, bool resume=false); + /** + * @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, bool notify=true); - /** - * @brief SignIn into ACS Server (required to activate device) - * - * @param adobeID AdobeID username - * @param adobePassword Adobe password - */ - void signIn(const std::string& adobeID, const std::string& adobePassword); + /** + * @brief Once fulfilled, ePub file needs to be downloaded. + * During this operation, DRM information is added into downloaded file + * + * @param item Item from fulfill() method + * @param path Output file path + * @param resume false if target file should be truncated, true to try resume download + * + * @return Type of downloaded item + */ + ITEM_TYPE download(FulfillmentItem* item, std::string path, bool resume=false); - /** - * @brief Activate newly created device (user must have successfuly signedIn before) - */ - void activateDevice(); + /** + * @brief SignIn into ACS Server (required to activate device) + * + * @param adobeID AdobeID username + * @param adobePassword Adobe password + */ + void signIn(const std::string& adobeID, const std::string& adobePassword); - /** - * @brief Return loaned book to server - * - * @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, bool notify=true); + /** + * @brief Activate newly created device (user must have successfuly signedIn before) + */ + void activateDevice(); - /** - * @brief Return default ADEPT directory (ie /home//.config/adept) - */ - static std::string getDefaultAdeptDir(void); - - /** - * @brief Create a new ADEPT environment (device.xml, devicesalt and activation.xml). - * - * @param client Client processor - * @param randomSerial Always generate a new device (or not) - * @param dirName Directory where to put generated files (.adept) - * @param hobbes Override hobbes default version - * @param ACSServer Override main ACS server (default adeactivate.adobe.com) - */ + /** + * @brief Return loaned book to server + * + * @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, bool notify=true); + + /** + * @brief Return default ADEPT directory (ie /home//.config/adept) + */ + static std::string getDefaultAdeptDir(void); + + /** + * @brief Create a new ADEPT environment (device.xml, devicesalt and activation.xml). + * + * @param client Client processor + * @param randomSerial Always generate a new device (or not) + * @param dirName Directory where to put generated files (.adept) + * @param hobbes Override hobbes default version + * @param ACSServer Override main ACS server (default adeactivate.adobe.com) + */ static DRMProcessor* createDRMProcessor(DRMProcessorClient* client, - bool randomSerial=false, std::string dirName=std::string(""), - const std::string& hobbes=std::string(HOBBES_DEFAULT_VERSION), - const std::string& ACSServer=ACS_SERVER); + bool randomSerial=false, std::string dirName=std::string(""), + const std::string& hobbes=std::string(HOBBES_DEFAULT_VERSION), + const std::string& ACSServer=ACS_SERVER); - /** - * @brief Get current log level - */ - static int getLogLevel(); + /** + * @brief Get current log level + */ + static int getLogLevel(); - /** - * @brief Set log level (higher number for verbose output) - */ - static void setLogLevel(int logLevel); + /** + * @brief Set log level (higher number for verbose output) + */ + static void setLogLevel(int logLevel); - /** - * Functions used internally, should not be called by user - */ + /** + * Functions used internally, should not be called by user + */ - /** - * @brief Send HTTP (GET or POST) request - * - * @param URL HTTP URL - * @param POSTData POST data if needed, if not set, a GET request is done - * @param contentType Optional content type of POST Data - * @param responseHeaders Optional Response headers of HTTP request - * @param fd Optional File descriptor to write received data - * @param resume false if target file should be truncated, true to try resume download (works only in combination of a valid fd) - * - * @return data of HTTP response - */ - ByteArray sendRequest(const std::string& URL, const std::string& POSTData=std::string(), const char* contentType=0, std::map* responseHeaders=0, int fd=0, bool resume=false); + /** + * @brief Send HTTP (GET or POST) request + * + * @param URL HTTP URL + * @param POSTData POST data if needed, if not set, a GET request is done + * @param contentType Optional content type of POST Data + * @param responseHeaders Optional Response headers of HTTP request + * @param fd Optional File descriptor to write received data + * @param resume false if target file should be truncated, true to try resume download (works only in combination of a valid fd) + * + * @return data of HTTP response + */ + ByteArray sendRequest(const std::string& URL, const std::string& POSTData=std::string(), const char* contentType=0, std::map* responseHeaders=0, int fd=0, bool resume=false); - /** - * @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 - */ - ByteArray encryptWithDeviceKey(const unsigned char* data, unsigned int len); + /** + * @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 decrypt data with private device key - */ - ByteArray decryptWithDeviceKey(const unsigned char* data, unsigned int len); + /** + * @brief In place encrypt data with private device key + */ + ByteArray encryptWithDeviceKey(const unsigned char* data, unsigned int len); - /** - * @brief Return base64 encoded value of RSA public key - */ - std::string serializeRSAPublicKey(void* rsa); + /** + * @brief In place decrypt data with private device key + */ + ByteArray decryptWithDeviceKey(const unsigned char* data, unsigned int len); - /** - * @brief Return base64 encoded value of RSA private key encrypted with private device key - */ - std::string serializeRSAPrivateKey(void* rsa); + /** + * @brief Return base64 encoded value of RSA public key + */ + std::string serializeRSAPublicKey(void* rsa); - /** - * @brief Export clear private license key into path - */ - void exportPrivateLicenseKey(std::string path); - - /** - * @brief Get current user - */ - User* getUser() { return user; } + /** + * @brief Return base64 encoded value of RSA private key encrypted with private device key + */ + std::string serializeRSAPrivateKey(void* rsa); - /** - * @brief Get current device - */ - Device* getDevice() { return device; } + /** + * @brief Export clear private license key into path + */ + void exportPrivateLicenseKey(std::string path); + + /** + * @brief Get current user + */ + User* getUser() { return user; } + + /** + * @brief Get current device + */ + Device* getDevice() { return device; } + + /** + * @brief Get current client + */ + DRMProcessorClient* getClient() { return client; } + + /** + * @brief Remove ADEPT DRM + * 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, const unsigned char* encryptionKey=0, unsigned encryptionKeySize=0); - /** - * @brief Get current client - */ - DRMProcessorClient* getClient() { return client; } - - /** - * @brief Remove ADEPT DRM - * 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, const unsigned char* encryptionKey=0, unsigned encryptionKeySize=0); - private: - gourou::DRMProcessorClient* client; + gourou::DRMProcessorClient* client; gourou::Device* device; gourou::User* user; - + DRMProcessor(DRMProcessorClient* client); - - void pushString(void* sha_ctx, const std::string& string); - void pushTag(void* sha_ctx, uint8_t tag); - void hashNode(const pugi::xml_node& root, void *sha_ctx, std::map nsHash); - void hashNode(const pugi::xml_node& root, unsigned char* sha_out); - void signNode(pugi::xml_node& rootNode); - void addNonce(pugi::xml_node& root); - void buildAuthRequest(pugi::xml_document& authReq); - void buildInitLicenseServiceRequest(pugi::xml_document& initLicReq, std::string operatorURL); - void doOperatorAuth(std::string operatorURL); - 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, - 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); - void generatePDFObjectKey(int version, - const unsigned char* masterKey, unsigned int masterKeyLength, - int objectId, int objectGenerationNumber, - unsigned char* keyOut); - void removePDFDRM(const std::string& filenameIn, const std::string& filenameOut, const unsigned char* encryptionKey, unsigned encryptionKeySize); + + void pushString(void* sha_ctx, const std::string& string); + void pushTag(void* sha_ctx, uint8_t tag); + void hashNode(const pugi::xml_node& root, void *sha_ctx, std::map nsHash); + void hashNode(const pugi::xml_node& root, unsigned char* sha_out); + void signNode(pugi::xml_node& rootNode); + void addNonce(pugi::xml_node& root); + void buildAuthRequest(pugi::xml_document& authReq); + void buildInitLicenseServiceRequest(pugi::xml_document& initLicReq, std::string operatorURL); + void doOperatorAuth(std::string operatorURL); + 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, + 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); + void generatePDFObjectKey(int version, + const unsigned char* masterKey, unsigned int masterKeyLength, + int objectId, int objectGenerationNumber, + unsigned char* keyOut); + void removePDFDRM(const std::string& filenameIn, const std::string& filenameOut, const unsigned char* encryptionKey, unsigned encryptionKeySize); }; } diff --git a/include/libgourou_common.h b/include/libgourou_common.h index 72f3915..6c57a4e 100644 --- a/include/libgourou_common.h +++ b/include/libgourou_common.h @@ -43,152 +43,152 @@ namespace gourou /** * Some common utilities */ - - #define ADOBE_ADEPT_NS "http://ns.adobe.com/adept" + +#define ADOBE_ADEPT_NS "http://ns.adobe.com/adept" static const int SHA1_LEN = 20; static const int RSA_KEY_SIZE = 128; static const int RSA_KEY_SIZE_BITS = (RSA_KEY_SIZE*8); - + enum GOUROU_ERROR { - GOUROU_DEVICE_DOES_NOT_MATCH = 0x1000, - GOUROU_INVALID_CLIENT, - GOUROU_TAG_NOT_FOUND, - GOUROU_ADEPT_ERROR, - GOUROU_FILE_ERROR, - GOUROU_INVALID_PROPERTY + GOUROU_DEVICE_DOES_NOT_MATCH = 0x1000, + GOUROU_INVALID_CLIENT, + GOUROU_TAG_NOT_FOUND, + GOUROU_ADEPT_ERROR, + GOUROU_FILE_ERROR, + GOUROU_INVALID_PROPERTY }; enum FULFILL_ERROR { - FF_ACSM_FILE_NOT_EXISTS = 0x1100, - FF_INVALID_ACSM_FILE, - FF_NO_HMAC_IN_ACSM_FILE, - FF_NOT_ACTIVATED, - FF_NO_OPERATOR_URL, - FF_SERVER_INTERNAL_ERROR + FF_ACSM_FILE_NOT_EXISTS = 0x1100, + FF_INVALID_ACSM_FILE, + FF_NO_HMAC_IN_ACSM_FILE, + FF_NOT_ACTIVATED, + FF_NO_OPERATOR_URL, + FF_SERVER_INTERNAL_ERROR }; enum DOWNLOAD_ERROR { - DW_NO_ITEM = 0x1200, - DW_NO_EBX_HANDLER, + DW_NO_ITEM = 0x1200, + DW_NO_EBX_HANDLER, }; enum SIGNIN_ERROR { - SIGN_INVALID_CREDENTIALS = 0x1300, + SIGN_INVALID_CREDENTIALS = 0x1300, }; - + enum ACTIVATE_ERROR { - ACTIVATE_NOT_SIGNEDIN = 0x1400 + ACTIVATE_NOT_SIGNEDIN = 0x1400 }; - + enum DEV_ERROR { - DEV_MKPATH = 0x2000, - DEV_MAC_ERROR, - DEV_INVALID_DEVICE_FILE, - DEV_INVALID_DEVICE_KEY_FILE, - DEV_INVALID_DEV_PROPERTY, + DEV_MKPATH = 0x2000, + DEV_MAC_ERROR, + DEV_INVALID_DEVICE_FILE, + DEV_INVALID_DEVICE_KEY_FILE, + DEV_INVALID_DEV_PROPERTY, }; enum USER_ERROR { - USER_MKPATH = 0x3000, - USER_INVALID_ACTIVATION_FILE, - USER_NO_AUTHENTICATION_URL, - USER_NO_PROPERTY, - USER_INVALID_INPUT, + USER_MKPATH = 0x3000, + USER_INVALID_ACTIVATION_FILE, + USER_NO_AUTHENTICATION_URL, + USER_NO_PROPERTY, + USER_INVALID_INPUT, }; enum FULFILL_ITEM_ERROR { - FFI_INVALID_FULFILLMENT_DATA = 0x4000, - FFI_INVALID_LOAN_TOKEN + FFI_INVALID_FULFILLMENT_DATA = 0x4000, + FFI_INVALID_LOAN_TOKEN }; - + enum CLIENT_ERROR { - CLIENT_BAD_PARAM = 0x5000, - CLIENT_INVALID_PKCS12, - CLIENT_INVALID_CERTIFICATE, - CLIENT_NO_PRIV_KEY, - CLIENT_NO_PUB_KEY, - CLIENT_RSA_ERROR, - CLIENT_BAD_CHAINING, - CLIENT_BAD_KEY_SIZE, - CLIENT_BAD_ZIP_FILE, - CLIENT_ZIP_ERROR, - CLIENT_GENERIC_EXCEPTION, - CLIENT_NETWORK_ERROR, - CLIENT_INVALID_PKCS8, - CLIENT_FILE_ERROR, - CLIENT_OSSL_ERROR, - CLIENT_CRYPT_ERROR, - CLIENT_DIGEST_ERROR, - CLIENT_HTTP_ERROR + CLIENT_BAD_PARAM = 0x5000, + CLIENT_INVALID_PKCS12, + CLIENT_INVALID_CERTIFICATE, + CLIENT_NO_PRIV_KEY, + CLIENT_NO_PUB_KEY, + CLIENT_RSA_ERROR, + CLIENT_BAD_CHAINING, + CLIENT_BAD_KEY_SIZE, + CLIENT_BAD_ZIP_FILE, + CLIENT_ZIP_ERROR, + CLIENT_GENERIC_EXCEPTION, + CLIENT_NETWORK_ERROR, + CLIENT_INVALID_PKCS8, + CLIENT_FILE_ERROR, + CLIENT_OSSL_ERROR, + CLIENT_CRYPT_ERROR, + CLIENT_DIGEST_ERROR, + CLIENT_HTTP_ERROR }; enum DRM_REMOVAL_ERROR { - DRM_ERR_ENCRYPTION_KEY = 0x6000, - DRM_VERSION_NOT_SUPPORTED, - DRM_FILE_ERROR, - DRM_FORMAT_NOT_SUPPORTED, - DRM_IN_OUT_EQUALS, - DRM_MISSING_PARAMETER, - DRM_INVALID_KEY_SIZE, - DRM_ERR_ENCRYPTION_KEY_FP, - DRM_INVALID_USER + DRM_ERR_ENCRYPTION_KEY = 0x6000, + DRM_VERSION_NOT_SUPPORTED, + DRM_FILE_ERROR, + DRM_FORMAT_NOT_SUPPORTED, + DRM_IN_OUT_EQUALS, + DRM_MISSING_PARAMETER, + DRM_INVALID_KEY_SIZE, + DRM_ERR_ENCRYPTION_KEY_FP, + DRM_INVALID_USER }; - - #ifndef _NOEXCEPT - #if __STDC_VERSION__ >= 201112L - # define _NOEXCEPT noexcept - # define _NOEXCEPT_(x) noexcept(x) - #else - # define _NOEXCEPT throw() - # define _NOEXCEPT_(x) - #endif - #endif /* !_NOEXCEPT */ - + +#ifndef _NOEXCEPT +#if __STDC_VERSION__ >= 201112L +# define _NOEXCEPT noexcept +# define _NOEXCEPT_(x) noexcept(x) +#else +# define _NOEXCEPT throw() +# define _NOEXCEPT_(x) +#endif +#endif /* !_NOEXCEPT */ + /** * Generic exception class */ class Exception : public std::exception { public: - Exception(int code, const char* message, const char* file, int line): - code(code), line(line), file(file) - { - std::stringstream msg; - msg << "Exception code : 0x" << std::setbase(16) << code << std::endl; - msg << "Message : " << message << std::endl; - if (logLevel >= LG_LOG_DEBUG) - msg << "File : " << file << ":" << std::setbase(10) << line << std::endl; - fullmessage = strdup(msg.str().c_str()); - } + Exception(int code, const char* message, const char* file, int line): + code(code), line(line), file(file) + { + std::stringstream msg; + msg << "Exception code : 0x" << std::setbase(16) << code << std::endl; + msg << "Message : " << message << std::endl; + if (logLevel >= LG_LOG_DEBUG) + msg << "File : " << file << ":" << std::setbase(10) << line << std::endl; + fullmessage = strdup(msg.str().c_str()); + } - Exception(const Exception& other) - { - this->code = other.code; - this->line = line; - this->file = file; - this->fullmessage = strdup(other.fullmessage); - } + Exception(const Exception& other) + { + this->code = other.code; + this->line = line; + this->file = file; + this->fullmessage = strdup(other.fullmessage); + } - ~Exception() _NOEXCEPT - { - free(fullmessage); - } + ~Exception() _NOEXCEPT + { + free(fullmessage); + } - const char * what () const throw () { return fullmessage; } - - int getErrorCode() {return code;} - - private: - int code, line; - const char* message, *file; - char* fullmessage; + const char * what () const throw () { return fullmessage; } + + int getErrorCode() {return code;} + + private: + int code, line; + const char* message, *file; + char* fullmessage; }; /** * @brief Throw an exception */ -#define EXCEPTION(code, message) \ +#define EXCEPTION(code, message) \ {std::stringstream __msg;__msg << message; throw gourou::Exception(code, __msg.str().c_str(), __FILE__, __LINE__);} /** @@ -197,15 +197,15 @@ namespace gourou class StringXMLWriter : public pugi::xml_writer { public: - virtual void write(const void* data, size_t size) - { - result.append(static_cast(data), size); - } + virtual void write(const void* data, size_t size) + { + result.append(static_cast(data), size); + } - const std::string& getResult() {return result;} + const std::string& getResult() {return result;} private: - std::string result; + std::string result; }; static const char* ws = " \t\n\r\f\v"; @@ -215,8 +215,8 @@ namespace gourou */ inline std::string& rtrim(std::string& s, const char* t = ws) { - s.erase(s.find_last_not_of(t) + 1); - return s; + s.erase(s.find_last_not_of(t) + 1); + return s; } /** @@ -224,8 +224,8 @@ namespace gourou */ inline std::string& ltrim(std::string& s, const char* t = ws) { - s.erase(0, s.find_first_not_of(t)); - return s; + s.erase(0, s.find_first_not_of(t)); + return s; } /** @@ -233,7 +233,7 @@ namespace gourou */ inline std::string& trim(std::string& s, const char* t = ws) { - return ltrim(rtrim(s, t), t); + return ltrim(rtrim(s, t), t); } static inline pugi::xml_node getNode(const pugi::xml_node& root, const char* tagName, bool throwOnNull=true) @@ -241,17 +241,17 @@ namespace gourou pugi::xpath_node xpath_node = root.select_node(tagName); if (!xpath_node) - { - if (throwOnNull) - EXCEPTION(GOUROU_TAG_NOT_FOUND, "Tag " << tagName << " not found"); - - return pugi::xml_node(); - } + { + if (throwOnNull) + EXCEPTION(GOUROU_TAG_NOT_FOUND, "Tag " << tagName << " not found"); - return xpath_node.node(); + return pugi::xml_node(); + } + + return xpath_node.node(); } - + /** * @brief Extract text node from tag in document * It can throw an exception if tag does not exists @@ -259,19 +259,19 @@ namespace gourou */ static inline std::string extractTextElem(const pugi::xml_node& root, const char* tagName, bool throwOnNull=true) { - pugi::xml_node node = getNode(root, tagName, throwOnNull); + pugi::xml_node node = getNode(root, tagName, throwOnNull); - node = node.first_child(); + node = node.first_child(); + + if (!node) + { + if (throwOnNull) + EXCEPTION(GOUROU_TAG_NOT_FOUND, "Text element for tag " << tagName << " not found"); - if (!node) - { - if (throwOnNull) - EXCEPTION(GOUROU_TAG_NOT_FOUND, "Text element for tag " << tagName << " not found"); - return ""; - } + } - std::string res = node.value(); + std::string res = node.value(); return trim(res); } @@ -280,23 +280,23 @@ namespace gourou * 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) + const std::string& value, bool throwOnNull=true) { - pugi::xml_node node = getNode(root, tagName, throwOnNull); + pugi::xml_node node = getNode(root, tagName, throwOnNull); - if (!node) - { - if (throwOnNull) - EXCEPTION(GOUROU_TAG_NOT_FOUND, "Text element for tag " << tagName << " not found"); + if (!node) + { + if (throwOnNull) + EXCEPTION(GOUROU_TAG_NOT_FOUND, "Text element for tag " << tagName << " not found"); return; - } + } - node = node.first_child(); + node = node.first_child(); - if (!node) - node.append_child(pugi::node_pcdata).set_value(value.c_str()); - else - node.set_value(value.c_str()); + if (!node) + node.append_child(pugi::node_pcdata).set_value(value.c_str()); + else + node.set_value(value.c_str()); } /** @@ -306,20 +306,20 @@ namespace gourou */ static inline std::string extractTextAttribute(const pugi::xml_node& root, const char* tagName, const char* attributeName, bool throwOnNull=true) { - pugi::xml_node node = getNode(root, tagName, throwOnNull); + pugi::xml_node node = getNode(root, tagName, throwOnNull); - pugi::xml_attribute attr = node.attribute(attributeName); + pugi::xml_attribute attr = node.attribute(attributeName); + + if (!attr) + { + if (throwOnNull) + EXCEPTION(GOUROU_TAG_NOT_FOUND, "Attribute element " << attributeName << " for tag " << tagName << " not found"); - if (!attr) - { - if (throwOnNull) - EXCEPTION(GOUROU_TAG_NOT_FOUND, "Attribute element " << attributeName << " for tag " << tagName << " not found"); - return ""; - } + } - std::string res = attr.value(); - return trim(res); + std::string res = attr.value(); + return trim(res); } /** @@ -331,8 +331,8 @@ namespace gourou */ static inline void appendTextElem(pugi::xml_node& root, const std::string& name, const std::string& value) { - pugi::xml_node node = root.append_child(name.c_str()); - node.append_child(pugi::node_pcdata).set_value(value.c_str()); + pugi::xml_node node = root.append_child(name.c_str()); + node.append_child(pugi::node_pcdata).set_value(value.c_str()); } /** @@ -343,21 +343,21 @@ namespace gourou */ static inline std::string extractIdFromUUID(const std::string& uuid) { - unsigned int i = 0; - std::string res; - - if (uuid.find("urn:uuid:") == 0) - i = 9; + unsigned int i = 0; + std::string res; - for(; i properties; + std::map properties; }; } diff --git a/include/user.h b/include/user.h index 4966dc5..8240dab 100644 --- a/include/user.h +++ b/include/user.h @@ -30,7 +30,7 @@ namespace gourou { class DRMProcessor; - + /** * @brief This class is a container for activation.xml (activation info). It should not be used by user. */ @@ -39,73 +39,73 @@ namespace gourou public: User(DRMProcessor* processor, const std::string& activationFile); - /** - * @brief Retrieve some values from activation.xml - */ - std::string& getUUID(); - std::string& getPKCS12(); - std::string& getDeviceUUID(); - std::string& getDeviceFingerprint(); - std::string& getUsername(); - std::string& getLoginMethod(); - std::string getLicenseServiceCertificate(std::string url); - std::string& getAuthenticationCertificate(); - std::string& getPrivateLicenseKey(); + /** + * @brief Retrieve some values from activation.xml + */ + std::string& getUUID(); + std::string& getPKCS12(); + std::string& getDeviceUUID(); + std::string& getDeviceFingerprint(); + std::string& getUsername(); + std::string& getLoginMethod(); + std::string getLicenseServiceCertificate(std::string url); + std::string& getAuthenticationCertificate(); + std::string& getPrivateLicenseKey(); - /** - * @brief Read activation.xml and put result into doc - */ - void readActivation(pugi::xml_document& doc); + /** + * @brief Read activation.xml and put result into doc + */ + void readActivation(pugi::xml_document& doc); - /** - * @brief Update activation.xml with new data - */ - void updateActivationFile(const char* data); + /** + * @brief Update activation.xml with new data + */ + void updateActivationFile(const char* data); - /** - * @brief Update activation.xml with doc data - */ - void updateActivationFile(const pugi::xml_document& doc); + /** + * @brief Update activation.xml with doc data + */ + void updateActivationFile(const pugi::xml_document& doc); - /** - * @brief Get one value of activation.xml - */ - std::string getProperty(const std::string property); - - /** - * @brief Get all nodes with property name - */ - pugi::xpath_node_set getProperties(const std::string property); - - /** - * @brief Create activation.xml and devicesalt files if they did not exists - * - * @param processor Instance of DRMProcessor - * @param dirName Directory where to put files (.adept) - * @param ACSServer Server used for signIn - */ - static User* createUser(DRMProcessor* processor, const std::string& dirName, const std::string& ACSServer); + /** + * @brief Get one value of activation.xml + */ + std::string getProperty(const std::string property); + + /** + * @brief Get all nodes with property name + */ + pugi::xpath_node_set getProperties(const std::string property); + + /** + * @brief Create activation.xml and devicesalt files if they did not exists + * + * @param processor Instance of DRMProcessor + * @param dirName Directory where to put files (.adept) + * @param ACSServer Server used for signIn + */ + static User* createUser(DRMProcessor* processor, const std::string& dirName, const std::string& ACSServer); private: - DRMProcessor* processor; - pugi::xml_document activationDoc; - - std::string activationFile; - std::string pkcs12; - std::string uuid; - std::string deviceUUID; - std::string deviceFingerprint; - std::string username; - std::string loginMethod; - std::map licenseServiceCertificates; - std::string authenticationCertificate; - std::string privateLicenseKey; + DRMProcessor* processor; + pugi::xml_document activationDoc; - User(DRMProcessor* processor); - - void parseActivationFile(bool throwOnNull=true); - ByteArray signIn(const std::string& adobeID, const std::string& adobePassword, - ByteArray authenticationCertificate); + std::string activationFile; + std::string pkcs12; + std::string uuid; + std::string deviceUUID; + std::string deviceFingerprint; + std::string username; + std::string loginMethod; + std::map licenseServiceCertificates; + std::string authenticationCertificate; + std::string privateLicenseKey; + + User(DRMProcessor* processor); + + void parseActivationFile(bool throwOnNull=true); + ByteArray signIn(const std::string& adobeID, const std::string& adobePassword, + ByteArray authenticationCertificate); }; } diff --git a/src/bytearray.cpp b/src/bytearray.cpp index f7ec87f..fb8fd54 100644 --- a/src/bytearray.cpp +++ b/src/bytearray.cpp @@ -26,232 +26,232 @@ namespace gourou { std::map ByteArray::refCounter; - + ByteArray::ByteArray(bool useMalloc):_useMalloc(useMalloc), _data(0), _length(0) {} ByteArray::ByteArray(unsigned int length, bool useMalloc): - _useMalloc(useMalloc) + _useMalloc(useMalloc) { - initData(0, length); + initData(0, length); } - - ByteArray::ByteArray(const unsigned char* data, unsigned int length): - _useMalloc(false) - { - initData(data, length); - } - - ByteArray::ByteArray(const char* data, int length): - _useMalloc(false) - { - if (length == -1) - length = strlen(data); - initData((unsigned char*)data, (unsigned int) length); - } - - ByteArray::ByteArray(const std::string& str): - _useMalloc(false) + ByteArray::ByteArray(const unsigned char* data, unsigned int length): + _useMalloc(false) { - initData((unsigned char*)str.c_str(), (unsigned int)str.length()); + initData(data, length); + } + + ByteArray::ByteArray(const char* data, int length): + _useMalloc(false) + { + if (length == -1) + length = strlen(data); + + initData((unsigned char*)data, (unsigned int) length); + } + + ByteArray::ByteArray(const std::string& str): + _useMalloc(false) + { + initData((unsigned char*)str.c_str(), (unsigned int)str.length()); } void ByteArray::initData(const unsigned char* data, unsigned int length) { - if (_useMalloc) - _data = (unsigned char*)malloc(length); - else - _data = new unsigned char[length]; + if (_useMalloc) + _data = (unsigned char*)malloc(length); + else + _data = new unsigned char[length]; - if (data) - memcpy((void*)_data, data, length); + if (data) + memcpy((void*)_data, data, length); - _length = length; + _length = length; - addRef(); + addRef(); } - + ByteArray::ByteArray(const ByteArray& other) { - this->_useMalloc = other._useMalloc; - this->_data = other._data; - this->_length = other._length; + this->_useMalloc = other._useMalloc; + this->_data = other._data; + this->_length = other._length; - addRef(); + addRef(); } ByteArray& ByteArray::operator=(const ByteArray& other) { - delRef(); - - this->_useMalloc = other._useMalloc; - this->_data = other._data; - this->_length = other._length; + delRef(); - addRef(); - - return *this; + this->_useMalloc = other._useMalloc; + this->_data = other._data; + this->_length = other._length; + + addRef(); + + return *this; } - + ByteArray::~ByteArray() { - delRef(); + delRef(); } - + void ByteArray::addRef() { - if (!_data) return; + if (!_data) return; - if (refCounter.count(_data) == 0) - refCounter[_data] = 1; - else - refCounter[_data]++; + if (refCounter.count(_data) == 0) + refCounter[_data] = 1; + else + refCounter[_data]++; } - + void ByteArray::delRef() { - if (!_data) return; - - if (refCounter[_data] == 1) - { - if (_useMalloc) - free(_data); - else - delete[] _data; - refCounter.erase(_data); - } - else - refCounter[_data]--; + if (!_data) return; + + if (refCounter[_data] == 1) + { + if (_useMalloc) + free(_data); + else + delete[] _data; + refCounter.erase(_data); + } + else + refCounter[_data]--; } ByteArray ByteArray::fromBase64(const ByteArray& other) { - std::string b64; + std::string b64; - macaron::Base64::Decode(std::string((char*)other._data, other._length), b64); + macaron::Base64::Decode(std::string((char*)other._data, other._length), b64); - return ByteArray(b64); + return ByteArray(b64); } - + ByteArray ByteArray::fromBase64(const char* data, int length) { - std::string b64; + std::string b64; - if (length == -1) - length = strlen(data); - - macaron::Base64::Decode(std::string(data, length), b64); + if (length == -1) + length = strlen(data); - return ByteArray(b64); + macaron::Base64::Decode(std::string(data, length), b64); + + return ByteArray(b64); } - + ByteArray ByteArray::fromBase64(const std::string& str) { - return ByteArray::fromBase64(str.c_str(), str.length()); + return ByteArray::fromBase64(str.c_str(), str.length()); } - + std::string ByteArray::toBase64() { - return macaron::Base64::Encode(std::string((char*)_data, _length)); + return macaron::Base64::Encode(std::string((char*)_data, _length)); } ByteArray ByteArray::fromHex(const std::string& str) { - if (str.size() % 2) - throw std::invalid_argument("Size of hex string not multiple of 2"); + if (str.size() % 2) + throw std::invalid_argument("Size of hex string not multiple of 2"); - ByteArray res((unsigned int)(str.size()/2)); - unsigned int i; + ByteArray res((unsigned int)(str.size()/2)); + unsigned int i; - unsigned char* data = res.data(); - unsigned char cur, tmp; - - for (i=0; i= 'a' && tmp <= 'f') - cur = (tmp - 'a' + 10) << 4; - else if (tmp >= 'A' && tmp <= 'F') - cur = (tmp - 'A' + 10) << 4; - else if (tmp >= '0' && tmp <= '9') - cur = (tmp - '0') << 4; - else - throw std::invalid_argument("Invalid character in hex string"); + for (i=0; i= 'a' && tmp <= 'f') - cur += tmp - 'a' + 10; - else if (tmp >= 'A' && tmp <= 'F') - cur += tmp - 'A' + 10; - else if (tmp >= '0' && tmp <= '9') - cur += tmp - '0'; - else - throw std::invalid_argument("Invalid character in hex string"); + tmp = str[i]; + if (tmp >= 'a' && tmp <= 'f') + cur = (tmp - 'a' + 10) << 4; + else if (tmp >= 'A' && tmp <= 'F') + cur = (tmp - 'A' + 10) << 4; + else if (tmp >= '0' && tmp <= '9') + cur = (tmp - '0') << 4; + else + throw std::invalid_argument("Invalid character in hex string"); - data[i/2] = cur; - } - - return res; + tmp = str[i+1]; + if (tmp >= 'a' && tmp <= 'f') + cur += tmp - 'a' + 10; + else if (tmp >= 'A' && tmp <= 'F') + cur += tmp - 'A' + 10; + else if (tmp >= '0' && tmp <= '9') + cur += tmp - '0'; + else + throw std::invalid_argument("Invalid character in hex string"); + + data[i/2] = cur; + } + + return res; } - + std::string ByteArray::toHex() { - char* tmp = new char[_length*2+1]; + char* tmp = new char[_length*2+1]; - for(int i=0; i<(int)_length; i++) - sprintf(&tmp[i*2], "%02x", _data[i]); + for(int i=0; i<(int)_length; i++) + sprintf(&tmp[i*2], "%02x", _data[i]); - tmp[_length*2] = 0; + tmp[_length*2] = 0; - std::string res = tmp; - delete[] tmp; - - return res; + std::string res = tmp; + delete[] tmp; + + return res; } void ByteArray::append(const unsigned char* data, unsigned int length) { - if (!length) - return; - - unsigned int oldLength = _length; + if (!length) + return; - resize(_length+length, true); + unsigned int oldLength = _length; - memcpy(&_data[oldLength], data, length); + resize(_length+length, true); + + memcpy(&_data[oldLength], data, length); } - + void ByteArray::append(unsigned char c) { append(&c, 1);} void ByteArray::append(const char* str) { append((const unsigned char*)str, strlen(str));} void ByteArray::append(const std::string& str) { append((const unsigned char*)str.c_str(), str.length()); } void ByteArray::resize(unsigned length, bool keepData) { - if (length == _length) - return; - else if (length < _length) - _length = length ; // Don't touch data - else // New size > - { - unsigned char* newData; + if (length == _length) + return; + else if (length < _length) + _length = length ; // Don't touch data + else // New size > + { + unsigned char* newData; - if (_useMalloc) - newData = (unsigned char*)malloc(_length+length); - else - newData = new unsigned char[_length+length]; + if (_useMalloc) + newData = (unsigned char*)malloc(_length+length); + else + newData = new unsigned char[_length+length]; - if (keepData) - memcpy(newData, _data, _length); - - delRef(); + if (keepData) + memcpy(newData, _data, _length); - _length = length; - _data = newData; - - addRef(); - } + delRef(); + + _length = length; + _data = newData; + + addRef(); + } } } diff --git a/src/device.cpp b/src/device.cpp index 1418d47..75e603e 100644 --- a/src/device.cpp +++ b/src/device.cpp @@ -36,7 +36,7 @@ #include #include #elif (defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \ - || defined(__bsdi__) || defined(__DragonFly__) || defined(__APPLE__)) + || defined(__bsdi__) || defined(__DragonFly__) || defined(__APPLE__)) #include #include #include @@ -78,8 +78,8 @@ int get_mac_address(unsigned char* mac_address) if (success) { - memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6); - return 0; + memcpy(mac_address, ifr.ifr_hwaddr.sa_data, 6); + return 0; } return 1; @@ -110,7 +110,7 @@ int get_mac_address(unsigned char* mac_address, const char* if_name = "en0") int get_mac_address(unsigned char* mac_address) { GOUROU_LOG(INFO, "get_mac_address() not implemented for your platform, using a static address"); - + mac_address[0] = 0x8D; mac_address[1] = 0x70; mac_address[2] = 0x13; @@ -126,227 +126,227 @@ int get_mac_address(unsigned char* mac_address) namespace gourou { Device::Device(DRMProcessor* processor): - processor(processor) + processor(processor) {} - + Device::Device(DRMProcessor* processor, const std::string& deviceFile, const std::string& deviceKeyFile): - processor(processor), deviceFile(deviceFile), deviceKeyFile(deviceKeyFile) + processor(processor), deviceFile(deviceFile), deviceKeyFile(deviceKeyFile) { - parseDeviceKeyFile(); - parseDeviceFile(); + parseDeviceKeyFile(); + parseDeviceFile(); } /* SHA1(uid ":" username ":" macaddress ":" */ std::string Device::makeSerial(bool random) { - unsigned char sha_out[SHA1_LEN]; - DRMProcessorClient* client = processor->getClient(); - - if (!random) - { - uid_t uid = getuid(); - struct passwd * passwd = getpwuid(uid); - // Default mac address in case of failure - unsigned char mac_address[6] = {0x01, 0x02, 0x03, 0x04, 0x05}; + unsigned char sha_out[SHA1_LEN]; + DRMProcessorClient* client = processor->getClient(); - get_mac_address(mac_address); + if (!random) + { + uid_t uid = getuid(); + struct passwd * passwd = getpwuid(uid); + // Default mac address in case of failure + unsigned char mac_address[6] = {0x01, 0x02, 0x03, 0x04, 0x05}; - int dataToHashLen = 10 /* UID */ + strlen(passwd->pw_name) + sizeof(mac_address)*2 /*mac address*/ + 1 /* \0 */; - dataToHashLen += 8; /* Separators */ - unsigned char* dataToHash = new unsigned char[dataToHashLen]; - dataToHashLen = snprintf((char*)dataToHash, dataToHashLen, "%d:%s:%02x:%02x:%02x:%02x:%02x:%02x:", - uid, passwd->pw_name, - mac_address[0], mac_address[1], mac_address[2], - mac_address[3], mac_address[4], mac_address[5]); - - client->digest("SHA1", dataToHash, dataToHashLen+1, sha_out); + get_mac_address(mac_address); - delete[] dataToHash; - } - else - { - client->randBytes(sha_out, sizeof(sha_out)); - } + int dataToHashLen = 10 /* UID */ + strlen(passwd->pw_name) + sizeof(mac_address)*2 /*mac address*/ + 1 /* \0 */; + dataToHashLen += 8; /* Separators */ + unsigned char* dataToHash = new unsigned char[dataToHashLen]; + dataToHashLen = snprintf((char*)dataToHash, dataToHashLen, "%d:%s:%02x:%02x:%02x:%02x:%02x:%02x:", + uid, passwd->pw_name, + mac_address[0], mac_address[1], mac_address[2], + mac_address[3], mac_address[4], mac_address[5]); - std::string res = ByteArray((const char*)sha_out, DEVICE_SERIAL_LEN).toHex(); - GOUROU_LOG(DEBUG, "Serial : " << res); - return res; + client->digest("SHA1", dataToHash, dataToHashLen+1, sha_out); + + delete[] dataToHash; + } + else + { + client->randBytes(sha_out, sizeof(sha_out)); + } + + std::string res = ByteArray((const char*)sha_out, DEVICE_SERIAL_LEN).toHex(); + GOUROU_LOG(DEBUG, "Serial : " << res); + return res; } /* base64(SHA1 (serial + privateKey)) */ std::string Device::makeFingerprint(const std::string& serial) { - DRMProcessorClient* client = processor->getClient(); - unsigned char sha_out[SHA1_LEN]; + DRMProcessorClient* client = processor->getClient(); + unsigned char sha_out[SHA1_LEN]; - void* handler = client->createDigest("SHA1"); - client->digestUpdate(handler, (unsigned char*) serial.c_str(), serial.length()); - client->digestUpdate(handler, deviceKey, sizeof(deviceKey)); - client->digestFinalize(handler, sha_out); + void* handler = client->createDigest("SHA1"); + client->digestUpdate(handler, (unsigned char*) serial.c_str(), serial.length()); + client->digestUpdate(handler, deviceKey, sizeof(deviceKey)); + client->digestFinalize(handler, sha_out); - std::string res = ByteArray(sha_out, sizeof(sha_out)).toBase64(); - GOUROU_LOG(DEBUG, "Fingerprint : " << res); - return res; + std::string res = ByteArray(sha_out, sizeof(sha_out)).toBase64(); + GOUROU_LOG(DEBUG, "Fingerprint : " << res); + return res; } - + void Device::createDeviceFile(const std::string& hobbes, bool randomSerial) { - struct utsname sysname; - uname(&sysname); + struct utsname sysname; + uname(&sysname); - std::string serial = makeSerial(randomSerial); - std::string fingerprint = makeFingerprint(serial); - - pugi::xml_document deviceDoc; - pugi::xml_node decl = deviceDoc.append_child(pugi::node_declaration); - decl.append_attribute("version") = "1.0"; - - pugi::xml_node root = deviceDoc.append_child("adept:deviceInfo"); - root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS; + std::string serial = makeSerial(randomSerial); + std::string fingerprint = makeFingerprint(serial); - appendTextElem(root, "adept:deviceClass", "Desktop"); - appendTextElem(root, "adept:deviceSerial", serial); - appendTextElem(root, "adept:deviceName", sysname.nodename); - appendTextElem(root, "adept:deviceType", "standalone"); + pugi::xml_document deviceDoc; + pugi::xml_node decl = deviceDoc.append_child(pugi::node_declaration); + decl.append_attribute("version") = "1.0"; - pugi::xml_node version = root.append_child("adept:version"); - version.append_attribute("name") = "hobbes"; - version.append_attribute("value") = hobbes.c_str(); - - version = root.append_child("adept:version"); - version.append_attribute("name") = "clientOS"; - std::string os = std::string(sysname.sysname) + " " + std::string(sysname.release); - version.append_attribute("value") = os.c_str(); - - version = root.append_child("adept:version"); - version.append_attribute("name") = "clientLocale"; - version.append_attribute("value") = setlocale(LC_ALL, NULL); + pugi::xml_node root = deviceDoc.append_child("adept:deviceInfo"); + root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS; - appendTextElem(root, "adept:fingerprint", fingerprint); + appendTextElem(root, "adept:deviceClass", "Desktop"); + appendTextElem(root, "adept:deviceSerial", serial); + appendTextElem(root, "adept:deviceName", sysname.nodename); + appendTextElem(root, "adept:deviceType", "standalone"); - StringXMLWriter xmlWriter; - deviceDoc.save(xmlWriter, " "); + pugi::xml_node version = root.append_child("adept:version"); + version.append_attribute("name") = "hobbes"; + version.append_attribute("value") = hobbes.c_str(); - GOUROU_LOG(DEBUG, "Create device file " << deviceFile); + version = root.append_child("adept:version"); + version.append_attribute("name") = "clientOS"; + std::string os = std::string(sysname.sysname) + " " + std::string(sysname.release); + version.append_attribute("value") = os.c_str(); - writeFile(deviceFile, xmlWriter.getResult()); + version = root.append_child("adept:version"); + version.append_attribute("name") = "clientLocale"; + version.append_attribute("value") = setlocale(LC_ALL, NULL); + + appendTextElem(root, "adept:fingerprint", fingerprint); + + StringXMLWriter xmlWriter; + deviceDoc.save(xmlWriter, " "); + + GOUROU_LOG(DEBUG, "Create device file " << deviceFile); + + writeFile(deviceFile, xmlWriter.getResult()); } void Device::createDeviceKeyFile() { - unsigned char key[DEVICE_KEY_SIZE]; + unsigned char key[DEVICE_KEY_SIZE]; - GOUROU_LOG(DEBUG, "Create device key file " << deviceKeyFile); + GOUROU_LOG(DEBUG, "Create device key file " << deviceKeyFile); - processor->getClient()->randBytes(key, sizeof(key)); + processor->getClient()->randBytes(key, sizeof(key)); - writeFile(deviceKeyFile, key, sizeof(key)); + writeFile(deviceKeyFile, key, sizeof(key)); } - + Device* Device::createDevice(DRMProcessor* processor, const std::string& dirName, const std::string& hobbes, bool randomSerial) { - struct stat _stat; + struct stat _stat; - if (stat(dirName.c_str(), &_stat) != 0) - { - if (mkdir_p(dirName.c_str(), S_IRWXU)) - EXCEPTION(DEV_MKPATH, "Unable to create " << dirName) - } + if (stat(dirName.c_str(), &_stat) != 0) + { + if (mkdir_p(dirName.c_str(), S_IRWXU)) + EXCEPTION(DEV_MKPATH, "Unable to create " << dirName) + } - Device* device = new Device(processor); + Device* device = new Device(processor); - device->deviceFile = dirName + "/device.xml"; - device->deviceKeyFile = dirName + "/devicesalt"; + device->deviceFile = dirName + "/device.xml"; + device->deviceKeyFile = dirName + "/devicesalt"; - try - { - device->parseDeviceKeyFile(); - } - catch (...) - { - device->createDeviceKeyFile(); - device->parseDeviceKeyFile(); - } + try + { + device->parseDeviceKeyFile(); + } + catch (...) + { + device->createDeviceKeyFile(); + device->parseDeviceKeyFile(); + } - try - { - device->parseDeviceFile(); - } - catch (...) - { - device->createDeviceFile(hobbes, randomSerial); - device->parseDeviceFile(); - } - - return device; + try + { + device->parseDeviceFile(); + } + catch (...) + { + device->createDeviceFile(hobbes, randomSerial); + device->parseDeviceFile(); + } + + return device; } - + const unsigned char* Device::getDeviceKey() { - return deviceKey; + return deviceKey; } void Device::parseDeviceFile() { - pugi::xml_document doc; + pugi::xml_document doc; - if (!doc.load_file(deviceFile.c_str())) - EXCEPTION(DEV_INVALID_DEVICE_FILE, "Invalid device file"); + if (!doc.load_file(deviceFile.c_str())) + EXCEPTION(DEV_INVALID_DEVICE_FILE, "Invalid device file"); - try - { - properties["deviceClass"] = gourou::extractTextElem(doc, "/adept:deviceInfo/adept:deviceClass"); - properties["deviceSerial"] = gourou::extractTextElem(doc, "/adept:deviceInfo/adept:deviceSerial"); - properties["deviceName"] = gourou::extractTextElem(doc, "/adept:deviceInfo/adept:deviceName"); - properties["deviceType"] = gourou::extractTextElem(doc, "/adept:deviceInfo/adept:deviceType"); - properties["fingerprint"] = gourou::extractTextElem(doc, "/adept:deviceInfo/adept:fingerprint"); + try + { + properties["deviceClass"] = gourou::extractTextElem(doc, "/adept:deviceInfo/adept:deviceClass"); + properties["deviceSerial"] = gourou::extractTextElem(doc, "/adept:deviceInfo/adept:deviceSerial"); + properties["deviceName"] = gourou::extractTextElem(doc, "/adept:deviceInfo/adept:deviceName"); + properties["deviceType"] = gourou::extractTextElem(doc, "/adept:deviceInfo/adept:deviceType"); + properties["fingerprint"] = gourou::extractTextElem(doc, "/adept:deviceInfo/adept:fingerprint"); - pugi::xpath_node_set nodeSet = doc.select_nodes("/adept:deviceInfo/adept:version"); + pugi::xpath_node_set nodeSet = doc.select_nodes("/adept:deviceInfo/adept:version"); - for (pugi::xpath_node_set::const_iterator it = nodeSet.begin(); - it != nodeSet.end(); ++it) - { - pugi::xml_node node = it->node(); - pugi::xml_attribute name = node.attribute("name"); - pugi::xml_attribute value = node.attribute("value"); + for (pugi::xpath_node_set::const_iterator it = nodeSet.begin(); + it != nodeSet.end(); ++it) + { + pugi::xml_node node = it->node(); + pugi::xml_attribute name = node.attribute("name"); + pugi::xml_attribute value = node.attribute("value"); - properties[name.value()] = value.value(); - } - } - catch (gourou::Exception& e) - { - EXCEPTION(DEV_INVALID_DEVICE_FILE, "Invalid device file"); - } + properties[name.value()] = value.value(); + } + } + catch (gourou::Exception& e) + { + EXCEPTION(DEV_INVALID_DEVICE_FILE, "Invalid device file"); + } } void Device::parseDeviceKeyFile() { - struct stat _stat; + struct stat _stat; - if (stat(deviceKeyFile.c_str(), &_stat) == 0 && - _stat.st_size == DEVICE_KEY_SIZE) - { - readFile(deviceKeyFile, deviceKey, sizeof(deviceKey)); - } - else - EXCEPTION(DEV_INVALID_DEVICE_KEY_FILE, "Invalid device key file"); + if (stat(deviceKeyFile.c_str(), &_stat) == 0 && + _stat.st_size == DEVICE_KEY_SIZE) + { + readFile(deviceKeyFile, deviceKey, sizeof(deviceKey)); + } + else + EXCEPTION(DEV_INVALID_DEVICE_KEY_FILE, "Invalid device key file"); } std::string Device::getProperty(const std::string& property, const std::string& _default) { - if (properties.find(property) == properties.end()) - { - if (_default == "") - EXCEPTION(DEV_INVALID_DEV_PROPERTY, "Invalid property " << property); + if (properties.find(property) == properties.end()) + { + if (_default == "") + EXCEPTION(DEV_INVALID_DEV_PROPERTY, "Invalid property " << property); - return _default; - } + return _default; + } - return properties[property]; + return properties[property]; } std::string Device::operator[](const std::string& property) { - return getProperty(property); + return getProperty(property); } } diff --git a/src/fulfillment_item.cpp b/src/fulfillment_item.cpp index dbd8d31..c3ac17f 100644 --- a/src/fulfillment_item.cpp +++ b/src/fulfillment_item.cpp @@ -25,109 +25,109 @@ namespace gourou { FulfillmentItem::FulfillmentItem(pugi::xml_document& doc, User* user) - : fulfillDoc(), loanToken(0) + : fulfillDoc(), loanToken(0) { - fulfillDoc.reset(doc); /* We must keep a copy */ - metadatas = fulfillDoc.select_node("//metadata").node(); - - if (!metadatas) - EXCEPTION(FFI_INVALID_FULFILLMENT_DATA, "No metadata tag in document"); - - pugi::xml_node node = doc.select_node("/envelope/fulfillmentResult/resourceItemInfo/src").node(); - downloadURL = node.first_child().value(); + fulfillDoc.reset(doc); /* We must keep a copy */ + metadatas = fulfillDoc.select_node("//metadata").node(); - if (downloadURL == "") - EXCEPTION(FFI_INVALID_FULFILLMENT_DATA, "No download URL in document"); - - node = doc.select_node("/envelope/fulfillmentResult/resourceItemInfo/resource").node(); - resource = node.first_child().value(); + if (!metadatas) + EXCEPTION(FFI_INVALID_FULFILLMENT_DATA, "No metadata tag in document"); - if (resource == "") - EXCEPTION(FFI_INVALID_FULFILLMENT_DATA, "No resource in document"); + pugi::xml_node node = doc.select_node("/envelope/fulfillmentResult/resourceItemInfo/src").node(); + downloadURL = node.first_child().value(); - pugi::xml_node licenseToken = doc.select_node("/envelope/fulfillmentResult/resourceItemInfo/licenseToken").node(); + if (downloadURL == "") + EXCEPTION(FFI_INVALID_FULFILLMENT_DATA, "No download URL in document"); - if (!licenseToken) - EXCEPTION(FFI_INVALID_FULFILLMENT_DATA, "Any license token in document"); - - buildRights(licenseToken, user); + node = doc.select_node("/envelope/fulfillmentResult/resourceItemInfo/resource").node(); + resource = node.first_child().value(); - 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()); - } + if (resource == "") + EXCEPTION(FFI_INVALID_FULFILLMENT_DATA, "No resource in document"); + + pugi::xml_node licenseToken = doc.select_node("/envelope/fulfillmentResult/resourceItemInfo/licenseToken").node(); + + if (!licenseToken) + 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; + if (loanToken) delete loanToken; } - + void FulfillmentItem::buildRights(const pugi::xml_node& licenseToken, User* user) { - pugi::xml_node decl = rights.append_child(pugi::node_declaration); - decl.append_attribute("version") = "1.0"; - - pugi::xml_node root = rights.append_child("adept:rights"); - root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS; - - pugi::xml_node newLicenseToken = root.append_copy(licenseToken); - if (!newLicenseToken.attribute("xmlns")) - newLicenseToken.append_attribute("xmlns") = ADOBE_ADEPT_NS; + pugi::xml_node decl = rights.append_child(pugi::node_declaration); + decl.append_attribute("version") = "1.0"; - pugi::xml_node licenseServiceInfo = root.append_child("adept:licenseServiceInfo"); - pugi::xml_node licenseURL = licenseToken.select_node("licenseURL").node(); - licenseURL.set_name("adept:licenseURL"); - licenseServiceInfo.append_copy(licenseURL); - pugi::xml_node certificate = licenseServiceInfo.append_child("adept:certificate"); - std::string certificateValue = user->getLicenseServiceCertificate(licenseURL.first_child().value()); - certificate.append_child(pugi::node_pcdata).set_value(certificateValue.c_str()); + pugi::xml_node root = rights.append_child("adept:rights"); + root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS; + + pugi::xml_node newLicenseToken = root.append_copy(licenseToken); + if (!newLicenseToken.attribute("xmlns")) + newLicenseToken.append_attribute("xmlns") = ADOBE_ADEPT_NS; + + pugi::xml_node licenseServiceInfo = root.append_child("adept:licenseServiceInfo"); + pugi::xml_node licenseURL = licenseToken.select_node("licenseURL").node(); + licenseURL.set_name("adept:licenseURL"); + licenseServiceInfo.append_copy(licenseURL); + pugi::xml_node certificate = licenseServiceInfo.append_child("adept:certificate"); + std::string certificateValue = user->getLicenseServiceCertificate(licenseURL.first_child().value()); + certificate.append_child(pugi::node_pcdata).set_value(certificateValue.c_str()); } - + std::string FulfillmentItem::getMetadata(std::string name) { - // https://stackoverflow.com/questions/313970/how-to-convert-an-instance-of-stdstring-to-lower-case - #if __STDC_VERSION__ >= 201112L - std::transform(name.begin(), name.end(), name.begin(), - [](unsigned char c){ return std::tolower(c); }); - #else - std::transform(name.begin(), name.end(), name.begin(), tolower); - #endif - name = std::string("dc:") + name; - pugi::xpath_node path = metadatas.select_node(name.c_str()); + // https://stackoverflow.com/questions/313970/how-to-convert-an-instance-of-stdstring-to-lower-case +#if __STDC_VERSION__ >= 201112L + std::transform(name.begin(), name.end(), name.begin(), + [](unsigned char c){ return std::tolower(c); }); +#else + std::transform(name.begin(), name.end(), name.begin(), tolower); +#endif + name = std::string("dc:") + name; + pugi::xpath_node path = metadatas.select_node(name.c_str()); - if (!path) - return ""; + if (!path) + return ""; - return path.node().first_child().value(); + return path.node().first_child().value(); } - + std::string FulfillmentItem::getRights() { - StringXMLWriter xmlWriter; - rights.save(xmlWriter, " "); - return xmlWriter.getResult(); + StringXMLWriter xmlWriter; + rights.save(xmlWriter, " "); + return xmlWriter.getResult(); } - + std::string FulfillmentItem::getDownloadURL() { - return downloadURL; + return downloadURL; } std::string FulfillmentItem::getResource() { - return resource; + return resource; } LoanToken* FulfillmentItem::getLoanToken() { - return loanToken; + return loanToken; } } diff --git a/src/libgourou.cpp b/src/libgourou.cpp index 4a1da8a..d9b15ce 100644 --- a/src/libgourou.cpp +++ b/src/libgourou.cpp @@ -42,1027 +42,1027 @@ namespace gourou { GOUROU_LOG_LEVEL logLevel = LG_LOG_WARN; const std::string DRMProcessor::VERSION = LIBGOUROU_VERSION; - + DRMProcessor::DRMProcessor(DRMProcessorClient* client):client(client), device(0), user(0) { - if (!client) - EXCEPTION(GOUROU_INVALID_CLIENT, "DRMProcessorClient is NULL"); + if (!client) + EXCEPTION(GOUROU_INVALID_CLIENT, "DRMProcessorClient is NULL"); } DRMProcessor::DRMProcessor(DRMProcessorClient* client, - const std::string& deviceFile, const std::string& activationFile, - const std::string& deviceKeyFile): - client(client), device(0), user(0) + const std::string& deviceFile, const std::string& activationFile, + const std::string& deviceKeyFile): + client(client), device(0), user(0) { - if (!client) - EXCEPTION(GOUROU_INVALID_CLIENT, "DRMProcessorClient is NULL"); - - device = new Device(this, deviceFile, deviceKeyFile); - user = new User(this, activationFile); + if (!client) + EXCEPTION(GOUROU_INVALID_CLIENT, "DRMProcessorClient is NULL"); - if (user->getDeviceFingerprint() != "" && - (*device)["fingerprint"] != user->getDeviceFingerprint()) - EXCEPTION(GOUROU_DEVICE_DOES_NOT_MATCH, "User and device fingerprint does not match"); + device = new Device(this, deviceFile, deviceKeyFile); + user = new User(this, activationFile); + + if (user->getDeviceFingerprint() != "" && + (*device)["fingerprint"] != user->getDeviceFingerprint()) + EXCEPTION(GOUROU_DEVICE_DOES_NOT_MATCH, "User and device fingerprint does not match"); } DRMProcessor::~DRMProcessor() { - if (device) delete device; - if (user) delete user; + if (device) delete device; + if (user) delete user; } // function to parse a date or time string. // https://www.geeksforgeeks.org/cpp/date-and-time-parsing-in-cpp/ static time_t parseDateTime(const char* datetimeString, const char* format) { - struct tm tmStruct; - strptime(datetimeString, format, &tmStruct); - return mktime(&tmStruct); + struct tm tmStruct; + strptime(datetimeString, format, &tmStruct); + return mktime(&tmStruct); } - + DRMProcessor* DRMProcessor::createDRMProcessor(DRMProcessorClient* client, bool randomSerial, std::string dirName, - const std::string& hobbes, const std::string& ACSServer) + const std::string& hobbes, const std::string& ACSServer) { - DRMProcessor* processor = new DRMProcessor(client); + DRMProcessor* processor = new DRMProcessor(client); - if (dirName == "") - dirName = getDefaultAdeptDir(); - - Device* device = Device::createDevice(processor, dirName, hobbes, randomSerial); - processor->device = device; + if (dirName == "") + dirName = getDefaultAdeptDir(); - User* user = User::createUser(processor, dirName, ACSServer); - processor->user = user; - - return processor; + Device* device = Device::createDevice(processor, dirName, hobbes, randomSerial); + processor->device = device; + + User* user = User::createUser(processor, dirName, ACSServer); + processor->user = user; + + return processor; } - + void DRMProcessor::pushString(void* sha_ctx, const std::string& string) { - int length = string.length(); - uint16_t nlength = htons(length); - char c; + int length = string.length(); + uint16_t nlength = htons(length); + char c; - if (logLevel >= LG_LOG_TRACE) - printf("%02x %02x ", ((uint8_t*)&nlength)[0], ((uint8_t*)&nlength)[1]); - - client->digestUpdate(sha_ctx, (unsigned char*)&nlength, sizeof(nlength)); + if (logLevel >= LG_LOG_TRACE) + printf("%02x %02x ", ((uint8_t*)&nlength)[0], ((uint8_t*)&nlength)[1]); - for(int i=0; idigestUpdate(sha_ctx, (unsigned char*)&c, 1); - if (logLevel >= LG_LOG_TRACE) - printf("%c", c); - } - if (logLevel >= LG_LOG_TRACE) - printf("\n"); + client->digestUpdate(sha_ctx, (unsigned char*)&nlength, sizeof(nlength)); + + for(int i=0; idigestUpdate(sha_ctx, (unsigned char*)&c, 1); + if (logLevel >= LG_LOG_TRACE) + printf("%c", c); + } + if (logLevel >= LG_LOG_TRACE) + printf("\n"); } void DRMProcessor::pushTag(void* sha_ctx, uint8_t tag) { - client->digestUpdate(sha_ctx, &tag, sizeof(tag)); - if (logLevel >= LG_LOG_TRACE) - printf("%02x ", tag); + client->digestUpdate(sha_ctx, &tag, sizeof(tag)); + if (logLevel >= LG_LOG_TRACE) + printf("%02x ", tag); } void DRMProcessor::hashNode(const pugi::xml_node& root, void *sha_ctx, std::map nsHash) { - switch(root.type()) - { - case pugi::node_element: - { - std::string name = root.name(); + switch(root.type()) + { + case pugi::node_element: + { + std::string name = root.name(); - // Look for "xmlns[:]" attribute - for (pugi::xml_attribute_iterator ait = root.attributes_begin(); - ait != root.attributes_end(); ++ait) - { - std::string attrName(ait->name()); + // Look for "xmlns[:]" attribute + for (pugi::xml_attribute_iterator ait = root.attributes_begin(); + ait != root.attributes_end(); ++ait) + { + std::string attrName(ait->name()); - if (attrName.find("xmlns") == 0) - { - std::string ns("GENERICNS"); - // Compound xmlns:Name attribute - if (attrName.find(':') != std::string::npos) - ns = attrName.substr(attrName.find(':')+1); + if (attrName.find("xmlns") == 0) + { + std::string ns("GENERICNS"); + // Compound xmlns:Name attribute + if (attrName.find(':') != std::string::npos) + ns = attrName.substr(attrName.find(':')+1); - nsHash[ns] = ait->value(); - // Don't break here because we may multiple xmlns definitions - // break; - } - } + nsHash[ns] = ait->value(); + // Don't break here because we may multiple xmlns definitions + // break; + } + } - // Remove namespace from tag - // If we have a namespace for the first time, put it to hash - if (name.find(':') != std::string::npos) - { - size_t nsIndex = name.find(':'); - std::string nodeNS = name.substr(0, nsIndex); + // Remove namespace from tag + // If we have a namespace for the first time, put it to hash + if (name.find(':') != std::string::npos) + { + size_t nsIndex = name.find(':'); + std::string nodeNS = name.substr(0, nsIndex); - pushTag(sha_ctx, ASN_NS_TAG); - pushString(sha_ctx, nsHash[nodeNS]); - - name = name.substr(nsIndex+1); - } - // Global xmlns, always send to hash - else if (nsHash.find("GENERICNS") != nsHash.end()) - { - pushTag(sha_ctx, ASN_NS_TAG); - pushString(sha_ctx, nsHash["GENERICNS"]); - } - - pushString(sha_ctx, name); + pushTag(sha_ctx, ASN_NS_TAG); + pushString(sha_ctx, nsHash[nodeNS]); - std::vector attributes; - pugi::xml_attribute attr; - - for (attr = root.first_attribute(); - attr; attr = attr.next_attribute()) - { - if (std::string(attr.name()).find("xmlns") != std::string::npos) - continue; + name = name.substr(nsIndex+1); + } + // Global xmlns, always send to hash + else if (nsHash.find("GENERICNS") != nsHash.end()) + { + pushTag(sha_ctx, ASN_NS_TAG); + pushString(sha_ctx, nsHash["GENERICNS"]); + } - attributes.push_back(attr.name()); - } + pushString(sha_ctx, name); - // Attributes must be handled in alphabetical order - std::sort(attributes.begin(), attributes.end()); + std::vector attributes; + pugi::xml_attribute attr; - std::vector::iterator attributesIt; - for(attributesIt = attributes.begin(); - attributesIt != attributes.end(); - attributesIt++) - { - attr = root.attribute(attributesIt->c_str()); - - pushTag(sha_ctx, ASN_ATTRIBUTE); - pushString(sha_ctx, ""); - - pushString(sha_ctx, attr.name()); - pushString(sha_ctx, attr.value()); - } + for (attr = root.first_attribute(); + attr; attr = attr.next_attribute()) + { + if (std::string(attr.name()).find("xmlns") != std::string::npos) + continue; - pushTag(sha_ctx, ASN_CHILD); + attributes.push_back(attr.name()); + } - for (pugi::xml_node child : root.children()) - hashNode(child, sha_ctx, nsHash); + // Attributes must be handled in alphabetical order + std::sort(attributes.begin(), attributes.end()); - pushTag(sha_ctx, ASN_END_TAG); - - break; - } - case pugi::node_pcdata: - { - std::string trimmed = root.value(); - trimmed = trim(trimmed); + std::vector::iterator attributesIt; + for(attributesIt = attributes.begin(); + attributesIt != attributes.end(); + attributesIt++) + { + attr = root.attribute(attributesIt->c_str()); - if (trimmed.length()) - { - pushTag(sha_ctx, ASN_TEXT); - pushString(sha_ctx, trimmed); - } + pushTag(sha_ctx, ASN_ATTRIBUTE); + pushString(sha_ctx, ""); - break; - } - default: - break; - } + pushString(sha_ctx, attr.name()); + pushString(sha_ctx, attr.value()); + } + + pushTag(sha_ctx, ASN_CHILD); + + for (pugi::xml_node child : root.children()) + hashNode(child, sha_ctx, nsHash); + + pushTag(sha_ctx, ASN_END_TAG); + + break; + } + case pugi::node_pcdata: + { + std::string trimmed = root.value(); + trimmed = trim(trimmed); + + if (trimmed.length()) + { + pushTag(sha_ctx, ASN_TEXT); + pushString(sha_ctx, trimmed); + } + + break; + } + default: + break; + } } - + void DRMProcessor::hashNode(const pugi::xml_node& root, unsigned char* sha_out) { - void* sha_ctx = client->createDigest("SHA1"); - - std::map nsHash; + void* sha_ctx = client->createDigest("SHA1"); - hashNode(root, sha_ctx, nsHash); + std::map nsHash; - client->digestFinalize(sha_ctx, sha_out); - - dumpBuffer(gourou::LG_LOG_DEBUG, "\nSHA OUT : ", sha_out, SHA1_LEN); + hashNode(root, sha_ctx, nsHash); + + client->digestFinalize(sha_ctx, sha_out); + + dumpBuffer(gourou::LG_LOG_DEBUG, "\nSHA OUT : ", sha_out, SHA1_LEN); } void DRMProcessor::signNode(pugi::xml_node& rootNode) { - // Compute hash - unsigned char sha_out[SHA1_LEN]; + // Compute hash + unsigned char sha_out[SHA1_LEN]; - hashNode(rootNode, sha_out); - - // Sign with private key - unsigned char res[RSA_KEY_SIZE]; - ByteArray deviceKey(device->getDeviceKey(), Device::DEVICE_KEY_SIZE); - std::string pkcs12 = user->getPKCS12(); - ByteArray privateRSAKey = ByteArray::fromBase64(pkcs12); - - client->RSAPrivateEncrypt(privateRSAKey.data(), privateRSAKey.length(), - RSAInterface::RSA_KEY_PKCS12, deviceKey.toBase64().data(), - sha_out, sizeof(sha_out), res); - - dumpBuffer(gourou::LG_LOG_DEBUG, "Sig : ", res, sizeof(res)); + hashNode(rootNode, sha_out); - std::string signature = ByteArray(res, sizeof(res)).toBase64(); - appendTextElem(rootNode, "adept:signature", signature); + // Sign with private key + unsigned char res[RSA_KEY_SIZE]; + ByteArray deviceKey(device->getDeviceKey(), Device::DEVICE_KEY_SIZE); + std::string pkcs12 = user->getPKCS12(); + ByteArray privateRSAKey = ByteArray::fromBase64(pkcs12); + + client->RSAPrivateEncrypt(privateRSAKey.data(), privateRSAKey.length(), + RSAInterface::RSA_KEY_PKCS12, deviceKey.toBase64().data(), + sha_out, sizeof(sha_out), res); + + dumpBuffer(gourou::LG_LOG_DEBUG, "Sig : ", res, sizeof(res)); + + std::string signature = ByteArray(res, sizeof(res)).toBase64(); + appendTextElem(rootNode, "adept:signature", signature); } void DRMProcessor::addNonce(pugi::xml_node& root) { - /* - r4 = tp->time - r3 = 0 - r2 = tm->militime - r0 = 0x6f046000 - r1 = 0x388a - - r3 += high(r4*1000) - r2 += low(r4*1000) - - r0 += r2 - r1 += r3 - */ - struct timeval tv; - gettimeofday(&tv, 0); - uint32_t nonce32[2] = {0x6f046000, 0x388a}; + /* + r4 = tp->time + r3 = 0 + r2 = tm->militime + r0 = 0x6f046000 + r1 = 0x388a + + r3 += high(r4*1000) + r2 += low(r4*1000) + + r0 += r2 + r1 += r3 + */ + struct timeval tv; + gettimeofday(&tv, 0); + uint32_t nonce32[2] = {0x6f046000, 0x388a}; #ifdef STATIC_NONCE - uint64_t bigtime = 0xAA001122BBCCAAULL; + uint64_t bigtime = 0xAA001122BBCCAAULL; #else - uint64_t bigtime = tv.tv_sec*1000; + uint64_t bigtime = tv.tv_sec*1000; #endif - nonce32[0] += (bigtime & 0xFFFFFFFF) + (tv.tv_usec/1000); - nonce32[1] += ((bigtime >> 32) & 0xFFFFFFFF); - - ByteArray nonce((const unsigned char*)&nonce32, sizeof(nonce32)); - uint32_t tmp = 0; - nonce.append((const unsigned char*)&tmp, sizeof(tmp)); - appendTextElem(root, "adept:nonce", nonce.toBase64().data()); + nonce32[0] += (bigtime & 0xFFFFFFFF) + (tv.tv_usec/1000); + nonce32[1] += ((bigtime >> 32) & 0xFFFFFFFF); - time_t _time = time(0) + 10*60; // Cur time + 10 minutes - struct tm* tm_info = gmtime(&_time); - char buffer[32]; + ByteArray nonce((const unsigned char*)&nonce32, sizeof(nonce32)); + uint32_t tmp = 0; + nonce.append((const unsigned char*)&tmp, sizeof(tmp)); + appendTextElem(root, "adept:nonce", nonce.toBase64().data()); - strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%SZ", tm_info); - appendTextElem(root, "adept:expiration", buffer); + time_t _time = time(0) + 10*60; // Cur time + 10 minutes + struct tm* tm_info = gmtime(&_time); + char buffer[32]; + + strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%SZ", tm_info); + appendTextElem(root, "adept:expiration", buffer); } - + ByteArray DRMProcessor::sendRequest(const std::string& URL, const std::string& POSTdata, const char* contentType, std::map* responseHeaders, int fd, bool resume) { - if (contentType == 0) - contentType = ""; - std::string reply = client->sendHTTPRequest(URL, POSTdata, contentType, responseHeaders, fd, resume); + if (contentType == 0) + contentType = ""; + std::string reply = client->sendHTTPRequest(URL, POSTdata, contentType, responseHeaders, fd, resume); - if (fd) return ByteArray(); - - pugi::xml_document replyDoc; - replyDoc.load_buffer(reply.c_str(), reply.length()); + if (fd) return ByteArray(); - pugi::xml_node root = replyDoc.first_child(); - if (std::string(root.name()) == "error") - { - EXCEPTION(GOUROU_ADEPT_ERROR, root.attribute("data").value()); - } - - return ByteArray(reply); + pugi::xml_document replyDoc; + replyDoc.load_buffer(reply.c_str(), reply.length()); + + pugi::xml_node root = replyDoc.first_child(); + if (std::string(root.name()) == "error") + { + EXCEPTION(GOUROU_ADEPT_ERROR, root.attribute("data").value()); + } + + return ByteArray(reply); } ByteArray DRMProcessor::sendRequest(const pugi::xml_document& document, const std::string& url) { - StringXMLWriter xmlWriter; - document.save(xmlWriter, " "); - std::string xmlStr = xmlWriter.getResult(); + StringXMLWriter xmlWriter; + document.save(xmlWriter, " "); + std::string xmlStr = xmlWriter.getResult(); - return sendRequest(url, xmlStr, (const char*)"application/vnd.adobe.adept+xml"); + return sendRequest(url, xmlStr, (const char*)"application/vnd.adobe.adept+xml"); } - + void DRMProcessor::buildAuthRequest(pugi::xml_document& authReq) { - pugi::xml_node decl = authReq.append_child(pugi::node_declaration); - decl.append_attribute("version") = "1.0"; - - pugi::xml_node root = authReq.append_child("adept:credentials"); - root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS; + pugi::xml_node decl = authReq.append_child(pugi::node_declaration); + decl.append_attribute("version") = "1.0"; - appendTextElem(root, "adept:user", user->getUUID()); + pugi::xml_node root = authReq.append_child("adept:credentials"); + root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS; - ByteArray deviceKey(device->getDeviceKey(), Device::DEVICE_KEY_SIZE); - unsigned char* pkcs12 = 0; - unsigned int pkcs12Length; - ByteArray pkcs12Cert = ByteArray::fromBase64(user->getPKCS12()); - - client->extractCertificate(pkcs12Cert.data(), pkcs12Cert.length(), - RSAInterface::RSA_KEY_PKCS12, deviceKey.toBase64().data(), - &pkcs12, &pkcs12Length); - ByteArray privateCertificate(pkcs12, pkcs12Length); - free(pkcs12); + appendTextElem(root, "adept:user", user->getUUID()); - appendTextElem(root, "adept:certificate", privateCertificate.toBase64()); - appendTextElem(root, "adept:licenseCertificate", user->getProperty("//adept:licenseCertificate")); - appendTextElem(root, "adept:authenticationCertificate", user->getProperty("//adept:authenticationCertificate")); + ByteArray deviceKey(device->getDeviceKey(), Device::DEVICE_KEY_SIZE); + unsigned char* pkcs12 = 0; + unsigned int pkcs12Length; + ByteArray pkcs12Cert = ByteArray::fromBase64(user->getPKCS12()); + + client->extractCertificate(pkcs12Cert.data(), pkcs12Cert.length(), + RSAInterface::RSA_KEY_PKCS12, deviceKey.toBase64().data(), + &pkcs12, &pkcs12Length); + ByteArray privateCertificate(pkcs12, pkcs12Length); + free(pkcs12); + + appendTextElem(root, "adept:certificate", privateCertificate.toBase64()); + appendTextElem(root, "adept:licenseCertificate", user->getProperty("//adept:licenseCertificate")); + appendTextElem(root, "adept:authenticationCertificate", user->getProperty("//adept:authenticationCertificate")); } - + void DRMProcessor::buildInitLicenseServiceRequest(pugi::xml_document& initLicReq, std::string operatorURL) { - pugi::xml_node decl = initLicReq.append_child(pugi::node_declaration); - decl.append_attribute("version") = "1.0"; - - pugi::xml_node root = initLicReq.append_child("adept:licenseServiceRequest"); - root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS; - root.append_attribute("identity") = "user"; + pugi::xml_node decl = initLicReq.append_child(pugi::node_declaration); + decl.append_attribute("version") = "1.0"; - appendTextElem(root, "adept:operatorURL", operatorURL); - addNonce(root); - appendTextElem(root, "adept:user", user->getUUID()); + pugi::xml_node root = initLicReq.append_child("adept:licenseServiceRequest"); + root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS; + root.append_attribute("identity") = "user"; - signNode(root); + appendTextElem(root, "adept:operatorURL", operatorURL); + addNonce(root); + appendTextElem(root, "adept:user", user->getUUID()); + + signNode(root); } - + void DRMProcessor::doOperatorAuth(std::string operatorURL) { - pugi::xml_document authReq; - buildAuthRequest(authReq); - std::string authURL = operatorURL; - unsigned int fulfillPos = authURL.rfind("Fulfill"); - if (fulfillPos == (authURL.size() - (sizeof("Fulfill")-1))) - authURL = authURL.substr(0, fulfillPos-1); - ByteArray replyData = sendRequest(authReq, authURL + "/Auth"); + pugi::xml_document authReq; + buildAuthRequest(authReq); + std::string authURL = operatorURL; + unsigned int fulfillPos = authURL.rfind("Fulfill"); + if (fulfillPos == (authURL.size() - (sizeof("Fulfill")-1))) + authURL = authURL.substr(0, fulfillPos-1); + ByteArray replyData = sendRequest(authReq, authURL + "/Auth"); - pugi::xml_document initLicReq; - std::string activationURL = user->getProperty("//adept:activationURL"); - buildInitLicenseServiceRequest(initLicReq, authURL); - sendRequest(initLicReq, activationURL + "/InitLicenseService"); + pugi::xml_document initLicReq; + std::string activationURL = user->getProperty("//adept:activationURL"); + buildInitLicenseServiceRequest(initLicReq, authURL); + sendRequest(initLicReq, activationURL + "/InitLicenseService"); } - + void DRMProcessor::operatorAuth(std::string operatorURL) { - pugi::xpath_node_set operatorList = user->getProperties("//adept:operatorURL"); - - for (pugi::xpath_node_set::const_iterator operatorIt = operatorList.begin(); - operatorIt != operatorList.end(); ++operatorIt) - { - std::string value = operatorIt->node().first_child().value(); - if (trim(value) == operatorURL) - { - GOUROU_LOG(DEBUG, "Already authenticated to operator " << operatorURL); - return; - } - } - - doOperatorAuth(operatorURL); + pugi::xpath_node_set operatorList = user->getProperties("//adept:operatorURL"); + + for (pugi::xpath_node_set::const_iterator operatorIt = operatorList.begin(); + operatorIt != operatorList.end(); ++operatorIt) + { + std::string value = operatorIt->node().first_child().value(); + if (trim(value) == operatorURL) + { + GOUROU_LOG(DEBUG, "Already authenticated to operator " << operatorURL); + return; + } + } + + doOperatorAuth(operatorURL); } void DRMProcessor::buildFulfillRequest(pugi::xml_document& acsmDoc, pugi::xml_document& fulfillReq) { - pugi::xml_node decl = fulfillReq.append_child(pugi::node_declaration); - decl.append_attribute("version") = "1.0"; - - pugi::xml_node root = fulfillReq.append_child("adept:fulfill"); - root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS; + pugi::xml_node decl = fulfillReq.append_child(pugi::node_declaration); + decl.append_attribute("version") = "1.0"; - appendTextElem(root, "adept:user", user->getUUID()); - appendTextElem(root, "adept:device", user->getDeviceUUID()); - appendTextElem(root, "adept:deviceType", (*device)["deviceType"]); + pugi::xml_node root = fulfillReq.append_child("adept:fulfill"); + root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS; - root.append_copy(acsmDoc.first_child()); + appendTextElem(root, "adept:user", user->getUUID()); + appendTextElem(root, "adept:device", user->getDeviceUUID()); + appendTextElem(root, "adept:deviceType", (*device)["deviceType"]); - pugi::xml_node targetDevice = root.append_child("adept:targetDevice"); - appendTextElem(targetDevice, "adept:softwareVersion", (*device)["hobbes"]); - appendTextElem(targetDevice, "adept:clientOS", (*device)["clientOS"]); - appendTextElem(targetDevice, "adept:clientLocale", (*device)["clientLocale"]); - appendTextElem(targetDevice, "adept:clientVersion", (*device)["deviceClass"]); - appendTextElem(targetDevice, "adept:deviceType", (*device)["deviceType"]); - appendTextElem(targetDevice, "adept:fingerprint", (*device)["fingerprint"]); - - pugi::xml_node activationToken = targetDevice.append_child("adept:activationToken"); - appendTextElem(activationToken, "adept:user", user->getUUID()); - appendTextElem(activationToken, "adept:device", user->getDeviceUUID()); + root.append_copy(acsmDoc.first_child()); + + pugi::xml_node targetDevice = root.append_child("adept:targetDevice"); + appendTextElem(targetDevice, "adept:softwareVersion", (*device)["hobbes"]); + appendTextElem(targetDevice, "adept:clientOS", (*device)["clientOS"]); + appendTextElem(targetDevice, "adept:clientLocale", (*device)["clientLocale"]); + appendTextElem(targetDevice, "adept:clientVersion", (*device)["deviceClass"]); + appendTextElem(targetDevice, "adept:deviceType", (*device)["deviceType"]); + appendTextElem(targetDevice, "adept:fingerprint", (*device)["fingerprint"]); + + pugi::xml_node activationToken = targetDevice.append_child("adept:activationToken"); + appendTextElem(activationToken, "adept:user", user->getUUID()); + appendTextElem(activationToken, "adept:device", user->getDeviceUUID()); } void DRMProcessor::fetchLicenseServiceCertificate(const std::string& licenseURL, - const std::string& operatorURL) + const std::string& operatorURL) { - if (user->getLicenseServiceCertificate(licenseURL) != "") - return; + if (user->getLicenseServiceCertificate(licenseURL) != "") + return; - std::string licenseServiceInfoReq = operatorURL + "/LicenseServiceInfo?licenseURL=" + licenseURL; - - ByteArray replyData; - replyData = sendRequest(licenseServiceInfoReq); + std::string licenseServiceInfoReq = operatorURL + "/LicenseServiceInfo?licenseURL=" + licenseURL; - pugi::xml_document licenseServicesDoc; - licenseServicesDoc.load_buffer(replyData.data(), replyData.length()); + ByteArray replyData; + replyData = sendRequest(licenseServiceInfoReq); - // Add new license certificate - pugi::xml_document activationDoc; - user->readActivation(activationDoc); + pugi::xml_document licenseServicesDoc; + licenseServicesDoc.load_buffer(replyData.data(), replyData.length()); - pugi::xml_node root; - pugi::xpath_node xpathRes = activationDoc.select_node("//adept:licenseServices"); + // Add new license certificate + pugi::xml_document activationDoc; + user->readActivation(activationDoc); - // Create adept:licenseServices if it doesn't exists - if (!xpathRes) - { - xpathRes = activationDoc.select_node("/activationInfo"); - root = xpathRes.node(); - root = root.append_child("adept:licenseServices"); - root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS; - } - else - root = xpathRes.node(); + pugi::xml_node root; + pugi::xpath_node xpathRes = activationDoc.select_node("//adept:licenseServices"); - root = root.append_child("adept:licenseServiceInfo"); + // Create adept:licenseServices if it doesn't exists + if (!xpathRes) + { + xpathRes = activationDoc.select_node("/activationInfo"); + root = xpathRes.node(); + root = root.append_child("adept:licenseServices"); + root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS; + } + else + root = xpathRes.node(); - std::string certificate = extractTextElem(licenseServicesDoc, - "/licenseServiceInfo/certificate"); + root = root.append_child("adept:licenseServiceInfo"); - appendTextElem(root, "adept:licenseURL", licenseURL); - appendTextElem(root, "adept:certificate", certificate); + std::string certificate = extractTextElem(licenseServicesDoc, + "/licenseServiceInfo/certificate"); - // Add new operatorURL to list - xpathRes = activationDoc.select_node("//adept:operatorURLList"); + appendTextElem(root, "adept:licenseURL", licenseURL); + appendTextElem(root, "adept:certificate", certificate); - // 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; + // Add new operatorURL to list + xpathRes = activationDoc.select_node("//adept:operatorURLList"); - appendTextElem(root, "adept:user", user->getUUID()); - } - else - root = xpathRes.node(); + // 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:operatorURL", operatorURL); + appendTextElem(root, "adept:user", user->getUUID()); + } + else + root = xpathRes.node(); - user->updateActivationFile(activationDoc); + appendTextElem(root, "adept:operatorURL", operatorURL); + + user->updateActivationFile(activationDoc); } - + FulfillmentItem* DRMProcessor::fulfill(const std::string& ACSMFile, bool notify) { - if (!user->getPKCS12().length()) - EXCEPTION(FF_NOT_ACTIVATED, "Device not activated"); - - pugi::xml_document acsmDoc; + if (!user->getPKCS12().length()) + EXCEPTION(FF_NOT_ACTIVATED, "Device not activated"); - if (!acsmDoc.load_file(ACSMFile.c_str(), pugi::parse_ws_pcdata_single|pugi::parse_escapes, pugi::encoding_utf8)) - EXCEPTION(FF_INVALID_ACSM_FILE, "Invalid ACSM file " << ACSMFile); + pugi::xml_document acsmDoc; - // Could be an server internal error - pugi::xml_node rootNode = acsmDoc.first_child(); - if (std::string(rootNode.name()) == "error") - { - EXCEPTION(FF_SERVER_INTERNAL_ERROR, rootNode.attribute("data").value()); - } + if (!acsmDoc.load_file(ACSMFile.c_str(), pugi::parse_ws_pcdata_single|pugi::parse_escapes, pugi::encoding_utf8)) + EXCEPTION(FF_INVALID_ACSM_FILE, "Invalid ACSM file " << ACSMFile); - GOUROU_LOG(INFO, "Fulfill " << ACSMFile); + // Could be an server internal error + pugi::xml_node rootNode = acsmDoc.first_child(); + if (std::string(rootNode.name()) == "error") + { + EXCEPTION(FF_SERVER_INTERNAL_ERROR, rootNode.attribute("data").value()); + } - std::string expiration = extractTextElem(rootNode, "expiration", false); + GOUROU_LOG(INFO, "Fulfill " << ACSMFile); - if (expiration != "") - { - time_t expirationTime = parseDateTime(expiration.c_str(), "%Y-%m-%dT%H:%M:%S"); + std::string expiration = extractTextElem(rootNode, "expiration", false); - if (time(NULL) > expirationTime) - GOUROU_LOG(WARN, "WARNING: ACSM file expired (" << expiration << "). It may not work"); - } - - // Build req file - pugi::xml_document fulfillReq; + if (expiration != "") + { + time_t expirationTime = parseDateTime(expiration.c_str(), "%Y-%m-%dT%H:%M:%S"); - buildFulfillRequest(acsmDoc, fulfillReq); - pugi::xpath_node root = fulfillReq.select_node("//adept:fulfill"); - rootNode = root.node(); + if (time(NULL) > expirationTime) + GOUROU_LOG(WARN, "WARNING: ACSM file expired (" << expiration << "). It may not work"); + } - // Remove HMAC - pugi::xpath_node xpathRes = fulfillReq.select_node("//hmac"); + // Build req file + pugi::xml_document fulfillReq; - if (!xpathRes) - EXCEPTION(FF_NO_HMAC_IN_ACSM_FILE, "hmac tag not found in ACSM file"); + buildFulfillRequest(acsmDoc, fulfillReq); + pugi::xpath_node root = fulfillReq.select_node("//adept:fulfill"); + rootNode = root.node(); - pugi::xml_node hmacNode = xpathRes.node(); - pugi::xml_node hmacParentNode = hmacNode.parent(); - - hmacParentNode.remove_child(hmacNode); + // Remove HMAC + pugi::xpath_node xpathRes = fulfillReq.select_node("//hmac"); - signNode(rootNode); - - // Add removed HMAC - appendTextElem(hmacParentNode, hmacNode.name(), hmacNode.first_child().value()); + if (!xpathRes) + EXCEPTION(FF_NO_HMAC_IN_ACSM_FILE, "hmac tag not found in ACSM file"); - pugi::xpath_node node = acsmDoc.select_node("//operatorURL"); - if (!node) - EXCEPTION(FF_NO_OPERATOR_URL, "OperatorURL not found in ACSM document"); - - std::string operatorURL = node.node().first_child().value(); - operatorURL = trim(operatorURL); - std::string fulfillURL = operatorURL + "/Fulfill"; + pugi::xml_node hmacNode = xpathRes.node(); + pugi::xml_node hmacParentNode = hmacNode.parent(); - operatorAuth(fulfillURL); - - ByteArray replyData; + hmacParentNode.remove_child(hmacNode); - try - { - replyData = sendRequest(fulfillReq, fulfillURL); - } - catch (gourou::Exception& e) - { - /* - Operator requires authentication even if it's already in - our operator list - */ - std::string errorMsg(e.what()); - if (e.getErrorCode() == GOUROU_ADEPT_ERROR && - errorMsg.find("E_ADEPT_DISTRIBUTOR_AUTH") != std::string::npos) - { - doOperatorAuth(fulfillURL); - replyData = sendRequest(fulfillReq, fulfillURL); - } - else - { - throw e; - } - } + signNode(rootNode); - pugi::xml_document fulfillReply; + // Add removed HMAC + appendTextElem(hmacParentNode, hmacNode.name(), hmacNode.first_child().value()); - fulfillReply.load_string((const char*)replyData.data()); - - std::string licenseURL = extractTextElem(fulfillReply, "//licenseToken/licenseURL"); - - fetchLicenseServiceCertificate(licenseURL, operatorURL); + pugi::xpath_node node = acsmDoc.select_node("//operatorURL"); + if (!node) + EXCEPTION(FF_NO_OPERATOR_URL, "OperatorURL not found in ACSM document"); - FulfillmentItem* item = new FulfillmentItem(fulfillReply, user); + std::string operatorURL = node.node().first_child().value(); + operatorURL = trim(operatorURL); + std::string fulfillURL = operatorURL + "/Fulfill"; - if (notify) - notifyServer(fulfillReply); - - return item; + operatorAuth(fulfillURL); + + ByteArray replyData; + + try + { + replyData = sendRequest(fulfillReq, fulfillURL); + } + catch (gourou::Exception& e) + { + /* + Operator requires authentication even if it's already in + our operator list + */ + std::string errorMsg(e.what()); + if (e.getErrorCode() == GOUROU_ADEPT_ERROR && + errorMsg.find("E_ADEPT_DISTRIBUTOR_AUTH") != std::string::npos) + { + doOperatorAuth(fulfillURL); + replyData = sendRequest(fulfillReq, fulfillURL); + } + else + { + throw e; + } + } + + pugi::xml_document fulfillReply; + + fulfillReply.load_string((const char*)replyData.data()); + + std::string licenseURL = extractTextElem(fulfillReply, "//licenseToken/licenseURL"); + + fetchLicenseServiceCertificate(licenseURL, operatorURL); + + FulfillmentItem* item = new FulfillmentItem(fulfillReply, user); + + if (notify) + notifyServer(fulfillReply); + + return item; } DRMProcessor::ITEM_TYPE DRMProcessor::download(FulfillmentItem* item, std::string path, bool resume) { - ITEM_TYPE res = EPUB; - - if (!item) - EXCEPTION(DW_NO_ITEM, "No item"); + ITEM_TYPE res = EPUB; - std::map headers; + if (!item) + EXCEPTION(DW_NO_ITEM, "No item"); - int fd = createNewFile(path, !resume); - - sendRequest(item->getDownloadURL(), "", 0, &headers, fd, resume); + std::map headers; - close(fd); + int fd = createNewFile(path, !resume); - GOUROU_LOG(INFO, "Download into " << path); + sendRequest(item->getDownloadURL(), "", 0, &headers, fd, resume); - ByteArray rightsStr(item->getRights()); + close(fd); - if (item->getMetadata("format").find("application/pdf") != std::string::npos) - res = PDF; + GOUROU_LOG(INFO, "Download into " << path); - if (headers.count("Content-Type") && - headers["Content-Type"].find("application/pdf") != std::string::npos) - res = PDF; - - if (res == EPUB) - { - void* handler = client->zipOpen(path); - client->zipWriteFile(handler, "META-INF/rights.xml", rightsStr); - client->zipClose(handler); - } - else if (res == PDF) - { - uPDFParser::Parser parser; - bool EBXHandlerFound = false; - - try - { - GOUROU_LOG(DEBUG, "Parse PDF"); - parser.parse(path); - } - catch(std::invalid_argument& e) - { - GOUROU_LOG(ERROR, "Invalid PDF"); - return res; - } + ByteArray rightsStr(item->getRights()); - std::vector objects = parser.objects(); - std::vector::reverse_iterator it; + if (item->getMetadata("format").find("application/pdf") != std::string::npos) + res = PDF; - for(it = objects.rbegin(); it != objects.rend(); it++) - { - // Update EBX_HANDLER with rights - if ((*it)->hasKey("Filter") && (**it)["Filter"]->str() == "/EBX_HANDLER") - { - EBXHandlerFound = true; - uPDFParser::Object* ebx = (*it)->clone(); - (*ebx)["ADEPT_ID"] = new uPDFParser::String(item->getResource()); - (*ebx)["EBX_BOOKID"] = new uPDFParser::String(item->getResource()); - ByteArray zipped; - client->deflate(rightsStr, zipped); - (*ebx)["ADEPT_LICENSE"] = new uPDFParser::String(zipped.toBase64()); - parser.addObject(ebx); - break; - } - } + if (headers.count("Content-Type") && + headers["Content-Type"].find("application/pdf") != std::string::npos) + res = PDF; - if (EBXHandlerFound) - parser.write(path, true); - else - { - EXCEPTION(DW_NO_EBX_HANDLER, "EBX_HANDLER not found"); - } - } + if (res == EPUB) + { + void* handler = client->zipOpen(path); + client->zipWriteFile(handler, "META-INF/rights.xml", rightsStr); + client->zipClose(handler); + } + else if (res == PDF) + { + uPDFParser::Parser parser; + bool EBXHandlerFound = false; - return res; + try + { + GOUROU_LOG(DEBUG, "Parse PDF"); + parser.parse(path); + } + catch(std::invalid_argument& e) + { + GOUROU_LOG(ERROR, "Invalid PDF"); + return res; + } + + std::vector objects = parser.objects(); + std::vector::reverse_iterator it; + + for(it = objects.rbegin(); it != objects.rend(); it++) + { + // Update EBX_HANDLER with rights + if ((*it)->hasKey("Filter") && (**it)["Filter"]->str() == "/EBX_HANDLER") + { + EBXHandlerFound = true; + uPDFParser::Object* ebx = (*it)->clone(); + (*ebx)["ADEPT_ID"] = new uPDFParser::String(item->getResource()); + (*ebx)["EBX_BOOKID"] = new uPDFParser::String(item->getResource()); + ByteArray zipped; + client->deflate(rightsStr, zipped); + (*ebx)["ADEPT_LICENSE"] = new uPDFParser::String(zipped.toBase64()); + parser.addObject(ebx); + break; + } + } + + if (EBXHandlerFound) + parser.write(path, true); + else + { + EXCEPTION(DW_NO_EBX_HANDLER, "EBX_HANDLER not found"); + } + } + + return res; } void DRMProcessor::buildSignInRequest(pugi::xml_document& signInRequest, - const std::string& adobeID, const std::string& adobePassword, - const std::string& authenticationCertificate) + const std::string& adobeID, const std::string& adobePassword, + const std::string& authenticationCertificate) { - pugi::xml_node decl = signInRequest.append_child(pugi::node_declaration); - decl.append_attribute("version") = "1.0"; - pugi::xml_node signIn = signInRequest.append_child("adept:signIn"); - signIn.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS; - std::string loginMethod = user->getLoginMethod(); + pugi::xml_node decl = signInRequest.append_child(pugi::node_declaration); + decl.append_attribute("version") = "1.0"; + pugi::xml_node signIn = signInRequest.append_child("adept:signIn"); + signIn.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS; + std::string loginMethod = user->getLoginMethod(); - if (adobeID == "anonymous") - signIn.append_attribute("method") = "anonymous"; - else if (loginMethod.size()) - signIn.append_attribute("method") = loginMethod.c_str(); - else - signIn.append_attribute("method") = "AdobeID"; + if (adobeID == "anonymous") + signIn.append_attribute("method") = "anonymous"; + else if (loginMethod.size()) + signIn.append_attribute("method") = loginMethod.c_str(); + else + signIn.append_attribute("method") = "AdobeID"; - unsigned char encryptedSignInData[RSA_KEY_SIZE]; - const unsigned char* deviceKey = device->getDeviceKey(); + unsigned char encryptedSignInData[RSA_KEY_SIZE]; + const unsigned char* deviceKey = device->getDeviceKey(); - ByteArray _authenticationCertificate = ByteArray::fromBase64(authenticationCertificate); + ByteArray _authenticationCertificate = ByteArray::fromBase64(authenticationCertificate); - // Build buffer - ByteArray ar(deviceKey, Device::DEVICE_KEY_SIZE); - ar.append((unsigned char)adobeID.length()); - ar.append(adobeID); - ar.append((unsigned char)adobePassword.length()); - ar.append(adobePassword); + // Build buffer + ByteArray ar(deviceKey, Device::DEVICE_KEY_SIZE); + ar.append((unsigned char)adobeID.length()); + ar.append(adobeID); + ar.append((unsigned char)adobePassword.length()); + ar.append(adobePassword); - // Encrypt with authentication certificate (public part) - client->RSAPublicEncrypt(_authenticationCertificate.data(), - _authenticationCertificate.length(), - RSAInterface::RSA_KEY_X509, - ar.data(), ar.length(), encryptedSignInData); + // Encrypt with authentication certificate (public part) + client->RSAPublicEncrypt(_authenticationCertificate.data(), + _authenticationCertificate.length(), + RSAInterface::RSA_KEY_X509, + ar.data(), ar.length(), encryptedSignInData); - ar = ByteArray(encryptedSignInData, sizeof(encryptedSignInData)); - appendTextElem(signIn, "adept:signInData", ar.toBase64()); - - // Generate Auth key and License Key - void* rsaAuth = client->generateRSAKey(RSA_KEY_SIZE_BITS); - void* rsaLicense = client->generateRSAKey(RSA_KEY_SIZE_BITS); + ar = ByteArray(encryptedSignInData, sizeof(encryptedSignInData)); + appendTextElem(signIn, "adept:signInData", ar.toBase64()); - std::string serializedData = serializeRSAPublicKey(rsaAuth); - appendTextElem(signIn, "adept:publicAuthKey", serializedData); - serializedData = serializeRSAPrivateKey(rsaAuth); - appendTextElem(signIn, "adept:encryptedPrivateAuthKey", serializedData.data()); + // Generate Auth key and License Key + void* rsaAuth = client->generateRSAKey(RSA_KEY_SIZE_BITS); + void* rsaLicense = client->generateRSAKey(RSA_KEY_SIZE_BITS); - serializedData = serializeRSAPublicKey(rsaLicense); - appendTextElem(signIn, "adept:publicLicenseKey", serializedData.data()); - serializedData = serializeRSAPrivateKey(rsaLicense); - appendTextElem(signIn, "adept:encryptedPrivateLicenseKey", serializedData.data()); + std::string serializedData = serializeRSAPublicKey(rsaAuth); + appendTextElem(signIn, "adept:publicAuthKey", serializedData); + serializedData = serializeRSAPrivateKey(rsaAuth); + appendTextElem(signIn, "adept:encryptedPrivateAuthKey", serializedData.data()); - client->destroyRSAHandler(rsaAuth); - client->destroyRSAHandler(rsaLicense); + serializedData = serializeRSAPublicKey(rsaLicense); + appendTextElem(signIn, "adept:publicLicenseKey", serializedData.data()); + serializedData = serializeRSAPrivateKey(rsaLicense); + appendTextElem(signIn, "adept:encryptedPrivateLicenseKey", serializedData.data()); + + client->destroyRSAHandler(rsaAuth); + client->destroyRSAHandler(rsaLicense); } - + void DRMProcessor::signIn(const std::string& adobeID, const std::string& adobePassword) { - pugi::xml_document signInRequest; - std::string authenticationCertificate = user->getAuthenticationCertificate(); - - buildSignInRequest(signInRequest, adobeID, adobePassword, authenticationCertificate); + pugi::xml_document signInRequest; + std::string authenticationCertificate = user->getAuthenticationCertificate(); - GOUROU_LOG(INFO, "SignIn " << adobeID); - - std::string signInURL = user->getProperty("//adept:authURL"); - signInURL += "/SignInDirect"; + buildSignInRequest(signInRequest, adobeID, adobePassword, authenticationCertificate); - ByteArray credentials = sendRequest(signInRequest, signInURL); - - pugi::xml_document credentialsDoc; - if (!credentialsDoc.load_buffer(credentials.data(), credentials.length())) - EXCEPTION(SIGN_INVALID_CREDENTIALS, "Invalid credentials reply"); + GOUROU_LOG(INFO, "SignIn " << adobeID); - struct adeptWalker: pugi::xml_tree_walker - { - void changeName(pugi::xml_node& node) - { - std::string name = std::string("adept:") + node.name(); - node.set_name(name.c_str()); - } - - bool begin(pugi::xml_node& node) - { - changeName(node); - return true; - } - - virtual bool for_each(pugi::xml_node& node) - { - if (node.type() == pugi::node_element) - changeName(node); - return true; // continue traversal - } - } adeptWalker; + std::string signInURL = user->getProperty("//adept:authURL"); + signInURL += "/SignInDirect"; - pugi::xml_node credentialsNode = credentialsDoc.first_child(); + ByteArray credentials = sendRequest(signInRequest, signInURL); - if (std::string(credentialsNode.name()) != "credentials") - EXCEPTION(SIGN_INVALID_CREDENTIALS, "Invalid credentials reply"); - - pugi::xpath_node encryptedPrivateLicenseKey = credentialsNode.select_node("encryptedPrivateLicenseKey"); - const char* privateKeyData = encryptedPrivateLicenseKey.node().first_child().value(); - ByteArray privateKeyDataStr = ByteArray::fromBase64(privateKeyData); - ByteArray privateKey = decryptWithDeviceKey(privateKeyDataStr.data(), privateKeyDataStr.length()); - credentialsNode.remove_child(encryptedPrivateLicenseKey.node()); - appendTextElem(credentialsNode, "privateLicenseKey", privateKey.toBase64().data()); + pugi::xml_document credentialsDoc; + if (!credentialsDoc.load_buffer(credentials.data(), credentials.length())) + EXCEPTION(SIGN_INVALID_CREDENTIALS, "Invalid credentials reply"); - // Add "adept:" prefix to all nodes - credentialsNode.remove_attribute("xmlns"); - credentialsNode.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS; - credentialsNode.traverse(adeptWalker); - - appendTextElem(credentialsNode, "adept:authenticationCertificate", authenticationCertificate.data()); + struct adeptWalker: pugi::xml_tree_walker + { + void changeName(pugi::xml_node& node) + { + std::string name = std::string("adept:") + node.name(); + node.set_name(name.c_str()); + } - pugi::xml_document activationDoc; - user->readActivation(activationDoc); - pugi::xml_node activationInfo = activationDoc.select_node("activationInfo").node(); - activationInfo.append_copy(credentialsNode); + bool begin(pugi::xml_node& node) + { + changeName(node); + return true; + } - user->updateActivationFile(activationDoc); + virtual bool for_each(pugi::xml_node& node) + { + if (node.type() == pugi::node_element) + changeName(node); + return true; // continue traversal + } + } adeptWalker; + + pugi::xml_node credentialsNode = credentialsDoc.first_child(); + + if (std::string(credentialsNode.name()) != "credentials") + EXCEPTION(SIGN_INVALID_CREDENTIALS, "Invalid credentials reply"); + + pugi::xpath_node encryptedPrivateLicenseKey = credentialsNode.select_node("encryptedPrivateLicenseKey"); + const char* privateKeyData = encryptedPrivateLicenseKey.node().first_child().value(); + ByteArray privateKeyDataStr = ByteArray::fromBase64(privateKeyData); + ByteArray privateKey = decryptWithDeviceKey(privateKeyDataStr.data(), privateKeyDataStr.length()); + credentialsNode.remove_child(encryptedPrivateLicenseKey.node()); + appendTextElem(credentialsNode, "privateLicenseKey", privateKey.toBase64().data()); + + // Add "adept:" prefix to all nodes + credentialsNode.remove_attribute("xmlns"); + credentialsNode.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS; + credentialsNode.traverse(adeptWalker); + + appendTextElem(credentialsNode, "adept:authenticationCertificate", authenticationCertificate.data()); + + pugi::xml_document activationDoc; + user->readActivation(activationDoc); + pugi::xml_node activationInfo = activationDoc.select_node("activationInfo").node(); + activationInfo.append_copy(credentialsNode); + + user->updateActivationFile(activationDoc); } - + void DRMProcessor::buildActivateReq(pugi::xml_document& activateReq) { - pugi::xml_node decl = activateReq.append_child(pugi::node_declaration); - decl.append_attribute("version") = "1.0"; - - pugi::xml_node root = activateReq.append_child("adept:activate"); - root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS; - root.append_attribute("requestType") = "initial"; + pugi::xml_node decl = activateReq.append_child(pugi::node_declaration); + decl.append_attribute("version") = "1.0"; - appendTextElem(root, "adept:fingerprint", (*device)["fingerprint"]); - appendTextElem(root, "adept:deviceType", (*device)["deviceType"]); - appendTextElem(root, "adept:clientOS", (*device)["clientOS"]); - appendTextElem(root, "adept:clientLocale", (*device)["clientLocale"]); - appendTextElem(root, "adept:clientVersion", (*device)["deviceClass"]); + pugi::xml_node root = activateReq.append_child("adept:activate"); + root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS; + root.append_attribute("requestType") = "initial"; - pugi::xml_node targetDevice = root.append_child("adept:targetDevice"); - appendTextElem(targetDevice, "adept:softwareVersion", (*device)["hobbes"]); - appendTextElem(targetDevice, "adept:clientOS", (*device)["clientOS"]); - appendTextElem(targetDevice, "adept:clientLocale", (*device)["clientLocale"]); - appendTextElem(targetDevice, "adept:clientVersion", (*device)["deviceClass"]); - appendTextElem(targetDevice, "adept:deviceType", (*device)["deviceType"]); - appendTextElem(targetDevice, "adept:fingerprint", (*device)["fingerprint"]); + appendTextElem(root, "adept:fingerprint", (*device)["fingerprint"]); + appendTextElem(root, "adept:deviceType", (*device)["deviceType"]); + appendTextElem(root, "adept:clientOS", (*device)["clientOS"]); + appendTextElem(root, "adept:clientLocale", (*device)["clientLocale"]); + appendTextElem(root, "adept:clientVersion", (*device)["deviceClass"]); - addNonce(root); - - appendTextElem(root, "adept:user", user->getUUID()); + pugi::xml_node targetDevice = root.append_child("adept:targetDevice"); + appendTextElem(targetDevice, "adept:softwareVersion", (*device)["hobbes"]); + appendTextElem(targetDevice, "adept:clientOS", (*device)["clientOS"]); + appendTextElem(targetDevice, "adept:clientLocale", (*device)["clientLocale"]); + appendTextElem(targetDevice, "adept:clientVersion", (*device)["deviceClass"]); + appendTextElem(targetDevice, "adept:deviceType", (*device)["deviceType"]); + appendTextElem(targetDevice, "adept:fingerprint", (*device)["fingerprint"]); + + addNonce(root); + + appendTextElem(root, "adept:user", user->getUUID()); } - + void DRMProcessor::activateDevice() { - pugi::xml_document activateReq; + pugi::xml_document activateReq; - GOUROU_LOG(INFO, "Activate device"); + GOUROU_LOG(INFO, "Activate device"); - buildActivateReq(activateReq); + buildActivateReq(activateReq); - pugi::xml_node root = activateReq.select_node("adept:activate").node(); + pugi::xml_node root = activateReq.select_node("adept:activate").node(); - signNode(root); + signNode(root); - pugi::xml_document activationDoc; - user->readActivation(activationDoc); + pugi::xml_document activationDoc; + user->readActivation(activationDoc); - std::string activationURL = user->getProperty("//adept:activationURL"); - activationURL += "/Activate"; - - ByteArray reply = sendRequest(activateReq, activationURL); + std::string activationURL = user->getProperty("//adept:activationURL"); + activationURL += "/Activate"; - pugi::xml_document activationToken; - activationToken.load_buffer(reply.data(), reply.length()); - - root = activationDoc.select_node("activationInfo").node(); - root.append_copy(activationToken.first_child()); - user->updateActivationFile(activationDoc); + ByteArray reply = sendRequest(activateReq, activationURL); + + pugi::xml_document activationToken; + activationToken.load_buffer(reply.data(), reply.length()); + + root = activationDoc.select_node("activationInfo").node(); + root.append_copy(activationToken.first_child()); + 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; + pugi::xml_node decl = returnReq.append_child(pugi::node_declaration); + decl.append_attribute("version") = "1.0"; - appendTextElem(root, "adept:user", user->getUUID()); - appendTextElem(root, "adept:device", user->getDeviceUUID()); - appendTextElem(root, "adept:loan", loanID); + pugi::xml_node root = returnReq.append_child("adept:loanReturn"); + root.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS; - addNonce(root); - signNode(root); + appendTextElem(root, "adept:user", user->getUUID()); + appendTextElem(root, "adept:device", user->getDeviceUUID()); + appendTextElem(root, "adept:loan", loanID); + + addNonce(root); + signNode(root); } std::string DRMProcessor::getDefaultAdeptDir(void) { #ifndef DEFAULT_ADEPT_DIR - const char* home = getenv("HOME"); - - if (home) - return home + std::string("/.config/adept/"); - else - { - const char* user = getenv("USER"); - - if (user && user[0]) - { - return std::string("/home/") + user + std::string("/.config/adept/"); - } - else - return LOCAL_ADEPT_DIR; - } + const char* home = getenv("HOME"); + + if (home) + return home + std::string("/.config/adept/"); + else + { + const char* user = getenv("USER"); + + if (user && user[0]) + { + return std::string("/home/") + user + std::string("/.config/adept/"); + } + else + return LOCAL_ADEPT_DIR; + } #else - return DEFAULT_ADEPT_DIR "/"; + return DEFAULT_ADEPT_DIR "/"; #endif } void DRMProcessor::returnLoan(const std::string& loanID, const std::string& operatorURL, - bool notify) + bool notify) { - pugi::xml_document returnReq; + pugi::xml_document returnReq; - GOUROU_LOG(INFO, "Return loan " << loanID); + GOUROU_LOG(INFO, "Return loan " << loanID); - buildReturnReq(returnReq, loanID, operatorURL); + buildReturnReq(returnReq, loanID, operatorURL); - ByteArray replyData = sendRequest(returnReq, operatorURL + "/LoanReturn"); - - pugi::xml_document fulfillReply; + ByteArray replyData = sendRequest(returnReq, operatorURL + "/LoanReturn"); - fulfillReply.load_string((const char*)replyData.data()); + pugi::xml_document fulfillReply; - if (notify) - notifyServer(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; + pugi::xml_node decl = returnReq.append_child(pugi::node_declaration); + decl.append_attribute("version") = "1.0"; - 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); + 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; - } + std::string notifyUrl = extractTextElem(notifyRoot, "//notifyURL", false); + pugi::xml_node notifyBody = getNode(notifyRoot, "//body", false); - if (!notifyBody) - { - GOUROU_LOG(INFO, "No notify body"); - return; - } + if (notifyUrl == "") + { + GOUROU_LOG(INFO, "No notify URL"); + return; + } - pugi::xml_document notifyReq; - buildNotifyReq(notifyReq, notifyBody); + if (!notifyBody) + { + GOUROU_LOG(INFO, "No notify body"); + return; + } - sendRequest(notifyReq, notifyUrl); + 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"); + pugi::xpath_node_set notifySet = fulfillReply.select_nodes("//notify"); - if (notifySet.empty()) - { - GOUROU_LOG(DEBUG, "No notify request"); - return; - } + 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); - } + 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(); - unsigned int outLen; - int remain = 0; - if ((len % 16)) - remain = 16 - (len%16); - int encrypted_data_len = 16 + len + remain; // IV + data + pad - unsigned char* encrypted_data = new unsigned char[encrypted_data_len]; - - // Generate IV in front - client->randBytes(encrypted_data, 16); - - client->encrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC, - deviceKey, 16, encrypted_data, 16, - data, len, - encrypted_data+16, &outLen); + const unsigned char* deviceKey = device->getDeviceKey(); + unsigned int outLen; + int remain = 0; + if ((len % 16)) + remain = 16 - (len%16); + int encrypted_data_len = 16 + len + remain; // IV + data + pad + unsigned char* encrypted_data = new unsigned char[encrypted_data_len]; - ByteArray res(encrypted_data, outLen+16); + // Generate IV in front + client->randBytes(encrypted_data, 16); - delete[] encrypted_data; + client->encrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC, + deviceKey, 16, encrypted_data, 16, + data, len, + encrypted_data+16, &outLen); - return res; + ByteArray res(encrypted_data, outLen+16); + + delete[] encrypted_data; + + return res; } /* First 16 bytes of data is IV for CBC chaining */ ByteArray DRMProcessor::decryptWithDeviceKey(const unsigned char* data, unsigned int len) { - unsigned int outLen; - const unsigned char* deviceKey = device->getDeviceKey(); - unsigned char* decrypted_data = new unsigned char[len-16]; + unsigned int outLen; + const unsigned char* deviceKey = device->getDeviceKey(); + unsigned char* decrypted_data = new unsigned char[len-16]; - client->decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC, - deviceKey, 16, data, 16, - data+16, len-16, - decrypted_data, &outLen); + client->decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC, + deviceKey, 16, data, 16, + data+16, len-16, + decrypted_data, &outLen); - ByteArray res(decrypted_data, outLen); + ByteArray res(decrypted_data, outLen); - delete[] decrypted_data; + delete[] decrypted_data; - return res; + return res; } std::string DRMProcessor::serializeRSAPublicKey(void* rsa) { - unsigned char* data = 0; - unsigned int len; - - client->extractRSAPublicKey(rsa, &data, &len); + unsigned char* data = 0; + unsigned int len; - ByteArray res(data, len); + client->extractRSAPublicKey(rsa, &data, &len); - free(data); - - return res.toBase64(); + ByteArray res(data, len); + + free(data); + + return res.toBase64(); } - + std::string DRMProcessor::serializeRSAPrivateKey(void* rsa) { - unsigned char* data = 0; - unsigned int len; - - client->extractRSAPrivateKey(rsa, &data, &len); + unsigned char* data = 0; + unsigned int len; - ByteArray res = encryptWithDeviceKey(data, len); + client->extractRSAPrivateKey(rsa, &data, &len); - free(data); - - return res.toBase64(); + ByteArray res = encryptWithDeviceKey(data, len); + + free(data); + + return res.toBase64(); } void DRMProcessor::exportPrivateLicenseKey(std::string path) { - int fd = open(path.c_str(), O_CREAT|O_TRUNC|O_WRONLY, S_IRWXU); - int ret; - - if (fd <= 0) - EXCEPTION(GOUROU_FILE_ERROR, "Unable to open " << path); + int fd = open(path.c_str(), O_CREAT|O_TRUNC|O_WRONLY, S_IRWXU); + int ret; - ByteArray privateLicenseKey = ByteArray::fromBase64(user->getPrivateLicenseKey()); - /* In adobekey.py, we get base64 decoded data [26:] */ - ret = write(fd, privateLicenseKey.data()+26, privateLicenseKey.length()-26); - close(fd); - if (ret != (int)(privateLicenseKey.length()-26)) - { - EXCEPTION(gourou::GOUROU_FILE_ERROR, "Error writing " << path); - } + if (fd <= 0) + EXCEPTION(GOUROU_FILE_ERROR, "Unable to open " << path); + + ByteArray privateLicenseKey = ByteArray::fromBase64(user->getPrivateLicenseKey()); + /* In adobekey.py, we get base64 decoded data [26:] */ + ret = write(fd, privateLicenseKey.data()+26, privateLicenseKey.length()-26); + close(fd); + if (ret != (int)(privateLicenseKey.length()-26)) + { + EXCEPTION(gourou::GOUROU_FILE_ERROR, "Error writing " << path); + } } int DRMProcessor::getLogLevel() {return (int)gourou::logLevel;} @@ -1078,456 +1078,456 @@ namespace gourou */ std::string DRMProcessor::encryptedKeyFirstPass(pugi::xml_document& rightsDoc, const std::string& encryptedKey, const std::string& keyType) { - unsigned char digest[32], key[16], iv[16]; - unsigned int dataOutLength; - std::string id; - - client->digest("SHA256", (unsigned char*)keyType.c_str(), keyType.size(), digest); + unsigned char digest[32], key[16], iv[16]; + unsigned int dataOutLength; + std::string id; - dumpBuffer(gourou::LG_LOG_DEBUG, "SHA of KeyType : ", digest, sizeof(digest)); + client->digest("SHA256", (unsigned char*)keyType.c_str(), keyType.size(), digest); - long nonce = std::stol(keyType); - int remainder = nonce % 16; + dumpBuffer(gourou::LG_LOG_DEBUG, "SHA of KeyType : ", digest, sizeof(digest)); - memcpy(key, &digest[remainder*2], 16-remainder); - memcpy(&key[16-remainder], &digest[remainder], remainder); - - id = extractTextElem(rightsDoc, "/adept:rights/licenseToken/device"); - if (id == "") - EXCEPTION(DRM_ERR_ENCRYPTION_KEY_FP, "Device id not found in rights.xml"); - ByteArray deviceId = ByteArray::fromHex(extractIdFromUUID(id)); - unsigned char* _deviceId = deviceId.data(); - - id = extractTextElem(rightsDoc, "/adept:rights/licenseToken/fulfillment"); - if (id == "") - EXCEPTION(DRM_ERR_ENCRYPTION_KEY_FP, "Fulfillment id not found in rights.xml"); - ByteArray fulfillmentId = ByteArray::fromHex(extractIdFromUUID(id)); - unsigned char* _fulfillmentId = fulfillmentId.data(); - - id = extractTextElem(rightsDoc, "/adept:rights/licenseToken/voucher"); - if (id == "") - EXCEPTION(DRM_ERR_ENCRYPTION_KEY_FP, "Voucher id not found in rights.xml"); - ByteArray voucherId = ByteArray::fromHex(extractIdFromUUID(id)); - unsigned char* _voucherId = voucherId.data(); - - if (deviceId.size() < sizeof(iv) || fulfillmentId.size() < sizeof(iv) || voucherId.size() < sizeof(iv)) - EXCEPTION(DRM_ERR_ENCRYPTION_KEY_FP, "One id has a bad length"); + long nonce = std::stol(keyType); + int remainder = nonce % 16; - for(unsigned int i=0; idecrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC, - (const unsigned char*)key, (unsigned int)sizeof(key), - (const unsigned char*)iv, (unsigned int)sizeof(iv), - (const unsigned char*)arrayEncryptedKey.data(), arrayEncryptedKey.size(), - (unsigned char*)clearRSAKey, &dataOutLength); + id = extractTextElem(rightsDoc, "/adept:rights/licenseToken/voucher"); + if (id == "") + EXCEPTION(DRM_ERR_ENCRYPTION_KEY_FP, "Voucher id not found in rights.xml"); + ByteArray voucherId = ByteArray::fromHex(extractIdFromUUID(id)); + unsigned char* _voucherId = voucherId.data(); - dumpBuffer(gourou::LG_LOG_DEBUG, "\nDecrypted key : ", clearRSAKey, dataOutLength); + if (deviceId.size() < sizeof(iv) || fulfillmentId.size() < sizeof(iv) || voucherId.size() < sizeof(iv)) + EXCEPTION(DRM_ERR_ENCRYPTION_KEY_FP, "One id has a bad length"); - /* Last block could be 0x10*16 which is OpenSSL padding, remove it if it's the case */ - bool skipLastLine = true; - for(unsigned int i=dataOutLength-16; idecrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC, + (const unsigned char*)key, (unsigned int)sizeof(key), + (const unsigned char*)iv, (unsigned int)sizeof(iv), + (const unsigned char*)arrayEncryptedKey.data(), arrayEncryptedKey.size(), + (unsigned char*)clearRSAKey, &dataOutLength); + + dumpBuffer(gourou::LG_LOG_DEBUG, "\nDecrypted key : ", clearRSAKey, dataOutLength); + + /* Last block could be 0x10*16 which is OpenSSL padding, remove it if it's the case */ + bool skipLastLine = true; + for(unsigned int i=dataOutLength-16; iuser->getUUID() != user) - { - EXCEPTION(DRM_INVALID_USER, "This book has been downloaded for another user (" << user << ")"); - } + std::string user = extractTextElem(rightsDoc, "/adept:rights/licenseToken/user"); - std::string encryptedKey = extractTextElem(rightsDoc, "/adept:rights/licenseToken/encryptedKey"); - std::string keyType = extractTextAttribute(rightsDoc, "/adept:rights/licenseToken/encryptedKey", "keyType", false); + if (!encryptionKey) + { + if (this->user->getUUID() != user) + { + EXCEPTION(DRM_INVALID_USER, "This book has been downloaded for another user (" << user << ")"); + } - if (keyType != "") - encryptedKey = encryptedKeyFirstPass(rightsDoc, encryptedKey, keyType); - - if (encryptedKey.size() != 172) - EXCEPTION(DRM_INVALID_KEY_SIZE, "Invalid encrypted key size (" << encryptedKey.size() << "). DRM version not supported"); + std::string encryptedKey = extractTextElem(rightsDoc, "/adept:rights/licenseToken/encryptedKey"); + std::string keyType = extractTextAttribute(rightsDoc, "/adept:rights/licenseToken/encryptedKey", "keyType", false); - ByteArray arrayEncryptedKey = ByteArray::fromBase64(encryptedKey); + if (keyType != "") + encryptedKey = encryptedKeyFirstPass(rightsDoc, encryptedKey, keyType); - std::string privateKeyData = this->user->getPrivateLicenseKey(); - ByteArray privateRSAKey = ByteArray::fromBase64(privateKeyData); - - dumpBuffer(gourou::LG_LOG_DEBUG, "To decrypt : ", arrayEncryptedKey.data(), arrayEncryptedKey.length()); + if (encryptedKey.size() != 172) + EXCEPTION(DRM_INVALID_KEY_SIZE, "Invalid encrypted key size (" << encryptedKey.size() << "). DRM version not supported"); - client->RSAPrivateDecrypt(privateRSAKey.data(), privateRSAKey.length(), - RSAInterface::RSA_KEY_PKCS8, "", - arrayEncryptedKey.data(), arrayEncryptedKey.length(), rsaKey); + ByteArray arrayEncryptedKey = ByteArray::fromBase64(encryptedKey); - dumpBuffer(gourou::LG_LOG_DEBUG, "Decrypted : ", rsaKey, sizeof(rsaKey)); + std::string privateKeyData = this->user->getPrivateLicenseKey(); + ByteArray privateRSAKey = ByteArray::fromBase64(privateKeyData); - if (rsaKey[0] != 0x00 || rsaKey[1] != 0x02 || - rsaKey[RSA_KEY_SIZE-16-1] != 0x00) - EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Unable to retrieve encryption key"); + dumpBuffer(gourou::LG_LOG_DEBUG, "To decrypt : ", arrayEncryptedKey.data(), arrayEncryptedKey.length()); - memcpy(decryptedKey, &rsaKey[sizeof(rsaKey)-16], 16); - } - else - { - GOUROU_LOG(DEBUG, "Use provided encryption key"); - if (encryptionKeySize != 16) - EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Provided encryption key must be 16 bytes"); + client->RSAPrivateDecrypt(privateRSAKey.data(), privateRSAKey.length(), + RSAInterface::RSA_KEY_PKCS8, "", + arrayEncryptedKey.data(), arrayEncryptedKey.length(), rsaKey); - memcpy(decryptedKey, encryptionKey, encryptionKeySize); - } + dumpBuffer(gourou::LG_LOG_DEBUG, "Decrypted : ", rsaKey, sizeof(rsaKey)); + + if (rsaKey[0] != 0x00 || rsaKey[1] != 0x02 || + rsaKey[RSA_KEY_SIZE-16-1] != 0x00) + EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Unable to retrieve encryption key"); + + memcpy(decryptedKey, &rsaKey[sizeof(rsaKey)-16], 16); + } + 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, encryptionKey, encryptionKeySize); + } } - + void DRMProcessor::removeEPubDRM(const std::string& filenameIn, const std::string& filenameOut, - const unsigned char* encryptionKey, unsigned encryptionKeySize) + const unsigned char* encryptionKey, unsigned encryptionKeySize) { - ByteArray zipData; - bool removeEncryptionXML = true; - void* zipHandler = client->zipOpen(filenameOut); + ByteArray zipData; + bool removeEncryptionXML = true; + void* zipHandler = client->zipOpen(filenameOut); - client->zipReadFile(zipHandler, "META-INF/rights.xml", zipData); - pugi::xml_document rightsDoc; - rightsDoc.load_string((const char*)zipData.data()); + client->zipReadFile(zipHandler, "META-INF/rights.xml", zipData); + pugi::xml_document rightsDoc; + rightsDoc.load_string((const char*)zipData.data()); - unsigned char decryptedKey[16]; + unsigned char decryptedKey[16]; - decryptADEPTKey(rightsDoc, decryptedKey, encryptionKey, encryptionKeySize); - - client->zipReadFile(zipHandler, "META-INF/encryption.xml", zipData); - pugi::xml_document encryptionDoc; - encryptionDoc.load_string((const char*)zipData.data()); + decryptADEPTKey(rightsDoc, decryptedKey, encryptionKey, encryptionKeySize); - pugi::xpath_node_set nodeSet = encryptionDoc.select_nodes("//EncryptedData"); + client->zipReadFile(zipHandler, "META-INF/encryption.xml", zipData); + pugi::xml_document encryptionDoc; + encryptionDoc.load_string((const char*)zipData.data()); - for (pugi::xpath_node_set::const_iterator it = nodeSet.begin(); - it != nodeSet.end(); ++it) - { - pugi::xml_node encryptionMethod = it->node().child("EncryptionMethod"); - pugi::xml_node cipherReference = it->node().child("CipherData").child("CipherReference"); + pugi::xpath_node_set nodeSet = encryptionDoc.select_nodes("//EncryptedData"); - std::string encryptionType = encryptionMethod.attribute("Algorithm").value(); - std::string encryptedFile = cipherReference.attribute("URI").value(); - - if (encryptionType == "") - { - EXCEPTION(DRM_MISSING_PARAMETER, "Missing Algorithm attribute in encryption.xml"); - } - else if (encryptionType == "http://www.w3.org/2001/04/xmlenc#aes128-cbc") - { - if (encryptedFile == "") - { - EXCEPTION(DRM_MISSING_PARAMETER, "Missing URI attribute in encryption.xml"); - } + for (pugi::xpath_node_set::const_iterator it = nodeSet.begin(); + it != nodeSet.end(); ++it) + { + pugi::xml_node encryptionMethod = it->node().child("EncryptionMethod"); + pugi::xml_node cipherReference = it->node().child("CipherData").child("CipherReference"); - GOUROU_LOG(DEBUG, "Encrypted file " << encryptedFile); + std::string encryptionType = encryptionMethod.attribute("Algorithm").value(); + std::string encryptedFile = cipherReference.attribute("URI").value(); - client->zipReadFile(zipHandler, encryptedFile, zipData, false); - - unsigned char* _data = zipData.data(); - ByteArray clearData(zipData.length()-16+1, true); /* Reserve 1 byte for 'Z' */ - unsigned char* _clearData = clearData.data(); - gourou::ByteArray inflateData(true); - unsigned int dataOutLength; + if (encryptionType == "") + { + EXCEPTION(DRM_MISSING_PARAMETER, "Missing Algorithm attribute in encryption.xml"); + } + else if (encryptionType == "http://www.w3.org/2001/04/xmlenc#aes128-cbc") + { + if (encryptedFile == "") + { + EXCEPTION(DRM_MISSING_PARAMETER, "Missing URI attribute in encryption.xml"); + } - client->decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC, - decryptedKey, sizeof(decryptedKey), /* Key */ - _data, 16, /* IV */ - &_data[16], zipData.length()-16, - _clearData, &dataOutLength); + GOUROU_LOG(DEBUG, "Encrypted file " << encryptedFile); - // Add 'Z' at the end, done in ineptepub.py - _clearData[dataOutLength] = 'Z'; - clearData.resize(dataOutLength+1); + client->zipReadFile(zipHandler, encryptedFile, zipData, false); - try - { - client->inflate(clearData, inflateData); - client->zipWriteFile(zipHandler, encryptedFile, inflateData); - } - catch(gourou::Exception& e) - { - if (e.getErrorCode() == CLIENT_ZIP_ERROR) - { - GOUROU_LOG(ERROR, e.what() << std::endl << "Skip file " << encryptedFile); - } - else - throw e; - } + unsigned char* _data = zipData.data(); + ByteArray clearData(zipData.length()-16+1, true); /* Reserve 1 byte for 'Z' */ + unsigned char* _clearData = clearData.data(); + gourou::ByteArray inflateData(true); + unsigned int dataOutLength; - it->node().parent().remove_child(it->node()); - } - else - { - GOUROU_LOG(WARN, "Unsupported encryption algorithm " << encryptionType << ", for file " << encryptedFile); - removeEncryptionXML = false; - } - } - - client->zipDeleteFile(zipHandler, "META-INF/rights.xml"); - if (removeEncryptionXML) - client->zipDeleteFile(zipHandler, "META-INF/encryption.xml"); - else - { - StringXMLWriter xmlWriter; - encryptionDoc.save(xmlWriter, " "); - std::string xmlStr = xmlWriter.getResult(); - ByteArray ba(xmlStr); - client->zipWriteFile(zipHandler, "META-INF/encryption.xml", ba); - } - - client->zipClose(zipHandler); + client->decrypt(CryptoInterface::ALGO_AES, CryptoInterface::CHAIN_CBC, + decryptedKey, sizeof(decryptedKey), /* Key */ + _data, 16, /* IV */ + &_data[16], zipData.length()-16, + _clearData, &dataOutLength); + + // Add 'Z' at the end, done in ineptepub.py + _clearData[dataOutLength] = 'Z'; + clearData.resize(dataOutLength+1); + + try + { + client->inflate(clearData, inflateData); + client->zipWriteFile(zipHandler, encryptedFile, inflateData); + } + catch(gourou::Exception& e) + { + if (e.getErrorCode() == CLIENT_ZIP_ERROR) + { + GOUROU_LOG(ERROR, e.what() << std::endl << "Skip file " << encryptedFile); + } + else + throw e; + } + + it->node().parent().remove_child(it->node()); + } + else + { + GOUROU_LOG(WARN, "Unsupported encryption algorithm " << encryptionType << ", for file " << encryptedFile); + removeEncryptionXML = false; + } + } + + client->zipDeleteFile(zipHandler, "META-INF/rights.xml"); + if (removeEncryptionXML) + client->zipDeleteFile(zipHandler, "META-INF/encryption.xml"); + else + { + StringXMLWriter xmlWriter; + encryptionDoc.save(xmlWriter, " "); + std::string xmlStr = xmlWriter.getResult(); + ByteArray ba(xmlStr); + client->zipWriteFile(zipHandler, "META-INF/encryption.xml", ba); + } + + client->zipClose(zipHandler); } - + void DRMProcessor::generatePDFObjectKey(int version, - const unsigned char* masterKey, unsigned int masterKeyLength, - int objectId, int objectGenerationNumber, - unsigned char* keyOut) + const unsigned char* masterKey, unsigned int masterKeyLength, + int objectId, int objectGenerationNumber, + unsigned char* keyOut) { - switch(version) - { - case 4: - ByteArray toHash(masterKey, masterKeyLength); - uint32_t _objectId = objectId; - uint32_t _objectGenerationNumber = objectGenerationNumber; - toHash.append((const unsigned char*)&_objectId, 3); // Fill 3 bytes - toHash.append((const unsigned char*)&_objectGenerationNumber, 2); // Fill 2 bytes + switch(version) + { + case 4: + ByteArray toHash(masterKey, masterKeyLength); + uint32_t _objectId = objectId; + uint32_t _objectGenerationNumber = objectGenerationNumber; + toHash.append((const unsigned char*)&_objectId, 3); // Fill 3 bytes + toHash.append((const unsigned char*)&_objectGenerationNumber, 2); // Fill 2 bytes - client->digest("md5", toHash.data(), toHash.length(), keyOut); - break; - } + client->digest("md5", toHash.data(), toHash.length(), keyOut); + break; + } } - + void DRMProcessor::removePDFDRM(const std::string& filenameIn, const std::string& filenameOut, - const unsigned char* encryptionKey, unsigned encryptionKeySize) + const unsigned char* encryptionKey, unsigned encryptionKeySize) { - uPDFParser::Parser parser; - bool EBXHandlerFound = false; - - if (filenameIn == filenameOut) - { - EXCEPTION(DRM_IN_OUT_EQUALS, "PDF IN must be different of PDF OUT"); - } - - try - { - GOUROU_LOG(DEBUG, "Parse PDF"); - parser.parse(filenameIn); - } - catch(std::invalid_argument& e) - { - GOUROU_LOG(ERROR, "Invalid PDF"); - return; - } + uPDFParser::Parser parser; + bool EBXHandlerFound = false; - uPDFParser::Integer* ebxVersion; - std::vector objects = parser.objects(); - std::vector::iterator it; - std::vector::reverse_iterator rIt; - std::vector ebxObjects; - unsigned char decryptedKey[16]; - int ebxId; - - for(rIt = objects.rbegin(); rIt != objects.rend(); rIt++) - { - // Update EBX_HANDLER with rights - if ((*rIt)->hasKey("Filter") && (**rIt)["Filter"]->str() == "/EBX_HANDLER") - { - EBXHandlerFound = true; - uPDFParser::Object* ebx = *rIt; - - ebxVersion = (uPDFParser::Integer*)(*ebx)["V"]; - if (ebxVersion->value() != 4) - { - EXCEPTION(DRM_VERSION_NOT_SUPPORTED, "EBX encryption version not supported " << ebxVersion->value()); - } + if (filenameIn == filenameOut) + { + EXCEPTION(DRM_IN_OUT_EQUALS, "PDF IN must be different of PDF OUT"); + } - if (!(ebx->hasKey("ADEPT_LICENSE"))) - { - EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "No ADEPT_LICENSE found"); - } - - uPDFParser::String* licenseObject = (uPDFParser::String*)(*ebx)["ADEPT_LICENSE"]; - - std::string value = licenseObject->value(); - // Pad with '=' - while ((value.size() % 4)) - value += "="; - ByteArray zippedData = ByteArray::fromBase64(value); + try + { + GOUROU_LOG(DEBUG, "Parse PDF"); + parser.parse(filenameIn); + } + catch(std::invalid_argument& e) + { + GOUROU_LOG(ERROR, "Invalid PDF"); + return; + } - if (zippedData.size() == 0) - EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Invalid ADEPT_LICENSE"); - - ByteArray rightsStr; - client->inflate(zippedData, rightsStr); + uPDFParser::Integer* ebxVersion; + std::vector objects = parser.objects(); + std::vector::iterator it; + std::vector::reverse_iterator rIt; + std::vector ebxObjects; + unsigned char decryptedKey[16]; + int ebxId; - pugi::xml_document rightsDoc; - rightsDoc.load_string((const char*)rightsStr.data()); + for(rIt = objects.rbegin(); rIt != objects.rend(); rIt++) + { + // Update EBX_HANDLER with rights + if ((*rIt)->hasKey("Filter") && (**rIt)["Filter"]->str() == "/EBX_HANDLER") + { + EBXHandlerFound = true; + uPDFParser::Object* ebx = *rIt; - decryptADEPTKey(rightsDoc, decryptedKey, encryptionKey, encryptionKeySize); - - ebxId = ebx->objectId(); + ebxVersion = (uPDFParser::Integer*)(*ebx)["V"]; + if (ebxVersion->value() != 4) + { + EXCEPTION(DRM_VERSION_NOT_SUPPORTED, "EBX encryption version not supported " << ebxVersion->value()); + } - break; - } - } + if (!(ebx->hasKey("ADEPT_LICENSE"))) + { + EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "No ADEPT_LICENSE found"); + } - if (!EBXHandlerFound) - { - EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "EBX_HANDLER not found"); - } + uPDFParser::String* licenseObject = (uPDFParser::String*)(*ebx)["ADEPT_LICENSE"]; - for(it = objects.begin(); it != objects.end(); it++) - { - uPDFParser::Object* object = *it; - - if (object->objectId() == ebxId) - { - ebxObjects.push_back(object); - continue; - } + std::string value = licenseObject->value(); + // Pad with '=' + while ((value.size() % 4)) + value += "="; + ByteArray zippedData = ByteArray::fromBase64(value); - // 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()); + if (zippedData.size() == 0) + EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "Invalid ADEPT_LICENSE"); - unsigned char tmpKey[sizeof(decryptedKey)]; + ByteArray rightsStr; + client->inflate(zippedData, rightsStr); - generatePDFObjectKey(ebxVersion->value(), - decryptedKey, sizeof(decryptedKey), - object->objectId(), object->generationNumber(), - tmpKey); + pugi::xml_document rightsDoc; + rightsDoc.load_string((const char*)rightsStr.data()); - uPDFParser::Dictionary& dictionary = object->dictionary(); - std::map& dictValues = dictionary.value(); - std::map::iterator dictIt; - std::map decodedStrings; - std::string string; - - /* Parse dictionary */ - for (dictIt = dictValues.begin(); dictIt != dictValues.end(); dictIt++) - { - uPDFParser::DataType* dictData = dictIt->second; - if (dictData->type() == uPDFParser::DataType::STRING) - { - string = ((uPDFParser::String*) dictData)->unescapedValue(); - - unsigned char* encryptedData = (unsigned char*)string.c_str(); - unsigned int dataLength = string.size(); - unsigned char* clearData = new unsigned char[dataLength]; - unsigned int dataOutLength; + decryptADEPTKey(rightsDoc, decryptedKey, encryptionKey, encryptionKeySize); - GOUROU_LOG(DEBUG, "Decrypt string " << dictIt->first << " " << dataLength); + ebxId = ebx->objectId(); - client->decrypt(CryptoInterface::ALGO_RC4, CryptoInterface::CHAIN_ECB, - tmpKey, sizeof(tmpKey), /* Key */ - NULL, 0, /* IV */ - encryptedData, dataLength, - clearData, &dataOutLength); + break; + } + } - decodedStrings[dictIt->first] = new uPDFParser::String( - std::string((const char*)clearData, dataOutLength)); + if (!EBXHandlerFound) + { + EXCEPTION(DRM_ERR_ENCRYPTION_KEY, "EBX_HANDLER not found"); + } - 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; + for(it = objects.begin(); it != objects.end(); it++) + { + uPDFParser::Object* object = *it; - GOUROU_LOG(DEBUG, "Decrypt hexa string " << dictIt->first << " " << dataLength); + if (object->objectId() == ebxId) + { + ebxObjects.push_back(object); + continue; + } - client->decrypt(CryptoInterface::ALGO_RC4, CryptoInterface::CHAIN_ECB, - tmpKey, sizeof(tmpKey), /* Key */ - NULL, 0, /* IV */ - encryptedData, dataLength, - clearData, &dataOutLength); + // Should not decrypt XRef stream + if (object->hasKey("Type") && (*object)["Type"]->str() == "/XRef") + { + GOUROU_LOG(DEBUG, "XRef stream at " << object->offset()); + continue; + } - ByteArray clearHexStr = ByteArray(clearData, dataOutLength); - decodedStrings[dictIt->first] = new uPDFParser::HexaString( - clearHexStr.toHex()); + GOUROU_LOG(DEBUG, "Obj " << object->objectId()); - delete[] clearData; - } - } - - for (dictIt = decodedStrings.begin(); dictIt != decodedStrings.end(); dictIt++) - dictionary.replace(dictIt->first, dictIt->second); - - std::vector::iterator datasIt; - std::vector& datas = object->data(); - uPDFParser::Stream* stream; - - for (datasIt = datas.begin(); datasIt != datas.end(); datasIt++) - { - if ((*datasIt)->type() != uPDFParser::DataType::STREAM) - continue; + unsigned char tmpKey[sizeof(decryptedKey)]; - stream = (uPDFParser::Stream*) (*datasIt); - unsigned char* encryptedData = stream->data(); - unsigned int dataLength = stream->dataLength(); - unsigned char* clearData = new unsigned char[dataLength]; - unsigned int dataOutLength; - - GOUROU_LOG(DEBUG, "Decrypt stream id " << object->objectId() << ", size " << stream->dataLength()); + generatePDFObjectKey(ebxVersion->value(), + decryptedKey, sizeof(decryptedKey), + object->objectId(), object->generationNumber(), + tmpKey); - client->decrypt(CryptoInterface::ALGO_RC4, CryptoInterface::CHAIN_ECB, - tmpKey, sizeof(tmpKey), /* Key */ - NULL, 0, /* IV */ - encryptedData, dataLength, - clearData, &dataOutLength); - - stream->setData(clearData, dataOutLength, true); - if (dataOutLength != dataLength) - GOUROU_LOG(DEBUG, "New size " << dataOutLength); - } - } + uPDFParser::Dictionary& dictionary = object->dictionary(); + std::map& dictValues = dictionary.value(); + std::map::iterator dictIt; + std::map decodedStrings; + std::string string; - for(it = ebxObjects.begin(); it != ebxObjects.end(); it++) - parser.removeObject(*it); - - uPDFParser::Object& trailer = parser.getTrailer(); - trailer.deleteKey("Encrypt"); + /* Parse dictionary */ + for (dictIt = dictValues.begin(); dictIt != dictValues.end(); dictIt++) + { + uPDFParser::DataType* dictData = dictIt->second; + if (dictData->type() == uPDFParser::DataType::STRING) + { + string = ((uPDFParser::String*) dictData)->unescapedValue(); - parser.write(filenameOut); + unsigned char* encryptedData = (unsigned char*)string.c_str(); + unsigned int dataLength = string.size(); + unsigned char* clearData = new unsigned char[dataLength]; + unsigned int dataOutLength; + + GOUROU_LOG(DEBUG, "Decrypt string " << dictIt->first << " " << dataLength); + + client->decrypt(CryptoInterface::ALGO_RC4, CryptoInterface::CHAIN_ECB, + tmpKey, sizeof(tmpKey), /* Key */ + NULL, 0, /* IV */ + encryptedData, dataLength, + clearData, &dataOutLength); + + decodedStrings[dictIt->first] = new uPDFParser::String( + std::string((const char*)clearData, dataOutLength)); + + 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++) + dictionary.replace(dictIt->first, dictIt->second); + + std::vector::iterator datasIt; + std::vector& datas = object->data(); + uPDFParser::Stream* stream; + + for (datasIt = datas.begin(); datasIt != datas.end(); datasIt++) + { + if ((*datasIt)->type() != uPDFParser::DataType::STREAM) + continue; + + stream = (uPDFParser::Stream*) (*datasIt); + unsigned char* encryptedData = stream->data(); + unsigned int dataLength = stream->dataLength(); + unsigned char* clearData = new unsigned char[dataLength]; + unsigned int dataOutLength; + + GOUROU_LOG(DEBUG, "Decrypt stream id " << object->objectId() << ", size " << stream->dataLength()); + + client->decrypt(CryptoInterface::ALGO_RC4, CryptoInterface::CHAIN_ECB, + tmpKey, sizeof(tmpKey), /* Key */ + NULL, 0, /* IV */ + encryptedData, dataLength, + clearData, &dataOutLength); + + stream->setData(clearData, dataOutLength, true); + if (dataOutLength != dataLength) + GOUROU_LOG(DEBUG, "New size " << dataOutLength); + } + } + + for(it = ebxObjects.begin(); it != ebxObjects.end(); it++) + parser.removeObject(*it); + + uPDFParser::Object& trailer = parser.getTrailer(); + trailer.deleteKey("Encrypt"); + + parser.write(filenameOut); } - + void DRMProcessor::removeDRM(const std::string& filenameIn, const std::string& filenameOut, - ITEM_TYPE type, const unsigned char* encryptionKey, unsigned encryptionKeySize) + ITEM_TYPE type, const unsigned char* encryptionKey, unsigned encryptionKeySize) { - if (type == PDF) - removePDFDRM(filenameIn, filenameOut, encryptionKey, encryptionKeySize); - else - removeEPubDRM(filenameIn, filenameOut, encryptionKey, encryptionKeySize); + if (type == PDF) + removePDFDRM(filenameIn, filenameOut, encryptionKey, encryptionKeySize); + else + removeEPubDRM(filenameIn, filenameOut, encryptionKey, encryptionKeySize); } } diff --git a/src/loan_token.cpp b/src/loan_token.cpp index 14b50eb..fe320aa 100644 --- a/src/loan_token.cpp +++ b/src/loan_token.cpp @@ -24,57 +24,57 @@ namespace gourou { LoanToken::LoanToken(pugi::xml_document& doc) { - pugi::xml_node node = doc.select_node("/envelope/loanToken").node(); + pugi::xml_node node = doc.select_node("/envelope/loanToken").node(); - if (!node) - EXCEPTION(FFI_INVALID_LOAN_TOKEN, "No loanToken element in document"); + if (!node) + EXCEPTION(FFI_INVALID_LOAN_TOKEN, "No loanToken element in document"); - node = doc.select_node("/envelope/fulfillmentResult/fulfillment").node(); + node = doc.select_node("/envelope/fulfillmentResult/fulfillment").node(); - if (node) - properties["id"] = node.first_child().value(); - else - { - EXCEPTION(FFI_INVALID_LOAN_TOKEN, "No fulfillment element in document"); - } + if (node) + properties["id"] = node.first_child().value(); + else + { + EXCEPTION(FFI_INVALID_LOAN_TOKEN, "No fulfillment element in document"); + } - node = doc.select_node("/envelope/loanToken/operatorURL").node(); + node = doc.select_node("/envelope/loanToken/operatorURL").node(); - if (!node) - EXCEPTION(FFI_INVALID_LOAN_TOKEN, "No loanToken/operatorURL element in document"); + if (!node) + EXCEPTION(FFI_INVALID_LOAN_TOKEN, "No loanToken/operatorURL element in document"); - properties["operatorURL"] = node.first_child().value(); + properties["operatorURL"] = node.first_child().value(); - node = doc.select_node("/envelope/fulfillmentResult/resourceItemInfo/licenseToken/permissions/display/until").node(); + 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 + { + 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"); - } + 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); + if (properties.find(property) == properties.end()) + { + if (_default == "") + EXCEPTION(GOUROU_INVALID_PROPERTY, "Invalid property " << property); - return _default; - } + return _default; + } - return properties[property]; + return properties[property]; } std::string LoanToken::operator[](const std::string& property) { - return getProperty(property); + return getProperty(property); } } diff --git a/src/user.cpp b/src/user.cpp index f09cb73..14251b0 100644 --- a/src/user.cpp +++ b/src/user.cpp @@ -24,60 +24,60 @@ namespace gourou { User::User(DRMProcessor* processor):processor(processor) {} - + User::User(DRMProcessor* processor, const std::string& activationFile): - processor(processor), activationFile(activationFile) + processor(processor), activationFile(activationFile) { - parseActivationFile(); + parseActivationFile(); } void User::parseActivationFile(bool throwOnNull) { - GOUROU_LOG(DEBUG, "Parse activation file " << activationFile); - - if (!activationDoc.load_file(activationFile.c_str())) - { - if (throwOnNull) - EXCEPTION(USER_INVALID_ACTIVATION_FILE, "Invalid activation file"); - return; - } + GOUROU_LOG(DEBUG, "Parse activation file " << activationFile); - try - { - pkcs12 = gourou::extractTextElem(activationDoc, "//adept:pkcs12", throwOnNull); - uuid = gourou::extractTextElem(activationDoc, "//adept:user", throwOnNull); - deviceUUID = gourou::extractTextElem(activationDoc, "//device", throwOnNull); - deviceFingerprint = gourou::extractTextElem(activationDoc, "//fingerprint", throwOnNull); - authenticationCertificate = gourou::extractTextElem(activationDoc, "//adept:authenticationCertificate", throwOnNull); - privateLicenseKey = gourou::extractTextElem(activationDoc, "//adept:privateLicenseKey", throwOnNull); + if (!activationDoc.load_file(activationFile.c_str())) + { + if (throwOnNull) + EXCEPTION(USER_INVALID_ACTIVATION_FILE, "Invalid activation file"); + return; + } - pugi::xpath_node xpath_node = activationDoc.select_node("//adept:username"); - if (xpath_node) - loginMethod = xpath_node.node().attribute("method").value(); - else - { - if (throwOnNull) - EXCEPTION(USER_INVALID_ACTIVATION_FILE, "Invalid activation file"); - } - - if (loginMethod == "anonymous") - username = "anonymous"; - else - username = gourou::extractTextElem(activationDoc, "//adept:username", throwOnNull); - - pugi::xpath_node_set nodeSet = activationDoc.select_nodes("//adept:licenseServices/adept:licenseServiceInfo"); - for (pugi::xpath_node_set::const_iterator it = nodeSet.begin(); - it != nodeSet.end(); ++it) - { - std::string url = gourou::extractTextElem(it->node(), "adept:licenseURL"); - std::string certificate = gourou::extractTextElem(it->node(), "adept:certificate"); - licenseServiceCertificates[url] = certificate; - } - } - catch(gourou::Exception& e) - { - EXCEPTION(USER_INVALID_ACTIVATION_FILE, "Invalid activation file"); - } + try + { + pkcs12 = gourou::extractTextElem(activationDoc, "//adept:pkcs12", throwOnNull); + uuid = gourou::extractTextElem(activationDoc, "//adept:user", throwOnNull); + deviceUUID = gourou::extractTextElem(activationDoc, "//device", throwOnNull); + deviceFingerprint = gourou::extractTextElem(activationDoc, "//fingerprint", throwOnNull); + authenticationCertificate = gourou::extractTextElem(activationDoc, "//adept:authenticationCertificate", throwOnNull); + privateLicenseKey = gourou::extractTextElem(activationDoc, "//adept:privateLicenseKey", throwOnNull); + + pugi::xpath_node xpath_node = activationDoc.select_node("//adept:username"); + if (xpath_node) + loginMethod = xpath_node.node().attribute("method").value(); + else + { + if (throwOnNull) + EXCEPTION(USER_INVALID_ACTIVATION_FILE, "Invalid activation file"); + } + + if (loginMethod == "anonymous") + username = "anonymous"; + else + username = gourou::extractTextElem(activationDoc, "//adept:username", throwOnNull); + + pugi::xpath_node_set nodeSet = activationDoc.select_nodes("//adept:licenseServices/adept:licenseServiceInfo"); + for (pugi::xpath_node_set::const_iterator it = nodeSet.begin(); + it != nodeSet.end(); ++it) + { + std::string url = gourou::extractTextElem(it->node(), "adept:licenseURL"); + std::string certificate = gourou::extractTextElem(it->node(), "adept:certificate"); + licenseServiceCertificates[url] = certificate; + } + } + catch(gourou::Exception& e) + { + EXCEPTION(USER_INVALID_ACTIVATION_FILE, "Invalid activation file"); + } } std::string& User::getUUID() { return uuid; } @@ -88,136 +88,136 @@ namespace gourou { std::string& User::getLoginMethod() { return loginMethod; } std::string& User::getAuthenticationCertificate() { return authenticationCertificate; } std::string& User::getPrivateLicenseKey() { return privateLicenseKey; } - + void User::readActivation(pugi::xml_document& doc) { - if (!doc.load_file(activationFile.c_str())) - EXCEPTION(USER_INVALID_ACTIVATION_FILE, "Invalid activation file"); + if (!doc.load_file(activationFile.c_str())) + EXCEPTION(USER_INVALID_ACTIVATION_FILE, "Invalid activation file"); } void User::updateActivationFile(const char* data) { - GOUROU_LOG(INFO, "Update Activation file : " << std::endl << data); + GOUROU_LOG(INFO, "Update Activation file : " << std::endl << data); - writeFile(activationFile, (unsigned char*)data, strlen(data)); - - parseActivationFile(false); + writeFile(activationFile, (unsigned char*)data, strlen(data)); + + parseActivationFile(false); } - + void User::updateActivationFile(const pugi::xml_document& doc) { - StringXMLWriter xmlWriter; - doc.save(xmlWriter, " "); - updateActivationFile(xmlWriter.getResult().c_str()); + StringXMLWriter xmlWriter; + doc.save(xmlWriter, " "); + updateActivationFile(xmlWriter.getResult().c_str()); } std::string User::getProperty(const std::string property) { - pugi::xpath_node xpathRes = activationDoc.select_node(property.c_str()); - if (!xpathRes) - EXCEPTION(USER_NO_PROPERTY, "Property " << property << " not found in activation.xml"); + pugi::xpath_node xpathRes = activationDoc.select_node(property.c_str()); + if (!xpathRes) + EXCEPTION(USER_NO_PROPERTY, "Property " << property << " not found in activation.xml"); - std::string res = xpathRes.node().first_child().value(); - return trim(res); + std::string res = xpathRes.node().first_child().value(); + return trim(res); } - + pugi::xpath_node_set User::getProperties(const std::string property) { - return activationDoc.select_nodes(property.c_str()); + return activationDoc.select_nodes(property.c_str()); } User* User::createUser(DRMProcessor* processor, const std::string& dirName, const std::string& ACSServer) { - struct stat _stat; + struct stat _stat; - if (stat(dirName.c_str(), &_stat) != 0) - { - if (mkdir_p(dirName.c_str(), S_IRWXU)) - EXCEPTION(USER_MKPATH, "Unable to create " << dirName) - } + if (stat(dirName.c_str(), &_stat) != 0) + { + if (mkdir_p(dirName.c_str(), S_IRWXU)) + EXCEPTION(USER_MKPATH, "Unable to create " << dirName) + } - User* user = new User(processor); - bool doUpdate = false; - - user->activationFile = dirName + "/activation.xml"; - user->parseActivationFile(false); + User* user = new User(processor); + bool doUpdate = false; - pugi::xpath_node nodeActivationInfo = user->activationDoc.select_node("activation_info"); - pugi::xpath_node nodeActivationServiceInfo = nodeActivationInfo.node().select_node("adept:activationServiceInfo"); - pugi::xml_node activationInfo; - pugi::xml_node activationServiceInfo; - - if (nodeActivationInfo && nodeActivationServiceInfo) - { - GOUROU_LOG(DEBUG, "Read previous activation configuration"); - activationInfo = nodeActivationInfo.node(); - activationServiceInfo = nodeActivationServiceInfo.node(); - } - else - { - GOUROU_LOG(DEBUG, "Create new activation"); + user->activationFile = dirName + "/activation.xml"; + user->parseActivationFile(false); - user->activationDoc.reset(); - - pugi::xml_node decl = user->activationDoc.append_child(pugi::node_declaration); - decl.append_attribute("version") = "1.0"; - activationInfo = user->activationDoc.append_child("activationInfo"); - activationInfo.append_attribute("xmlns") = ADOBE_ADEPT_NS; - activationServiceInfo = activationInfo.append_child("adept:activationServiceInfo"); - activationServiceInfo.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS; - - // Go to activation Service Info - std::string activationURL = ACSServer + "/ActivationServiceInfo"; - ByteArray activationServiceInfoReply = processor->sendRequest(activationURL); - pugi::xml_document docActivationServiceInfo; - docActivationServiceInfo.load_buffer(activationServiceInfoReply.data(), - activationServiceInfoReply.length()); + pugi::xpath_node nodeActivationInfo = user->activationDoc.select_node("activation_info"); + pugi::xpath_node nodeActivationServiceInfo = nodeActivationInfo.node().select_node("adept:activationServiceInfo"); + pugi::xml_node activationInfo; + pugi::xml_node activationServiceInfo; - pugi::xpath_node path = docActivationServiceInfo.select_node("//authURL"); - appendTextElem(activationServiceInfo, "adept:authURL", path.node().first_child().value()); - path = docActivationServiceInfo.select_node("//userInfoURL"); - appendTextElem(activationServiceInfo, "adept:userInfoURL", path.node().first_child().value()); - appendTextElem(activationServiceInfo, "adept:activationURL", ACSServer); - path = docActivationServiceInfo.select_node("//certificate"); - appendTextElem(activationServiceInfo, "adept:certificate", path.node().first_child().value()); - doUpdate = true; - } - - pugi::xpath_node nodeAuthenticationCertificate = activationServiceInfo.select_node("adept:authenticationCertificate"); + if (nodeActivationInfo && nodeActivationServiceInfo) + { + GOUROU_LOG(DEBUG, "Read previous activation configuration"); + activationInfo = nodeActivationInfo.node(); + activationServiceInfo = nodeActivationServiceInfo.node(); + } + else + { + GOUROU_LOG(DEBUG, "Create new activation"); - if (!nodeAuthenticationCertificate) - { - GOUROU_LOG(DEBUG, "Create new activation, authentication part"); + user->activationDoc.reset(); - pugi::xpath_node xpathRes = activationServiceInfo.select_node("adept:authURL"); - if (!xpathRes) - EXCEPTION(USER_NO_AUTHENTICATION_URL, "No authentication URL"); - - std::string authenticationURL = xpathRes.node().first_child().value(); - authenticationURL = trim(authenticationURL) + "/AuthenticationServiceInfo"; - - // Go to authentication Service Info - ByteArray authenticationServiceInfo = processor->sendRequest(authenticationURL); - pugi::xml_document docAuthenticationServiceInfo; - docAuthenticationServiceInfo.load_buffer(authenticationServiceInfo.data(), authenticationServiceInfo.length()); - pugi::xpath_node path = docAuthenticationServiceInfo.select_node("//certificate"); - appendTextElem(activationServiceInfo, "adept:authenticationCertificate", path.node().first_child().value()); - doUpdate = true; - } + pugi::xml_node decl = user->activationDoc.append_child(pugi::node_declaration); + decl.append_attribute("version") = "1.0"; + activationInfo = user->activationDoc.append_child("activationInfo"); + activationInfo.append_attribute("xmlns") = ADOBE_ADEPT_NS; + activationServiceInfo = activationInfo.append_child("adept:activationServiceInfo"); + activationServiceInfo.append_attribute("xmlns:adept") = ADOBE_ADEPT_NS; - if (doUpdate) - user->updateActivationFile(user->activationDoc); - - - return user; + // Go to activation Service Info + std::string activationURL = ACSServer + "/ActivationServiceInfo"; + ByteArray activationServiceInfoReply = processor->sendRequest(activationURL); + pugi::xml_document docActivationServiceInfo; + docActivationServiceInfo.load_buffer(activationServiceInfoReply.data(), + activationServiceInfoReply.length()); + + pugi::xpath_node path = docActivationServiceInfo.select_node("//authURL"); + appendTextElem(activationServiceInfo, "adept:authURL", path.node().first_child().value()); + path = docActivationServiceInfo.select_node("//userInfoURL"); + appendTextElem(activationServiceInfo, "adept:userInfoURL", path.node().first_child().value()); + appendTextElem(activationServiceInfo, "adept:activationURL", ACSServer); + path = docActivationServiceInfo.select_node("//certificate"); + appendTextElem(activationServiceInfo, "adept:certificate", path.node().first_child().value()); + doUpdate = true; + } + + pugi::xpath_node nodeAuthenticationCertificate = activationServiceInfo.select_node("adept:authenticationCertificate"); + + if (!nodeAuthenticationCertificate) + { + GOUROU_LOG(DEBUG, "Create new activation, authentication part"); + + pugi::xpath_node xpathRes = activationServiceInfo.select_node("adept:authURL"); + if (!xpathRes) + EXCEPTION(USER_NO_AUTHENTICATION_URL, "No authentication URL"); + + std::string authenticationURL = xpathRes.node().first_child().value(); + authenticationURL = trim(authenticationURL) + "/AuthenticationServiceInfo"; + + // Go to authentication Service Info + ByteArray authenticationServiceInfo = processor->sendRequest(authenticationURL); + pugi::xml_document docAuthenticationServiceInfo; + docAuthenticationServiceInfo.load_buffer(authenticationServiceInfo.data(), authenticationServiceInfo.length()); + pugi::xpath_node path = docAuthenticationServiceInfo.select_node("//certificate"); + appendTextElem(activationServiceInfo, "adept:authenticationCertificate", path.node().first_child().value()); + doUpdate = true; + } + + if (doUpdate) + user->updateActivationFile(user->activationDoc); + + + return user; } std::string User::getLicenseServiceCertificate(std::string url) { - if (licenseServiceCertificates.count(trim(url))) - return licenseServiceCertificates[trim(url)]; + if (licenseServiceCertificates.count(trim(url))) + return licenseServiceCertificates[trim(url)]; - return ""; + return ""; } } diff --git a/utils/Makefile b/utils/Makefile index 5fa5fe2..ba85377 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -6,9 +6,10 @@ TARGETS=$(TARGET_BINARIES) launcher MAN_PAGES=acsmdownloader adept_activate adept_remove adept_loan_mgt -CXXFLAGS=-Wall -fPIC -I$(ROOT)/include +CXXFLAGS=-Wall -fPIC -I$(ROOT)/include -fdata-sections -ffunction-sections STATIC_DEP= +# LDFLAGS += -Wl,--gc-sections LDFLAGS += -L$(ROOT) -lcrypto -lzip -lz -lcurl -lpugixml ifneq ($(STATIC_UTILS),) @@ -31,7 +32,7 @@ COMMON_LIB = utils.a all: $(TARGETS) ${COMMON_LIB}: $(COMMON_DEPS) - $(CXX) $(CXXFLAGS) $(COMMON_DEPS) $(LDFLAGS) -c + $(CXX) $(CXXFLAGS) $(COMMON_DEPS) -c $(AR) crs $@ $(COMMON_OBJECTS) %: %.cpp $(COMMON_LIB) $(STATIC_DEP) diff --git a/utils/acsmdownloader.cpp b/utils/acsmdownloader.cpp index 4d23748..554a1a9 100644 --- a/utils/acsmdownloader.cpp +++ b/utils/acsmdownloader.cpp @@ -4,16 +4,16 @@ 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. + 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. + 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. - + 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 @@ -52,137 +52,137 @@ static bool notify = true; class ACSMDownloader { public: - + int run() { - int ret = 0; - try - { - gourou::DRMProcessor processor(&client, deviceFile, activationFile, devicekeyFile); - gourou::User* user = processor.getUser(); - - if (exportPrivateKey) - { - std::string filename; - if (!outputFile) - filename = std::string("Adobe_PrivateLicenseKey--") + user->getUsername() + ".der"; - else - filename = outputFile; - - if (outputDir) - { - if (!fileExists(outputDir)) - mkpath(outputDir); - - filename = std::string(outputDir) + "/" + filename; - } + int ret = 0; + try + { + gourou::DRMProcessor processor(&client, deviceFile, activationFile, devicekeyFile); + gourou::User* user = processor.getUser(); + + if (exportPrivateKey) + { + std::string filename; + if (!outputFile) + filename = std::string("Adobe_PrivateLicenseKey--") + user->getUsername() + ".der"; + else + filename = outputFile; + + if (outputDir) + { + if (!fileExists(outputDir)) + mkpath(outputDir); + + filename = std::string(outputDir) + "/" + filename; + } - processor.exportPrivateLicenseKey(filename); + processor.exportPrivateLicenseKey(filename); - std::cout << "Private license key exported to " << filename << std::endl; - } - else - { - gourou::FulfillmentItem* item = processor.fulfill(acsmFile, notify); + std::cout << "Private license key exported to " << filename << std::endl; + } + else + { + gourou::FulfillmentItem* item = processor.fulfill(acsmFile, notify); - std::string filename; - if (!outputFile) - { - filename = item->getMetadata("title"); - if (filename == "") - filename = "output"; - else - { - // Remove invalid characters - std::replace(filename.begin(), filename.end(), '/', '_'); - } - } - else - filename = outputFile; - - if (outputDir) - { - if (!fileExists(outputDir)) - mkpath(outputDir); + std::string filename; + if (!outputFile) + { + filename = item->getMetadata("title"); + if (filename == "") + filename = "output"; + else + { + // Remove invalid characters + std::replace(filename.begin(), filename.end(), '/', '_'); + } + } + else + filename = outputFile; + + if (outputDir) + { + if (!fileExists(outputDir)) + mkpath(outputDir); - filename = std::string(outputDir) + "/" + filename; - } - - gourou::DRMProcessor::ITEM_TYPE type = processor.download(item, filename, resume); + filename = std::string(outputDir) + "/" + filename; + } + + gourou::DRMProcessor::ITEM_TYPE type = processor.download(item, filename, resume); - if (!outputFile) - { - std::string finalName = filename; - if (type == gourou::DRMProcessor::ITEM_TYPE::PDF) - finalName += ".pdf"; - else - finalName += ".epub"; - rename(filename.c_str(), finalName.c_str()); - filename = finalName; - } - std::cout << "Created " << filename << std::endl; + if (!outputFile) + { + std::string finalName = filename; + if (type == gourou::DRMProcessor::ITEM_TYPE::PDF) + finalName += ".pdf"; + else + finalName += ".epub"; + rename(filename.c_str(), finalName.c_str()); + filename = finalName; + } + std::cout << "Created " << filename << std::endl; - serializeLoanToken(item); - } - } catch(std::exception& e) - { - std::cout << e.what() << std::endl; - ret = 1; - } + serializeLoanToken(item); + } + } catch(std::exception& e) + { + std::cout << e.what() << std::endl; + ret = 1; + } - return ret; + return ret; } void serializeLoanToken(gourou::FulfillmentItem* item) { - gourou::LoanToken* token = item->getLoanToken(); + gourou::LoanToken* token = item->getLoanToken(); - // No loan token available - if (!token) - return; + // No loan token available + if (!token) + return; - pugi::xml_document doc; + pugi::xml_document doc; - pugi::xml_node decl = doc.append_child(pugi::node_declaration); - decl.append_attribute("version") = "1.0"; + pugi::xml_node decl = doc.append_child(pugi::node_declaration); + decl.append_attribute("version") = "1.0"; - pugi::xml_node root = doc.append_child("loanToken"); - gourou::appendTextElem(root, "id", (*token)["id"]); - gourou::appendTextElem(root, "operatorURL", (*token)["operatorURL"]); - gourou::appendTextElem(root, "validity", (*token)["validity"]); - gourou::appendTextElem(root, "name", item->getMetadata("title")); + pugi::xml_node root = doc.append_child("loanToken"); + gourou::appendTextElem(root, "id", (*token)["id"]); + gourou::appendTextElem(root, "operatorURL", (*token)["operatorURL"]); + gourou::appendTextElem(root, "validity", (*token)["validity"]); + gourou::appendTextElem(root, "name", item->getMetadata("title")); - char * activationDir = strdup(deviceFile); - activationDir = dirname(activationDir); - - gourou::StringXMLWriter xmlWriter; - doc.save(xmlWriter, " "); - std::string xmlStr = xmlWriter.getResult(); + char * activationDir = strdup(deviceFile); + activationDir = dirname(activationDir); + + gourou::StringXMLWriter xmlWriter; + doc.save(xmlWriter, " "); + std::string xmlStr = xmlWriter.getResult(); - // Use first bytes of SHA1(id) as filename - unsigned char sha1[gourou::SHA1_LEN]; - client.digest("SHA1", (unsigned char*)(*token)["id"].c_str(), (*token)["id"].size(), sha1); - gourou::ByteArray tmp(sha1, sizeof(sha1)); - std::string filenameHex = tmp.toHex(); - std::string filename(filenameHex.c_str(), ID_HASH_SIZE); - std::string fullPath = std::string(activationDir); - fullPath += std::string ("/") + std::string(LOANS_DIR); - mkpath(fullPath.c_str()); - fullPath += filename + std::string(".xml"); - gourou::writeFile(fullPath, xmlStr); + // Use first bytes of SHA1(id) as filename + unsigned char sha1[gourou::SHA1_LEN]; + client.digest("SHA1", (unsigned char*)(*token)["id"].c_str(), (*token)["id"].size(), sha1); + gourou::ByteArray tmp(sha1, sizeof(sha1)); + std::string filenameHex = tmp.toHex(); + std::string filename(filenameHex.c_str(), ID_HASH_SIZE); + std::string fullPath = std::string(activationDir); + fullPath += std::string ("/") + std::string(LOANS_DIR); + mkpath(fullPath.c_str()); + fullPath += filename + std::string(".xml"); + gourou::writeFile(fullPath, xmlStr); - std::cout << "Loan token serialized into " << fullPath << std::endl; + std::cout << "Loan token serialized into " << fullPath << std::endl; - free(activationDir); + free(activationDir); } - + private: DRMProcessorClientImpl client; -}; +}; static void usage(const char* cmd) -{ +{ std::cout << basename((char*)cmd) << " download EPUB file from ACSM request file" << std::endl << std::endl; std::cout << "Usage: " << basename((char*)cmd) << " [OPTIONS] file.acsm" << std::endl << std::endl; std::cout << "Global Options:" << std::endl; @@ -203,7 +203,7 @@ static void usage(const char* cmd) std::cout << " " << "-k|--device-key-file" << "\t" << "private device key file (eg devicesalt/devkey.bin) from eReader" << std::endl; std::cout << std::endl; - + std::cout << "Environment:" << std::endl; std::cout << "Device file, activation file and device key file are optionals. If not set, they are looked into :" << std::endl << std::endl; std::cout << " * $ADEPT_DIR environment variable" << std::endl; @@ -223,90 +223,90 @@ int main(int argc, char** argv) int verbose = gourou::DRMProcessor::getLogLevel(); while (1) { - int option_index = 0; - static struct option long_options[] = { - {"adept-directory", required_argument, 0, 'D' }, - {"device-file", required_argument, 0, 'd' }, - {"activation-file", required_argument, 0, 'a' }, - {"device-key-file", required_argument, 0, 'k' }, - {"output-dir", required_argument, 0, 'O' }, - {"output-file", required_argument, 0, 'o' }, - {"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 } - }; + int option_index = 0; + static struct option long_options[] = { + {"adept-directory", required_argument, 0, 'D' }, + {"device-file", required_argument, 0, 'd' }, + {"activation-file", required_argument, 0, 'a' }, + {"device-key-file", required_argument, 0, 'k' }, + {"output-dir", required_argument, 0, 'O' }, + {"output-file", required_argument, 0, 'o' }, + {"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:erNvVh", + c = getopt_long(argc, argv, "D:d:a:k:O:o:f:erNvVh", long_options, &option_index); - if (c == -1) - break; + if (c == -1) + break; - switch (c) { - case 'D': - _deviceFile = std::string(optarg) + "/device.xml"; - _activationFile = std::string(optarg) + "/activation.xml"; - _devicekeyFile = std::string(optarg) + "/devicesalt"; - deviceFile = _deviceFile.c_str(); - activationFile = _activationFile.c_str(); - devicekeyFile = _devicekeyFile.c_str(); - break; - case 'd': - deviceFile = optarg; - break; - case 'a': - activationFile = optarg; - break; - case 'k': - devicekeyFile = optarg; - break; - case 'f': - acsmFile = optarg; - break; - case 'O': - outputDir = optarg; - break; - case 'o': - outputFile = optarg; - break; - case 'e': - exportPrivateKey = true; - break; - case 'r': - resume = true; - break; - case 'N': - notify = false; - break; - case 'v': - verbose++; - break; - case 'V': - version(); - return 0; - case 'h': - usage(argv[0]); - return 0; - default: - usage(argv[0]); - return -1; - } + switch (c) { + case 'D': + _deviceFile = std::string(optarg) + "/device.xml"; + _activationFile = std::string(optarg) + "/activation.xml"; + _devicekeyFile = std::string(optarg) + "/devicesalt"; + deviceFile = _deviceFile.c_str(); + activationFile = _activationFile.c_str(); + devicekeyFile = _devicekeyFile.c_str(); + break; + case 'd': + deviceFile = optarg; + break; + case 'a': + activationFile = optarg; + break; + case 'k': + devicekeyFile = optarg; + break; + case 'f': + acsmFile = optarg; + break; + case 'O': + outputDir = optarg; + break; + case 'o': + outputFile = optarg; + break; + case 'e': + exportPrivateKey = true; + break; + case 'r': + resume = true; + break; + case 'N': + notify = false; + break; + case 'v': + verbose++; + break; + case 'V': + version(); + return 0; + case 'h': + usage(argv[0]); + return 0; + default: + usage(argv[0]); + return -1; + } } - + gourou::DRMProcessor::setLogLevel(verbose); if (optind == argc-1) - acsmFile = argv[optind]; + acsmFile = argv[optind]; if ((!acsmFile && !exportPrivateKey) || (outputDir && !outputDir[0]) || - (outputFile && !outputFile[0])) + (outputFile && !outputFile[0])) { - usage(argv[0]); - return -1; + usage(argv[0]); + return -1; } ACSMDownloader downloader; @@ -316,44 +316,44 @@ int main(int argc, char** argv) const char* orig; for (i=0; i<(int)ARRAY_SIZE(files); i++) { - orig = *files[i]; - *files[i] = findFile(*files[i]); - if (!*files[i]) - { - std::cout << "Error : " << orig << " doesn't exists, did you activate your device ?" << std::endl; - ret = -1; - hasErrors = true; - } + orig = *files[i]; + *files[i] = findFile(*files[i]); + if (!*files[i]) + { + std::cout << "Error : " << orig << " doesn't exists, did you activate your device ?" << std::endl; + ret = -1; + hasErrors = true; + } } if (hasErrors) - goto end; - + goto end; + if (exportPrivateKey) { - if (acsmFile) - { - usage(argv[0]); - return -1; - } + if (acsmFile) + { + usage(argv[0]); + return -1; + } } else { - if (!fileExists(acsmFile)) - { - std::cout << "Error : " << acsmFile << " doesn't exists" << std::endl; - ret = -1; - goto end; - } + if (!fileExists(acsmFile)) + { + std::cout << "Error : " << acsmFile << " doesn't exists" << std::endl; + ret = -1; + goto end; + } } - + ret = downloader.run(); end: for (i=0; i<(int)ARRAY_SIZE(files); i++) { - if (*files[i]) - free((void*)*files[i]); + if (*files[i]) + free((void*)*files[i]); } return ret; diff --git a/utils/adept_activate.cpp b/utils/adept_activate.cpp index 808fbe1..b224de5 100644 --- a/utils/adept_activate.cpp +++ b/utils/adept_activate.cpp @@ -4,16 +4,16 @@ 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. + 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. + 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. - + 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 @@ -64,69 +64,69 @@ static int getch() { static std::string getpass(const char *prompt, bool show_asterisk=false) { - const char BACKSPACE=127; - const char RETURN=10; + const char BACKSPACE=127; + const char RETURN=10; - std::string password; - unsigned char ch=0; + std::string password; + unsigned char ch=0; - std::cout <signIn(username, password); - processor->activateDevice(); + processor->signIn(username, password); + processor->activateDevice(); - std::cout << username << " fully signed and device activated in " << outputDir << std::endl; - } catch(std::exception& e) - { - std::cout << e.what() << std::endl; - ret = 1; - } + std::cout << username << " fully signed and device activated in " << outputDir << std::endl; + } catch(std::exception& e) + { + std::cout << e.what() << std::endl; + ret = 1; + } - return ret; + return ret; } -}; +}; static void usage(const char* cmd) { std::cout << basename((char*)cmd) << " create new device files used by ADEPT DRM" << std::endl << std::endl; - + std::cout << "Usage: " << basename((char*)cmd) << " OPTIONS" << std::endl << std::endl; std::cout << "Global Options:" << std::endl; @@ -160,129 +160,129 @@ int main(int argc, char** argv) const char* _outputDir = outputDir; int verbose = gourou::DRMProcessor::getLogLevel(); bool anonymous = false; - + while (1) { - int option_index = 0; - static struct option long_options[] = { - {"anonymous", no_argument , 0, 'a' }, - {"username", required_argument, 0, 'u' }, - {"password", required_argument, 0, 'p' }, - {"output-dir", required_argument, 0, 'O' }, - {"hobbes-version",required_argument, 0, 'H' }, - {"random-serial", no_argument, 0, 'r' }, - {"verbose", no_argument, 0, 'v' }, - {"version", no_argument, 0, 'V' }, - {"help", no_argument, 0, 'h' }, - {0, 0, 0, 0 } - }; + int option_index = 0; + static struct option long_options[] = { + {"anonymous", no_argument , 0, 'a' }, + {"username", required_argument, 0, 'u' }, + {"password", required_argument, 0, 'p' }, + {"output-dir", required_argument, 0, 'O' }, + {"hobbes-version",required_argument, 0, 'H' }, + {"random-serial", no_argument, 0, 'r' }, + {"verbose", no_argument, 0, 'v' }, + {"version", no_argument, 0, 'V' }, + {"help", no_argument, 0, 'h' }, + {0, 0, 0, 0 } + }; - c = getopt_long(argc, argv, "au:p:O:H:rvVh", + c = getopt_long(argc, argv, "au:p:O:H:rvVh", long_options, &option_index); - if (c == -1) - break; + if (c == -1) + break; - switch (c) { - case 'a': - anonymous = true; - break; - case 'u': - username = optarg; - break; - case 'p': - password = optarg; - break; - case 'O': - _outputDir = optarg; - break; - case 'H': - hobbesVersion = optarg; - break; - case 'v': - verbose++; - break; - case 'V': - version(); - return 0; - case 'h': - usage(argv[0]); - return 0; - case 'r': - randomSerial = true; - break; - default: - usage(argv[0]); - return -1; - } + switch (c) { + case 'a': + anonymous = true; + break; + case 'u': + username = optarg; + break; + case 'p': + password = optarg; + break; + case 'O': + _outputDir = optarg; + break; + case 'H': + hobbesVersion = optarg; + break; + case 'v': + verbose++; + break; + case 'V': + version(); + return 0; + case 'h': + usage(argv[0]); + return 0; + case 'r': + randomSerial = true; + break; + default: + usage(argv[0]); + return -1; + } } - + gourou::DRMProcessor::setLogLevel(verbose); if ((!username && !anonymous) || - (username && anonymous)) + (username && anonymous)) { - usage(argv[0]); - return -1; + usage(argv[0]); + return -1; } if (anonymous) { - username = "anonymous"; - password = ""; + username = "anonymous"; + password = ""; } - + if (!_outputDir || _outputDir[0] == 0) { - outputDir = strdup(gourou::DRMProcessor::getDefaultAdeptDir().c_str()); + outputDir = strdup(gourou::DRMProcessor::getDefaultAdeptDir().c_str()); } else { - // Relative path - if (_outputDir[0] == '.' || _outputDir[0] != '/') - { - // realpath doesn't works if file/dir doesn't exists - if (fileExists(_outputDir)) - outputDir = strdup(realpath(_outputDir, 0)); - else - outputDir = strdup(abspath(_outputDir)); - } - else - outputDir = strdup(_outputDir); + // Relative path + if (_outputDir[0] == '.' || _outputDir[0] != '/') + { + // realpath doesn't works if file/dir doesn't exists + if (fileExists(_outputDir)) + outputDir = strdup(realpath(_outputDir, 0)); + else + outputDir = strdup(abspath(_outputDir)); + } + else + outputDir = strdup(_outputDir); } std::string pass; if (fileExists(outputDir)) { - int key; - - while (true) - { - std::cout << "!! Warning !! : " << outputDir << " already exists." << std::endl; - std::cout << "All your data will be overwrite. Would you like to continue ? [y/N] " << std::flush ; - key = getchar(); - if (key == 'n' || key == 'N' || key == '\n' || key == '\r') - goto end; - if (key == 'y' || key == 'Y') - break; - } + int key; + + while (true) + { + std::cout << "!! Warning !! : " << outputDir << " already exists." << std::endl; + std::cout << "All your data will be overwrite. Would you like to continue ? [y/N] " << std::flush ; + key = getchar(); + if (key == 'n' || key == 'N' || key == '\n' || key == '\r') + goto end; + if (key == 'y' || key == 'Y') + break; + } - // Clean STDIN buf - while ((key = getchar()) != '\n') - ; + // Clean STDIN buf + while ((key = getchar()) != '\n') + ; } if (!password) { - char prompt[128]; - std::snprintf(prompt, sizeof(prompt), "Enter password for <%s> : ", username); - pass = getpass((const char*)prompt, false); - password = pass.c_str(); + char prompt[128]; + std::snprintf(prompt, sizeof(prompt), "Enter password for <%s> : ", username); + pass = getpass((const char*)prompt, false); + password = pass.c_str(); } - + ADEPTActivate activate; ret = activate.run(); -end: +end: free((void*)outputDir); return ret; } diff --git a/utils/adept_loan_mgt.cpp b/utils/adept_loan_mgt.cpp index ab52d3f..fd060ab 100644 --- a/utils/adept_loan_mgt.cpp +++ b/utils/adept_loan_mgt.cpp @@ -4,16 +4,16 @@ 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. + 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. + 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. - + 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 @@ -60,297 +60,297 @@ struct Loan std::string operatorURL; std::string validity; std::string bookName; - + std::string path; }; - + class LoanMGT { public: ~LoanMGT() { - for (const auto& kv : loanedBooks) - delete kv.second; + for (const auto& kv : loanedBooks) + delete kv.second; } - + int run() { - int ret = 0; - try - { - DRMProcessorClientImpl client; - gourou::DRMProcessor processor(&client, deviceFile, activationFile, devicekeyFile); + int ret = 0; + try + { + DRMProcessorClientImpl client; + gourou::DRMProcessor processor(&client, deviceFile, activationFile, devicekeyFile); - loadLoanedBooks(); + loadLoanedBooks(); - if (list) - displayLoanList(); - else if (returnID) - returnBook(processor); - else if (deleteID) - deleteLoan(); - } catch(std::exception& e) - { - std::cout << e.what() << std::endl; - ret = 1; - } + if (list) + displayLoanList(); + else if (returnID) + returnBook(processor); + else if (deleteID) + deleteLoan(); + } catch(std::exception& e) + { + std::cout << e.what() << std::endl; + ret = 1; + } - return ret; + return ret; } private: void loadLoanedBooks() { - DIR *dp; - struct dirent *ep; - int entryLen; - struct Loan* loan; - char * res; - - std::string loanDir = std::string(adeptDir) + std::string("/") + LOANS_DIR; + DIR *dp; + struct dirent *ep; + int entryLen; + struct Loan* loan; + char * res; + + std::string loanDir = std::string(adeptDir) + std::string("/") + LOANS_DIR; - if (!fileExists(loanDir.c_str())) - return; - - dp = opendir (loanDir.c_str()); + if (!fileExists(loanDir.c_str())) + return; + + dp = opendir (loanDir.c_str()); - if(!dp) - EXCEPTION(gourou::USER_INVALID_INPUT, "Cannot read directory " << loanDir); + if(!dp) + EXCEPTION(gourou::USER_INVALID_INPUT, "Cannot read directory " << loanDir); - while ((ep = readdir (dp))) - { - if (ep->d_type != DT_LNK && - ep->d_type != DT_REG) - continue; + while ((ep = readdir (dp))) + { + if (ep->d_type != DT_LNK && + ep->d_type != DT_REG) + continue; - entryLen = strlen(ep->d_name); + entryLen = strlen(ep->d_name); - if (entryLen <= 4 || - ep->d_name[entryLen-4] != '.' || - ep->d_name[entryLen-3] != 'x' || - ep->d_name[entryLen-2] != 'm' || - ep->d_name[entryLen-1] != 'l') - continue; + if (entryLen <= 4 || + ep->d_name[entryLen-4] != '.' || + ep->d_name[entryLen-3] != 'x' || + ep->d_name[entryLen-2] != 'm' || + ep->d_name[entryLen-1] != 'l') + continue; - std::string id = std::string(ep->d_name, entryLen-4); - - loan = new Loan; - loan->path = loanDir + std::string("/") + ep->d_name; + std::string id = std::string(ep->d_name, entryLen-4); + + loan = new Loan; + loan->path = loanDir + std::string("/") + ep->d_name; - pugi::xml_document xmlDoc; - pugi::xml_node node; + pugi::xml_document xmlDoc; + pugi::xml_node node; - if (!xmlDoc.load_file(loan->path.c_str(), pugi::parse_ws_pcdata_single|pugi::parse_escapes, pugi::encoding_utf8)) - { - std::cout << "Invalid loan entry " << loan->path << std::endl; - goto error; - } + if (!xmlDoc.load_file(loan->path.c_str(), pugi::parse_ws_pcdata_single|pugi::parse_escapes, pugi::encoding_utf8)) + { + std::cout << "Invalid loan entry " << loan->path << std::endl; + goto error; + } - // id - node = xmlDoc.select_node("//id").node(); - if (!node) - { - std::cout << "Invalid loan entry " << ep->d_name << ", no id element" << std::endl; - goto error; - } - loan->id = node.first_child().value(); + // id + node = xmlDoc.select_node("//id").node(); + if (!node) + { + std::cout << "Invalid loan entry " << ep->d_name << ", no id element" << std::endl; + goto error; + } + loan->id = node.first_child().value(); - // operatorURL - node = xmlDoc.select_node("//operatorURL").node(); - if (!node) - { - std::cout << "Invalid loan entry " << ep->d_name << ", no operatorURL element" << std::endl; - goto error; - } - loan->operatorURL = node.first_child().value(); + // operatorURL + node = xmlDoc.select_node("//operatorURL").node(); + if (!node) + { + std::cout << "Invalid loan entry " << ep->d_name << ", no operatorURL element" << std::endl; + goto error; + } + loan->operatorURL = node.first_child().value(); - // validity - node = xmlDoc.select_node("//validity").node(); - if (!node) - { - std::cout << "Invalid loan entry " << ep->d_name << ", no validity element" << std::endl; - goto error; - } - loan->validity = node.first_child().value(); + // validity + node = xmlDoc.select_node("//validity").node(); + if (!node) + { + std::cout << "Invalid loan entry " << ep->d_name << ", no validity element" << std::endl; + goto error; + } + loan->validity = node.first_child().value(); - // bookName - node = xmlDoc.select_node("//name").node(); - if (!node) - { - std::cout << "Invalid loan entry " << ep->d_name << ", no name element" << std::endl; - goto error; - } - loan->bookName = node.first_child().value(); + // bookName + node = xmlDoc.select_node("//name").node(); + if (!node) + { + std::cout << "Invalid loan entry " << ep->d_name << ", no name element" << std::endl; + goto error; + } + loan->bookName = node.first_child().value(); - struct tm tm; + struct tm tm; #ifdef __ANDROID__ - res = strptime(loan->validity.c_str(), "%Y-%m-%dT%H:%M:%S%z", &tm); + res = strptime(loan->validity.c_str(), "%Y-%m-%dT%H:%M:%S%z", &tm); #else - res = strptime(loan->validity.c_str(), "%Y-%m-%dT%H:%M:%S%Z", &tm); + res = strptime(loan->validity.c_str(), "%Y-%m-%dT%H:%M:%S%Z", &tm); #endif - - if (res != NULL && *res == 0) - { - if (mktime(&tm) <= time(NULL)) - loan->validity = " (Expired)"; - } - else - { - std::cout << "Unable to parse validity timestamp :" << loan->validity << std::endl; - loan->validity = " (Unknown)"; - } - - loanedBooks[id] = loan; - continue; + + if (res != NULL && *res == 0) + { + if (mktime(&tm) <= time(NULL)) + loan->validity = " (Expired)"; + } + else + { + std::cout << "Unable to parse validity timestamp :" << loan->validity << std::endl; + loan->validity = " (Unknown)"; + } + + loanedBooks[id] = loan; + continue; - error: - if (loan) - delete loan; - } + error: + if (loan) + delete loan; + } - closedir (dp); + closedir (dp); } void displayLoanList() { - if (!loanedBooks.size()) - { - std::cout << "No books loaned" << std::endl; - return; - } + if (!loanedBooks.size()) + { + std::cout << "No books loaned" << std::endl; + return; + } - struct Loan* loan; - unsigned int maxSizeBookName=0; - // Compute max size - for (const auto& kv : loanedBooks) - { - loan = kv.second; - if (loan->bookName.size() > maxSizeBookName) - maxSizeBookName = loan->bookName.size(); - } + struct Loan* loan; + unsigned int maxSizeBookName=0; + // Compute max size + for (const auto& kv : loanedBooks) + { + loan = kv.second; + if (loan->bookName.size() > maxSizeBookName) + maxSizeBookName = loan->bookName.size(); + } - /* 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++; + /* 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++; - // std::cout << " ID Book Expiration" << std::endl; - // std::cout << "------------------------------" << std::endl; + // std::cout << " ID Book Expiration" << std::endl; + // std::cout << "------------------------------" << std::endl; - int fillID, fillBookName, fillExpiration=(20 - 10)/2; - - fillID = (ID_HASH_SIZE - 2) / 2; - fillBookName = (maxSizeBookName - 4) / 2; + int fillID, fillBookName, fillExpiration=(20 - 10)/2; + + fillID = (ID_HASH_SIZE - 2) / 2; + fillBookName = (maxSizeBookName - 4) / 2; - std::cout.width (fillID); - std::cout << ""; - std::cout << "ID" ; - std::cout.width (fillID); - std::cout << ""; - std::cout << " " ; - - std::cout.width (fillBookName); - std::cout << ""; - std::cout << "Book" ; - std::cout.width (fillBookName); - std::cout << ""; - std::cout << " " ; + std::cout.width (fillID); + std::cout << ""; + std::cout << "ID" ; + std::cout.width (fillID); + std::cout << ""; + std::cout << " " ; + + std::cout.width (fillBookName); + std::cout << ""; + std::cout << "Book" ; + std::cout.width (fillBookName); + std::cout << ""; + std::cout << " " ; - std::cout.width (fillExpiration); - std::cout << ""; - std::cout << "Expiration"; - std::cout.width (fillExpiration); - std::cout << "" << std::endl; + std::cout.width (fillExpiration); + std::cout << ""; + std::cout << "Expiration"; + std::cout.width (fillExpiration); + std::cout << "" << std::endl; - std::cout.fill ('-'); - std::cout.width (ID_HASH_SIZE + 4 + maxSizeBookName + 4 + 20); - std::cout << "" << std::endl; - std::cout.fill (' '); + std::cout.fill ('-'); + std::cout.width (ID_HASH_SIZE + 4 + maxSizeBookName + 4 + 20); + std::cout << "" << std::endl; + std::cout.fill (' '); - std::string bookName; + std::string bookName; - for (const auto& kv : loanedBooks) - { - loan = kv.second; + for (const auto& kv : loanedBooks) + { + loan = kv.second; - std::cout << kv.first; - std::cout << " "; + std::cout << kv.first; + std::cout << " "; - 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; + 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; - std::cout << bookName; - std::cout.width (maxSizeBookName - bookName.size()); - std::cout << ""; - std::cout << " "; - - std::cout << loan->validity << std::endl; - } + std::cout << bookName; + std::cout.width (maxSizeBookName - bookName.size()); + std::cout << ""; + std::cout << " "; + + std::cout << loan->validity << std::endl; + } - std::cout << std::endl; + std::cout << std::endl; } void returnBook(gourou::DRMProcessor& processor) { - struct Loan* loan = loanedBooks[std::string(returnID)]; + struct Loan* loan = loanedBooks[std::string(returnID)]; - if (!loan) - { - std::cout << "Error : Loan " << returnID << " doesn't exists" << std::endl; - return; - } + if (!loan) + { + std::cout << "Error : Loan " << returnID << " doesn't exists" << std::endl; + return; + } - processor.returnLoan(loan->id, loan->operatorURL, notify); - - deleteID = returnID; - if (deleteLoan(false)) - { - std::cout << "Loan " << returnID << " successfully returned" << std::endl; - } - } + processor.returnLoan(loan->id, loan->operatorURL, notify); + deleteID = returnID; + if (deleteLoan(false)) + { + std::cout << "Loan " << returnID << " successfully returned" << std::endl; + } + } + bool deleteLoan(bool displayResult=true) { - struct Loan* loan = loanedBooks[std::string(deleteID)]; + struct Loan* loan = loanedBooks[std::string(deleteID)]; - if (!loan) - { - std::cout << "Error : Loan " << deleteID << " doesn't exists" << std::endl; - return false; - } + if (!loan) + { + std::cout << "Error : Loan " << deleteID << " doesn't exists" << std::endl; + return false; + } - if (unlink(loan->path.c_str())) - { - std::cout << "Error : Cannot delete " << loan->path << std::endl; - return false; - } - else if (displayResult) - { - std::cout << "Loan " << deleteID << " deleted" << std::endl; - } - - return true; + if (unlink(loan->path.c_str())) + { + std::cout << "Error : Cannot delete " << loan->path << std::endl; + return false; + } + else if (displayResult) + { + std::cout << "Loan " << deleteID << " deleted" << std::endl; + } + + return true; } std::map loanedBooks; -}; +}; static void usage(const char* cmd) { std::cout << basename((char*)cmd) << " manage loaned books" << std::endl << std::endl; - + std::cout << "Usage: " << basename((char*)cmd) << " [OPTIONS]" << std::endl << std::endl; - + std::cout << "Global Options:" << std::endl; std::cout << " " << "-l|--list" << "\t\t" << "List all loaned books" << std::endl; std::cout << " " << "-r|--return" << "\t\t" << "Return a loaned book" << std::endl; @@ -381,69 +381,69 @@ int main(int argc, char** argv) const char** files[] = {&devicekeyFile, &deviceFile, &activationFile}; int verbose = gourou::DRMProcessor::getLogLevel(); int actions = 0; - + while (1) { - int option_index = 0; - static struct option long_options[] = { - {"adept-directory", required_argument, 0, 'D' }, - {"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' }, - {0, 0, 0, 0 } - }; + int option_index = 0; + static struct option long_options[] = { + {"adept-directory", required_argument, 0, 'D' }, + {"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' }, + {0, 0, 0, 0 } + }; - c = getopt_long(argc, argv, "d:lr:D:vVh", + c = getopt_long(argc, argv, "d:lr:D:vVh", long_options, &option_index); - if (c == -1) - break; + if (c == -1) + break; - switch (c) { - case 'D': - adeptDir = optarg; - break; - case 'l': - list = true; - actions++; - break; - case 'r': - returnID = optarg; - actions++; - break; - case 'd': - deleteID = optarg; - actions++; - break; - case 'N': - notify = false; - break; - case 'v': - verbose++; - break; - case 'V': - version(); - return 0; - case 'h': - usage(argv[0]); - return 0; - default: - usage(argv[0]); - return -1; - } + switch (c) { + case 'D': + adeptDir = optarg; + break; + case 'l': + list = true; + actions++; + break; + case 'r': + returnID = optarg; + actions++; + break; + case 'd': + deleteID = optarg; + actions++; + break; + case 'N': + notify = false; + break; + case 'v': + verbose++; + break; + case 'V': + version(); + return 0; + case 'h': + usage(argv[0]); + return 0; + default: + usage(argv[0]); + return -1; + } } - + gourou::DRMProcessor::setLogLevel(verbose); // By default, simply list books loaned if (actions == 0) - list = true; + list = true; else if (actions != 1) { - usage(argv[0]); - return -1; + usage(argv[0]); + return -1; } LoanMGT loanMGT; @@ -454,50 +454,50 @@ int main(int argc, char** argv) char *filename; for (i=0; i<(int)ARRAY_SIZE(files); i++) { - orig = *files[i]; - - if (adeptDir) - { - std::string path = std::string(adeptDir) + std::string("/") + orig; - filename = strdup(path.c_str()); - } - else - filename = strdup(orig); - *files[i] = findFile(filename); - free(filename); - if (!*files[i]) - { - std::cout << "Error : " << orig << " doesn't exists, did you activate your device ?" << std::endl; - hasErrors = true; - } + orig = *files[i]; + + if (adeptDir) + { + std::string path = std::string(adeptDir) + std::string("/") + orig; + filename = strdup(path.c_str()); + } + else + filename = strdup(orig); + *files[i] = findFile(filename); + free(filename); + if (!*files[i]) + { + std::cout << "Error : " << orig << " doesn't exists, did you activate your device ?" << std::endl; + hasErrors = true; + } } if (hasErrors) { - // In case of adept dir was provided by user - adeptDir = 0; - goto end; + // In case of adept dir was provided by user + adeptDir = 0; + goto end; } if (adeptDir) - adeptDir = strdup(adeptDir); // For below free + adeptDir = strdup(adeptDir); // For below free else { - adeptDir = strdup(deviceFile); - adeptDir = dirname(adeptDir); + adeptDir = strdup(deviceFile); + adeptDir = dirname(adeptDir); } ret = loanMGT.run(); - + end: for (i=0; i<(int)ARRAY_SIZE(files); i++) { - if (*files[i]) - free((void*)*files[i]); + if (*files[i]) + free((void*)*files[i]); } if (adeptDir) - free(adeptDir); - + free(adeptDir); + return ret; } diff --git a/utils/adept_remove.cpp b/utils/adept_remove.cpp index c8d3ecb..c678f3d 100644 --- a/utils/adept_remove.cpp +++ b/utils/adept_remove.cpp @@ -4,16 +4,16 @@ 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. + 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. + 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. - + 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 @@ -51,13 +51,13 @@ static unsigned encryptionKeySize = 0; static inline unsigned char htoi(unsigned char c) { if (c >= '0' && c <= '9') - c -= '0'; + c -= '0'; else if (c >= 'a' && c <= 'f') - c -= 'a' - 10; + c -= 'a' - 10; else if (c >= 'A' && c <= 'F') - c -= 'A' - 10; + c -= 'A' - 10; else - EXCEPTION(gourou::USER_INVALID_INPUT, "Invalid character " << c << " in encryption key"); + EXCEPTION(gourou::USER_INVALID_INPUT, "Invalid character " << c << " in encryption key"); return c; } @@ -70,82 +70,82 @@ static inline bool endsWith(const std::string& s, const std::string& suffix) class ADEPTRemove { public: - + int run() { - int ret = 0; - try - { - gourou::DRMProcessor::ITEM_TYPE type; - DRMProcessorClientImpl client; - gourou::DRMProcessor processor(&client, deviceFile, activationFile, devicekeyFile); - - std::string filename; - if (!outputFile) - filename = std::string(inputFile); - else - filename = outputFile; - - if (outputDir) - { - if (!fileExists(outputDir)) - mkpath(outputDir); + int ret = 0; + try + { + gourou::DRMProcessor::ITEM_TYPE type; + DRMProcessorClientImpl client; + gourou::DRMProcessor processor(&client, deviceFile, activationFile, devicekeyFile); + + std::string filename; + if (!outputFile) + filename = std::string(inputFile); + else + filename = outputFile; + + if (outputDir) + { + if (!fileExists(outputDir)) + mkpath(outputDir); - filename = std::string(outputDir) + "/" + filename; - } + filename = std::string(outputDir) + "/" + filename; + } - if (endsWith(filename, ".epub")) - type = gourou::DRMProcessor::ITEM_TYPE::EPUB; - else if (endsWith(filename, ".pdf")) - type = gourou::DRMProcessor::ITEM_TYPE::PDF; - else - { - EXCEPTION(gourou::DRM_FORMAT_NOT_SUPPORTED, "Unsupported file format of " << filename); - } - - if (inputFile != filename) - { - unlink(filename.c_str()); - fileCopy(inputFile, filename.c_str()); - processor.removeDRM(inputFile, filename, type, encryptionKey, encryptionKeySize); - std::cout << "DRM removed into new file " << filename << std::endl; - } - else - { - // Use temp file for PDF - if (type == gourou::DRMProcessor::ITEM_TYPE::PDF) - { - std::string tempFile = filename + ".tmp"; - /* Be sure there is not already a temp file */ - unlink(tempFile.c_str()); - processor.removeDRM(filename, tempFile, type, encryptionKey, encryptionKeySize); - /* Original file must be removed before doing a copy... */ - unlink(filename.c_str()); - if (rename(tempFile.c_str(), filename.c_str())) - { - EXCEPTION(gourou::DRM_FILE_ERROR, "Unable to copy " << tempFile << " into " << filename); - } - } - else - processor.removeDRM(inputFile, filename, type, encryptionKey, encryptionKeySize); - std::cout << "DRM removed from " << filename << std::endl; - } - } catch(std::exception& e) - { - std::cout << e.what() << std::endl; - ret = 1; - } + if (endsWith(filename, ".epub")) + type = gourou::DRMProcessor::ITEM_TYPE::EPUB; + else if (endsWith(filename, ".pdf")) + type = gourou::DRMProcessor::ITEM_TYPE::PDF; + else + { + EXCEPTION(gourou::DRM_FORMAT_NOT_SUPPORTED, "Unsupported file format of " << filename); + } + + if (inputFile != filename) + { + unlink(filename.c_str()); + fileCopy(inputFile, filename.c_str()); + processor.removeDRM(inputFile, filename, type, encryptionKey, encryptionKeySize); + std::cout << "DRM removed into new file " << filename << std::endl; + } + else + { + // Use temp file for PDF + if (type == gourou::DRMProcessor::ITEM_TYPE::PDF) + { + std::string tempFile = filename + ".tmp"; + /* Be sure there is not already a temp file */ + unlink(tempFile.c_str()); + processor.removeDRM(filename, tempFile, type, encryptionKey, encryptionKeySize); + /* Original file must be removed before doing a copy... */ + unlink(filename.c_str()); + if (rename(tempFile.c_str(), filename.c_str())) + { + EXCEPTION(gourou::DRM_FILE_ERROR, "Unable to copy " << tempFile << " into " << filename); + } + } + else + processor.removeDRM(inputFile, filename, type, encryptionKey, encryptionKeySize); + std::cout << "DRM removed from " << filename << std::endl; + } + } catch(std::exception& e) + { + std::cout << e.what() << std::endl; + ret = 1; + } - return ret; + return ret; } -}; +}; static void usage(const char* cmd) { std::cout << basename((char*)cmd) << " remove ADEPT DRM (from Adobe) of EPUB/PDF file" << std::endl << std::endl; - + std::cout << "Usage: " << basename((char*)cmd) << " [OPTIONS] file(.epub|pdf)" << std::endl << std::endl; - + std::cout << "Global Options:" << std::endl; std::cout << " " << "-O|--output-dir" << "\t" << "Optional output directory were to put result (default ./)" << std::endl; std::cout << " " << "-o|--output-file" << "\t" << "Optional output filename (default inplace DRM removal>)" << std::endl; @@ -181,82 +181,82 @@ int main(int argc, char** argv) std::string _deviceFile, _activationFile, _devicekeyFile; while (1) { - int option_index = 0; - static struct option long_options[] = { - {"adept-directory", required_argument, 0, 'D' }, - {"device-file", required_argument, 0, 'd' }, - {"activation-file", required_argument, 0, 'a' }, - {"device-key-file", required_argument, 0, 'k' }, - {"output-dir", required_argument, 0, 'O' }, - {"output-file", required_argument, 0, 'o' }, - {"input-file", required_argument, 0, 'f' }, - {"encryption-key", required_argument, 0, 'K' }, // Private option - {"verbose", no_argument, 0, 'v' }, - {"version", no_argument, 0, 'V' }, - {"help", no_argument, 0, 'h' }, - {0, 0, 0, 0 } - }; + int option_index = 0; + static struct option long_options[] = { + {"adept-directory", required_argument, 0, 'D' }, + {"device-file", required_argument, 0, 'd' }, + {"activation-file", required_argument, 0, 'a' }, + {"device-key-file", required_argument, 0, 'k' }, + {"output-dir", required_argument, 0, 'O' }, + {"output-file", required_argument, 0, 'o' }, + {"input-file", required_argument, 0, 'f' }, + {"encryption-key", required_argument, 0, 'K' }, // Private option + {"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:K:vVh", + c = getopt_long(argc, argv, "D:d:a:k:O:o:f:K:vVh", long_options, &option_index); - if (c == -1) - break; + if (c == -1) + break; - switch (c) { - case 'D': - _deviceFile = std::string(optarg) + "/device.xml"; - _activationFile = std::string(optarg) + "/activation.xml"; - _devicekeyFile = std::string(optarg) + "/devicesalt"; - deviceFile = _deviceFile.c_str(); - activationFile = _activationFile.c_str(); - devicekeyFile = _devicekeyFile.c_str(); - break; - case 'd': - deviceFile = optarg; - break; - case 'a': - activationFile = optarg; - break; - case 'k': - devicekeyFile = optarg; - break; - case 'f': - inputFile = optarg; - break; - case 'O': - outputDir = optarg; - break; - case 'o': - outputFile = optarg; - break; - case 'K': - encryptionKeyUser = optarg; - break; - case 'v': - verbose++; - break; - case 'V': - version(); - return 0; - case 'h': - usage(argv[0]); - return 0; - default: - usage(argv[0]); - return -1; - } + switch (c) { + case 'D': + _deviceFile = std::string(optarg) + "/device.xml"; + _activationFile = std::string(optarg) + "/activation.xml"; + _devicekeyFile = std::string(optarg) + "/devicesalt"; + deviceFile = _deviceFile.c_str(); + activationFile = _activationFile.c_str(); + devicekeyFile = _devicekeyFile.c_str(); + break; + case 'd': + deviceFile = optarg; + break; + case 'a': + activationFile = optarg; + break; + case 'k': + devicekeyFile = optarg; + break; + case 'f': + inputFile = optarg; + break; + case 'O': + outputDir = optarg; + break; + case 'o': + outputFile = optarg; + break; + case 'K': + encryptionKeyUser = optarg; + break; + case 'v': + verbose++; + break; + case 'V': + version(); + return 0; + case 'h': + usage(argv[0]); + return 0; + default: + usage(argv[0]); + return -1; + } } - + gourou::DRMProcessor::setLogLevel(verbose); if (optind == argc-1) - inputFile = argv[optind]; + inputFile = argv[optind]; if (!inputFile || (outputDir && !outputDir[0]) || - (outputFile && !outputFile[0])) + (outputFile && !outputFile[0])) { - usage(argv[0]); - return -1; + usage(argv[0]); + return -1; } ADEPTRemove remover; @@ -266,56 +266,56 @@ int main(int argc, char** argv) const char* orig; for (i=0; i<(int)ARRAY_SIZE(files); i++) { - orig = *files[i]; - *files[i] = findFile(*files[i]); - if (!*files[i]) - { - std::cout << "Error : " << orig << " doesn't exists, did you activate your device ?" << std::endl; - ret = -1; - hasErrors = true; - } + orig = *files[i]; + *files[i] = findFile(*files[i]); + if (!*files[i]) + { + std::cout << "Error : " << orig << " doesn't exists, did you activate your device ?" << std::endl; + ret = -1; + hasErrors = true; + } } if (encryptionKeyUser) { - int size = std::string(encryptionKeyUser).size(); - if ((size % 2)) - { - std::cout << "Error : Encryption key must be odd length" << std::endl; - goto end; - } + 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; - } + if (encryptionKeyUser[0] == '0' && encryptionKeyUser[1] == 'x') + { + encryptionKeyUser += 2; + size -= 2; + } - encryptionKey = new unsigned char[size/2]; + encryptionKey = new unsigned char[size/2]; - for(i=0; i -#include +#include #include #include #include @@ -62,13 +62,13 @@ DRMProcessorClientImpl::DRMProcessorClientImpl(): legacy = OSSL_PROVIDER_load(NULL, "legacy"); if (!legacy) { - ERR_print_errors_cb(error_cb, NULL); - EXCEPTION(gourou::CLIENT_OSSL_ERROR, "Error, OpenSSL legacy provider not available"); + ERR_print_errors_cb(error_cb, NULL); + EXCEPTION(gourou::CLIENT_OSSL_ERROR, "Error, OpenSSL legacy provider not available"); } deflt = OSSL_PROVIDER_load(NULL, "default"); if (!deflt) - EXCEPTION(gourou::CLIENT_OSSL_ERROR, "Error, OpenSSL default provider not available"); + EXCEPTION(gourou::CLIENT_OSSL_ERROR, "Error, OpenSSL default provider not available"); OSSL_PROVIDER_load(NULL, "base"); #endif @@ -77,13 +77,13 @@ DRMProcessorClientImpl::DRMProcessorClientImpl(): #else strcpy(cookiejar, "/tmp/libgourou_cookie_jar_XXXXXX"); #endif - + int fd = mkstemp(cookiejar); if (fd >= 0) - close(fd); + close(fd); else { - EXCEPTION(gourou::CLIENT_FILE_ERROR, "mkstemp error"); + EXCEPTION(gourou::CLIENT_FILE_ERROR, "mkstemp error"); } } @@ -91,10 +91,10 @@ DRMProcessorClientImpl::~DRMProcessorClientImpl() { #if OPENSSL_VERSION_MAJOR >= 3 if (legacy) - OSSL_PROVIDER_unload(legacy); + OSSL_PROVIDER_unload(legacy); if (deflt) - OSSL_PROVIDER_unload(deflt); + OSSL_PROVIDER_unload(deflt); #endif unlink(cookiejar); @@ -108,8 +108,8 @@ void* DRMProcessorClientImpl::createDigest(const std::string& digestName) if (EVP_DigestInit(md_ctx, md) != 1) { - EVP_MD_CTX_free(md_ctx); - EXCEPTION(gourou::CLIENT_DIGEST_ERROR, ERR_error_string(ERR_get_error(), NULL)); + EVP_MD_CTX_free(md_ctx); + EXCEPTION(gourou::CLIENT_DIGEST_ERROR, ERR_error_string(ERR_get_error(), NULL)); } return md_ctx; @@ -118,7 +118,7 @@ void* DRMProcessorClientImpl::createDigest(const std::string& digestName) void DRMProcessorClientImpl::digestUpdate(void* handler, unsigned char* data, unsigned int length) { if (EVP_DigestUpdate((EVP_MD_CTX *)handler, data, length) != 1) - EXCEPTION(gourou::CLIENT_DIGEST_ERROR, ERR_error_string(ERR_get_error(), NULL)); + EXCEPTION(gourou::CLIENT_DIGEST_ERROR, ERR_error_string(ERR_get_error(), NULL)); } void DRMProcessorClientImpl::digestFinalize(void* handler, unsigned char* digestOut) @@ -127,7 +127,7 @@ void DRMProcessorClientImpl::digestFinalize(void* handler, unsigned char* digest EVP_MD_CTX_free((EVP_MD_CTX *)handler); if (res <= 0) - EXCEPTION(gourou::CLIENT_DIGEST_ERROR, ERR_error_string(ERR_get_error(), NULL)); + EXCEPTION(gourou::CLIENT_DIGEST_ERROR, ERR_error_string(ERR_get_error(), NULL)); } void DRMProcessorClientImpl::digest(const std::string& digestName, unsigned char* data, unsigned int length, unsigned char* digestOut) @@ -149,16 +149,16 @@ void DRMProcessorClientImpl::randBytes(unsigned char* bytesOut, unsigned int len static unsigned downloadedBytes; static int downloadProgress(void *clientp, curl_off_t dltotal, curl_off_t dlnow, - curl_off_t ultotal, curl_off_t ulnow) + curl_off_t ultotal, curl_off_t ulnow) { // For "big" files only if (dltotal >= DISPLAY_THRESHOLD && gourou::logLevel >= gourou::LG_LOG_WARN) { - int percent = 0; - if (dltotal) - percent = (dlnow * 100) / dltotal; + int percent = 0; + if (dltotal) + percent = (dlnow * 100) / dltotal; - std::cout << "\rDownload " << percent << "%" << std::flush; + std::cout << "\rDownload " << percent << "%" << std::flush; } return 0; @@ -167,7 +167,7 @@ static int downloadProgress(void *clientp, curl_off_t dltotal, curl_off_t dlnow, static size_t curlRead(void *data, size_t size, size_t nmemb, void *userp) { gourou::ByteArray* replyData = (gourou::ByteArray*) userp; - + replyData->append((unsigned char*)data, size*nmemb); return size*nmemb; @@ -194,18 +194,18 @@ static size_t curlHeaders(char *buffer, size_t size, size_t nitems, void *userda if (pos != std::string::npos) { - std::string key = std::string(buffer, pos); - std::string value = std::string(&buffer[pos+1], (size*nitems)-(pos+1)); + std::string key = std::string(buffer, pos); + std::string value = std::string(&buffer[pos+1], (size*nitems)-(pos+1)); - key = gourou::trim(key); - value = gourou::trim(value); + key = gourou::trim(key); + value = gourou::trim(value); - (*responseHeaders)[key] = value; - - if (gourou::logLevel >= gourou::LG_LOG_DEBUG) - std::cout << key << " : " << value << std::endl; + (*responseHeaders)[key] = value; + + if (gourou::logLevel >= gourou::LG_LOG_DEBUG) + std::cout << key << " : " << value << std::endl; } - + return size*nitems; } @@ -215,42 +215,42 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons std::map localHeaders; if (!responseHeaders) - responseHeaders = &localHeaders; - + responseHeaders = &localHeaders; + GOUROU_LOG(INFO, "Send request to " << URL); if (POSTData.size()) { - GOUROU_LOG(DEBUG, "<<< " << std::endl << POSTData); + GOUROU_LOG(DEBUG, "<<< " << std::endl << POSTData); } unsigned prevDownloadedBytes; downloadedBytes = 0; if (fd && resume) { - struct stat _stat; - if (!fstat(fd, &_stat)) - { - GOUROU_LOG(WARN, "Resume download @ " << _stat.st_size << " bytes"); - downloadedBytes = _stat.st_size; - } - else - GOUROU_LOG(WARN, "Want to resume, but fstat failed"); + struct stat _stat; + if (!fstat(fd, &_stat)) + { + GOUROU_LOG(WARN, "Resume download @ " << _stat.st_size << " bytes"); + downloadedBytes = _stat.st_size; + } + else + GOUROU_LOG(WARN, "Want to resume, but fstat failed"); } - + CURL *curl = curl_easy_init(); 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); - + 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()); + _contentType = "Content-Type: " + contentType; + list = curl_slist_append(list, _contentType.c_str()); } curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); @@ -258,138 +258,138 @@ std::string DRMProcessorClientImpl::sendHTTPRequest(const std::string& URL, cons if (POSTData.size()) { - curl_easy_setopt(curl, CURLOPT_POST, 1L); - curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, POSTData.size()); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, POSTData.data()); + curl_easy_setopt(curl, CURLOPT_POST, 1L); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, POSTData.size()); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, POSTData.data()); } if (fd) { - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlReadFd); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&fd); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlReadFd); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&fd); } else { - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlRead); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&replyData); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlRead); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&replyData); } - + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curlHeaders); 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 try again without incrementing tries - else if (res == CURLE_RECV_ERROR) - { - if (prevDownloadedBytes != downloadedBytes) - { - GOUROU_LOG(WARN, "\nConnection broken, but data received, try again"); - i--; - } - else - GOUROU_LOG(WARN, "\nConnection 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)); - } + 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(WARN, "\nConnection 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(WARN, "\nConnection broken, but data received, try again"); + i--; + } + else + GOUROU_LOG(WARN, "\nConnection 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); 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)); + EXCEPTION(gourou::CLIENT_NETWORK_ERROR, "Error " << curl_easy_strerror(res)); if (http_code >= 400) - EXCEPTION(gourou::CLIENT_HTTP_ERROR, "HTTP Error code " << http_code); + 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; + gourou::logLevel >= gourou::LG_LOG_WARN) + std::cout << std::endl; if ((*responseHeaders)["Content-Type"] == "application/vnd.adobe.adept+xml") { - GOUROU_LOG(DEBUG, ">>> " << std::endl << replyData.data()); + GOUROU_LOG(DEBUG, ">>> " << std::endl << replyData.data()); } - + return std::string((char*)replyData.data(), replyData.length()); } void DRMProcessorClientImpl::padWithPKCS1(unsigned char* out, unsigned int outLength, - const unsigned char* in, unsigned int inLength) + const unsigned char* in, unsigned int inLength) { if (outLength < (inLength + 3)) - EXCEPTION(gourou::CLIENT_RSA_ERROR, "Not enough space for PKCS1 padding"); - + EXCEPTION(gourou::CLIENT_RSA_ERROR, "Not enough space for PKCS1 padding"); + /* PKCS1v5 Padding is : - 0x00 0x01 0xff * n 0x00 dataIn + 0x00 0x01 0xff * n 0x00 dataIn */ - + memset(out, 0xFF, outLength - inLength - 1); - + out[0] = 0x0; out[1] = 0x1; out[outLength - inLength - 1] = 0x00; - + memcpy(&out[outLength - inLength], in, inLength); } void DRMProcessorClientImpl::padWithPKCS1Type2(unsigned char* out, unsigned int outLength, - const unsigned char* in, unsigned int inLength) + const unsigned char* in, unsigned int inLength) { if (outLength < (inLength + 3)) - EXCEPTION(gourou::CLIENT_RSA_ERROR, "Not enough space for PKCS1 padding"); - + EXCEPTION(gourou::CLIENT_RSA_ERROR, "Not enough space for PKCS1 padding"); + /* PKCS1v5 type 2 Padding is : - 0x00 0x02 0xXX * n 0x00 dataIn - XX is random non zero data + 0x00 0x02 0xXX * n 0x00 dataIn + XX is random non zero data */ RAND_bytes(&out[2], outLength - inLength - 1); for(unsigned int i=2; i* responseHeaders=0, int fd=0, bool resume=false); virtual void RSAPrivateEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength, - const RSA_KEY_TYPE keyType, const std::string& password, - const unsigned char* data, unsigned dataLength, - unsigned char* res); - + const RSA_KEY_TYPE keyType, const std::string& password, + const unsigned char* data, unsigned dataLength, + unsigned char* res); + virtual void RSAPrivateDecrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength, - const RSA_KEY_TYPE keyType, const std::string& password, - const unsigned char* data, unsigned dataLength, - unsigned char* res); + const RSA_KEY_TYPE keyType, const std::string& password, + const unsigned char* data, unsigned dataLength, + unsigned char* res); virtual void RSAPublicEncrypt(const unsigned char* RSAKey, unsigned int RSAKeyLength, - const RSA_KEY_TYPE keyType, - const unsigned char* data, unsigned dataLength, - unsigned char* res); + const RSA_KEY_TYPE keyType, + const unsigned char* data, unsigned dataLength, + unsigned char* res); virtual void* generateRSAKey(int keyLengthBits); virtual void destroyRSAHandler(void* handler); - + virtual void extractRSAPublicKey(void* RSAKeyHandler, unsigned char** keyOut, unsigned int* keyOutLength); virtual void extractRSAPrivateKey(void* RSAKeyHandler, unsigned char** keyOut, unsigned int* keyOutLength); virtual void extractCertificate(const unsigned char* RSAKey, unsigned int RSAKeyLength, - const RSA_KEY_TYPE keyType, const std::string& password, - unsigned char** certOut, unsigned int* certOutLength); - + const RSA_KEY_TYPE keyType, const std::string& password, + unsigned char** certOut, unsigned int* certOutLength); + /* Crypto interface */ virtual void encrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining, - const unsigned char* key, unsigned int keyLength, - const unsigned char* iv, unsigned int ivLength, - const unsigned char* dataIn, unsigned int dataInLength, - unsigned char* dataOut, unsigned int* dataOutLength); + const unsigned char* key, unsigned int keyLength, + const unsigned char* iv, unsigned int ivLength, + const unsigned char* dataIn, unsigned int dataInLength, + unsigned char* dataOut, unsigned int* dataOutLength); virtual void* encryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining, - const unsigned char* key, unsigned int keyLength, - const unsigned char* iv=0, unsigned int ivLength=0); + const unsigned char* key, unsigned int keyLength, + const unsigned char* iv=0, unsigned int ivLength=0); virtual void encryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength, - unsigned char* dataOut, unsigned int* dataOutLength); + unsigned char* dataOut, unsigned int* dataOutLength); virtual void encryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength); virtual void decrypt(CRYPTO_ALGO algo, CHAINING_MODE chaining, - const unsigned char* key, unsigned int keyLength, - const unsigned char* iv, unsigned int ivLength, - const unsigned char* dataIn, unsigned int dataInLength, - unsigned char* dataOut, unsigned int* dataOutLength); + const unsigned char* key, unsigned int keyLength, + const unsigned char* iv, unsigned int ivLength, + const unsigned char* dataIn, unsigned int dataInLength, + unsigned char* dataOut, unsigned int* dataOutLength); virtual void* decryptInit(CRYPTO_ALGO algo, CHAINING_MODE chaining, - const unsigned char* key, unsigned int keyLength, - const unsigned char* iv=0, unsigned int ivLength=0); + const unsigned char* key, unsigned int keyLength, + const unsigned char* iv=0, unsigned int ivLength=0); virtual void decryptUpdate(void* handler, const unsigned char* dataIn, unsigned int dataInLength, - unsigned char* dataOut, unsigned int* dataOutLength); + unsigned char* dataOut, unsigned int* dataOutLength); virtual void decryptFinalize(void* handler, unsigned char* dataOut, unsigned int* dataOutLength); /* ZIP Interface */ virtual void* zipOpen(const std::string& path); - + virtual void zipReadFile(void* handler, const std::string& path, gourou::ByteArray& result, bool decompress=true); - + virtual void zipWriteFile(void* handler, const std::string& path, gourou::ByteArray& content); - + virtual void zipDeleteFile(void* handler, const std::string& path); - + virtual void zipClose(void* handler); - + virtual void inflate(gourou::ByteArray& data, gourou::ByteArray& result, - int wbits=-15); - + int wbits=-15); + virtual void deflate(gourou::ByteArray& data, gourou::ByteArray& result, - int wbits=-15, int compressionLevel=8); + int wbits=-15, int compressionLevel=8); private: void padWithPKCS1(unsigned char* out, unsigned int outLength, - const unsigned char* in, unsigned int inLength); + const unsigned char* in, unsigned int inLength); void padWithPKCS1Type2(unsigned char* out, unsigned int outLength, - const unsigned char* in, unsigned int inLength); - + const unsigned char* in, unsigned int inLength); + #if OPENSSL_VERSION_MAJOR >= 3 OSSL_PROVIDER *legacy, *deflt; #else diff --git a/utils/launcher.cpp b/utils/launcher.cpp index 99f8495..6eb2bcf 100644 --- a/utils/launcher.cpp +++ b/utils/launcher.cpp @@ -24,17 +24,17 @@ int main(int argc, char** argv) fullPath = std::string(mountPoint) + util; if (std::string(util) == "launcher" || !fileExists(fullPath.c_str())) - fullPath = std::string(mountPoint) + DEFAULT_UTIL; + fullPath = std::string(mountPoint) + DEFAULT_UTIL; free(argv0); - + argv[0] = strdup(fullPath.c_str()); if (execvp(argv[0], argv)) - std::cout << "Unable to launch '" << argv[0] << "'" << std::endl; + std::cout << "Unable to launch '" << argv[0] << "'" << std::endl; /* Should not happens */ free(argv[0]); - + return 0; } diff --git a/utils/utils_common.cpp b/utils/utils_common.cpp index a686b40..383af5f 100644 --- a/utils/utils_common.cpp +++ b/utils/utils_common.cpp @@ -4,16 +4,16 @@ 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. + 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. + 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. - + 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 @@ -62,31 +62,31 @@ bool fileExists(const char* filename) const char* findFile(const char* filename, bool inDefaultDirs) { std::string path; - + const char* adeptDir = getenv("ADEPT_DIR"); if (adeptDir && adeptDir[0]) { - path = adeptDir + std::string("/") + filename; - if (fileExists(path.c_str())) - return strdup(path.c_str()); + path = adeptDir + std::string("/") + filename; + if (fileExists(path.c_str())) + return strdup(path.c_str()); } path = gourou::DRMProcessor::getDefaultAdeptDir() + filename; if (fileExists(path.c_str())) - return strdup(path.c_str()); + return strdup(path.c_str()); if (fileExists(filename)) - return strdup(filename); + return strdup(filename); if (!inDefaultDirs) return 0; - + for (int i=0; i<(int)ARRAY_SIZE(defaultDirs); i++) { - path = std::string(defaultDirs[i]) + filename; - if (fileExists(path.c_str())) - return strdup(path.c_str()); + path = std::string(defaultDirs[i]) + filename; + if (fileExists(path.c_str())) + return strdup(path.c_str()); } - + return 0; } @@ -118,35 +118,35 @@ void fileCopy(const char* in, const char* out) fdIn = open(in, O_RDONLY); if (!fdIn) - EXCEPTION(gourou::CLIENT_FILE_ERROR, "Unable to open " << in); - + 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); + close (fdIn); + EXCEPTION(gourou::CLIENT_FILE_ERROR, "Unable to open " << out); } while (true) { - ret = ::read(fdIn, buffer, sizeof(buffer)); - if (ret <= 0) - break; - do - { - _buffer = buffer; - ret2 = ::write(fdOut, _buffer, ret); - if (ret2 >= 0) - { - ret -= ret2; - _buffer += ret2; - } - else - { - EXCEPTION(gourou::CLIENT_FILE_ERROR, "Error writing " << out); - } - } while (ret); + ret = ::read(fdIn, buffer, sizeof(buffer)); + if (ret <= 0) + break; + do + { + _buffer = buffer; + ret2 = ::write(fdOut, _buffer, ret); + if (ret2 >= 0) + { + ret -= ret2; + _buffer += ret2; + } + else + { + EXCEPTION(gourou::CLIENT_FILE_ERROR, "Error writing " << out); + } + } while (ret); } close (fdIn); diff --git a/utils/utils_common.h b/utils/utils_common.h index 5bad2a9..9289209 100644 --- a/utils/utils_common.h +++ b/utils/utils_common.h @@ -4,16 +4,16 @@ 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. + 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. + 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. - + 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