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 GNUNET_REST_JSONAPI_IDENTITY_EGO "ego"
42 #define GNUNET_REST_JSONAPI_IDENTITY_KEY "key"
44 #define GNUNET_REST_JSONAPI_IDENTITY_NEWNAME "newname"
46 #define GNUNET_REST_JSONAPI_IDENTITY_SUBSYSTEM "subsystem"
49 * @brief struct returned by the initialization function of the plugin
53 const struct GNUNET_CONFIGURATION_Handle *cfg;
56 const struct GNUNET_CONFIGURATION_Handle *cfg;
63 struct EgoEntry *next;
68 struct EgoEntry *prev;
78 struct GNUNET_CRYPTO_EcdsaPublicKey pk;
83 struct GNUNET_IDENTITY_Ego *ego;
92 struct EgoEntry *ego_head;
97 struct EgoEntry *ego_tail;
99 struct RestConnectionDataHandle *conndata_handle;
102 * The processing state
107 * Handle to GNS service.
109 struct GNUNET_IDENTITY_Handle *identity_handle;
114 struct GNUNET_IDENTITY_Operation *op;
117 * Desired timeout for the lookup (default is no timeout).
119 struct GNUNET_TIME_Relative timeout;
122 * ID of a task associated with the resolution process.
124 struct GNUNET_SCHEDULER_Task * timeout_task;
127 * The root of the received JSON or NULL
132 * The plugin result processor
134 GNUNET_REST_ResultProcessor proc;
137 * The closure of the result processor
142 * The name to look up
147 * The subsystem set from REST
157 * The data from the REST request
162 * the length of the REST data
175 * Cleanup lookup handle
176 * @param handle Handle to clean up
179 cleanup_handle (struct RequestHandle *handle)
181 struct EgoEntry *ego_entry;
182 struct EgoEntry *ego_tmp;
183 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
185 if (NULL != handle->name)
186 GNUNET_free (handle->name);
187 if (NULL != handle->timeout_task)
188 GNUNET_SCHEDULER_cancel (handle->timeout_task);
189 if (NULL != handle->identity_handle)
190 GNUNET_IDENTITY_disconnect (handle->identity_handle);
191 if (NULL != handle->subsys)
192 GNUNET_free (handle->subsys);
193 if (NULL != handle->url)
194 GNUNET_free (handle->url);
195 for (ego_entry = handle->ego_head;
199 ego_entry = ego_entry->next;
200 GNUNET_free (ego_tmp->identifier);
201 GNUNET_free (ego_tmp);
203 GNUNET_free (handle);
208 * Task run on shutdown. Cleans up everything.
211 * @param tc scheduler context
215 const struct GNUNET_SCHEDULER_TaskContext *tc)
217 struct RequestHandle *handle = cls;
218 struct MHD_Response *resp = GNUNET_REST_create_json_response (NULL);
219 handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
220 cleanup_handle (handle);
224 * Callback for IDENTITY_get()
226 * @param cls the RequestHandle
227 * @param ego the Ego found
228 * @param ctx the context
229 * @param name the id of the ego
232 get_ego_for_subsys (void *cls,
233 struct GNUNET_IDENTITY_Ego *ego,
237 struct RequestHandle *handle = cls;
238 struct JsonApiObject *json_object;
239 struct JsonApiResource *json_resource;
240 struct EgoEntry *ego_entry;
241 struct MHD_Response *resp;
246 json_object = GNUNET_REST_jsonapi_object_new ();
248 for (ego_entry = handle->ego_head;
250 ego_entry = ego_entry->next)
252 if ( (NULL != name) && (0 != strcmp (name, ego_entry->identifier)) )
256 json_resource = GNUNET_REST_jsonapi_resource_new (GNUNET_REST_JSONAPI_IDENTITY_EGO, ego_entry->identifier);
257 keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&ego_entry->pk);
258 key_json = json_string (keystring);
259 GNUNET_REST_jsonapi_resource_add_attr (json_resource, GNUNET_REST_JSONAPI_IDENTITY_KEY, key_json);
260 json_decref (key_json);
261 GNUNET_free (keystring);
262 GNUNET_REST_jsonapi_object_resource_add (json_object, json_resource);
265 if (0 == GNUNET_REST_jsonapi_object_resource_count (json_object))
267 GNUNET_REST_jsonapi_object_delete (json_object);
268 GNUNET_SCHEDULER_add_now (&do_error, handle);
271 GNUNET_REST_jsonapi_data_serialize (json_object, &result_str);
272 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
273 resp = GNUNET_REST_create_json_response (result_str);
274 GNUNET_REST_jsonapi_object_delete (json_object);
275 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
276 GNUNET_free (result_str);
277 cleanup_handle (handle);
281 * Create a response with requested ego(s)
283 * @param handle the RequestHandle
286 ego_info_response (struct RestConnectionDataHandle *con,
294 struct RequestHandle *handle = cls;
295 struct EgoEntry *ego_entry;
296 struct GNUNET_HashCode key;
297 struct MHD_Response *resp;
298 struct JsonApiObject *json_object;
299 struct JsonApiResource *json_resource;
301 if (GNUNET_NO == GNUNET_REST_namespace_match (handle->url, GNUNET_REST_API_NS_IDENTITY))
303 resp = GNUNET_REST_create_json_response (NULL);
304 handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
305 cleanup_handle (handle);
308 if ( (strlen (GNUNET_REST_API_NS_IDENTITY) == strlen (handle->url) )) {
309 GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_IDENTITY_SUBSYSTEM, strlen (GNUNET_REST_JSONAPI_IDENTITY_SUBSYSTEM), &key);
311 GNUNET_CONTAINER_multihashmap_contains (handle->conndata_handle->url_param_map,
314 subsys_val = GNUNET_CONTAINER_multihashmap_get (handle->conndata_handle->url_param_map,
316 if (NULL != subsys_val)
318 GNUNET_asprintf (&handle->subsys, "%s", subsys_val);
319 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Looking for %s's ego\n", subsys_val);
320 handle->op = GNUNET_IDENTITY_get (handle->identity_handle,
328 json_object = GNUNET_REST_jsonapi_object_new ();
329 egoname = &handle->url[strlen (GNUNET_REST_API_NS_IDENTITY)+1];
331 if (strlen (GNUNET_REST_API_NS_IDENTITY) == strlen (handle->url))
336 for (ego_entry = handle->ego_head;
338 ego_entry = ego_entry->next)
340 if ( (NULL != egoname) && (0 != strcmp (egoname, ego_entry->identifier)) )
342 keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&ego_entry->pk);
343 json_resource = GNUNET_REST_jsonapi_resource_new (GNUNET_REST_JSONAPI_IDENTITY_EGO, ego_entry->identifier);
344 key_str = json_string (keystring);
345 GNUNET_REST_jsonapi_resource_add_attr (json_resource,
346 GNUNET_REST_JSONAPI_IDENTITY_KEY,
348 json_decref (key_str);
349 GNUNET_free (keystring);
350 GNUNET_REST_jsonapi_object_resource_add (json_object, json_resource);
352 if (0 == GNUNET_REST_jsonapi_object_resource_count (json_object))
354 GNUNET_REST_jsonapi_object_delete (json_object);
355 GNUNET_SCHEDULER_add_now (&do_error, handle);
358 GNUNET_REST_jsonapi_data_serialize (json_object, &result_str);
359 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
360 resp = GNUNET_REST_create_json_response (result_str);
361 GNUNET_REST_jsonapi_object_delete (json_object);
362 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
363 GNUNET_free (result_str);
364 cleanup_handle (handle);
368 do_finished (void *cls, const char *emsg)
370 struct RequestHandle *handle = cls;
371 struct MHD_Response *resp;
376 GNUNET_SCHEDULER_add_now (&do_error, handle);
379 resp = GNUNET_REST_create_json_response (NULL);
380 handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
381 cleanup_handle (handle);
385 edit_finished (void *cls, const char *emsg)
387 struct RequestHandle *handle = cls;
388 struct MHD_Response *resp;
393 GNUNET_SCHEDULER_add_now (&do_error, handle);
396 resp = GNUNET_REST_create_json_response (NULL);
397 handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
398 cleanup_handle (handle);
402 create_finished (void *cls, const char *emsg)
404 struct RequestHandle *handle = cls;
405 struct MHD_Response *resp;
410 GNUNET_SCHEDULER_add_now (&do_error, handle);
413 resp = GNUNET_REST_create_json_response (NULL);
414 handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
415 cleanup_handle (handle);
419 ego_create_cont (struct RestConnectionDataHandle *con,
423 struct RequestHandle *handle = cls;
424 struct EgoEntry *ego_entry;
425 struct MHD_Response *resp;
426 struct JsonApiObject *json_obj;
427 struct JsonApiResource *json_res;
428 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 if (NULL == json_obj)
447 GNUNET_SCHEDULER_add_now (&do_error, handle);
450 if (1 != GNUNET_REST_jsonapi_object_resource_count (json_obj))
452 GNUNET_REST_jsonapi_object_delete (json_obj);
453 GNUNET_SCHEDULER_add_now (&do_error, handle);
456 json_res = GNUNET_REST_jsonapi_object_get_resource (json_obj, 0);
457 if (GNUNET_NO == GNUNET_REST_jsonapi_resource_check_type (json_res, GNUNET_REST_JSONAPI_IDENTITY_EGO))
459 GNUNET_REST_jsonapi_object_delete (json_obj);
460 resp = GNUNET_REST_create_json_response (NULL);
461 handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
462 cleanup_handle (handle);
465 egoname_json = GNUNET_REST_jsonapi_resource_read_attr (json_res, GNUNET_REST_JSONAPI_KEY_ID);
466 if (!json_is_string (egoname_json))
468 GNUNET_REST_jsonapi_object_delete (json_obj);
469 GNUNET_SCHEDULER_add_now (&do_error, handle);
472 egoname = json_string_value (egoname_json);
473 for (ego_entry = handle->ego_head;
475 ego_entry = ego_entry->next)
477 if (0 == strcasecmp (egoname, ego_entry->identifier))
479 GNUNET_REST_jsonapi_object_delete (json_obj);
480 resp = GNUNET_REST_create_json_response (NULL);
481 handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
482 cleanup_handle (handle);
486 GNUNET_asprintf (&handle->name, "%s", egoname);
487 GNUNET_REST_jsonapi_object_delete (json_obj);
488 handle->op = GNUNET_IDENTITY_create (handle->identity_handle,
496 * Handle ego edit request
498 * @param con rest connection handle
499 * @param url the url that is requested
500 * @param cls the RequestHandle
503 ego_edit_cont (struct RestConnectionDataHandle *con,
507 struct JsonApiObject *json_obj;
508 struct JsonApiResource *json_res;
509 struct RequestHandle *handle = cls;
510 struct EgoEntry *ego_entry;
511 struct MHD_Response *resp;
517 char term_data[handle->data_size+1];
518 int ego_exists = GNUNET_NO;
520 if (strlen (GNUNET_REST_API_NS_IDENTITY) > strlen (handle->url))
522 GNUNET_SCHEDULER_add_now (&do_error, handle);
526 egoname = &handle->url[strlen(GNUNET_REST_API_NS_IDENTITY)+1];
527 for (ego_entry = handle->ego_head;
529 ego_entry = ego_entry->next)
531 if (0 == strcasecmp (egoname, ego_entry->identifier))
533 ego_exists = GNUNET_YES;
537 if (GNUNET_NO == ego_exists)
539 resp = GNUNET_REST_create_json_response (NULL);
540 handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND);
541 cleanup_handle (handle);
545 if (0 >= handle->data_size)
547 GNUNET_SCHEDULER_add_now (&do_error, handle);
551 term_data[handle->data_size] = '\0';
552 memcpy (term_data, handle->data, handle->data_size);
553 json_obj = GNUNET_REST_jsonapi_object_parse (term_data);
555 if (NULL == json_obj)
557 GNUNET_SCHEDULER_add_now (&do_error, handle);
560 if (1 != GNUNET_REST_jsonapi_object_resource_count (json_obj))
562 GNUNET_REST_jsonapi_object_delete (json_obj);
563 GNUNET_SCHEDULER_add_now (&do_error, handle);
566 json_res = GNUNET_REST_jsonapi_object_get_resource (json_obj, 0);
567 if (GNUNET_NO == GNUNET_REST_jsonapi_resource_check_type (json_res, GNUNET_REST_JSONAPI_IDENTITY_EGO))
569 GNUNET_REST_jsonapi_object_delete (json_obj);
570 GNUNET_SCHEDULER_add_now (&do_error, handle);
574 name_json = GNUNET_REST_jsonapi_resource_read_attr (json_res, GNUNET_REST_JSONAPI_IDENTITY_NEWNAME);
575 if (NULL != name_json)
578 if (json_is_string (name_json))
580 newname = json_string_value (name_json);
581 handle->op = GNUNET_IDENTITY_rename (handle->identity_handle,
586 GNUNET_REST_jsonapi_object_delete (json_obj);
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,
608 GNUNET_REST_jsonapi_object_delete (json_obj);
609 GNUNET_SCHEDULER_add_now (&do_error, handle);
614 ego_delete_cont (struct RestConnectionDataHandle *con_handle,
619 struct EgoEntry *ego_entry;
620 struct MHD_Response *resp;
621 struct RequestHandle *handle = cls;
622 int ego_exists = GNUNET_NO;
624 if (strlen (GNUNET_REST_API_NS_IDENTITY) >= strlen (handle->url))
626 GNUNET_SCHEDULER_add_now (&do_error, handle);
630 egoname = &handle->url[strlen(GNUNET_REST_API_NS_IDENTITY)+1];
631 for (ego_entry = handle->ego_head;
633 ego_entry = ego_entry->next)
635 if (0 == strcasecmp (egoname, ego_entry->identifier))
637 ego_exists = GNUNET_YES;
641 if (GNUNET_NO == ego_exists)
643 resp = GNUNET_REST_create_json_response (NULL);
644 handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND);
645 cleanup_handle (handle);
648 handle->op = GNUNET_IDENTITY_delete (handle->identity_handle,
657 * Handle rest request
659 * @param handle the request handle
662 init_cont (struct RequestHandle *handle)
664 static const struct GNUNET_REST_RestConnectionHandler handlers[] = {
665 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY, &ego_info_response},
666 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY, &ego_create_cont},
667 {MHD_HTTP_METHOD_PUT, GNUNET_REST_API_NS_IDENTITY, &ego_edit_cont},
668 {MHD_HTTP_METHOD_DELETE, GNUNET_REST_API_NS_IDENTITY, &ego_delete_cont},
669 GNUNET_REST_HANDLER_END
672 if (GNUNET_NO == GNUNET_REST_handle_request (handle->conndata_handle, handlers, handle))
673 GNUNET_SCHEDULER_add_now (&do_error, handle);
677 * If listing is enabled, prints information about the egos.
679 * This function is initially called for all egos and then again
680 * whenever a ego's identifier changes or if it is deleted. At the
681 * end of the initial pass over all egos, the function is once called
682 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
683 * be invoked in the future or that there was an error.
685 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
686 * this function is only called ONCE, and 'NULL' being passed in
687 * 'ego' does indicate an error (i.e. name is taken or no default
688 * value is known). If 'ego' is non-NULL and if '*ctx'
689 * is set in those callbacks, the value WILL be passed to a subsequent
690 * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
691 * that one was not NULL).
693 * When an identity is renamed, this function is called with the
694 * (known) ego but the NEW identifier.
696 * When an identity is deleted, this function is called with the
697 * (known) ego and "NULL" for the 'identifier'. In this case,
698 * the 'ego' is henceforth invalid (and the 'ctx' should also be
702 * @param ego ego handle
703 * @param ctx context for application to store data for this ego
704 * (during the lifetime of this process, initially NULL)
705 * @param identifier identifier assigned by the user for this ego,
706 * NULL if the user just deleted the ego and it
707 * must thus no longer be used
711 struct GNUNET_IDENTITY_Ego *ego,
713 const char *identifier)
715 struct RequestHandle *handle = cls;
716 struct EgoEntry *ego_entry;
718 if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
720 handle->state = ID_REST_STATE_POST_INIT;
724 if (ID_REST_STATE_INIT == handle->state) {
725 ego_entry = GNUNET_new (struct EgoEntry);
726 GNUNET_IDENTITY_ego_get_public_key (ego, &(ego_entry->pk));
727 ego_entry->ego = ego;
728 GNUNET_asprintf (&ego_entry->identifier, "%s", identifier);
729 GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
735 * Function processing the REST call
737 * @param method HTTP method
738 * @param url URL of the HTTP request
739 * @param data body of the HTTP request (optional)
740 * @param data_size length of the body
741 * @param proc callback function for the result
742 * @param proc_cls closure for callback function
743 * @return GNUNET_OK if request accepted
746 rest_identity_process_request(struct RestConnectionDataHandle *conndata_handle,
747 GNUNET_REST_ResultProcessor proc,
750 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
754 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
756 handle->proc_cls = proc_cls;
758 handle->state = ID_REST_STATE_INIT;
759 handle->conndata_handle = conndata_handle;
760 handle->data = conndata_handle->data;
761 handle->data_size = conndata_handle->data_size;
762 handle->method = conndata_handle->method;
763 GNUNET_asprintf (&handle->url, "%s", conndata_handle->url);
764 if (handle->url[strlen (handle->url)-1] == '/')
765 handle->url[strlen (handle->url)-1] = '\0';
766 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
768 handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
771 handle->timeout_task =
772 GNUNET_SCHEDULER_add_delayed (handle->timeout,
777 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
782 * Entry point for the plugin.
784 * @param cls Config info
785 * @return NULL on error, otherwise the plugin context
788 libgnunet_plugin_rest_identity_init (void *cls)
790 static struct Plugin plugin;
792 struct GNUNET_REST_Plugin *api;
794 if (NULL != plugin.cfg)
795 return NULL; /* can only initialize once! */
796 memset (&plugin, 0, sizeof (struct Plugin));
798 api = GNUNET_new (struct GNUNET_REST_Plugin);
800 api->name = GNUNET_REST_API_NS_IDENTITY;
801 api->process_request = &rest_identity_process_request;
802 GNUNET_asprintf (&api->allow_methods,
803 "%s, %s, %s, %s, %s",
805 MHD_HTTP_METHOD_POST,
807 MHD_HTTP_METHOD_DELETE,
808 MHD_HTTP_METHOD_OPTIONS);
810 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
811 _("Identity REST API initialized\n"));
817 * Exit point from the plugin.
819 * @param cls the plugin context (as returned by "init")
820 * @return always NULL
823 libgnunet_plugin_rest_identity_done (void *cls)
825 struct GNUNET_REST_Plugin *api = cls;
826 struct Plugin *plugin = api->cls;
829 GNUNET_free_non_null (api->allow_methods);
831 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
832 "Identity REST plugin is finished\n");
836 /* end of plugin_rest_gns.c */