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 API_NAMESPACE "/identity"
36 #define EGO_NAMESPACE "/identity/egos"
38 #define ID_REST_STATE_INIT 0
40 #define ID_REST_STATE_POST_INIT 1
42 #define URL_PARAM_SUBSYS "service"
44 #define GNUNET_REST_JSONAPI_IDENTITY_EGO "ego"
46 #define GNUNET_REST_JSONAPI_IDENTITY_KEY "key"
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;
91 struct EgoEntry *ego_head;
96 struct EgoEntry *ego_tail;
98 struct RestConnectionDataHandle *conndata_handle;
101 * The processing state
106 * Handle to GNS service.
108 struct GNUNET_IDENTITY_Handle *identity_handle;
113 struct GNUNET_IDENTITY_Operation *op;
116 * Desired timeout for the lookup (default is no timeout).
118 struct GNUNET_TIME_Relative timeout;
121 * ID of a task associated with the resolution process.
123 struct GNUNET_SCHEDULER_Task * timeout_task;
126 * The root of the received JSON or NULL
131 * The plugin result processor
133 GNUNET_REST_ResultProcessor proc;
136 * The closure of the result processor
141 * The name to look up
146 * The subsystem set from REST
156 * The data from the REST request
161 * the length of the REST data
174 * Cleanup lookup handle
175 * @praram handle Handle to clean up
178 cleanup_handle (struct RequestHandle *handle)
180 struct EgoEntry *ego_entry;
181 struct EgoEntry *ego_tmp;
182 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
184 if (NULL != handle->json_root)
185 json_decref (handle->json_root);
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 EgoEntry *ego_entry;
240 struct MHD_Response *resp;
246 root_json = json_object ();
249 for (ego_entry = handle->ego_head;
251 ego_entry = ego_entry->next)
253 if ( (NULL != name) && (0 != strcmp (name, ego_entry->identifier)) )
257 ego_json = json_object ();
258 keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&ego_entry->pk);
259 json_object_set_new (ego_json, "id", json_string (ego_entry->identifier));
260 json_object_set_new (ego_json, "type", json_string (GNUNET_REST_JSONAPI_IDENTITY_EGO));
261 json_object_set_new (ego_json, "key", json_string (keystring));
262 GNUNET_free (keystring);
265 if (NULL == ego_json)
267 json_decref (root_json);
268 GNUNET_SCHEDULER_add_now (&do_error, handle);
271 json_object_set (root_json, GNUNET_REST_JSONAPI_KEY_DATA, ego_json);
272 result_str = json_dumps (root_json, JSON_COMPACT);
273 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
274 json_decref (ego_json);
275 json_decref (root_json);
276 resp = GNUNET_REST_create_json_response (result_str);
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 RequestHandle *handle)
294 struct EgoEntry *ego_entry;
295 struct GNUNET_HashCode key;
296 struct MHD_Response *resp;
297 struct JsonApiResponse *json_response;
298 struct JsonApiResource *json_resource;
301 if (GNUNET_NO == GNUNET_REST_namespace_match (handle->url, EGO_NAMESPACE))
303 resp = GNUNET_REST_create_json_response (NULL);
304 handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
305 cleanup_handle (handle);
309 json_response = GNUNET_REST_jsonapi_response_new ();
310 if ( (strlen (EGO_NAMESPACE) == strlen (handle->url) )) {
311 GNUNET_CRYPTO_hash (URL_PARAM_SUBSYS, strlen (URL_PARAM_SUBSYS), &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_response = GNUNET_REST_jsonapi_response_new ();
331 egoname = &handle->url[strlen (EGO_NAMESPACE)+1];
333 if (strlen (EGO_NAMESPACE) == strlen (handle->url))
339 for (ego_entry = handle->ego_head;
341 ego_entry = ego_entry->next)
343 if ( (NULL != egoname) && (0 != strcmp (egoname, ego_entry->identifier)) )
345 keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&ego_entry->pk);
346 json_resource = GNUNET_REST_jsonapi_resource_new (GNUNET_REST_JSONAPI_IDENTITY_EGO, ego_entry->identifier);
347 key_str = json_string (keystring);
348 GNUNET_REST_jsonapi_resource_add_attr (json_resource,
349 GNUNET_REST_JSONAPI_IDENTITY_KEY,
351 json_decref (key_str);
352 GNUNET_free (keystring);
353 GNUNET_REST_jsonapi_response_resource_add (json_response, json_resource);
356 GNUNET_REST_jsonapi_data_serialize (json_response, &result_str);
358 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
359 resp = GNUNET_REST_create_json_response (result_str);
360 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
361 GNUNET_free (result_str);
362 cleanup_handle (handle);
366 do_finished (void *cls, const char *emsg)
368 struct RequestHandle *handle = cls;
369 struct MHD_Response *resp;
374 GNUNET_SCHEDULER_add_now (&do_error, handle);
377 resp = GNUNET_REST_create_json_response (NULL);
378 handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
379 cleanup_handle (handle);
383 set_finished (void *cls, const char *emsg)
385 struct RequestHandle *handle = cls;
386 struct MHD_Response *resp;
391 GNUNET_SCHEDULER_add_now (&do_error, handle);
394 resp = GNUNET_REST_create_json_response (NULL);
395 handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
396 cleanup_handle (handle);
400 create_finished (void *cls, const char *emsg)
402 struct RequestHandle *handle = cls;
403 struct MHD_Response *resp;
408 GNUNET_SCHEDULER_add_now (&do_error, handle);
411 resp = GNUNET_REST_create_json_response (NULL);
412 handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
413 cleanup_handle (handle);
417 ego_create_cont (struct RequestHandle *handle)
420 char term_data[handle->data_size+1];
421 struct EgoEntry *ego_entry;
422 struct MHD_Response *resp;
426 json_t *egoname_json;
429 if (strlen (API_NAMESPACE) != strlen (handle->url))
431 GNUNET_SCHEDULER_add_now (&do_error, handle);
434 if (0 >= handle->data_size)
436 GNUNET_SCHEDULER_add_now (&do_error, handle);
440 term_data[handle->data_size] = '\0';
441 memcpy (term_data, handle->data, handle->data_size);
442 root_json = json_loads (term_data, 0, &error);
444 if ((NULL == root_json) || !json_is_object (root_json))
446 GNUNET_SCHEDULER_add_now (&do_error, handle);
449 data_json = json_object_get (root_json, GNUNET_REST_JSONAPI_KEY_DATA);
450 if ((NULL == data_json) || !json_is_object (data_json))
452 GNUNET_SCHEDULER_add_now (&do_error, handle);
455 type_json = json_object_get (data_json, "type");
456 if (!json_is_string (type_json) ||
457 (0 != strcmp (GNUNET_REST_JSONAPI_IDENTITY_EGO, json_string_value (type_json))))
459 json_decref (data_json);
460 json_decref (root_json);
461 resp = GNUNET_REST_create_json_response (NULL);
462 handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
463 cleanup_handle (handle);
466 json_decref (type_json);
467 egoname_json = json_object_get (data_json, "id");
468 if (!json_is_string (egoname_json))
470 json_decref (data_json);
471 json_decref (root_json);
472 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 json_decref (data_json);
485 json_decref (root_json);
486 resp = GNUNET_REST_create_json_response (NULL);
487 handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
488 cleanup_handle (handle);
492 GNUNET_asprintf (&handle->name, "%s", egoname);
493 json_decref (egoname_json);
494 json_decref (data_json);
495 json_decref (root_json);
496 handle->op = GNUNET_IDENTITY_create (handle->identity_handle,
503 subsys_set_cont (struct RequestHandle *handle)
507 char term_data[handle->data_size+1];
508 struct EgoEntry *ego_entry;
509 struct MHD_Response *resp;
510 int ego_exists = GNUNET_NO;
518 if (strlen (API_NAMESPACE) > strlen (handle->url))
520 GNUNET_SCHEDULER_add_now (&do_error, handle);
524 egoname = &handle->url[strlen(EGO_NAMESPACE)+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 root_json = json_loads (term_data, 0, &error);
553 if ((NULL == root_json) || !json_is_object (root_json))
555 GNUNET_SCHEDULER_add_now (&do_error, handle);
558 data_json = json_object_get (root_json, "data");
559 if (!json_is_object (data_json))
561 json_decref (root_json);
562 GNUNET_SCHEDULER_add_now (&do_error, handle);
565 id_json = json_object_get (data_json, "id");
566 if (!json_is_string (id_json) ||
567 (0 != strcmp (egoname, json_string_value (id_json))))
569 json_decref (root_json);
570 json_decref (data_json);
571 GNUNET_SCHEDULER_add_now (&do_error, handle);
574 json_decref (id_json);
576 type_json = json_object_get (data_json, "type");
577 if (!json_is_string (type_json) ||
578 (0 != strcmp (GNUNET_REST_JSONAPI_IDENTITY_EGO, json_string_value (type_json))))
580 json_decref (root_json);
581 json_decref (data_json);
582 GNUNET_SCHEDULER_add_now (&do_error, handle);
585 json_decref (type_json);
587 subsys_json = json_object_get (data_json, "subsystem");
588 if (!json_is_string (subsys_json))
590 json_decref (root_json);
591 json_decref (data_json);
592 GNUNET_SCHEDULER_add_now (&do_error, handle);
595 subsys = json_string_value (subsys_json);
596 GNUNET_asprintf (&handle->subsys, "%s", subsys);
597 json_decref (subsys_json);
598 json_decref (root_json);
599 handle->op = GNUNET_IDENTITY_set (handle->identity_handle,
607 ego_delete_cont (struct RequestHandle *handle)
610 struct EgoEntry *ego_entry;
611 struct MHD_Response *resp;
612 int ego_exists = GNUNET_NO;
614 if (strlen (API_NAMESPACE) >= strlen (handle->url))
616 GNUNET_SCHEDULER_add_now (&do_error, handle);
620 egoname = &handle->url[strlen(API_NAMESPACE)+1];
621 for (ego_entry = handle->ego_head;
623 ego_entry = ego_entry->next)
625 if (0 == strcasecmp (egoname, ego_entry->identifier))
627 ego_exists = GNUNET_YES;
631 if (GNUNET_NO == ego_exists)
633 resp = GNUNET_REST_create_json_response (NULL);
634 handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND);
635 cleanup_handle (handle);
638 handle->op = GNUNET_IDENTITY_delete (handle->identity_handle,
646 init_cont (struct RequestHandle *handle)
648 if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_GET))
649 ego_info_response (handle);
650 else if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_POST))
651 ego_create_cont (handle);
652 else if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_PUT))
653 subsys_set_cont (handle);
654 else if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_DELETE))
655 ego_delete_cont (handle);
657 GNUNET_SCHEDULER_add_now (&do_error, handle);
661 * If listing is enabled, prints information about the egos.
663 * This function is initially called for all egos and then again
664 * whenever a ego's identifier changes or if it is deleted. At the
665 * end of the initial pass over all egos, the function is once called
666 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
667 * be invoked in the future or that there was an error.
669 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
670 * this function is only called ONCE, and 'NULL' being passed in
671 * 'ego' does indicate an error (i.e. name is taken or no default
672 * value is known). If 'ego' is non-NULL and if '*ctx'
673 * is set in those callbacks, the value WILL be passed to a subsequent
674 * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
675 * that one was not NULL).
677 * When an identity is renamed, this function is called with the
678 * (known) ego but the NEW identifier.
680 * When an identity is deleted, this function is called with the
681 * (known) ego and "NULL" for the 'identifier'. In this case,
682 * the 'ego' is henceforth invalid (and the 'ctx' should also be
686 * @param ego ego handle
687 * @param ctx context for application to store data for this ego
688 * (during the lifetime of this process, initially NULL)
689 * @param identifier identifier assigned by the user for this ego,
690 * NULL if the user just deleted the ego and it
691 * must thus no longer be used
695 struct GNUNET_IDENTITY_Ego *ego,
697 const char *identifier)
699 struct RequestHandle *handle = cls;
700 struct EgoEntry *ego_entry;
702 if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
704 handle->state = ID_REST_STATE_POST_INIT;
708 if (ID_REST_STATE_INIT == handle->state) {
709 ego_entry = GNUNET_new (struct EgoEntry);
710 GNUNET_IDENTITY_ego_get_public_key (ego, &(ego_entry->pk));
711 ego_entry->ego = ego;
712 GNUNET_asprintf (&ego_entry->identifier, "%s", identifier);
713 GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
719 * Function processing the REST call
721 * @param method HTTP method
722 * @param url URL of the HTTP request
723 * @param data body of the HTTP request (optional)
724 * @param data_size length of the body
725 * @param proc callback function for the result
726 * @param proc_cls closure for callback function
727 * @return GNUNET_OK if request accepted
730 rest_identity_process_request(struct RestConnectionDataHandle *conndata_handle,
731 GNUNET_REST_ResultProcessor proc,
734 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
738 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
740 handle->proc_cls = proc_cls;
742 handle->state = ID_REST_STATE_INIT;
743 handle->conndata_handle = conndata_handle;
744 handle->data = conndata_handle->data;
745 handle->data_size = conndata_handle->data_size;
746 handle->method = conndata_handle->method;
747 GNUNET_asprintf (&handle->url, "%s", conndata_handle->url);
748 if (handle->url[strlen (handle->url)-1] == '/')
749 handle->url[strlen (handle->url)-1] = '\0';
750 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
752 handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
755 handle->timeout_task =
756 GNUNET_SCHEDULER_add_delayed (handle->timeout,
761 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
766 * Entry point for the plugin.
768 * @param cls Config info
769 * @return NULL on error, otherwise the plugin context
772 libgnunet_plugin_rest_identity_init (void *cls)
774 static struct Plugin plugin;
776 struct GNUNET_REST_Plugin *api;
778 if (NULL != plugin.cfg)
779 return NULL; /* can only initialize once! */
780 memset (&plugin, 0, sizeof (struct Plugin));
782 api = GNUNET_new (struct GNUNET_REST_Plugin);
784 api->name = API_NAMESPACE;
785 api->process_request = &rest_identity_process_request;
786 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
787 _("Identity REST API initialized\n"));
793 * Exit point from the plugin.
795 * @param cls the plugin context (as returned by "init")
796 * @return always NULL
799 libgnunet_plugin_rest_identity_done (void *cls)
801 struct GNUNET_REST_Plugin *api = cls;
802 struct Plugin *plugin = api->cls;
806 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
807 "Identity REST plugin is finished\n");
811 /* end of plugin_rest_gns.c */