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