From 6efdf409484a0b694bb3d6bcf95b1891f70886fb Mon Sep 17 00:00:00 2001 From: Phil Date: Tue, 3 Jul 2018 10:47:40 +0200 Subject: -wip namestore --- src/include/gnunet_json_lib.h | 64 ++ src/json/Makefile.am | 3 +- src/json/json_parser.c | 169 +++++ src/namestore/plugin_rest_namestore.c | 1163 +++++++++++------------------- src/namestore/plugin_rest_namestore2.c | 1229 ++++++++++++++++++++++++++++++++ 5 files changed, 1882 insertions(+), 746 deletions(-) create mode 100644 src/json/json_parser.c create mode 100644 src/namestore/plugin_rest_namestore2.c diff --git a/src/include/gnunet_json_lib.h b/src/include/gnunet_json_lib.h index 06749590c..6340d1f41 100644 --- a/src/include/gnunet_json_lib.h +++ b/src/include/gnunet_json_lib.h @@ -479,6 +479,70 @@ GNUNET_JSON_getopt (char shortName, const char *description, json_t **json); +/* ****************** GETOPT JSON parser ******************* */ + +struct GNUNET_REST_JSON_Data +{ + /** + * Public key of an identity + */ + char *pubkey; + + /** + * Name + */ + char *name; + + /** + * Nickname + */ + char *nickname; + + /** + * New name + */ + char *new_name; + + /** + * Name of subsystem + */ + char *subsystem; + + /** + * Should data be handled as public (GNUNET_YES or GNUNET_NO) + */ + int is_public; + + /** + * Expiration date of data + */ + char *expiration_time; + + /** + * Type of data + */ + char *type; + + /** + * Value of data + */ + char *value; + + /** + * Zone + */ + char *zone; +}; +/* + * Test + */ +int +GNUNET_REST_JSON_parse (struct GNUNET_REST_JSON_Data** rest_json_data, + json_t *json_data); + +int +GNUNET_REST_JSON_free (struct GNUNET_REST_JSON_Data* rest_json_data); + #endif diff --git a/src/json/Makefile.am b/src/json/Makefile.am index da19e7955..5aac23654 100644 --- a/src/json/Makefile.am +++ b/src/json/Makefile.am @@ -16,7 +16,8 @@ libgnunetjson_la_SOURCES = \ json.c \ json_mhd.c \ json_generator.c \ - json_helper.c + json_helper.c \ + json_parser.c libgnunetjson_la_LIBADD = \ $(top_builddir)/src/util/libgnunetutil.la \ -ljansson \ diff --git a/src/json/json_parser.c b/src/json/json_parser.c new file mode 100644 index 000000000..cfe553b34 --- /dev/null +++ b/src/json/json_parser.c @@ -0,0 +1,169 @@ +/* + This file is part of GNUnet + Copyright (C) 2014, 2015, 2016 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +/** + * @file json/json_helper.c + * @brief functions for REST JSON parsing + * @author Philippe Buschmann + */ + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_json_lib.h" + +#define GNUNET_REST_JSON_PUBKEY_ENTRY "pubkey" +#define GNUNET_REST_JSON_NAME_ENTRY "name" +#define GNUNET_REST_JSON_NICKNAME_ENTRY "nickname" +#define GNUNET_REST_JSON_NEWNAME_ENTRY "newname" +#define GNUNET_REST_JSON_SUBSYSTEM_ENTRY "subsystem" +#define GNUNET_REST_JSON_IS_PUBLIC_ENTRY "is_public" +#define GNUNET_REST_JSON_EXPIRATION_DATE_ENTRY "expiration_time" +#define GNUNET_REST_JSON_TYPE_ENTRY "type" +#define GNUNET_REST_JSON_VALUE_ENTRY "value" +#define GNUNET_REST_JSON_ZONE_ENTRY "zone" + + +int +GNUNET_REST_JSON_parse (struct GNUNET_REST_JSON_Data** output_data ,json_t *json_data) +{ + struct GNUNET_REST_JSON_Data *rest_json_data; + json_t *cache; + + rest_json_data = GNUNET_malloc(sizeof(struct GNUNET_REST_JSON_Data)); + + cache = json_object_get (json_data, GNUNET_REST_JSON_EXPIRATION_DATE_ENTRY); + rest_json_data->expiration_time = NULL; + if (NULL != cache) + { + if (json_is_string(cache)) + { + rest_json_data->expiration_time = GNUNET_strdup(json_string_value(cache)); + } + } + cache = json_object_get (json_data, GNUNET_REST_JSON_NAME_ENTRY); + rest_json_data->name = NULL; + if (NULL != cache) + { + if (json_is_string(cache)) + { + rest_json_data->name = GNUNET_strdup(json_string_value(cache)); + } + } + cache = json_object_get (json_data, GNUNET_REST_JSON_NEWNAME_ENTRY); + rest_json_data->new_name = NULL; + if (NULL != cache) + { + if (json_is_string(cache)) + { + rest_json_data->new_name = GNUNET_strdup(json_string_value(cache)); + } + } + cache = json_object_get (json_data, GNUNET_REST_JSON_NICKNAME_ENTRY); + rest_json_data->nickname = NULL; + if (NULL != cache) + { + if (json_is_string(cache)) + { + rest_json_data->nickname = GNUNET_strdup(json_string_value(cache)); + } + } + cache = json_object_get (json_data, GNUNET_REST_JSON_PUBKEY_ENTRY); + rest_json_data->pubkey = NULL; + if (NULL != cache) + { + if (json_is_string(cache)) + { + rest_json_data->pubkey = GNUNET_strdup(json_string_value(cache)); + } + } + cache = json_object_get (json_data, GNUNET_REST_JSON_SUBSYSTEM_ENTRY); + rest_json_data->subsystem = NULL; + if (NULL != cache) + { + if (json_is_string(cache)) + { + rest_json_data->subsystem = GNUNET_strdup(json_string_value(cache)); + } + } + cache = json_object_get (json_data, GNUNET_REST_JSON_TYPE_ENTRY); + rest_json_data->type = NULL; + if (NULL != cache) + { + if (json_is_string(cache)) + { + rest_json_data->type = GNUNET_strdup(json_string_value(cache)); + } + } + cache = json_object_get (json_data, GNUNET_REST_JSON_VALUE_ENTRY); + rest_json_data->value = NULL; + if (NULL != cache) + { + if (json_is_string(cache)) + { + rest_json_data->value = GNUNET_strdup(json_string_value(cache)); + } + } + cache = json_object_get (json_data, GNUNET_REST_JSON_ZONE_ENTRY); + rest_json_data->zone = NULL; + if (NULL != cache) + { + if (json_is_string(cache)) + { + rest_json_data->zone = GNUNET_strdup(json_string_value(cache)); + } + } + cache = json_object_get (json_data, GNUNET_REST_JSON_IS_PUBLIC_ENTRY); + if (NULL != cache) + { + if (json_is_integer(cache)) + { + rest_json_data->is_public = json_integer_value(cache); + } + } + *output_data = rest_json_data; + return GNUNET_OK; +} + + + +int +GNUNET_REST_JSON_free (struct GNUNET_REST_JSON_Data* rest_json_data) +{ + if (rest_json_data != NULL) + { + GNUNET_free_non_null(rest_json_data->expiration_time); + GNUNET_free_non_null(rest_json_data->name); + GNUNET_free_non_null(rest_json_data->new_name); + GNUNET_free_non_null(rest_json_data->nickname); + GNUNET_free_non_null(rest_json_data->pubkey); + GNUNET_free_non_null(rest_json_data->subsystem); + GNUNET_free_non_null(rest_json_data->type); + GNUNET_free_non_null(rest_json_data->value); + GNUNET_free_non_null(rest_json_data->zone); + } + GNUNET_free_non_null(rest_json_data); + return GNUNET_OK; +} + + + + + + + + + diff --git a/src/namestore/plugin_rest_namestore.c b/src/namestore/plugin_rest_namestore.c index ec44046e0..ab490c04f 100644 --- a/src/namestore/plugin_rest_namestore.c +++ b/src/namestore/plugin_rest_namestore.c @@ -2,24 +2,26 @@ This file is part of GNUnet. Copyright (C) 2012-2015 GNUnet e.V. - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ /** * @author Martin Schanzenbach + * @author Philippe Buschmann * @file namestore/plugin_rest_namestore.c * @brief GNUnet Namestore REST plugin - * */ #include "platform.h" @@ -28,38 +30,32 @@ #include "gnunet_namestore_service.h" #include "gnunet_identity_service.h" #include "gnunet_rest_lib.h" -#include "gnunet_jsonapi_lib.h" -#include "gnunet_jsonapi_util.h" +#include "gnunet_json_lib.h" #include "microhttpd.h" #include -#define GNUNET_REST_API_NS_NAMESTORE "/names" - -#define GNUNET_REST_API_NS_NAMESTORE_ZKEY "/names/zkey" - -#define GNUNET_REST_JSONAPI_NAMESTORE_TYPEINFO "record" - -#define GNUNET_REST_JSONAPI_NAMESTORE_NAME "name" - -#define GNUNET_REST_JSONAPI_NAMESTORE_REVINFO "revinfo" +#define GNUNET_REST_API_NS_NAMESTORE "/namestore" -#define GNUNET_REST_JSONAPI_NAMESTORE_RECORD GNUNET_REST_JSONAPI_NAMESTORE_TYPEINFO +#define GNUNET_REST_SUBSYSTEM_NAMESTORE "namestore" -#define GNUNET_REST_JSONAPI_NAMESTORE_RECORD_TYPE "record_type" +#define GNUNET_REST_JSON_NAMESTORE_RECORD_TYPE "record_type" +#define GNUNET_REST_JSON_NAMESTORE_VALUE "value" +#define GNUNET_REST_JSON_NAMESTORE_EXPIRATION "expiration" +#define GNUNET_REST_JSON_NAMESTORE_EXPIRED "expired" -#define GNUNET_REST_JSONAPI_NAMESTORE_VALUE "value" +#define GNUNET_REST_NAMESTORE_RD_COUNT 1 -#define GNUNET_REST_JSONAPI_NAMESTORE_PUBLIC "public" +//TODO define other variables -#define GNUNET_REST_JSONAPI_NAMESTORE_SHADOW "shadow" - -#define GNUNET_REST_JSONAPI_NAMESTORE_PKEY "pkey" - -#define GNUNET_REST_JSONAPI_NAMESTORE_ZKEY "zkey" - -#define GNUNET_REST_JSONAPI_NAMESTORE_EXPIRATION "expiration" +/** + * The configuration handle + */ +const struct GNUNET_CONFIGURATION_Handle *cfg; -#define GNUNET_REST_JSONAPI_NAMESTORE_EGO "ego" +/** + * HTTP methods allows for this plugin + */ +static char* allow_methods; /** * @brief struct returned by the initialization function of the plugin @@ -69,54 +65,53 @@ struct Plugin const struct GNUNET_CONFIGURATION_Handle *cfg; }; +//TODO add specific structs /** - * HTTP methods allows for this plugin + * The default namestore ego */ -static char* allow_methods; - -const struct GNUNET_CONFIGURATION_Handle *cfg; - -struct RecordEntry +struct EgoEntry { /** - * DLL + * Ego Identifier */ - struct RecordEntry *next; + const char *identifier; /** - * DLL + * Public key string */ - struct RecordEntry *prev; + char *keystring; + /** + * The Ego + */ + struct GNUNET_IDENTITY_Ego *ego; }; + struct RequestHandle { - /** - * Ego list - */ - struct RecordEntry *record_head; + //TODO add specific entries /** - * Ego list + * Records to store */ - struct record_entry *record_tail; + struct GNUNET_GNSRECORD_Data *rd; /** - * JSON response object + * NAMESTORE Operation */ - struct GNUNET_JSONAPI_Document *resp_object; + struct GNUNET_NAMESTORE_QueueEntry *add_qe; /** - * Rest connection + * JSON data parser */ - struct GNUNET_REST_RequestHandle *rest_handle; + struct GNUNET_REST_JSON_Data *json_data; /** - * Handle to GNS service. + * Response object */ - struct GNUNET_IDENTITY_Handle *identity_handle; + json_t *resp_object; /** * Handle to NAMESTORE @@ -134,70 +129,25 @@ struct RequestHandle struct GNUNET_CRYPTO_EcdsaPrivateKey zone_pkey; /** - * Handle to identity lookup - */ - struct GNUNET_IDENTITY_EgoLookup *ego_lookup; - - /** - * Default Ego operation - */ - struct GNUNET_IDENTITY_Operation *get_default; - - /** - * Name of the ego - */ - char *ego_name; - - /** - * Record is public - */ - int is_public; - - /** - * Shadow record - */ - int is_shadow; - - /** - * Name of the record to modify - */ - char *name; - - /** - * Value of the record - */ - char *value; - - /** - * Zkey string - */ - const char* zkey_str; - - /** - * record type + * IDENTITY Operation */ - uint32_t type; - - /** - * Records to store - */ - struct GNUNET_GNSRECORD_Data *rd; + struct EgoEntry *ego_entry; /** - * record count + * IDENTITY Operation */ - unsigned int rd_count; + struct GNUNET_IDENTITY_Operation *op; /** - * NAMESTORE Operation + * Handle to Identity service. */ - struct GNUNET_NAMESTORE_QueueEntry *add_qe; + struct GNUNET_IDENTITY_Handle *identity_handle; /** - * NAMESTORE Operation + * Rest connection */ - struct GNUNET_NAMESTORE_QueueEntry *reverse_qe; - + struct GNUNET_REST_RequestHandle *rest_handle; + /** * Desired timeout for the lookup (default is no timeout). */ @@ -206,7 +156,7 @@ struct RequestHandle /** * ID of a task associated with the resolution process. */ - struct GNUNET_SCHEDULER_Task * timeout_task; + struct GNUNET_SCHEDULER_Task *timeout_task; /** * The plugin result processor @@ -224,75 +174,122 @@ struct RequestHandle char *url; /** - * Cfg + * Error response message */ - const struct GNUNET_CONFIGURATION_Handle *cfg; + char *emsg; /** - * HTTP response code + * Reponse code */ int response_code; }; +//TODO add specific cleanup /** * Cleanup lookup handle - * * @param handle Handle to clean up */ static void cleanup_handle (struct RequestHandle *handle) { - struct RecordEntry *record_entry; - struct RecordEntry *record_tmp; + size_t index; + json_t *json_ego; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n"); - if (NULL != handle->resp_object) - GNUNET_JSONAPI_document_delete (handle->resp_object); - if (NULL != handle->name) - GNUNET_free (handle->name); if (NULL != handle->timeout_task) + { GNUNET_SCHEDULER_cancel (handle->timeout_task); - if (NULL != handle->ego_lookup) - GNUNET_IDENTITY_ego_lookup_cancel (handle->ego_lookup); - if (NULL != handle->get_default) - GNUNET_IDENTITY_cancel (handle->get_default); + handle->timeout_task = NULL; + } + if (NULL != handle->url) + GNUNET_free(handle->url); + if (NULL != handle->emsg) + GNUNET_free(handle->emsg); + if (NULL != handle->rd) + { + if (NULL != handle->rd->data) + GNUNET_free((void*)handle->rd->data); + GNUNET_free(handle->rd); + } + if (NULL != handle->timeout_task) + GNUNET_SCHEDULER_cancel(handle->timeout_task); if (NULL != handle->list_it) - GNUNET_NAMESTORE_zone_iteration_stop (handle->list_it); + GNUNET_NAMESTORE_zone_iteration_stop(handle->list_it); if (NULL != handle->add_qe) - GNUNET_NAMESTORE_cancel (handle->add_qe); + GNUNET_NAMESTORE_cancel(handle->add_qe); if (NULL != handle->identity_handle) - GNUNET_IDENTITY_disconnect (handle->identity_handle); + GNUNET_IDENTITY_disconnect(handle->identity_handle); if (NULL != handle->ns_handle) - GNUNET_NAMESTORE_disconnect (handle->ns_handle); - if (NULL != handle->url) - GNUNET_free (handle->url); - if (NULL != handle->value) - GNUNET_free (handle->value); - if (NULL != handle->rd) { - for (unsigned int i = 0; i < handle->rd_count; i++) - { - if (NULL != handle->rd[i].data) - GNUNET_free ((void*)handle->rd[i].data); - } - GNUNET_free (handle->rd); + GNUNET_NAMESTORE_disconnect(handle->ns_handle); } - if (NULL != handle->ego_name) - GNUNET_free (handle->ego_name); - for (record_entry = handle->record_head; - NULL != record_entry;) + + if (NULL != handle->ego_entry) { - record_tmp = record_entry; - record_entry = record_entry->next; - GNUNET_free (record_tmp); + if (NULL != handle->ego_entry->keystring) + GNUNET_free(handle->ego_entry->keystring); + + GNUNET_free(handle->ego_entry); + } + + if(NULL != handle->resp_object) + { + json_array_foreach(handle->resp_object, index, json_ego ) + { + json_decref (json_ego); + } + json_decref(handle->resp_object); } + + if (NULL != handle->json_data) + GNUNET_REST_JSON_free(handle->json_data); + GNUNET_free (handle); } +/** + * Task run on errors. Reports an error and cleans up everything. + * + * @param cls the `struct RequestHandle` + */ +static void +do_error (void *cls) +{ + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + char *json_error; + + if (NULL == handle->emsg) + handle->emsg = GNUNET_strdup("Unknown Error"); + + GNUNET_asprintf (&json_error, "{\"error\": \"%s\"}", handle->emsg); + + if (0 == handle->response_code) + handle->response_code = MHD_HTTP_OK; + + resp = GNUNET_REST_create_response (json_error); + handle->proc (handle->proc_cls, resp, handle->response_code); + cleanup_handle (handle); + GNUNET_free(json_error); +} + +/** + * Does internal server error when iteration failed. + */ +static void +namestore_iteration_error (void *cls) +{ + struct RequestHandle *handle = cls; + struct MHD_Response *resp = GNUNET_REST_create_response (NULL); + handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; + handle->proc (handle->proc_cls, resp, handle->response_code); + cleanup_handle (handle); +} + /** * Create json representation of a GNSRECORD * @@ -313,19 +310,19 @@ gnsrecord_to_json (const struct GNUNET_GNSRECORD_Data *rd) if (NULL == string_val) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Record of type %d malformed, skipping\n", (int) rd->record_type); return NULL; } record_obj = json_object(); json_object_set_new (record_obj, - GNUNET_REST_JSONAPI_NAMESTORE_RECORD_TYPE, + GNUNET_REST_JSON_NAMESTORE_RECORD_TYPE, json_string (typename)); json_object_set_new (record_obj, - GNUNET_REST_JSONAPI_NAMESTORE_VALUE, + GNUNET_REST_JSON_NAMESTORE_VALUE, json_string (string_val)); - GNUNET_free (string_val); + //GNUNET_free (string_val); if (GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION & rd->flags) { @@ -339,52 +336,33 @@ gnsrecord_to_json (const struct GNUNET_GNSRECORD_Data *rd) time_abs.abs_value_us = rd->expiration_time; exp_str = GNUNET_STRINGS_absolute_time_to_string (time_abs); } - json_object_set_new (record_obj, GNUNET_REST_JSONAPI_NAMESTORE_EXPIRATION, json_string (exp_str)); - + json_object_set_new (record_obj, + GNUNET_REST_JSON_NAMESTORE_EXPIRATION, + json_string (exp_str)); json_object_set_new (record_obj, "expired", json_boolean (GNUNET_YES == GNUNET_GNSRECORD_is_expired (rd))); return record_obj; } -/** - * Task run on error. Generates error response and cleans up. - * - * @param cls the request to generate an error response for - */ -static void -do_error (void *cls) -{ - struct RequestHandle *handle = cls; - struct MHD_Response *resp = GNUNET_REST_create_response (NULL); - - handle->proc (handle->proc_cls, resp, handle->response_code); - cleanup_handle (handle); -} - - -/** - * Task run on timeout. - * - * @param cls the request to time out - */ static void -do_timeout (void *cls) +create_finished (void *cls, int32_t success, const char *emsg) { struct RequestHandle *handle = cls; + struct MHD_Response *resp; - handle->timeout_task = NULL; - do_error (handle); -} - - -static void -cleanup_handle_delayed (void *cls) -{ - cleanup_handle (cls); + handle->add_qe = NULL; + if (GNUNET_YES != success) + { + handle->emsg = GNUNET_strdup("Error storing records"); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + resp = GNUNET_REST_create_response (NULL); + handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT); + cleanup_handle(handle); } - /** * Iteration over all results finished, build final * response. @@ -395,29 +373,25 @@ static void namestore_list_finished (void *cls) { struct RequestHandle *handle = cls; - char *result; + char *result_str; struct MHD_Response *resp; handle->list_it = NULL; - if (NULL == handle->resp_object) - handle->resp_object = GNUNET_JSONAPI_document_new (); - if (GNUNET_SYSERR == - GNUNET_JSONAPI_document_serialize (handle->resp_object, - &result)) + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "HEY\n"); + if (NULL == handle->resp_object) { - handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - GNUNET_SCHEDULER_add_now (&do_error, - handle); + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "OH\n"); + GNUNET_SCHEDULER_add_now (&do_error, handle); return; } - resp = GNUNET_REST_create_response (result); - handle->proc (handle->proc_cls, - resp, - MHD_HTTP_OK); - GNUNET_free_non_null (result); - GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, - handle); + + result_str = json_dumps (handle->resp_object, 0); + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str); + resp = GNUNET_REST_create_response (result_str); + handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); + GNUNET_free_non_null (result_str); + cleanup_handle(handle); } @@ -428,84 +402,172 @@ namestore_list_finished (void *cls) * @param handle the RequestHandle */ static void -namestore_list_response (void *cls, +namestore_list_iteration (void *cls, const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key, const char *rname, unsigned int rd_len, const struct GNUNET_GNSRECORD_Data *rd) { struct RequestHandle *handle = cls; - struct GNUNET_JSONAPI_Resource *json_resource; - json_t *result_array; json_t *record_obj; if (NULL == handle->resp_object) - handle->resp_object = GNUNET_JSONAPI_document_new (); + handle->resp_object = json_array(); - if ( (NULL != handle->name) && - (0 != strcmp (handle->name, + char *result_str = json_dumps (handle->resp_object, 0); + GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "%s\n", result_str); + GNUNET_free(result_str); + /*if ( (NULL != handle->ego_entry->identifier) && + (0 != strcmp (handle->ego_entry->identifier, rname)) ) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%s does not match %s\n", - rname, - handle->name); - GNUNET_NAMESTORE_zone_iterator_next (handle->list_it, - 1); + "%s does not match %s\n", rname, + handle->ego_entry->identifier); + GNUNET_NAMESTORE_zone_iterator_next (handle->list_it, 1); return; - } + }*/ - result_array = json_array (); - for (unsigned int i=0; itype) && - (GNUNET_GNSRECORD_TYPE_ANY != handle->type) ) - continue; record_obj = gnsrecord_to_json (&rd[i]); - json_array_append (result_array, + + if(NULL == record_obj) + continue; + + json_array_append (handle->resp_object, record_obj); json_decref (record_obj); } - if (0 < json_array_size(result_array)) + GNUNET_NAMESTORE_zone_iterator_next (handle->list_it, 1); +} + + +/** + * Handle namestore GET request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +void +namestore_get (struct GNUNET_REST_RequestHandle *con_handle, + const char* url, + void *cls) +{ + struct RequestHandle *handle = cls; + if (strlen (GNUNET_REST_API_NS_NAMESTORE) != strlen (handle->url)) { - json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_NAMESTORE_TYPEINFO, - rname); - GNUNET_JSONAPI_resource_add_attr (json_resource, - GNUNET_REST_JSONAPI_NAMESTORE_RECORD, - result_array); - GNUNET_JSONAPI_document_resource_add (handle->resp_object, json_resource); + handle->emsg = GNUNET_strdup("Wrong URL"); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; } - - json_decref (result_array); - GNUNET_NAMESTORE_zone_iterator_next (handle->list_it, - 1); + handle->list_it = GNUNET_NAMESTORE_zone_iteration_start (handle->ns_handle, + &handle->zone_pkey, + &namestore_iteration_error, + handle, + &namestore_list_iteration, + handle, + &namestore_list_finished, + handle); } +int +check_needed_data(struct RequestHandle *handle){ + if(NULL == handle->json_data->name) + { + handle->emsg = GNUNET_strdup("Missing JSON parameter: name"); + return GNUNET_SYSERR; + } + + if(NULL == handle->json_data->type) + { + handle->emsg = GNUNET_strdup("Missing JSON parameter: type"); + return GNUNET_SYSERR; + } -static void -create_finished (void *cls, int32_t success, const char *emsg) + if(NULL == handle->json_data->value) + { + handle->emsg = GNUNET_strdup("Missing JSON parameter: value"); + return GNUNET_SYSERR; + } + + if(NULL == handle->json_data->expiration_time) + { + handle->emsg = GNUNET_strdup("Missing JSON parameter: expiration time"); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + +//TODO filter input +static int +json_to_gnsrecord (struct RequestHandle *handle) { - struct RequestHandle *handle = cls; - struct MHD_Response *resp; + struct GNUNET_TIME_Relative etime_rel; + struct GNUNET_TIME_Absolute etime_abs; + void *rdata; + size_t rdata_size; - handle->add_qe = NULL; - if (GNUNET_YES != success) + handle->rd = GNUNET_new_array(GNUNET_REST_NAMESTORE_RD_COUNT, + struct GNUNET_GNSRECORD_Data); + memset (handle->rd, 0, sizeof(struct GNUNET_GNSRECORD_Data)); + handle->rd->record_type = GNUNET_GNSRECORD_typename_to_number ( + handle->json_data->type); + if (UINT32_MAX == (*handle->rd).record_type) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Error storing records%s%s\n", - (NULL == emsg) ? "" : ": ", - (NULL == emsg) ? "" : emsg); - GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); - return; + handle->emsg = GNUNET_strdup("Unsupported type"); + return GNUNET_SYSERR; } - resp = GNUNET_REST_create_response (NULL); - handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT); - GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); + if (GNUNET_OK + != GNUNET_GNSRECORD_string_to_value ((*handle->rd).record_type, + handle->json_data->value, &rdata, + &rdata_size)) + { + handle->emsg = GNUNET_strdup("Value invalid for record type"); + return GNUNET_SYSERR; + } + (*handle->rd).data = rdata; + (*handle->rd).data_size = rdata_size; + //TODO other flags + if (0 == handle->json_data->is_public) + { + handle->rd->flags |= GNUNET_GNSRECORD_RF_PRIVATE; + } + /**TODO + * if (1 == handle->is_shadow) + rde->flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD; + if (1 != handle->is_public) + rde->flags |= GNUNET_GNSRECORD_RF_PRIVATE; + */ + if (0 == strcmp (handle->json_data->expiration_time, "never")) + { + (*handle->rd).expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us; + } + else if (GNUNET_OK + == GNUNET_STRINGS_fancy_time_to_relative ( + handle->json_data->expiration_time, &etime_rel)) + { + (*handle->rd).expiration_time = etime_rel.rel_value_us; + (*handle->rd).flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; + } + else if (GNUNET_OK + == GNUNET_STRINGS_fancy_time_to_absolute ( + handle->json_data->expiration_time, &etime_abs)) + { + (*handle->rd).expiration_time = etime_abs.abs_value_us; + } + else + { + handle->emsg = GNUNET_strdup("Value invalid for record type"); + return GNUNET_SYSERR; + } + return GNUNET_OK; } @@ -529,7 +591,7 @@ create_new_record_cont (void *cls, struct RequestHandle *handle = cls; handle->add_qe = NULL; - if (0 != strcmp (rec_name, handle->name)) + if (0 != strcmp (rec_name, handle->json_data->name)) { GNUNET_break (0); do_error (handle); @@ -541,426 +603,99 @@ create_new_record_cont (void *cls, handle->proc (handle->proc_cls, GNUNET_REST_create_response (NULL), MHD_HTTP_CONFLICT); - GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); + cleanup_handle(handle); return; } - - GNUNET_assert (NULL != handle->name); handle->add_qe = GNUNET_NAMESTORE_records_store (handle->ns_handle, &handle->zone_pkey, - handle->name, - handle->rd_count, + handle->json_data->name, + GNUNET_REST_NAMESTORE_RD_COUNT, handle->rd, &create_finished, handle); } -static void -del_finished (void *cls, - int32_t success, - const char *emsg) -{ - struct RequestHandle *handle = cls; - - handle->add_qe = NULL; - if (GNUNET_NO == success) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Deleting record failed, record does not exist%s%s\n"), - (NULL != emsg) ? ": " : "", - (NULL != emsg) ? emsg : ""); - GNUNET_SCHEDULER_add_now (&do_error, handle); //do_not_found TODO - return; - } - if (GNUNET_SYSERR == success) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Deleting record failed%s%s\n"), - (NULL != emsg) ? ": " : "", - (NULL != emsg) ? emsg : ""); - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - handle->proc (handle->proc_cls, - GNUNET_REST_create_response (NULL), - MHD_HTTP_NO_CONTENT); - GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); -} - - -static void -del_cont (void *cls, - const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - struct RequestHandle *handle = cls; - - handle->add_qe = NULL; - if (0 == rd_count) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("There are no records under label `%s' that could be deleted.\n"), - label); - do_error (handle); - return; - } - - handle->add_qe = GNUNET_NAMESTORE_records_store (handle->ns_handle, - &handle->zone_pkey, - handle->name, - 0, NULL, - &del_finished, - handle); -} - - -static void -namestore_delete_cont (struct GNUNET_REST_RequestHandle *con, - const char *url, - void *cls) -{ - struct RequestHandle *handle = cls; - - if (NULL == handle->name) - { - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - - handle->add_qe = GNUNET_NAMESTORE_records_lookup (handle->ns_handle, - &handle->zone_pkey, - handle->name, - &do_error, - handle, - &del_cont, - handle); -} - - -static int -json_to_gnsrecord (const json_t *records_json, - struct GNUNET_GNSRECORD_Data **rd, - unsigned int *rd_count) -{ - struct GNUNET_TIME_Relative etime_rel; - struct GNUNET_TIME_Absolute etime_abs; - char *value; - void *rdata; - size_t rdata_size; - const char *typestring; - const char *expirationstring; - json_t *type_json; - json_t *value_json; - json_t *record_json; - json_t *exp_json; - - *rd_count = json_array_size (records_json); - *rd = GNUNET_new_array (*rd_count, - struct GNUNET_GNSRECORD_Data); - for (unsigned int i = 0; i < *rd_count; i++) - { - memset (&(*rd)[i], - 0, - sizeof (struct GNUNET_GNSRECORD_Data)); - record_json = json_array_get (records_json, - i); - type_json = json_object_get (record_json, - GNUNET_REST_JSONAPI_NAMESTORE_RECORD_TYPE); - if (! json_is_string (type_json)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Type property is no string\n"); - return GNUNET_SYSERR; - } - typestring = json_string_value (type_json); - (*rd)[i].record_type = GNUNET_GNSRECORD_typename_to_number (typestring); - if (UINT32_MAX == (*rd)[i].record_type) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Unsupported type `%s'\n"), - json_string_value (type_json)); - return GNUNET_SYSERR; - } - value_json = json_object_get (record_json, - GNUNET_REST_JSONAPI_NAMESTORE_VALUE); - if (! json_is_string (value_json)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Value property is no string\n"); - return GNUNET_SYSERR; - } - value = GNUNET_strdup (json_string_value (value_json)); - if (GNUNET_OK != - GNUNET_GNSRECORD_string_to_value ((*rd)[i].record_type, - value, - &rdata, - &rdata_size)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Value `%s' invalid for record type `%s'\n"), - value, - typestring); - return GNUNET_SYSERR; - } - (*rd)[i].data = rdata; - (*rd)[i].data_size = rdata_size; - /**TODO - * if (1 == handle->is_shadow) - rde->flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD; - if (1 != handle->is_public) - rde->flags |= GNUNET_GNSRECORD_RF_PRIVATE; - */ - exp_json = json_object_get (record_json, - GNUNET_REST_JSONAPI_NAMESTORE_EXPIRATION); - if (! json_is_string (exp_json)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Expiration property is no string\n"); - return GNUNET_SYSERR; - } - expirationstring = json_string_value (exp_json); - if (0 == strcmp (expirationstring, "never")) - { - (*rd)[i].expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us; - } - else if (GNUNET_OK == - GNUNET_STRINGS_fancy_time_to_relative (expirationstring, - &etime_rel)) - { - (*rd)[i].expiration_time = etime_rel.rel_value_us; - (*rd)[i].flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; - } - else if (GNUNET_OK == - GNUNET_STRINGS_fancy_time_to_absolute (expirationstring, - &etime_abs)) - { - (*rd)[i].expiration_time = etime_abs.abs_value_us; - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Value `%s' invalid for record type `%s'\n"), - value, - typestring); - return GNUNET_SYSERR; - } - } - return GNUNET_OK; -} - - -static void -namestore_create_cont (struct GNUNET_REST_RequestHandle *con, - const char *url, - void *cls) +/** + * Handle namestore POST request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +void +namestore_add (struct GNUNET_REST_RequestHandle *con_handle, + const char* url, + void *cls) { struct RequestHandle *handle = cls; - struct MHD_Response *resp; - struct GNUNET_JSONAPI_Document *json_obj; - struct GNUNET_JSONAPI_Resource *json_res; - json_t *records_json; json_t *data_js; json_error_t err; - char term_data[handle->rest_handle->data_size+1]; - struct GNUNET_JSON_Specification docspec[] = { - GNUNET_JSON_spec_jsonapi_document (&json_obj), - GNUNET_JSON_spec_end() - }; + char term_data[handle->rest_handle->data_size + 1]; if (strlen (GNUNET_REST_API_NS_NAMESTORE) != strlen (handle->url)) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Cannot create under %s\n", handle->url); + handle->emsg = GNUNET_strdup("Wrong URL"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } if (0 >= handle->rest_handle->data_size) { + handle->emsg = GNUNET_strdup("No data"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } term_data[handle->rest_handle->data_size] = '\0'; - GNUNET_memcpy (term_data, - handle->rest_handle->data, - handle->rest_handle->data_size); - data_js = json_loads (term_data, - JSON_DECODE_ANY, - &err); - GNUNET_assert (GNUNET_OK == - GNUNET_JSON_parse (data_js, docspec, - NULL, NULL)); - json_decref (data_js); - if (NULL == json_obj) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unable to parse JSONAPI Object from %s\n", - term_data); - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - if (1 != GNUNET_JSONAPI_document_resource_count (json_obj)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Cannot create more than 1 resource! (Got %d)\n", - GNUNET_JSONAPI_document_resource_count (json_obj)); - GNUNET_JSONAPI_document_delete (json_obj); - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - json_res = GNUNET_JSONAPI_document_get_resource (json_obj, 0); - if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type (json_res, - GNUNET_REST_JSONAPI_NAMESTORE_RECORD)) + GNUNET_memcpy(term_data, handle->rest_handle->data, + handle->rest_handle->data_size); + data_js = json_loads (term_data, JSON_DECODE_ANY, &err); + GNUNET_REST_JSON_parse(&handle->json_data, data_js); + if(NULL == handle->json_data) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unsupported JSON data type\n"); - GNUNET_JSONAPI_document_delete (json_obj); - resp = GNUNET_REST_create_response (NULL); - handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT); - cleanup_handle (handle); - return; - } - handle->name = GNUNET_strdup (GNUNET_JSONAPI_resource_get_id (json_res)); - records_json = GNUNET_JSONAPI_resource_read_attr (json_res, - GNUNET_REST_JSONAPI_NAMESTORE_RECORD); - if (NULL == records_json) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "No records given\n"); - GNUNET_JSONAPI_document_delete (json_obj); + handle->emsg = GNUNET_strdup("Wrong data"); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } - if (GNUNET_SYSERR == json_to_gnsrecord (records_json, &handle->rd, &handle->rd_count)) + if(GNUNET_SYSERR == check_needed_data(handle)) { - GNUNET_JSONAPI_document_delete (json_obj); + json_decref (data_js); GNUNET_SCHEDULER_add_now (&do_error, handle); return; } - GNUNET_JSONAPI_document_delete (json_obj); - + json_decref (data_js); + json_to_gnsrecord (handle); handle->add_qe = GNUNET_NAMESTORE_records_lookup (handle->ns_handle, - &handle->zone_pkey, - handle->name, - &do_error, - handle, - &create_new_record_cont, - handle); -} - - -static void -namestore_zkey_response (void *cls, - const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone, - const char *label, - unsigned int rd_count, - const struct GNUNET_GNSRECORD_Data *rd) -{ - struct RequestHandle *handle = cls; - struct MHD_Response *resp; - struct GNUNET_JSONAPI_Document *json_obj; - struct GNUNET_JSONAPI_Resource *json_res; - json_t *name_json; - char* result; - - handle->reverse_qe = NULL; - json_obj = GNUNET_JSONAPI_document_new (); - if (NULL != label) - { - name_json = json_string (label); - json_res = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_NAMESTORE_REVINFO, - handle->zkey_str); - GNUNET_JSONAPI_resource_add_attr (json_res, - GNUNET_REST_JSONAPI_NAMESTORE_NAME, - name_json); - GNUNET_JSONAPI_document_resource_add (json_obj, json_res); - json_decref (name_json); - } - //Handle response - if (GNUNET_SYSERR == GNUNET_JSONAPI_document_serialize (json_obj, &result)) - { - GNUNET_JSONAPI_document_delete (json_obj); - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - resp = GNUNET_REST_create_response (result); - handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); - GNUNET_JSONAPI_document_delete (json_obj); - GNUNET_free (result); - GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); -} - - -static void -namestore_zkey_cont (struct GNUNET_REST_RequestHandle *con, - const char *url, - void *cls) -{ - struct RequestHandle *handle = cls; - struct GNUNET_HashCode key; - struct GNUNET_CRYPTO_EcdsaPublicKey pubkey; - - GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_NAMESTORE_ZKEY, - strlen (GNUNET_REST_JSONAPI_NAMESTORE_ZKEY), - &key); - if ( GNUNET_NO == - GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, - &key) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "No zkey given %s\n", handle->url); - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - handle->zkey_str = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, - &key); - if ((NULL == handle->zkey_str) || - (GNUNET_OK != - GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->zkey_str, - strlen (handle->zkey_str), - &pubkey))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Zkey invalid %s\n", handle->zkey_str); - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - handle->reverse_qe = GNUNET_NAMESTORE_zone_to_name (handle->ns_handle, - &handle->zone_pkey, - &pubkey, - &do_error, - handle, - &namestore_zkey_response, - handle); + &handle->zone_pkey, + handle->json_data->name, + &do_error, + handle, + &create_new_record_cont, + handle); } -static void -namestore_info_cont (struct GNUNET_REST_RequestHandle *con, - const char *url, - void *cls) +/** + * Handle namestore DELETE request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +void +namestore_delete (struct GNUNET_REST_RequestHandle *con_handle, + const char* url, + void *cls) { struct RequestHandle *handle = cls; - - handle->list_it = GNUNET_NAMESTORE_zone_iteration_start (handle->ns_handle, - &handle->zone_pkey, - &do_error, - handle, - &namestore_list_response, - handle, - &namestore_list_finished, - handle); + + //TODO add behaviour and response + + handle->emsg = GNUNET_strdup ("Not implemented yet"); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; } -static char* -get_name_from_url (const char* url) -{ - if (strlen (url) <= strlen (GNUNET_REST_API_NS_NAMESTORE)) - return NULL; - return (char*)url + strlen (GNUNET_REST_API_NS_NAMESTORE) + 1; -} /** * Respond to OPTIONS request @@ -977,77 +712,48 @@ options_cont (struct GNUNET_REST_RequestHandle *con_handle, struct MHD_Response *resp; struct RequestHandle *handle = cls; - //For now, independent of path return all options + //independent of path return all options resp = GNUNET_REST_create_response (NULL); MHD_add_response_header (resp, "Access-Control-Allow-Methods", allow_methods); handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); cleanup_handle (handle); + return; } /** - * Callback invoked from identity service with ego information. - * An @a ego of NULL means the ego was not found. + * Handle rest request * - * @param cls closure with the configuration - * @param ego an ego known to identity service, or NULL + * @param handle the request handle */ static void -identity_cb (void *cls, - const struct GNUNET_IDENTITY_Ego *ego) +init_cont (struct RequestHandle *handle) { - struct RequestHandle *handle = cls; - struct MHD_Response *resp; + //TODO specify parameter of init_cont if necessary struct GNUNET_REST_RequestHandlerError err; static const struct GNUNET_REST_RequestHandler handlers[] = { - {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_NAMESTORE_ZKEY, &namestore_zkey_cont}, //reverse - {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_NAMESTORE, &namestore_info_cont}, //list - {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_NAMESTORE, &namestore_create_cont}, //create - // {MHD_HTTP_METHOD_PUT, GNUNET_REST_API_NS_NAMESTORE, &namestore_edit_cont}, //update. TODO this shoul be PATCH - {MHD_HTTP_METHOD_DELETE, GNUNET_REST_API_NS_NAMESTORE, &namestore_delete_cont}, //delete + {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_NAMESTORE, &namestore_get}, + {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_NAMESTORE, &namestore_add}, + {MHD_HTTP_METHOD_DELETE, GNUNET_REST_API_NS_NAMESTORE, &namestore_delete}, {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_NAMESTORE, &options_cont}, GNUNET_REST_HANDLER_END }; - handle->ego_lookup = NULL; - if (NULL == ego) - { - if (NULL != handle->ego_name) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Ego `%s' not known to identity service\n"), - handle->ego_name); - } - resp = GNUNET_REST_create_response (NULL); - handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND); - cleanup_handle (handle); - return; - } - handle->zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego); - handle->ns_handle = GNUNET_NAMESTORE_connect (cfg); - if (NULL == handle->ns_handle) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Failed to connect to namestore\n")); - GNUNET_SCHEDULER_add_now (&do_error, handle); - return; - } - - if (GNUNET_OK != - GNUNET_JSONAPI_handle_request (handle->rest_handle, - handlers, - &err, - handle)) + if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle, + handlers, + &err, + handle)) { handle->response_code = err.error_code; - GNUNET_SCHEDULER_add_now (&do_error, - (void *) handle); + GNUNET_SCHEDULER_add_now (&do_error, handle); } } - +/** + * + */ static void default_ego_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego, @@ -1055,23 +761,31 @@ default_ego_cb (void *cls, const char *name) { struct RequestHandle *handle = cls; - struct MHD_Response *resp; - handle->get_default = NULL; + struct EgoEntry *ego_entry; + struct GNUNET_CRYPTO_EcdsaPublicKey pk; + + handle->op = NULL; + if (NULL == ego) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("No default ego configured in identity service\n")); - resp = GNUNET_REST_create_response (NULL); - handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND); - cleanup_handle (handle); + handle->emsg = GNUNET_strdup ("No default ego configured in identity service"); + GNUNET_SCHEDULER_add_now (&do_error, handle); return; } - else - { - identity_cb (cls, ego); - } + ego_entry = GNUNET_new(struct EgoEntry); + GNUNET_IDENTITY_ego_get_public_key (ego, &pk); + ego_entry->keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk); + ego_entry->identifier = name; + ego_entry->ego = ego; + handle->ego_entry = ego_entry; + handle->zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego); + init_cont (handle); } + +/** + * Connect to identity callback + */ static void id_connect_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego, @@ -1081,9 +795,10 @@ id_connect_cb (void *cls, struct RequestHandle *handle = cls; if (NULL == ego) { - handle->get_default = GNUNET_IDENTITY_get (handle->identity_handle, - "namestore", - &default_ego_cb, handle); + handle->op = GNUNET_IDENTITY_get (handle->identity_handle, + GNUNET_REST_SUBSYSTEM_NAMESTORE, + &default_ego_cb, + handle); } } @@ -1097,81 +812,37 @@ id_connect_cb (void *cls, * @param data_size length of the body * @param proc callback function for the result * @param proc_cls closure for callback function - * @return #GNUNET_OK if request accepted + * @return GNUNET_OK if request accepted */ static void -rest_identity_process_request(struct GNUNET_REST_RequestHandle *rest_handle, +rest_process_request(struct GNUNET_REST_RequestHandle *rest_handle, GNUNET_REST_ResultProcessor proc, void *proc_cls) { struct RequestHandle *handle = GNUNET_new (struct RequestHandle); - struct MHD_Response *resp; - struct GNUNET_HashCode key; - char *ego; - char *name; - char *type; - + + handle->response_code = 0; handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL; handle->proc_cls = proc_cls; handle->proc = proc; handle->rest_handle = rest_handle; + handle->url = GNUNET_strdup (rest_handle->url); if (handle->url[strlen (handle->url)-1] == '/') handle->url[strlen (handle->url)-1] = '\0'; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Connecting...\n"); - handle->cfg = cfg; - ego = NULL; - GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_NAMESTORE_EGO, - strlen (GNUNET_REST_JSONAPI_NAMESTORE_EGO), - &key); - if ( GNUNET_YES == - GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, - &key) ) - { - ego = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, - &key); - } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n"); - handle->type = GNUNET_GNSRECORD_TYPE_ANY; - GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_NAMESTORE_RECORD_TYPE, - strlen (GNUNET_REST_JSONAPI_NAMESTORE_RECORD_TYPE), - &key); - if ( GNUNET_YES == - GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, - &key) ) - { - type = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, - &key); - if (NULL != type) - handle->type = GNUNET_GNSRECORD_typename_to_number (type); - } - name = get_name_from_url (handle->url); - if (NULL != ego) - handle->ego_name = GNUNET_strdup (ego); - if (NULL != name) - handle->name = GNUNET_strdup (name); - if (NULL == handle->ego_name) - { - handle->identity_handle = GNUNET_IDENTITY_connect (handle->cfg, &id_connect_cb, handle); - if (NULL == handle->identity_handle) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Cannot connect to identity service\n")); - resp = GNUNET_REST_create_response (NULL); - handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND); - cleanup_handle (handle); - } - return; - } - handle->ego_lookup = GNUNET_IDENTITY_ego_lookup (cfg, - handle->ego_name, - &identity_cb, - handle); - handle->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->timeout, - &do_timeout, - handle); + handle->identity_handle = GNUNET_IDENTITY_connect (cfg, &id_connect_cb, handle); + handle->ns_handle = GNUNET_NAMESTORE_connect (cfg); + handle->timeout_task = + GNUNET_SCHEDULER_add_delayed (handle->timeout, + &do_error, + handle); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n"); } + /** * Entry point for the plugin. * @@ -1182,9 +853,9 @@ void * libgnunet_plugin_rest_namestore_init (void *cls) { static struct Plugin plugin; - cfg = cls; struct GNUNET_REST_Plugin *api; + cfg = cls; if (NULL != plugin.cfg) return NULL; /* can only initialize once! */ memset (&plugin, 0, sizeof (struct Plugin)); @@ -1192,7 +863,7 @@ libgnunet_plugin_rest_namestore_init (void *cls) api = GNUNET_new (struct GNUNET_REST_Plugin); api->cls = &plugin; api->name = GNUNET_REST_API_NS_NAMESTORE; - api->process_request = &rest_identity_process_request; + api->process_request = &rest_process_request; GNUNET_asprintf (&allow_methods, "%s, %s, %s, %s, %s", MHD_HTTP_METHOD_GET, @@ -1200,7 +871,8 @@ libgnunet_plugin_rest_namestore_init (void *cls) MHD_HTTP_METHOD_PUT, MHD_HTTP_METHOD_DELETE, MHD_HTTP_METHOD_OPTIONS); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Namestore REST API initialized\n")); return api; } @@ -1217,13 +889,14 @@ libgnunet_plugin_rest_namestore_done (void *cls) { struct GNUNET_REST_Plugin *api = cls; struct Plugin *plugin = api->cls; - plugin->cfg = NULL; - GNUNET_free (api); + GNUNET_free_non_null (allow_methods); + GNUNET_free (api); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Namestore REST plugin is finished\n"); return NULL; } /* end of plugin_rest_namestore.c */ + diff --git a/src/namestore/plugin_rest_namestore2.c b/src/namestore/plugin_rest_namestore2.c new file mode 100644 index 000000000..ec44046e0 --- /dev/null +++ b/src/namestore/plugin_rest_namestore2.c @@ -0,0 +1,1229 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012-2015 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ +/** + * @author Martin Schanzenbach + * @file namestore/plugin_rest_namestore.c + * @brief GNUnet Namestore REST plugin + * + */ + +#include "platform.h" +#include "gnunet_rest_plugin.h" +#include "gnunet_gns_service.h" +#include "gnunet_namestore_service.h" +#include "gnunet_identity_service.h" +#include "gnunet_rest_lib.h" +#include "gnunet_jsonapi_lib.h" +#include "gnunet_jsonapi_util.h" +#include "microhttpd.h" +#include + +#define GNUNET_REST_API_NS_NAMESTORE "/names" + +#define GNUNET_REST_API_NS_NAMESTORE_ZKEY "/names/zkey" + +#define GNUNET_REST_JSONAPI_NAMESTORE_TYPEINFO "record" + +#define GNUNET_REST_JSONAPI_NAMESTORE_NAME "name" + +#define GNUNET_REST_JSONAPI_NAMESTORE_REVINFO "revinfo" + +#define GNUNET_REST_JSONAPI_NAMESTORE_RECORD GNUNET_REST_JSONAPI_NAMESTORE_TYPEINFO + +#define GNUNET_REST_JSONAPI_NAMESTORE_RECORD_TYPE "record_type" + +#define GNUNET_REST_JSONAPI_NAMESTORE_VALUE "value" + +#define GNUNET_REST_JSONAPI_NAMESTORE_PUBLIC "public" + +#define GNUNET_REST_JSONAPI_NAMESTORE_SHADOW "shadow" + +#define GNUNET_REST_JSONAPI_NAMESTORE_PKEY "pkey" + +#define GNUNET_REST_JSONAPI_NAMESTORE_ZKEY "zkey" + +#define GNUNET_REST_JSONAPI_NAMESTORE_EXPIRATION "expiration" + +#define GNUNET_REST_JSONAPI_NAMESTORE_EGO "ego" + +/** + * @brief struct returned by the initialization function of the plugin + */ +struct Plugin +{ + const struct GNUNET_CONFIGURATION_Handle *cfg; +}; + + +/** + * HTTP methods allows for this plugin + */ +static char* allow_methods; + +const struct GNUNET_CONFIGURATION_Handle *cfg; + +struct RecordEntry +{ + /** + * DLL + */ + struct RecordEntry *next; + + /** + * DLL + */ + struct RecordEntry *prev; + +}; + +struct RequestHandle +{ + /** + * Ego list + */ + struct RecordEntry *record_head; + + /** + * Ego list + */ + struct record_entry *record_tail; + + /** + * JSON response object + */ + struct GNUNET_JSONAPI_Document *resp_object; + + /** + * Rest connection + */ + struct GNUNET_REST_RequestHandle *rest_handle; + + /** + * Handle to GNS service. + */ + struct GNUNET_IDENTITY_Handle *identity_handle; + + /** + * Handle to NAMESTORE + */ + struct GNUNET_NAMESTORE_Handle *ns_handle; + + /** + * Handle to NAMESTORE it + */ + struct GNUNET_NAMESTORE_ZoneIterator *list_it; + + /** + * Private key for the zone + */ + struct GNUNET_CRYPTO_EcdsaPrivateKey zone_pkey; + + /** + * Handle to identity lookup + */ + struct GNUNET_IDENTITY_EgoLookup *ego_lookup; + + /** + * Default Ego operation + */ + struct GNUNET_IDENTITY_Operation *get_default; + + /** + * Name of the ego + */ + char *ego_name; + + /** + * Record is public + */ + int is_public; + + /** + * Shadow record + */ + int is_shadow; + + /** + * Name of the record to modify + */ + char *name; + + /** + * Value of the record + */ + char *value; + + /** + * Zkey string + */ + const char* zkey_str; + + /** + * record type + */ + uint32_t type; + + /** + * Records to store + */ + struct GNUNET_GNSRECORD_Data *rd; + + /** + * record count + */ + unsigned int rd_count; + + /** + * NAMESTORE Operation + */ + struct GNUNET_NAMESTORE_QueueEntry *add_qe; + + /** + * NAMESTORE Operation + */ + struct GNUNET_NAMESTORE_QueueEntry *reverse_qe; + + /** + * Desired timeout for the lookup (default is no timeout). + */ + struct GNUNET_TIME_Relative timeout; + + /** + * ID of a task associated with the resolution process. + */ + struct GNUNET_SCHEDULER_Task * timeout_task; + + /** + * The plugin result processor + */ + GNUNET_REST_ResultProcessor proc; + + /** + * The closure of the result processor + */ + void *proc_cls; + + /** + * The url + */ + char *url; + + /** + * Cfg + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * HTTP response code + */ + int response_code; + +}; + + +/** + * Cleanup lookup handle + * + * @param handle Handle to clean up + */ +static void +cleanup_handle (struct RequestHandle *handle) +{ + struct RecordEntry *record_entry; + struct RecordEntry *record_tmp; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Cleaning up\n"); + if (NULL != handle->resp_object) + GNUNET_JSONAPI_document_delete (handle->resp_object); + if (NULL != handle->name) + GNUNET_free (handle->name); + if (NULL != handle->timeout_task) + GNUNET_SCHEDULER_cancel (handle->timeout_task); + if (NULL != handle->ego_lookup) + GNUNET_IDENTITY_ego_lookup_cancel (handle->ego_lookup); + if (NULL != handle->get_default) + GNUNET_IDENTITY_cancel (handle->get_default); + if (NULL != handle->list_it) + GNUNET_NAMESTORE_zone_iteration_stop (handle->list_it); + if (NULL != handle->add_qe) + GNUNET_NAMESTORE_cancel (handle->add_qe); + if (NULL != handle->identity_handle) + GNUNET_IDENTITY_disconnect (handle->identity_handle); + if (NULL != handle->ns_handle) + GNUNET_NAMESTORE_disconnect (handle->ns_handle); + if (NULL != handle->url) + GNUNET_free (handle->url); + if (NULL != handle->value) + GNUNET_free (handle->value); + if (NULL != handle->rd) + { + for (unsigned int i = 0; i < handle->rd_count; i++) + { + if (NULL != handle->rd[i].data) + GNUNET_free ((void*)handle->rd[i].data); + } + GNUNET_free (handle->rd); + } + if (NULL != handle->ego_name) + GNUNET_free (handle->ego_name); + for (record_entry = handle->record_head; + NULL != record_entry;) + { + record_tmp = record_entry; + record_entry = record_entry->next; + GNUNET_free (record_tmp); + } + GNUNET_free (handle); +} + + +/** + * Create json representation of a GNSRECORD + * + * @param rd the GNSRECORD_Data + */ +static json_t * +gnsrecord_to_json (const struct GNUNET_GNSRECORD_Data *rd) +{ + const char *typename; + char *string_val; + const char *exp_str; + json_t *record_obj; + + typename = GNUNET_GNSRECORD_number_to_typename (rd->record_type); + string_val = GNUNET_GNSRECORD_value_to_string (rd->record_type, + rd->data, + rd->data_size); + + if (NULL == string_val) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Record of type %d malformed, skipping\n", + (int) rd->record_type); + return NULL; + } + record_obj = json_object(); + json_object_set_new (record_obj, + GNUNET_REST_JSONAPI_NAMESTORE_RECORD_TYPE, + json_string (typename)); + json_object_set_new (record_obj, + GNUNET_REST_JSONAPI_NAMESTORE_VALUE, + json_string (string_val)); + GNUNET_free (string_val); + + if (GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION & rd->flags) + { + struct GNUNET_TIME_Relative time_rel; + time_rel.rel_value_us = rd->expiration_time; + exp_str = GNUNET_STRINGS_relative_time_to_string (time_rel, 1); + } + else + { + struct GNUNET_TIME_Absolute time_abs; + time_abs.abs_value_us = rd->expiration_time; + exp_str = GNUNET_STRINGS_absolute_time_to_string (time_abs); + } + json_object_set_new (record_obj, GNUNET_REST_JSONAPI_NAMESTORE_EXPIRATION, json_string (exp_str)); + + json_object_set_new (record_obj, "expired", + json_boolean (GNUNET_YES == GNUNET_GNSRECORD_is_expired (rd))); + return record_obj; +} + + +/** + * Task run on error. Generates error response and cleans up. + * + * @param cls the request to generate an error response for + */ +static void +do_error (void *cls) +{ + struct RequestHandle *handle = cls; + struct MHD_Response *resp = GNUNET_REST_create_response (NULL); + + handle->proc (handle->proc_cls, resp, handle->response_code); + cleanup_handle (handle); +} + + +/** + * Task run on timeout. + * + * @param cls the request to time out + */ +static void +do_timeout (void *cls) +{ + struct RequestHandle *handle = cls; + + handle->timeout_task = NULL; + do_error (handle); +} + + +static void +cleanup_handle_delayed (void *cls) +{ + cleanup_handle (cls); +} + + +/** + * Iteration over all results finished, build final + * response. + * + * @param cls the `struct RequestHandle` + */ +static void +namestore_list_finished (void *cls) +{ + struct RequestHandle *handle = cls; + char *result; + struct MHD_Response *resp; + + handle->list_it = NULL; + if (NULL == handle->resp_object) + handle->resp_object = GNUNET_JSONAPI_document_new (); + + if (GNUNET_SYSERR == + GNUNET_JSONAPI_document_serialize (handle->resp_object, + &result)) + { + handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; + GNUNET_SCHEDULER_add_now (&do_error, + handle); + return; + } + resp = GNUNET_REST_create_response (result); + handle->proc (handle->proc_cls, + resp, + MHD_HTTP_OK); + GNUNET_free_non_null (result); + GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, + handle); +} + + + +/** + * Create a response with requested records + * + * @param handle the RequestHandle + */ +static void +namestore_list_response (void *cls, + const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key, + const char *rname, + unsigned int rd_len, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct RequestHandle *handle = cls; + struct GNUNET_JSONAPI_Resource *json_resource; + json_t *result_array; + json_t *record_obj; + + if (NULL == handle->resp_object) + handle->resp_object = GNUNET_JSONAPI_document_new (); + + if ( (NULL != handle->name) && + (0 != strcmp (handle->name, + rname)) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "%s does not match %s\n", + rname, + handle->name); + GNUNET_NAMESTORE_zone_iterator_next (handle->list_it, + 1); + return; + } + + result_array = json_array (); + for (unsigned int i=0; itype) && + (GNUNET_GNSRECORD_TYPE_ANY != handle->type) ) + continue; + record_obj = gnsrecord_to_json (&rd[i]); + json_array_append (result_array, + record_obj); + json_decref (record_obj); + } + + if (0 < json_array_size(result_array)) + { + json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_NAMESTORE_TYPEINFO, + rname); + GNUNET_JSONAPI_resource_add_attr (json_resource, + GNUNET_REST_JSONAPI_NAMESTORE_RECORD, + result_array); + GNUNET_JSONAPI_document_resource_add (handle->resp_object, json_resource); + } + + json_decref (result_array); + GNUNET_NAMESTORE_zone_iterator_next (handle->list_it, + 1); +} + + +static void +create_finished (void *cls, int32_t success, const char *emsg) +{ + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + + handle->add_qe = NULL; + if (GNUNET_YES != success) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error storing records%s%s\n", + (NULL == emsg) ? "" : ": ", + (NULL == emsg) ? "" : emsg); + GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); + return; + } + resp = GNUNET_REST_create_response (NULL); + handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT); + GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); +} + + +/** + * We're storing a new record; this requires + * that no record already exists + * + * @param cls closure, unused + * @param zone_key private key of the zone + * @param rec_name name that is being mapped (at most 255 characters long) + * @param rd_count number of entries in @a rd array + * @param rd array of records with data to store + */ +static void +create_new_record_cont (void *cls, + const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key, + const char *rec_name, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct RequestHandle *handle = cls; + + handle->add_qe = NULL; + if (0 != strcmp (rec_name, handle->name)) + { + GNUNET_break (0); + do_error (handle); + return; + } + + if (0 != rd_count) + { + handle->proc (handle->proc_cls, + GNUNET_REST_create_response (NULL), + MHD_HTTP_CONFLICT); + GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); + return; + } + + GNUNET_assert (NULL != handle->name); + handle->add_qe = GNUNET_NAMESTORE_records_store (handle->ns_handle, + &handle->zone_pkey, + handle->name, + handle->rd_count, + handle->rd, + &create_finished, + handle); +} + + +static void +del_finished (void *cls, + int32_t success, + const char *emsg) +{ + struct RequestHandle *handle = cls; + + handle->add_qe = NULL; + if (GNUNET_NO == success) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Deleting record failed, record does not exist%s%s\n"), + (NULL != emsg) ? ": " : "", + (NULL != emsg) ? emsg : ""); + GNUNET_SCHEDULER_add_now (&do_error, handle); //do_not_found TODO + return; + } + if (GNUNET_SYSERR == success) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Deleting record failed%s%s\n"), + (NULL != emsg) ? ": " : "", + (NULL != emsg) ? emsg : ""); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + handle->proc (handle->proc_cls, + GNUNET_REST_create_response (NULL), + MHD_HTTP_NO_CONTENT); + GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); +} + + +static void +del_cont (void *cls, + const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct RequestHandle *handle = cls; + + handle->add_qe = NULL; + if (0 == rd_count) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("There are no records under label `%s' that could be deleted.\n"), + label); + do_error (handle); + return; + } + + handle->add_qe = GNUNET_NAMESTORE_records_store (handle->ns_handle, + &handle->zone_pkey, + handle->name, + 0, NULL, + &del_finished, + handle); +} + + +static void +namestore_delete_cont (struct GNUNET_REST_RequestHandle *con, + const char *url, + void *cls) +{ + struct RequestHandle *handle = cls; + + if (NULL == handle->name) + { + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + + handle->add_qe = GNUNET_NAMESTORE_records_lookup (handle->ns_handle, + &handle->zone_pkey, + handle->name, + &do_error, + handle, + &del_cont, + handle); +} + + +static int +json_to_gnsrecord (const json_t *records_json, + struct GNUNET_GNSRECORD_Data **rd, + unsigned int *rd_count) +{ + struct GNUNET_TIME_Relative etime_rel; + struct GNUNET_TIME_Absolute etime_abs; + char *value; + void *rdata; + size_t rdata_size; + const char *typestring; + const char *expirationstring; + json_t *type_json; + json_t *value_json; + json_t *record_json; + json_t *exp_json; + + *rd_count = json_array_size (records_json); + *rd = GNUNET_new_array (*rd_count, + struct GNUNET_GNSRECORD_Data); + for (unsigned int i = 0; i < *rd_count; i++) + { + memset (&(*rd)[i], + 0, + sizeof (struct GNUNET_GNSRECORD_Data)); + record_json = json_array_get (records_json, + i); + type_json = json_object_get (record_json, + GNUNET_REST_JSONAPI_NAMESTORE_RECORD_TYPE); + if (! json_is_string (type_json)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Type property is no string\n"); + return GNUNET_SYSERR; + } + typestring = json_string_value (type_json); + (*rd)[i].record_type = GNUNET_GNSRECORD_typename_to_number (typestring); + if (UINT32_MAX == (*rd)[i].record_type) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Unsupported type `%s'\n"), + json_string_value (type_json)); + return GNUNET_SYSERR; + } + value_json = json_object_get (record_json, + GNUNET_REST_JSONAPI_NAMESTORE_VALUE); + if (! json_is_string (value_json)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Value property is no string\n"); + return GNUNET_SYSERR; + } + value = GNUNET_strdup (json_string_value (value_json)); + if (GNUNET_OK != + GNUNET_GNSRECORD_string_to_value ((*rd)[i].record_type, + value, + &rdata, + &rdata_size)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Value `%s' invalid for record type `%s'\n"), + value, + typestring); + return GNUNET_SYSERR; + } + (*rd)[i].data = rdata; + (*rd)[i].data_size = rdata_size; + /**TODO + * if (1 == handle->is_shadow) + rde->flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD; + if (1 != handle->is_public) + rde->flags |= GNUNET_GNSRECORD_RF_PRIVATE; + */ + exp_json = json_object_get (record_json, + GNUNET_REST_JSONAPI_NAMESTORE_EXPIRATION); + if (! json_is_string (exp_json)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Expiration property is no string\n"); + return GNUNET_SYSERR; + } + expirationstring = json_string_value (exp_json); + if (0 == strcmp (expirationstring, "never")) + { + (*rd)[i].expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us; + } + else if (GNUNET_OK == + GNUNET_STRINGS_fancy_time_to_relative (expirationstring, + &etime_rel)) + { + (*rd)[i].expiration_time = etime_rel.rel_value_us; + (*rd)[i].flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; + } + else if (GNUNET_OK == + GNUNET_STRINGS_fancy_time_to_absolute (expirationstring, + &etime_abs)) + { + (*rd)[i].expiration_time = etime_abs.abs_value_us; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Value `%s' invalid for record type `%s'\n"), + value, + typestring); + return GNUNET_SYSERR; + } + } + return GNUNET_OK; +} + + +static void +namestore_create_cont (struct GNUNET_REST_RequestHandle *con, + const char *url, + void *cls) +{ + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + struct GNUNET_JSONAPI_Document *json_obj; + struct GNUNET_JSONAPI_Resource *json_res; + json_t *records_json; + json_t *data_js; + json_error_t err; + char term_data[handle->rest_handle->data_size+1]; + struct GNUNET_JSON_Specification docspec[] = { + GNUNET_JSON_spec_jsonapi_document (&json_obj), + GNUNET_JSON_spec_end() + }; + + if (strlen (GNUNET_REST_API_NS_NAMESTORE) != strlen (handle->url)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Cannot create under %s\n", handle->url); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + if (0 >= handle->rest_handle->data_size) + { + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + term_data[handle->rest_handle->data_size] = '\0'; + GNUNET_memcpy (term_data, + handle->rest_handle->data, + handle->rest_handle->data_size); + data_js = json_loads (term_data, + JSON_DECODE_ANY, + &err); + GNUNET_assert (GNUNET_OK == + GNUNET_JSON_parse (data_js, docspec, + NULL, NULL)); + json_decref (data_js); + if (NULL == json_obj) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unable to parse JSONAPI Object from %s\n", + term_data); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + if (1 != GNUNET_JSONAPI_document_resource_count (json_obj)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Cannot create more than 1 resource! (Got %d)\n", + GNUNET_JSONAPI_document_resource_count (json_obj)); + GNUNET_JSONAPI_document_delete (json_obj); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + json_res = GNUNET_JSONAPI_document_get_resource (json_obj, 0); + if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type (json_res, + GNUNET_REST_JSONAPI_NAMESTORE_RECORD)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unsupported JSON data type\n"); + GNUNET_JSONAPI_document_delete (json_obj); + resp = GNUNET_REST_create_response (NULL); + handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT); + cleanup_handle (handle); + return; + } + handle->name = GNUNET_strdup (GNUNET_JSONAPI_resource_get_id (json_res)); + records_json = GNUNET_JSONAPI_resource_read_attr (json_res, + GNUNET_REST_JSONAPI_NAMESTORE_RECORD); + if (NULL == records_json) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No records given\n"); + GNUNET_JSONAPI_document_delete (json_obj); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + if (GNUNET_SYSERR == json_to_gnsrecord (records_json, &handle->rd, &handle->rd_count)) + { + GNUNET_JSONAPI_document_delete (json_obj); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + GNUNET_JSONAPI_document_delete (json_obj); + + handle->add_qe = GNUNET_NAMESTORE_records_lookup (handle->ns_handle, + &handle->zone_pkey, + handle->name, + &do_error, + handle, + &create_new_record_cont, + handle); +} + + +static void +namestore_zkey_response (void *cls, + const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone, + const char *label, + unsigned int rd_count, + const struct GNUNET_GNSRECORD_Data *rd) +{ + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + struct GNUNET_JSONAPI_Document *json_obj; + struct GNUNET_JSONAPI_Resource *json_res; + json_t *name_json; + char* result; + + handle->reverse_qe = NULL; + json_obj = GNUNET_JSONAPI_document_new (); + if (NULL != label) + { + name_json = json_string (label); + json_res = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_NAMESTORE_REVINFO, + handle->zkey_str); + GNUNET_JSONAPI_resource_add_attr (json_res, + GNUNET_REST_JSONAPI_NAMESTORE_NAME, + name_json); + GNUNET_JSONAPI_document_resource_add (json_obj, json_res); + json_decref (name_json); + } + //Handle response + if (GNUNET_SYSERR == GNUNET_JSONAPI_document_serialize (json_obj, &result)) + { + GNUNET_JSONAPI_document_delete (json_obj); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + resp = GNUNET_REST_create_response (result); + handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); + GNUNET_JSONAPI_document_delete (json_obj); + GNUNET_free (result); + GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle); +} + + +static void +namestore_zkey_cont (struct GNUNET_REST_RequestHandle *con, + const char *url, + void *cls) +{ + struct RequestHandle *handle = cls; + struct GNUNET_HashCode key; + struct GNUNET_CRYPTO_EcdsaPublicKey pubkey; + + GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_NAMESTORE_ZKEY, + strlen (GNUNET_REST_JSONAPI_NAMESTORE_ZKEY), + &key); + if ( GNUNET_NO == + GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, + &key) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No zkey given %s\n", handle->url); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + handle->zkey_str = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, + &key); + if ((NULL == handle->zkey_str) || + (GNUNET_OK != + GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->zkey_str, + strlen (handle->zkey_str), + &pubkey))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Zkey invalid %s\n", handle->zkey_str); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + handle->reverse_qe = GNUNET_NAMESTORE_zone_to_name (handle->ns_handle, + &handle->zone_pkey, + &pubkey, + &do_error, + handle, + &namestore_zkey_response, + handle); +} + + +static void +namestore_info_cont (struct GNUNET_REST_RequestHandle *con, + const char *url, + void *cls) +{ + struct RequestHandle *handle = cls; + + handle->list_it = GNUNET_NAMESTORE_zone_iteration_start (handle->ns_handle, + &handle->zone_pkey, + &do_error, + handle, + &namestore_list_response, + handle, + &namestore_list_finished, + handle); +} + + +static char* +get_name_from_url (const char* url) +{ + if (strlen (url) <= strlen (GNUNET_REST_API_NS_NAMESTORE)) + return NULL; + return (char*)url + strlen (GNUNET_REST_API_NS_NAMESTORE) + 1; +} + +/** + * Respond to OPTIONS request + * + * @param con_handle the connection handle + * @param url the url + * @param cls the RequestHandle + */ +static void +options_cont (struct GNUNET_REST_RequestHandle *con_handle, + const char* url, + void *cls) +{ + struct MHD_Response *resp; + struct RequestHandle *handle = cls; + + //For now, independent of path return all options + resp = GNUNET_REST_create_response (NULL); + MHD_add_response_header (resp, + "Access-Control-Allow-Methods", + allow_methods); + handle->proc (handle->proc_cls, resp, MHD_HTTP_OK); + cleanup_handle (handle); +} + + +/** + * Callback invoked from identity service with ego information. + * An @a ego of NULL means the ego was not found. + * + * @param cls closure with the configuration + * @param ego an ego known to identity service, or NULL + */ +static void +identity_cb (void *cls, + const struct GNUNET_IDENTITY_Ego *ego) +{ + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + struct GNUNET_REST_RequestHandlerError err; + static const struct GNUNET_REST_RequestHandler handlers[] = { + {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_NAMESTORE_ZKEY, &namestore_zkey_cont}, //reverse + {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_NAMESTORE, &namestore_info_cont}, //list + {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_NAMESTORE, &namestore_create_cont}, //create + // {MHD_HTTP_METHOD_PUT, GNUNET_REST_API_NS_NAMESTORE, &namestore_edit_cont}, //update. TODO this shoul be PATCH + {MHD_HTTP_METHOD_DELETE, GNUNET_REST_API_NS_NAMESTORE, &namestore_delete_cont}, //delete + {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_NAMESTORE, &options_cont}, + GNUNET_REST_HANDLER_END + }; + + handle->ego_lookup = NULL; + if (NULL == ego) + { + if (NULL != handle->ego_name) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Ego `%s' not known to identity service\n"), + handle->ego_name); + } + resp = GNUNET_REST_create_response (NULL); + handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND); + cleanup_handle (handle); + return; + } + handle->zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego); + handle->ns_handle = GNUNET_NAMESTORE_connect (cfg); + if (NULL == handle->ns_handle) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Failed to connect to namestore\n")); + GNUNET_SCHEDULER_add_now (&do_error, handle); + return; + } + + if (GNUNET_OK != + GNUNET_JSONAPI_handle_request (handle->rest_handle, + handlers, + &err, + handle)) + { + handle->response_code = err.error_code; + GNUNET_SCHEDULER_add_now (&do_error, + (void *) handle); + } +} + + +static void +default_ego_cb (void *cls, + struct GNUNET_IDENTITY_Ego *ego, + void **ctx, + const char *name) +{ + struct RequestHandle *handle = cls; + struct MHD_Response *resp; + handle->get_default = NULL; + if (NULL == ego) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("No default ego configured in identity service\n")); + resp = GNUNET_REST_create_response (NULL); + handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND); + cleanup_handle (handle); + return; + } + else + { + identity_cb (cls, ego); + } +} + +static void +id_connect_cb (void *cls, + struct GNUNET_IDENTITY_Ego *ego, + void **ctx, + const char *name) +{ + struct RequestHandle *handle = cls; + if (NULL == ego) + { + handle->get_default = GNUNET_IDENTITY_get (handle->identity_handle, + "namestore", + &default_ego_cb, handle); + } +} + + +/** + * Function processing the REST call + * + * @param method HTTP method + * @param url URL of the HTTP request + * @param data body of the HTTP request (optional) + * @param data_size length of the body + * @param proc callback function for the result + * @param proc_cls closure for callback function + * @return #GNUNET_OK if request accepted + */ +static void +rest_identity_process_request(struct GNUNET_REST_RequestHandle *rest_handle, + GNUNET_REST_ResultProcessor proc, + void *proc_cls) +{ + struct RequestHandle *handle = GNUNET_new (struct RequestHandle); + struct MHD_Response *resp; + struct GNUNET_HashCode key; + char *ego; + char *name; + char *type; + + handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL; + handle->proc_cls = proc_cls; + handle->proc = proc; + handle->rest_handle = rest_handle; + handle->url = GNUNET_strdup (rest_handle->url); + if (handle->url[strlen (handle->url)-1] == '/') + handle->url[strlen (handle->url)-1] = '\0'; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connecting...\n"); + handle->cfg = cfg; + ego = NULL; + GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_NAMESTORE_EGO, + strlen (GNUNET_REST_JSONAPI_NAMESTORE_EGO), + &key); + if ( GNUNET_YES == + GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, + &key) ) + { + ego = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, + &key); + } + + handle->type = GNUNET_GNSRECORD_TYPE_ANY; + GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_NAMESTORE_RECORD_TYPE, + strlen (GNUNET_REST_JSONAPI_NAMESTORE_RECORD_TYPE), + &key); + if ( GNUNET_YES == + GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map, + &key) ) + { + type = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, + &key); + if (NULL != type) + handle->type = GNUNET_GNSRECORD_typename_to_number (type); + } + name = get_name_from_url (handle->url); + if (NULL != ego) + handle->ego_name = GNUNET_strdup (ego); + if (NULL != name) + handle->name = GNUNET_strdup (name); + if (NULL == handle->ego_name) + { + handle->identity_handle = GNUNET_IDENTITY_connect (handle->cfg, &id_connect_cb, handle); + if (NULL == handle->identity_handle) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Cannot connect to identity service\n")); + resp = GNUNET_REST_create_response (NULL); + handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND); + cleanup_handle (handle); + } + return; + } + handle->ego_lookup = GNUNET_IDENTITY_ego_lookup (cfg, + handle->ego_name, + &identity_cb, + handle); + handle->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->timeout, + &do_timeout, + handle); +} + +/** + * Entry point for the plugin. + * + * @param cls Config info + * @return NULL on error, otherwise the plugin context + */ +void * +libgnunet_plugin_rest_namestore_init (void *cls) +{ + static struct Plugin plugin; + cfg = cls; + struct GNUNET_REST_Plugin *api; + + if (NULL != plugin.cfg) + return NULL; /* can only initialize once! */ + memset (&plugin, 0, sizeof (struct Plugin)); + plugin.cfg = cfg; + api = GNUNET_new (struct GNUNET_REST_Plugin); + api->cls = &plugin; + api->name = GNUNET_REST_API_NS_NAMESTORE; + api->process_request = &rest_identity_process_request; + GNUNET_asprintf (&allow_methods, + "%s, %s, %s, %s, %s", + MHD_HTTP_METHOD_GET, + MHD_HTTP_METHOD_POST, + MHD_HTTP_METHOD_PUT, + MHD_HTTP_METHOD_DELETE, + MHD_HTTP_METHOD_OPTIONS); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Namestore REST API initialized\n")); + return api; +} + + +/** + * Exit point from the plugin. + * + * @param cls the plugin context (as returned by "init") + * @return always NULL + */ +void * +libgnunet_plugin_rest_namestore_done (void *cls) +{ + struct GNUNET_REST_Plugin *api = cls; + struct Plugin *plugin = api->cls; + + plugin->cfg = NULL; + GNUNET_free (api); + GNUNET_free_non_null (allow_methods); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Namestore REST plugin is finished\n"); + return NULL; +} + +/* end of plugin_rest_namestore.c */ -- cgit v1.2.3