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 "microhttpd.h"
33 #define API_NAMESPACE "/identity"
35 #define EGO_NAMESPACE "/identity/egos"
37 #define ID_REST_STATE_INIT 0
39 #define ID_REST_STATE_POST_INIT 1
41 #define URL_PARAM_SUBSYS "service"
43 #define JSON_API_TYPE_EGO "ego"
45 #define JSON_API_TYPE_DATA "data"
48 * @brief struct returned by the initialization function of the plugin
52 const struct GNUNET_CONFIGURATION_Handle *cfg;
55 const struct GNUNET_CONFIGURATION_Handle *cfg;
62 struct EgoEntry *next;
67 struct EgoEntry *prev;
77 struct GNUNET_CRYPTO_EcdsaPublicKey pk;
82 struct GNUNET_IDENTITY_Ego *ego;
90 struct EgoEntry *ego_head;
95 struct EgoEntry *ego_tail;
97 struct RestConnectionDataHandle *conndata_handle;
100 * The processing state
105 * Handle to GNS service.
107 struct GNUNET_IDENTITY_Handle *identity_handle;
112 struct GNUNET_IDENTITY_Operation *op;
115 * Desired timeout for the lookup (default is no timeout).
117 struct GNUNET_TIME_Relative timeout;
120 * ID of a task associated with the resolution process.
122 struct GNUNET_SCHEDULER_Task * timeout_task;
125 * The root of the received JSON or NULL
130 * The plugin result processor
132 GNUNET_REST_ResultProcessor proc;
135 * The closure of the result processor
140 * The name to look up
145 * The subsystem set from REST
155 * The data from the REST request
160 * the length of the REST data
172 * Cleanup lookup handle
173 * @praram handle Handle to clean up
176 cleanup_handle (struct RequestHandle *handle)
178 struct EgoEntry *ego_entry;
179 struct EgoEntry *ego_tmp;
180 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
182 if (NULL != handle->json_root)
183 json_decref (handle->json_root);
184 if (NULL != handle->name)
185 GNUNET_free (handle->name);
186 if (NULL != handle->timeout_task)
187 GNUNET_SCHEDULER_cancel (handle->timeout_task);
188 if (NULL != handle->identity_handle)
189 GNUNET_IDENTITY_disconnect (handle->identity_handle);
190 if (NULL != handle->subsys)
191 GNUNET_free (handle->subsys);
192 if (NULL != handle->url)
193 GNUNET_free (handle->url);
194 for (ego_entry = handle->ego_head;
198 ego_entry = ego_entry->next;
199 GNUNET_free (ego_tmp->identifier);
200 GNUNET_free (ego_tmp);
202 GNUNET_free (handle);
207 * Task run on shutdown. Cleans up everything.
210 * @param tc scheduler context
214 const struct GNUNET_SCHEDULER_TaskContext *tc)
216 struct RequestHandle *handle = cls;
217 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
218 cleanup_handle (handle);
222 get_ego_for_subsys (void *cls,
223 struct GNUNET_IDENTITY_Ego *ego,
227 struct RequestHandle *handle = cls;
228 struct EgoEntry *ego_entry;
234 root_json = json_object ();
237 for (ego_entry = handle->ego_head;
239 ego_entry = ego_entry->next)
241 if ( (NULL != name) && (0 != strcmp (name, ego_entry->identifier)) )
245 ego_json = json_object ();
246 keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&ego_entry->pk);
247 json_object_set_new (ego_json, "id", json_string (ego_entry->identifier));
248 json_object_set_new (ego_json, "type", json_string (JSON_API_TYPE_EGO));
249 json_object_set_new (ego_json, "key", json_string (keystring));
250 GNUNET_free (keystring);
253 if (NULL == ego_json)
255 json_decref (root_json);
256 GNUNET_SCHEDULER_add_now (&do_error, handle);
259 json_object_set (root_json, JSON_API_TYPE_DATA, ego_json);
260 result_str = json_dumps (root_json, JSON_COMPACT);
261 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
262 json_decref (ego_json);
263 json_decref (root_json);
264 handle->proc (handle->proc_cls, result_str, strlen (result_str), GNUNET_OK);
265 GNUNET_free (result_str);
266 cleanup_handle (handle);
270 check_namespace (const char *url, const char *ns)
272 if (0 != strncmp (EGO_NAMESPACE, url, strlen (EGO_NAMESPACE)))
277 if ((strlen (EGO_NAMESPACE) < strlen (url)) &&
278 (url[strlen (EGO_NAMESPACE)] != '/'))
288 ego_info_response (struct RequestHandle *handle)
294 struct EgoEntry *ego_entry;
295 struct GNUNET_HashCode key;
300 if (GNUNET_NO == check_namespace (handle->url, EGO_NAMESPACE))
302 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
303 cleanup_handle (handle);
307 if ( (strlen (EGO_NAMESPACE) == strlen (handle->url) )) {
308 GNUNET_CRYPTO_hash (URL_PARAM_SUBSYS, strlen (URL_PARAM_SUBSYS), &key);
310 GNUNET_CONTAINER_multihashmap_contains (handle->conndata_handle->url_param_map,
313 subsys_val = GNUNET_CONTAINER_multihashmap_get (handle->conndata_handle->url_param_map,
315 if (NULL != subsys_val)
317 GNUNET_asprintf (&handle->subsys, "%s", subsys_val);
318 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Looking for %s's ego\n", subsys_val);
319 handle->op = GNUNET_IDENTITY_get (handle->identity_handle,
327 ego_arr = json_array ();
328 root_json = json_object ();
329 egoname = &handle->url[strlen (EGO_NAMESPACE)+1];
331 if (strlen (EGO_NAMESPACE) == 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 ego_json = json_object ();
344 keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&ego_entry->pk);
345 json_object_set_new (ego_json, "id", json_string (ego_entry->identifier));
346 json_object_set_new (ego_json, "key", json_string (keystring));
347 json_object_set_new (ego_json, "type", json_string (JSON_API_TYPE_EGO));
348 GNUNET_free (keystring);
352 json_array_append (ego_arr, ego_json);
353 json_decref (ego_json);
360 json_object_set (root_json, JSON_API_TYPE_DATA, ego_arr);
362 json_object_set (root_json, JSON_API_TYPE_DATA, ego_json);
364 result_str = json_dumps (root_json, JSON_COMPACT);
365 json_decref (ego_arr);
367 json_decref (ego_json);
369 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
370 handle->proc (handle->proc_cls, result_str, strlen (result_str), MHD_HTTP_OK);
371 GNUNET_free (result_str);
372 cleanup_handle (handle);
377 do_finished (void *cls, const char *emsg)
379 struct RequestHandle *handle = cls;
384 GNUNET_SCHEDULER_add_now (&do_error, handle);
387 handle->proc (handle->proc_cls, NULL, 0, GNUNET_OK);
388 cleanup_handle (handle);
392 set_finished (void *cls, const char *emsg)
394 struct RequestHandle *handle = cls;
399 GNUNET_SCHEDULER_add_now (&do_error, handle);
402 handle->proc (handle->proc_cls, NULL, 0, MHD_HTTP_NO_CONTENT);
403 cleanup_handle (handle);
407 create_finished (void *cls, const char *emsg)
409 struct RequestHandle *handle = cls;
414 GNUNET_SCHEDULER_add_now (&do_error, handle);
417 handle->proc (handle->proc_cls, NULL, 0, MHD_HTTP_NO_CONTENT);
418 cleanup_handle (handle);
422 ego_create_cont (struct RequestHandle *handle)
425 char term_data[handle->data_size+1];
426 json_t *egoname_json;
429 struct EgoEntry *ego_entry;
431 if (strlen (API_NAMESPACE) != strlen (handle->url))
434 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
435 cleanup_handle (handle);
438 if (0 >= handle->data_size)
441 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
442 cleanup_handle (handle);
446 term_data[handle->data_size] = '\0';
447 memcpy (term_data, handle->data, handle->data_size);
448 root_json = json_loads (term_data, 0, &error);
450 if ((NULL == root_json) || !json_is_object (root_json))
453 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
454 cleanup_handle (handle);
457 egoname_json = json_object_get (root_json, "ego");
458 if (!json_is_string (egoname_json))
461 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
462 cleanup_handle (handle);
465 egoname = json_string_value (egoname_json);
466 for (ego_entry = handle->ego_head;
468 ego_entry = ego_entry->next)
470 if (0 == strcasecmp (egoname, ego_entry->identifier))
472 json_decref (egoname_json);
473 json_decref (root_json);
474 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
475 cleanup_handle (handle);
479 GNUNET_asprintf (&handle->name, "%s", egoname);
480 json_decref (egoname_json);
481 json_decref (root_json);
482 handle->op = GNUNET_IDENTITY_create (handle->identity_handle,
489 subsys_set_cont (struct RequestHandle *handle)
493 char term_data[handle->data_size+1];
494 struct EgoEntry *ego_entry;
495 int ego_exists = GNUNET_NO;
503 if (strlen (API_NAMESPACE) > strlen (handle->url))
506 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
507 cleanup_handle (handle);
511 egoname = &handle->url[strlen(EGO_NAMESPACE)+1];
512 for (ego_entry = handle->ego_head;
514 ego_entry = ego_entry->next)
516 if (0 == strcasecmp (egoname, ego_entry->identifier))
518 ego_exists = GNUNET_YES;
522 if (GNUNET_NO == ego_exists)
525 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
526 cleanup_handle (handle);
530 if (0 >= handle->data_size)
533 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
534 cleanup_handle (handle);
538 term_data[handle->data_size] = '\0';
539 memcpy (term_data, handle->data, handle->data_size);
540 root_json = json_loads (term_data, 0, &error);
542 if ((NULL == root_json) || !json_is_object (root_json))
544 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
545 cleanup_handle (handle);
548 data_json = json_object_get (root_json, "data");
549 if (!json_is_object (data_json))
551 json_decref (root_json);
552 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
553 cleanup_handle (handle);
556 id_json = json_object_get (data_json, "id");
557 if (!json_is_string (id_json) ||
558 (0 != strcmp (egoname, json_string_value (id_json))))
560 json_decref (root_json);
561 json_decref (data_json);
562 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
563 cleanup_handle (handle);
566 json_decref (id_json);
568 type_json = json_object_get (data_json, "type");
569 if (!json_is_string (type_json) ||
570 (0 != strcmp (JSON_API_TYPE_EGO, json_string_value (type_json))))
572 json_decref (root_json);
573 json_decref (data_json);
574 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
575 cleanup_handle (handle);
579 json_decref (type_json);
581 subsys_json = json_object_get (data_json, "subsystem");
582 if (!json_is_string (subsys_json))
584 json_decref (root_json);
585 json_decref (data_json);
586 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
587 cleanup_handle (handle);
590 subsys = json_string_value (subsys_json);
591 GNUNET_asprintf (&handle->subsys, "%s", subsys);
592 json_decref (subsys_json);
593 json_decref (root_json);
594 handle->op = GNUNET_IDENTITY_set (handle->identity_handle,
602 ego_delete_cont (struct RequestHandle *handle)
605 struct EgoEntry *ego_entry;
606 int ego_exists = GNUNET_NO;
608 if (strlen (API_NAMESPACE) >= strlen (handle->url))
611 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
612 cleanup_handle (handle);
616 egoname = &handle->url[strlen(API_NAMESPACE)+1];
617 for (ego_entry = handle->ego_head;
619 ego_entry = ego_entry->next)
621 if (0 == strcasecmp (egoname, ego_entry->identifier))
623 ego_exists = GNUNET_YES;
627 if (GNUNET_NO == ego_exists)
630 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
631 cleanup_handle (handle);
634 handle->op = GNUNET_IDENTITY_delete (handle->identity_handle,
642 init_cont (struct RequestHandle *handle)
644 if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_GET))
645 ego_info_response (handle);
646 else if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_POST))
647 ego_create_cont (handle);
648 else if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_PUT))
649 subsys_set_cont (handle);
650 else if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_DELETE))
651 ego_delete_cont (handle);
653 GNUNET_SCHEDULER_add_now (&do_error, handle);
657 * If listing is enabled, prints information about the egos.
659 * This function is initially called for all egos and then again
660 * whenever a ego's identifier changes or if it is deleted. At the
661 * end of the initial pass over all egos, the function is once called
662 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
663 * be invoked in the future or that there was an error.
665 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
666 * this function is only called ONCE, and 'NULL' being passed in
667 * 'ego' does indicate an error (i.e. name is taken or no default
668 * value is known). If 'ego' is non-NULL and if '*ctx'
669 * is set in those callbacks, the value WILL be passed to a subsequent
670 * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
671 * that one was not NULL).
673 * When an identity is renamed, this function is called with the
674 * (known) ego but the NEW identifier.
676 * When an identity is deleted, this function is called with the
677 * (known) ego and "NULL" for the 'identifier'. In this case,
678 * the 'ego' is henceforth invalid (and the 'ctx' should also be
682 * @param ego ego handle
683 * @param ctx context for application to store data for this ego
684 * (during the lifetime of this process, initially NULL)
685 * @param identifier identifier assigned by the user for this ego,
686 * NULL if the user just deleted the ego and it
687 * must thus no longer be used
691 struct GNUNET_IDENTITY_Ego *ego,
693 const char *identifier)
695 struct RequestHandle *handle = cls;
696 struct EgoEntry *ego_entry;
698 if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
700 handle->state = ID_REST_STATE_POST_INIT;
704 if (ID_REST_STATE_INIT == handle->state) {
705 ego_entry = GNUNET_new (struct EgoEntry);
706 GNUNET_IDENTITY_ego_get_public_key (ego, &(ego_entry->pk));
707 ego_entry->ego = ego;
708 GNUNET_asprintf (&ego_entry->identifier, "%s", identifier);
709 GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
715 * Function processing the REST call
717 * @param method HTTP method
718 * @param url URL of the HTTP request
719 * @param data body of the HTTP request (optional)
720 * @param data_size length of the body
721 * @param proc callback function for the result
722 * @param proc_cls closure for callback function
723 * @return GNUNET_OK if request accepted
726 rest_identity_process_request(struct RestConnectionDataHandle *conndata_handle,
727 GNUNET_REST_ResultProcessor proc,
730 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
734 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
736 handle->proc_cls = proc_cls;
738 handle->state = ID_REST_STATE_INIT;
739 handle->conndata_handle = conndata_handle;
740 handle->data = conndata_handle->data;
741 handle->data_size = conndata_handle->data_size;
742 handle->method = conndata_handle->method;
743 GNUNET_asprintf (&handle->url, "%s", conndata_handle->url);
744 if (handle->url[strlen (handle->url)-1] == '/')
745 handle->url[strlen (handle->url)-1] = '\0';
746 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
748 handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
751 handle->timeout_task =
752 GNUNET_SCHEDULER_add_delayed (handle->timeout,
757 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
762 * Entry point for the plugin.
764 * @param cls Config info
765 * @return NULL on error, otherwise the plugin context
768 libgnunet_plugin_rest_identity_init (void *cls)
770 static struct Plugin plugin;
772 struct GNUNET_REST_Plugin *api;
774 if (NULL != plugin.cfg)
775 return NULL; /* can only initialize once! */
776 memset (&plugin, 0, sizeof (struct Plugin));
778 api = GNUNET_new (struct GNUNET_REST_Plugin);
780 api->name = API_NAMESPACE;
781 api->process_request = &rest_identity_process_request;
782 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
783 _("Identity REST API initialized\n"));
789 * Exit point from the plugin.
791 * @param cls the plugin context (as returned by "init")
792 * @return always NULL
795 libgnunet_plugin_rest_identity_done (void *cls)
797 struct GNUNET_REST_Plugin *api = cls;
798 struct Plugin *plugin = api->cls;
802 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
803 "Identity REST plugin is finished\n");
807 /* end of plugin_rest_gns.c */