/* Copyright 2021 Grégory Soutadé This file is part of libgourou. libgourou is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. libgourou is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with libgourou. If not, see . */ #ifndef _LIBGOUROU_COMMON_H_ #define _LIBGOUROU_COMMON_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include "bytearray.h" namespace gourou { /** * Some common utilities */ #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 }; 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 }; enum DOWNLOAD_ERROR { DW_NO_ITEM = 0x1200, DW_NO_EBX_HANDLER, }; enum SIGNIN_ERROR { SIGN_INVALID_CREDENTIALS = 0x1300, }; enum ACTIVATE_ERROR { 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, }; enum USER_ERROR { 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 }; 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, }; 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 }; #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(const Exception& other) { this->code = other.code; this->line = line; this->file = file; this->fullmessage = strdup(other.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; }; /** * @brief Throw an exception */ #define EXCEPTION(code, message) \ {std::stringstream __msg;__msg << message; throw gourou::Exception(code, __msg.str().c_str(), __FILE__, __LINE__);} /** * Stream writer for pugi::xml */ class StringXMLWriter : public pugi::xml_writer { public: virtual void write(const void* data, size_t size) { result.append(static_cast(data), size); } const std::string& getResult() {return result;} private: std::string result; }; static const char* ws = " \t\n\r\f\v"; /** * @brief trim from end of string (right) */ inline std::string& rtrim(std::string& s, const char* t = ws) { s.erase(s.find_last_not_of(t) + 1); return s; } /** * @brief trim from beginning of string (left) */ inline std::string& ltrim(std::string& s, const char* t = ws) { s.erase(0, s.find_first_not_of(t)); return s; } /** * @brief trim from both ends of string (right then left) */ inline std::string& trim(std::string& s, const char* t = ws) { return ltrim(rtrim(s, t), t); } /** * @brief Extract text node from tag in document * It can throw an exception if tag does not exists * or just return an empty value */ static inline std::string extractTextElem(const pugi::xml_node& root, const char* tagName, bool throwOnNull=true) { 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 node = xpath_node.node().first_child(); if (!node) { if (throwOnNull) EXCEPTION(GOUROU_TAG_NOT_FOUND, "Text element for tag " << tagName << " not found"); return ""; } std::string res = node.value(); return trim(res); } /** * @brief Extract text attribute from tag in document * It can throw an exception if attribute does not exists * or just return an empty value */ static inline std::string extractTextAttribute(const pugi::xml_node& root, const char* tagName, const char* attributeName, bool throwOnNull=true) { pugi::xpath_node xpath_node = root.select_node(tagName); if (!xpath_node) { if (throwOnNull) EXCEPTION(GOUROU_TAG_NOT_FOUND, "Tag " << tagName << " not found"); return ""; } pugi::xml_attribute attr = xpath_node.node().attribute(attributeName); 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); } /** * @brief Append an element to root with a sub text element * * @param root Root node where to put child * @param name Tag name for child * @param value Text child value of tag element */ 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()); } /** * Remove "urn:uuid:" prefix and all '-' from uuid * urn:uuid:9cb786e8-586a-4950-8901-fff8d2ee6025 * -> * 9cb786e8586a49508901fff8d2ee6025 */ static inline std::string extractIdFromUUID(const std::string& uuid) { unsigned int i = 0; std::string res; if (uuid.find("urn:uuid:") == 0) i = 9; for(; i