Fix for #4553
[oweals/gnunet.git] / src / jsonapi / jsonapi_document.c
1
2 #include "platform.h"
3 #include "gnunet_util_lib.h"
4 #include "gnunet_json_lib.h"
5 #include "jsonapi_objects.h"
6
7 /**
8  * Get a JSON API object resource count
9  *
10  * @param resp the JSON API object
11  * @return the number of resources
12  */
13 int
14 GNUNET_JSONAPI_document_resource_count (struct GNUNET_JSONAPI_Document *doc)
15 {
16   return doc->res_count;
17 }
18
19 /**
20  * Get a JSON API object resource by index
21  *
22  * @param resp the JSON API object
23  * @param idx index of the resource
24  * @return the resource
25  */
26 struct GNUNET_JSONAPI_Resource*
27 GNUNET_JSONAPI_document_get_resource (struct GNUNET_JSONAPI_Document *doc,
28                                       int idx)
29 {
30   struct GNUNET_JSONAPI_Resource *res;
31   int i;
32
33   if ((0 == doc->res_count) ||
34       (idx >= doc->res_count))
35     return NULL;
36   res = doc->res_list_head;
37   for (i = 0; i < idx; i++)
38   {
39     res = res->next;
40   }
41   return res;
42 }
43
44 /**
45  * Delete a JSON API primary data
46  *
47  * @param type the JSON API resource type
48  * @param id the JSON API resource id
49  * @return a new JSON API resource or NULL on error.
50  */
51 void
52 GNUNET_JSONAPI_document_delete (struct GNUNET_JSONAPI_Document *doc)
53 {
54   struct GNUNET_JSONAPI_Resource *res;
55   struct GNUNET_JSONAPI_Resource *res_next;
56   struct GNUNET_JSONAPI_Error *err;
57   struct GNUNET_JSONAPI_Error *err_next;
58
59
60   for (err = doc->err_list_head;
61        err != NULL;)
62   {
63     err_next = err->next;
64     GNUNET_CONTAINER_DLL_remove (doc->err_list_head,
65                                  doc->err_list_tail,
66                                  err);
67     GNUNET_JSONAPI_error_delete (err);
68     err = err_next;
69   }
70
71   for (res = doc->res_list_head;
72        res != NULL;)
73   {
74     res_next = res->next;
75     GNUNET_CONTAINER_DLL_remove (doc->res_list_head,
76                                  doc->res_list_tail,
77                                  res);
78     GNUNET_JSONAPI_resource_delete (res);
79     res = res_next;
80   }
81
82   if (NULL != doc->meta)
83     json_decref (doc->meta);
84   GNUNET_free (doc);
85   doc = NULL;
86 }
87
88 /**
89  * Create a JSON API primary data
90  *
91  * @return a new JSON API resource or NULL on error.
92  */
93 struct GNUNET_JSONAPI_Document*
94 GNUNET_JSONAPI_document_new ()
95 {
96   struct GNUNET_JSONAPI_Document *result;
97
98   result = GNUNET_new (struct GNUNET_JSONAPI_Document);
99   result->res_count = 0;
100   result->err_count = 0;
101   result->meta = 0;
102   return result;
103 }
104
105 /**
106  * Add a JSON API error to document
107  *
108  * @param data The JSON API document to add to
109  * @param res the JSON API error to add
110  * @return the new number of resources
111  */
112 void
113 GNUNET_JSONAPI_document_error_add (struct GNUNET_JSONAPI_Document *doc,
114                                    struct GNUNET_JSONAPI_Error *err)
115 {
116   GNUNET_CONTAINER_DLL_insert (doc->err_list_head,
117                                doc->err_list_tail,
118                                err);
119
120   doc->err_count++;
121 }
122
123 /**
124  * Add a JSON API resource to primary data
125  *
126  * @param data The JSON API data to add to
127  * @param res the JSON API resource to add
128  * @return the new number of resources
129  */
130 void
131 GNUNET_JSONAPI_document_resource_add (struct GNUNET_JSONAPI_Document *doc,
132                                       struct GNUNET_JSONAPI_Resource *res)
133 {
134   GNUNET_CONTAINER_DLL_insert (doc->res_list_head,
135                                doc->res_list_tail,
136                                res);
137
138   doc->res_count++;
139 }
140
141
142 /**
143  * Parse given JSON object to jsonapi document.
144  *
145  * @param cls closure, NULL
146  * @param root the json object representing data
147  * @param[out] spec where to write the data
148  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
149  */
150 static int
151 parse_jsonapiobject (void *cls,
152                      json_t *root,
153                      struct GNUNET_JSON_Specification *spec)
154 {
155   struct GNUNET_JSONAPI_Document *result;
156   struct GNUNET_JSONAPI_Error *error;
157   struct GNUNET_JSONAPI_Resource *resource;
158   json_t *meta_json;
159   json_t *resource_json;
160   json_t *errors_json;
161   json_t *value;
162   size_t index;
163
164   struct GNUNET_JSON_Specification jsonapispecerrors[] = {
165     GNUNET_JSON_spec_json (GNUNET_JSONAPI_KEY_ERRORS, &errors_json),
166     GNUNET_JSON_spec_end()
167   };
168   if (GNUNET_OK !=
169       GNUNET_JSON_parse (root, jsonapispecerrors,
170                          NULL, NULL))
171   {
172     GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
173                 "JSONAPI document does not contain error objects\n");
174   } else if (!json_is_array (errors_json))
175   {
176     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
177                 "Error object is not array!\n");
178     GNUNET_JSON_parse_free (jsonapispecerrors);
179     return GNUNET_SYSERR;
180   }
181   struct GNUNET_JSON_Specification jsonapispecmeta[] = {
182     GNUNET_JSON_spec_json (GNUNET_JSONAPI_KEY_META, &meta_json),
183     GNUNET_JSON_spec_end()
184   };
185   if (GNUNET_OK !=
186       GNUNET_JSON_parse (root, jsonapispecmeta,
187                          NULL, NULL))
188   {
189     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
190                 "JSONAPI document does not contain error objects\n");
191   }
192   struct GNUNET_JSON_Specification jsonapispecresource[] = {
193     GNUNET_JSON_spec_json (GNUNET_JSONAPI_KEY_DATA, &resource_json),
194     GNUNET_JSON_spec_end()
195   };
196   if (GNUNET_OK !=
197       GNUNET_JSON_parse (root, jsonapispecresource,
198                          NULL, NULL))
199   {
200     if (NULL == errors_json)
201     {
202       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
203                   "JSONAPI document contains neither error nor data!\n");
204       GNUNET_JSON_parse_free (jsonapispecerrors);
205       GNUNET_JSON_parse_free (jsonapispecmeta);
206       return GNUNET_SYSERR;
207     }
208   } else {
209     if (NULL != errors_json)
210     {
211       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
212                   "JSONAPI document contains both error and data!\n");
213       GNUNET_JSON_parse_free (jsonapispecerrors);
214       GNUNET_JSON_parse_free (jsonapispecmeta);
215       GNUNET_JSON_parse_free (jsonapispecresource);
216       return GNUNET_SYSERR;
217     }
218   }
219
220   result = GNUNET_new (struct GNUNET_JSONAPI_Document);
221   result->res_count = 0;
222   result->err_count = 0;
223   if (NULL != meta_json)
224     result->meta = json_deep_copy (meta_json);
225   if (NULL != errors_json) {
226     json_array_foreach(errors_json, index, value) {
227       GNUNET_assert (GNUNET_OK == 
228                      GNUNET_JSONAPI_json_to_error (value,
229                                                    &error));
230       GNUNET_JSONAPI_document_error_add (result, error);
231     }
232   }
233   if (NULL != resource_json) {
234     if (0 != json_is_array (resource_json))
235     {
236       json_array_foreach(resource_json, index, value) {
237         GNUNET_assert (GNUNET_OK == 
238                        GNUNET_JSONAPI_json_to_resource (value,
239                                                         &resource));
240         GNUNET_JSONAPI_document_resource_add (result, resource);
241       }
242     } else {
243       GNUNET_assert (GNUNET_OK == 
244                      GNUNET_JSONAPI_json_to_resource (resource_json,
245                                                       &resource));
246       GNUNET_JSONAPI_document_resource_add (result, resource);
247     }
248   }
249   if (NULL != errors_json)
250     GNUNET_JSON_parse_free (jsonapispecerrors);
251   if (NULL != resource)
252     GNUNET_JSON_parse_free (jsonapispecresource);
253   if (NULL != meta_json)
254     GNUNET_JSON_parse_free (jsonapispecmeta);
255   *(struct GNUNET_JSONAPI_Document **) spec->ptr = result;
256   return GNUNET_OK;
257 }
258
259
260 /**
261  * Cleanup data left from parsing RSA public key.
262  *
263  * @param cls closure, NULL
264  * @param[out] spec where to free the data
265  */
266 static void
267 clean_jsonapiobject (void *cls,
268                      struct GNUNET_JSON_Specification *spec)
269 {
270   struct GNUNET_JSONAPI_Document **jsonapi_obj;
271   jsonapi_obj = (struct GNUNET_JSONAPI_Document **) spec->ptr;
272   if (NULL != *jsonapi_obj)
273   {
274     GNUNET_JSONAPI_document_delete (*jsonapi_obj);
275     *jsonapi_obj = NULL;
276   }
277 }
278
279 /**
280  * Add a JSON API resource to primary data
281  *
282  * @param data The JSON API data to add to
283  * @param res the JSON API resource to add
284  * @return the new number of resources
285  */
286 void
287 GNUNET_JSONAPI_document_resource_remove (struct GNUNET_JSONAPI_Document *resp,
288                                          struct GNUNET_JSONAPI_Resource *res)
289 {
290   GNUNET_CONTAINER_DLL_remove (resp->res_list_head,
291                                resp->res_list_tail,
292                                res);
293   resp->res_count--;
294 }
295
296
297 /**
298  * String serialze jsonapi primary data
299  *
300  * @param data the JSON API primary data
301  * @param result where to store the result
302  * @return GNUNET_SYSERR on error else GNUNET_OK
303  */
304 int
305 GNUNET_JSONAPI_document_to_json (const struct GNUNET_JSONAPI_Document *doc,
306                                  json_t **root_json)
307 {
308   struct GNUNET_JSONAPI_Resource *res;
309   struct GNUNET_JSONAPI_Error *error;
310   json_t *res_json;
311   json_t *res_json_tmp;
312
313   if ((NULL == doc))
314     return GNUNET_SYSERR;
315
316   *root_json = json_object ();
317
318   //Check for errors first 
319   if (doc->err_count != 0)
320   {
321     res_json = json_array ();
322     for (error = doc->err_list_head;
323          error != NULL;
324          error = error->next)
325     {
326       GNUNET_assert (GNUNET_OK ==
327                      GNUNET_JSONAPI_error_to_json (error,
328                                                    &res_json_tmp));
329       json_array_append_new (res_json, res_json_tmp);
330     }
331     json_object_set_new (*root_json,
332                          GNUNET_JSONAPI_KEY_ERRORS,
333                          res_json);
334   } else {
335     switch (doc->res_count)
336     {
337       case 0:
338         res_json = json_null();
339         break;
340       case 1:
341         GNUNET_assert (GNUNET_OK ==
342                        GNUNET_JSONAPI_resource_to_json (doc->res_list_head,
343                                                         &res_json));
344         break;
345       default:
346         res_json = json_array ();
347         for (res = doc->res_list_head;
348              res != NULL;
349              res = res->next)
350         {
351           GNUNET_assert (GNUNET_OK ==
352                          GNUNET_JSONAPI_resource_to_json (res,
353                                                           &res_json_tmp));
354           json_array_append (res_json, res_json_tmp);
355         }
356         break;
357     }
358     json_object_set_new (*root_json,
359                          GNUNET_JSONAPI_KEY_DATA,
360                          res_json);
361   }
362   json_object_set (*root_json,
363                    GNUNET_JSONAPI_KEY_META,
364                    doc->meta);
365   return GNUNET_OK;
366 }
367
368 /**
369  * String serialze jsonapi primary data
370  *
371  * @param data the JSON API primary data
372  * @param result where to store the result
373  * @return GNUNET_SYSERR on error else GNUNET_OK
374  */
375 int
376 GNUNET_JSONAPI_document_serialize (const struct GNUNET_JSONAPI_Document *doc,
377                                    char **result)
378 {
379   json_t *json_doc;
380   if (GNUNET_OK != GNUNET_JSONAPI_document_to_json (doc,
381                                                     &json_doc))
382     return GNUNET_SYSERR;
383
384   *result = json_dumps (json_doc, JSON_INDENT(2));
385   json_decref (json_doc);
386   return GNUNET_OK;
387 }
388
389 /**
390  * JSON object.
391  *
392  * @param name name of the JSON field
393  * @param[out] jsonp where to store the JSON found under @a name
394  */
395 struct GNUNET_JSON_Specification
396 GNUNET_JSON_spec_jsonapi_document (struct GNUNET_JSONAPI_Document **jsonapi_object)
397 {
398   struct GNUNET_JSON_Specification ret = {
399     .parser = &parse_jsonapiobject,
400     .cleaner = &clean_jsonapiobject,
401     .cls = NULL,
402     .field = NULL,
403     .ptr = jsonapi_object,
404     .ptr_size = 0,
405     .size_ptr = NULL
406   };
407   *jsonapi_object = NULL;
408   return ret;
409 }
410
411