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 Martin Schanzenbach
20 * @author Philippe Buschmann
21 * @file namestore/plugin_rest_namestore.c
22 * @brief GNUnet Namestore REST plugin
26 #include "gnunet_rest_plugin.h"
27 #include "gnunet_gns_service.h"
28 #include "gnunet_namestore_service.h"
29 #include "gnunet_identity_service.h"
30 #include "gnunet_rest_lib.h"
31 #include "gnunet_json_lib.h"
32 #include "microhttpd.h"
36 #define GNUNET_REST_API_NS_NAMESTORE "/namestore"
38 #define GNUNET_REST_SUBSYSTEM_NAMESTORE "namestore"
40 #define GNUNET_REST_NAMESTORE_ERROR_UNKNOWN "Unknown Error"
42 #define GNUNET_REST_NAMESTORE_RD_COUNT 1
45 * The configuration handle
47 const struct GNUNET_CONFIGURATION_Handle *cfg;
50 * HTTP methods allows for this plugin
52 static char* allow_methods;
55 * @brief struct returned by the initialization function of the plugin
59 const struct GNUNET_CONFIGURATION_Handle *cfg;
63 * The default namestore ego
70 const char *identifier;
80 struct GNUNET_IDENTITY_Ego *ego;
94 struct GNUNET_GNSRECORD_Data *rd;
99 struct GNUNET_NAMESTORE_QueueEntry *add_qe;
107 * Handle to NAMESTORE
109 struct GNUNET_NAMESTORE_Handle *ns_handle;
112 * Handle to NAMESTORE it
114 struct GNUNET_NAMESTORE_ZoneIterator *list_it;
117 * Private key for the zone
119 struct GNUNET_CRYPTO_EcdsaPrivateKey zone_pkey;
124 struct EgoEntry *ego_entry;
129 struct GNUNET_IDENTITY_Operation *op;
132 * Handle to Identity service.
134 struct GNUNET_IDENTITY_Handle *identity_handle;
139 struct GNUNET_REST_RequestHandle *rest_handle;
142 * Desired timeout for the lookup (default is no timeout).
144 struct GNUNET_TIME_Relative timeout;
147 * ID of a task associated with the resolution process.
149 struct GNUNET_SCHEDULER_Task *timeout_task;
152 * The plugin result processor
154 GNUNET_REST_ResultProcessor proc;
157 * The closure of the result processor
167 * Error response message
179 * Cleanup lookup handle
180 * @param handle Handle to clean up
183 cleanup_handle (void *cls)
185 struct RequestHandle *handle = cls;
187 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
189 if (NULL != handle->timeout_task)
191 GNUNET_SCHEDULER_cancel (handle->timeout_task);
192 handle->timeout_task = NULL;
194 if (NULL != handle->label_name)
195 GNUNET_free(handle->label_name);
196 if (NULL != handle->url)
197 GNUNET_free(handle->url);
198 if (NULL != handle->emsg)
199 GNUNET_free(handle->emsg);
200 if (NULL != handle->rd)
202 if (NULL != handle->rd->data)
203 GNUNET_free((void*)handle->rd->data);
204 GNUNET_free(handle->rd);
206 if (NULL != handle->timeout_task)
207 GNUNET_SCHEDULER_cancel(handle->timeout_task);
208 if (NULL != handle->list_it)
209 GNUNET_NAMESTORE_zone_iteration_stop(handle->list_it);
210 if (NULL != handle->add_qe)
211 GNUNET_NAMESTORE_cancel(handle->add_qe);
212 if (NULL != handle->identity_handle)
213 GNUNET_IDENTITY_disconnect(handle->identity_handle);
214 if (NULL != handle->ns_handle)
216 GNUNET_NAMESTORE_disconnect(handle->ns_handle);
219 if (NULL != handle->ego_entry)
221 if (NULL != handle->ego_entry->keystring)
222 GNUNET_free(handle->ego_entry->keystring);
224 GNUNET_free(handle->ego_entry);
227 if(NULL != handle->resp_object)
229 json_decref(handle->resp_object);
232 GNUNET_free (handle);
237 * Task run on errors. Reports an error and cleans up everything.
239 * @param cls the `struct RequestHandle`
244 struct RequestHandle *handle = cls;
245 struct MHD_Response *resp;
246 json_t *json_error = json_object();
249 if (NULL == handle->emsg)
250 handle->emsg = GNUNET_strdup(GNUNET_REST_NAMESTORE_ERROR_UNKNOWN);
252 json_object_set_new(json_error,"error", json_string(handle->emsg));
254 if (0 == handle->response_code)
255 handle->response_code = MHD_HTTP_OK;
256 response = json_dumps (json_error, 0);
257 resp = GNUNET_REST_create_response (response);
258 handle->proc (handle->proc_cls, resp, handle->response_code);
259 json_decref(json_error);
260 GNUNET_free(response);
261 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
265 * Does internal server error when iteration failed.
268 namestore_iteration_error (void *cls)
270 struct RequestHandle *handle = cls;
271 struct MHD_Response *resp = GNUNET_REST_create_response (NULL);
272 handle->proc (handle->proc_cls, resp, MHD_HTTP_INTERNAL_SERVER_ERROR);
273 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
277 create_finished (void *cls, int32_t success, const char *emsg)
279 struct RequestHandle *handle = cls;
280 struct MHD_Response *resp;
282 handle->add_qe = NULL;
283 if (GNUNET_YES != success)
285 handle->emsg = GNUNET_strdup("Error storing records");
286 GNUNET_SCHEDULER_add_now (&do_error, handle);
289 resp = GNUNET_REST_create_response (NULL);
290 handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
291 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
295 del_finished (void *cls, int32_t success, const char *emsg)
297 struct RequestHandle *handle = cls;
299 handle->add_qe = NULL;
300 if (GNUNET_NO == success)
302 handle->emsg = GNUNET_strdup("Deleting record failed. Record does not exist");
303 GNUNET_SCHEDULER_add_now (&do_error, handle);
306 if (GNUNET_SYSERR == success)
308 handle->emsg = GNUNET_strdup("Deleting record failed");
309 GNUNET_SCHEDULER_add_now (&do_error, handle);
312 handle->proc (handle->proc_cls,
313 GNUNET_REST_create_response (NULL),
314 MHD_HTTP_NO_CONTENT);
315 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
318 * Iteration over all results finished, build final
321 * @param cls the `struct RequestHandle`
324 namestore_list_finished (void *cls)
326 struct RequestHandle *handle = cls;
328 struct MHD_Response *resp;
330 handle->list_it = NULL;
332 if (NULL == handle->resp_object)
334 GNUNET_SCHEDULER_add_now (&do_error, handle);
338 result_str = json_dumps (handle->resp_object, 0);
339 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
340 resp = GNUNET_REST_create_response (result_str);
341 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
342 GNUNET_free_non_null (result_str);
343 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
349 * Create a response with requested records
351 * @param handle the RequestHandle
354 namestore_list_iteration (void *cls,
355 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
358 const struct GNUNET_GNSRECORD_Data *rd)
360 struct RequestHandle *handle = cls;
363 if (NULL == handle->resp_object)
364 handle->resp_object = json_array();
366 /*if ( (NULL != handle->ego_entry->identifier) &&
367 (0 != strcmp (handle->ego_entry->identifier,
370 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
371 "%s does not match %s\n", rname,
372 handle->ego_entry->identifier);
373 GNUNET_NAMESTORE_zone_iterator_next (handle->list_it, 1);
377 for (unsigned int i = 0; i < rd_len; i++)
379 if ( (GNUNET_GNSRECORD_TYPE_NICK == rd[i].record_type) &&
380 (0 != strcmp (rname, GNUNET_GNS_EMPTY_LABEL_AT)) )
383 record_obj = GNUNET_JSON_from_gns_record(rname,rd);
385 if(NULL == record_obj)
388 json_array_append (handle->resp_object, record_obj);
389 json_decref (record_obj);
392 GNUNET_NAMESTORE_zone_iterator_next (handle->list_it, 1);
397 * Handle namestore GET request
399 * @param con_handle the connection handle
401 * @param cls the RequestHandle
404 namestore_get (struct GNUNET_REST_RequestHandle *con_handle,
408 struct RequestHandle *handle = cls;
409 if (strlen (GNUNET_REST_API_NS_NAMESTORE) != strlen (handle->url))
411 handle->emsg = GNUNET_strdup("Wrong URL");
412 GNUNET_SCHEDULER_add_now (&do_error, handle);
415 handle->list_it = GNUNET_NAMESTORE_zone_iteration_start (handle->ns_handle,
417 &namestore_iteration_error,
419 &namestore_list_iteration,
421 &namestore_list_finished,
427 * We're storing a new record; this requires
428 * that no record already exists
430 * @param cls closure, unused
431 * @param zone_key private key of the zone
432 * @param rec_name name that is being mapped (at most 255 characters long)
433 * @param rd_count number of entries in @a rd array
434 * @param rd array of records with data to store
437 create_new_record_cont (void *cls,
438 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
439 const char *rec_name,
440 unsigned int rd_count,
441 const struct GNUNET_GNSRECORD_Data *rd)
443 struct RequestHandle *handle = cls;
445 handle->add_qe = NULL;
446 if (0 != strcmp (rec_name, handle->label_name))
449 GNUNET_SCHEDULER_add_now (&do_error, handle);
455 handle->proc (handle->proc_cls,
456 GNUNET_REST_create_response (NULL),
458 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
461 handle->add_qe = GNUNET_NAMESTORE_records_store (handle->ns_handle,
464 GNUNET_REST_NAMESTORE_RD_COUNT,
471 * Handle namestore POST request
473 * @param con_handle the connection handle
475 * @param cls the RequestHandle
478 namestore_add (struct GNUNET_REST_RequestHandle *con_handle,
482 struct RequestHandle *handle = cls;
483 struct GNUNET_GNSRECORD_Data *gns_record;
487 char term_data[handle->rest_handle->data_size + 1];
488 struct GNUNET_JSON_Specification gnsspec[] = {
489 GNUNET_JSON_spec_gnsrecord_data(&gns_record),
490 GNUNET_JSON_spec_end ()
493 if (strlen (GNUNET_REST_API_NS_NAMESTORE) != strlen (handle->url))
495 handle->emsg = GNUNET_strdup("Wrong URL");
496 GNUNET_SCHEDULER_add_now (&do_error, handle);
499 if (0 >= handle->rest_handle->data_size)
501 handle->emsg = GNUNET_strdup("No data");
502 GNUNET_SCHEDULER_add_now (&do_error, handle);
505 term_data[handle->rest_handle->data_size] = '\0';
506 GNUNET_memcpy(term_data, handle->rest_handle->data,
507 handle->rest_handle->data_size);
508 data_js = json_loads (term_data, JSON_DECODE_ANY, &err);
509 GNUNET_assert (GNUNET_OK == GNUNET_JSON_parse (data_js, gnsspec, NULL, NULL));
510 name_json = json_object_get(data_js, "label");
511 if (!json_is_string(name_json))
513 handle->emsg = GNUNET_strdup("Missing name");
514 GNUNET_SCHEDULER_add_now (&do_error, handle);
517 handle->label_name = GNUNET_strdup(json_string_value(name_json));
518 if(NULL == handle->label_name)
520 handle->emsg = GNUNET_strdup("Missing name");
521 GNUNET_SCHEDULER_add_now (&do_error, handle);
524 json_decref (data_js);
525 handle->rd = gns_record;
526 handle->add_qe = GNUNET_NAMESTORE_records_lookup (handle->ns_handle,
531 &create_new_record_cont,
538 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
540 unsigned int rd_count,
541 const struct GNUNET_GNSRECORD_Data *rd)
543 struct RequestHandle *handle = cls;
545 handle->add_qe = NULL;
548 handle->emsg = GNUNET_strdup("Record not found");
549 GNUNET_SCHEDULER_add_now (&do_error, handle);
553 handle->add_qe = GNUNET_NAMESTORE_records_store (handle->ns_handle,
562 * Handle namestore DELETE request
564 * @param con_handle the connection handle
566 * @param cls the RequestHandle
569 namestore_delete (struct GNUNET_REST_RequestHandle *con_handle,
573 struct RequestHandle *handle = cls;
574 struct GNUNET_HashCode key;
576 GNUNET_CRYPTO_hash ("label", strlen ("label"), &key);
578 == GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map,
581 handle->emsg = GNUNET_strdup("Missing name");
582 GNUNET_SCHEDULER_add_now (&do_error, handle);
585 handle->label_name = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map,
588 handle->add_qe = GNUNET_NAMESTORE_records_lookup (handle->ns_handle,
601 * Respond to OPTIONS request
603 * @param con_handle the connection handle
605 * @param cls the RequestHandle
608 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
612 struct MHD_Response *resp;
613 struct RequestHandle *handle = cls;
615 //independent of path return all options
616 resp = GNUNET_REST_create_response (NULL);
617 MHD_add_response_header (resp,
618 "Access-Control-Allow-Methods",
620 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
621 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
627 * Handle rest request
629 * @param handle the request handle
632 init_cont (struct RequestHandle *handle)
634 struct GNUNET_REST_RequestHandlerError err;
635 static const struct GNUNET_REST_RequestHandler handlers[] = {
636 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_NAMESTORE, &namestore_get},
637 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_NAMESTORE, &namestore_add},
638 {MHD_HTTP_METHOD_DELETE, GNUNET_REST_API_NS_NAMESTORE, &namestore_delete},
639 {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_NAMESTORE, &options_cont},
640 GNUNET_REST_HANDLER_END
643 if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle,
648 handle->response_code = err.error_code;
649 GNUNET_SCHEDULER_add_now (&do_error, handle);
657 default_ego_cb (void *cls,
658 struct GNUNET_IDENTITY_Ego *ego,
662 struct RequestHandle *handle = cls;
663 struct EgoEntry *ego_entry;
664 struct GNUNET_CRYPTO_EcdsaPublicKey pk;
670 handle->emsg = GNUNET_strdup ("No default ego configured in identity service");
671 GNUNET_SCHEDULER_add_now (&do_error, handle);
674 ego_entry = GNUNET_new(struct EgoEntry);
675 GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
676 ego_entry->keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
677 ego_entry->identifier = name;
678 ego_entry->ego = ego;
679 handle->ego_entry = ego_entry;
680 handle->zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
686 * Connect to identity callback
689 id_connect_cb (void *cls,
690 struct GNUNET_IDENTITY_Ego *ego,
694 struct RequestHandle *handle = cls;
697 handle->op = GNUNET_IDENTITY_get (handle->identity_handle,
698 GNUNET_REST_SUBSYSTEM_NAMESTORE,
706 * Function processing the REST call
708 * @param method HTTP method
709 * @param url URL of the HTTP request
710 * @param data body of the HTTP request (optional)
711 * @param data_size length of the body
712 * @param proc callback function for the result
713 * @param proc_cls closure for callback function
714 * @return GNUNET_OK if request accepted
717 rest_process_request(struct GNUNET_REST_RequestHandle *rest_handle,
718 GNUNET_REST_ResultProcessor proc,
721 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
723 handle->response_code = 0;
724 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
725 handle->proc_cls = proc_cls;
727 handle->rest_handle = rest_handle;
729 handle->url = GNUNET_strdup (rest_handle->url);
730 if (handle->url[strlen (handle->url)-1] == '/')
731 handle->url[strlen (handle->url)-1] = '\0';
732 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
734 handle->identity_handle = GNUNET_IDENTITY_connect (cfg, &id_connect_cb, handle);
735 handle->ns_handle = GNUNET_NAMESTORE_connect (cfg);
736 handle->timeout_task =
737 GNUNET_SCHEDULER_add_delayed (handle->timeout,
741 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
746 * Entry point for the plugin.
748 * @param cls Config info
749 * @return NULL on error, otherwise the plugin context
752 libgnunet_plugin_rest_namestore_init (void *cls)
754 static struct Plugin plugin;
755 struct GNUNET_REST_Plugin *api;
758 if (NULL != plugin.cfg)
759 return NULL; /* can only initialize once! */
760 memset (&plugin, 0, sizeof (struct Plugin));
762 api = GNUNET_new (struct GNUNET_REST_Plugin);
764 api->name = GNUNET_REST_API_NS_NAMESTORE;
765 api->process_request = &rest_process_request;
766 GNUNET_asprintf (&allow_methods,
767 "%s, %s, %s, %s, %s",
769 MHD_HTTP_METHOD_POST,
771 MHD_HTTP_METHOD_DELETE,
772 MHD_HTTP_METHOD_OPTIONS);
774 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
775 _("Namestore REST API initialized\n"));
781 * Exit point from the plugin.
783 * @param cls the plugin context (as returned by "init")
784 * @return always NULL
787 libgnunet_plugin_rest_namestore_done (void *cls)
789 struct GNUNET_REST_Plugin *api = cls;
790 struct Plugin *plugin = api->cls;
793 GNUNET_free_non_null (allow_methods);
795 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
796 "Namestore REST plugin is finished\n");
800 /* end of plugin_rest_namestore.c */