- fix leaks, add token list
[oweals/gnunet.git] / src / rest / rest.c
1 /*
2       This file is part of GNUnet
3       Copyright (C) 2010-2015 Christian Grothoff (and other contributing authors)
4
5       GNUnet is free software; you can redistribute it and/or modify
6       it under the terms of the GNU General Public License as published
7       by the Free Software Foundation; either version 3, or (at your
8       option) any later version.
9
10       GNUnet is distributed in the hope that it will be useful, but
11       WITHOUT ANY WARRANTY; without even the implied warranty of
12       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13       General Public License for more details.
14
15       You should have received a copy of the GNU General Public License
16       along with GNUnet; see the file COPYING.  If not, write to the
17       Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18       Boston, MA 02110-1301, USA.
19  */
20
21 /**
22  * @file rest/rest.c
23  * @brief helper library to create JSON REST Objects and handle REST
24  * responses/requests.
25  * @author Martin Schanzenbach
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_rest_lib.h"
30 #include "microhttpd.h"
31 #include <jansson.h>
32
33
34 struct JsonApiResource
35 {
36   /**
37    * DLL
38    */
39   struct JsonApiResource *next;
40
41   /**
42    * DLL
43    */
44   struct JsonApiResource *prev;
45
46   /**
47    * Resource content
48    */
49   json_t *res_obj;
50 };
51
52
53 struct JsonApiObject
54 {
55   /**
56    * DLL Resource
57    */
58   struct JsonApiResource *res_list_head;
59
60   /**
61    * DLL Resource
62    */
63   struct JsonApiResource *res_list_tail;
64
65   /**
66    * num resources
67    */
68   int res_count;
69 };
70
71
72 /**
73  * JSON API
74  */
75
76 /**
77  * Create a JSON API resource
78  *
79  * @param type the JSON API resource type
80  * @param id the JSON API resource id
81  * @return a new JSON API resource or NULL on error.
82  */
83 struct JsonApiResource*
84 GNUNET_REST_jsonapi_resource_new (const char *type, const char *id)
85 {
86   struct JsonApiResource *res;
87
88   if ( (NULL == type) || (0 == strlen (type)) )
89     return NULL;
90   if ( (NULL == id) || (0 == strlen (id)) )
91     return NULL;
92
93   res = GNUNET_new (struct JsonApiResource);
94   res->prev = NULL;
95   res->next = NULL;
96   
97   res->res_obj = json_object ();
98
99   json_object_set_new (res->res_obj, GNUNET_REST_JSONAPI_KEY_ID, json_string (id));
100   json_object_set_new (res->res_obj, GNUNET_REST_JSONAPI_KEY_TYPE, json_string (type));
101
102   return res;
103 }
104
105 /**
106  * Delete a JSON API resource
107  *
108  * @param res the JSON resource
109  * @param result Pointer where the resource should be stored
110  */
111 void
112 GNUNET_REST_jsonapi_resource_delete (struct JsonApiResource *resource)
113 {
114   json_decref (resource->res_obj);
115   GNUNET_free (resource);
116 }
117
118 /**
119  * Add a JSON API attribute
120  *
121  * @param res the JSON resource
122  * @param key the key for the attribute
123  * @param json the json_t attribute to add
124  * @return #GNUNET_OK if added successfully
125  *         #GNUNET_SYSERR if not
126  */
127 int
128 GNUNET_REST_jsonapi_resource_add_attr (const struct JsonApiResource *resource,
129                                        const char* key,
130                                        json_t *json)
131 {
132   if ( (NULL == resource) ||
133        (NULL == key) ||
134        (NULL == json) )
135     return GNUNET_SYSERR;
136   json_object_set (resource->res_obj, key, json);
137   return GNUNET_OK;
138 }
139
140 /**
141  * Read a JSON API attribute
142  *
143  * @param res the JSON resource
144  * @param key the key for the attribute
145  * @return the json_t object
146  */
147 json_t*
148 GNUNET_REST_jsonapi_resource_read_attr (const struct JsonApiResource *resource,
149                                        const char* key)
150 {
151   if ( (NULL == resource) ||
152        (NULL == key))
153     return NULL;
154   return json_object_get (resource->res_obj, key);
155 }
156
157 int
158 check_resource_attr_str (const struct JsonApiResource *resource,
159                          const char* key,
160                          const char* attr)
161 {
162   json_t *value;
163   if ( (NULL == resource) ||
164        (NULL == key) ||
165        (NULL == attr))
166     return GNUNET_NO;
167   value = json_object_get (resource->res_obj, key);
168   if (NULL == value)
169     return GNUNET_NO;
170   if (!json_is_string (value) ||
171       (0 != strcmp (attr, json_string_value(value))))
172   {
173     return GNUNET_NO;
174   }
175   return GNUNET_YES;
176 }
177
178 /**
179  * Check a JSON API resource id
180  *
181  * @param res the JSON resource
182  * @param id the expected id
183  * @return GNUNET_YES if id matches
184  */
185 int
186 GNUNET_REST_jsonapi_resource_check_id (const struct JsonApiResource *resource,
187                                        const char* id)
188 {
189   return check_resource_attr_str (resource, GNUNET_REST_JSONAPI_KEY_ID, id);  
190 }
191
192
193 /**
194  * Check a JSON API resource type
195  *
196  * @param res the JSON resource
197  * @param type the expected type
198  * @return GNUNET_YES if id matches
199  */
200 int
201 GNUNET_REST_jsonapi_resource_check_type (const struct JsonApiResource *resource,
202                                          const char* type)
203 {
204   return check_resource_attr_str (resource, GNUNET_REST_JSONAPI_KEY_TYPE, type);  
205 }
206
207
208 /**
209  * Create a JSON API primary data
210  *
211  * @return a new JSON API resource or NULL on error.
212  */
213 struct JsonApiObject*
214 GNUNET_REST_jsonapi_object_new ()
215 {
216   struct JsonApiObject *result;
217
218   result = GNUNET_new (struct JsonApiObject);
219   result->res_count = 0;
220   return result;
221 }
222
223
224 static void
225 add_json_resource (struct JsonApiObject *obj,
226                    const json_t *res_json)
227 {
228   struct JsonApiResource *res;
229   json_t *type_json;
230
231   type_json = json_object_get (res_json, GNUNET_REST_JSONAPI_KEY_TYPE);
232   if (!json_is_string (type_json))
233     return;
234   res = GNUNET_new (struct JsonApiResource);
235   res->next = NULL;
236   res->prev = NULL;
237   res->res_obj = json_deep_copy (res_json);
238   GNUNET_REST_jsonapi_object_resource_add (obj, res);
239 }
240
241 /**
242  * Create a JSON API primary data from a string
243  *
244  * @param data the string of the JSON API data
245  * @return a new JSON API resource or NULL on error.
246  */
247 struct JsonApiObject*
248 GNUNET_REST_jsonapi_object_parse (const char* data)
249 {
250   struct JsonApiObject *result;
251   json_t *root_json;
252   json_t *data_json;
253   json_error_t error;
254   int res_count = 0;
255   int i;
256   if (NULL == data)
257     return NULL;
258   root_json = json_loads (data, 0, &error);
259
260   if ( (NULL == root_json) || !json_is_object (root_json))
261   {
262     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "json error: %s", error.text); 
263     return NULL;
264   }
265   data_json = json_object_get (root_json, GNUNET_REST_JSONAPI_KEY_DATA);
266   if (NULL == data_json)
267   {
268     json_decref (root_json);
269     return NULL;
270   }
271
272   result = GNUNET_new (struct JsonApiObject);
273   result->res_count = 0;
274   if (json_is_object (data_json))
275     add_json_resource (result, data_json);
276   else if (json_is_array (data_json))
277   {
278     res_count = json_array_size (data_json);
279     for (i = 0; i < res_count; i++)
280       add_json_resource (result, json_array_get (data_json, i));
281   }
282   json_decref (root_json);
283   if (0 == result->res_count)
284   {
285     GNUNET_free (result);
286     result = NULL;
287   }
288   return result;
289 }
290
291
292 /**
293  * Delete a JSON API primary data
294  *
295  * @param type the JSON API resource type
296  * @param id the JSON API resource id
297  * @return a new JSON API resource or NULL on error.
298  */
299 void
300 GNUNET_REST_jsonapi_object_delete (struct JsonApiObject *resp)
301 {
302   struct JsonApiResource *res;
303   struct JsonApiResource *res_next;
304   
305   for (res = resp->res_list_head; 
306        res != NULL;)
307   {
308     res_next = res->next;
309     GNUNET_CONTAINER_DLL_remove (resp->res_list_head,
310                                  resp->res_list_tail,
311                                  res);
312     GNUNET_REST_jsonapi_resource_delete (res);
313     res = res_next;
314   }
315   GNUNET_free (resp);
316 }
317
318 /**
319  * Add a JSON API object to primary data
320  *
321  * @param data The JSON API data to add to
322  * @param res the JSON API resource to add
323  * @return the new number of resources
324  */
325 void
326 GNUNET_REST_jsonapi_object_resource_add (struct JsonApiObject *resp,
327                                            struct JsonApiResource *res)
328 {
329   GNUNET_CONTAINER_DLL_insert (resp->res_list_head,
330                             resp->res_list_tail,
331                             res);
332   
333   resp->res_count++;
334 }
335
336
337 /**
338  * Get a JSON API object resource count
339  *
340  * @param resp the JSON API object
341  * @return the number of resources
342  */
343 int
344 GNUNET_REST_jsonapi_object_resource_count (struct JsonApiObject *resp)
345 {
346   return resp->res_count;
347 }
348
349 /**
350  * Get a JSON API object resource num
351  *
352  * @param resp the JSON API object
353  * @param num the number of the resource
354  * @return the resource
355  */
356 struct JsonApiResource*
357 GNUNET_REST_jsonapi_object_get_resource (struct JsonApiObject *resp, int num)
358 {
359   struct JsonApiResource *res;
360   int i;
361
362   if ((0 == resp->res_count) ||
363       (num >= resp->res_count))
364     return NULL;
365   res = resp->res_list_head;
366   for (i = 0; i < num; i++)
367   {
368     res = res->next;
369   }
370   return res;
371 }
372
373
374 /**
375  * Add a JSON API resource to primary data
376  *
377  * @param data The JSON API data to add to
378  * @param res the JSON API resource to add
379  * @return the new number of resources
380  */
381 void
382 GNUNET_REST_jsonapi_data_resource_remove (struct JsonApiObject *resp,
383                                           struct JsonApiResource *res)
384 {
385   GNUNET_CONTAINER_DLL_remove (resp->res_list_head,
386                                resp->res_list_tail,
387                               res);
388   resp->res_count--;
389 }
390
391 /**
392  * String serialze jsonapi primary data
393  *
394  * @param data the JSON API primary data
395  * @param result where to store the result
396  * @return GNUNET_SYSERR on error else GNUNET_OK
397  */
398 int
399 GNUNET_REST_jsonapi_data_serialize (const struct JsonApiObject *resp,
400                                     char **result)
401 {
402   struct JsonApiResource *res;
403   json_t *root_json;
404   json_t *res_arr;
405   
406   if ((NULL == resp))
407     return GNUNET_SYSERR;
408
409   root_json = json_object ();
410   res_arr = json_array ();
411   for (res = resp->res_list_head; 
412        res != NULL;
413        res = res->next)
414   {
415     json_array_append (res_arr, res->res_obj);
416   }
417   json_object_set (root_json, GNUNET_REST_JSONAPI_KEY_DATA, res_arr);
418   *result = json_dumps (root_json, JSON_INDENT(2));
419   json_decref (root_json);
420   json_decref (res_arr);
421   return GNUNET_OK;
422 }
423
424 /**
425  * REST Utilities
426  */
427
428   /**
429    * Check if namespace is in URL.
430    *
431    * @param url URL to check
432    * @param namespace namespace to check against
433    * @retun GNUNET_YES if namespace matches
434    */
435 int
436 GNUNET_REST_namespace_match (const char *url, const char *namespace)
437 {
438   if (0 != strncmp (namespace, url, strlen (namespace)))
439     return GNUNET_NO;
440
441   if ((strlen (namespace) < strlen (url)) &&
442       (url[strlen (namespace)] != '/'))
443     return GNUNET_NO;
444
445   return GNUNET_YES;
446 }
447
448 /**
449  * Create JSON API MHD response
450  *
451  * @param data JSON result
452  * @retun MHD response
453  */
454 struct MHD_Response*
455 GNUNET_REST_create_json_response (const char *data)
456 {
457   struct MHD_Response *resp;
458   size_t len;
459
460   if (NULL == data)
461   {
462     len = 0;
463     data = "";
464   }
465   else
466     len = strlen (data);
467   resp = MHD_create_response_from_buffer (len,
468                                           (void*)data,
469                                           MHD_RESPMEM_MUST_COPY);
470   MHD_add_response_header (resp,MHD_HTTP_HEADER_CONTENT_TYPE,"application/json");
471   return resp;
472
473 }
474
475 int
476 GNUNET_REST_handle_request (struct RestConnectionDataHandle *conn,
477                             const struct GNUNET_REST_RestConnectionHandler *handlers,
478                             void *cls)
479 {
480   int count;
481   int i;
482   char *url;
483
484   count = 0;
485   while (NULL != handlers[count].method)
486     count++;
487
488   GNUNET_asprintf (&url, "%s", conn->url);
489   if (url[strlen (url)-1] == '/')
490     url[strlen (url)-1] = '\0';
491   for (i = 0; i < count; i++)
492   {
493     if (0 != strcasecmp (conn->method, handlers[i].method))
494       continue;
495     if (strlen (url) < strlen (handlers[i].namespace))
496       continue;
497     if (GNUNET_NO == GNUNET_REST_namespace_match (url, handlers[i].namespace))
498       continue;
499     //Match
500     handlers[i].proc (conn, (const char*)url, cls);
501     GNUNET_free (url);
502     return GNUNET_YES;
503   }
504   GNUNET_free (url);
505   return GNUNET_NO;
506 }
507
508 /* end of rest.c */