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