225798db9683421bba7b6d7e3658f2999d35a634
[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., 59 Temple Place - Suite 330,
18       Boston, MA 02111-1307, 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     json_decref (value);
174     return GNUNET_NO;
175   }
176   json_decref (value);
177   return GNUNET_YES;
178 }
179
180 /**
181  * Check a JSON API resource id
182  *
183  * @param res the JSON resource
184  * @param id the expected id
185  * @return GNUNET_YES if id matches
186  */
187 int
188 GNUNET_REST_jsonapi_resource_check_id (const struct JsonApiResource *resource,
189                                        const char* id)
190 {
191   return check_resource_attr_str (resource, GNUNET_REST_JSONAPI_KEY_ID, id);  
192 }
193
194
195 /**
196  * Check a JSON API resource type
197  *
198  * @param res the JSON resource
199  * @param type the expected type
200  * @return GNUNET_YES if id matches
201  */
202 int
203 GNUNET_REST_jsonapi_resource_check_type (const struct JsonApiResource *resource,
204                                          const char* type)
205 {
206   return check_resource_attr_str (resource, GNUNET_REST_JSONAPI_KEY_TYPE, type);  
207 }
208
209
210 /**
211  * Create a JSON API primary data
212  *
213  * @return a new JSON API resource or NULL on error.
214  */
215 struct JsonApiObject*
216 GNUNET_REST_jsonapi_object_new ()
217 {
218   struct JsonApiObject *result;
219
220   result = GNUNET_new (struct JsonApiObject);
221   result->res_count = 0;
222   return result;
223 }
224
225
226 static void
227 add_json_resource (struct JsonApiObject *obj,
228                    const json_t *res_json)
229 {
230   struct JsonApiResource *res;
231   json_t *type_json;
232   json_t *id_json;
233
234   id_json = json_object_get (res_json, GNUNET_REST_JSONAPI_KEY_ID);
235   type_json = json_object_get (res_json, GNUNET_REST_JSONAPI_KEY_TYPE);
236   if (!json_is_string (id_json) || !json_is_string (type_json))
237     return;
238   res = GNUNET_new (struct JsonApiResource);
239   res->next = NULL;
240   res->prev = NULL;
241   res->res_obj = json_deep_copy (res_json);
242   GNUNET_REST_jsonapi_object_resource_add (obj, res);
243 }
244
245 /**
246  * Create a JSON API primary data from a string
247  *
248  * @param data the string of the JSON API data
249  * @return a new JSON API resource or NULL on error.
250  */
251 struct JsonApiObject*
252 GNUNET_REST_jsonapi_object_parse (const char* data)
253 {
254   struct JsonApiObject *result;
255   json_t *root_json;
256   json_t *data_json;
257   json_error_t error;
258   int res_count = 0;
259   int i;
260   if (NULL == data)
261     return NULL;
262   root_json = json_loads (data, 0, &error);
263
264   if ( (NULL == root_json) || !json_is_object (root_json))
265   {
266     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "json error: %s", error.text); 
267     return NULL;
268   }
269   data_json = json_object_get (root_json, GNUNET_REST_JSONAPI_KEY_DATA);
270   if (NULL == data_json)
271   {
272     json_decref (root_json);
273     return NULL;
274   }
275
276   result = GNUNET_new (struct JsonApiObject);
277   result->res_count = 0;
278   if (json_is_object (data_json))
279     add_json_resource (result, data_json);
280   else if (json_is_array (data_json))
281   {
282     res_count = json_array_size (data_json);
283     for (i = 0; i < res_count; i++)
284       add_json_resource (result, json_array_get (data_json, i));
285   }
286   json_decref (root_json);
287   if (0 == result->res_count)
288   {
289     GNUNET_free (result);
290     result = NULL;
291   }
292   return result;
293 }
294
295
296 /**
297  * Delete a JSON API primary data
298  *
299  * @param type the JSON API resource type
300  * @param id the JSON API resource id
301  * @return a new JSON API resource or NULL on error.
302  */
303 void
304 GNUNET_REST_jsonapi_object_delete (struct JsonApiObject *resp)
305 {
306   struct JsonApiResource *res;
307   struct JsonApiResource *res_next;
308   
309   for (res = resp->res_list_head; 
310        res != NULL;)
311   {
312     GNUNET_CONTAINER_DLL_remove (resp->res_list_head,
313                                  resp->res_list_tail,
314                                  res);
315     res_next = res->next;
316     GNUNET_REST_jsonapi_resource_delete (res);
317     res = res_next;
318   }
319   GNUNET_free (resp);
320 }
321
322 /**
323  * Add a JSON API object to primary data
324  *
325  * @param data The JSON API data to add to
326  * @param res the JSON API resource to add
327  * @return the new number of resources
328  */
329 void
330 GNUNET_REST_jsonapi_object_resource_add (struct JsonApiObject *resp,
331                                            struct JsonApiResource *res)
332 {
333   GNUNET_CONTAINER_DLL_insert (resp->res_list_head,
334                             resp->res_list_tail,
335                             res);
336   
337   resp->res_count++;
338 }
339
340
341 /**
342  * Get a JSON API object resource count
343  *
344  * @param resp the JSON API object
345  * @return the number of resources
346  */
347 int
348 GNUNET_REST_jsonapi_object_resource_count (struct JsonApiObject *resp)
349 {
350   return resp->res_count++;
351 }
352
353 /**
354  * Get a JSON API object resource #num
355  *
356  * @param resp the JSON API object
357  * @param num the number of the resource
358  * @return the resource
359  */
360 struct JsonApiResource*
361 GNUNET_REST_jsonapi_object_get_resource (struct JsonApiObject *resp, int num)
362 {
363   struct JsonApiResource *res;
364   int i;
365
366   if ((0 == resp->res_count) ||
367       (num >= resp->res_count))
368     return NULL;
369   res = resp->res_list_head;
370   for (i = 0; i < num; i++)
371   {
372     res = res->next;
373   }
374   return res;
375 }
376
377
378 /**
379  * Add a JSON API resource to primary data
380  *
381  * @param data The JSON API data to add to
382  * @param res the JSON API resource to add
383  * @return the new number of resources
384  */
385 void
386 GNUNET_REST_jsonapi_data_resource_remove (struct JsonApiObject *resp,
387                                           struct JsonApiResource *res)
388 {
389   GNUNET_CONTAINER_DLL_remove (resp->res_list_head,
390                                resp->res_list_tail,
391                               res);
392   resp->res_count--;
393 }
394
395 /**
396  * String serialze jsonapi primary data
397  *
398  * @param data the JSON API primary data
399  * @param result where to store the result
400  * @return GNUNET_SYSERR on error else GNUNET_OK
401  */
402 int
403 GNUNET_REST_jsonapi_data_serialize (const struct JsonApiObject *resp,
404                                     char **result)
405 {
406   struct JsonApiResource *res;
407   json_t *root_json;
408   json_t *res_arr;
409   
410   if ( (NULL == resp) ||
411        (0 == resp->res_count) )
412     return GNUNET_SYSERR;
413
414   root_json = json_object ();
415   
416   if (1 == resp->res_count)
417   {
418     json_object_set (root_json, GNUNET_REST_JSONAPI_KEY_DATA, resp->res_list_head->res_obj);
419   }
420   else
421   {
422     res_arr = json_array ();
423     for (res = resp->res_list_head; 
424          res != NULL;
425          res = res->next)
426     {
427       json_array_append (res_arr, res->res_obj);
428     }
429     json_object_set (root_json, GNUNET_REST_JSONAPI_KEY_DATA, res_arr);
430   }
431   *result = json_dumps (root_json, JSON_INDENT(2));
432   return GNUNET_OK;
433 }
434
435 /**
436  * REST Utilities
437  */
438
439 /**
440  * Check if namespace is in URL.
441  *
442  * @param url URL to check
443  * @param namespace namespace to check against
444  * @retun GNUNET_YES if namespace matches
445  */
446 int
447 GNUNET_REST_namespace_match (const char *url, const char *namespace)
448 {
449   if (0 != strncmp (namespace, url, strlen (namespace)))
450     return GNUNET_NO;
451
452   if ((strlen (namespace) < strlen (url)) &&
453       (url[strlen (namespace)] != '/'))
454     return GNUNET_NO;
455
456   return GNUNET_YES;
457 }
458
459 /**
460  * Create JSON API MHD response
461  *
462  * @param data JSON result
463  * @retun MHD response
464  */
465 struct MHD_Response*
466 GNUNET_REST_create_json_response (const char *data)
467 {
468   struct MHD_Response *resp;
469   size_t len;
470
471   if (NULL == data)
472   {
473     len = 0;
474     data = "";
475   }
476   else
477     len = strlen (data);
478   resp = MHD_create_response_from_buffer (len,
479                                           (void*)data,
480                                           MHD_RESPMEM_MUST_COPY);
481   MHD_add_response_header (resp,MHD_HTTP_HEADER_CONTENT_TYPE,"application/json");
482   return resp;
483
484 }
485
486 int
487 GNUNET_REST_handle_request (struct RestConnectionDataHandle *conn,
488                             const struct GNUNET_REST_RestConnectionHandler *handlers,
489                             void *cls)
490 {
491   int count;
492   int i;
493   char *url;
494
495   count = 0;
496   while (NULL != handlers[count].method)
497     count++;
498
499   GNUNET_asprintf (&url, "%s", conn->url);
500   if (url[strlen (url)-1] == '/')
501     url[strlen (url)-1] = '\0';
502   for (i = 0; i < count; i++)
503   {
504     if (0 != strcasecmp (conn->method, handlers[i].method))
505       continue;
506     if (strlen (url) < strlen (handlers[i].namespace))
507       continue;
508     if (GNUNET_NO == GNUNET_REST_namespace_match (url, handlers[i].namespace))
509       continue;
510     //Match
511     handlers[i].proc (conn, (const char*)url, cls);
512     GNUNET_free (url);
513     return GNUNET_YES;
514   }
515   GNUNET_free (url);
516   return GNUNET_NO;
517 }
518
519 /* end of rest.c */