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"
33 #define GNUNET_REST_API_NS_GNS "/gns"
36 #define GNUNET_REST_GNS_PARAM_NAME "name"
38 #define GNUNET_REST_GNS_PARAM_RECORD_TYPE "record_type"
39 #define GNUNET_REST_GNS_ERROR_UNKNOWN "Unknown Error"
42 * The configuration handle
44 const struct GNUNET_CONFIGURATION_Handle *cfg;
47 * HTTP methods allows for this plugin
49 static char* allow_methods;
52 * @brief struct returned by the initialization function of the plugin
56 const struct GNUNET_CONFIGURATION_Handle *cfg;
66 struct GNUNET_GNS_Handle *gns;
71 struct GNUNET_GNS_LookupWithTldRequest *gns_lookup;
79 * Record type to look up
86 struct GNUNET_REST_RequestHandle *rest_handle;
89 * Desired timeout for the lookup (default is no timeout).
91 struct GNUNET_TIME_Relative timeout;
94 * ID of a task associated with the resolution process.
96 struct GNUNET_SCHEDULER_Task *timeout_task;
99 * The plugin result processor
101 GNUNET_REST_ResultProcessor proc;
104 * The closure of the result processor
114 * Error response message
127 * Cleanup lookup handle
128 * @param handle Handle to clean up
131 cleanup_handle (void *cls)
133 struct RequestHandle *handle = cls;
134 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
137 if (NULL != handle->gns_lookup)
139 GNUNET_GNS_lookup_with_tld_cancel (handle->gns_lookup);
140 handle->gns_lookup = NULL;
142 if (NULL != handle->gns)
144 GNUNET_GNS_disconnect (handle->gns);
148 if (NULL != handle->timeout_task)
150 GNUNET_SCHEDULER_cancel (handle->timeout_task);
151 handle->timeout_task = NULL;
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);
160 GNUNET_free (handle);
165 * Task run on errors. Reports an error and cleans up everything.
167 * @param cls the `struct RequestHandle`
172 struct RequestHandle *handle = cls;
173 struct MHD_Response *resp;
174 json_t *json_error = json_object();
177 if (NULL == handle->emsg)
178 handle->emsg = GNUNET_strdup(GNUNET_REST_GNS_ERROR_UNKNOWN);
180 json_object_set_new(json_error,"error", json_string(handle->emsg));
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);
194 * Iterator called on obtained result for a GNS lookup.
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
202 handle_gns_response (void *cls,
205 const struct GNUNET_GNSRECORD_Data *rd)
207 struct RequestHandle *handle = cls;
208 struct MHD_Response *resp;
209 json_t *result_array;
213 handle->gns_lookup = NULL;
215 if (GNUNET_NO == was_gns)
217 handle->emsg = GNUNET_strdup("Name not found in GNS");
218 GNUNET_SCHEDULER_add_now (&do_error, handle);
222 result_array = json_array();
223 for (uint32_t i=0;i<rd_count;i++)
225 if ((rd[i].record_type != handle->record_type) &&
226 (GNUNET_GNSRECORD_TYPE_ANY != handle->record_type) )
231 record_obj = GNUNET_JSON_from_gns_record(NULL,&rd[i]);
232 json_array_append (result_array, record_obj);
233 json_decref (record_obj);
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);
247 * Handle gns GET request
249 * @param con_handle the connection handle
251 * @param cls the RequestHandle
254 get_gns_cont (struct GNUNET_REST_RequestHandle *con_handle,
258 struct RequestHandle *handle = cls;
259 struct GNUNET_HashCode key;
263 GNUNET_CRYPTO_hash (GNUNET_REST_GNS_PARAM_NAME,
264 strlen (GNUNET_REST_GNS_PARAM_NAME),
267 == GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map,
270 handle->emsg = GNUNET_strdup("Parameter name is missing");
271 GNUNET_SCHEDULER_add_now (&do_error, handle);
274 name = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map,&key);
275 if(0 >= strlen (name))
277 handle->emsg = GNUNET_strdup("Length of parameter name is zero");
278 GNUNET_SCHEDULER_add_now (&do_error, handle);
281 handle->name = GNUNET_strdup(name);
283 handle->record_type = UINT32_MAX;
284 GNUNET_CRYPTO_hash (GNUNET_REST_GNS_PARAM_RECORD_TYPE,
285 strlen (GNUNET_REST_GNS_PARAM_RECORD_TYPE),
288 == GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map,
291 record_type = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map, &key);
292 handle->record_type = GNUNET_GNSRECORD_typename_to_number(record_type);
296 if(UINT32_MAX == handle->record_type)
298 handle->record_type = GNUNET_GNSRECORD_TYPE_ANY;
301 handle->gns = GNUNET_GNS_connect (cfg);
302 if (NULL == handle->gns)
304 handle->emsg = GNUNET_strdup ("GNS not available");
305 GNUNET_SCHEDULER_add_now (&do_error, handle);
309 handle->gns_lookup = GNUNET_GNS_lookup_with_tld (handle->gns,
313 &handle_gns_response,
321 * Respond to OPTIONS request
323 * @param con_handle the connection handle
325 * @param cls the RequestHandle
328 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
332 struct MHD_Response *resp;
333 struct RequestHandle *handle = cls;
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",
340 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
341 GNUNET_SCHEDULER_add_now(&cleanup_handle, handle);
347 * Handle rest request
349 * @param handle the request handle
352 init_cont (struct RequestHandle *handle)
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
361 if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle,
366 handle->response_code = err.error_code;
367 GNUNET_SCHEDULER_add_now (&do_error, handle);
373 * Function processing the REST call
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
384 rest_process_request(struct GNUNET_REST_RequestHandle *rest_handle,
385 GNUNET_REST_ResultProcessor proc,
388 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
390 handle->response_code = 0;
391 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
392 handle->proc_cls = proc_cls;
394 handle->rest_handle = rest_handle;
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");
403 handle->timeout_task =
404 GNUNET_SCHEDULER_add_delayed (handle->timeout,
408 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
413 * Entry point for the plugin.
415 * @param cls Config info
416 * @return NULL on error, otherwise the plugin context
419 libgnunet_plugin_rest_gns_init (void *cls)
421 static struct Plugin plugin;
422 struct GNUNET_REST_Plugin *api;
425 if (NULL != plugin.cfg)
426 return NULL; /* can only initialize once! */
427 memset (&plugin, 0, sizeof (struct Plugin));
429 api = GNUNET_new (struct GNUNET_REST_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",
436 MHD_HTTP_METHOD_POST,
438 MHD_HTTP_METHOD_DELETE,
439 MHD_HTTP_METHOD_OPTIONS);
441 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
442 _("Gns REST API initialized\n"));
448 * Exit point from the plugin.
450 * @param cls the plugin context (as returned by "init")
451 * @return always NULL
454 libgnunet_plugin_rest_gns_done (void *cls)
456 struct GNUNET_REST_Plugin *api = cls;
457 struct Plugin *plugin = api->cls;
460 GNUNET_free_non_null (allow_methods);
462 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
463 "Gns REST plugin is finished\n");
467 /* end of plugin_rest_gns.c */