X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fidentity%2Fidentity_api.c;h=a31086f5db88498b8288ea4f3cd347e7edfc3c0f;hb=8aa21ee7de6c4a6cb9c1e132ab4218d3d10e0e30;hp=091a1d60c47207184dbb48b2dd2b12be7658658f;hpb=e43155e9ea536991a489e0fe04890e0269869af1;p=oweals%2Fgnunet.git diff --git a/src/identity/identity_api.c b/src/identity/identity_api.c index 091a1d60c..a31086f5d 100644 --- a/src/identity/identity_api.c +++ b/src/identity/identity_api.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - (C) 2013 Christian Grothoff (and other contributing authors) + Copyright (C) 2013 Christian Grothoff (and other contributing authors) GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public Liceidentity as published @@ -20,23 +20,92 @@ /** * @file identity/identity_api.c - * @brief api to get information from the network size estimation service - * @author Nathan Evans + * @brief api to interact with the identity service + * @author Christian Grothoff */ #include "platform.h" -#include "gnunet_client_lib.h" +#include "gnunet_util_lib.h" #include "gnunet_constants.h" -#include "gnunet_container_lib.h" -#include "gnunet_arm_service.h" -#include "gnunet_hello_lib.h" #include "gnunet_protocols.h" -#include "gnunet_server_lib.h" -#include "gnunet_time_lib.h" #include "gnunet_identity_service.h" #include "identity.h" #define LOG(kind,...) GNUNET_log_from (kind, "identity-api",__VA_ARGS__) +/** + * Handle for an ego. + */ +struct GNUNET_IDENTITY_Ego +{ + /** + * Private key associated with this ego. + */ + struct GNUNET_CRYPTO_EcdsaPrivateKey *pk; + + /** + * Current name associated with this ego. + */ + char *name; + + /** + * Client context associated with this ego. + */ + void *ctx; + + /** + * Hash of the public key of this ego. + */ + struct GNUNET_HashCode id; +}; + + +/** + * Handle for an operation with the identity service. + */ +struct GNUNET_IDENTITY_Operation +{ + + /** + * Main identity handle. + */ + struct GNUNET_IDENTITY_Handle *h; + + /** + * We keep operations in a DLL. + */ + struct GNUNET_IDENTITY_Operation *next; + + /** + * We keep operations in a DLL. + */ + struct GNUNET_IDENTITY_Operation *prev; + + /** + * Message to send to the identity service. + * Allocated at the end of this struct. + */ + const struct GNUNET_MessageHeader *msg; + + /** + * Continuation to invoke with the result of the transmission; @e cb + * will be NULL in this case. + */ + GNUNET_IDENTITY_Continuation cont; + + /** + * Continuation to invoke with the result of the transmission for + * 'get' operations (@e cont will be NULL in this case). + */ + GNUNET_IDENTITY_Callback cb; + + /** + * Closure for @e cont or @e cb. + */ + void *cls; + +}; + + /** * Handle for the service. */ @@ -53,60 +122,84 @@ struct GNUNET_IDENTITY_Handle struct GNUNET_CLIENT_Connection *client; /** - * Currently pending transmission request. + * Hash map from the hash of the public key to the + * respective 'GNUNET_IDENTITY_Ego' handle. + */ + struct GNUNET_CONTAINER_MultiHashMap *egos; + + /** + * Function to call when we receive updates. + */ + GNUNET_IDENTITY_Callback cb; + + /** + * Closure for 'cb'. + */ + void *cb_cls; + + /** + * Head of active operations. + */ + struct GNUNET_IDENTITY_Operation *op_head; + + /** + * Tail of active operations. + */ + struct GNUNET_IDENTITY_Operation *op_tail; + + /** + * Currently pending transmission request, or NULL for none. */ struct GNUNET_CLIENT_TransmitHandle *th; /** * Task doing exponential back-off trying to reconnect. */ - GNUNET_SCHEDULER_TaskIdentifier reconnect_task; + struct GNUNET_SCHEDULER_Task * reconnect_task; /** * Time for next connect retry. */ struct GNUNET_TIME_Relative reconnect_delay; + /** + * Are we polling for incoming messages right now? + */ + int in_receive; + }; /** - * Try again to connect to network size estimation service. + * Obtain the ego representing 'anonymous' users. * - * @param cls the handle to the transport service - * @param tc scheduler context + * @return handle for the anonymous user, must not be freed */ -static void -reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); +const struct GNUNET_IDENTITY_Ego * +GNUNET_IDENTITY_ego_get_anonymous () +{ + static struct GNUNET_IDENTITY_Ego anon; + struct GNUNET_CRYPTO_EcdsaPublicKey pub; + + if (NULL != anon.pk) + return &anon; + anon.pk = (struct GNUNET_CRYPTO_EcdsaPrivateKey *) GNUNET_CRYPTO_ecdsa_key_get_anonymous (); + GNUNET_CRYPTO_ecdsa_key_get_public (anon.pk, + &pub); + GNUNET_CRYPTO_hash (&pub, sizeof (pub), &anon.id); + return &anon; +} /** - * Type of a function to call when we receive a message - * from the service. + * Try again to connect to the identity service. * - * @param cls closure - * @param msg message received, NULL on timeout or fatal error + * @param cls handle to the identity service. + * @param tc scheduler context */ static void -message_handler (void *cls, const struct GNUNET_MessageHeader *msg) -{ - struct GNUNET_IDENTITY_Handle *h = cls; - const struct GNUNET_IDENTITY_ClientMessage *client_msg; - - if (msg == NULL) - { - /* Error, timeout, death */ - GNUNET_CLIENT_disconnect (h->client); - h->client = NULL; - h->reconnect_task = - GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, &reconnect, h); - return; - } - // FIXME: process... - GNUNET_CLIENT_receive (h->client, &message_handler, h, - GNUNET_TIME_UNIT_FOREVER_REL); -} - +reconnect (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc); /** @@ -117,7 +210,7 @@ message_handler (void *cls, const struct GNUNET_MessageHeader *msg) static void reschedule_connect (struct GNUNET_IDENTITY_Handle *h) { - GNUNET_assert (h->reconnect_task == GNUNET_SCHEDULER_NO_TASK); + GNUNET_assert (h->reconnect_task == NULL); if (NULL != h->th) { @@ -129,10 +222,10 @@ reschedule_connect (struct GNUNET_IDENTITY_Handle *h) GNUNET_CLIENT_disconnect (h->client); h->client = NULL; } - + h->in_receive = GNUNET_NO; LOG (GNUNET_ERROR_TYPE_DEBUG, - "Scheduling task to reconnect to identity service in %llu ms.\n", - h->reconnect_delay.rel_value); + "Scheduling task to reconnect to identity service in %s.\n", + GNUNET_STRINGS_relative_time_to_string (h->reconnect_delay, GNUNET_YES)); h->reconnect_task = GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, &reconnect, h); h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay); @@ -140,49 +233,337 @@ reschedule_connect (struct GNUNET_IDENTITY_Handle *h) /** - * Transmit START message to service. + * Type of a function to call when we receive a message + * from the service. + * + * @param cls closure + * @param msg message received, NULL on timeout or fatal error + */ +static void +message_handler (void *cls, + const struct GNUNET_MessageHeader *msg) +{ + struct GNUNET_IDENTITY_Handle *h = cls; + struct GNUNET_IDENTITY_Operation *op; + struct GNUNET_IDENTITY_Ego *ego; + const struct GNUNET_IDENTITY_ResultCodeMessage *rcm; + const struct GNUNET_IDENTITY_UpdateMessage *um; + const struct GNUNET_IDENTITY_SetDefaultMessage *sdm; + struct GNUNET_CRYPTO_EcdsaPublicKey pub; + struct GNUNET_HashCode id; + const char *str; + uint16_t size; + uint16_t name_len; + + if (NULL == msg) + { + reschedule_connect (h); + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received message of type %d from identity service\n", + ntohs (msg->type)); + size = ntohs (msg->size); + switch (ntohs (msg->type)) + { + case GNUNET_MESSAGE_TYPE_IDENTITY_RESULT_CODE: + if (size < sizeof (struct GNUNET_IDENTITY_ResultCodeMessage)) + { + GNUNET_break (0); + reschedule_connect (h); + return; + } + rcm = (const struct GNUNET_IDENTITY_ResultCodeMessage *) msg; + str = (const char *) &rcm[1]; + if ( (size > sizeof (struct GNUNET_IDENTITY_ResultCodeMessage)) && + ('\0' != str[size - sizeof (struct GNUNET_IDENTITY_ResultCodeMessage) - 1]) ) + { + GNUNET_break (0); + reschedule_connect (h); + return; + } + if (size == sizeof (struct GNUNET_IDENTITY_ResultCodeMessage)) + str = NULL; + + op = h->op_head; + GNUNET_CONTAINER_DLL_remove (h->op_head, + h->op_tail, + op); + GNUNET_CLIENT_receive (h->client, &message_handler, h, + GNUNET_TIME_UNIT_FOREVER_REL); + if (NULL != op->cont) + op->cont (op->cls, + str); + else if (NULL != op->cb) + op->cb (op->cls, NULL, NULL, NULL); + GNUNET_free (op); + break; + case GNUNET_MESSAGE_TYPE_IDENTITY_UPDATE: + if (size < sizeof (struct GNUNET_IDENTITY_UpdateMessage)) + { + GNUNET_break (0); + reschedule_connect (h); + return; + } + um = (const struct GNUNET_IDENTITY_UpdateMessage *) msg; + name_len = ntohs (um->name_len); + + str = (const char *) &um[1]; + if ( (size != name_len + sizeof (struct GNUNET_IDENTITY_UpdateMessage)) || + ( (0 != name_len) && + ('\0' != str[name_len - 1])) ) + { + GNUNET_break (0); + reschedule_connect (h); + return; + } + if (GNUNET_YES == ntohs (um->end_of_list)) + { + /* end of initial list of data */ + GNUNET_CLIENT_receive (h->client, &message_handler, h, + GNUNET_TIME_UNIT_FOREVER_REL); + if (NULL != h->cb) + h->cb (h->cb_cls, NULL, NULL, NULL); + break; + } + GNUNET_CRYPTO_ecdsa_key_get_public (&um->private_key, + &pub); + GNUNET_CRYPTO_hash (&pub, sizeof (pub), &id); + if (0 == name_len) + str = NULL; + else + str = (const char *) &um[1]; + ego = GNUNET_CONTAINER_multihashmap_get (h->egos, + &id); + if (NULL == ego) + { + /* ego was created */ + if (NULL == str) + { + /* deletion of unknown ego? not allowed */ + GNUNET_break (0); + reschedule_connect (h); + return; + } + ego = GNUNET_new (struct GNUNET_IDENTITY_Ego); + ego->pk = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPrivateKey); + *ego->pk = um->private_key; + ego->name = GNUNET_strdup (str); + ego->id = id; + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_put (h->egos, + &ego->id, + ego, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + } + if (NULL == str) + { + /* ego was deleted */ + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (h->egos, + &ego->id, + ego)); + } + else + { + /* ego changed name */ + GNUNET_free (ego->name); + ego->name = GNUNET_strdup (str); + } + GNUNET_CLIENT_receive (h->client, &message_handler, h, + GNUNET_TIME_UNIT_FOREVER_REL); + /* inform application about change */ + if (NULL != h->cb) + h->cb (h->cb_cls, + ego, + &ego->ctx, + str); + if (NULL == str) + { + GNUNET_free (ego->pk); + GNUNET_free (ego->name); + GNUNET_free (ego); + } + break; + case GNUNET_MESSAGE_TYPE_IDENTITY_SET_DEFAULT: + if (size < sizeof (struct GNUNET_IDENTITY_SetDefaultMessage)) + { + GNUNET_break (0); + reschedule_connect (h); + return; + } + sdm = (const struct GNUNET_IDENTITY_SetDefaultMessage *) msg; + GNUNET_break (0 == ntohs (sdm->reserved)); + name_len = ntohs (sdm->name_len); + str = (const char *) &sdm[1]; + if ( (size != name_len + sizeof (struct GNUNET_IDENTITY_SetDefaultMessage)) || + ( (0 != name_len) && + ('\0' != str[name_len - 1]) ) ) + { + GNUNET_break (0); + reschedule_connect (h); + return; + } + /* Note: we know which service this should be for, so we're not + really using 'str' henceforth */ + GNUNET_CRYPTO_ecdsa_key_get_public (&sdm->private_key, + &pub); + GNUNET_CRYPTO_hash (&pub, sizeof (pub), &id); + ego = GNUNET_CONTAINER_multihashmap_get (h->egos, + &id); + if (NULL == ego) + { + GNUNET_break (0); + reschedule_connect (h); + return; + } + op = h->op_head; + if (NULL == op) + { + GNUNET_break (0); + reschedule_connect (h); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received SET_DEFAULT message from identity service\n"); + GNUNET_CONTAINER_DLL_remove (h->op_head, + h->op_tail, + op); + GNUNET_CLIENT_receive (h->client, &message_handler, h, + GNUNET_TIME_UNIT_FOREVER_REL); + if (NULL != op->cb) + op->cb (op->cls, + ego, + &ego->ctx, + ego->name); + GNUNET_free (op); + break; + default: + GNUNET_break (0); + reschedule_connect (h); + return; + } +} + + +/** + * Schedule transmission of the next message from our queue. * - * @param cls unused - * @param size number of bytes available in buf + * @param h identity handle + */ +static void +transmit_next (struct GNUNET_IDENTITY_Handle *h); + + +/** + * Transmit next message to service. + * + * @param cls the `struct GNUNET_IDENTITY_Handle`. + * @param size number of bytes available in @a buf * @param buf where to copy the message * @return number of bytes copied to buf */ static size_t -send_start (void *cls, size_t size, void *buf) +send_next_message (void *cls, + size_t size, + void *buf) { - return sizeof (struct GNUNET_MessageHeader); + struct GNUNET_IDENTITY_Handle *h = cls; + struct GNUNET_IDENTITY_Operation *op = h->op_head; + size_t ret; + + h->th = NULL; + if (NULL == op) + return 0; + ret = ntohs (op->msg->size); + if (ret > size) + { + reschedule_connect (h); + return 0; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sending message of type %d to identity service\n", + ntohs (op->msg->type)); + memcpy (buf, op->msg, ret); + if ( (NULL == op->cont) && + (NULL == op->cb) ) + { + GNUNET_CONTAINER_DLL_remove (h->op_head, + h->op_tail, + op); + GNUNET_free (op); + transmit_next (h); + } + if (GNUNET_NO == h->in_receive) + { + h->in_receive = GNUNET_YES; + GNUNET_CLIENT_receive (h->client, + &message_handler, h, + GNUNET_TIME_UNIT_FOREVER_REL); + } + return ret; } /** - * Try again to connect to network size estimation service. + * Schedule transmission of the next message from our queue. * - * @param cls the handle to the transport service + * @param h identity handle + */ +static void +transmit_next (struct GNUNET_IDENTITY_Handle *h) +{ + struct GNUNET_IDENTITY_Operation *op = h->op_head; + + GNUNET_assert (NULL == h->th); + if (NULL == op) + return; + if (NULL == h->client) + return; + h->th = GNUNET_CLIENT_notify_transmit_ready (h->client, + ntohs (op->msg->size), + GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_NO, + &send_next_message, + h); +} + + +/** + * Try again to connect to the identity service. + * + * @param cls handle to the identity service. * @param tc scheduler context */ static void reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct GNUNET_IDENTITY_Handle *h = cls; + struct GNUNET_IDENTITY_Operation *op; + struct GNUNET_MessageHeader msg; - h->reconnect_task = GNUNET_SCHEDULER_NO_TASK; - if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) - { - /* shutdown, just give up */ - return; - } + h->reconnect_task = NULL; LOG (GNUNET_ERROR_TYPE_DEBUG, - "Connecting to network size estimation service.\n"); - GNUNET_assert (h->client == NULL); + "Connecting to identity service.\n"); + GNUNET_assert (NULL == h->client); h->client = GNUNET_CLIENT_connect ("identity", h->cfg); - GNUNET_assert (h->client != NULL); - - h->th = - GNUNET_CLIENT_notify_transmit_ready (h->client, - sizeof (struct GNUNET_MessageHeader), - GNUNET_TIME_UNIT_FOREVER_REL, - GNUNET_NO, &send_start, h); - GNUNET_assert (h->th != NULL); + GNUNET_assert (NULL != h->client); + if ( (NULL == h->op_head) || + (GNUNET_MESSAGE_TYPE_IDENTITY_START != ntohs (h->op_head->msg->type)) ) + { + op = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_Operation) + + sizeof (struct GNUNET_MessageHeader)); + op->h = h; + op->msg = (const struct GNUNET_MessageHeader *) &op[1]; + msg.size = htons (sizeof (msg)); + msg.type = htons (GNUNET_MESSAGE_TYPE_IDENTITY_START); + memcpy (&op[1], &msg, sizeof (msg)); + GNUNET_CONTAINER_DLL_insert (h->op_head, + h->op_tail, + op); + } + transmit_next (h); + GNUNET_assert (NULL != h->th); } @@ -191,7 +572,7 @@ reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) * * @param cfg the configuration to use * @param cb function to call on all identity events, can be NULL - * @param cb_cls closure for 'cb' + * @param cb_cls closure for @a cb * @return handle to use */ struct GNUNET_IDENTITY_Handle * @@ -199,13 +580,378 @@ GNUNET_IDENTITY_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, GNUNET_IDENTITY_Callback cb, void *cb_cls) { - struct GNUNET_IDENTITY_Handle *ret; + struct GNUNET_IDENTITY_Handle *h; + + h = GNUNET_new (struct GNUNET_IDENTITY_Handle); + h->cfg = cfg; + h->cb = cb; + h->cb_cls = cb_cls; + h->egos = GNUNET_CONTAINER_multihashmap_create (16, GNUNET_YES); + h->reconnect_delay = GNUNET_TIME_UNIT_ZERO; + h->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, h); + return h; +} - ret = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_Handle)); - ret->cfg = cfg; - ret->reconnect_delay = GNUNET_TIME_UNIT_ZERO; - ret->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, ret); - return ret; + +/** + * Obtain the ECC key associated with a ego. + * + * @param ego the ego + * @return associated ECC key, valid as long as the ego is valid + */ +const struct GNUNET_CRYPTO_EcdsaPrivateKey * +GNUNET_IDENTITY_ego_get_private_key (const struct GNUNET_IDENTITY_Ego *ego) +{ + return ego->pk; +} + + +/** + * Get the identifier (public key) of an ego. + * + * @param ego identity handle with the private key + * @param pk set to ego's public key + */ +void +GNUNET_IDENTITY_ego_get_public_key (const struct GNUNET_IDENTITY_Ego *ego, + struct GNUNET_CRYPTO_EcdsaPublicKey *pk) +{ + GNUNET_CRYPTO_ecdsa_key_get_public (ego->pk, + pk); +} + + +/** + * Obtain the identity that is currently preferred/default + * for a service. + * + * @param id identity service to query + * @param service_name for which service is an identity wanted + * @param cb function to call with the result (will only be called once) + * @param cb_cls closure for @a cb + * @return handle to abort the operation + */ +struct GNUNET_IDENTITY_Operation * +GNUNET_IDENTITY_get (struct GNUNET_IDENTITY_Handle *id, + const char *service_name, + GNUNET_IDENTITY_Callback cb, + void *cb_cls) +{ + struct GNUNET_IDENTITY_Operation *op; + struct GNUNET_IDENTITY_GetDefaultMessage *gdm; + size_t slen; + + slen = strlen (service_name) + 1; + if (slen >= GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_IDENTITY_GetDefaultMessage)) + { + GNUNET_break (0); + return NULL; + } + op = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_Operation) + + sizeof (struct GNUNET_IDENTITY_GetDefaultMessage) + + slen); + op->h = id; + op->cb = cb; + op->cls = cb_cls; + gdm = (struct GNUNET_IDENTITY_GetDefaultMessage *) &op[1]; + gdm->header.type = htons (GNUNET_MESSAGE_TYPE_IDENTITY_GET_DEFAULT); + gdm->header.size = htons (sizeof (struct GNUNET_IDENTITY_GetDefaultMessage) + + slen); + gdm->name_len = htons (slen); + gdm->reserved = htons (0); + memcpy (&gdm[1], service_name, slen); + op->msg = &gdm->header; + GNUNET_CONTAINER_DLL_insert_tail (id->op_head, + id->op_tail, + op); + if (NULL == id->th) + transmit_next (id); + return op; +} + + +/** + * Set the preferred/default identity for a service. + * + * @param id identity service to inform + * @param service_name for which service is an identity set + * @param ego new default identity to be set for this service + * @param cont function to call once the operation finished + * @param cont_cls closure for @a cont + * @return handle to abort the operation + */ +struct GNUNET_IDENTITY_Operation * +GNUNET_IDENTITY_set (struct GNUNET_IDENTITY_Handle *id, + const char *service_name, + struct GNUNET_IDENTITY_Ego *ego, + GNUNET_IDENTITY_Continuation cont, + void *cont_cls) +{ + struct GNUNET_IDENTITY_Operation *op; + struct GNUNET_IDENTITY_SetDefaultMessage *sdm; + size_t slen; + + slen = strlen (service_name) + 1; + if (slen >= GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_IDENTITY_SetDefaultMessage)) + { + GNUNET_break (0); + return NULL; + } + op = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_Operation) + + sizeof (struct GNUNET_IDENTITY_SetDefaultMessage) + + slen); + op->h = id; + op->cont = cont; + op->cls = cont_cls; + sdm = (struct GNUNET_IDENTITY_SetDefaultMessage *) &op[1]; + sdm->header.type = htons (GNUNET_MESSAGE_TYPE_IDENTITY_SET_DEFAULT); + sdm->header.size = htons (sizeof (struct GNUNET_IDENTITY_SetDefaultMessage) + + slen); + sdm->name_len = htons (slen); + sdm->reserved = htons (0); + sdm->private_key = *ego->pk; + memcpy (&sdm[1], service_name, slen); + op->msg = &sdm->header; + GNUNET_CONTAINER_DLL_insert_tail (id->op_head, + id->op_tail, + op); + if (NULL == id->th) + transmit_next (id); + return op; +} + + +/** + * Create a new identity with the given name. + * + * @param id identity service to use + * @param name desired name + * @param cont function to call with the result (will only be called once) + * @param cont_cls closure for @a cont + * @return handle to abort the operation + */ +struct GNUNET_IDENTITY_Operation * +GNUNET_IDENTITY_create (struct GNUNET_IDENTITY_Handle *id, + const char *name, + GNUNET_IDENTITY_Continuation cont, + void *cont_cls) +{ + struct GNUNET_IDENTITY_Operation *op; + struct GNUNET_IDENTITY_CreateRequestMessage *crm; + struct GNUNET_CRYPTO_EcdsaPrivateKey *pk; + size_t slen; + + slen = strlen (name) + 1; + pk = GNUNET_CRYPTO_ecdsa_key_create (); + + if (slen >= GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_IDENTITY_CreateRequestMessage)) + { + GNUNET_break (0); + GNUNET_free (pk); + return NULL; + } + op = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_Operation) + + sizeof (struct GNUNET_IDENTITY_CreateRequestMessage) + + slen); + op->h = id; + op->cont = cont; + op->cls = cont_cls; + crm = (struct GNUNET_IDENTITY_CreateRequestMessage *) &op[1]; + crm->header.type = htons (GNUNET_MESSAGE_TYPE_IDENTITY_CREATE); + crm->header.size = htons (sizeof (struct GNUNET_IDENTITY_CreateRequestMessage) + + slen); + crm->name_len = htons (slen); + crm->reserved = htons (0); + crm->private_key = *pk; + memcpy (&crm[1], name, slen); + op->msg = &crm->header; + GNUNET_CONTAINER_DLL_insert_tail (id->op_head, + id->op_tail, + op); + if (NULL == id->th) + transmit_next (id); + GNUNET_free (pk); + return op; +} + + +/** + * Renames an existing identity. + * + * @param id identity service to use + * @param old_name old name + * @param new_name desired new name + * @param cb function to call with the result (will only be called once) + * @param cb_cls closure for @a cb + * @return handle to abort the operation + */ +struct GNUNET_IDENTITY_Operation * +GNUNET_IDENTITY_rename (struct GNUNET_IDENTITY_Handle *id, + const char *old_name, + const char *new_name, + GNUNET_IDENTITY_Continuation cb, + void *cb_cls) +{ + struct GNUNET_IDENTITY_Operation *op; + struct GNUNET_IDENTITY_RenameMessage *grm; + size_t slen_old; + size_t slen_new; + char *dst; + + slen_old = strlen (old_name) + 1; + slen_new = strlen (new_name) + 1; + if ( (slen_old >= GNUNET_SERVER_MAX_MESSAGE_SIZE) || + (slen_new >= GNUNET_SERVER_MAX_MESSAGE_SIZE) || + (slen_old + slen_new >= GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_IDENTITY_RenameMessage)) ) + { + GNUNET_break (0); + return NULL; + } + op = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_Operation) + + sizeof (struct GNUNET_IDENTITY_RenameMessage) + + slen_old + slen_new); + op->h = id; + op->cont = cb; + op->cls = cb_cls; + grm = (struct GNUNET_IDENTITY_RenameMessage *) &op[1]; + grm->header.type = htons (GNUNET_MESSAGE_TYPE_IDENTITY_RENAME); + grm->header.size = htons (sizeof (struct GNUNET_IDENTITY_RenameMessage) + + slen_old + slen_new); + grm->old_name_len = htons (slen_old); + grm->new_name_len = htons (slen_new); + dst = (char *) &grm[1]; + memcpy (dst, old_name, slen_old); + memcpy (&dst[slen_old], new_name, slen_new); + op->msg = &grm->header; + GNUNET_CONTAINER_DLL_insert_tail (id->op_head, + id->op_tail, + op); + if (NULL == id->th) + transmit_next (id); + return op; +} + + +/** + * Delete an existing identity. + * + * @param id identity service to use + * @param name name of the identity to delete + * @param cb function to call with the result (will only be called once) + * @param cb_cls closure for @a cb + * @return handle to abort the operation + */ +struct GNUNET_IDENTITY_Operation * +GNUNET_IDENTITY_delete (struct GNUNET_IDENTITY_Handle *id, + const char *name, + GNUNET_IDENTITY_Continuation cb, + void *cb_cls) +{ + struct GNUNET_IDENTITY_Operation *op; + struct GNUNET_IDENTITY_DeleteMessage *gdm; + size_t slen; + + slen = strlen (name) + 1; + if (slen >= GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct GNUNET_IDENTITY_DeleteMessage)) + { + GNUNET_break (0); + return NULL; + } + op = GNUNET_malloc (sizeof (struct GNUNET_IDENTITY_Operation) + + sizeof (struct GNUNET_IDENTITY_DeleteMessage) + + slen); + op->h = id; + op->cont = cb; + op->cls = cb_cls; + gdm = (struct GNUNET_IDENTITY_DeleteMessage *) &op[1]; + gdm->header.type = htons (GNUNET_MESSAGE_TYPE_IDENTITY_DELETE); + gdm->header.size = htons (sizeof (struct GNUNET_IDENTITY_DeleteMessage) + + slen); + gdm->name_len = htons (slen); + gdm->reserved = htons (0); + memcpy (&gdm[1], name, slen); + op->msg = &gdm->header; + GNUNET_CONTAINER_DLL_insert_tail (id->op_head, + id->op_tail, + op); + if (NULL == id->th) + transmit_next (id); + return op; +} + + +/** + * Cancel an identity operation. Note that the operation MAY still + * be executed; this merely cancels the continuation; if the request + * was already transmitted, the service may still choose to complete + * the operation. + * + * @param op operation to cancel + */ +void +GNUNET_IDENTITY_cancel (struct GNUNET_IDENTITY_Operation *op) +{ + struct GNUNET_IDENTITY_Handle *h = op->h; + + if ( (h->op_head != op) || + (NULL == h->client) ) + { + /* request not active, can simply remove */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client aborted non-head operation, simply removing it\n"); + GNUNET_CONTAINER_DLL_remove (h->op_head, + h->op_tail, + op); + GNUNET_free (op); + return; + } + if (NULL != h->th) + { + /* request active but not yet with service, can still abort */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client aborted head operation prior to transmission, aborting it\n"); + GNUNET_CLIENT_notify_transmit_ready_cancel (h->th); + h->th = NULL; + GNUNET_CONTAINER_DLL_remove (h->op_head, + h->op_tail, + op); + GNUNET_free (op); + transmit_next (h); + return; + } + /* request active with service, simply ensure continuations are not called */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Client aborted active request, NULLing continuation\n"); + op->cont = NULL; + op->cb = NULL; +} + + +/** + * Free ego from hash map. + * + * @param cls identity service handle + * @param key unused + * @param value ego to free + * @return #GNUNET_OK (continue to iterate) + */ +static int +free_ego (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct GNUNET_IDENTITY_Handle *h = cls; + struct GNUNET_IDENTITY_Ego *ego = value; + + if (NULL != h->cb) + h->cb (h->cb_cls, + ego, + &ego->ctx, + NULL); + GNUNET_free (ego->pk); + GNUNET_free (ego->name); + GNUNET_free (ego); + return GNUNET_OK; } @@ -217,18 +963,36 @@ GNUNET_IDENTITY_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, void GNUNET_IDENTITY_disconnect (struct GNUNET_IDENTITY_Handle *h) { + struct GNUNET_IDENTITY_Operation *op; + GNUNET_assert (NULL != h); - if (h->reconnect_task != GNUNET_SCHEDULER_NO_TASK) + if (h->reconnect_task != NULL) { GNUNET_SCHEDULER_cancel (h->reconnect_task); - h->reconnect_task = GNUNET_SCHEDULER_NO_TASK; + h->reconnect_task = NULL; } - if (h->th != NULL) + if (NULL != h->th) { GNUNET_CLIENT_notify_transmit_ready_cancel (h->th); h->th = NULL; } - if (h->client != NULL) + if (NULL != h->egos) + { + GNUNET_CONTAINER_multihashmap_iterate (h->egos, + &free_ego, + h); + GNUNET_CONTAINER_multihashmap_destroy (h->egos); + h->egos = NULL; + } + while (NULL != (op = h->op_head)) + { + GNUNET_break (NULL == op->cont); + GNUNET_CONTAINER_DLL_remove (h->op_head, + h->op_tail, + op); + GNUNET_free (op); + } + if (NULL != h->client) { GNUNET_CLIENT_disconnect (h->client); h->client = NULL;