2 This file is part of GNUnet.
3 Copyright (C) 2012-2015 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 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 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
21 * @author Martin Schanzenbach
22 * @file identity/plugin_rest_identity.c
23 * @brief GNUnet Namestore REST plugin
28 #include "gnunet_rest_plugin.h"
29 #include "gnunet_identity_service.h"
30 #include "gnunet_rest_lib.h"
31 #include "microhttpd.h"
34 #define GNUNET_REST_API_NS_IDENTITY "/identity"
36 #define ID_REST_STATE_INIT 0
38 #define ID_REST_STATE_POST_INIT 1
40 #define URL_PARAM_SUBSYS "service"
42 #define GNUNET_REST_JSONAPI_IDENTITY_EGO "ego"
44 #define GNUNET_REST_JSONAPI_IDENTITY_KEY "key"
46 #define GNUNET_REST_JSONAPI_IDENTITY_NEWNAME "newname"
48 #define GNUNET_REST_JSONAPI_IDENTITY_SUBSYSTEM "subsystem"
51 * @brief struct returned by the initialization function of the plugin
55 const struct GNUNET_CONFIGURATION_Handle *cfg;
58 const struct GNUNET_CONFIGURATION_Handle *cfg;
65 struct EgoEntry *next;
70 struct EgoEntry *prev;
80 struct GNUNET_CRYPTO_EcdsaPublicKey pk;
85 struct GNUNET_IDENTITY_Ego *ego;
93 struct EgoEntry *ego_head;
98 struct EgoEntry *ego_tail;
100 struct RestConnectionDataHandle *conndata_handle;
103 * The processing state
108 * Handle to GNS service.
110 struct GNUNET_IDENTITY_Handle *identity_handle;
115 struct GNUNET_IDENTITY_Operation *op;
118 * Desired timeout for the lookup (default is no timeout).
120 struct GNUNET_TIME_Relative timeout;
123 * ID of a task associated with the resolution process.
125 struct GNUNET_SCHEDULER_Task * timeout_task;
128 * The root of the received JSON or NULL
133 * The plugin result processor
135 GNUNET_REST_ResultProcessor proc;
138 * The closure of the result processor
143 * The name to look up
148 * The subsystem set from REST
158 * The data from the REST request
163 * the length of the REST data
176 * Cleanup lookup handle
177 * @praram handle Handle to clean up
180 cleanup_handle (struct RequestHandle *handle)
182 struct EgoEntry *ego_entry;
183 struct EgoEntry *ego_tmp;
184 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
186 if (NULL != handle->name)
187 GNUNET_free (handle->name);
188 if (NULL != handle->timeout_task)
189 GNUNET_SCHEDULER_cancel (handle->timeout_task);
190 if (NULL != handle->identity_handle)
191 GNUNET_IDENTITY_disconnect (handle->identity_handle);
192 if (NULL != handle->subsys)
193 GNUNET_free (handle->subsys);
194 if (NULL != handle->url)
195 GNUNET_free (handle->url);
196 for (ego_entry = handle->ego_head;
200 ego_entry = ego_entry->next;
201 GNUNET_free (ego_tmp->identifier);
202 GNUNET_free (ego_tmp);
204 GNUNET_free (handle);
209 * Task run on shutdown. Cleans up everything.
212 * @param tc scheduler context
216 const struct GNUNET_SCHEDULER_TaskContext *tc)
218 struct RequestHandle *handle = cls;
219 struct MHD_Response *resp = GNUNET_REST_create_json_response (NULL);
220 handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
221 cleanup_handle (handle);
225 * Callback for IDENTITY_get()
227 * @param cls the RequestHandle
228 * @param ego the Ego found
229 * @param ctx the context
230 * @param name the id of the ego
233 get_ego_for_subsys (void *cls,
234 struct GNUNET_IDENTITY_Ego *ego,
238 struct RequestHandle *handle = cls;
239 struct JsonApiObject *json_object;
240 struct JsonApiResource *json_resource;
241 struct EgoEntry *ego_entry;
242 struct MHD_Response *resp;
247 json_object = GNUNET_REST_jsonapi_object_new ();
249 for (ego_entry = handle->ego_head;
251 ego_entry = ego_entry->next)
253 if ( (NULL != name) && (0 != strcmp (name, ego_entry->identifier)) )
257 json_resource = GNUNET_REST_jsonapi_resource_new (GNUNET_REST_JSONAPI_IDENTITY_EGO, ego_entry->identifier);
258 keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&ego_entry->pk);
259 key_json = json_string (keystring);
260 GNUNET_REST_jsonapi_resource_add_attr (json_resource, GNUNET_REST_JSONAPI_IDENTITY_KEY, key_json);
261 json_decref (key_json);
262 GNUNET_free (keystring);
263 GNUNET_REST_jsonapi_object_resource_add (json_object, json_resource);
266 if (0 == GNUNET_REST_jsonapi_object_resource_count (json_object))
268 GNUNET_REST_jsonapi_object_delete (json_object);
269 GNUNET_SCHEDULER_add_now (&do_error, handle);
272 GNUNET_REST_jsonapi_data_serialize (json_object, &result_str);
273 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
274 resp = GNUNET_REST_create_json_response (result_str);
275 GNUNET_REST_jsonapi_object_delete (json_object);
276 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
277 GNUNET_free (result_str);
278 cleanup_handle (handle);
282 * Create a response with requested ego(s)
284 * @param handle the RequestHandle
287 ego_info_response (struct RestConnectionDataHandle *con,
295 struct RequestHandle *handle = cls;
296 struct EgoEntry *ego_entry;
297 struct GNUNET_HashCode key;
298 struct MHD_Response *resp;
299 struct JsonApiObject *json_object;
300 struct JsonApiResource *json_resource;
302 if (GNUNET_NO == GNUNET_REST_namespace_match (handle->url, GNUNET_REST_API_NS_IDENTITY))
304 resp = GNUNET_REST_create_json_response (NULL);
305 handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
306 cleanup_handle (handle);
309 if ( (strlen (GNUNET_REST_API_NS_IDENTITY) == strlen (handle->url) )) {
310 GNUNET_CRYPTO_hash (URL_PARAM_SUBSYS, strlen (URL_PARAM_SUBSYS), &key);
312 GNUNET_CONTAINER_multihashmap_contains (handle->conndata_handle->url_param_map,
315 subsys_val = GNUNET_CONTAINER_multihashmap_get (handle->conndata_handle->url_param_map,
317 if (NULL != subsys_val)
319 GNUNET_asprintf (&handle->subsys, "%s", subsys_val);
320 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Looking for %s's ego\n", subsys_val);
321 handle->op = GNUNET_IDENTITY_get (handle->identity_handle,
329 json_object = GNUNET_REST_jsonapi_object_new ();
330 egoname = &handle->url[strlen (GNUNET_REST_API_NS_IDENTITY)+1];
332 if (strlen (GNUNET_REST_API_NS_IDENTITY) == strlen (handle->url))
337 for (ego_entry = handle->ego_head;
339 ego_entry = ego_entry->next)
341 if ( (NULL != egoname) && (0 != strcmp (egoname, ego_entry->identifier)) )
343 keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&ego_entry->pk);
344 json_resource = GNUNET_REST_jsonapi_resource_new (GNUNET_REST_JSONAPI_IDENTITY_EGO, ego_entry->identifier);
345 key_str = json_string (keystring);
346 GNUNET_REST_jsonapi_resource_add_attr (json_resource,
347 GNUNET_REST_JSONAPI_IDENTITY_KEY,
349 json_decref (key_str);
350 GNUNET_free (keystring);
351 GNUNET_REST_jsonapi_object_resource_add (json_object, json_resource);
353 if (0 == GNUNET_REST_jsonapi_object_resource_count (json_object))
355 GNUNET_REST_jsonapi_object_delete (json_object);
356 GNUNET_SCHEDULER_add_now (&do_error, handle);
359 GNUNET_REST_jsonapi_data_serialize (json_object, &result_str);
360 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
361 resp = GNUNET_REST_create_json_response (result_str);
362 GNUNET_REST_jsonapi_object_delete (json_object);
363 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
364 GNUNET_free (result_str);
365 cleanup_handle (handle);
369 do_finished (void *cls, const char *emsg)
371 struct RequestHandle *handle = cls;
372 struct MHD_Response *resp;
377 GNUNET_SCHEDULER_add_now (&do_error, handle);
380 resp = GNUNET_REST_create_json_response (NULL);
381 handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
382 cleanup_handle (handle);
386 edit_finished (void *cls, const char *emsg)
388 struct RequestHandle *handle = cls;
389 struct MHD_Response *resp;
394 GNUNET_SCHEDULER_add_now (&do_error, handle);
397 resp = GNUNET_REST_create_json_response (NULL);
398 handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
399 cleanup_handle (handle);
403 create_finished (void *cls, const char *emsg)
405 struct RequestHandle *handle = cls;
406 struct MHD_Response *resp;
411 GNUNET_SCHEDULER_add_now (&do_error, handle);
414 resp = GNUNET_REST_create_json_response (NULL);
415 handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
416 cleanup_handle (handle);
420 ego_create_cont (struct RestConnectionDataHandle *con,
424 struct RequestHandle *handle = cls;
425 struct EgoEntry *ego_entry;
426 struct MHD_Response *resp;
427 struct JsonApiObject *json_obj;
428 struct JsonApiResource *json_res;
429 json_t *egoname_json;
430 char term_data[handle->data_size+1];
432 if (strlen (GNUNET_REST_API_NS_IDENTITY) != strlen (handle->url))
434 GNUNET_SCHEDULER_add_now (&do_error, handle);
437 if (0 >= handle->data_size)
439 GNUNET_SCHEDULER_add_now (&do_error, handle);
442 term_data[handle->data_size] = '\0';
443 memcpy (term_data, handle->data, handle->data_size);
444 json_obj = GNUNET_REST_jsonapi_object_parse (term_data);
445 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "2\n");
446 if (NULL == json_obj)
448 GNUNET_SCHEDULER_add_now (&do_error, handle);
451 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "2.1\n");
452 if (1 != GNUNET_REST_jsonapi_object_resource_count (json_obj))
454 GNUNET_REST_jsonapi_object_delete (json_obj);
455 GNUNET_SCHEDULER_add_now (&do_error, handle);
458 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "3\n");
459 json_res = GNUNET_REST_jsonapi_object_get_resource (json_obj, 0);
460 if (GNUNET_NO == GNUNET_REST_jsonapi_resource_check_type (json_res, GNUNET_REST_JSONAPI_IDENTITY_EGO))
462 GNUNET_REST_jsonapi_object_delete (json_obj);
463 resp = GNUNET_REST_create_json_response (NULL);
464 handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
465 cleanup_handle (handle);
468 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "4\n");
469 egoname_json = GNUNET_REST_jsonapi_resource_read_attr (json_res, GNUNET_REST_JSONAPI_KEY_ID);
470 if (!json_is_string (egoname_json))
472 GNUNET_REST_jsonapi_object_delete (json_obj);
473 GNUNET_SCHEDULER_add_now (&do_error, handle);
476 egoname = json_string_value (egoname_json);
477 for (ego_entry = handle->ego_head;
479 ego_entry = ego_entry->next)
481 if (0 == strcasecmp (egoname, ego_entry->identifier))
483 json_decref (egoname_json);
484 GNUNET_REST_jsonapi_object_delete (json_obj);
485 resp = GNUNET_REST_create_json_response (NULL);
486 handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
487 cleanup_handle (handle);
491 GNUNET_asprintf (&handle->name, "%s", egoname);
492 json_decref (egoname_json);
493 GNUNET_REST_jsonapi_object_delete (json_obj);
494 handle->op = GNUNET_IDENTITY_create (handle->identity_handle,
501 ego_edit_cont (struct RestConnectionDataHandle *con,
505 struct JsonApiObject *json_obj;
506 struct JsonApiResource *json_res;
510 struct RequestHandle *handle = cls;
511 char term_data[handle->data_size+1];
512 struct EgoEntry *ego_entry;
513 struct MHD_Response *resp;
514 int ego_exists = GNUNET_NO;
518 if (strlen (GNUNET_REST_API_NS_IDENTITY) > strlen (handle->url))
520 GNUNET_SCHEDULER_add_now (&do_error, handle);
524 egoname = &handle->url[strlen(GNUNET_REST_API_NS_IDENTITY)+1];
525 for (ego_entry = handle->ego_head;
527 ego_entry = ego_entry->next)
529 if (0 == strcasecmp (egoname, ego_entry->identifier))
531 ego_exists = GNUNET_YES;
535 if (GNUNET_NO == ego_exists)
537 resp = GNUNET_REST_create_json_response (NULL);
538 handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND);
539 cleanup_handle (handle);
543 if (0 >= handle->data_size)
545 GNUNET_SCHEDULER_add_now (&do_error, handle);
549 term_data[handle->data_size] = '\0';
550 memcpy (term_data, handle->data, handle->data_size);
551 json_obj = GNUNET_REST_jsonapi_object_parse (term_data);
553 if (NULL == json_obj)
555 GNUNET_SCHEDULER_add_now (&do_error, handle);
558 if (1 != GNUNET_REST_jsonapi_object_resource_count (json_obj))
560 GNUNET_REST_jsonapi_object_delete (json_obj);
561 GNUNET_SCHEDULER_add_now (&do_error, handle);
564 json_res = GNUNET_REST_jsonapi_object_get_resource (json_obj, 0);
565 if (GNUNET_NO == GNUNET_REST_jsonapi_resource_check_type (json_res, GNUNET_REST_JSONAPI_IDENTITY_EGO))
567 GNUNET_REST_jsonapi_object_delete (json_obj);
568 GNUNET_SCHEDULER_add_now (&do_error, handle);
572 name_json = GNUNET_REST_jsonapi_resource_read_attr (json_res, GNUNET_REST_JSONAPI_IDENTITY_NEWNAME);
573 if (NULL != name_json)
576 if (json_is_string (name_json))
578 newname = json_string_value (name_json);
579 handle->op = GNUNET_IDENTITY_rename (handle->identity_handle,
584 GNUNET_REST_jsonapi_object_delete (json_obj);
585 json_decref (name_json);
588 json_decref (name_json);
591 subsys_json = GNUNET_REST_jsonapi_resource_read_attr (json_res, GNUNET_REST_JSONAPI_IDENTITY_SUBSYSTEM);
592 if (NULL != subsys_json)
595 if (json_is_string (subsys_json))
597 subsys = json_string_value (subsys_json);
598 GNUNET_asprintf (&handle->subsys, "%s", subsys);
599 GNUNET_REST_jsonapi_object_delete (json_obj);
600 handle->op = GNUNET_IDENTITY_set (handle->identity_handle,
605 json_decref (subsys_json);
608 json_decref (subsys_json);
610 GNUNET_REST_jsonapi_object_delete (json_obj);
611 GNUNET_SCHEDULER_add_now (&do_error, handle);
616 ego_delete_cont (struct RestConnectionDataHandle *con_handle,
621 struct EgoEntry *ego_entry;
622 struct MHD_Response *resp;
623 struct RequestHandle *handle = cls;
624 int ego_exists = GNUNET_NO;
626 if (strlen (GNUNET_REST_API_NS_IDENTITY) >= strlen (handle->url))
628 GNUNET_SCHEDULER_add_now (&do_error, handle);
632 egoname = &handle->url[strlen(GNUNET_REST_API_NS_IDENTITY)+1];
633 for (ego_entry = handle->ego_head;
635 ego_entry = ego_entry->next)
637 if (0 == strcasecmp (egoname, ego_entry->identifier))
639 ego_exists = GNUNET_YES;
643 if (GNUNET_NO == ego_exists)
645 resp = GNUNET_REST_create_json_response (NULL);
646 handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND);
647 cleanup_handle (handle);
650 handle->op = GNUNET_IDENTITY_delete (handle->identity_handle,
658 init_cont (struct RequestHandle *handle)
660 static const struct GNUNET_REST_RestConnectionHandler handlers[] = {
661 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY, &ego_info_response},
662 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY, &ego_create_cont},
663 {MHD_HTTP_METHOD_PUT, GNUNET_REST_API_NS_IDENTITY, &ego_edit_cont},
664 {MHD_HTTP_METHOD_DELETE, GNUNET_REST_API_NS_IDENTITY, &ego_delete_cont},
665 GNUNET_REST_HANDLER_END
668 if (GNUNET_NO == GNUNET_REST_handle_request (handle->conndata_handle, handlers, handle))
669 GNUNET_SCHEDULER_add_now (&do_error, handle);
673 * If listing is enabled, prints information about the egos.
675 * This function is initially called for all egos and then again
676 * whenever a ego's identifier changes or if it is deleted. At the
677 * end of the initial pass over all egos, the function is once called
678 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
679 * be invoked in the future or that there was an error.
681 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
682 * this function is only called ONCE, and 'NULL' being passed in
683 * 'ego' does indicate an error (i.e. name is taken or no default
684 * value is known). If 'ego' is non-NULL and if '*ctx'
685 * is set in those callbacks, the value WILL be passed to a subsequent
686 * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
687 * that one was not NULL).
689 * When an identity is renamed, this function is called with the
690 * (known) ego but the NEW identifier.
692 * When an identity is deleted, this function is called with the
693 * (known) ego and "NULL" for the 'identifier'. In this case,
694 * the 'ego' is henceforth invalid (and the 'ctx' should also be
698 * @param ego ego handle
699 * @param ctx context for application to store data for this ego
700 * (during the lifetime of this process, initially NULL)
701 * @param identifier identifier assigned by the user for this ego,
702 * NULL if the user just deleted the ego and it
703 * must thus no longer be used
707 struct GNUNET_IDENTITY_Ego *ego,
709 const char *identifier)
711 struct RequestHandle *handle = cls;
712 struct EgoEntry *ego_entry;
714 if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
716 handle->state = ID_REST_STATE_POST_INIT;
720 if (ID_REST_STATE_INIT == handle->state) {
721 ego_entry = GNUNET_new (struct EgoEntry);
722 GNUNET_IDENTITY_ego_get_public_key (ego, &(ego_entry->pk));
723 ego_entry->ego = ego;
724 GNUNET_asprintf (&ego_entry->identifier, "%s", identifier);
725 GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
731 * Function processing the REST call
733 * @param method HTTP method
734 * @param url URL of the HTTP request
735 * @param data body of the HTTP request (optional)
736 * @param data_size length of the body
737 * @param proc callback function for the result
738 * @param proc_cls closure for callback function
739 * @return GNUNET_OK if request accepted
742 rest_identity_process_request(struct RestConnectionDataHandle *conndata_handle,
743 GNUNET_REST_ResultProcessor proc,
746 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
750 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
752 handle->proc_cls = proc_cls;
754 handle->state = ID_REST_STATE_INIT;
755 handle->conndata_handle = conndata_handle;
756 handle->data = conndata_handle->data;
757 handle->data_size = conndata_handle->data_size;
758 handle->method = conndata_handle->method;
759 GNUNET_asprintf (&handle->url, "%s", conndata_handle->url);
760 if (handle->url[strlen (handle->url)-1] == '/')
761 handle->url[strlen (handle->url)-1] = '\0';
762 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
764 handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
767 handle->timeout_task =
768 GNUNET_SCHEDULER_add_delayed (handle->timeout,
773 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
778 * Entry point for the plugin.
780 * @param cls Config info
781 * @return NULL on error, otherwise the plugin context
784 libgnunet_plugin_rest_identity_init (void *cls)
786 static struct Plugin plugin;
788 struct GNUNET_REST_Plugin *api;
790 if (NULL != plugin.cfg)
791 return NULL; /* can only initialize once! */
792 memset (&plugin, 0, sizeof (struct Plugin));
794 api = GNUNET_new (struct GNUNET_REST_Plugin);
796 api->name = GNUNET_REST_API_NS_IDENTITY;
797 api->process_request = &rest_identity_process_request;
798 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
799 _("Identity REST API initialized\n"));
805 * Exit point from the plugin.
807 * @param cls the plugin context (as returned by "init")
808 * @return always NULL
811 libgnunet_plugin_rest_identity_done (void *cls)
813 struct GNUNET_REST_Plugin *api = cls;
814 struct Plugin *plugin = api->cls;
818 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
819 "Identity REST plugin is finished\n");
823 /* end of plugin_rest_gns.c */