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
59 };
60
61 enum FULFILL_ERROR {
62FF_ACSM_FILE_NOT_EXISTS = 0x1100,
63FF_INVALID_ACSM_FILE,
64FF_NO_HMAC_IN_ACSM_FILE,
65FF_NOT_ACTIVATED,
66FF_NO_OPERATOR_URL
67 };
68
69 enum DOWNLOAD_ERROR {
70DW_NO_ITEM = 0x1200,
71DW_NO_EBX_HANDLER,
72 };
73
74 enum SIGNIN_ERROR {
75SIGN_INVALID_CREDENTIALS = 0x1300,
76 };
77
78 enum ACTIVATE_ERROR {
79ACTIVATE_NOT_SIGNEDIN = 0x1400
80 };
81
82 enum DEV_ERROR {
83DEV_MKPATH = 0x2000,
84DEV_MAC_ERROR,
85DEV_INVALID_DEVICE_FILE,
86DEV_INVALID_DEVICE_KEY_FILE,
87DEV_INVALID_DEV_PROPERTY,
88 };
89
90 enum USER_ERROR {
91USER_MKPATH = 0x3000,
92USER_INVALID_ACTIVATION_FILE,
93USER_NO_AUTHENTICATION_URL,
94USER_NO_PROPERTY,
95 };
96
97 enum FULFILL_ITEM_ERROR {
98FFI_INVALID_FULFILLMENT_DATA = 0x4000
99 };
100
101 enum CLIENT_ERROR {
102CLIENT_BAD_PARAM = 0x5000,
103CLIENT_INVALID_PKCS12,
104CLIENT_INVALID_CERTIFICATE,
105CLIENT_NO_PRIV_KEY,
106CLIENT_RSA_ERROR,
107CLIENT_BAD_CHAINING,
108CLIENT_BAD_KEY_SIZE,
109CLIENT_BAD_ZIP_FILE,
110CLIENT_ZIP_ERROR,
111CLIENT_GENERIC_EXCEPTION,
112CLIENT_NETWORK_ERROR,
113 };
114
115 /**
116 * Generic exception class
117 */
118 class Exception : public std::exception
119 {
120 public:
121Exception(int code, const char* message, const char* file, int line):
122 code(code), line(line), file(file)
123{
124 std::stringstream msg;
125 msg << "Exception code : 0x" << std::setbase(16) << code << std::endl;
126 msg << "Message : " << message << std::endl;
127 if (logLevel >= DEBUG)
128msg << "File : " << file << ":" << std::setbase(10) << line << std::endl;
129 fullmessage = strdup(msg.str().c_str());
130}
131
132Exception(const Exception& other)
133{
134 this->code = other.code;
135 this->line = line;
136 this->file = file;
137 this->fullmessage = strdup(other.fullmessage);
138}
139
140~Exception()
141{
142 free(fullmessage);
143}
144
145const char * what () const throw () { return fullmessage; }
146
147int getErrorCode() {return code;}
148
149private:
150int code, line;
151const char* message, *file;
152char* fullmessage;
153 };
154
155 /**
156 * @brief Throw an exception
157 */
158#define EXCEPTION(code, message)\
159 {std::stringstream __msg;__msg << message; throw gourou::Exception(code, __msg.str().c_str(), __FILE__, __LINE__);}
160
161 /**
162 * Stream writer for pugi::xml
163 */
164 class StringXMLWriter : public pugi::xml_writer
165 {
166 public:
167virtual void write(const void* data, size_t size)
168{
169 result.append(static_cast<const char*>(data), size);
170}
171
172const std::string& getResult() {return result;}
173
174 private:
175std::string result;
176 };
177
178 static const char* ws = " \t\n\r\f\v";
179
180 /**
181 * @brief trim from end of string (right)
182 */
183 inline std::string& rtrim(std::string& s, const char* t = ws)
184 {
185s.erase(s.find_last_not_of(t) + 1);
186return s;
187 }
188
189 /**
190 * @brief trim from beginning of string (left)
191 */
192 inline std::string& ltrim(std::string& s, const char* t = ws)
193 {
194s.erase(0, s.find_first_not_of(t));
195return s;
196 }
197
198 /**
199 * @brief trim from both ends of string (right then left)
200 */
201 inline std::string& trim(std::string& s, const char* t = ws)
202 {
203return ltrim(rtrim(s, t), t);
204 }
205
206 /**
207 * @brief Extract text node from tag in document
208 * It can throw an exception if tag does not exists
209 * or just return an empty value
210 */
211 static inline std::string extractTextElem(const pugi::xml_document& doc, const char* tagName, bool throwOnNull=true)
212 {
213 pugi::xpath_node xpath_node = doc.select_node(tagName);
214
215 if (!xpath_node)
216{
217 if (throwOnNull)
218EXCEPTION(GOUROU_TAG_NOT_FOUND, "Tag " << tagName << " not found");
219
220 return "";
221}
222
223pugi::xml_node node = xpath_node.node().first_child();
224
225if (!node)
226{
227 if (throwOnNull)
228EXCEPTION(GOUROU_TAG_NOT_FOUND, "Text element for tag " << tagName << " not found");
229
230 return "";
231}
232
233std::string res = node.value();
234 return trim(res);
235 }
236
237 static inline std::string extractTextElem(const pugi::xml_node& doc, const char* tagName, bool throwOnNull=true)
238 {
239 pugi::xpath_node xpath_node = doc.select_node(tagName);
240
241 if (!xpath_node)
242{
243 if (throwOnNull)
244EXCEPTION(GOUROU_TAG_NOT_FOUND, "Tag " << tagName << " not found");
245
246 return "";
247}
248
249pugi::xml_node node = xpath_node.node().first_child();
250
251if (!node)
252{
253 if (throwOnNull)
254EXCEPTION(GOUROU_TAG_NOT_FOUND, "Text element for tag " << tagName << " not found");
255
256 return "";
257}
258
259std::string res = node.value();
260 return trim(res);
261 }
262
263 /**
264 * @brief Append an element to root with a sub text element
265 *
266 * @param root Root node where to put child
267 * @param name Tag name for child
268 * @param value Text child value of tag element
269 */
270 static inline void appendTextElem(pugi::xml_node& root, const std::string& name, const std::string& value)
271 {
272pugi::xml_node node = root.append_child(name.c_str());
273node.append_child(pugi::node_pcdata).set_value(value.c_str());
274 }
275
276 /**
277 * @brief Write data in a file. If it already exists, it's truncated
278 */
279 static inline void writeFile(std::string path, const unsigned char* data, unsigned int length)
280 {
281int fd = open(path.c_str(), O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
282
283if (fd <= 0)
284 EXCEPTION(GOUROU_FILE_ERROR, "Unable to create " << path);
285
286if (write(fd, data, length) != length)
287 EXCEPTION(GOUROU_FILE_ERROR, "Write error for file " << path);
288
289close (fd);
290 }
291
292 /**
293 * @brief Write data in a file. If it already exists, it's truncated
294 */
295 static inline void writeFile(std::string path, ByteArray& data)
296 {
297writeFile(path, data.data(), data.length());
298 }
299
300 /**
301 * @brief Write data in a file. If it already exists, it's truncated
302 */
303 static inline void writeFile(std::string path, const std::string& data)
304 {
305writeFile(path, (const unsigned char*)data.c_str(), data.length());
306 }
307
308 /**
309 * Read data from file
310 */
311 static inline void readFile(std::string path, const unsigned char* data, unsigned int length)
312 {
313int fd = open(path.c_str(), O_RDONLY);
314
315if (fd <= 0)
316 EXCEPTION(GOUROU_FILE_ERROR, "Unable to open " << path);
317
318if (read(fd, (void*)data, length) != length)
319 EXCEPTION(GOUROU_FILE_ERROR, "Read error for file " << path);
320
321close (fd);
322 }
323
324#define PATH_MAX_STRING_SIZE 256
325
326 // https://gist.github.com/ChisholmKyle/0cbedcd3e64132243a39
327/* recursive mkdir */
328 static inline int mkdir_p(const char *dir, const mode_t mode) {
329char tmp[PATH_MAX_STRING_SIZE];
330char *p = NULL;
331struct stat sb;
332size_t len;
333
334/* copy path */
335len = strnlen (dir, PATH_MAX_STRING_SIZE);
336if (len == 0 || len == PATH_MAX_STRING_SIZE) {
337 return -1;
338}
339memcpy (tmp, dir, len);
340tmp[len] = '\0';
341
342/* remove trailing slash */
343if(tmp[len - 1] == '/') {
344 tmp[len - 1] = '\0';
345}
346
347/* check if path exists and is a directory */
348if (stat (tmp, &sb) == 0) {
349 if (S_ISDIR (sb.st_mode)) {
350return 0;
351 }
352}
353
354/* recursive mkdir */
355for(p = tmp + 1; *p; p++) {
356 if(*p == '/') {
357*p = 0;
358/* test path */
359if (stat(tmp, &sb) != 0) {
360 /* path does not exist - create directory */
361 if (mkdir(tmp, mode) < 0) {
362return -1;
363 }
364} else if (!S_ISDIR(sb.st_mode)) {
365 /* not a directory */
366 return -1;
367}
368*p = '/';
369 }
370}
371/* test path */
372if (stat(tmp, &sb) != 0) {
373 /* path does not exist - create directory */
374 if (mkdir(tmp, mode) < 0) {
375return -1;
376 }
377} else if (!S_ISDIR(sb.st_mode)) {
378 /* not a directory */
379 return -1;
380}
381return 0;
382 }
383}
384
385#endif

Archive Download this file