Line data Source code
1 : /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 : /*
3 : * unpacking.c
4 : * This file is part of "Sauvegarde" project.
5 : *
6 : * (C) Copyright 2014 - 2016 Olivier Delhomme
7 : * e-mail : olivier.delhomme@free.fr
8 : *
9 : * "Sauvegarde" is free software: you can redistribute it and/or modify
10 : * it under the terms of the GNU General Public License as published by
11 : * the Free Software Foundation, either version 3 of the License, or
12 : * (at your option) any later version.
13 : *
14 : * "Sauvegarde" is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * You should have received a copy of the GNU General Public License
20 : * along with "Sauvegarde". If not, see <http://www.gnu.org/licenses/>
21 : */
22 :
23 : /**
24 : * @file unpacking.c
25 : * This file contains the functions to unpack messages for all the
26 : * programs of "Sauvegarde" project.
27 : */
28 :
29 : #include "libcdpfgl.h"
30 :
31 : static guint8 get_guint8_from_json_root(json_t *root, gchar *keyname);
32 : static guint32 get_guint32_from_json_root(json_t *root, gchar *keyname);
33 : static guint64 get_guint64_from_json_root(json_t *root, gchar *keyname);
34 :
35 : /**
36 : * gets a json_t *value into the json_t *root array.
37 : * @param[in,out] root is the root that contains all meta data values
38 : * @param keyname is the keyname associated with the value that we want to
39 : * get back.
40 : * @returns the json_t "encoded" value from key keyname from the root
41 : */
42 1579533 : json_t *get_json_value_from_json_root(json_t *root, gchar *keyname)
43 : {
44 1579533 : json_t *value = NULL;
45 :
46 1579533 : if (root != NULL && keyname != NULL)
47 : {
48 1579533 : value = json_object_get(root, keyname);
49 :
50 1579533 : if (value == NULL)
51 : {
52 0 : print_error(__FILE__, __LINE__, _("Error while converting to JSON from keyname %s\n"), keyname);
53 : }
54 : }
55 :
56 1579533 : return value;
57 : }
58 :
59 :
60 : /**
61 : * returns the boolean with key keyname from the json tree root. It is used
62 : * by server to get the hostname from the json received message.
63 : * @note Freeing json_t *str here is a bad idea as it will free it into
64 : * json_t *root variable that is freed afterwards.
65 : * @param[in,out] root is the main json tree
66 : * @param keyname is the key for which we seek the string value.
67 : * @returns a newlly allocated gchar * string that is the value associated
68 : * with key keyname. It can be freed with free_variable() when no longer
69 : * needed.
70 : */
71 49926 : gboolean get_boolean_from_json_root(json_t *root, gchar *keyname)
72 : {
73 49926 : json_t *bool = NULL;
74 :
75 49926 : if (root != NULL && keyname != NULL)
76 : {
77 49926 : bool = get_json_value_from_json_root(root, keyname);
78 :
79 49926 : if (bool == json_true())
80 : {
81 : return TRUE;
82 : }
83 : else
84 : {
85 49923 : return FALSE;
86 : }
87 : }
88 :
89 : return FALSE;
90 : }
91 :
92 :
93 : /**
94 : * returns the string with key keyname from the json tree root. It is used
95 : * by server to get the hostname from the json received message.
96 : * @note Freeing json_t *str here is a bad idea as it will free it into
97 : * json_t *root variable that is freed afterwards.
98 : * @param[in,out] root is the main json tree
99 : * @param keyname is the key for which we seek the string value.
100 : * @returns a newlly allocated gchar * string that is the value associated
101 : * with key keyname. It can be freed with free_variable() when no longer
102 : * needed.
103 : */
104 249630 : gchar *get_string_from_json_root(json_t *root, gchar *keyname)
105 : {
106 249630 : json_t *str = NULL;
107 249630 : gchar *a_string = NULL;
108 :
109 249630 : if (root != NULL && keyname != NULL)
110 : {
111 249630 : str = get_json_value_from_json_root(root, keyname);
112 249630 : a_string = g_strdup(json_string_value(str));
113 : }
114 :
115 249630 : return a_string;
116 : }
117 :
118 :
119 : /**
120 : * returns the guint8 value associated with key keyname from the json tree
121 : * root.
122 : * @note Freeing json_t *value here is a bad idea as it will free it into
123 : * json_t *root variable that is freed afterwards.
124 : * @param[in,out] root is the main json tree
125 : * @param keyname is the key for which we seek the guint8 value.
126 : * @returns a guint8 number that is the value associated with key keyname.
127 : */
128 49926 : static guint8 get_guint8_from_json_root(json_t *root, gchar *keyname)
129 : {
130 49926 : json_t *value = NULL;
131 49926 : guint8 number = 0;
132 :
133 49926 : if (root != NULL && keyname != NULL)
134 : {
135 49926 : value = get_json_value_from_json_root(root, keyname);
136 49926 : number = (guint8) json_integer_value(value);
137 : }
138 :
139 49926 : return number;
140 : }
141 :
142 :
143 : /**
144 : * returns the guint32 value associated with key keyname from the json tree
145 : * root.
146 : * @note Freeing json_t *value here is a bad idea as it will free it into
147 : * json_t *root variable that is freed afterwards.
148 : * @param[in,out] root is the main json tree
149 : * @param keyname is the key for which we seek the guint32 value.
150 : * @returns a guint32 number that is the value associated with key keyname.
151 : */
152 149778 : static guint32 get_guint32_from_json_root(json_t *root, gchar *keyname)
153 : {
154 149778 : json_t *value = NULL;
155 149778 : guint32 number = 0;
156 :
157 149778 : if (root != NULL && keyname != NULL)
158 : {
159 149778 : value = get_json_value_from_json_root(root, keyname);
160 149778 : number = (guint32) json_integer_value(value);
161 : }
162 :
163 149778 : return number;
164 : }
165 :
166 :
167 : /**
168 : * returns the guint64 value associated with key keyname from the json tree
169 : * root.
170 : * @note Freeing json_t *value here is a bad idea as it will free it into
171 : * json_t *root variable that is freed afterwards.
172 : * @param[in,out] root is the main json tree
173 : * @param keyname is the key for which we seek the guint64 value.
174 : * @returns a guint64 number that is the value associated with key keyname.
175 : */
176 478904 : static guint64 get_guint64_from_json_root(json_t *root, gchar *keyname)
177 : {
178 478904 : json_t *value = NULL;
179 478904 : guint64 number = 0;
180 :
181 478904 : if (root != NULL && keyname != NULL)
182 : {
183 478904 : value = get_json_value_from_json_root(root, keyname);
184 478904 : number = (guint64) json_integer_value(value);
185 : }
186 :
187 478904 : return number;
188 : }
189 :
190 :
191 : /**
192 : * This function loads a JSON string into a json_t struture
193 : * @param json_str is the json string
194 : * @returns a pointer to a filled json_t * structure or NULL upon error
195 : */
196 145753 : json_t *load_json(gchar *json_str)
197 : {
198 145753 : json_t *root = NULL; /** json_t *root is the json tree where we will extract things */
199 : json_error_t error; /** json_error_t *error handle json errors if any. */
200 :
201 145753 : if (json_str != NULL)
202 : {
203 145753 : root = json_loads(json_str, 0, &error);
204 :
205 145753 : if (root == NULL)
206 : {
207 0 : print_error(__FILE__, __LINE__, _("Error while trying to load JSON: %s\nline: %d, column: %d, position: %d, string: %s\n"), error.text, error.line, error.column, error.position, json_str);
208 : }
209 : }
210 :
211 145753 : return root;
212 : }
213 :
214 :
215 : /**
216 : * This function returns the MESSAGE_ID from msg_id JSON field
217 : * @param json_str : a gchar * containing the JSON formated string.
218 : * @returns a gint that correspond to the msg_id field found in json_str.
219 : * If the field is not found it returns ENC_NOT_FOUND. This field
220 : * is based on ENC_* constants that are also used for the
221 : * communication between threads in client
222 : */
223 0 : gint get_json_message_id(gchar *json_str)
224 : {
225 0 : json_t *root = NULL; /** json_t *root is the json tree from which we will extract msg_id */
226 0 : gint msg_id = ENC_NOT_FOUND; /** gint msg_id is the message id from the JSON string by default it is ENC_NOT_FOUND */
227 :
228 0 : if (json_str != NULL)
229 : {
230 0 : root = load_json(json_str);
231 :
232 0 : if (root != NULL)
233 : {
234 0 : msg_id = get_guint8_from_json_root(root, "msg_id");
235 :
236 0 : json_decref(root);
237 : }
238 : }
239 :
240 0 : return msg_id;
241 : }
242 :
243 :
244 : /**
245 : * Gets the version of a version json string as returned by server's
246 : * server.
247 : * @param json_str : a gchar * containing the JSON formated string.
248 : * @returns version string or NULL
249 : */
250 0 : gchar *get_json_version(gchar *json_str)
251 : {
252 0 : json_t *root = NULL; /** json_t *root is the json tree from which we will extract version's string */
253 0 : gchar *version = NULL; /** version'string has extracted or NULL */
254 :
255 0 : if (json_str != NULL)
256 : {
257 0 : root = load_json(json_str);
258 :
259 0 : if (root != NULL)
260 : {
261 0 : version = get_string_from_json_root(root, "version");
262 :
263 0 : json_decref(root);
264 : }
265 : }
266 :
267 0 : return version;
268 : }
269 :
270 :
271 : /**
272 : * This function returns a list of hash_data_t * from an json array
273 : * @param root is the root json string that may contain an array named "name"
274 : * @param name is the name of the array to look for into
275 : * @param only_hash is a boolean saying that we only have a hash list in
276 : * root if set to TRUE and that we have a complete hash_data_t list
277 : * if set to FALSE.
278 : * @returns a GSList that me be composed of 0 element (ie NULL). Elements
279 : * are of type hash_data_t *.
280 : */
281 142809 : GList *extract_glist_from_array(json_t *root, gchar *name, gboolean only_hash)
282 : {
283 142809 : json_t *array = NULL; /** json_t *array is the retrieved array used to iter over to fill the list */
284 142809 : size_t index = 0; /** size_t index is the iterator to iter over the array */
285 142809 : json_t *value = NULL; /** json_t *value : value = array[index] when iterating with json_array_foreach */
286 142809 : GList *head = NULL; /** GSList *head the list to build and iclude into meta_data_t *meta */
287 142809 : guchar *a_hash = NULL; /** guchar *a_hash is one base64 decoded hash (binary format) */
288 142809 : gsize hash_len = 0; /** gsize hash_len is the length of the decoded hash (must alwas be HASH_LEN) */
289 142809 : hash_data_t *hash_data = NULL;
290 :
291 142809 : if (root != NULL && name != NULL)
292 : {
293 :
294 : /* creating a list with the json array found in root json string */
295 142809 : array = get_json_value_from_json_root(root, name);
296 :
297 : /**
298 : * @note : This is a loop from jansson library for the array.
299 : * One needs at least jansson 2.5 to compile this.
300 : */
301 1126752 : json_array_foreach(array, index, value)
302 : {
303 983943 : if (only_hash == TRUE)
304 : {
305 757782 : a_hash = g_base64_decode(json_string_value(value), &hash_len);
306 757782 : hash_data = new_hash_data_t(NULL, 0, a_hash);
307 : }
308 : else
309 : {
310 226161 : hash_data = convert_json_t_to_hash_data(value);
311 : }
312 :
313 983943 : head = g_list_prepend(head, hash_data);
314 : }
315 :
316 142809 : head = g_list_reverse(head);
317 : }
318 :
319 142809 : return head;
320 : }
321 :
322 :
323 : /**
324 : * Fills a server_meta_data_t from data that are in json_t *root
325 : * @param root is the JSON string that should contain all data needed
326 : * to fill the server_meta_data_t * structure.
327 : * @returns a newly allocated server_meta_data_t * structure filled
328 : * accordingly.
329 : */
330 49926 : static server_meta_data_t *fills_server_meta_data_t_from_json_t(json_t *root)
331 : {
332 49926 : meta_data_t *meta = NULL; /** meta_data_t *meta will be returned in smeta and contain file's metadata */
333 49926 : server_meta_data_t *smeta = NULL; /** server_meta_data_t *smeta will be returned at the end */
334 :
335 49926 : if (root != NULL)
336 : {
337 49926 : smeta = new_smeta_data_t();
338 49926 : meta = new_meta_data_t();
339 :
340 49926 : meta->file_type = get_guint8_from_json_root(root, "filetype");
341 49926 : meta->mode = get_guint32_from_json_root(root, "mode");
342 :
343 49926 : meta->atime = get_guint64_from_json_root(root, "atime");
344 49926 : meta->ctime = get_guint64_from_json_root(root, "ctime");
345 49926 : meta->mtime = get_guint64_from_json_root(root, "mtime");
346 49926 : meta->size = get_guint64_from_json_root(root, "fsize");
347 49926 : meta->inode = get_guint64_from_json_root(root, "inode");
348 :
349 49926 : meta->owner = get_string_from_json_root(root, "owner");
350 49926 : meta->group = get_string_from_json_root(root, "group");
351 :
352 49926 : meta->uid = get_guint32_from_json_root(root, "uid");
353 49926 : meta->gid = get_guint32_from_json_root(root, "gid");
354 :
355 49926 : meta->name = get_string_from_json_root(root, "name");
356 49926 : meta->link = get_string_from_json_root(root, "link");
357 :
358 49926 : meta->hash_data_list = extract_glist_from_array(root, "hash_list", TRUE);
359 :
360 49926 : smeta->meta = meta;
361 49926 : smeta->hostname = get_string_from_json_root(root, "hostname");
362 49926 : smeta->data_sent = get_boolean_from_json_root(root, "data_sent");
363 : }
364 :
365 49926 : return smeta;
366 : }
367 :
368 :
369 : /**
370 : * This function returns a list from an json array.
371 : * @param root is the root json string that must contain an array named
372 : * "file_list"
373 : * @returns a GSList that may be composed of 0 element (ie NULL). Elements
374 : * are of type server_meta_data_t *.
375 : */
376 12 : GSList *extract_smeta_gslist_from_file_list(json_t *root)
377 : {
378 12 : json_t *array = NULL; /** json_t *array is the retrieved array used to iter over to fill the list */
379 12 : size_t index = 0; /** size_t index is the iterator to iter over the array */
380 12 : json_t *value = NULL; /** json_t *value : value = array[index] when iterating with json_array_foreach */
381 12 : GSList *head = NULL; /** GSList *head the list to build and iclude into meta_data_t *meta */
382 12 : server_meta_data_t *smeta = NULL; /** server_meta_data_t *smeta will be returned at the end */
383 :
384 12 : if (root != NULL)
385 : {
386 :
387 : /* creating a list with the json array found in root json string */
388 12 : array = get_json_value_from_json_root(root, "file_list");
389 :
390 : /**
391 : * @note : This is a loop from jansson library for the array.
392 : * One needs at least jansson 2.5 to compile this.
393 : */
394 193 : json_array_foreach(array, index, value)
395 : {
396 181 : smeta = fills_server_meta_data_t_from_json_t(value);
397 181 : head = g_slist_prepend(head, smeta);
398 : }
399 : }
400 :
401 12 : return head;
402 : }
403 :
404 :
405 : /**
406 : * Function that converts json_t * root containing the keys "hash", "data"
407 : * and "read" into hash_data_t structure.
408 : * @param root is a json_t * variable containing the keys "hash", "data"
409 : * and read
410 : * @returns a newly allocated hash_data_t structure with the
411 : * corresponding data in it.
412 : */
413 229274 : hash_data_t *convert_json_t_to_hash_data(json_t *root)
414 : {
415 229274 : guchar *data = NULL;
416 229274 : guint8 *hash = NULL;
417 229274 : gsize data_len = 0;
418 229274 : gsize hash_len = 0;
419 229274 : gssize read = 0;
420 229274 : gchar *string_read = NULL;
421 229274 : gchar *string_hash_len = NULL;
422 229274 : gchar *string_data_len = NULL;
423 :
424 229274 : hash_data_t *hash_data = NULL;
425 :
426 229274 : if (root != NULL)
427 : {
428 : /* This code is 10% faster then the older one (which was clearer) */
429 229274 : data = (guchar *) g_base64_decode(json_string_value(get_json_value_from_json_root(root, "data")), &data_len);
430 229274 : hash = (guint8 *) g_base64_decode(json_string_value(get_json_value_from_json_root(root, "hash")), &hash_len);
431 :
432 229274 : read = get_guint64_from_json_root(root, "size");
433 :
434 : /* Some basic verifications */
435 229274 : if (data_len == read && hash_len == HASH_LEN)
436 : {
437 229274 : hash_data = new_hash_data_t(data, read, hash);
438 : }
439 : else
440 : {
441 : /**
442 : * We need to translated this number into a string before
443 : * inserting it into the final string in order to allow
444 : * this final string to be translated in an other language.
445 : */
446 0 : string_data_len = g_strdup_printf("%" G_GSIZE_FORMAT, data_len);
447 0 : string_hash_len = g_strdup_printf("%" G_GSIZE_FORMAT, hash_len);
448 0 : string_read = g_strdup_printf("%" G_GSSIZE_FORMAT, read);
449 :
450 0 : print_error(__FILE__, __LINE__, _("Something is wrong with lengths: data_len = %s, read = %s, hash_len = %s, HASH_LEN = %d\n"), string_data_len, string_read, string_hash_len, HASH_LEN);
451 :
452 0 : free_variable(string_data_len);
453 0 : free_variable(string_hash_len);
454 0 : free_variable(string_read);
455 : }
456 : }
457 :
458 229274 : return hash_data;
459 : }
460 :
461 :
462 : /**
463 : * Function that converts json_str containing the keys "hash", "data"
464 : * and "read" into hash_data_t structure.
465 : * @param json_str is a json string containing the keys "hash", "data"
466 : * and read
467 : * @returns a newly allocated hash_data_t structure with the
468 : * corresponding data in it.
469 : */
470 3113 : hash_data_t *convert_string_to_hash_data(gchar *json_str)
471 : {
472 3113 : json_t *root = NULL;
473 :
474 3113 : hash_data_t *hash_data = NULL;
475 :
476 3113 : if (json_str != NULL)
477 : {
478 3113 : root = load_json(json_str);
479 :
480 3113 : if (root != NULL)
481 : {
482 3113 : hash_data = convert_json_t_to_hash_data(root);
483 3113 : json_decref(root);
484 : }
485 : }
486 :
487 3113 : return hash_data;
488 : }
489 :
490 :
491 : /**
492 : * This function should return a newly allocated server_meta_data_t *
493 : * structure with all informations included from the json string.
494 : * @param json_str is a gchar * containing the JSON formated string.
495 : * @returns a newly_allocated server_meta_data_t * structure that can be
496 : * freed when no longer needed with free_smeta_data_t() function.
497 : * This function can return NULL if json_str is NULL itself.
498 : */
499 49745 : server_meta_data_t *convert_json_to_smeta_data(gchar *json_str)
500 : {
501 49745 : json_t *root = NULL; /** json_t *root is the json tree from which we will extract everything */
502 49745 : server_meta_data_t *smeta = NULL; /** server_meta_data_t *smeta will be returned at the end */
503 :
504 : /**
505 : * @todo : validate that we have a json string and make
506 : * sure that this is a meta_data one.
507 : */
508 :
509 49745 : if (json_str != NULL)
510 : {
511 :
512 49745 : root = load_json(json_str);
513 :
514 49745 : if (root != NULL)
515 : {
516 49745 : smeta = fills_server_meta_data_t_from_json_t(root);
517 :
518 49745 : json_decref(root);
519 : }
520 : }
521 :
522 49745 : return smeta;
523 : }
|