- Refactor jsonapi into separate module
[oweals/gnunet.git] / src / jsonapi / jsonapi.c
1 /*
2   This file is part of GNUnet
3   Copyright (C) 2014, 2015, 2016 GNUnet e.V.
4
5   GNUnet 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   GNUnet 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   GNUnet; see the file COPYING.  If not, If not, see <http://www.gnu.org/licenses/>
15 */
16 /**
17  * @file json/jsonapi.c
18  * @brief functions to generate specifciations for JSONAPI parsing
19  * @author Martin Schanzenbach
20  */
21 #include "platform.h"
22 #include "gnunet_json_lib.h"
23
24 #define GNUNET_JSONAPI_JSONAPI_KEY_DATA "data"
25
26 #define GNUNET_JSONAPI_JSONAPI_KEY_ID "id"
27
28 #define GNUNET_JSONAPI_JSONAPI_KEY_TYPE "type"
29
30 struct GNUNET_JSONAPI_Resource
31 {
32   /**
33    * DLL
34    */
35   struct GNUNET_JSONAPI_Resource *next;
36
37   /**
38    * DLL
39    */
40   struct GNUNET_JSONAPI_Resource *prev;
41
42   /**
43    * Resource content
44    */
45   json_t *res_obj;
46 };
47
48
49 struct GNUNET_JSONAPI_Object
50 {
51   /**
52    * DLL Resource
53    */
54   struct GNUNET_JSONAPI_Resource *res_list_head;
55
56   /**
57    * DLL Resource
58    */
59   struct GNUNET_JSONAPI_Resource *res_list_tail;
60
61   /**
62    * num resources
63    */
64   int res_count;
65 };
66
67
68 /**
69  * JSON API
70  */
71
72
73 /**
74  * Create a JSON API resource
75  *
76  * @param type the JSON API resource type
77  * @param id the JSON API resource id
78  * @return a new JSON API resource or NULL on error.
79  */
80 struct GNUNET_JSONAPI_Resource*
81 GNUNET_JSONAPI_resource_new (const char *type, const char *id)
82 {
83   struct GNUNET_JSONAPI_Resource *res;
84
85   if ( (NULL == type) || (0 == strlen (type)) )
86     return NULL;
87   if ( (NULL == id) || (0 == strlen (id)) )
88     return NULL;
89
90   res = GNUNET_new (struct GNUNET_JSONAPI_Resource);
91   res->prev = NULL;
92   res->next = NULL;
93
94   res->res_obj = json_object ();
95
96   json_object_set_new (res->res_obj, GNUNET_JSONAPI_JSONAPI_KEY_ID, json_string (id));
97   json_object_set_new (res->res_obj, GNUNET_JSONAPI_JSONAPI_KEY_TYPE, json_string (type));
98
99   return res;
100 }
101
102
103
104 /**
105  * Add a JSON API attribute
106  *
107  * @param res the JSON resource
108  * @param key the key for the attribute
109  * @param json the json_t attribute to add
110  * @return #GNUNET_OK if added successfully
111  *         #GNUNET_SYSERR if not
112  */
113 int
114 GNUNET_JSONAPI_resource_add_attr (const struct GNUNET_JSONAPI_Resource *resource,
115                                        const char* key,
116                                        json_t *json)
117 {
118   if ( (NULL == resource) ||
119        (NULL == key) ||
120        (NULL == json) )
121     return GNUNET_SYSERR;
122   json_object_set (resource->res_obj, key, json);
123   return GNUNET_OK;
124 }
125
126 /**
127  * Read a JSON API attribute
128  *
129  * @param res the JSON resource
130  * @param key the key for the attribute
131  * @return the json_t object
132  */
133 json_t*
134 GNUNET_JSONAPI_resource_read_attr (const struct GNUNET_JSONAPI_Resource *resource,
135                                        const char* key)
136 {
137   if ( (NULL == resource) ||
138        (NULL == key))
139     return NULL;
140   return json_object_get (resource->res_obj, key);
141 }
142
143 int
144 check_resource_attr_str (const struct GNUNET_JSONAPI_Resource *resource,
145                          const char* key,
146                          const char* attr)
147 {
148   json_t *value;
149   if ( (NULL == resource) ||
150        (NULL == key) ||
151        (NULL == attr))
152     return GNUNET_NO;
153   value = json_object_get (resource->res_obj, key);
154   if (NULL == value)
155     return GNUNET_NO;
156   if (!json_is_string (value) ||
157       (0 != strcmp (attr, json_string_value(value))))
158   {
159     return GNUNET_NO;
160   }
161   return GNUNET_YES;
162 }
163
164 /**
165  * Check a JSON API resource id
166  *
167  * @param res the JSON resource
168  * @param id the expected id
169  * @return GNUNET_YES if id matches
170  */
171 int
172 GNUNET_JSONAPI_resource_check_id (const struct GNUNET_JSONAPI_Resource *resource,
173                                        const char* id)
174 {
175   return check_resource_attr_str (resource, GNUNET_JSONAPI_JSONAPI_KEY_ID, id);
176 }
177
178 /**
179  * Check a JSON API resource id
180  *
181  * @param res the JSON resource
182  * @return the resource id
183  */
184 json_t*
185 GNUNET_JSONAPI_resource_get_id (const struct GNUNET_JSONAPI_Resource *resource)
186 {
187   return GNUNET_JSONAPI_resource_read_attr (resource, GNUNET_JSONAPI_JSONAPI_KEY_ID);
188 }
189
190 /**
191  * Check a JSON API resource type
192  *
193  * @param res the JSON resource
194  * @param type the expected type
195  * @return GNUNET_YES if id matches
196  */
197 int
198 GNUNET_JSONAPI_resource_check_type (const struct GNUNET_JSONAPI_Resource *resource,
199                                          const char* type)
200 {
201   return check_resource_attr_str (resource, GNUNET_JSONAPI_JSONAPI_KEY_TYPE, type);
202 }
203
204 /**
205  * Get a JSON API object resource count
206  *
207  * @param resp the JSON API object
208  * @return the number of resources
209  */
210 int
211 GNUNET_JSONAPI_object_resource_count (struct GNUNET_JSONAPI_Object *resp)
212 {
213   return resp->res_count;
214 }
215
216 /**
217  * Get a JSON API object resource by index
218  *
219  * @param resp the JSON API object
220  * @param num the number of the resource
221  * @return the resource
222  */
223 struct GNUNET_JSONAPI_Resource*
224 GNUNET_JSONAPI_object_get_resource (struct GNUNET_JSONAPI_Object *resp,
225                                          int num)
226 {
227   struct GNUNET_JSONAPI_Resource *res;
228   int i;
229
230   if ((0 == resp->res_count) ||
231       (num >= resp->res_count))
232     return NULL;
233   res = resp->res_list_head;
234   for (i = 0; i < num; i++)
235   {
236     res = res->next;
237   }
238   return res;
239 }
240
241 /**
242  * Delete a JSON API resource
243  *
244  * @param res the JSON resource
245  * @param result Pointer where the resource should be stored
246  */
247 void
248 GNUNET_JSONAPI_resource_delete (struct GNUNET_JSONAPI_Resource *resource)
249 {
250   json_decref (resource->res_obj);
251   GNUNET_free (resource);
252   resource = NULL;
253 }
254
255 /**
256  * Delete a JSON API primary data
257  *
258  * @param type the JSON API resource type
259  * @param id the JSON API resource id
260  * @return a new JSON API resource or NULL on error.
261  */
262 void
263 GNUNET_JSONAPI_object_delete (struct GNUNET_JSONAPI_Object *resp)
264 {
265   struct GNUNET_JSONAPI_Resource *res;
266   struct GNUNET_JSONAPI_Resource *res_next;
267
268   for (res = resp->res_list_head;
269        res != NULL;)
270   {
271     res_next = res->next;
272     GNUNET_CONTAINER_DLL_remove (resp->res_list_head,
273                                  resp->res_list_tail,
274                                  res);
275     GNUNET_JSONAPI_resource_delete (res);
276     res = res_next;
277   }
278   GNUNET_free (resp);
279   resp = NULL;
280 }
281
282 /**
283  * Create a JSON API primary data
284  *
285  * @return a new JSON API resource or NULL on error.
286  */
287 struct GNUNET_JSONAPI_Object*
288 GNUNET_JSONAPI_jsonapi_object_new ()
289 {
290   struct GNUNET_JSONAPI_Object *result;
291
292   result = GNUNET_new (struct GNUNET_JSONAPI_Object);
293   result->res_count = 0;
294   return result;
295 }
296
297 /**
298  * Add a JSON API object to primary data
299  *
300  * @param data The JSON API data to add to
301  * @param res the JSON API resource to add
302  * @return the new number of resources
303  */
304 void
305 GNUNET_JSONAPI_object_resource_add (struct GNUNET_JSONAPI_Object *resp,
306                                          struct GNUNET_JSONAPI_Resource *res)
307 {
308   GNUNET_CONTAINER_DLL_insert (resp->res_list_head,
309                                resp->res_list_tail,
310                                res);
311
312   resp->res_count++;
313 }
314
315 static void
316 add_json_resource (struct GNUNET_JSONAPI_Object *obj,
317                    const json_t *res_json)
318 {
319   struct GNUNET_JSONAPI_Resource *res;
320   const char *type_json;
321
322   struct GNUNET_JSON_Specification dspec[] = {
323     GNUNET_JSON_spec_string (GNUNET_JSONAPI_JSONAPI_KEY_TYPE, &type_json),
324     GNUNET_JSON_spec_end()
325   };
326
327   GNUNET_assert (GNUNET_OK ==
328                  GNUNET_JSON_parse (res_json, dspec,
329                                     NULL, NULL));
330   GNUNET_JSON_parse_free (dspec);
331   res = GNUNET_new (struct GNUNET_JSONAPI_Resource);
332   res->next = NULL;
333   res->prev = NULL;
334   res->res_obj = json_deep_copy (res_json);
335   GNUNET_JSONAPI_object_resource_add (obj, res);
336 }
337
338 /**
339  * Parse given JSON object to RSA public key.
340  *
341  * @param cls closure, NULL
342  * @param root the json object representing data
343  * @param[out] spec where to write the data
344  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
345  */
346 static int
347 parse_jsonapiobject (void *cls,
348                      json_t *root,
349                      struct GNUNET_JSON_Specification *spec)
350 {
351   struct GNUNET_JSONAPI_Object *result;
352   json_t *data_json;
353   int res_count = 0;
354   int i;
355
356   struct GNUNET_JSON_Specification jsonapispec[] = {
357     GNUNET_JSON_spec_json (GNUNET_JSONAPI_JSONAPI_KEY_DATA, &data_json),
358     GNUNET_JSON_spec_end()
359   };
360   if (GNUNET_OK !=
361                  GNUNET_JSON_parse (root, jsonapispec,
362                                     NULL, NULL) || (NULL == data_json))
363   {
364     return GNUNET_SYSERR;
365   }
366
367   result = GNUNET_new (struct GNUNET_JSONAPI_Object);
368   result->res_count = 0;
369   if (json_is_object (data_json))
370     add_json_resource (result, data_json);
371   else if (json_is_array (data_json))
372   {
373     res_count = json_array_size (data_json);
374     for (i = 0; i < res_count; i++)
375       add_json_resource (result, json_array_get (data_json, i));
376   }
377   if (0 == result->res_count)
378   {
379     GNUNET_free (result);
380     GNUNET_JSON_parse_free (jsonapispec);
381     return GNUNET_SYSERR;
382   }
383   *(struct GNUNET_JSONAPI_Object **) spec->ptr = result;
384   GNUNET_JSON_parse_free (jsonapispec);
385   return GNUNET_OK;
386 }
387
388
389 /**
390  * Cleanup data left from parsing RSA public key.
391  *
392  * @param cls closure, NULL
393  * @param[out] spec where to free the data
394  */
395 static void
396 clean_jsonapiobject (void *cls,
397                      struct GNUNET_JSON_Specification *spec)
398 {
399   struct GNUNET_JSONAPI_Object **jsonapi_obj;
400   jsonapi_obj = (struct GNUNET_JSONAPI_Object **) spec->ptr;
401   if (NULL != *jsonapi_obj)
402   {
403     GNUNET_JSONAPI_object_delete (*jsonapi_obj);
404     *jsonapi_obj = NULL;
405   }
406 }
407
408 /**
409  * Add a JSON API resource to primary data
410  *
411  * @param data The JSON API data to add to
412  * @param res the JSON API resource to add
413  * @return the new number of resources
414  */
415 void
416 GNUNET_JSONAPI_data_resource_remove (struct GNUNET_JSONAPI_Object *resp,
417                                           struct GNUNET_JSONAPI_Resource *res)
418 {
419   GNUNET_CONTAINER_DLL_remove (resp->res_list_head,
420                                resp->res_list_tail,
421                               res);
422   resp->res_count--;
423 }
424
425 /**
426  * String serialze jsonapi primary data
427  *
428  * @param data the JSON API primary data
429  * @param result where to store the result
430  * @return GNUNET_SYSERR on error else GNUNET_OK
431  */
432 int
433 GNUNET_JSONAPI_data_serialize (const struct GNUNET_JSONAPI_Object *resp,
434                                     char **result)
435 {
436   struct GNUNET_JSONAPI_Resource *res;
437   json_t *root_json;
438   json_t *res_arr;
439
440   if ((NULL == resp))
441     return GNUNET_SYSERR;
442
443   root_json = json_object ();
444   res_arr = json_array ();
445   for (res = resp->res_list_head;
446        res != NULL;
447        res = res->next)
448   {
449     json_array_append (res_arr, res->res_obj);
450   }
451   json_object_set (root_json, GNUNET_JSONAPI_JSONAPI_KEY_DATA, res_arr);
452   *result = json_dumps (root_json, JSON_INDENT(2));
453   json_decref (root_json);
454   json_decref (res_arr);
455   return GNUNET_OK;
456 }
457
458 /**
459  * JSON object.
460  *
461  * @param name name of the JSON field
462  * @param[out] jsonp where to store the JSON found under @a name
463  */
464 struct GNUNET_JSON_Specification
465 GNUNET_JSON_spec_jsonapi (struct GNUNET_JSONAPI_Object **jsonapi_object)
466 {
467   struct GNUNET_JSON_Specification ret = {
468     .parser = &parse_jsonapiobject,
469     .cleaner = &clean_jsonapiobject,
470     .cls = NULL,
471     .field = NULL,
472     .ptr = jsonapi_object,
473     .ptr_size = 0,
474     .size_ptr = NULL
475   };
476   *jsonapi_object = NULL;
477   return ret;
478 }