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
68 };
69
70 enum DOWNLOAD_ERROR {
71DW_NO_ITEM = 0x1200,
72DW_NO_EBX_HANDLER,
73 };
74
75 enum SIGNIN_ERROR {
76SIGN_INVALID_CREDENTIALS = 0x1300,
77 };
78
79 enum ACTIVATE_ERROR {
80ACTIVATE_NOT_SIGNEDIN = 0x1400
81 };
82
83 enum DEV_ERROR {
84DEV_MKPATH = 0x2000,
85DEV_MAC_ERROR,
86DEV_INVALID_DEVICE_FILE,
87DEV_INVALID_DEVICE_KEY_FILE,
88DEV_INVALID_DEV_PROPERTY,
89 };
90
91 enum USER_ERROR {
92USER_MKPATH = 0x3000,
93USER_INVALID_ACTIVATION_FILE,
94USER_NO_AUTHENTICATION_URL,
95USER_NO_PROPERTY,
96USER_INVALID_INPUT,
97 };
98
99 enum FULFILL_ITEM_ERROR {
100FFI_INVALID_FULFILLMENT_DATA = 0x4000,
101FFI_INVALID_LOAN_TOKEN
102 };
103
104 enum CLIENT_ERROR {
105CLIENT_BAD_PARAM = 0x5000,
106CLIENT_INVALID_PKCS12,
107CLIENT_INVALID_CERTIFICATE,
108CLIENT_NO_PRIV_KEY,
109CLIENT_NO_PUB_KEY,
110CLIENT_RSA_ERROR,
111CLIENT_BAD_CHAINING,
112CLIENT_BAD_KEY_SIZE,
113CLIENT_BAD_ZIP_FILE,
114CLIENT_ZIP_ERROR,
115CLIENT_GENERIC_EXCEPTION,
116CLIENT_NETWORK_ERROR,
117CLIENT_INVALID_PKCS8,
118CLIENT_FILE_ERROR,
119CLIENT_OSSL_ERROR,
120CLIENT_CRYPT_ERROR,
121CLIENT_DIGEST_ERROR,
122 };
123
124 enum DRM_REMOVAL_ERROR {
125DRM_ERR_ENCRYPTION_KEY = 0x6000,
126DRM_VERSION_NOT_SUPPORTED,
127DRM_FILE_ERROR,
128DRM_FORMAT_NOT_SUPPORTED,
129DRM_IN_OUT_EQUALS,
130DRM_MISSING_PARAMETER,
131DRM_INVALID_KEY_SIZE,
132DRM_ERR_ENCRYPTION_KEY_FP
133 };
134
135 /**
136 * Generic exception class
137 */
138 class Exception : public std::exception
139 {
140 public:
141Exception(int code, const char* message, const char* file, int line):
142 code(code), line(line), file(file)
143{
144 std::stringstream msg;
145 msg << "Exception code : 0x" << std::setbase(16) << code << std::endl;
146 msg << "Message : " << message << std::endl;
147 if (logLevel >= LG_LOG_DEBUG)
148msg << "File : " << file << ":" << std::setbase(10) << line << std::endl;
149 fullmessage = strdup(msg.str().c_str());
150}
151
152Exception(const Exception& other)
153{
154 this->code = other.code;
155 this->line = line;
156 this->file = file;
157 this->fullmessage = strdup(other.fullmessage);
158}
159
160~Exception()
161{
162 free(fullmessage);
163}
164
165const char * what () const throw () { return fullmessage; }
166
167int getErrorCode() {return code;}
168
169private:
170int code, line;
171const char* message, *file;
172char* fullmessage;
173 };
174
175 /**
176 * @brief Throw an exception
177 */
178#define EXCEPTION(code, message)\
179 {std::stringstream __msg;__msg << message; throw gourou::Exception(code, __msg.str().c_str(), __FILE__, __LINE__);}
180
181 /**
182 * Stream writer for pugi::xml
183 */
184 class StringXMLWriter : public pugi::xml_writer
185 {
186 public:
187virtual void write(const void* data, size_t size)
188{
189 result.append(static_cast<const char*>(data), size);
190}
191
192const std::string& getResult() {return result;}
193
194 private:
195std::string result;
196 };
197
198 static const char* ws = " \t\n\r\f\v";
199
200 /**
201 * @brief trim from end of string (right)
202 */
203 inline std::string& rtrim(std::string& s, const char* t = ws)
204 {
205s.erase(s.find_last_not_of(t) + 1);
206return s;
207 }
208
209 /**
210 * @brief trim from beginning of string (left)
211 */
212 inline std::string& ltrim(std::string& s, const char* t = ws)
213 {
214s.erase(0, s.find_first_not_of(t));
215return s;
216 }
217
218 /**
219 * @brief trim from both ends of string (right then left)
220 */
221 inline std::string& trim(std::string& s, const char* t = ws)
222 {
223return ltrim(rtrim(s, t), t);
224 }
225
226 /**
227 * @brief Extract text node from tag in document
228 * It can throw an exception if tag does not exists
229 * or just return an empty value
230 */
231 static inline std::string extractTextElem(const pugi::xml_node& root, const char* tagName, bool throwOnNull=true)
232 {
233 pugi::xpath_node xpath_node = root.select_node(tagName);
234
235 if (!xpath_node)
236{
237 if (throwOnNull)
238EXCEPTION(GOUROU_TAG_NOT_FOUND, "Tag " << tagName << " not found");
239
240 return "";
241}
242
243pugi::xml_node node = xpath_node.node().first_child();
244
245if (!node)
246{
247 if (throwOnNull)
248EXCEPTION(GOUROU_TAG_NOT_FOUND, "Text element for tag " << tagName << " not found");
249
250 return "";
251}
252
253std::string res = node.value();
254 return trim(res);
255 }
256
257 /**
258 * @brief Extract text attribute from tag in document
259 * It can throw an exception if attribute does not exists
260 * or just return an empty value
261 */
262 static inline std::string extractTextAttribute(const pugi::xml_node& root, const char* tagName, const char* attributeName, bool throwOnNull=true)
263 {
264 pugi::xpath_node xpath_node = root.select_node(tagName);
265
266 if (!xpath_node)
267{
268 if (throwOnNull)
269EXCEPTION(GOUROU_TAG_NOT_FOUND, "Tag " << tagName << " not found");
270
271 return "";
272}
273
274pugi::xml_attribute attr = xpath_node.node().attribute(attributeName);
275
276if (!attr)
277{
278 if (throwOnNull)
279EXCEPTION(GOUROU_TAG_NOT_FOUND, "Attribute element " << attributeName << " for tag " << tagName << " not found");
280
281 return "";
282}
283
284std::string res = attr.value();
285return trim(res);
286 }
287
288 /**
289 * @brief Append an element to root with a sub text element
290 *
291 * @param root Root node where to put child
292 * @param name Tag name for child
293 * @param value Text child value of tag element
294 */
295 static inline void appendTextElem(pugi::xml_node& root, const std::string& name, const std::string& value)
296 {
297pugi::xml_node node = root.append_child(name.c_str());
298node.append_child(pugi::node_pcdata).set_value(value.c_str());
299 }
300
301 /**
302 * Remove "urn:uuid:" prefix and all '-' from uuid
303 * urn:uuid:9cb786e8-586a-4950-8901-fff8d2ee6025
304 * ->
305 * 9cb786e8586a49508901fff8d2ee6025
306 */
307 static inline std::string extractIdFromUUID(const std::string& uuid)
308 {
309unsigned int i = 0;
310std::string res;
311
312if (uuid.find("urn:uuid:") == 0)
313 i = 9;
314
315for(; i<uuid.size(); i++)
316{
317 if (uuid[i] != '-')
318res += uuid[i];
319}
320
321return res;
322 }
323
324 /**
325 * @brief Open a file descriptor on path. If it already exists and truncate == true, it's truncated
326 *
327 * @return Created fd, must be closed
328 */
329 static inline int createNewFile(std::string path, bool truncate=true)
330 {
331int options = O_CREAT|O_WRONLY;
332if (truncate)
333 options |= O_TRUNC;
334else
335 options |= O_APPEND;
336
337int fd = open(path.c_str(), options, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
338
339if (fd <= 0)
340 EXCEPTION(GOUROU_FILE_ERROR, "Unable to create " << path);
341
342return fd;
343 }
344
345 /**
346 * @brief Write data in a file. If it already exists, it's truncated
347 */
348 static inline void writeFile(std::string path, const unsigned char* data, unsigned int length)
349 {
350int fd = createNewFile(path);
351
352if (write(fd, data, length) != length)
353 EXCEPTION(GOUROU_FILE_ERROR, "Write error for file " << path);
354
355close (fd);
356 }
357
358 /**
359 * @brief Write data in a file. If it already exists, it's truncated
360 */
361 static inline void writeFile(std::string path, ByteArray& data)
362 {
363writeFile(path, data.data(), data.length());
364 }
365
366 /**
367 * @brief Write data in a file. If it already exists, it's truncated
368 */
369 static inline void writeFile(std::string path, const std::string& data)
370 {
371writeFile(path, (const unsigned char*)data.c_str(), data.length());
372 }
373
374 /**
375 * Read data from file
376 */
377 static inline void readFile(std::string path, const unsigned char* data, unsigned int length)
378 {
379int fd = open(path.c_str(), O_RDONLY);
380
381if (fd <= 0)
382 EXCEPTION(GOUROU_FILE_ERROR, "Unable to open " << path);
383
384if (read(fd, (void*)data, length) != length)
385 EXCEPTION(GOUROU_FILE_ERROR, "Read error for file " << path);
386
387close (fd);
388 }
389
390#define PATH_MAX_STRING_SIZE 256
391
392 // https://gist.github.com/ChisholmKyle/0cbedcd3e64132243a39
393/* recursive mkdir */
394 static inline int mkdir_p(const char *dir, const mode_t mode) {
395char tmp[PATH_MAX_STRING_SIZE];
396char *p = NULL;
397struct stat sb;
398size_t len;
399
400/* copy path */
401len = strnlen (dir, PATH_MAX_STRING_SIZE);
402if (len == 0 || len == PATH_MAX_STRING_SIZE) {
403 return -1;
404}
405memcpy (tmp, dir, len);
406tmp[len] = '\0';
407
408/* remove trailing slash */
409if(tmp[len - 1] == '/') {
410 tmp[len - 1] = '\0';
411}
412
413/* check if path exists and is a directory */
414if (stat (tmp, &sb) == 0) {
415 if (S_ISDIR (sb.st_mode)) {
416return 0;
417 }
418}
419
420/* recursive mkdir */
421for(p = tmp + 1; *p; p++) {
422 if(*p == '/') {
423*p = 0;
424/* test path */
425if (stat(tmp, &sb) != 0) {
426 /* path does not exist - create directory */
427 if (mkdir(tmp, mode) < 0) {
428return -1;
429 }
430} else if (!S_ISDIR(sb.st_mode)) {
431 /* not a directory */
432 return -1;
433}
434*p = '/';
435 }
436}
437/* test path */
438if (stat(tmp, &sb) != 0) {
439 /* path does not exist - create directory */
440 if (mkdir(tmp, mode) < 0) {
441return -1;
442 }
443} else if (!S_ISDIR(sb.st_mode)) {
444 /* not a directory */
445 return -1;
446}
447return 0;
448 }
449
450 static inline void dumpBuffer(GOUROU_LOG_LEVEL level, const char* title, const unsigned char* data, unsigned int len)
451 {
452if (gourou::logLevel < level)
453 return;
454
455printf("%s", title);
456for(unsigned int i=0; i<len; i++)
457{
458 if (i && !(i%16)) printf("\n");
459 printf("%02x ", data[i]);
460}
461printf("\n");
462 }
463}
464
465#endif

Archive Download this file