fix gns and identity test script
[oweals/gnunet.git] / src / gns / plugin_rest_gns.c
1 /*
2    This file is part of GNUnet.
3    Copyright (C) 2012-2015 GNUnet e.V.
4
5    GNUnet is free software: you can redistribute it and/or modify it
6    under the terms of the GNU Affero General Public License as published
7    by the Free Software Foundation, either version 3 of the License,
8    or (at your 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    Affero General Public License for more details.
14
15    You should have received a copy of the GNU Affero General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17    */
18 /**
19  * @author Philippe Buschmann
20  * @file gns/plugin_rest_gns.c
21  * @brief GNUnet Gns REST plugin
22  */
23
24 #include "platform.h"
25 #include "gnunet_rest_plugin.h"
26 #include "gnunet_rest_lib.h"
27 #include "gnunet_json_lib.h"
28 #include "gnunet_gnsrecord_lib.h"
29 #include "gnunet_gns_service.h"
30 #include "microhttpd.h"
31 #include <jansson.h>
32
33 #define GNUNET_REST_API_NS_GNS "/gns"
34
35
36 #define GNUNET_REST_GNS_PARAM_NAME "name"
37
38 #define GNUNET_REST_GNS_PARAM_RECORD_TYPE "record_type"
39 #define GNUNET_REST_GNS_ERROR_UNKNOWN "Unknown Error"
40
41 /**
42  * The configuration handle
43  */
44 const struct GNUNET_CONFIGURATION_Handle *cfg;
45
46 /**
47  * HTTP methods allows for this plugin
48  */
49 static char* allow_methods;
50
51 /**
52  * @brief struct returned by the initialization function of the plugin
53  */
54 struct Plugin
55 {
56   const struct GNUNET_CONFIGURATION_Handle *cfg;
57 };
58
59
60 struct RequestHandle
61 {
62
63   /**
64    * Connection to GNS
65    */
66   struct GNUNET_GNS_Handle *gns;
67
68   /**
69    * Active GNS lookup
70    */
71   struct GNUNET_GNS_LookupWithTldRequest *gns_lookup;
72
73   /**
74    * Name to look up
75    */
76   char *name;
77
78   /**
79    * Record type to look up
80    */
81   int record_type;
82
83   /**
84    * Rest connection
85    */
86   struct GNUNET_REST_RequestHandle *rest_handle;
87   
88   /**
89    * Desired timeout for the lookup (default is no timeout).
90    */
91   struct GNUNET_TIME_Relative timeout;
92
93   /**
94    * ID of a task associated with the resolution process.
95    */
96   struct GNUNET_SCHEDULER_Task *timeout_task;
97
98   /**
99    * The plugin result processor
100    */
101   GNUNET_REST_ResultProcessor proc;
102
103   /**
104    * The closure of the result processor
105    */
106   void *proc_cls;
107
108   /**
109    * The url
110    */
111   char *url;
112
113   /**
114    * Error response message
115    */
116   char *emsg;
117
118   /**
119    * Reponse code
120    */
121   int response_code;
122
123 };
124
125
126 /**
127  * Cleanup lookup handle
128  * @param handle Handle to clean up
129  */
130 static void
131 cleanup_handle (void *cls)
132 {
133   struct RequestHandle *handle = cls;
134   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
135               "Cleaning up\n");
136
137   if (NULL != handle->gns_lookup)
138   {
139     GNUNET_GNS_lookup_with_tld_cancel (handle->gns_lookup);
140     handle->gns_lookup = NULL;
141   }
142   if (NULL != handle->gns)
143   {
144     GNUNET_GNS_disconnect (handle->gns);
145     handle->gns = NULL;
146   }
147
148   if (NULL != handle->timeout_task)
149   {
150     GNUNET_SCHEDULER_cancel (handle->timeout_task);
151     handle->timeout_task = NULL;
152   }
153   if (NULL != handle->url)
154     GNUNET_free (handle->url);
155   if (NULL != handle->name)
156     GNUNET_free (handle->name);
157   if (NULL != handle->emsg)
158     GNUNET_free (handle->emsg);
159   
160   GNUNET_free (handle);
161 }
162
163
164 /**
165  * Task run on errors.  Reports an error and cleans up everything.
166  *
167  * @param cls the `struct RequestHandle`
168  */
169 static void
170 do_error (void *cls)
171 {
172   struct RequestHandle *handle = cls;
173   struct MHD_Response *resp;
174   json_t *json_error = json_object();
175   char *response;
176
177   if (NULL == handle->emsg)
178     handle->emsg = GNUNET_strdup(GNUNET_REST_GNS_ERROR_UNKNOWN);
179
180   json_object_set_new(json_error,"error", json_string(handle->emsg));
181
182   if (0 == handle->response_code)
183     handle->response_code = MHD_HTTP_OK;
184   response = json_dumps (json_error, 0);
185   resp = GNUNET_REST_create_response (response);
186   handle->proc (handle->proc_cls, resp, handle->response_code);
187   json_decref(json_error);
188   GNUNET_free(response);
189   GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
190 }
191
192
193 /**
194  * Iterator called on obtained result for a GNS lookup.
195  *
196  * @param cls closure with the object
197  * @param was_gns #GNUNET_NO if name was not a GNS name
198  * @param rd_count number of records in @a rd
199  * @param rd the records in reply
200  */
201 static void
202 handle_gns_response (void *cls,
203                      int was_gns,
204                      uint32_t rd_count,
205                      const struct GNUNET_GNSRECORD_Data *rd)
206 {
207   struct RequestHandle *handle = cls;
208   struct MHD_Response *resp;
209   json_t *result_array;
210   json_t *record_obj;
211   char *result;
212
213   handle->gns_lookup = NULL;
214
215   if (GNUNET_NO == was_gns)
216   {
217     handle->emsg = GNUNET_strdup("Name not found in GNS");
218     GNUNET_SCHEDULER_add_now (&do_error, handle);
219     return;
220   }
221
222   result_array = json_array();
223   for (uint32_t i=0;i<rd_count;i++)
224   {
225     if ((rd[i].record_type != handle->record_type) &&
226         (GNUNET_GNSRECORD_TYPE_ANY != handle->record_type) )
227     {
228       continue;
229     }
230
231     record_obj = GNUNET_JSON_from_gns_record(NULL,&rd[i]);
232     json_array_append (result_array, record_obj);
233     json_decref (record_obj);
234   }
235
236   result = json_dumps(result_array, 0);
237   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result);
238   resp = GNUNET_REST_create_response (result);
239   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
240   GNUNET_free (result);
241   json_decref (result_array);
242   GNUNET_SCHEDULER_add_now(&cleanup_handle, handle);
243 }
244
245
246 /**
247  * Handle gns GET request
248  *
249  * @param con_handle the connection handle
250  * @param url the url
251  * @param cls the RequestHandle
252  */
253 void
254 get_gns_cont (struct GNUNET_REST_RequestHandle *con_handle,
255               const char* url,
256               void *cls)
257 {
258   struct RequestHandle *handle = cls;
259   struct GNUNET_HashCode key;
260   char *record_type;
261   char *name;
262
263   GNUNET_CRYPTO_hash (GNUNET_REST_GNS_PARAM_NAME,
264                       strlen (GNUNET_REST_GNS_PARAM_NAME),
265                       &key);
266   if ( GNUNET_NO
267         == GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map,
268                                                  &key))
269   {
270     handle->emsg = GNUNET_strdup("Parameter name is missing");
271     GNUNET_SCHEDULER_add_now (&do_error, handle);
272     return;
273   }
274   name = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map,&key);
275   if(0 >= strlen (name))
276   {
277     handle->emsg = GNUNET_strdup("Length of parameter name is zero");
278     GNUNET_SCHEDULER_add_now (&do_error, handle);
279     return;
280   }
281   handle->name = GNUNET_strdup(name);
282
283   handle->record_type = UINT32_MAX;
284   GNUNET_CRYPTO_hash (GNUNET_REST_GNS_PARAM_RECORD_TYPE,
285                       strlen (GNUNET_REST_GNS_PARAM_RECORD_TYPE),
286                       &key);
287   if ( GNUNET_YES
288       == GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map,
289                                                  &key))
290   {
291     record_type = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map, &key);
292     handle->record_type = GNUNET_GNSRECORD_typename_to_number(record_type);
293   }
294
295
296   if(UINT32_MAX == handle->record_type)
297   {
298     handle->record_type = GNUNET_GNSRECORD_TYPE_ANY;
299   }
300
301   handle->gns = GNUNET_GNS_connect (cfg);
302   if (NULL == handle->gns)
303   {
304     handle->emsg = GNUNET_strdup ("GNS not available");
305     GNUNET_SCHEDULER_add_now (&do_error, handle);
306     return;
307   }
308
309   handle->gns_lookup = GNUNET_GNS_lookup_with_tld (handle->gns,
310                                                  handle->name,
311                                                  handle->record_type,
312                                                  GNUNET_NO,
313                                                  &handle_gns_response,
314                                                  handle);
315   return;
316 }
317
318
319
320 /**
321  * Respond to OPTIONS request
322  *
323  * @param con_handle the connection handle
324  * @param url the url
325  * @param cls the RequestHandle
326  */
327 static void
328 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
329               const char* url,
330               void *cls)
331 {
332   struct MHD_Response *resp;
333   struct RequestHandle *handle = cls;
334
335   //independent of path return all options
336   resp = GNUNET_REST_create_response (NULL);
337   MHD_add_response_header (resp,
338                            "Access-Control-Allow-Methods",
339                            allow_methods);
340   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
341   GNUNET_SCHEDULER_add_now(&cleanup_handle, handle);
342   return;
343 }
344
345
346 /**
347  * Handle rest request
348  *
349  * @param handle the request handle
350  */
351 static void
352 init_cont (struct RequestHandle *handle)
353 {
354   struct GNUNET_REST_RequestHandlerError err;
355   static const struct GNUNET_REST_RequestHandler handlers[] = {
356     {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_GNS, &get_gns_cont},
357     {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_GNS, &options_cont},
358     GNUNET_REST_HANDLER_END
359   };
360
361   if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle,
362                                                handlers,
363                                                &err,
364                                                handle))
365   {
366     handle->response_code = err.error_code;
367     GNUNET_SCHEDULER_add_now (&do_error, handle);
368   }
369 }
370
371
372 /**
373  * Function processing the REST call
374  *
375  * @param method HTTP method
376  * @param url URL of the HTTP request
377  * @param data body of the HTTP request (optional)
378  * @param data_size length of the body
379  * @param proc callback function for the result
380  * @param proc_cls closure for callback function
381  * @return GNUNET_OK if request accepted
382  */
383 static void
384 rest_process_request(struct GNUNET_REST_RequestHandle *rest_handle,
385                               GNUNET_REST_ResultProcessor proc,
386                               void *proc_cls)
387 {
388   struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
389   
390   handle->response_code = 0;
391   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
392   handle->proc_cls = proc_cls;
393   handle->proc = proc;
394   handle->rest_handle = rest_handle;
395   
396   handle->url = GNUNET_strdup (rest_handle->url);
397   if (handle->url[strlen (handle->url)-1] == '/')
398     handle->url[strlen (handle->url)-1] = '\0';
399   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
400
401   init_cont(handle);
402
403   handle->timeout_task =
404     GNUNET_SCHEDULER_add_delayed (handle->timeout,
405                                   &do_error,
406                                   handle);
407   
408   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
409 }
410
411
412 /**
413  * Entry point for the plugin.
414  *
415  * @param cls Config info
416  * @return NULL on error, otherwise the plugin context
417  */
418 void *
419 libgnunet_plugin_rest_gns_init (void *cls)
420 {
421   static struct Plugin plugin;
422   struct GNUNET_REST_Plugin *api;
423
424   cfg = cls;
425   if (NULL != plugin.cfg)
426     return NULL;                /* can only initialize once! */
427   memset (&plugin, 0, sizeof (struct Plugin));
428   plugin.cfg = cfg;
429   api = GNUNET_new (struct GNUNET_REST_Plugin);
430   api->cls = &plugin;
431   api->name = GNUNET_REST_API_NS_GNS;
432   api->process_request = &rest_process_request;
433   GNUNET_asprintf (&allow_methods,
434                    "%s, %s, %s, %s, %s",
435                    MHD_HTTP_METHOD_GET,
436                    MHD_HTTP_METHOD_POST,
437                    MHD_HTTP_METHOD_PUT,
438                    MHD_HTTP_METHOD_DELETE,
439                    MHD_HTTP_METHOD_OPTIONS);
440
441   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
442               _("Gns REST API initialized\n"));
443   return api;
444 }
445
446
447 /**
448  * Exit point from the plugin.
449  *
450  * @param cls the plugin context (as returned by "init")
451  * @return always NULL
452  */
453 void *
454 libgnunet_plugin_rest_gns_done (void *cls)
455 {
456   struct GNUNET_REST_Plugin *api = cls;
457   struct Plugin *plugin = api->cls;
458   plugin->cfg = NULL;
459
460   GNUNET_free_non_null (allow_methods);
461   GNUNET_free (api);
462   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
463               "Gns REST plugin is finished\n");
464   return NULL;
465 }
466
467 /* end of plugin_rest_gns.c */
468