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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
21 * @author Philippe Buschmann
22 * @file gns/plugin_rest_gns.c
23 * @brief GNUnet Gns REST plugin
27 #include "gnunet_rest_plugin.h"
28 #include "gnunet_rest_lib.h"
29 #include "gnunet_json_lib.h"
30 #include "gnunet_gnsrecord_lib.h"
31 #include "gnunet_gns_service.h"
32 #include "microhttpd.h"
36 * Rest API GNS Namespace
38 #define GNUNET_REST_API_NS_GNS "/gns"
41 * Rest API GNS Parameter record_type
43 #define GNUNET_REST_GNS_PARAM_RECORD_TYPE "record_type"
46 * Rest API GNS ERROR Unknown Error
48 #define GNUNET_REST_GNS_ERROR_UNKNOWN "Unknown Error"
51 * Rest API GNS ERROR Record not found
53 #define GNUNET_REST_GNS_NOT_FOUND "Record not found"
56 * The configuration handle
58 const struct GNUNET_CONFIGURATION_Handle *cfg;
61 * HTTP methods allows for this plugin
63 static char *allow_methods;
66 * @brief struct returned by the initialization function of the plugin
69 const struct GNUNET_CONFIGURATION_Handle *cfg;
75 struct RequestHandle {
79 struct GNUNET_GNS_Handle *gns;
84 struct GNUNET_GNS_LookupWithTldRequest *gns_lookup;
92 * Record type to look up
99 struct GNUNET_REST_RequestHandle *rest_handle;
102 * Desired timeout for the lookup (default is no timeout).
104 struct GNUNET_TIME_Relative timeout;
107 * ID of a task associated with the resolution process.
109 struct GNUNET_SCHEDULER_Task *timeout_task;
112 * The plugin result processor
114 GNUNET_REST_ResultProcessor proc;
117 * The closure of the result processor
127 * Error response message
139 * Cleanup lookup handle
140 * @param handle Handle to clean up
143 cleanup_handle(void *cls)
145 struct RequestHandle *handle = cls;
147 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
149 if (NULL != handle->gns_lookup)
151 GNUNET_GNS_lookup_with_tld_cancel(handle->gns_lookup);
152 handle->gns_lookup = NULL;
154 if (NULL != handle->gns)
156 GNUNET_GNS_disconnect(handle->gns);
160 if (NULL != handle->timeout_task)
162 GNUNET_SCHEDULER_cancel(handle->timeout_task);
163 handle->timeout_task = NULL;
165 if (NULL != handle->url)
166 GNUNET_free(handle->url);
167 if (NULL != handle->name)
168 GNUNET_free(handle->name);
169 if (NULL != handle->emsg)
170 GNUNET_free(handle->emsg);
177 * Task run on errors. Reports an error and cleans up everything.
179 * @param cls the `struct RequestHandle`
184 struct RequestHandle *handle = cls;
185 struct MHD_Response *resp;
186 json_t *json_error = json_object();
189 if (NULL != handle->timeout_task)
190 GNUNET_SCHEDULER_cancel(handle->timeout_task);
191 handle->timeout_task = NULL;
192 if (NULL == handle->emsg)
193 handle->emsg = GNUNET_strdup(GNUNET_REST_GNS_ERROR_UNKNOWN);
195 json_object_set_new(json_error, "error", json_string(handle->emsg));
197 if (0 == handle->response_code)
198 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
199 response = json_dumps(json_error, 0);
200 resp = GNUNET_REST_create_response(response);
201 handle->proc(handle->proc_cls, resp, handle->response_code);
202 json_decref(json_error);
203 GNUNET_free(response);
204 GNUNET_SCHEDULER_add_now(&cleanup_handle, handle);
209 do_timeout(void *cls)
211 struct RequestHandle *handle = cls;
213 handle->timeout_task = NULL;
214 handle->response_code = MHD_HTTP_REQUEST_TIMEOUT;
220 * Iterator called on obtained result for a GNS lookup.
222 * @param cls closure with the object
223 * @param was_gns #GNUNET_NO if name was not a GNS name
224 * @param rd_count number of records in @a rd
225 * @param rd the records in reply
228 handle_gns_response(void *cls,
231 const struct GNUNET_GNSRECORD_Data *rd)
233 struct RequestHandle *handle = cls;
234 struct MHD_Response *resp;
238 handle->gns_lookup = NULL;
240 if (GNUNET_NO == was_gns)
242 handle->response_code = MHD_HTTP_NOT_FOUND;
243 handle->emsg = GNUNET_strdup(GNUNET_REST_GNS_NOT_FOUND);
244 GNUNET_SCHEDULER_add_now(&do_error, handle);
248 result_obj = GNUNET_JSON_from_gnsrecord(handle->name, rd, rd_count);
250 result = json_dumps(result_obj, 0);
251 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result);
252 resp = GNUNET_REST_create_response(result);
253 handle->proc(handle->proc_cls, resp, MHD_HTTP_OK);
255 json_decref(result_obj);
256 GNUNET_SCHEDULER_add_now(&cleanup_handle, handle);
261 * Handle gns GET request
263 * @param con_handle the connection handle
265 * @param cls the RequestHandle
268 get_gns_cont(struct GNUNET_REST_RequestHandle *con_handle,
272 struct RequestHandle *handle = cls;
273 struct GNUNET_HashCode key;
279 if (strlen(GNUNET_REST_API_NS_GNS) < strlen(handle->url))
281 name = &handle->url[strlen(GNUNET_REST_API_NS_GNS) + 1];
286 handle->response_code = MHD_HTTP_NOT_FOUND;
287 handle->emsg = GNUNET_strdup(GNUNET_REST_GNS_NOT_FOUND);
288 GNUNET_SCHEDULER_add_now(&do_error, handle);
291 if (0 >= strlen(name))
293 handle->response_code = MHD_HTTP_NOT_FOUND;
294 handle->emsg = GNUNET_strdup(GNUNET_REST_GNS_NOT_FOUND);
295 GNUNET_SCHEDULER_add_now(&do_error, handle);
298 handle->name = GNUNET_strdup(name);
300 handle->record_type = UINT32_MAX;
301 GNUNET_CRYPTO_hash(GNUNET_REST_GNS_PARAM_RECORD_TYPE,
302 strlen(GNUNET_REST_GNS_PARAM_RECORD_TYPE),
305 GNUNET_CONTAINER_multihashmap_contains(con_handle->url_param_map, &key))
308 GNUNET_CONTAINER_multihashmap_get(con_handle->url_param_map, &key);
309 handle->record_type = GNUNET_GNSRECORD_typename_to_number(record_type);
312 if (UINT32_MAX == handle->record_type)
314 handle->record_type = GNUNET_GNSRECORD_TYPE_ANY;
317 handle->gns_lookup = GNUNET_GNS_lookup_with_tld(handle->gns,
321 &handle_gns_response,
327 * Respond to OPTIONS request
329 * @param con_handle the connection handle
331 * @param cls the RequestHandle
334 options_cont(struct GNUNET_REST_RequestHandle *con_handle,
338 struct MHD_Response *resp;
339 struct RequestHandle *handle = cls;
341 //independent of path return all options
342 resp = GNUNET_REST_create_response(NULL);
343 MHD_add_response_header(resp, "Access-Control-Allow-Methods", allow_methods);
344 handle->proc(handle->proc_cls, resp, MHD_HTTP_OK);
345 GNUNET_SCHEDULER_add_now(&cleanup_handle, handle);
351 * Handle rest request
353 * @param handle the request handle
356 init_cont(struct RequestHandle *handle)
358 struct GNUNET_REST_RequestHandlerError err;
359 static const struct GNUNET_REST_RequestHandler handlers[] =
360 { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_GNS, &get_gns_cont },
361 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_GNS, &options_cont },
362 GNUNET_REST_HANDLER_END };
365 GNUNET_REST_handle_request(handle->rest_handle, handlers, &err, handle))
367 handle->response_code = err.error_code;
368 GNUNET_SCHEDULER_add_now(&do_error, handle);
374 * Function processing the REST call
376 * @param method HTTP method
377 * @param url URL of the HTTP request
378 * @param data body of the HTTP request (optional)
379 * @param data_size length of the body
380 * @param proc callback function for the result
381 * @param proc_cls closure for callback function
382 * @return GNUNET_OK if request accepted
385 rest_process_request(struct GNUNET_REST_RequestHandle *rest_handle,
386 GNUNET_REST_ResultProcessor proc,
389 struct RequestHandle *handle = GNUNET_new(struct RequestHandle);
391 handle->response_code = 0;
393 GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 60);
394 handle->proc_cls = proc_cls;
396 handle->rest_handle = rest_handle;
398 handle->url = GNUNET_strdup(rest_handle->url);
399 if (handle->url[strlen(handle->url) - 1] == '/')
400 handle->url[strlen(handle->url) - 1] = '\0';
401 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
402 handle->gns = GNUNET_GNS_connect(cfg);
405 handle->timeout_task =
406 GNUNET_SCHEDULER_add_delayed(handle->timeout, &do_timeout, handle);
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, _("Gns REST API initialized\n"));
447 * Exit point from the plugin.
449 * @param cls the plugin context (as returned by "init")
450 * @return always NULL
453 libgnunet_plugin_rest_gns_done(void *cls)
455 struct GNUNET_REST_Plugin *api = cls;
456 struct Plugin *plugin = api->cls;
460 GNUNET_free_non_null(allow_methods);
462 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Gns REST plugin is finished\n");
466 /* end of plugin_rest_gns.c */