Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2014, 2015, 2016, 2020, 2021 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file json/json.c
18 : * @brief helper functions for JSON processing using libjansson
19 : * @author Sree Harsha Totakura <sreeharsha@totakura.in>
20 : * @author Christian Grothoff
21 : */
22 : #include "platform.h"
23 : #include <gnunet/gnunet_util_lib.h>
24 : #include "taler_util.h"
25 : #include "taler_json_lib.h"
26 : #include <unistr.h>
27 :
28 :
29 : /**
30 : * Check if @a json contains a 'real' value anywhere.
31 : *
32 : * @param json json to check
33 : * @return true if a real is in it somewhere
34 : */
35 : static bool
36 67 : contains_real (const json_t *json)
37 : {
38 67 : if (json_is_real (json))
39 0 : return true;
40 67 : if (json_is_object (json))
41 : {
42 : json_t *member;
43 : const char *name;
44 :
45 75 : json_object_foreach ((json_t *) json, name, member)
46 42 : if (contains_real (member))
47 0 : return true;
48 33 : return false;
49 : }
50 34 : if (json_is_array (json))
51 : {
52 : json_t *member;
53 : size_t index;
54 :
55 0 : json_array_foreach ((json_t *) json, index, member)
56 0 : if (contains_real (member))
57 0 : return true;
58 0 : return false;
59 : }
60 34 : return false;
61 : }
62 :
63 :
64 : /**
65 : * Dump character in the low range into @a buf
66 : * following RFC 8785.
67 : *
68 : * @param[in,out] buf buffer to modify
69 : * @param val value to dump
70 : */
71 : static void
72 3 : lowdump (struct GNUNET_Buffer *buf,
73 : unsigned char val)
74 : {
75 : char scratch[7];
76 :
77 3 : switch (val)
78 : {
79 0 : case 0x8:
80 0 : GNUNET_buffer_write (buf,
81 : "\\b",
82 : 2);
83 0 : break;
84 0 : case 0x9:
85 0 : GNUNET_buffer_write (buf,
86 : "\\t",
87 : 2);
88 0 : break;
89 0 : case 0xA:
90 0 : GNUNET_buffer_write (buf,
91 : "\\n",
92 : 2);
93 0 : break;
94 0 : case 0xC:
95 0 : GNUNET_buffer_write (buf,
96 : "\\f",
97 : 2);
98 0 : break;
99 0 : case 0xD:
100 0 : GNUNET_buffer_write (buf,
101 : "\\r",
102 : 2);
103 0 : break;
104 3 : default:
105 3 : GNUNET_snprintf (scratch,
106 : sizeof (scratch),
107 : "\\u%04x",
108 : (unsigned int) val);
109 3 : GNUNET_buffer_write (buf,
110 : scratch,
111 : 6);
112 3 : break;
113 : }
114 3 : }
115 :
116 :
117 : /**
118 : * Re-encode string at @a inp to match RFC 8785 (section 3.2.2.2).
119 : *
120 : * @param[in,out] inp pointer to string to re-encode
121 : * @return number of bytes in resulting @a inp
122 : */
123 : static size_t
124 28 : rfc8785encode (char **inp)
125 : {
126 28 : struct GNUNET_Buffer buf = { 0 };
127 28 : size_t left = strlen (*inp) + 1;
128 : size_t olen;
129 28 : char *in = *inp;
130 28 : const char *pos = in;
131 :
132 28 : GNUNET_buffer_prealloc (&buf,
133 : left + 40);
134 28 : buf.warn_grow = 0; /* disable, + 40 is just a wild guess */
135 : while (1)
136 2548 : {
137 2576 : int mbl = u8_mblen ((unsigned char *) pos,
138 : left);
139 : unsigned char val;
140 :
141 2576 : if (0 == mbl)
142 28 : break;
143 2548 : val = (unsigned char) *pos;
144 2548 : if ( (1 == mbl) &&
145 : (val <= 0x1F) )
146 : {
147 : /* Should not happen, as input is produced by
148 : * JSON stringification */
149 0 : GNUNET_break (0);
150 0 : lowdump (&buf,
151 : val);
152 : }
153 2548 : else if ( (1 == mbl) && ('\\' == *pos) )
154 : {
155 8 : switch (*(pos + 1))
156 : {
157 1 : case '\\':
158 1 : mbl = 2;
159 1 : GNUNET_buffer_write (&buf,
160 : pos,
161 : mbl);
162 1 : break;
163 3 : case 'u':
164 : {
165 : unsigned int num;
166 : uint32_t n32;
167 : unsigned char res[8];
168 : size_t rlen;
169 :
170 3 : GNUNET_assert ( (1 ==
171 : sscanf (pos + 2,
172 : "%4x",
173 : &num)) ||
174 : (1 ==
175 : sscanf (pos + 2,
176 : "%4X",
177 : &num)) );
178 3 : mbl = 6;
179 3 : n32 = (uint32_t) num;
180 3 : rlen = sizeof (res);
181 3 : u32_to_u8 (&n32,
182 : 1,
183 : res,
184 : &rlen);
185 3 : if ( (1 == rlen) &&
186 3 : (res[0] <= 0x1F) )
187 : {
188 3 : lowdump (&buf,
189 3 : res[0]);
190 : }
191 : else
192 : {
193 0 : GNUNET_buffer_write (&buf,
194 : (const char *) res,
195 : rlen);
196 : }
197 : }
198 3 : break;
199 4 : default:
200 4 : mbl = 2;
201 4 : GNUNET_buffer_write (&buf,
202 : pos,
203 : mbl);
204 4 : break;
205 : }
206 : }
207 : else
208 : {
209 2540 : GNUNET_buffer_write (&buf,
210 : pos,
211 : mbl);
212 : }
213 2548 : left -= mbl;
214 2548 : pos += mbl;
215 : }
216 :
217 : /* 0-terminate buffer */
218 28 : GNUNET_buffer_write (&buf,
219 : "",
220 : 1);
221 28 : GNUNET_free (in);
222 28 : *inp = GNUNET_buffer_reap (&buf,
223 : &olen);
224 28 : return olen;
225 : }
226 :
227 :
228 : /**
229 : * Dump the @a json to a string and hash it.
230 : *
231 : * @param json value to hash
232 : * @param salt salt value to include when using HKDF,
233 : * NULL to not use any salt and to use SHA512
234 : * @param[out] hc where to store the hash
235 : * @return #GNUNET_OK on success,
236 : * #GNUNET_NO if @a json was not hash-able
237 : * #GNUNET_SYSERR on failure
238 : */
239 : static enum GNUNET_GenericReturnValue
240 25 : dump_and_hash (const json_t *json,
241 : const char *salt,
242 : struct GNUNET_HashCode *hc)
243 : {
244 : char *wire_enc;
245 : size_t len;
246 :
247 25 : if (NULL == json)
248 : {
249 0 : GNUNET_break_op (0);
250 0 : return GNUNET_NO;
251 : }
252 25 : if (contains_real (json))
253 : {
254 0 : GNUNET_break_op (0);
255 0 : return GNUNET_NO;
256 : }
257 25 : if (NULL == (wire_enc = json_dumps (json,
258 : JSON_ENCODE_ANY
259 : | JSON_COMPACT
260 : | JSON_SORT_KEYS)))
261 : {
262 0 : GNUNET_break (0);
263 0 : return GNUNET_SYSERR;
264 : }
265 25 : len = rfc8785encode (&wire_enc);
266 25 : if (NULL == salt)
267 : {
268 9 : GNUNET_CRYPTO_hash (wire_enc,
269 : len,
270 : hc);
271 : }
272 : else
273 : {
274 16 : if (GNUNET_YES !=
275 16 : GNUNET_CRYPTO_kdf (hc,
276 : sizeof (*hc),
277 : salt,
278 16 : strlen (salt) + 1,
279 : wire_enc,
280 : len,
281 : NULL,
282 : 0))
283 : {
284 0 : GNUNET_break (0);
285 0 : free (wire_enc);
286 0 : return GNUNET_SYSERR;
287 : }
288 : }
289 25 : free (wire_enc);
290 25 : return GNUNET_OK;
291 : }
292 :
293 :
294 : /**
295 : * Replace "forgettable" parts of a JSON object with their salted hash.
296 : *
297 : * @param[in] in some JSON value
298 : * @param[out] out resulting JSON value
299 : * @return #GNUNET_OK on success,
300 : * #GNUNET_NO if @a json was not hash-able
301 : * #GNUNET_SYSERR on failure
302 : */
303 : static enum GNUNET_GenericReturnValue
304 46 : forget (const json_t *in,
305 : json_t **out)
306 : {
307 46 : if (json_is_real (in))
308 : {
309 : /* floating point is not allowed! */
310 0 : GNUNET_break_op (0);
311 0 : return GNUNET_NO;
312 : }
313 46 : if (json_is_array (in))
314 : {
315 : /* array is a JSON array */
316 : size_t index;
317 : json_t *value;
318 : json_t *ret;
319 :
320 0 : ret = json_array ();
321 0 : if (NULL == ret)
322 : {
323 0 : GNUNET_break (0);
324 0 : return GNUNET_SYSERR;
325 : }
326 0 : json_array_foreach (in, index, value) {
327 : enum GNUNET_GenericReturnValue iret;
328 : json_t *t;
329 :
330 0 : iret = forget (value,
331 : &t);
332 0 : if (GNUNET_OK != iret)
333 : {
334 0 : json_decref (ret);
335 0 : return iret;
336 : }
337 0 : if (0 != json_array_append_new (ret,
338 : t))
339 : {
340 0 : GNUNET_break (0);
341 0 : json_decref (ret);
342 0 : return GNUNET_SYSERR;
343 : }
344 : }
345 0 : *out = ret;
346 0 : return GNUNET_OK;
347 : }
348 46 : if (json_is_object (in))
349 : {
350 : json_t *ret;
351 : const char *key;
352 : json_t *value;
353 : json_t *fg;
354 : json_t *rx;
355 :
356 24 : fg = json_object_get (in,
357 : "$forgettable");
358 24 : rx = json_object_get (in,
359 : "$forgotten");
360 24 : if (NULL != rx)
361 : {
362 6 : rx = json_deep_copy (rx); /* should be shallow
363 : by structure, but
364 : deep copy is safer */
365 6 : if (NULL == rx)
366 : {
367 0 : GNUNET_break (0);
368 0 : return GNUNET_SYSERR;
369 : }
370 : }
371 24 : ret = json_object ();
372 24 : if (NULL == ret)
373 : {
374 0 : GNUNET_break (0);
375 0 : return GNUNET_SYSERR;
376 : }
377 72 : json_object_foreach ((json_t*) in, key, value) {
378 : json_t *t;
379 : json_t *salt;
380 : enum GNUNET_GenericReturnValue iret;
381 :
382 48 : if (fg == value)
383 15 : continue; /* skip! */
384 33 : if (rx == value)
385 0 : continue; /* skip! */
386 46 : if ( (NULL != rx) &&
387 : (NULL !=
388 13 : json_object_get (rx,
389 : key)) )
390 : {
391 0 : (void) json_object_del (ret,
392 : key);
393 0 : continue; /* already forgotten earlier */
394 : }
395 33 : iret = forget (value,
396 : &t);
397 33 : if (GNUNET_OK != iret)
398 : {
399 0 : json_decref (ret);
400 0 : json_decref (rx);
401 0 : return iret;
402 : }
403 55 : if ( (NULL != fg) &&
404 22 : (NULL != (salt = json_object_get (fg,
405 : key))) )
406 12 : {
407 : /* 't' is to be forgotten! */
408 : struct GNUNET_HashCode hc;
409 :
410 12 : if (! json_is_string (salt))
411 : {
412 0 : GNUNET_break_op (0);
413 0 : json_decref (ret);
414 0 : json_decref (rx);
415 0 : json_decref (t);
416 0 : return GNUNET_NO;
417 : }
418 12 : iret = dump_and_hash (t,
419 : json_string_value (salt),
420 : &hc);
421 12 : if (GNUNET_OK != iret)
422 : {
423 0 : json_decref (ret);
424 0 : json_decref (rx);
425 0 : json_decref (t);
426 0 : return iret;
427 : }
428 12 : json_decref (t);
429 : /* scrub salt */
430 12 : if (0 !=
431 12 : json_object_del (fg,
432 : key))
433 : {
434 0 : GNUNET_break_op (0);
435 0 : json_decref (ret);
436 0 : json_decref (rx);
437 0 : return GNUNET_NO;
438 : }
439 12 : if (NULL == rx)
440 9 : rx = json_object ();
441 12 : if (NULL == rx)
442 : {
443 0 : GNUNET_break (0);
444 0 : json_decref (ret);
445 0 : return GNUNET_SYSERR;
446 : }
447 12 : if (0 !=
448 12 : json_object_set_new (rx,
449 : key,
450 : GNUNET_JSON_from_data_auto (&hc)))
451 : {
452 0 : GNUNET_break (0);
453 0 : json_decref (ret);
454 0 : json_decref (rx);
455 0 : return GNUNET_SYSERR;
456 : }
457 : }
458 : else
459 : {
460 : /* 't' to be used without 'forgetting' */
461 21 : if (0 !=
462 21 : json_object_set_new (ret,
463 : key,
464 : t))
465 : {
466 0 : GNUNET_break (0);
467 0 : json_decref (ret);
468 0 : json_decref (rx);
469 0 : return GNUNET_SYSERR;
470 : }
471 : }
472 : } /* json_object_foreach */
473 39 : if ( (NULL != rx) &&
474 : (0 !=
475 15 : json_object_set_new (ret,
476 : "$forgotten",
477 : rx)) )
478 : {
479 0 : GNUNET_break (0);
480 0 : json_decref (ret);
481 0 : return GNUNET_SYSERR;
482 : }
483 24 : *out = ret;
484 24 : return GNUNET_OK;
485 : }
486 22 : *out = json_incref ((json_t *) in);
487 22 : return GNUNET_OK;
488 : }
489 :
490 :
491 : enum GNUNET_GenericReturnValue
492 9 : TALER_JSON_contract_hash (const json_t *json,
493 : struct TALER_PrivateContractHashP *hc)
494 : {
495 : enum GNUNET_GenericReturnValue ret;
496 : json_t *cjson;
497 : json_t *dc;
498 :
499 9 : dc = json_deep_copy (json);
500 9 : ret = forget (dc,
501 : &cjson);
502 9 : json_decref (dc);
503 9 : if (GNUNET_OK != ret)
504 0 : return ret;
505 9 : ret = dump_and_hash (cjson,
506 : NULL,
507 : &hc->hash);
508 9 : json_decref (cjson);
509 9 : return ret;
510 : }
511 :
512 :
513 : enum GNUNET_GenericReturnValue
514 4 : TALER_JSON_contract_mark_forgettable (json_t *json,
515 : const char *field)
516 : {
517 : json_t *fg;
518 : struct GNUNET_ShortHashCode salt;
519 :
520 4 : if (! json_is_object (json))
521 : {
522 0 : GNUNET_break (0);
523 0 : return GNUNET_SYSERR;
524 : }
525 : /* check field name is legal for forgettable field */
526 12 : for (const char *f = field; '\0' != *f; f++)
527 : {
528 8 : char c = *f;
529 :
530 8 : if ( (c >= 'a') && (c <= 'z') )
531 4 : continue;
532 4 : if ( (c >= 'A') && (c <= 'Z') )
533 0 : continue;
534 4 : if ( (c >= '0') && (c <= '9') )
535 4 : continue;
536 0 : if ('_' == c)
537 0 : continue;
538 0 : GNUNET_break (0);
539 0 : return GNUNET_SYSERR;
540 : }
541 4 : if (NULL == json_object_get (json,
542 : field))
543 : {
544 : /* field must exist */
545 0 : GNUNET_break (0);
546 0 : return GNUNET_SYSERR;
547 : }
548 4 : fg = json_object_get (json,
549 : "$forgettable");
550 4 : if (NULL == fg)
551 : {
552 3 : fg = json_object ();
553 3 : if (0 !=
554 3 : json_object_set_new (json,
555 : "$forgettable",
556 : fg))
557 : {
558 0 : GNUNET_break (0);
559 0 : return GNUNET_SYSERR;
560 : }
561 : }
562 :
563 4 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
564 : &salt,
565 : sizeof (salt));
566 4 : if (0 !=
567 4 : json_object_set_new (fg,
568 : field,
569 : GNUNET_JSON_from_data_auto (&salt)))
570 : {
571 0 : GNUNET_break (0);
572 0 : return GNUNET_SYSERR;
573 : }
574 4 : return GNUNET_OK;
575 : }
576 :
577 :
578 : enum GNUNET_GenericReturnValue
579 4 : TALER_JSON_contract_part_forget (json_t *json,
580 : const char *field)
581 : {
582 : json_t *fg;
583 : const json_t *part;
584 : json_t *fp;
585 : json_t *rx;
586 : struct GNUNET_HashCode hc;
587 : const char *salt;
588 : enum GNUNET_GenericReturnValue ret;
589 :
590 4 : if (! json_is_object (json))
591 : {
592 0 : GNUNET_break (0);
593 0 : return GNUNET_SYSERR;
594 : }
595 4 : if (NULL == (part = json_object_get (json,
596 : field)))
597 : {
598 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
599 : "Did not find field `%s' we were asked to forget\n",
600 : field);
601 0 : return GNUNET_SYSERR;
602 : }
603 4 : fg = json_object_get (json,
604 : "$forgettable");
605 4 : if (NULL == fg)
606 : {
607 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
608 : "Did not find '$forgettable' attribute trying to forget field `%s'\n",
609 : field);
610 0 : return GNUNET_SYSERR;
611 : }
612 4 : rx = json_object_get (json,
613 : "$forgotten");
614 4 : if (NULL == rx)
615 : {
616 3 : rx = json_object ();
617 3 : if (0 !=
618 3 : json_object_set_new (json,
619 : "$forgotten",
620 : rx))
621 : {
622 0 : GNUNET_break (0);
623 0 : return GNUNET_SYSERR;
624 : }
625 : }
626 4 : if (NULL !=
627 4 : json_object_get (rx,
628 : field))
629 : {
630 0 : if (! json_is_null (json_object_get (json,
631 : field)))
632 : {
633 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
634 : "Field `%s' market as forgotten, but still exists!\n",
635 : field);
636 0 : return GNUNET_SYSERR;
637 : }
638 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
639 : "Already forgot field `%s'\n",
640 : field);
641 0 : return GNUNET_NO;
642 : }
643 4 : salt = json_string_value (json_object_get (fg,
644 : field));
645 4 : if (NULL == salt)
646 : {
647 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
648 : "Did not find required salt to forget field `%s'\n",
649 : field);
650 0 : return GNUNET_SYSERR;
651 : }
652 :
653 : /* need to recursively forget to compute 'hc' */
654 4 : ret = forget (part,
655 : &fp);
656 4 : if (GNUNET_OK != ret)
657 0 : return ret;
658 4 : if (GNUNET_OK !=
659 4 : dump_and_hash (fp,
660 : salt,
661 : &hc))
662 : {
663 0 : json_decref (fp);
664 0 : GNUNET_break (0);
665 0 : return GNUNET_SYSERR;
666 : }
667 4 : json_decref (fp);
668 : /* drop salt */
669 4 : if (0 !=
670 4 : json_object_del (fg,
671 : field))
672 : {
673 0 : json_decref (fp);
674 0 : GNUNET_break (0);
675 0 : return GNUNET_SYSERR;
676 : }
677 :
678 : /* remember field as 'forgotten' */
679 4 : if (0 !=
680 4 : json_object_set_new (rx,
681 : field,
682 : GNUNET_JSON_from_data_auto (&hc)))
683 : {
684 0 : GNUNET_break (0);
685 0 : return GNUNET_SYSERR;
686 : }
687 : /* finally, set 'forgotten' field to null */
688 4 : if (0 !=
689 4 : json_object_del (json,
690 : field))
691 : {
692 0 : GNUNET_break (0);
693 0 : return GNUNET_SYSERR;
694 : }
695 4 : return GNUNET_OK;
696 : }
697 :
698 :
699 : /**
700 : * Look over all of the values of a '$forgettable' object. Replace 'True'
701 : * values with proper random salts. Fails if any forgettable values are
702 : * neither 'True' nor valid salts (strings).
703 : *
704 : * @param[in,out] f JSON to transform
705 : * @return #GNUNET_OK on success
706 : */
707 : static enum GNUNET_GenericReturnValue
708 1 : seed_forgettable (json_t *f)
709 : {
710 : const char *key;
711 : json_t *val;
712 :
713 2 : json_object_foreach (f,
714 : key,
715 : val)
716 : {
717 1 : if (json_is_string (val))
718 0 : continue;
719 1 : if (json_is_true (val))
720 : {
721 : struct GNUNET_ShortHashCode sh;
722 :
723 1 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
724 : &sh,
725 : sizeof (sh));
726 1 : if (0 !=
727 1 : json_object_set_new (f,
728 : key,
729 : GNUNET_JSON_from_data_auto (&sh)))
730 : {
731 0 : GNUNET_break (0);
732 0 : return GNUNET_SYSERR;
733 : }
734 1 : continue;
735 : }
736 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
737 : "Forgettable field `%s' has invalid value\n",
738 : key);
739 0 : return GNUNET_SYSERR;
740 : }
741 1 : return GNUNET_OK;
742 : }
743 :
744 :
745 : /**
746 : * Take a given contract with "forgettable" fields marked
747 : * but with 'True' instead of a real salt. Replaces all
748 : * 'True' values with proper random salts. Fails if any
749 : * forgettable markers are neither 'True' nor valid salts.
750 : *
751 : * @param[in,out] json JSON to transform
752 : * @return #GNUNET_OK on success
753 : */
754 : enum GNUNET_GenericReturnValue
755 4 : TALER_JSON_contract_seed_forgettable (json_t *json)
756 : {
757 4 : if (json_is_object (json))
758 : {
759 : const char *key;
760 : json_t *val;
761 :
762 6 : json_object_foreach (json,
763 : key,
764 : val)
765 : {
766 4 : if (0 == strcmp ("$forgettable",
767 : key))
768 : {
769 1 : if (GNUNET_OK !=
770 1 : seed_forgettable (val))
771 0 : return GNUNET_SYSERR;
772 1 : continue;
773 : }
774 3 : if (GNUNET_OK !=
775 3 : TALER_JSON_contract_seed_forgettable (val))
776 0 : return GNUNET_SYSERR;
777 : }
778 : }
779 4 : if (json_is_array (json))
780 : {
781 : size_t index;
782 : json_t *val;
783 :
784 0 : json_array_foreach (json,
785 : index,
786 : val)
787 : {
788 0 : if (GNUNET_OK !=
789 0 : TALER_JSON_contract_seed_forgettable (val))
790 0 : return GNUNET_SYSERR;
791 : }
792 : }
793 4 : return GNUNET_OK;
794 : }
795 :
796 :
797 : /**
798 : * Parse a json path.
799 : *
800 : * @param obj the object that the path is relative to.
801 : * @param prev the parent of @e obj.
802 : * @param path the path to parse.
803 : * @param cb the callback to call, if we get to the end of @e path.
804 : * @param cb_cls the closure for the callback.
805 : * @return #GNUNET_OK on success, #GNUNET_SYSERR if @e path is malformed.
806 : */
807 : static enum GNUNET_GenericReturnValue
808 16 : parse_path (json_t *obj,
809 : json_t *prev,
810 : const char *path,
811 : TALER_JSON_ExpandPathCallback cb,
812 : void *cb_cls)
813 : {
814 16 : char *id = GNUNET_strdup (path);
815 16 : char *next_id = strchr (id,
816 : '.');
817 : char *next_path;
818 : char *bracket;
819 16 : json_t *next_obj = NULL;
820 : char *next_dot;
821 :
822 16 : if (NULL == next_id)
823 : {
824 5 : cb (cb_cls,
825 : id,
826 : prev);
827 5 : GNUNET_free (id);
828 5 : return GNUNET_OK;
829 : }
830 11 : bracket = strchr (next_id,
831 : '[');
832 11 : *next_id = '\0';
833 11 : next_id++;
834 11 : next_path = GNUNET_strdup (next_id);
835 11 : next_dot = strchr (next_id,
836 : '.');
837 11 : if (NULL != next_dot)
838 3 : *next_dot = '\0';
839 : /* If this is the first time this is called, make sure id is "$" */
840 11 : if ( (NULL == prev) &&
841 6 : (0 != strcmp (id,
842 : "$")))
843 : {
844 1 : GNUNET_free (id);
845 1 : GNUNET_free (next_path);
846 1 : return GNUNET_SYSERR;
847 : }
848 :
849 : /* Check for bracketed indices */
850 10 : if (NULL != bracket)
851 : {
852 3 : char *end_bracket = strchr (bracket,
853 : ']');
854 3 : if (NULL == end_bracket)
855 : {
856 0 : GNUNET_free (id);
857 0 : GNUNET_free (next_path);
858 0 : return GNUNET_SYSERR;
859 : }
860 3 : *end_bracket = '\0';
861 :
862 3 : *bracket = '\0';
863 3 : bracket++;
864 :
865 3 : json_t *array = json_object_get (obj,
866 : next_id);
867 3 : if (0 == strcmp (bracket,
868 : "*"))
869 : {
870 : size_t index;
871 : json_t *value;
872 1 : int ret = GNUNET_OK;
873 :
874 4 : json_array_foreach (array, index, value) {
875 3 : ret = parse_path (value,
876 : obj,
877 : next_path,
878 : cb,
879 : cb_cls);
880 3 : if (GNUNET_OK != ret)
881 : {
882 0 : GNUNET_free (id);
883 0 : GNUNET_free (next_path);
884 0 : return ret;
885 : }
886 : }
887 : }
888 : else
889 : {
890 : unsigned int index;
891 : char dummy;
892 :
893 2 : if (1 != sscanf (bracket,
894 : "%u%c",
895 : &index,
896 : &dummy))
897 : {
898 1 : GNUNET_free (id);
899 1 : GNUNET_free (next_path);
900 1 : return GNUNET_SYSERR;
901 : }
902 1 : next_obj = json_array_get (array,
903 : index);
904 : }
905 : }
906 : else
907 : {
908 : /* No brackets, so just fetch the object by name */
909 7 : next_obj = json_object_get (obj,
910 : next_id);
911 : }
912 :
913 9 : if (NULL != next_obj)
914 : {
915 7 : int ret = parse_path (next_obj,
916 : obj,
917 : next_path,
918 : cb,
919 : cb_cls);
920 7 : GNUNET_free (id);
921 7 : GNUNET_free (next_path);
922 7 : return ret;
923 : }
924 2 : GNUNET_free (id);
925 2 : GNUNET_free (next_path);
926 2 : return GNUNET_OK;
927 : }
928 :
929 :
930 : enum GNUNET_GenericReturnValue
931 6 : TALER_JSON_expand_path (json_t *json,
932 : const char *path,
933 : TALER_JSON_ExpandPathCallback cb,
934 : void *cb_cls)
935 : {
936 6 : return parse_path (json,
937 : NULL,
938 : path,
939 : cb,
940 : cb_cls);
941 : }
942 :
943 :
944 : enum TALER_ErrorCode
945 2 : TALER_JSON_get_error_code (const json_t *json)
946 : {
947 : const json_t *jc;
948 :
949 2 : if (NULL == json)
950 0 : return TALER_EC_GENERIC_INVALID_RESPONSE;
951 2 : jc = json_object_get (json, "code");
952 : /* The caller already knows that the JSON represents an error,
953 : so we are dealing with a missing error code here. */
954 2 : if (NULL == jc)
955 : {
956 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
957 : "Expected Taler error code `code' in JSON, but field does not exist!\n");
958 0 : return TALER_EC_INVALID;
959 : }
960 2 : if (json_is_integer (jc))
961 2 : return (enum TALER_ErrorCode) json_integer_value (jc);
962 0 : GNUNET_break_op (0);
963 0 : return TALER_EC_INVALID;
964 : }
965 :
966 :
967 : const char *
968 0 : TALER_JSON_get_error_hint (const json_t *json)
969 : {
970 : const json_t *jc;
971 :
972 0 : if (NULL == json)
973 0 : return NULL;
974 0 : jc = json_object_get (json,
975 : "hint");
976 0 : if (NULL == jc)
977 0 : return NULL; /* no hint, is allowed */
978 0 : if (! json_is_string (jc))
979 : {
980 : /* Hints must be strings */
981 0 : GNUNET_break_op (0);
982 0 : return NULL;
983 : }
984 0 : return json_string_value (jc);
985 : }
986 :
987 :
988 : enum TALER_ErrorCode
989 0 : TALER_JSON_get_error_code2 (const void *data,
990 : size_t data_size)
991 : {
992 : json_t *json;
993 : enum TALER_ErrorCode ec;
994 : json_error_t err;
995 :
996 0 : json = json_loadb (data,
997 : data_size,
998 : JSON_REJECT_DUPLICATES,
999 : &err);
1000 0 : if (NULL == json)
1001 0 : return TALER_EC_INVALID;
1002 0 : ec = TALER_JSON_get_error_code (json);
1003 0 : json_decref (json);
1004 0 : if (ec == TALER_EC_NONE)
1005 0 : return TALER_EC_INVALID;
1006 0 : return ec;
1007 : }
1008 :
1009 :
1010 : void
1011 0 : TALER_deposit_extension_hash (const json_t *extensions,
1012 : struct TALER_ExtensionContractHashP *ech)
1013 : {
1014 0 : GNUNET_assert (GNUNET_OK ==
1015 : dump_and_hash (extensions,
1016 : "taler-contract-extensions",
1017 : &ech->hash));
1018 0 : }
1019 :
1020 :
1021 : char *
1022 3 : TALER_JSON_canonicalize (const json_t *input)
1023 : {
1024 : char *wire_enc;
1025 :
1026 3 : if (NULL == (wire_enc = json_dumps (input,
1027 : JSON_ENCODE_ANY
1028 : | JSON_COMPACT
1029 : | JSON_SORT_KEYS)))
1030 : {
1031 0 : GNUNET_break (0);
1032 0 : return NULL;
1033 : }
1034 3 : rfc8785encode (&wire_enc);
1035 3 : return wire_enc;
1036 : }
1037 :
1038 :
1039 : enum GNUNET_GenericReturnValue
1040 0 : TALER_JSON_extensions_config_hash (const json_t *config,
1041 : struct TALER_ExtensionConfigHashP *ech)
1042 : {
1043 0 : return dump_and_hash (config,
1044 : "taler-extension-configuration",
1045 : &ech->hash);
1046 : }
1047 :
1048 :
1049 : /* End of json/json.c */
|