Libgourou

Libgourou Git Source Tree

Root/include/libgourou_common.h

1/*
2 Copyright 2021 Grégory Soutadé
3
4 This file is part of libgourou.
5
6 libgourou is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 libgourou is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with libgourou. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#ifndef _LIBGOUROU_COMMON_H_
21#define _LIBGOUROU_COMMON_H_
22
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <fcntl.h>
26#include <unistd.h>
27
28#include <pugixml.hpp>
29
30#include <exception>
31#include <iostream>
32#include <sstream>
33#include <iomanip>
34#include <algorithm>
35
36#include <string.h>
37
38#include <libgourou_log.h>
39#include "bytearray.h"
40
41namespace gourou
42{
43 /**
44 * Some common utilities
45 */
46
47 #define ADOBE_ADEPT_NS "http://ns.adobe.com/adept"
48
49 static const int SHA1_LEN = 20;
50 static const int RSA_KEY_SIZE = 128;
51 static const int RSA_KEY_SIZE_BITS = (RSA_KEY_SIZE*8);
52
53 enum GOUROU_ERROR {
54GOUROU_DEVICE_DOES_NOT_MATCH = 0x1000,
55GOUROU_INVALID_CLIENT,
56GOUROU_TAG_NOT_FOUND,
57GOUROU_ADEPT_ERROR,
58GOUROU_FILE_ERROR,
59GOUROU_INVALID_PROPERTY
60 };
61
62 enum FULFILL_ERROR {
63FF_ACSM_FILE_NOT_EXISTS = 0x1100,
64FF_INVALID_ACSM_FILE,
65FF_NO_HMAC_IN_ACSM_FILE,
66FF_NOT_ACTIVATED,
67FF_NO_OPERATOR_URL,
68FF_SERVER_INTERNAL_ERROR
69 };
70
71 enum DOWNLOAD_ERROR {
72DW_NO_ITEM = 0x1200,
73DW_NO_EBX_HANDLER,
74 };
75
76 enum SIGNIN_ERROR {
77SIGN_INVALID_CREDENTIALS = 0x1300,
78 };
79
80 enum ACTIVATE_ERROR {
81ACTIVATE_NOT_SIGNEDIN = 0x1400
82 };
83
84 enum DEV_ERROR {
85DEV_MKPATH = 0x2000,
86DEV_MAC_ERROR,
87DEV_INVALID_DEVICE_FILE,
88DEV_INVALID_DEVICE_KEY_FILE,
89DEV_INVALID_DEV_PROPERTY,
90 };
91
92 enum USER_ERROR {
93USER_MKPATH = 0x3000,
94USER_INVALID_ACTIVATION_FILE,
95USER_NO_AUTHENTICATION_URL,
96USER_NO_PROPERTY,
97USER_INVALID_INPUT,
98 };
99
100 enum FULFILL_ITEM_ERROR {
101FFI_INVALID_FULFILLMENT_DATA = 0x4000,
102FFI_INVALID_LOAN_TOKEN
103 };
104
105 enum CLIENT_ERROR {
106CLIENT_BAD_PARAM = 0x5000,
107CLIENT_INVALID_PKCS12,
108CLIENT_INVALID_CERTIFICATE,
109CLIENT_NO_PRIV_KEY,
110CLIENT_NO_PUB_KEY,
111CLIENT_RSA_ERROR,
112CLIENT_BAD_CHAINING,
113CLIENT_BAD_KEY_SIZE,
114CLIENT_BAD_ZIP_FILE,
115CLIENT_ZIP_ERROR,
116CLIENT_GENERIC_EXCEPTION,
117CLIENT_NETWORK_ERROR,
118CLIENT_INVALID_PKCS8,
119CLIENT_FILE_ERROR,
120CLIENT_OSSL_ERROR,
121CLIENT_CRYPT_ERROR,
122CLIENT_DIGEST_ERROR,
123 };
124
125 enum DRM_REMOVAL_ERROR {
126DRM_ERR_ENCRYPTION_KEY = 0x6000,
127DRM_VERSION_NOT_SUPPORTED,
128DRM_FILE_ERROR,
129DRM_FORMAT_NOT_SUPPORTED,
130DRM_IN_OUT_EQUALS,
131DRM_MISSING_PARAMETER,
132DRM_INVALID_KEY_SIZE,
133DRM_ERR_ENCRYPTION_KEY_FP,
134DRM_INVALID_USER
135 };
136
137 #ifndef _NOEXCEPT
138 #if __STDC_VERSION__ >= 201112L
139 # define _NOEXCEPT noexcept
140 # define _NOEXCEPT_(x) noexcept(x)
141 #else
142 # define _NOEXCEPT throw()
143 # define _NOEXCEPT_(x)
144 #endif
145 #endif /* !_NOEXCEPT */
146
147 /**
148 * Generic exception class
149 */
150 class Exception : public std::exception
151 {
152 public:
153Exception(int code, const char* message, const char* file, int line):
154 code(code), line(line), file(file)
155{
156 std::stringstream msg;
157 msg << "Exception code : 0x" << std::setbase(16) << code << std::endl;
158 msg << "Message : " << message << std::endl;
159 if (logLevel >= LG_LOG_DEBUG)
160msg << "File : " << file << ":" << std::setbase(10) << line << std::endl;
161 fullmessage = strdup(msg.str().c_str());
162}
163
164Exception(const Exception& other)
165{
166 this->code = other.code;
167 this->line = line;
168 this->file = file;
169 this->fullmessage = strdup(other.fullmessage);
170}
171
172~Exception() _NOEXCEPT
173{
174 free(fullmessage);
175}
176
177const char * what () const throw () { return fullmessage; }
178
179int getErrorCode() {return code;}
180
181private:
182int code, line;
183const char* message, *file;
184char* fullmessage;
185 };
186
187 /**
188 * @brief Throw an exception
189 */
190#define EXCEPTION(code, message)\
191 {std::stringstream __msg;__msg << message; throw gourou::Exception(code, __msg.str().c_str(), __FILE__, __LINE__);}
192
193 /**
194 * Stream writer for pugi::xml
195 */
196 class StringXMLWriter : public pugi::xml_writer
197 {
198 public:
199virtual void write(const void* data, size_t size)
200{
201 result.append(static_cast<const char*>(data), size);
202}
203
204const std::string& getResult() {return result;}
205
206 private:
207std::string result;
208 };
209
210 static const char* ws = " \t\n\r\f\v";
211
212 /**
213 * @brief trim from end of string (right)
214 */
215 inline std::string& rtrim(std::string& s, const char* t = ws)
216 {
217s.erase(s.find_last_not_of(t) + 1);
218return s;
219 }
220
221 /**
222 * @brief trim from beginning of string (left)
223 */
224 inline std::string& ltrim(std::string& s, const char* t = ws)
225 {
226s.erase(0, s.find_first_not_of(t));
227return s;
228 }
229
230 /**
231 * @brief trim from both ends of string (right then left)
232 */
233 inline std::string& trim(std::string& s, const char* t = ws)
234 {
235return ltrim(rtrim(s, t), t);
236 }
237
238 /**
239 * @brief Extract text node from tag in document
240 * It can throw an exception if tag does not exists
241 * or just return an empty value
242 */
243 static inline std::string extractTextElem(const pugi::xml_node& root, const char* tagName, bool throwOnNull=true)
244 {
245 pugi::xpath_node xpath_node = root.select_node(tagName);
246
247 if (!xpath_node)
248{
249 if (throwOnNull)
250EXCEPTION(GOUROU_TAG_NOT_FOUND, "Tag " << tagName << " not found");
251
252 return "";
253}
254
255pugi::xml_node node = xpath_node.node().first_child();
256
257if (!node)
258{
259 if (throwOnNull)
260EXCEPTION(GOUROU_TAG_NOT_FOUND, "Text element for tag " << tagName << " not found");
261
262 return "";
263}
264
265std::string res = node.value();
266 return trim(res);
267 }
268
269 /**
270 * @brief Extract text attribute from tag in document
271 * It can throw an exception if attribute does not exists
272 * or just return an empty value
273 */
274 static inline std::string extractTextAttribute(const pugi::xml_node& root, const char* tagName, const char* attributeName, bool throwOnNull=true)
275 {
276 pugi::xpath_node xpath_node = root.select_node(tagName);
277
278 if (!xpath_node)
279{
280 if (throwOnNull)
281EXCEPTION(GOUROU_TAG_NOT_FOUND, "Tag " << tagName << " not found");
282
283 return "";
284}
285
286pugi::xml_attribute attr = xpath_node.node().attribute(attributeName);
287
288if (!attr)
289{
290 if (throwOnNull)
291EXCEPTION(GOUROU_TAG_NOT_FOUND, "Attribute element " << attributeName << " for tag " << tagName << " not found");
292
293 return "";
294}
295
296std::string res = attr.value();
297return trim(res);
298 }
299
300 /**
301 * @brief Append an element to root with a sub text element
302 *
303 * @param root Root node where to put child
304 * @param name Tag name for child
305 * @param value Text child value of tag element
306 */
307 static inline void appendTextElem(pugi::xml_node& root, const std::string& name, const std::string& value)
308 {
309pugi::xml_node node = root.append_child(name.c_str());
310node.append_child(pugi::node_pcdata).set_value(value.c_str());
311 }
312
313 /**
314 * Remove "urn:uuid:" prefix and all '-' from uuid
315 * urn:uuid:9cb786e8-586a-4950-8901-fff8d2ee6025
316 * ->
317 * 9cb786e8586a49508901fff8d2ee6025
318 */
319 static inline std::string extractIdFromUUID(const std::string& uuid)
320 {
321unsigned int i = 0;
322std::string res;
323
324if (uuid.find("urn:uuid:") == 0)
325 i = 9;
326
327for(; i<uuid.size(); i++)
328{
329 if (uuid[i] != '-')
330res += uuid[i];
331}
332
333return res;
334 }
335
336 /**
337 * @brief Open a file descriptor on path. If it already exists and truncate == true, it's truncated
338 *
339 * @return Created fd, must be closed
340 */
341 static inline int createNewFile(std::string path, bool truncate=true)
342 {
343int options = O_CREAT|O_WRONLY;
344if (truncate)
345 options |= O_TRUNC;
346else
347 options |= O_APPEND;
348
349int fd = open(path.c_str(), options, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
350
351if (fd <= 0)
352 EXCEPTION(GOUROU_FILE_ERROR, "Unable to create " << path);
353
354return fd;
355 }
356
357 /**
358 * @brief Write data in a file. If it already exists, it's truncated
359 */
360 static inline void writeFile(std::string path, const unsigned char* data, unsigned int length)
361 {
362int fd = createNewFile(path);
363
364if (write(fd, data, length) != length)
365 EXCEPTION(GOUROU_FILE_ERROR, "Write error for file " << path);
366
367close (fd);
368 }
369
370 /**
371 * @brief Write data in a file. If it already exists, it's truncated
372 */
373 static inline void writeFile(std::string path, ByteArray& data)
374 {
375writeFile(path, data.data(), data.length());
376 }
377
378 /**
379 * @brief Write data in a file. If it already exists, it's truncated
380 */
381 static inline void writeFile(std::string path, const std::string& data)
382 {
383writeFile(path, (const unsigned char*)data.c_str(), data.length());
384 }
385
386 /**
387 * Read data from file
388 */
389 static inline void readFile(std::string path, const unsigned char* data, unsigned int length)
390 {
391int fd = open(path.c_str(), O_RDONLY);
392
393if (fd <= 0)
394 EXCEPTION(GOUROU_FILE_ERROR, "Unable to open " << path);
395
396if (read(fd, (void*)data, length) != length)
397 EXCEPTION(GOUROU_FILE_ERROR, "Read error for file " << path);
398
399close (fd);
400 }
401
402#define PATH_MAX_STRING_SIZE 256
403
404 // https://gist.github.com/ChisholmKyle/0cbedcd3e64132243a39
405/* recursive mkdir */
406 static inline int mkdir_p(const char *dir, const mode_t mode) {
407char tmp[PATH_MAX_STRING_SIZE];
408char *p = NULL;
409struct stat sb;
410size_t len;
411
412/* copy path */
413len = strnlen (dir, PATH_MAX_STRING_SIZE);
414if (len == 0 || len == PATH_MAX_STRING_SIZE) {
415 return -1;
416}
417memcpy (tmp, dir, len);
418tmp[len] = '\0';
419
420/* remove trailing slash */
421if(tmp[len - 1] == '/') {
422 tmp[len - 1] = '\0';
423}
424
425/* check if path exists and is a directory */
426if (stat (tmp, &sb) == 0) {
427 if (S_ISDIR (sb.st_mode)) {
428return 0;
429 }
430}
431
432/* recursive mkdir */
433for(p = tmp + 1; *p; p++) {
434 if(*p == '/') {
435*p = 0;
436/* test path */
437if (stat(tmp, &sb) != 0) {
438 /* path does not exist - create directory */
439 if (mkdir(tmp, mode) < 0) {
440return -1;
441 }
442} else if (!S_ISDIR(sb.st_mode)) {
443 /* not a directory */
444 return -1;
445}
446*p = '/';
447 }
448}
449/* test path */
450if (stat(tmp, &sb) != 0) {
451 /* path does not exist - create directory */
452 if (mkdir(tmp, mode) < 0) {
453return -1;
454 }
455} else if (!S_ISDIR(sb.st_mode)) {
456 /* not a directory */
457 return -1;
458}
459return 0;
460 }
461
462 static inline void dumpBuffer(GOUROU_LOG_LEVEL level, const char* title, const unsigned char* data, unsigned int len)
463 {
464if (gourou::logLevel < level)
465 return;
466
467printf("%s", title);
468for(unsigned int i=0; i<len; i++)
469{
470 if (i && !(i%16)) printf("\n");
471 printf("%02x ", data[i]);
472}
473printf("\n");
474 }
475}
476
477#endif

Archive Download this file