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"
37 #define ID_REST_STATE_INIT 0
39 #define ID_REST_STATE_POST_INIT 1
42 * @brief struct returned by the initialization function of the plugin
46 const struct GNUNET_CONFIGURATION_Handle *cfg;
49 const struct GNUNET_CONFIGURATION_Handle *cfg;
56 struct EgoEntry *next;
61 struct EgoEntry *prev;
71 struct GNUNET_CRYPTO_EcdsaPublicKey pk;
76 struct GNUNET_IDENTITY_Ego *ego;
84 struct EgoEntry *ego_head;
89 struct EgoEntry *ego_tail;
92 * The processing state
97 * Handle to GNS service.
99 struct GNUNET_IDENTITY_Handle *identity_handle;
104 struct GNUNET_IDENTITY_Operation *op;
107 * Desired timeout for the lookup (default is no timeout).
109 struct GNUNET_TIME_Relative timeout;
112 * ID of a task associated with the resolution process.
114 struct GNUNET_SCHEDULER_Task * timeout_task;
117 * The root of the received JSON or NULL
122 * The plugin result processor
124 GNUNET_REST_ResultProcessor proc;
127 * The closure of the result processor
132 * The name to look up
137 * The subsystem set from REST
147 * The data from the REST request
152 * the length of the REST data
164 * Cleanup lookup handle
165 * @praram handle Handle to clean up
168 cleanup_handle (struct RequestHandle *handle)
170 struct EgoEntry *ego_entry;
171 struct EgoEntry *ego_tmp;
172 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
174 if (NULL != handle->json_root)
175 json_decref (handle->json_root);
176 if (NULL != handle->name)
177 GNUNET_free (handle->name);
178 if (NULL != handle->timeout_task)
179 GNUNET_SCHEDULER_cancel (handle->timeout_task);
180 if (NULL != handle->identity_handle)
181 GNUNET_IDENTITY_disconnect (handle->identity_handle);
182 if (NULL != handle->subsys)
183 GNUNET_free (handle->subsys);
184 for (ego_entry = handle->ego_head;
188 ego_entry = ego_entry->next;
189 GNUNET_free (ego_tmp->identifier);
190 GNUNET_free (ego_tmp);
192 GNUNET_free (handle);
197 * Task run on shutdown. Cleans up everything.
200 * @param tc scheduler context
204 const struct GNUNET_SCHEDULER_TaskContext *tc)
206 struct RequestHandle *handle = cls;
207 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
208 cleanup_handle (handle);
214 ego_info_response (struct RequestHandle *handle)
219 struct EgoEntry *ego_entry;
223 if (strlen (EGO_NAMESPACE) > strlen (handle->url))
225 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
226 cleanup_handle (handle);
230 ego_arr = json_array ();
232 egoname = &handle->url[strlen (EGO_NAMESPACE)];
234 if (strlen (EGO_NAMESPACE) + 1 >= strlen (handle->url))
240 for (ego_entry = handle->ego_head;
242 ego_entry = ego_entry->next)
244 if ( (NULL != egoname) && (0 != strcmp (egoname, ego_entry->identifier)) )
246 ego_json = json_object ();
247 keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&ego_entry->pk);
248 json_object_set_new (ego_json, "identity", json_string (ego_entry->identifier));
249 json_object_set_new (ego_json, "key", json_string (keystring));
250 json_array_append (ego_arr, ego_json);
251 json_decref (ego_json);
252 GNUNET_free (keystring);
254 result_str = json_dumps (ego_arr, JSON_COMPACT);
255 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
256 json_decref (ego_arr);
257 handle->proc (handle->proc_cls, result_str, strlen (result_str), GNUNET_OK);
258 GNUNET_free (result_str);
259 cleanup_handle (handle);
264 do_finished (void *cls, const char *emsg)
266 struct RequestHandle *handle = cls;
271 GNUNET_SCHEDULER_add_now (&do_error, handle);
273 handle->proc (handle->proc_cls, NULL, 0, GNUNET_OK);
274 cleanup_handle (handle);
278 ego_create_cont (struct RequestHandle *handle)
281 char term_data[handle->data_size+1];
282 json_t *egoname_json;
285 struct EgoEntry *ego_entry;
287 if (strlen (API_NAMESPACE) != strlen (handle->url))
290 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
291 cleanup_handle (handle);
294 if (0 >= handle->data_size)
297 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
298 cleanup_handle (handle);
302 term_data[handle->data_size] = '\0';
303 memcpy (term_data, handle->data, handle->data_size);
304 root_json = json_loads (term_data, 0, &error);
306 if ((NULL == root_json) || !json_is_object (root_json))
309 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
310 cleanup_handle (handle);
313 egoname_json = json_object_get (root_json, "ego");
314 if (!json_is_string (egoname_json))
317 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
318 cleanup_handle (handle);
321 egoname = json_string_value (egoname_json);
322 for (ego_entry = handle->ego_head;
324 ego_entry = ego_entry->next)
326 if (0 == strcasecmp (egoname, ego_entry->identifier))
328 json_decref (egoname_json);
329 json_decref (root_json);
330 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
331 cleanup_handle (handle);
335 GNUNET_asprintf (&handle->name, "%s", egoname);
336 json_decref (egoname_json);
337 json_decref (root_json);
338 handle->op = GNUNET_IDENTITY_create (handle->identity_handle,
345 subsys_set_cont (struct RequestHandle *handle)
349 char term_data[handle->data_size+1];
350 struct EgoEntry *ego_entry;
351 int ego_exists = GNUNET_NO;
356 if (strlen (API_NAMESPACE)+1 >= strlen (handle->url))
359 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
360 cleanup_handle (handle);
364 egoname = &handle->url[strlen(API_NAMESPACE)+1];
365 for (ego_entry = handle->ego_head;
367 ego_entry = ego_entry->next)
369 if (0 == strcasecmp (egoname, ego_entry->identifier))
371 ego_exists = GNUNET_YES;
375 if (GNUNET_NO == ego_exists)
378 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
379 cleanup_handle (handle);
383 if (0 >= handle->data_size)
386 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
387 cleanup_handle (handle);
391 term_data[handle->data_size] = '\0';
392 memcpy (term_data, handle->data, handle->data_size);
393 root_json = json_loads (term_data, 0, &error);
395 if ((NULL == root_json) || !json_is_object (root_json))
398 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
399 cleanup_handle (handle);
402 subsys_json = json_object_get (root_json, "subsystem");
403 if (!json_is_string (subsys_json))
406 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
407 cleanup_handle (handle);
410 subsys = json_string_value (subsys_json);
411 GNUNET_asprintf (&handle->subsys, "%s", subsys);
412 json_decref (subsys_json);
413 json_decref (root_json);
414 handle->op = GNUNET_IDENTITY_set (handle->identity_handle,
422 ego_delete_cont (struct RequestHandle *handle)
425 struct EgoEntry *ego_entry;
426 int ego_exists = GNUNET_NO;
428 if (strlen (API_NAMESPACE)+1 >= strlen (handle->url))
431 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
432 cleanup_handle (handle);
436 egoname = &handle->url[strlen(API_NAMESPACE)+1];
437 for (ego_entry = handle->ego_head;
439 ego_entry = ego_entry->next)
441 if (0 == strcasecmp (egoname, ego_entry->identifier))
443 ego_exists = GNUNET_YES;
447 if (GNUNET_NO == ego_exists)
450 handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
451 cleanup_handle (handle);
454 handle->op = GNUNET_IDENTITY_delete (handle->identity_handle,
462 init_cont (struct RequestHandle *handle)
464 if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_GET))
465 ego_info_response (handle);
466 else if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_POST))
467 ego_create_cont (handle);
468 else if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_PUT))
469 subsys_set_cont (handle);
470 else if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_DELETE))
471 ego_delete_cont (handle);
473 GNUNET_SCHEDULER_add_now (&do_error, handle);
477 * If listing is enabled, prints information about the egos.
479 * This function is initially called for all egos and then again
480 * whenever a ego's identifier changes or if it is deleted. At the
481 * end of the initial pass over all egos, the function is once called
482 * with 'NULL' for 'ego'. That does NOT mean that the callback won't
483 * be invoked in the future or that there was an error.
485 * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
486 * this function is only called ONCE, and 'NULL' being passed in
487 * 'ego' does indicate an error (i.e. name is taken or no default
488 * value is known). If 'ego' is non-NULL and if '*ctx'
489 * is set in those callbacks, the value WILL be passed to a subsequent
490 * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
491 * that one was not NULL).
493 * When an identity is renamed, this function is called with the
494 * (known) ego but the NEW identifier.
496 * When an identity is deleted, this function is called with the
497 * (known) ego and "NULL" for the 'identifier'. In this case,
498 * the 'ego' is henceforth invalid (and the 'ctx' should also be
502 * @param ego ego handle
503 * @param ctx context for application to store data for this ego
504 * (during the lifetime of this process, initially NULL)
505 * @param identifier identifier assigned by the user for this ego,
506 * NULL if the user just deleted the ego and it
507 * must thus no longer be used
511 struct GNUNET_IDENTITY_Ego *ego,
513 const char *identifier)
515 struct RequestHandle *handle = cls;
516 struct EgoEntry *ego_entry;
518 if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
520 handle->state = ID_REST_STATE_POST_INIT;
524 if (ID_REST_STATE_INIT == handle->state) {
525 ego_entry = GNUNET_new (struct EgoEntry);
526 GNUNET_IDENTITY_ego_get_public_key (ego, &(ego_entry->pk));
527 ego_entry->ego = ego;
528 GNUNET_asprintf (&ego_entry->identifier, "%s", identifier);
529 GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
535 * Function processing the REST call
537 * @param method HTTP method
538 * @param url URL of the HTTP request
539 * @param data body of the HTTP request (optional)
540 * @param data_size length of the body
541 * @param proc callback function for the result
542 * @param proc_cls closure for callback function
543 * @return GNUNET_OK if request accepted
546 rest_identity_process_request(struct RestConnectionDataHandle *conndata_handle,
547 GNUNET_REST_ResultProcessor proc,
550 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
554 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
556 handle->proc_cls = proc_cls;
558 handle->state = ID_REST_STATE_INIT;
559 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
561 handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
564 handle->timeout_task =
565 GNUNET_SCHEDULER_add_delayed (handle->timeout,
568 handle->data = conndata_handle->data;
569 handle->data_size = conndata_handle->data_size;
570 handle->url = conndata_handle->url;
571 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
573 handle->method = conndata_handle->method;
577 * Entry point for the plugin.
579 * @param cls Config info
580 * @return NULL on error, otherwise the plugin context
583 libgnunet_plugin_rest_identity_init (void *cls)
585 static struct Plugin plugin;
587 struct GNUNET_REST_Plugin *api;
589 if (NULL != plugin.cfg)
590 return NULL; /* can only initialize once! */
591 memset (&plugin, 0, sizeof (struct Plugin));
593 api = GNUNET_new (struct GNUNET_REST_Plugin);
595 api->name = API_NAMESPACE;
596 api->process_request = &rest_identity_process_request;
597 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
598 _("Identity REST API initialized\n"));
604 * Exit point from the plugin.
606 * @param cls the plugin context (as returned by "init")
607 * @return always NULL
610 libgnunet_plugin_rest_identity_done (void *cls)
612 struct GNUNET_REST_Plugin *api = cls;
613 struct Plugin *plugin = api->cls;
617 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
618 "Identity REST plugin is finished\n");
622 /* end of plugin_rest_gns.c */