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), GNUNET_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);
386 handle->proc (handle->proc_cls, NULL, 0, GNUNET_OK);
387 cleanup_handle (handle);
391 ego_create_cont (struct RequestHandle *handle)
394 char term_data[handle->data_size+1];
395 json_t *egoname_json;
398 struct EgoEntry *ego_entry;
400 if (strlen (API_NAMESPACE) != strlen (handle->url))
403 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
404 cleanup_handle (handle);
407 if (0 >= handle->data_size)
410 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
411 cleanup_handle (handle);
415 term_data[handle->data_size] = '\0';
416 memcpy (term_data, handle->data, handle->data_size);
417 root_json = json_loads (term_data, 0, &error);
419 if ((NULL == root_json) || !json_is_object (root_json))
422 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
423 cleanup_handle (handle);
426 egoname_json = json_object_get (root_json, "ego");
427 if (!json_is_string (egoname_json))
430 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
431 cleanup_handle (handle);
434 egoname = json_string_value (egoname_json);
435 for (ego_entry = handle->ego_head;
437 ego_entry = ego_entry->next)
439 if (0 == strcasecmp (egoname, ego_entry->identifier))
441 json_decref (egoname_json);
442 json_decref (root_json);
443 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
444 cleanup_handle (handle);
448 GNUNET_asprintf (&handle->name, "%s", egoname);
449 json_decref (egoname_json);
450 json_decref (root_json);
451 handle->op = GNUNET_IDENTITY_create (handle->identity_handle,
458 subsys_set_cont (struct RequestHandle *handle)
462 char term_data[handle->data_size+1];
463 struct EgoEntry *ego_entry;
464 int ego_exists = GNUNET_NO;
469 if (strlen (API_NAMESPACE) >= strlen (handle->url))
472 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
473 cleanup_handle (handle);
477 egoname = &handle->url[strlen(API_NAMESPACE)+1];
478 for (ego_entry = handle->ego_head;
480 ego_entry = ego_entry->next)
482 if (0 == strcasecmp (egoname, ego_entry->identifier))
484 ego_exists = GNUNET_YES;
488 if (GNUNET_NO == ego_exists)
491 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
492 cleanup_handle (handle);
496 if (0 >= handle->data_size)
499 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
500 cleanup_handle (handle);
504 term_data[handle->data_size] = '\0';
505 memcpy (term_data, handle->data, handle->data_size);
506 root_json = json_loads (term_data, 0, &error);
508 if ((NULL == root_json) || !json_is_object (root_json))
511 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
512 cleanup_handle (handle);
515 subsys_json = json_object_get (root_json, "subsystem");
516 if (!json_is_string (subsys_json))
519 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
520 cleanup_handle (handle);
523 subsys = json_string_value (subsys_json);
524 GNUNET_asprintf (&handle->subsys, "%s", subsys);
525 json_decref (subsys_json);
526 json_decref (root_json);
527 handle->op = GNUNET_IDENTITY_set (handle->identity_handle,
535 ego_delete_cont (struct RequestHandle *handle)
538 struct EgoEntry *ego_entry;
539 int ego_exists = GNUNET_NO;
541 if (strlen (API_NAMESPACE) >= strlen (handle->url))
544 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
545 cleanup_handle (handle);
549 egoname = &handle->url[strlen(API_NAMESPACE)+1];
550 for (ego_entry = handle->ego_head;
552 ego_entry = ego_entry->next)
554 if (0 == strcasecmp (egoname, ego_entry->identifier))
556 ego_exists = GNUNET_YES;
560 if (GNUNET_NO == ego_exists)
563 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
564 cleanup_handle (handle);
567 handle->op = GNUNET_IDENTITY_delete (handle->identity_handle,
575 init_cont (struct RequestHandle *handle)
577 if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_GET))
578 ego_info_response (handle);
579 else if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_POST))
580 ego_create_cont (handle);
581 else if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_PUT))
582 subsys_set_cont (handle);
583 else if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_DELETE))
584 ego_delete_cont (handle);
586 GNUNET_SCHEDULER_add_now (&do_error, handle);
590 * If listing is enabled, prints information about the egos.
592 * This function is initially called for all egos and then again
593 * whenever a ego's identifier changes or if it is deleted. At the
594 * end of the initial pass over all egos, the function is once called
595 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
596 * be invoked in the future or that there was an error.
598 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
599 * this function is only called ONCE, and 'NULL' being passed in
600 * 'ego' does indicate an error (i.e. name is taken or no default
601 * value is known). If 'ego' is non-NULL and if '*ctx'
602 * is set in those callbacks, the value WILL be passed to a subsequent
603 * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
604 * that one was not NULL).
606 * When an identity is renamed, this function is called with the
607 * (known) ego but the NEW identifier.
609 * When an identity is deleted, this function is called with the
610 * (known) ego and "NULL" for the 'identifier'. In this case,
611 * the 'ego' is henceforth invalid (and the 'ctx' should also be
615 * @param ego ego handle
616 * @param ctx context for application to store data for this ego
617 * (during the lifetime of this process, initially NULL)
618 * @param identifier identifier assigned by the user for this ego,
619 * NULL if the user just deleted the ego and it
620 * must thus no longer be used
624 struct GNUNET_IDENTITY_Ego *ego,
626 const char *identifier)
628 struct RequestHandle *handle = cls;
629 struct EgoEntry *ego_entry;
631 if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
633 handle->state = ID_REST_STATE_POST_INIT;
637 if (ID_REST_STATE_INIT == handle->state) {
638 ego_entry = GNUNET_new (struct EgoEntry);
639 GNUNET_IDENTITY_ego_get_public_key (ego, &(ego_entry->pk));
640 ego_entry->ego = ego;
641 GNUNET_asprintf (&ego_entry->identifier, "%s", identifier);
642 GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
648 * Function processing the REST call
650 * @param method HTTP method
651 * @param url URL of the HTTP request
652 * @param data body of the HTTP request (optional)
653 * @param data_size length of the body
654 * @param proc callback function for the result
655 * @param proc_cls closure for callback function
656 * @return GNUNET_OK if request accepted
659 rest_identity_process_request(struct RestConnectionDataHandle *conndata_handle,
660 GNUNET_REST_ResultProcessor proc,
663 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
667 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
669 handle->proc_cls = proc_cls;
671 handle->state = ID_REST_STATE_INIT;
672 handle->conndata_handle = conndata_handle;
673 handle->data = conndata_handle->data;
674 handle->data_size = conndata_handle->data_size;
675 handle->method = conndata_handle->method;
676 GNUNET_asprintf (&handle->url, "%s", conndata_handle->url);
677 if (handle->url[strlen (handle->url)-1] == '/')
678 handle->url[strlen (handle->url)-1] = '\0';
679 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
681 handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
684 handle->timeout_task =
685 GNUNET_SCHEDULER_add_delayed (handle->timeout,
690 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
695 * Entry point for the plugin.
697 * @param cls Config info
698 * @return NULL on error, otherwise the plugin context
701 libgnunet_plugin_rest_identity_init (void *cls)
703 static struct Plugin plugin;
705 struct GNUNET_REST_Plugin *api;
707 if (NULL != plugin.cfg)
708 return NULL; /* can only initialize once! */
709 memset (&plugin, 0, sizeof (struct Plugin));
711 api = GNUNET_new (struct GNUNET_REST_Plugin);
713 api->name = API_NAMESPACE;
714 api->process_request = &rest_identity_process_request;
715 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
716 _("Identity REST API initialized\n"));
722 * Exit point from the plugin.
724 * @param cls the plugin context (as returned by "init")
725 * @return always NULL
728 libgnunet_plugin_rest_identity_done (void *cls)
730 struct GNUNET_REST_Plugin *api = cls;
731 struct Plugin *plugin = api->cls;
735 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
736 "Identity REST plugin is finished\n");
740 /* end of plugin_rest_gns.c */