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;
58 static char* allow_methods;
65 struct EgoEntry *next;
70 struct EgoEntry *prev;
80 struct GNUNET_CRYPTO_EcdsaPublicKey pk;
85 struct GNUNET_IDENTITY_Ego *ego;
94 struct EgoEntry *ego_head;
99 struct EgoEntry *ego_tail;
101 struct RestConnectionDataHandle *conndata_handle;
104 * The processing state
109 * Handle to GNS service.
111 struct GNUNET_IDENTITY_Handle *identity_handle;
116 struct GNUNET_IDENTITY_Operation *op;
119 * Desired timeout for the lookup (default is no timeout).
121 struct GNUNET_TIME_Relative timeout;
124 * ID of a task associated with the resolution process.
126 struct GNUNET_SCHEDULER_Task * timeout_task;
129 * The root of the received JSON or NULL
134 * The plugin result processor
136 GNUNET_REST_ResultProcessor proc;
139 * The closure of the result processor
144 * The name to look up
149 * The subsystem set from REST
159 * The data from the REST request
164 * the length of the REST data
177 * Cleanup lookup handle
178 * @param handle Handle to clean up
181 cleanup_handle (struct RequestHandle *handle)
183 struct EgoEntry *ego_entry;
184 struct EgoEntry *ego_tmp;
185 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
187 if (NULL != handle->name)
188 GNUNET_free (handle->name);
189 if (NULL != handle->timeout_task)
190 GNUNET_SCHEDULER_cancel (handle->timeout_task);
191 if (NULL != handle->identity_handle)
192 GNUNET_IDENTITY_disconnect (handle->identity_handle);
193 if (NULL != handle->subsys)
194 GNUNET_free (handle->subsys);
195 if (NULL != handle->url)
196 GNUNET_free (handle->url);
197 for (ego_entry = handle->ego_head;
201 ego_entry = ego_entry->next;
202 GNUNET_free (ego_tmp->identifier);
203 GNUNET_free (ego_tmp);
205 GNUNET_free (handle);
210 * Task run on shutdown. Cleans up everything.
213 * @param tc scheduler context
217 const struct GNUNET_SCHEDULER_TaskContext *tc)
219 struct RequestHandle *handle = cls;
220 struct MHD_Response *resp = GNUNET_REST_create_json_response (NULL);
221 handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
222 cleanup_handle (handle);
226 * Callback for IDENTITY_get()
228 * @param cls the RequestHandle
229 * @param ego the Ego found
230 * @param ctx the context
231 * @param name the id of the ego
234 get_ego_for_subsys (void *cls,
235 struct GNUNET_IDENTITY_Ego *ego,
239 struct RequestHandle *handle = cls;
240 struct JsonApiObject *json_object;
241 struct JsonApiResource *json_resource;
242 struct EgoEntry *ego_entry;
243 struct MHD_Response *resp;
248 json_object = GNUNET_REST_jsonapi_object_new ();
250 for (ego_entry = handle->ego_head;
252 ego_entry = ego_entry->next)
254 if ( (NULL != name) && (0 != strcmp (name, ego_entry->identifier)) )
258 json_resource = GNUNET_REST_jsonapi_resource_new (GNUNET_REST_JSONAPI_IDENTITY_EGO, ego_entry->identifier);
259 keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&ego_entry->pk);
260 key_json = json_string (keystring);
261 GNUNET_REST_jsonapi_resource_add_attr (json_resource, GNUNET_REST_JSONAPI_IDENTITY_KEY, key_json);
262 json_decref (key_json);
263 GNUNET_free (keystring);
264 GNUNET_REST_jsonapi_object_resource_add (json_object, json_resource);
267 if (0 == GNUNET_REST_jsonapi_object_resource_count (json_object))
269 GNUNET_REST_jsonapi_object_delete (json_object);
270 GNUNET_SCHEDULER_add_now (&do_error, handle);
273 GNUNET_REST_jsonapi_data_serialize (json_object, &result_str);
274 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
275 resp = GNUNET_REST_create_json_response (result_str);
276 GNUNET_REST_jsonapi_object_delete (json_object);
277 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
278 GNUNET_free (result_str);
279 cleanup_handle (handle);
283 * Create a response with requested ego(s)
285 * @param handle the RequestHandle
288 ego_info_response (struct RestConnectionDataHandle *con,
296 struct RequestHandle *handle = cls;
297 struct EgoEntry *ego_entry;
298 struct GNUNET_HashCode key;
299 struct MHD_Response *resp;
300 struct JsonApiObject *json_object;
301 struct JsonApiResource *json_resource;
303 if (GNUNET_NO == GNUNET_REST_namespace_match (handle->url, GNUNET_REST_API_NS_IDENTITY))
305 resp = GNUNET_REST_create_json_response (NULL);
306 handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
307 cleanup_handle (handle);
310 if ( (strlen (GNUNET_REST_API_NS_IDENTITY) == strlen (handle->url) )) {
311 GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_IDENTITY_SUBSYSTEM, strlen (GNUNET_REST_JSONAPI_IDENTITY_SUBSYSTEM), &key);
313 GNUNET_CONTAINER_multihashmap_contains (handle->conndata_handle->url_param_map,
316 subsys_val = GNUNET_CONTAINER_multihashmap_get (handle->conndata_handle->url_param_map,
318 if (NULL != subsys_val)
320 GNUNET_asprintf (&handle->subsys, "%s", subsys_val);
321 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Looking for %s's ego\n", subsys_val);
322 handle->op = GNUNET_IDENTITY_get (handle->identity_handle,
330 json_object = GNUNET_REST_jsonapi_object_new ();
331 egoname = &handle->url[strlen (GNUNET_REST_API_NS_IDENTITY)+1];
333 if (strlen (GNUNET_REST_API_NS_IDENTITY) == strlen (handle->url))
338 for (ego_entry = handle->ego_head;
340 ego_entry = ego_entry->next)
342 if ( (NULL != egoname) && (0 != strcmp (egoname, ego_entry->identifier)) )
344 keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&ego_entry->pk);
345 json_resource = GNUNET_REST_jsonapi_resource_new (GNUNET_REST_JSONAPI_IDENTITY_EGO, ego_entry->identifier);
346 key_str = json_string (keystring);
347 GNUNET_REST_jsonapi_resource_add_attr (json_resource,
348 GNUNET_REST_JSONAPI_IDENTITY_KEY,
350 json_decref (key_str);
351 GNUNET_free (keystring);
352 GNUNET_REST_jsonapi_object_resource_add (json_object, json_resource);
354 if (0 == GNUNET_REST_jsonapi_object_resource_count (json_object))
356 GNUNET_REST_jsonapi_object_delete (json_object);
357 GNUNET_SCHEDULER_add_now (&do_error, handle);
360 GNUNET_REST_jsonapi_data_serialize (json_object, &result_str);
361 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
362 resp = GNUNET_REST_create_json_response (result_str);
363 GNUNET_REST_jsonapi_object_delete (json_object);
364 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
365 GNUNET_free (result_str);
366 cleanup_handle (handle);
370 do_finished (void *cls, const char *emsg)
372 struct RequestHandle *handle = cls;
373 struct MHD_Response *resp;
378 GNUNET_SCHEDULER_add_now (&do_error, handle);
381 resp = GNUNET_REST_create_json_response (NULL);
382 handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
383 cleanup_handle (handle);
387 edit_finished (void *cls, const char *emsg)
389 struct RequestHandle *handle = cls;
390 struct MHD_Response *resp;
395 GNUNET_SCHEDULER_add_now (&do_error, handle);
398 resp = GNUNET_REST_create_json_response (NULL);
399 handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
400 cleanup_handle (handle);
404 create_finished (void *cls, const char *emsg)
406 struct RequestHandle *handle = cls;
407 struct MHD_Response *resp;
412 GNUNET_SCHEDULER_add_now (&do_error, handle);
415 resp = GNUNET_REST_create_json_response (NULL);
416 handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
417 cleanup_handle (handle);
421 ego_create_cont (struct RestConnectionDataHandle *con,
425 struct RequestHandle *handle = cls;
426 struct EgoEntry *ego_entry;
427 struct MHD_Response *resp;
428 struct JsonApiObject *json_obj;
429 struct JsonApiResource *json_res;
430 json_t *egoname_json;
432 char term_data[handle->data_size+1];
434 if (strlen (GNUNET_REST_API_NS_IDENTITY) != strlen (handle->url))
436 GNUNET_SCHEDULER_add_now (&do_error, handle);
439 if (0 >= handle->data_size)
441 GNUNET_SCHEDULER_add_now (&do_error, handle);
444 term_data[handle->data_size] = '\0';
445 memcpy (term_data, handle->data, handle->data_size);
446 json_obj = GNUNET_REST_jsonapi_object_parse (term_data);
447 if (NULL == json_obj)
449 GNUNET_SCHEDULER_add_now (&do_error, handle);
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 json_res = GNUNET_REST_jsonapi_object_get_resource (json_obj, 0);
459 if (GNUNET_NO == GNUNET_REST_jsonapi_resource_check_type (json_res, GNUNET_REST_JSONAPI_IDENTITY_EGO))
461 GNUNET_REST_jsonapi_object_delete (json_obj);
462 resp = GNUNET_REST_create_json_response (NULL);
463 handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
464 cleanup_handle (handle);
467 egoname_json = GNUNET_REST_jsonapi_resource_read_attr (json_res, GNUNET_REST_JSONAPI_KEY_ID);
468 if (!json_is_string (egoname_json))
470 GNUNET_REST_jsonapi_object_delete (json_obj);
471 GNUNET_SCHEDULER_add_now (&do_error, handle);
474 egoname = json_string_value (egoname_json);
475 for (ego_entry = handle->ego_head;
477 ego_entry = ego_entry->next)
479 if (0 == strcasecmp (egoname, ego_entry->identifier))
481 GNUNET_REST_jsonapi_object_delete (json_obj);
482 resp = GNUNET_REST_create_json_response (NULL);
483 handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
484 cleanup_handle (handle);
488 GNUNET_asprintf (&handle->name, "%s", egoname);
489 GNUNET_REST_jsonapi_object_delete (json_obj);
490 handle->op = GNUNET_IDENTITY_create (handle->identity_handle,
498 * Handle ego edit request
500 * @param con rest connection handle
501 * @param url the url that is requested
502 * @param cls the RequestHandle
505 ego_edit_cont (struct RestConnectionDataHandle *con,
509 struct JsonApiObject *json_obj;
510 struct JsonApiResource *json_res;
511 struct RequestHandle *handle = cls;
512 struct EgoEntry *ego_entry;
513 struct MHD_Response *resp;
519 char term_data[handle->data_size+1];
520 int ego_exists = GNUNET_NO;
522 if (strlen (GNUNET_REST_API_NS_IDENTITY) > strlen (handle->url))
524 GNUNET_SCHEDULER_add_now (&do_error, handle);
528 egoname = &handle->url[strlen(GNUNET_REST_API_NS_IDENTITY)+1];
529 for (ego_entry = handle->ego_head;
531 ego_entry = ego_entry->next)
533 if (0 == strcasecmp (egoname, ego_entry->identifier))
535 ego_exists = GNUNET_YES;
539 if (GNUNET_NO == ego_exists)
541 resp = GNUNET_REST_create_json_response (NULL);
542 handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND);
543 cleanup_handle (handle);
547 if (0 >= handle->data_size)
549 GNUNET_SCHEDULER_add_now (&do_error, handle);
553 term_data[handle->data_size] = '\0';
554 memcpy (term_data, handle->data, handle->data_size);
555 json_obj = GNUNET_REST_jsonapi_object_parse (term_data);
557 if (NULL == json_obj)
559 GNUNET_SCHEDULER_add_now (&do_error, handle);
562 if (1 != GNUNET_REST_jsonapi_object_resource_count (json_obj))
564 GNUNET_REST_jsonapi_object_delete (json_obj);
565 GNUNET_SCHEDULER_add_now (&do_error, handle);
568 json_res = GNUNET_REST_jsonapi_object_get_resource (json_obj, 0);
569 if (GNUNET_NO == GNUNET_REST_jsonapi_resource_check_type (json_res, GNUNET_REST_JSONAPI_IDENTITY_EGO))
571 GNUNET_REST_jsonapi_object_delete (json_obj);
572 GNUNET_SCHEDULER_add_now (&do_error, handle);
576 name_json = GNUNET_REST_jsonapi_resource_read_attr (json_res, GNUNET_REST_JSONAPI_IDENTITY_NEWNAME);
577 if (NULL != name_json)
580 if (json_is_string (name_json))
582 newname = json_string_value (name_json);
583 handle->op = GNUNET_IDENTITY_rename (handle->identity_handle,
588 GNUNET_REST_jsonapi_object_delete (json_obj);
593 subsys_json = GNUNET_REST_jsonapi_resource_read_attr (json_res, GNUNET_REST_JSONAPI_IDENTITY_SUBSYSTEM);
594 if (NULL != subsys_json)
597 if (json_is_string (subsys_json))
599 subsys = json_string_value (subsys_json);
600 GNUNET_asprintf (&handle->subsys, "%s", subsys);
601 GNUNET_REST_jsonapi_object_delete (json_obj);
602 handle->op = GNUNET_IDENTITY_set (handle->identity_handle,
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 options_cont (struct RestConnectionDataHandle *con_handle,
662 struct MHD_Response *resp;
663 struct RequestHandle *handle = cls;
665 //For now, independent of path return all options
666 resp = GNUNET_REST_create_json_response (NULL);
667 MHD_add_response_header (resp,
668 "Access-Control-Allow-Methods",
670 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
671 cleanup_handle (handle);
676 * Handle rest request
678 * @param handle the request handle
681 init_cont (struct RequestHandle *handle)
683 static const struct GNUNET_REST_RestConnectionHandler handlers[] = {
684 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY, &ego_info_response},
685 {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY, &ego_create_cont},
686 {MHD_HTTP_METHOD_PUT, GNUNET_REST_API_NS_IDENTITY, &ego_edit_cont},
687 {MHD_HTTP_METHOD_DELETE, GNUNET_REST_API_NS_IDENTITY, &ego_delete_cont},
688 {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_IDENTITY, &options_cont},
689 GNUNET_REST_HANDLER_END
692 if (GNUNET_NO == GNUNET_REST_handle_request (handle->conndata_handle, handlers, handle))
693 GNUNET_SCHEDULER_add_now (&do_error, handle);
697 * If listing is enabled, prints information about the egos.
699 * This function is initially called for all egos and then again
700 * whenever a ego's identifier changes or if it is deleted. At the
701 * end of the initial pass over all egos, the function is once called
702 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
703 * be invoked in the future or that there was an error.
705 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
706 * this function is only called ONCE, and 'NULL' being passed in
707 * 'ego' does indicate an error (i.e. name is taken or no default
708 * value is known). If 'ego' is non-NULL and if '*ctx'
709 * is set in those callbacks, the value WILL be passed to a subsequent
710 * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
711 * that one was not NULL).
713 * When an identity is renamed, this function is called with the
714 * (known) ego but the NEW identifier.
716 * When an identity is deleted, this function is called with the
717 * (known) ego and "NULL" for the 'identifier'. In this case,
718 * the 'ego' is henceforth invalid (and the 'ctx' should also be
722 * @param ego ego handle
723 * @param ctx context for application to store data for this ego
724 * (during the lifetime of this process, initially NULL)
725 * @param identifier identifier assigned by the user for this ego,
726 * NULL if the user just deleted the ego and it
727 * must thus no longer be used
731 struct GNUNET_IDENTITY_Ego *ego,
733 const char *identifier)
735 struct RequestHandle *handle = cls;
736 struct EgoEntry *ego_entry;
738 if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
740 handle->state = ID_REST_STATE_POST_INIT;
744 if (ID_REST_STATE_INIT == handle->state) {
745 ego_entry = GNUNET_new (struct EgoEntry);
746 GNUNET_IDENTITY_ego_get_public_key (ego, &(ego_entry->pk));
747 ego_entry->ego = ego;
748 GNUNET_asprintf (&ego_entry->identifier, "%s", identifier);
749 GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
755 * Function processing the REST call
757 * @param method HTTP method
758 * @param url URL of the HTTP request
759 * @param data body of the HTTP request (optional)
760 * @param data_size length of the body
761 * @param proc callback function for the result
762 * @param proc_cls closure for callback function
763 * @return GNUNET_OK if request accepted
766 rest_identity_process_request(struct RestConnectionDataHandle *conndata_handle,
767 GNUNET_REST_ResultProcessor proc,
770 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
774 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
776 handle->proc_cls = proc_cls;
778 handle->state = ID_REST_STATE_INIT;
779 handle->conndata_handle = conndata_handle;
780 handle->data = conndata_handle->data;
781 handle->data_size = conndata_handle->data_size;
782 handle->method = conndata_handle->method;
783 GNUNET_asprintf (&handle->url, "%s", conndata_handle->url);
784 if (handle->url[strlen (handle->url)-1] == '/')
785 handle->url[strlen (handle->url)-1] = '\0';
786 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
788 handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
791 handle->timeout_task =
792 GNUNET_SCHEDULER_add_delayed (handle->timeout,
797 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
802 * Entry point for the plugin.
804 * @param cls Config info
805 * @return NULL on error, otherwise the plugin context
808 libgnunet_plugin_rest_identity_init (void *cls)
810 static struct Plugin plugin;
812 struct GNUNET_REST_Plugin *api;
814 if (NULL != plugin.cfg)
815 return NULL; /* can only initialize once! */
816 memset (&plugin, 0, sizeof (struct Plugin));
818 api = GNUNET_new (struct GNUNET_REST_Plugin);
820 api->name = GNUNET_REST_API_NS_IDENTITY;
821 api->process_request = &rest_identity_process_request;
822 GNUNET_asprintf (&allow_methods,
823 "%s, %s, %s, %s, %s",
825 MHD_HTTP_METHOD_POST,
827 MHD_HTTP_METHOD_DELETE,
828 MHD_HTTP_METHOD_OPTIONS);
830 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
831 _("Identity REST API initialized\n"));
837 * Exit point from the plugin.
839 * @param cls the plugin context (as returned by "init")
840 * @return always NULL
843 libgnunet_plugin_rest_identity_done (void *cls)
845 struct GNUNET_REST_Plugin *api = cls;
846 struct Plugin *plugin = api->cls;
849 GNUNET_free_non_null (allow_methods);
851 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
852 "Identity REST plugin is finished\n");
856 /* end of plugin_rest_gns.c */