2 This file is part of GNUnet.
3 Copyright (C) 2012-2015 GNUnet e.V.
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.
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.
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/>.
19 * @author Philippe Buschmann
20 * @file gns/plugin_rest_gns.c
21 * @brief GNUnet Gns REST plugin
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"
34 * Rest API GNS Namespace
36 #define GNUNET_REST_API_NS_GNS "/gns"
39 * Rest API GNS Parameter record_type
41 #define GNUNET_REST_GNS_PARAM_RECORD_TYPE "record_type"
44 * Rest API GNS ERROR Unknown Error
46 #define GNUNET_REST_GNS_ERROR_UNKNOWN "Unknown Error"
49 * Rest API GNS ERROR Record not found
51 #define GNUNET_REST_GNS_NOT_FOUND "Record not found"
54 * The configuration handle
56 const struct GNUNET_CONFIGURATION_Handle *cfg;
59 * HTTP methods allows for this plugin
61 static char* allow_methods;
64 * @brief struct returned by the initialization function of the plugin
68 const struct GNUNET_CONFIGURATION_Handle *cfg;
80 struct GNUNET_GNS_Handle *gns;
85 struct GNUNET_GNS_LookupWithTldRequest *gns_lookup;
93 * Record type to look up
100 struct GNUNET_REST_RequestHandle *rest_handle;
103 * Desired timeout for the lookup (default is no timeout).
105 struct GNUNET_TIME_Relative timeout;
108 * ID of a task associated with the resolution process.
110 struct GNUNET_SCHEDULER_Task *timeout_task;
113 * The plugin result processor
115 GNUNET_REST_ResultProcessor proc;
118 * The closure of the result processor
128 * Error response message
141 * Cleanup lookup handle
142 * @param handle Handle to clean up
145 cleanup_handle (void *cls)
147 struct RequestHandle *handle = cls;
148 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
151 if (NULL != handle->gns_lookup)
153 GNUNET_GNS_lookup_with_tld_cancel (handle->gns_lookup);
154 handle->gns_lookup = NULL;
156 if (NULL != handle->gns)
158 GNUNET_GNS_disconnect (handle->gns);
162 if (NULL != handle->timeout_task)
164 GNUNET_SCHEDULER_cancel (handle->timeout_task);
165 handle->timeout_task = NULL;
167 if (NULL != handle->url)
168 GNUNET_free (handle->url);
169 if (NULL != handle->name)
170 GNUNET_free (handle->name);
171 if (NULL != handle->emsg)
172 GNUNET_free (handle->emsg);
174 GNUNET_free (handle);
179 * Task run on errors. Reports an error and cleans up everything.
181 * @param cls the `struct RequestHandle`
186 struct RequestHandle *handle = cls;
187 struct MHD_Response *resp;
188 json_t *json_error = json_object();
191 if (NULL == handle->emsg)
192 handle->emsg = GNUNET_strdup(GNUNET_REST_GNS_ERROR_UNKNOWN);
194 json_object_set_new(json_error,"error", json_string(handle->emsg));
196 if (0 == handle->response_code)
197 handle->response_code = MHD_HTTP_OK;
198 response = json_dumps (json_error, 0);
199 resp = GNUNET_REST_create_response (response);
200 handle->proc (handle->proc_cls, resp, handle->response_code);
201 json_decref(json_error);
202 GNUNET_free(response);
203 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
208 * Iterator called on obtained result for a GNS lookup.
210 * @param cls closure with the object
211 * @param was_gns #GNUNET_NO if name was not a GNS name
212 * @param rd_count number of records in @a rd
213 * @param rd the records in reply
216 handle_gns_response (void *cls,
219 const struct GNUNET_GNSRECORD_Data *rd)
221 struct RequestHandle *handle = cls;
222 struct MHD_Response *resp;
223 json_t *result_array;
227 handle->gns_lookup = NULL;
229 if (GNUNET_NO == was_gns)
231 handle->response_code = MHD_HTTP_NOT_FOUND;
232 handle->emsg = GNUNET_strdup(GNUNET_REST_GNS_NOT_FOUND);
233 GNUNET_SCHEDULER_add_now (&do_error, handle);
237 result_array = json_array();
238 for (uint32_t i=0;i<rd_count;i++)
240 if ((rd[i].record_type != handle->record_type) &&
241 (GNUNET_GNSRECORD_TYPE_ANY != handle->record_type) )
246 record_obj = GNUNET_JSON_from_gns_record(NULL,&rd[i]);
247 json_array_append (result_array, record_obj);
248 json_decref (record_obj);
251 result = json_dumps(result_array, 0);
252 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result);
253 resp = GNUNET_REST_create_response (result);
254 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
255 GNUNET_free (result);
256 json_decref (result_array);
257 GNUNET_SCHEDULER_add_now(&cleanup_handle, handle);
262 * Handle gns GET request
264 * @param con_handle the connection handle
266 * @param cls the RequestHandle
269 get_gns_cont (struct GNUNET_REST_RequestHandle *con_handle,
273 struct RequestHandle *handle = cls;
274 struct GNUNET_HashCode key;
280 if (strlen (GNUNET_REST_API_NS_GNS) < strlen (handle->url))
282 name = &handle->url[strlen (GNUNET_REST_API_NS_GNS) + 1];
287 handle->response_code = MHD_HTTP_NOT_FOUND;
288 handle->emsg = GNUNET_strdup(GNUNET_REST_GNS_NOT_FOUND);
289 GNUNET_SCHEDULER_add_now (&do_error, handle);
292 if (0 >= strlen (name))
294 handle->response_code = MHD_HTTP_NOT_FOUND;
295 handle->emsg = GNUNET_strdup(GNUNET_REST_GNS_NOT_FOUND);
296 GNUNET_SCHEDULER_add_now (&do_error, handle);
299 handle->name = GNUNET_strdup(name);
301 handle->record_type = UINT32_MAX;
302 GNUNET_CRYPTO_hash (GNUNET_REST_GNS_PARAM_RECORD_TYPE,
303 strlen (GNUNET_REST_GNS_PARAM_RECORD_TYPE),
306 == GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map,
309 record_type = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map, &key);
310 handle->record_type = GNUNET_GNSRECORD_typename_to_number(record_type);
313 if(UINT32_MAX == handle->record_type)
315 handle->record_type = GNUNET_GNSRECORD_TYPE_ANY;
318 handle->gns_lookup = GNUNET_GNS_lookup_with_tld (handle->gns,
322 &handle_gns_response,
329 * Respond to OPTIONS request
331 * @param con_handle the connection handle
333 * @param cls the RequestHandle
336 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
340 struct MHD_Response *resp;
341 struct RequestHandle *handle = cls;
343 //independent of path return all options
344 resp = GNUNET_REST_create_response (NULL);
345 MHD_add_response_header (resp,
346 "Access-Control-Allow-Methods",
348 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
349 GNUNET_SCHEDULER_add_now(&cleanup_handle, handle);
355 * Handle rest request
357 * @param handle the request handle
360 init_cont (struct RequestHandle *handle)
362 struct GNUNET_REST_RequestHandlerError err;
363 static const struct GNUNET_REST_RequestHandler handlers[] = {
364 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_GNS, &get_gns_cont},
365 {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_GNS, &options_cont},
366 GNUNET_REST_HANDLER_END
369 if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle,
374 handle->response_code = err.error_code;
375 GNUNET_SCHEDULER_add_now (&do_error, handle);
381 * Function processing the REST call
383 * @param method HTTP method
384 * @param url URL of the HTTP request
385 * @param data body of the HTTP request (optional)
386 * @param data_size length of the body
387 * @param proc callback function for the result
388 * @param proc_cls closure for callback function
389 * @return GNUNET_OK if request accepted
392 rest_process_request(struct GNUNET_REST_RequestHandle *rest_handle,
393 GNUNET_REST_ResultProcessor proc,
396 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
398 handle->response_code = 0;
399 handle->timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60);
400 handle->proc_cls = proc_cls;
402 handle->rest_handle = rest_handle;
404 handle->url = GNUNET_strdup (rest_handle->url);
405 if (handle->url[strlen (handle->url)-1] == '/')
406 handle->url[strlen (handle->url)-1] = '\0';
407 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
408 handle->gns = GNUNET_GNS_connect (cfg);
411 handle->timeout_task =
412 GNUNET_SCHEDULER_add_delayed (handle->timeout,
416 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
421 * Entry point for the plugin.
423 * @param cls Config info
424 * @return NULL on error, otherwise the plugin context
427 libgnunet_plugin_rest_gns_init (void *cls)
429 static struct Plugin plugin;
430 struct GNUNET_REST_Plugin *api;
433 if (NULL != plugin.cfg)
434 return NULL; /* can only initialize once! */
435 memset (&plugin, 0, sizeof (struct Plugin));
437 api = GNUNET_new (struct GNUNET_REST_Plugin);
439 api->name = GNUNET_REST_API_NS_GNS;
440 api->process_request = &rest_process_request;
441 GNUNET_asprintf (&allow_methods,
442 "%s, %s, %s, %s, %s",
444 MHD_HTTP_METHOD_POST,
446 MHD_HTTP_METHOD_DELETE,
447 MHD_HTTP_METHOD_OPTIONS);
449 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
450 _("Gns REST API initialized\n"));
456 * Exit point from the plugin.
458 * @param cls the plugin context (as returned by "init")
459 * @return always NULL
462 libgnunet_plugin_rest_gns_done (void *cls)
464 struct GNUNET_REST_Plugin *api = cls;
465 struct Plugin *plugin = api->cls;
468 GNUNET_free_non_null (allow_methods);
470 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
471 "Gns REST plugin is finished\n");
475 /* end of plugin_rest_gns.c */