X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fsocial%2Fsocial_api.c;h=89843831be02164972b6c04c6877066d88156391;hb=0120859e1ea2f0591602f446d4bc054e9230c801;hp=afe85cbdc5f9a2ce1a0365618e74b562f3bf486f;hpb=c2d9d1e64c9801122caaa6b429fc67706db5c9d7;p=oweals%2Fgnunet.git diff --git a/src/social/social_api.c b/src/social/social_api.c index afe85cbdc..89843831b 100644 --- a/src/social/social_api.c +++ b/src/social/social_api.c @@ -1,6 +1,6 @@ - /* +/* * This file is part of GNUnet - * Copyright (C) 2013 Christian Grothoff (and other contributing authors) + * Copyright (C) 2013 GNUnet e.V. * * GNUnet is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published @@ -14,14 +14,15 @@ * * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. */ /** - * @file social/social_api.c - * @brief Social service; implements social interactions using the PSYC service. * @author Gabor X Toth + * + * @file + * Social service; implements social interactions using the PSYC service. */ #include @@ -29,11 +30,6 @@ #include "platform.h" #include "gnunet_util_lib.h" -#include "gnunet_env_lib.h" -#include "gnunet_core_service.h" -#include "gnunet_identity_service.h" -#include "gnunet_namestore_service.h" -#include "gnunet_gns_service.h" #include "gnunet_psyc_service.h" #include "gnunet_psyc_util_lib.h" #include "gnunet_social_service.h" @@ -41,11 +37,106 @@ #define LOG(kind,...) GNUNET_log_from (kind, "social-api",__VA_ARGS__) +/** + * Handle for an ego. + */ +struct GNUNET_SOCIAL_Ego +{ + struct GNUNET_CRYPTO_EcdsaPublicKey pub_key; + struct GNUNET_HashCode pub_key_hash; + char *name; +}; + + +/** + * Handle for a pseudonym of another user in the network. + */ +struct GNUNET_SOCIAL_Nym +{ + struct GNUNET_CRYPTO_EcdsaPublicKey pub_key; + struct GNUNET_HashCode pub_key_hash; +}; + + +/** + * Handle for an application. + */ +struct GNUNET_SOCIAL_App +{ + /** + * Configuration to use. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Client connection to the service. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Message to send on connect. + */ + struct GNUNET_MQ_Envelope *connect_env; + + /** + * Time to wait until we try to reconnect on failure. + */ + struct GNUNET_TIME_Relative reconnect_delay; + + /** + * Task for reconnecting when the listener fails. + */ + struct GNUNET_SCHEDULER_Task *reconnect_task; + + /** + * Async operations. + */ + struct GNUNET_OP_Handle *op; + + /** + * Function called after disconnected from the service. + */ + GNUNET_ContinuationCallback disconnect_cb; + + /** + * Closure for @a disconnect_cb. + */ + void *disconnect_cls; + + /** + * Application ID. + */ + char *id; + + /** + * Hash map of all egos. + * pub_key_hash -> struct GNUNET_SOCIAL_Ego * + */ + struct GNUNET_CONTAINER_MultiHashMap *egos; + + GNUNET_SOCIAL_AppEgoCallback ego_cb; + GNUNET_SOCIAL_AppHostPlaceCallback host_cb; + GNUNET_SOCIAL_AppGuestPlaceCallback guest_cb; + GNUNET_SOCIAL_AppConnectedCallback connected_cb; + void *cb_cls; +}; + + +struct GNUNET_SOCIAL_HostConnection +{ + struct GNUNET_SOCIAL_App *app; + + struct AppPlaceMessage plc_msg; +}; + + +struct GNUNET_SOCIAL_GuestConnection +{ + struct GNUNET_SOCIAL_App *app; + + struct AppPlaceMessage plc_msg; +}; -static struct GNUNET_CORE_Handle *core; -static struct GNUNET_GNS_Handle *gns; -static struct GNUNET_NAMESTORE_Handle *namestore; -static struct GNUNET_PeerIdentity this_peer; /** * Handle for a place where social interactions happen. @@ -60,28 +151,39 @@ struct GNUNET_SOCIAL_Place /** * Client connection to the service. */ - struct GNUNET_CLIENT_MANAGER_Connection *client; + struct GNUNET_MQ_Handle *mq; /** - * Transmission handle; + * Message to send on connect. */ - struct GNUNET_PSYC_TransmitHandle *tmit; + struct GNUNET_MQ_Envelope *connect_env; /** - * Receipt handle; + * Time to wait until we try to reconnect on failure. */ - struct GNUNET_PSYC_ReceiveHandle *recv; + struct GNUNET_TIME_Relative reconnect_delay; /** - * Message to send on reconnect. + * Task for reconnecting when the listener fails. */ - struct GNUNET_MessageHeader *connect_msg; + struct GNUNET_SCHEDULER_Task *reconnect_task; /** - * Slicer for processing incoming methods. + * Async operations. */ - struct GNUNET_SOCIAL_Slicer *slicer; + struct GNUNET_OP_Handle *op; + /** + * Transmission handle. + */ + struct GNUNET_PSYC_TransmitHandle *tmit; + + /** + * Slicer for processing incoming messages. + */ + struct GNUNET_PSYC_Slicer *slicer; + + // FIXME: do we need is_disconnecing like on the psyc and multicast APIs? /** * Function called after disconnected from the service. */ @@ -98,20 +200,14 @@ struct GNUNET_SOCIAL_Place struct GNUNET_CRYPTO_EddsaPublicKey pub_key; /** - * Private key of the ego. + * Public key of the ego. */ - struct GNUNET_CRYPTO_EcdsaPrivateKey ego_key; + struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key; /** * Does this place belong to a host (#GNUNET_YES) or guest (#GNUNET_NO)? */ uint8_t is_host; - - /** - * Is this place in the process of disconnecting from the service? - * #GNUNET_YES or #GNUNET_NO - */ - uint8_t is_disconnecting; }; @@ -122,7 +218,10 @@ struct GNUNET_SOCIAL_Host { struct GNUNET_SOCIAL_Place plc; - struct GNUNET_CRYPTO_EddsaPrivateKey place_key; + /** + * Slicer for processing incoming messages from guests. + */ + struct GNUNET_PSYC_Slicer *slicer; GNUNET_SOCIAL_HostEnterCallback enter_cb; @@ -134,6 +233,9 @@ struct GNUNET_SOCIAL_Host * Closure for callbacks. */ void *cb_cls; + + struct GNUNET_SOCIAL_Nym *notice_place_leave_nym; + struct GNUNET_PSYC_Environment *notice_place_leave_env; }; @@ -156,118 +258,183 @@ struct GNUNET_SOCIAL_Guest /** - * Handle for a pseudonym of another user in the network. + * Hash map of all nyms. + * pub_key_hash -> struct GNUNET_SOCIAL_Nym * */ -struct GNUNET_SOCIAL_Nym +struct GNUNET_CONTAINER_MultiHashMap *nyms; + + +/** + * Handle for an announcement request. + */ +struct GNUNET_SOCIAL_Announcement { - struct GNUNET_CRYPTO_EcdsaPublicKey pub_key; - struct GNUNET_HashCode pub_key_hash; + }; /** - * Hash map of all nyms. - * pub_key_hash -> struct GNUNET_SOCIAL_Nym * + * A talk request. */ -struct GNUNET_CONTAINER_MultiHashMap *nyms; +struct GNUNET_SOCIAL_TalkRequest +{ + +}; /** - * Handle for a try-and-slice instance. + * A history lesson. */ -struct GNUNET_SOCIAL_Slicer +struct GNUNET_SOCIAL_HistoryRequest { /** - * Message handlers: method_name -> SlicerCallbacks + * Place. + */ + struct GNUNET_SOCIAL_Place *plc; + + /** + * Operation ID. */ - struct GNUNET_CONTAINER_MultiHashMap *handlers; + uint64_t op_id; + /** + * Slicer for processing incoming messages. + */ + struct GNUNET_PSYC_Slicer *slicer; /** - * Currently being processed message part. + * Function to call when the operation finished. */ - const struct GNUNET_MessageHeader *msg; + GNUNET_ResultCallback result_cb; /** - * ID of currently being received message. + * Closure for @a result_cb. */ - uint64_t message_id; + void *cls; +}; + +struct GNUNET_SOCIAL_LookHandle +{ /** - * Method name of currently being received message. + * Place. */ - char *method_name; + struct GNUNET_SOCIAL_Place *plc; /** - * Public key of the nym the current message originates from. + * Operation ID. */ - struct GNUNET_CRYPTO_EcdsaPublicKey nym_key; + uint64_t op_id; /** - * Size of @a method_name (including terminating \0). + * State variable result callback. */ - uint16_t method_name_size; -}; + GNUNET_PSYC_StateVarCallback var_cb; + /** + * Function to call when the operation finished. + */ + GNUNET_ResultCallback result_cb; -/** - * Callbacks for a slicer method handler. - */ -struct SlicerCallbacks -{ - GNUNET_SOCIAL_MethodCallback method_cb; - GNUNET_SOCIAL_ModifierCallback modifier_cb; - GNUNET_SOCIAL_DataCallback data_cb; - GNUNET_SOCIAL_EndOfMessageCallback eom_cb; + /** + * Name of current modifier being received. + */ + char *mod_name; + + /** + * Size of current modifier value being received. + */ + size_t mod_value_size; + + /** + * Remaining size of current modifier value still to be received. + */ + size_t mod_value_remaining; + + /** + * Closure for @a result_cb. + */ void *cls; }; -struct SlicerRemoveClosure +struct ZoneAddPlaceHandle { - struct GNUNET_SOCIAL_Slicer *slicer; - struct SlicerCallbacks rm_cbs; + GNUNET_ResultCallback result_cb; + void *result_cls; }; -/** - * Handle for an announcement request. - */ -struct GNUNET_SOCIAL_Announcement +struct ZoneAddNymHandle { - + GNUNET_ResultCallback result_cb; + void *result_cls; }; -struct GNUNET_SOCIAL_WatchHandle -{ - -}; +/*** CLEANUP / DISCONNECT ***/ -struct GNUNET_SOCIAL_LookHandle +static void +host_cleanup (struct GNUNET_SOCIAL_Host *hst) { + if (NULL != hst->slicer) + { + GNUNET_PSYC_slicer_destroy (hst->slicer); + hst->slicer = NULL; + } + GNUNET_free (hst); +} -}; + +static void +guest_cleanup (struct GNUNET_SOCIAL_Guest *gst) +{ + GNUNET_free (gst); +} -/** - * A talk request. - */ -struct GNUNET_SOCIAL_TalkRequest +static void +place_cleanup (struct GNUNET_SOCIAL_Place *plc) { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "cleaning up place %p\n", + plc); + if (NULL != plc->tmit) + { + GNUNET_PSYC_transmit_destroy (plc->tmit); + plc->tmit = NULL; + } + if (NULL != plc->connect_env) + { + GNUNET_MQ_discard (plc->connect_env); + plc->connect_env = NULL; + } + if (NULL != plc->mq) + { + GNUNET_MQ_destroy (plc->mq); + plc->mq = NULL; + } + if (NULL != plc->disconnect_cb) + { + plc->disconnect_cb (plc->disconnect_cls); + plc->disconnect_cb = NULL; + } -}; + (GNUNET_YES == plc->is_host) + ? host_cleanup ((struct GNUNET_SOCIAL_Host *) plc) + : guest_cleanup ((struct GNUNET_SOCIAL_Guest *) plc); +} -/** - * A history lesson. - */ -struct GNUNET_SOCIAL_HistoryLesson +static void +place_disconnect (struct GNUNET_SOCIAL_Place *plc) { + place_cleanup (plc); +} -}; +/*** NYM ***/ static struct GNUNET_SOCIAL_Nym * nym_get_or_create (const struct GNUNET_CRYPTO_EcdsaPublicKey *pub_key) @@ -305,513 +472,726 @@ nym_destroy (struct GNUNET_SOCIAL_Nym *nym) } -/** - * Call a handler for an incoming message part. - * - * @param cls - * @param key - * @param value - * - * @return - */ -int -slicer_handler_notify (void *cls, const struct GNUNET_HashCode *key, - void *value) +/*** MESSAGE HANDLERS ***/ + +/** _notice_place_leave from guests */ + +static void +host_recv_notice_place_leave_method (void *cls, + const struct GNUNET_PSYC_MessageHeader *msg, + const struct GNUNET_PSYC_MessageMethod *meth, + uint64_t message_id, + const char *method_name) { - struct GNUNET_SOCIAL_Slicer *slicer = cls; - const struct GNUNET_MessageHeader *msg = slicer->msg; - struct SlicerCallbacks *cbs = value; - uint16_t ptype = ntohs (msg->type); + struct GNUNET_SOCIAL_Host *hst = cls; - switch (ptype) - { - case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD: - { - if (NULL == cbs->method_cb) - break; - struct GNUNET_PSYC_MessageMethod * - meth = (struct GNUNET_PSYC_MessageMethod *) msg; - cbs->method_cb (cbs->cls, meth, slicer->message_id, - ntohl (meth->flags), - nym_get_or_create (&slicer->nym_key), - slicer->method_name); - break; - } + if (0 == memcmp (&(struct GNUNET_CRYPTO_EcdsaPublicKey) {}, + &msg->slave_pub_key, sizeof (msg->slave_pub_key))) + return; - case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER: - { - if (NULL == cbs->modifier_cb) - break; - struct GNUNET_PSYC_MessageModifier * - mod = (struct GNUNET_PSYC_MessageModifier *) msg; - cbs->modifier_cb (cbs->cls, mod, slicer->message_id, - mod->oper, (const char *) &mod[1], - (const void *) &mod[1] + ntohs (mod->name_size), - ntohs (mod->value_size)); - break; - } + struct GNUNET_SOCIAL_Nym *nym = nym_get_or_create (&msg->slave_pub_key); - case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT: - { - if (NULL == cbs->modifier_cb) - break; - /* FIXME: concatenate until done */ - break; - } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Host received method for message ID %" PRIu64 " from nym %s: %s\n", + message_id, GNUNET_h2s (&nym->pub_key_hash), method_name); - case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA: - { - if (NULL == cbs->data_cb) - break; - uint64_t data_offset = 0; // FIXME - cbs->data_cb (cbs->cls, msg, slicer->message_id, - data_offset, &msg[1], ntohs (msg->size) - sizeof (*msg)); - break; - } + hst->notice_place_leave_nym = (struct GNUNET_SOCIAL_Nym *) nym; + hst->notice_place_leave_env = GNUNET_PSYC_env_create (); - case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END: - if (NULL == cbs->eom_cb) - break; - cbs->eom_cb (cbs->cls, msg, slicer->message_id, GNUNET_NO); - break; + char *str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&hst->notice_place_leave_nym->pub_key); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "_notice_place_leave: got method from nym %s (%s).\n", + GNUNET_h2s (&hst->notice_place_leave_nym->pub_key_hash), str); + GNUNET_free (str); +} - case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL: - if (NULL == cbs->eom_cb) - break; - cbs->eom_cb (cbs->cls, msg, slicer->message_id, GNUNET_YES); - break; - } - return GNUNET_YES; + +static void +host_recv_notice_place_leave_modifier (void *cls, + const struct GNUNET_PSYC_MessageHeader *msg, + const struct GNUNET_MessageHeader *pmsg, + uint64_t message_id, + enum GNUNET_PSYC_Operator oper, + const char *name, + const void *value, + uint16_t value_size, + uint16_t full_value_size) +{ + struct GNUNET_SOCIAL_Host *hst = cls; + if (NULL == hst->notice_place_leave_env) + return; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Host received modifier for _notice_place_leave message with ID %" PRIu64 ":\n" + "%c%s: %.*s\n", + message_id, oper, name, value_size, (const char *) value); + + /* skip _nym, it's added later in eom() */ + if (0 == memcmp (name, "_nym", sizeof ("_nym")) + || 0 == memcmp (name, "_nym_", sizeof ("_nym_") - 1)) + return; + + GNUNET_PSYC_env_add (hst->notice_place_leave_env, + GNUNET_PSYC_OP_SET, name, value, value_size); } -/** - * Process an incoming message part and call matching handlers. - * - * @param cls - * Closure. - * @param message_id - * ID of the message. - * @param flags - * Flags for the message. - * @see enum GNUNET_PSYC_MessageFlags - * @param msg - * The message part. as it arrived from the network. - */ static void -slicer_message (void *cls, uint64_t message_id, uint64_t fragment_offset, - uint32_t flags, const struct GNUNET_MessageHeader *msg) +host_recv_notice_place_leave_eom (void *cls, + const struct GNUNET_PSYC_MessageHeader *msg, + const struct GNUNET_MessageHeader *pmsg, + uint64_t message_id, + uint8_t is_cancelled) { - struct GNUNET_SOCIAL_Slicer *slicer = cls; - uint16_t ptype = ntohs (msg->type); - if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD == ptype) - { - struct GNUNET_PSYC_MessageMethod * - meth = (struct GNUNET_PSYC_MessageMethod *) msg; - slicer->method_name_size = ntohs (meth->header.size) - sizeof (*meth); - slicer->method_name = GNUNET_malloc (slicer->method_name_size); - memcpy (slicer->method_name, &meth[1], slicer->method_name_size); - slicer->message_id = message_id; - } - else - { - GNUNET_assert (message_id == slicer->message_id); - } + struct GNUNET_SOCIAL_Host *hst = cls; + if (NULL == hst->notice_place_leave_env) + return; - LOG (GNUNET_ERROR_TYPE_WARNING, - "Slicer received message of type %u and size %u, " - "with ID %" PRIu64 " and method %s\n", - ptype, ntohs (msg->size), message_id, slicer->method_name); + char *str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&hst->notice_place_leave_nym->pub_key); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "_notice_place_leave: got EOM from nym %s (%s).\n", + GNUNET_h2s (&hst->notice_place_leave_nym->pub_key_hash), str); + GNUNET_free (str); - slicer->msg = msg; - char *name = GNUNET_malloc (slicer->method_name_size); - memcpy (name, slicer->method_name, slicer->method_name_size); - do + if (GNUNET_YES != is_cancelled) { - struct GNUNET_HashCode key; - uint16_t name_len = strlen (name); - GNUNET_CRYPTO_hash (name, name_len, &key); - GNUNET_CONTAINER_multihashmap_get_multiple (slicer->handlers, &key, - &slicer_handler_notify, slicer); - char *p = strrchr (name, '_'); - if (NULL == p) - break; - *p = '\0'; - } while (1); - GNUNET_free (name); - slicer->msg = NULL; - - if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= ptype) - GNUNET_free (slicer->method_name); + if (NULL != hst->farewell_cb) + hst->farewell_cb (hst->cb_cls, hst->notice_place_leave_nym, + hst->notice_place_leave_env); + /* announce leaving guest to place */ + GNUNET_PSYC_env_add (hst->notice_place_leave_env, GNUNET_PSYC_OP_SET, + "_nym", hst->notice_place_leave_nym, + sizeof (*hst->notice_place_leave_nym)); + GNUNET_SOCIAL_host_announce (hst, "_notice_place_leave", + hst->notice_place_leave_env, + NULL, NULL, GNUNET_SOCIAL_ANNOUNCE_NONE); + nym_destroy (hst->notice_place_leave_nym); + } + GNUNET_PSYC_env_destroy (hst->notice_place_leave_env); + hst->notice_place_leave_env = NULL; } -/** - * Create a try-and-slice instance. - * - * @return A new try-and-slice construct. - */ -struct GNUNET_SOCIAL_Slicer * -GNUNET_SOCIAL_slicer_create (void) +/*** PLACE ***/ + + +static int +check_place_result (void *cls, + const struct GNUNET_OperationResultMessage *res) { - struct GNUNET_SOCIAL_Slicer *slicer = GNUNET_malloc (sizeof (*slicer)); - slicer->handlers = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO); - return slicer; + uint16_t size = ntohs (res->header.size); + if (size < sizeof (*res)) + { /* Error, message too small. */ + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; } -/** - * Add a method to the try-and-slice instance. - * - * A slicer processes messages and calls methods that match a message. A match - * happens whenever the method name of a message starts with the method_name - * parameter given here. - * - * @param slicer The try-and-slice instance to extend. - * @param method_name Name of the given method, use empty string for default. - * @param method Method to invoke. - * @param method_cls Closure for method. - */ -void -GNUNET_SOCIAL_slicer_add (struct GNUNET_SOCIAL_Slicer *slicer, - const char *method_name, - GNUNET_SOCIAL_MethodCallback method_cb, - GNUNET_SOCIAL_ModifierCallback modifier_cb, - GNUNET_SOCIAL_DataCallback data_cb, - GNUNET_SOCIAL_EndOfMessageCallback eom_cb, - void *cls) +static void +handle_place_result (void *cls, + const struct GNUNET_OperationResultMessage *res) { - struct GNUNET_HashCode key; - GNUNET_CRYPTO_hash (method_name, strlen (method_name), &key); + struct GNUNET_SOCIAL_Place *plc = cls; - struct SlicerCallbacks *cbs = GNUNET_malloc (sizeof (*cbs)); - cbs->method_cb = method_cb; - cbs->modifier_cb = modifier_cb; - cbs->data_cb = data_cb; - cbs->eom_cb = eom_cb; - cbs->cls = cls; + uint16_t size = ntohs (res->header.size); + uint16_t data_size = size - sizeof (*res); + const char *data = (0 < data_size) ? (const char *) &res[1] : NULL; - GNUNET_CONTAINER_multihashmap_put (slicer->handlers, &key, cbs, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + GNUNET_OP_result (plc->op, GNUNET_ntohll (res->op_id), + GNUNET_ntohll (res->result_code), + data, data_size, NULL); } -int -slicer_remove_handler (void *cls, const struct GNUNET_HashCode *key, void *value) +static int +check_app_result (void *cls, + const struct GNUNET_OperationResultMessage *res) { - struct SlicerRemoveClosure *rm_cls = cls; - struct GNUNET_SOCIAL_Slicer *slicer = rm_cls->slicer; - struct SlicerCallbacks *rm_cbs = &rm_cls->rm_cbs; - struct SlicerCallbacks *cbs = value; - - if (cbs->method_cb == rm_cbs->method_cb - && cbs->modifier_cb == rm_cbs->modifier_cb - && cbs->data_cb == rm_cbs->data_cb - && cbs->eom_cb == rm_cbs->eom_cb) - { - GNUNET_CONTAINER_multihashmap_remove (slicer->handlers, key, cbs); - GNUNET_free (cbs); - return GNUNET_NO; + uint16_t size = ntohs (res->header.size); + if (size < sizeof (*res)) + { /* Error, message too small. */ + GNUNET_break (0); + return GNUNET_SYSERR; } - return GNUNET_YES; + return GNUNET_OK; } -/** - * Remove a registered method from the try-and-slice instance. - * - * Removes the first matching handler registered with @a method and the given callbacks. - * - * @param slicer The try-and-slice instance. - * @param method_name Name of the method to remove. - * @param method Method handler. - * - * @return #GNUNET_OK if a method handler was removed, - * #GNUNET_NO if no handler matched the given method name and callbacks. - */ -int -GNUNET_SOCIAL_slicer_remove (struct GNUNET_SOCIAL_Slicer *slicer, - const char *method_name, - GNUNET_SOCIAL_MethodCallback method_cb, - GNUNET_SOCIAL_ModifierCallback modifier_cb, - GNUNET_SOCIAL_DataCallback data_cb, - GNUNET_SOCIAL_EndOfMessageCallback eom_cb) +static void +handle_app_result (void *cls, + const struct GNUNET_OperationResultMessage *res) { - struct GNUNET_HashCode key; - GNUNET_CRYPTO_hash (method_name, strlen (method_name), &key); + struct GNUNET_SOCIAL_App *app = cls; - struct SlicerRemoveClosure rm_cls; - rm_cls.slicer = slicer; - struct SlicerCallbacks *rm_cbs = &rm_cls.rm_cbs; - rm_cbs->method_cb = method_cb; - rm_cbs->modifier_cb = modifier_cb; - rm_cbs->data_cb = data_cb; - rm_cbs->eom_cb = eom_cb; + uint16_t size = ntohs (res->header.size); + uint16_t data_size = size - sizeof (*res); + const char *data = (0 < data_size) ? (const char *) &res[1] : NULL; - return - (GNUNET_SYSERR - == GNUNET_CONTAINER_multihashmap_get_multiple (slicer->handlers, &key, - &slicer_remove_handler, - &rm_cls)) - ? GNUNET_NO - : GNUNET_OK; + GNUNET_OP_result (app->op, GNUNET_ntohll (res->op_id), + GNUNET_ntohll (res->result_code), + data, data_size, NULL); } -int -slicer_free_handler (void *cls, const struct GNUNET_HashCode *key, void *value) +static void +op_recv_history_result (void *cls, int64_t result, + const void *err_msg, uint16_t err_msg_size) { - struct SlicerCallbacks *cbs = value; - GNUNET_free (cbs); - return GNUNET_YES; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received history replay result: %" PRId64 ".\n", result); + + struct GNUNET_SOCIAL_HistoryRequest *hist = cls; + + if (NULL != hist->result_cb) + hist->result_cb (hist->cls, result, err_msg, err_msg_size); + + GNUNET_free (hist); } -/** - * Destroy a given try-and-slice instance. - * - * @param slicer - * Slicer to destroy - */ -void -GNUNET_SOCIAL_slicer_destroy (struct GNUNET_SOCIAL_Slicer *slicer) +static void +op_recv_state_result (void *cls, int64_t result, + const void *err_msg, uint16_t err_msg_size) +{ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received state request result: %" PRId64 ".\n", result); + + struct GNUNET_SOCIAL_LookHandle *look = cls; + + if (NULL != look->result_cb) + look->result_cb (look->cls, result, err_msg, err_msg_size); + + GNUNET_free (look); +} + + +static int +check_place_history_result (void *cls, + const struct GNUNET_OperationResultMessage *res) { - GNUNET_CONTAINER_multihashmap_iterate (slicer->handlers, &slicer_free_handler, - NULL); - GNUNET_CONTAINER_multihashmap_destroy (slicer->handlers); - GNUNET_free (slicer); + struct GNUNET_PSYC_MessageHeader * + pmsg = (struct GNUNET_PSYC_MessageHeader *) GNUNET_MQ_extract_nested_mh (res); + uint16_t size = ntohs (res->header.size); + + if (NULL == pmsg || size < sizeof (*res) + sizeof (*pmsg)) + { /* Error, message too small. */ + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; } static void -place_send_connect_msg (struct GNUNET_SOCIAL_Place *plc) +handle_place_history_result (void *cls, + const struct GNUNET_OperationResultMessage *res) +{ + struct GNUNET_SOCIAL_Place *plc = cls; + struct GNUNET_PSYC_MessageHeader * + pmsg = (struct GNUNET_PSYC_MessageHeader *) GNUNET_MQ_extract_nested_mh (res); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "%p Received historic fragment for message #%" PRIu64 ".\n", + plc, GNUNET_ntohll (pmsg->message_id)); + + GNUNET_ResultCallback result_cb = NULL; + struct GNUNET_SOCIAL_HistoryRequest *hist = NULL; + + if (GNUNET_YES != GNUNET_OP_get (plc->op, + GNUNET_ntohll (res->op_id), + &result_cb, (void *) &hist, NULL)) + { /* Operation not found. */ + LOG (GNUNET_ERROR_TYPE_WARNING, + "%p Replay operation not found for historic fragment of message #%" + PRIu64 ".\n", + plc, GNUNET_ntohll (pmsg->message_id)); + return; + } + + GNUNET_PSYC_slicer_message (hist->slicer, + (const struct GNUNET_PSYC_MessageHeader *) pmsg); +} + + +static int +check_place_state_result (void *cls, + const struct GNUNET_OperationResultMessage *res) { - uint16_t cmsg_size = ntohs (plc->connect_msg->size); - struct GNUNET_MessageHeader * cmsg = GNUNET_malloc (cmsg_size); - memcpy (cmsg, plc->connect_msg, cmsg_size); - GNUNET_CLIENT_MANAGER_transmit_now (plc->client, cmsg); + const struct GNUNET_MessageHeader *mod = GNUNET_MQ_extract_nested_mh (res); + if (NULL == mod) + { + GNUNET_break_op (0); + LOG (GNUNET_ERROR_TYPE_WARNING, + "Invalid modifier in state result\n"); + return GNUNET_SYSERR; + } + + uint16_t size = ntohs (res->header.size); + uint16_t mod_size = ntohs (mod->size); + if (size - sizeof (*res) != mod_size) + { + GNUNET_break_op (0); + LOG (GNUNET_ERROR_TYPE_WARNING, + "Invalid modifier size in state result: %u - %u != %u\n", + ntohs (res->header.size), sizeof (*res), mod_size); + return GNUNET_SYSERR; + } + return GNUNET_OK; } static void -place_recv_message_ack (void *cls, - struct GNUNET_CLIENT_MANAGER_Connection *client, - const struct GNUNET_MessageHeader *msg) +handle_place_state_result (void *cls, + const struct GNUNET_OperationResultMessage *res) +{ + struct GNUNET_SOCIAL_Place *plc = cls; + + GNUNET_ResultCallback result_cb = NULL; + struct GNUNET_SOCIAL_LookHandle *look = NULL; + + if (GNUNET_YES != GNUNET_OP_get (plc->op, + GNUNET_ntohll (res->op_id), + &result_cb, (void *) &look, NULL)) + { /* Operation not found. */ + return; + } + + const struct GNUNET_MessageHeader *mod = GNUNET_MQ_extract_nested_mh (res); + uint16_t mod_size = ntohs (mod->size); + + switch (ntohs (mod->type)) + { + case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER: + { + const struct GNUNET_PSYC_MessageModifier * + pmod = (const struct GNUNET_PSYC_MessageModifier *) mod; + + const char *name = (const char *) &pmod[1]; + uint16_t name_size = ntohs (pmod->name_size); + if (0 == name_size + || mod_size - sizeof (*pmod) < name_size + || '\0' != name[name_size - 1]) + { + GNUNET_break_op (0); + LOG (GNUNET_ERROR_TYPE_WARNING, + "Invalid modifier name in state result\n"); + return; + } + look->mod_value_size = ntohs (pmod->value_size); + look->var_cb (look->cls, mod, name, name + name_size, + mod_size - sizeof (*mod) - name_size, + look->mod_value_size); + if (look->mod_value_size > mod_size - sizeof (*mod) - name_size) + { + look->mod_value_remaining = look->mod_value_size; + look->mod_name = GNUNET_malloc (name_size); + GNUNET_memcpy (look->mod_name, name, name_size); + } + break; + } + + case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT: + look->var_cb (look->cls, mod, look->mod_name, (const char *) &mod[1], + mod_size - sizeof (*mod), look->mod_value_size); + look->mod_value_remaining -= mod_size - sizeof (*mod); + if (0 == look->mod_value_remaining) + { + GNUNET_free (look->mod_name); + } + break; + } +} + + +static void +handle_place_message_ack (void *cls, + const struct GNUNET_MessageHeader *msg) { - struct GNUNET_SOCIAL_Place * - plc = GNUNET_CLIENT_MANAGER_get_user_context_ (client, sizeof (*plc)); + struct GNUNET_SOCIAL_Place *plc = cls; + GNUNET_PSYC_transmit_got_ack (plc->tmit); } +static int +check_place_message (void *cls, + const struct GNUNET_PSYC_MessageHeader *pmsg) +{ + return GNUNET_OK; +} + + static void -place_recv_message (void *cls, - struct GNUNET_CLIENT_MANAGER_Connection *client, - const struct GNUNET_MessageHeader *msg) +handle_place_message (void *cls, + const struct GNUNET_PSYC_MessageHeader *pmsg) +{ + struct GNUNET_SOCIAL_Place *plc = cls; + + GNUNET_PSYC_slicer_message (plc->slicer, pmsg); +} + + +static int +check_host_message (void *cls, + const struct GNUNET_PSYC_MessageHeader *pmsg) { - struct GNUNET_SOCIAL_Place * - plc = GNUNET_CLIENT_MANAGER_get_user_context_ (client, sizeof (*plc)); - GNUNET_PSYC_receive_message (plc->recv, - (const struct GNUNET_PSYC_MessageHeader *) msg); + return GNUNET_OK; } static void -place_recv_disconnect (void *cls, - struct GNUNET_CLIENT_MANAGER_Connection *client, - const struct GNUNET_MessageHeader *msg) +handle_host_message (void *cls, + const struct GNUNET_PSYC_MessageHeader *pmsg) { - struct GNUNET_SOCIAL_Place * - plc = GNUNET_CLIENT_MANAGER_get_user_context_ (client, sizeof (*plc)); + struct GNUNET_SOCIAL_Host *hst = cls; - GNUNET_CLIENT_MANAGER_reconnect (client); - place_send_connect_msg (plc); + GNUNET_PSYC_slicer_message (hst->slicer, pmsg); + GNUNET_PSYC_slicer_message (hst->plc.slicer, pmsg); } static void -host_recv_enter_ack (void *cls, - struct GNUNET_CLIENT_MANAGER_Connection *client, - const struct GNUNET_MessageHeader *msg) +handle_host_enter_ack (void *cls, + const struct HostEnterAck *hack) { - struct GNUNET_SOCIAL_Host * - hst = GNUNET_CLIENT_MANAGER_get_user_context_ (client, - sizeof (struct GNUNET_SOCIAL_Place)); + struct GNUNET_SOCIAL_Host *hst = cls; + + hst->plc.pub_key = hack->place_pub_key; - struct GNUNET_PSYC_CountersResultMessage * - cres = (struct GNUNET_PSYC_CountersResultMessage *) msg; - int32_t result = ntohl (cres->result_code) + INT32_MIN; + int32_t result = ntohl (hack->result_code); if (NULL != hst->enter_cb) - hst->enter_cb (hst->cb_cls, result, GNUNET_ntohll (cres->max_message_id)); + hst->enter_cb (hst->cb_cls, result, &hack->place_pub_key, + GNUNET_ntohll (hack->max_message_id)); +} + + +static int +check_host_enter_request (void *cls, + const struct GNUNET_PSYC_JoinRequestMessage *req) +{ + return GNUNET_OK; } static void -host_recv_enter_request (void *cls, - struct GNUNET_CLIENT_MANAGER_Connection *client, - const struct GNUNET_MessageHeader *msg) +handle_host_enter_request (void *cls, + const struct GNUNET_PSYC_JoinRequestMessage *req) { - struct GNUNET_SOCIAL_Host * - hst = GNUNET_CLIENT_MANAGER_get_user_context_ (client, - sizeof (struct GNUNET_SOCIAL_Place)); + struct GNUNET_SOCIAL_Host *hst = cls; + if (NULL == hst->answer_door_cb) return; const char *method_name = NULL; - struct GNUNET_ENV_Environment *env = NULL; + struct GNUNET_PSYC_Environment *env = NULL; + struct GNUNET_PSYC_MessageHeader *entry_pmsg = NULL; const void *data = NULL; uint16_t data_size = 0; char *str; - const struct GNUNET_PSYC_JoinRequestMessage * - req = (const struct GNUNET_PSYC_JoinRequestMessage *) msg; - const struct GNUNET_PSYC_Message *entry_msg = NULL; + const struct GNUNET_PSYC_Message *join_msg = NULL; - if (sizeof (*req) + sizeof (*entry_msg) <= ntohs (req->header.size)) + do { - entry_msg = (struct GNUNET_PSYC_Message *) &req[1]; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received entry_msg of type %u and size %u.\n", - ntohs (entry_msg->header.type), ntohs (entry_msg->header.size)); - - env = GNUNET_ENV_environment_create (); - if (GNUNET_OK != GNUNET_PSYC_message_parse (entry_msg, &method_name, env, - &data, &data_size)) + if (sizeof (*req) + sizeof (*join_msg) <= ntohs (req->header.size)) { - GNUNET_break_op (0); - str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&req->slave_key); - LOG (GNUNET_ERROR_TYPE_WARNING, - "Ignoring invalid entry request from nym %s.\n", - str); - GNUNET_free (str); - GNUNET_ENV_environment_destroy (env); - return; + join_msg = (struct GNUNET_PSYC_Message *) GNUNET_MQ_extract_nested_mh (req); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received join_msg of type %u and size %u.\n", + ntohs (join_msg->header.type), ntohs (join_msg->header.size)); + + env = GNUNET_PSYC_env_create (); + entry_pmsg = GNUNET_PSYC_message_header_create_from_psyc (join_msg); + if (GNUNET_OK != GNUNET_PSYC_message_parse (entry_pmsg, &method_name, env, + &data, &data_size)) + { + GNUNET_break_op (0); + str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&req->slave_pub_key); + LOG (GNUNET_ERROR_TYPE_WARNING, + "Ignoring invalid entry request from nym %s.\n", + str); + GNUNET_free (str); + break; + } } - } - struct GNUNET_SOCIAL_Nym *nym = nym_get_or_create (&req->slave_key); - hst->answer_door_cb (hst->cb_cls, nym, method_name, env, - data_size, data); + struct GNUNET_SOCIAL_Nym *nym = nym_get_or_create (&req->slave_pub_key); + hst->answer_door_cb (hst->cb_cls, nym, method_name, env, + data, data_size); + } while (0); if (NULL != env) - GNUNET_ENV_environment_destroy (env); + GNUNET_PSYC_env_destroy (env); + if (NULL != entry_pmsg) + GNUNET_free (entry_pmsg); } static void -guest_recv_enter_ack (void *cls, - struct GNUNET_CLIENT_MANAGER_Connection *client, - const struct GNUNET_MessageHeader *msg) +handle_guest_enter_ack (void *cls, + const struct GNUNET_PSYC_CountersResultMessage *cres) { - struct GNUNET_SOCIAL_Guest * - gst = GNUNET_CLIENT_MANAGER_get_user_context_ (client, - sizeof (struct GNUNET_SOCIAL_Place)); + struct GNUNET_SOCIAL_Guest *gst = cls; - struct GNUNET_PSYC_CountersResultMessage * - cres = (struct GNUNET_PSYC_CountersResultMessage *) msg; - int32_t result = ntohl (cres->result_code) + INT32_MIN; + int32_t result = ntohl (cres->result_code); if (NULL != gst->enter_cb) - gst->enter_cb (gst->cb_cls, result, GNUNET_ntohll (cres->max_message_id)); + gst->enter_cb (gst->cb_cls, result, &gst->plc.pub_key, + GNUNET_ntohll (cres->max_message_id)); +} + + +static int +check_guest_enter_decision (void *cls, + const struct GNUNET_PSYC_JoinDecisionMessage *dcsn) +{ + return GNUNET_OK; } static void -guest_recv_join_decision (void *cls, - struct GNUNET_CLIENT_MANAGER_Connection *client, - const struct GNUNET_MessageHeader *msg) +handle_guest_enter_decision (void *cls, + const struct GNUNET_PSYC_JoinDecisionMessage *dcsn) { - struct GNUNET_SOCIAL_Guest * - gst = GNUNET_CLIENT_MANAGER_get_user_context_ (client, - sizeof (struct GNUNET_SOCIAL_Place)); - const struct GNUNET_PSYC_JoinDecisionMessage * - dcsn = (const struct GNUNET_PSYC_JoinDecisionMessage *) msg; + struct GNUNET_SOCIAL_Guest *gst = cls; struct GNUNET_PSYC_Message *pmsg = NULL; - if (ntohs (dcsn->header.size) <= sizeof (*dcsn) + sizeof (*pmsg)) - pmsg = (struct GNUNET_PSYC_Message *) &dcsn[1]; + if (ntohs (dcsn->header.size) > sizeof (*dcsn)) + pmsg = (struct GNUNET_PSYC_Message *) GNUNET_MQ_extract_nested_mh (dcsn); if (NULL != gst->entry_dcsn_cb) gst->entry_dcsn_cb (gst->cb_cls, ntohl (dcsn->is_admitted), pmsg); } -static struct GNUNET_CLIENT_MANAGER_MessageHandler host_handlers[] = +static int +check_app_ego (void *cls, + const struct AppEgoMessage *emsg) { - { &host_recv_enter_ack, NULL, - GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER_ACK, - sizeof (struct GNUNET_PSYC_CountersResultMessage), GNUNET_NO }, + return GNUNET_OK; +} - { &host_recv_enter_request, NULL, - GNUNET_MESSAGE_TYPE_PSYC_JOIN_REQUEST, - sizeof (struct GNUNET_PSYC_JoinRequestMessage), GNUNET_YES }, - { &place_recv_message, NULL, - GNUNET_MESSAGE_TYPE_PSYC_MESSAGE, - sizeof (struct GNUNET_PSYC_MessageHeader), GNUNET_YES }, +static void +handle_app_ego (void *cls, + const struct AppEgoMessage *emsg) +{ + struct GNUNET_SOCIAL_App *app = cls; - { &place_recv_message_ack, NULL, - GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK, - sizeof (struct GNUNET_MessageHeader), GNUNET_NO }, + uint16_t name_size = ntohs (emsg->header.size) - sizeof (*emsg); - { &place_recv_disconnect, NULL, 0, 0, GNUNET_NO }, + struct GNUNET_HashCode ego_pub_hash; + GNUNET_CRYPTO_hash (&emsg->ego_pub_key, sizeof (emsg->ego_pub_key), + &ego_pub_hash); - { NULL, NULL, 0, 0, GNUNET_NO } -}; + struct GNUNET_SOCIAL_Ego * + ego = GNUNET_CONTAINER_multihashmap_get (app->egos, &ego_pub_hash); + if (NULL == ego) + { + ego = GNUNET_malloc (sizeof (*ego)); + ego->pub_key = emsg->ego_pub_key; + ego->name = GNUNET_malloc (name_size); + GNUNET_memcpy (ego->name, &emsg[1], name_size); + } + else + { + ego->name = GNUNET_realloc (ego->name, name_size); + GNUNET_memcpy (ego->name, &emsg[1], name_size); + } + + GNUNET_CONTAINER_multihashmap_put (app->egos, &ego_pub_hash, ego, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE); + + if (NULL != app->ego_cb) + app->ego_cb (app->cb_cls, ego, &ego->pub_key, ego->name); +} + + +static void +handle_app_ego_end (void *cls, + const struct GNUNET_MessageHeader *msg) +{ + //struct GNUNET_SOCIAL_App *app = cls; +} + + +static int +check_app_place (void *cls, + const struct AppPlaceMessage *pmsg) +{ + return GNUNET_OK; +} -static struct GNUNET_CLIENT_MANAGER_MessageHandler guest_handlers[] = +static void +handle_app_place (void *cls, + const struct AppPlaceMessage *pmsg) { - { &guest_recv_enter_ack, NULL, - GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_ACK, - sizeof (struct GNUNET_PSYC_CountersResultMessage), GNUNET_NO }, + struct GNUNET_SOCIAL_App *app = cls; - { &host_recv_enter_request, NULL, - GNUNET_MESSAGE_TYPE_PSYC_JOIN_REQUEST, - sizeof (struct GNUNET_PSYC_JoinRequestMessage), GNUNET_YES }, + if ((GNUNET_YES == pmsg->is_host && NULL == app->host_cb) + || (GNUNET_NO == pmsg->is_host && NULL == app->guest_cb)) + return; - { &place_recv_message, NULL, - GNUNET_MESSAGE_TYPE_PSYC_MESSAGE, - sizeof (struct GNUNET_PSYC_MessageHeader), GNUNET_YES }, + struct GNUNET_HashCode ego_pub_hash; + GNUNET_CRYPTO_hash (&pmsg->ego_pub_key, sizeof (pmsg->ego_pub_key), + &ego_pub_hash); + struct GNUNET_SOCIAL_Ego * + ego = GNUNET_CONTAINER_multihashmap_get (app->egos, &ego_pub_hash); + if (NULL == ego) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failure to obtain ego %s.\n", + GNUNET_h2s (&ego_pub_hash)); + GNUNET_break (0); + return; + } - { &place_recv_message_ack, NULL, - GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK, - sizeof (struct GNUNET_MessageHeader), GNUNET_NO }, + if (GNUNET_YES == pmsg->is_host) + { + if (NULL != app->host_cb) { + struct GNUNET_SOCIAL_HostConnection *hconn = GNUNET_malloc (sizeof (*hconn)); + hconn->app = app; + hconn->plc_msg = *pmsg; + app->host_cb (app->cb_cls, hconn, ego, &pmsg->place_pub_key, pmsg->place_state); + GNUNET_free (hconn); + } + } + else if (NULL != app->guest_cb) + { + struct GNUNET_SOCIAL_GuestConnection *gconn = GNUNET_malloc (sizeof (*gconn)); + gconn->app = app; + gconn->plc_msg = *pmsg; + app->guest_cb (app->cb_cls, gconn, ego, &pmsg->place_pub_key, pmsg->place_state); + GNUNET_free (gconn); + } +} - { &guest_recv_join_decision, NULL, - GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION, - sizeof (struct GNUNET_PSYC_JoinDecisionMessage), GNUNET_YES }, - { &place_recv_disconnect, NULL, 0, 0, GNUNET_NO }, +static void +handle_app_place_end (void *cls, + const struct GNUNET_MessageHeader *msg) +{ + struct GNUNET_SOCIAL_App *app = cls; - { NULL, NULL, 0, 0, GNUNET_NO } -}; + if (NULL != app->connected_cb) + app->connected_cb (app->cb_cls); +} +/** + * Handler for a #GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE_ACK message received + * from the social service. + * + * @param cls the place of type `struct GNUNET_SOCIAL_Place` + * @param msg the message received from the service + */ static void -place_cleanup (struct GNUNET_SOCIAL_Place *plc) +handle_place_leave_ack (void *cls, + const struct GNUNET_MessageHeader *msg) { - GNUNET_PSYC_transmit_destroy (plc->tmit); - GNUNET_PSYC_receive_destroy (plc->recv); - GNUNET_free (plc->connect_msg); - if (NULL != plc->disconnect_cb) - plc->disconnect_cb (plc->disconnect_cls); + struct GNUNET_SOCIAL_Place *plc = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "%s left place %p\n", + plc->is_host ? "host" : "guest", + plc); + place_disconnect (plc); +} + + +/*** HOST ***/ + + +static void +host_connect (struct GNUNET_SOCIAL_Host *hst); + + +static void +host_reconnect (void *cls) +{ + host_connect (cls); } +/** + * Host client disconnected from service. + * + * Reconnect after backoff period. + */ static void -host_cleanup (void *cls) +host_disconnected (void *cls, enum GNUNET_MQ_Error error) { struct GNUNET_SOCIAL_Host *hst = cls; - place_cleanup (&hst->plc); - GNUNET_free (hst); + struct GNUNET_SOCIAL_Place *plc = &hst->plc; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Host client disconnected (%d), re-connecting\n", + (int) error); + if (NULL != plc->tmit) + { + GNUNET_PSYC_transmit_destroy (plc->tmit); + plc->tmit = NULL; + } + if (NULL != plc->mq) + { + GNUNET_MQ_destroy (plc->mq); + plc->mq = NULL; + } + + plc->reconnect_task = GNUNET_SCHEDULER_add_delayed (plc->reconnect_delay, + host_reconnect, + hst); + plc->reconnect_delay = GNUNET_TIME_STD_BACKOFF (plc->reconnect_delay); } static void -guest_cleanup (void *cls) +host_connect (struct GNUNET_SOCIAL_Host *hst) { - struct GNUNET_SOCIAL_Guest *gst = cls; - place_cleanup (&gst->plc); - GNUNET_free (gst); + struct GNUNET_SOCIAL_Place *plc = &hst->plc; + + struct GNUNET_MQ_MessageHandler handlers[] = { + GNUNET_MQ_hd_fixed_size (host_enter_ack, + GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER_ACK, + struct HostEnterAck, + hst), + GNUNET_MQ_hd_fixed_size (place_leave_ack, + GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE_ACK, + struct GNUNET_MessageHeader, + plc), + GNUNET_MQ_hd_var_size (host_enter_request, + GNUNET_MESSAGE_TYPE_PSYC_JOIN_REQUEST, + struct GNUNET_PSYC_JoinRequestMessage, + hst), + GNUNET_MQ_hd_var_size (host_message, + GNUNET_MESSAGE_TYPE_PSYC_MESSAGE, + struct GNUNET_PSYC_MessageHeader, + hst), + GNUNET_MQ_hd_fixed_size (place_message_ack, + GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK, + struct GNUNET_MessageHeader, + plc), + GNUNET_MQ_hd_var_size (place_history_result, + GNUNET_MESSAGE_TYPE_PSYC_HISTORY_RESULT, + struct GNUNET_OperationResultMessage, + plc), + GNUNET_MQ_hd_var_size (place_state_result, + GNUNET_MESSAGE_TYPE_PSYC_STATE_RESULT, + struct GNUNET_OperationResultMessage, + plc), + GNUNET_MQ_hd_var_size (place_result, + GNUNET_MESSAGE_TYPE_PSYC_RESULT_CODE, + struct GNUNET_OperationResultMessage, + plc), + GNUNET_MQ_handler_end () + }; + + plc->mq = GNUNET_CLIENT_connect (plc->cfg, "social", + handlers, host_disconnected, hst); + GNUNET_assert (NULL != plc->mq); + plc->tmit = GNUNET_PSYC_transmit_create (plc->mq); + + GNUNET_MQ_send_copy (plc->mq, plc->connect_env); } @@ -821,17 +1201,16 @@ guest_cleanup (void *cls) * A place is created upon first entering, and it is active until permanently * left using GNUNET_SOCIAL_host_leave(). * - * @param cfg - * Configuration to contact the social service. + * @param app + * Application handle. * @param ego * Identity of the host. - * @param place_key - * Private-public key pair of the place. - * NULL for ephemeral places. * @param policy * Policy specifying entry and history restrictions for the place. * @param slicer * Slicer to handle incoming messages. + * @param enter_cb + * Function called when the place is entered and ready to use. * @param answer_door_cb * Function to handle new nyms that want to enter. * @param farewell_cb @@ -839,14 +1218,13 @@ guest_cleanup (void *cls) * @param cls * Closure for the callbacks. * - * @return Handle for the host. + * @return Handle for the host. This handle contains the pubkey. */ struct GNUNET_SOCIAL_Host * -GNUNET_SOCIAL_host_enter (const struct GNUNET_CONFIGURATION_Handle *cfg, - const struct GNUNET_IDENTITY_Ego *ego, - const struct GNUNET_CRYPTO_EddsaPrivateKey *place_key, +GNUNET_SOCIAL_host_enter (const struct GNUNET_SOCIAL_App *app, + const struct GNUNET_SOCIAL_Ego *ego, enum GNUNET_PSYC_Policy policy, - struct GNUNET_SOCIAL_Slicer *slicer, + struct GNUNET_PSYC_Slicer *slicer, GNUNET_SOCIAL_HostEnterCallback enter_cb, GNUNET_SOCIAL_AnswerDoorCallback answer_door_cb, GNUNET_SOCIAL_FarewellCallback farewell_cb, @@ -854,67 +1232,47 @@ GNUNET_SOCIAL_host_enter (const struct GNUNET_CONFIGURATION_Handle *cfg, { struct GNUNET_SOCIAL_Host *hst = GNUNET_malloc (sizeof (*hst)); struct GNUNET_SOCIAL_Place *plc = &hst->plc; - struct HostEnterRequest *req = GNUNET_malloc (sizeof (*req)); - - if (NULL != place_key) - { - hst->place_key = *place_key; - } - else - { - struct GNUNET_CRYPTO_EddsaPrivateKey * - ephemeral_key = GNUNET_CRYPTO_eddsa_key_create (); - hst->place_key = *ephemeral_key; - GNUNET_CRYPTO_eddsa_key_get_public (&hst->place_key, &plc->pub_key); - GNUNET_CRYPTO_eddsa_key_clear (ephemeral_key); - GNUNET_free (ephemeral_key); - } - plc->ego_key = *GNUNET_IDENTITY_ego_get_private_key (ego); - req->header.size = htons (sizeof (*req)); - req->header.type = htons (GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER); - req->policy = policy; - req->place_key = hst->place_key; - req->host_key = plc->ego_key; - - plc->connect_msg = (struct GNUNET_MessageHeader *) req; - plc->cfg = cfg; + plc->cfg = app->cfg; plc->is_host = GNUNET_YES; plc->slicer = slicer; - hst->plc.ego_key = *GNUNET_IDENTITY_ego_get_private_key (ego); hst->enter_cb = enter_cb; hst->answer_door_cb = answer_door_cb; + hst->farewell_cb = farewell_cb; hst->cb_cls = cls; - plc->client = GNUNET_CLIENT_MANAGER_connect (cfg, "social", host_handlers); - GNUNET_CLIENT_MANAGER_set_user_context_ (plc->client, hst, sizeof (*plc)); + plc->op = GNUNET_OP_create (); + + hst->slicer = GNUNET_PSYC_slicer_create (); + GNUNET_PSYC_slicer_method_add (hst->slicer, "_notice_place_leave", NULL, + host_recv_notice_place_leave_method, + host_recv_notice_place_leave_modifier, + NULL, host_recv_notice_place_leave_eom, hst); - plc->tmit = GNUNET_PSYC_transmit_create (plc->client); - plc->recv = GNUNET_PSYC_receive_create (NULL, &slicer_message, plc->slicer); + uint16_t app_id_size = strlen (app->id) + 1; + struct HostEnterRequest *hreq; + plc->connect_env = GNUNET_MQ_msg_extra (hreq, app_id_size, + GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER); + hreq->policy = policy; + hreq->ego_pub_key = ego->pub_key; + GNUNET_memcpy (&hreq[1], app->id, app_id_size); - place_send_connect_msg (plc); + host_connect (hst); return hst; } /** - * Enter a place as host. - * - * A place is created upon first entering, and it is active until permanently - * left using GNUNET_SOCIAL_host_leave(). + * Reconnect to an already entered place as host. * - * @param cfg - * Configuration to contact the social service. - * @param ego - * Identity of the host. - * @param gns_name - * GNS name in the zone of the @a ego that contains the - * public key of the place in a PLACE record. - * @param policy - * Policy specifying entry and history restrictions for the place. + * @param hconn + * Host connection handle. + * @see GNUNET_SOCIAL_app_connect() & GNUNET_SOCIAL_AppHostPlaceCallback() * @param slicer * Slicer to handle incoming messages. + * @param enter_cb + * Function called when the place is entered and ready to use. * @param answer_door_cb * Function to handle new nyms that want to enter. * @param farewell_cb @@ -924,27 +1282,46 @@ GNUNET_SOCIAL_host_enter (const struct GNUNET_CONFIGURATION_Handle *cfg, * * @return Handle for the host. */ -struct GNUNET_SOCIAL_Host * -GNUNET_SOCIAL_host_enter_by_name (const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_IDENTITY_Ego *ego, - const char *gns_name, - enum GNUNET_PSYC_Policy policy, - struct GNUNET_SOCIAL_Slicer *slicer, - GNUNET_SOCIAL_HostEnterCallback enter_cb, - GNUNET_SOCIAL_AnswerDoorCallback answer_door_cb, - GNUNET_SOCIAL_FarewellCallback farewell_cb, - void *cls) -{ - struct GNUNET_CRYPTO_EddsaPrivateKey place_key = {}; - - /* FIXME: - * 1. get public key by looking up PLACE entry under gns_name - * in the zone of the ego. - * 2. get private key from $GNUNET_DATA_HOME/social/places/PUB_KEY_HASH - */ + struct GNUNET_SOCIAL_Host * +GNUNET_SOCIAL_host_enter_reconnect (struct GNUNET_SOCIAL_HostConnection *hconn, + struct GNUNET_PSYC_Slicer *slicer, + GNUNET_SOCIAL_HostEnterCallback enter_cb, + GNUNET_SOCIAL_AnswerDoorCallback answer_door_cb, + GNUNET_SOCIAL_FarewellCallback farewell_cb, + void *cls) +{ + struct GNUNET_SOCIAL_Host *hst = GNUNET_malloc (sizeof (*hst)); + struct GNUNET_SOCIAL_Place *plc = &hst->plc; + + hst->enter_cb = enter_cb; + hst->answer_door_cb = answer_door_cb; + hst->farewell_cb = farewell_cb; + hst->cb_cls = cls; - return GNUNET_SOCIAL_host_enter (cfg, ego, &place_key, policy, slicer, - enter_cb, answer_door_cb, farewell_cb, cls); + plc->cfg = hconn->app->cfg; + plc->is_host = GNUNET_YES; + plc->slicer = slicer; + plc->pub_key = hconn->plc_msg.place_pub_key; + plc->ego_pub_key = hconn->plc_msg.ego_pub_key; + + plc->op = GNUNET_OP_create (); + + hst->slicer = GNUNET_PSYC_slicer_create (); + GNUNET_PSYC_slicer_method_add (hst->slicer, "_notice_place_leave", NULL, + host_recv_notice_place_leave_method, + host_recv_notice_place_leave_modifier, + NULL, host_recv_notice_place_leave_eom, hst); + + size_t app_id_size = strlen (hconn->app->id) + 1; + struct HostEnterRequest *hreq; + plc->connect_env = GNUNET_MQ_msg_extra (hreq, app_id_size, + GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER); + hreq->place_pub_key = hconn->plc_msg.place_pub_key; + hreq->ego_pub_key = hconn->plc_msg.ego_pub_key; + GNUNET_memcpy (&hreq[1], hconn->app->id, app_id_size); + + host_connect (hst); + return hst; } @@ -976,6 +1353,7 @@ GNUNET_SOCIAL_host_entry_decision (struct GNUNET_SOCIAL_Host *hst, int is_admitted, const struct GNUNET_PSYC_Message *entry_resp) { + struct GNUNET_SOCIAL_Place *plc = &hst->plc; struct GNUNET_PSYC_JoinDecisionMessage *dcsn; uint16_t entry_resp_size = (NULL != entry_resp) ? ntohs (entry_resp->header.size) : 0; @@ -983,16 +1361,16 @@ GNUNET_SOCIAL_host_entry_decision (struct GNUNET_SOCIAL_Host *hst, if (GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD < sizeof (*dcsn) + entry_resp_size) return GNUNET_SYSERR; - dcsn = GNUNET_malloc (sizeof (*dcsn) + entry_resp_size); - dcsn->header.size = htons (sizeof (*dcsn) + entry_resp_size); - dcsn->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION); + struct GNUNET_MQ_Envelope * + env = GNUNET_MQ_msg_extra (dcsn, entry_resp_size, + GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION); dcsn->is_admitted = htonl (is_admitted); - dcsn->slave_key = nym->pub_key; + dcsn->slave_pub_key = nym->pub_key; if (0 < entry_resp_size) - memcpy (&dcsn[1], entry_resp, entry_resp_size); + GNUNET_memcpy (&dcsn[1], entry_resp, entry_resp_size); - GNUNET_CLIENT_MANAGER_transmit (hst->plc.client, &dcsn->header); + GNUNET_MQ_send (plc->mq, env); return GNUNET_OK; } @@ -1004,111 +1382,104 @@ GNUNET_SOCIAL_host_entry_decision (struct GNUNET_SOCIAL_Host *hst, * #GNUNET_SOCIAL_FarewellCallback is invoked, * which should be very soon after this call. * - * @param host Host of the place. - * @param nym Handle for the entity to be ejected. + * @param host + * Host of the place. + * @param nym + * Handle for the entity to be ejected. + * @param env + * Environment for the message or NULL. */ void -GNUNET_SOCIAL_host_eject (struct GNUNET_SOCIAL_Host *host, - struct GNUNET_SOCIAL_Nym *nym) -{ - +GNUNET_SOCIAL_host_eject (struct GNUNET_SOCIAL_Host *hst, + const struct GNUNET_SOCIAL_Nym *nym, + struct GNUNET_PSYC_Environment *e) +{ + struct GNUNET_PSYC_Environment *env = e; + if (NULL == env) + env = GNUNET_PSYC_env_create (); + GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET, + "_nym", &nym->pub_key, sizeof (nym->pub_key)); + GNUNET_SOCIAL_host_announce (hst, "_notice_place_leave", env, NULL, NULL, + GNUNET_SOCIAL_ANNOUNCE_NONE); + if (NULL == e) + GNUNET_PSYC_env_destroy (env); } /** - * Get the public key of a @a nym. + * Get the public key of @a ego. * - * Suitable, for example, to be used with GNUNET_NAMESTORE_zone_to_name(). + * @param ego + * Ego. * - * @param nym Pseudonym to map to a cryptographic identifier. - * @param[out] nym_key Set to the public key of the nym. + * @return Public key of ego. */ -struct GNUNET_CRYPTO_EcdsaPublicKey * -GNUNET_SOCIAL_nym_get_key (struct GNUNET_SOCIAL_Nym *nym) +const struct GNUNET_CRYPTO_EcdsaPublicKey * +GNUNET_SOCIAL_ego_get_pub_key (const struct GNUNET_SOCIAL_Ego *ego) { - return &nym->pub_key; + return &ego->pub_key; } /** - * Obtain the private-public key pair of the hosted place. + * Get the hash of the public key of @a ego. * - * The public part is suitable for storing in GNS within a PLACE record, - * along with peer IDs to join at. - * - * @param host - * Host of the place. + * @param ego + * Ego. * - * @return Private-public key pair of the hosted place. + * @return Hash of the public key of @a ego. */ -const struct GNUNET_CRYPTO_EddsaPrivateKey * -GNUNET_SOCIAL_host_get_place_key (struct GNUNET_SOCIAL_Host *hst) +const struct GNUNET_HashCode * +GNUNET_SOCIAL_ego_get_pub_key_hash (const struct GNUNET_SOCIAL_Ego *ego) { - return &hst->place_key; + return &ego->pub_key_hash; } -static void -namestore_result_host_advertise (void *cls, int32_t success, const char *emsg) +/** + * Get the name of @a ego. + * + * @param ego + * Ego. + * + * @return Public key of @a ego. + */ +const char * +GNUNET_SOCIAL_ego_get_name (const struct GNUNET_SOCIAL_Ego *ego) { - + return ego->name; } /** - * Connected to core service. + * Get the public key of @a nym. + * + * Suitable, for example, to be used with GNUNET_SOCIAL_zone_add_nym(). + * + * @param nym + * Pseudonym. + * + * @return Public key of @a nym. */ -static void -core_connected_cb (void *cls, const struct GNUNET_PeerIdentity *my_identity) +const struct GNUNET_CRYPTO_EcdsaPublicKey * +GNUNET_SOCIAL_nym_get_pub_key (const struct GNUNET_SOCIAL_Nym *nym) { - this_peer = *my_identity; - // FIXME + return &nym->pub_key; } /** - * Advertise the place in the GNS zone of the @e ego of the @a host. - * - * @param hst Host of the place. - * @param name The name for the PLACE record to put in the zone. - * @param peer_count Number of elements in the @a peers array. - * @param peers List of peers in the PLACE record that can be used to send join - * requests to. - * @param expiration_time Expiration time of the record, use 0 to remove the record. - * @param password Password used to encrypt the record. + * Get the hash of the public key of @a nym. + * + * @param nym + * Pseudonym. + * + * @return Hash of the public key of @a nym. */ -void -GNUNET_SOCIAL_host_advertise (struct GNUNET_SOCIAL_Host *hst, - const char *name, - size_t peer_count, - const struct GNUNET_PeerIdentity *peers, - struct GNUNET_TIME_Relative expiration_time, - const char *password) +const struct GNUNET_HashCode * +GNUNET_SOCIAL_nym_get_pub_key_hash (const struct GNUNET_SOCIAL_Nym *nym) { - struct GNUNET_SOCIAL_Place *plc = &hst->plc; - if (NULL == namestore) - namestore = GNUNET_NAMESTORE_connect (plc->cfg); - if (NULL == core) - core = GNUNET_CORE_connect (plc->cfg, NULL, core_connected_cb, NULL, NULL, - NULL, GNUNET_NO, NULL, GNUNET_NO, NULL); - - struct GNUNET_GNSRECORD_Data rd = { 0 }; - rd.record_type = GNUNET_GNSRECORD_TYPE_PLACE; - rd.flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; - rd.expiration_time - = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_WEEKS, 1).rel_value_us; - - struct GNUNET_GNSRECORD_PlaceData *rec = GNUNET_malloc (sizeof (*rec)); - rec->place_key = plc->pub_key; - rec->origin = this_peer; - rec->relay_count = htons (0); // FIXME - - rd.data_size = sizeof (*rec); - rd.data = rec; - - GNUNET_NAMESTORE_records_store (namestore, &hst->plc.ego_key, - name, 1, &rd, namestore_result_host_advertise, - hst); + return &nym->pub_key_hash; } @@ -1131,15 +1502,20 @@ GNUNET_SOCIAL_host_advertise (struct GNUNET_SOCIAL_Host *hst, struct GNUNET_SOCIAL_Announcement * GNUNET_SOCIAL_host_announce (struct GNUNET_SOCIAL_Host *hst, const char *method_name, - const struct GNUNET_ENV_Environment *env, + const struct GNUNET_PSYC_Environment *env, GNUNET_PSYC_TransmitNotifyData notify_data, void *notify_data_cls, enum GNUNET_SOCIAL_AnnounceFlags flags) { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "PSYC_transmit_message for host, method: %s\n", + method_name); if (GNUNET_OK == GNUNET_PSYC_transmit_message (hst->plc.tmit, method_name, env, - NULL, notify_data, notify_data_cls, flags)); - return (struct GNUNET_SOCIAL_Announcement *) hst->plc.tmit; + NULL, notify_data, notify_data_cls, flags)) + return (struct GNUNET_SOCIAL_Announcement *) hst->plc.tmit; + else + return NULL; } @@ -1186,90 +1562,234 @@ GNUNET_SOCIAL_host_get_place (struct GNUNET_SOCIAL_Host *hst) /** - * Stop hosting a place. + * Disconnect from a home. * * Invalidates host handle. * - * @param host Host leaving the place. - * @param keep_active Keep the place active after last host disconnected. + * @param hst + * The host to disconnect. */ void -GNUNET_SOCIAL_host_leave (struct GNUNET_SOCIAL_Host *hst, - int keep_active, - GNUNET_ContinuationCallback leave_cb, - void *leave_cls) +GNUNET_SOCIAL_host_disconnect (struct GNUNET_SOCIAL_Host *hst, + GNUNET_ContinuationCallback disconnect_cb, + void *cls) { struct GNUNET_SOCIAL_Place *plc = &hst->plc; - /* FIXME: send msg to service */ + plc->disconnect_cb = disconnect_cb; + plc->disconnect_cls = cls; + place_disconnect (plc); +} + + +/** + * Stop hosting the home. + * + * Sends a _notice_place_closing announcement to the home. + * Invalidates host handle. + * + * @param hst + * The host leaving. + * @param env + * Environment for the message or NULL. + * _nym is set to @e nym regardless whether an @e env is provided. + * @param disconnect_cb + * Function called after the host left the place + * and disconnected from the social service. + * @param cls + * Closure for @a disconnect_cb. + */ +void +GNUNET_SOCIAL_host_leave (struct GNUNET_SOCIAL_Host *hst, + const struct GNUNET_PSYC_Environment *env, + GNUNET_ContinuationCallback disconnect_cb, + void *cls) +{ + struct GNUNET_MQ_Envelope *envelope; + + GNUNET_SOCIAL_host_announce (hst, "_notice_place_closing", env, NULL, NULL, + GNUNET_SOCIAL_ANNOUNCE_NONE); + hst->plc.disconnect_cb = disconnect_cb; + hst->plc.disconnect_cls = cls; + envelope = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE); + GNUNET_MQ_send (hst->plc.mq, + envelope); +} + + +/*** GUEST ***/ + + +static void +guest_connect (struct GNUNET_SOCIAL_Guest *gst); + + +static void +guest_reconnect (void *cls) +{ + guest_connect (cls); +} + + +/** + * Guest client disconnected from service. + * + * Reconnect after backoff period. + */ +static void +guest_disconnected (void *cls, enum GNUNET_MQ_Error error) +{ + struct GNUNET_SOCIAL_Guest *gst = cls; + struct GNUNET_SOCIAL_Place *plc = &gst->plc; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Guest client disconnected (%d), re-connecting\n", + (int) error); + if (NULL != plc->tmit) + { + GNUNET_PSYC_transmit_destroy (plc->tmit); + plc->tmit = NULL; + } + if (NULL != plc->mq) + { + GNUNET_MQ_destroy (plc->mq); + plc->mq = NULL; + } + + plc->reconnect_task = GNUNET_SCHEDULER_add_delayed (plc->reconnect_delay, + guest_reconnect, + gst); + plc->reconnect_delay = GNUNET_TIME_STD_BACKOFF (plc->reconnect_delay); +} + - plc->is_disconnecting = GNUNET_YES; - plc->disconnect_cb = leave_cb; - plc->disconnect_cls = leave_cls; +static void +guest_connect (struct GNUNET_SOCIAL_Guest *gst) +{ + struct GNUNET_SOCIAL_Place *plc = &gst->plc; - GNUNET_CLIENT_MANAGER_disconnect (plc->client, GNUNET_YES, - &host_cleanup, hst); + struct GNUNET_MQ_MessageHandler handlers[] = { + GNUNET_MQ_hd_fixed_size (guest_enter_ack, + GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_ACK, + struct GNUNET_PSYC_CountersResultMessage, + gst), + GNUNET_MQ_hd_fixed_size (place_leave_ack, + GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE_ACK, + struct GNUNET_MessageHeader, + plc), + GNUNET_MQ_hd_var_size (guest_enter_decision, + GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION, + struct GNUNET_PSYC_JoinDecisionMessage, + gst), + GNUNET_MQ_hd_var_size (place_message, + GNUNET_MESSAGE_TYPE_PSYC_MESSAGE, + struct GNUNET_PSYC_MessageHeader, + plc), + GNUNET_MQ_hd_fixed_size (place_message_ack, + GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK, + struct GNUNET_MessageHeader, + plc), + GNUNET_MQ_hd_var_size (place_history_result, + GNUNET_MESSAGE_TYPE_PSYC_HISTORY_RESULT, + struct GNUNET_OperationResultMessage, + plc), + GNUNET_MQ_hd_var_size (place_state_result, + GNUNET_MESSAGE_TYPE_PSYC_STATE_RESULT, + struct GNUNET_OperationResultMessage, + plc), + GNUNET_MQ_hd_var_size (place_result, + GNUNET_MESSAGE_TYPE_PSYC_RESULT_CODE, + struct GNUNET_OperationResultMessage, + plc), + GNUNET_MQ_handler_end () + }; + + plc->mq = GNUNET_CLIENT_connect (plc->cfg, "social", + handlers, guest_disconnected, gst); + GNUNET_assert (NULL != plc->mq); + plc->tmit = GNUNET_PSYC_transmit_create (plc->mq); + + GNUNET_MQ_send_copy (plc->mq, plc->connect_env); } -static struct GuestEnterRequest * -guest_enter_request_create (const struct GNUNET_CRYPTO_EcdsaPrivateKey *guest_key, - const struct GNUNET_CRYPTO_EddsaPublicKey *place_key, +static struct GNUNET_MQ_Envelope * +guest_enter_request_create (const char *app_id, + const struct GNUNET_CRYPTO_EcdsaPublicKey *ego_pub_key, + const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key, const struct GNUNET_PeerIdentity *origin, size_t relay_count, const struct GNUNET_PeerIdentity *relays, const struct GNUNET_PSYC_Message *join_msg) { + uint16_t app_id_size = strlen (app_id) + 1; uint16_t join_msg_size = ntohs (join_msg->header.size); uint16_t relay_size = relay_count * sizeof (*relays); - struct GuestEnterRequest * - req = GNUNET_malloc (sizeof (*req) + relay_size + join_msg_size); + struct GuestEnterRequest *greq; + struct GNUNET_MQ_Envelope * + env = GNUNET_MQ_msg_extra (greq, app_id_size + relay_size + join_msg_size, + GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER); + greq->place_pub_key = *place_pub_key; + greq->ego_pub_key = *ego_pub_key; + greq->origin = *origin; + greq->relay_count = htonl (relay_count); - req->header.size = htons (sizeof (*req) + relay_size + join_msg_size); - req->header.type = htons (GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER); - req->place_key = *place_key; - req->guest_key = *guest_key; - req->origin = *origin; - req->relay_count = relay_count; + char *p = (char *) &greq[1]; + GNUNET_memcpy (p, app_id, app_id_size); + p += app_id_size; - uint16_t p = sizeof (*req); if (0 < relay_size) { - memcpy ((char *) req + p, relays, relay_size); + GNUNET_memcpy (p, relays, relay_size); p += relay_size; } - memcpy ((char *) req + p, join_msg, join_msg_size); - return req; + GNUNET_memcpy (p, join_msg, join_msg_size); + return env; } + /** * Request entry to a place as a guest. * - * @param cfg Configuration to contact the social service. - * @param ego Identity of the guest. - * @param crypto_address Public key of the place to enter. - * @param origin Peer identity of the origin of the underlying multicast group. - * @param relay_count Number of elements in the @a relays array. - * @param relays Relays for the underlying multicast group. - * @param method_name Method name for the message. - * @param env Environment containing variables for the message, or NULL. - * @param data Payload for the message to give to the enter callback. - * @param data_size Number of bytes in @a data. - * @param slicer Slicer to use for processing incoming requests from guests. + * @param app + * Application handle. + * @param ego + * Identity of the guest. + * @param place_pub_key + * Public key of the place to enter. + * @param flags + * Flags for the entry. + * @param origin + * Peer identity of the origin of the underlying multicast group. + * @param relay_count + * Number of elements in the @a relays array. + * @param relays + * Relays for the underlying multicast group. + * @param method_name + * Method name for the message. + * @param env + * Environment containing variables for the message, or NULL. + * @param data + * Payload for the message to give to the enter callback. + * @param data_size + * Number of bytes in @a data. + * @param slicer + * Slicer to use for processing incoming requests from guests. * * @return NULL on errors, otherwise handle for the guest. */ struct GNUNET_SOCIAL_Guest * -GNUNET_SOCIAL_guest_enter (const struct GNUNET_CONFIGURATION_Handle *cfg, - const struct GNUNET_IDENTITY_Ego *ego, - const struct GNUNET_CRYPTO_EddsaPublicKey *place_key, +GNUNET_SOCIAL_guest_enter (const struct GNUNET_SOCIAL_App *app, + const struct GNUNET_SOCIAL_Ego *ego, + const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key, + enum GNUNET_PSYC_SlaveJoinFlags flags, const struct GNUNET_PeerIdentity *origin, uint32_t relay_count, const struct GNUNET_PeerIdentity *relays, const struct GNUNET_PSYC_Message *entry_msg, - struct GNUNET_SOCIAL_Slicer *slicer, + struct GNUNET_PSYC_Slicer *slicer, GNUNET_SOCIAL_GuestEnterCallback local_enter_cb, GNUNET_SOCIAL_EntryDecisionCallback entry_dcsn_cb, void *cls) @@ -1277,167 +1797,226 @@ GNUNET_SOCIAL_guest_enter (const struct GNUNET_CONFIGURATION_Handle *cfg, struct GNUNET_SOCIAL_Guest *gst = GNUNET_malloc (sizeof (*gst)); struct GNUNET_SOCIAL_Place *plc = &gst->plc; - struct GuestEnterRequest * - req = guest_enter_request_create (&plc->ego_key, place_key, origin, - relay_count, relays, entry_msg); - plc->connect_msg = &req->header; - plc->ego_key = *GNUNET_IDENTITY_ego_get_private_key (ego); - plc->pub_key = *place_key; - plc->cfg = cfg; - plc->is_host = GNUNET_YES; + plc->ego_pub_key = ego->pub_key; + plc->pub_key = *place_pub_key; + plc->cfg = app->cfg; + plc->is_host = GNUNET_NO; plc->slicer = slicer; + plc->op = GNUNET_OP_create (); + + plc->connect_env + = guest_enter_request_create (app->id, &ego->pub_key, &plc->pub_key, + origin, relay_count, relays, entry_msg); + gst->enter_cb = local_enter_cb; gst->entry_dcsn_cb = entry_dcsn_cb; gst->cb_cls = cls; - plc->client = GNUNET_CLIENT_MANAGER_connect (cfg, "social", guest_handlers); - GNUNET_CLIENT_MANAGER_set_user_context_ (plc->client, gst, sizeof (*plc)); - - plc->tmit = GNUNET_PSYC_transmit_create (plc->client); - plc->recv = GNUNET_PSYC_receive_create (NULL, &slicer_message, plc->slicer); - - place_send_connect_msg (plc); + guest_connect (gst); return gst; } /** - * Result of a GNS name lookup for entering a place. + * Request entry to a place by name as a guest. + * + * @param app + * Application handle. + * @param ego + * Identity of the guest. + * @param gns_name + * GNS name of the place to enter. Either in the form of + * 'room.friend.gnu', or 'NYMPUBKEY.zkey'. This latter case refers to + * the 'PLACE' record of the empty label ("+") in the GNS zone with the + * nym's public key 'NYMPUBKEY', and can be used to request entry to a + * pseudonym's place directly. + * @param password + * Password to decrypt the record, or NULL for cleartext records. + * @param join_msg + * Entry request message or NULL. + * @param slicer + * Slicer to use for processing incoming requests from guests. + * @param local_enter_cb + * Called upon connection established to the social service. + * @param entry_decision_cb + * Called upon receiving entry decision. * - * @see GNUNET_SOCIAL_guest_enter_by_name + * @return NULL on errors, otherwise handle for the guest. */ -static void -gns_result_guest_enter (void *cls, uint32_t rd_count, - const struct GNUNET_GNSRECORD_Data *rd) +struct GNUNET_SOCIAL_Guest * +GNUNET_SOCIAL_guest_enter_by_name (const struct GNUNET_SOCIAL_App *app, + const struct GNUNET_SOCIAL_Ego *ego, + const char *gns_name, + const char *password, + const struct GNUNET_PSYC_Message *join_msg, + struct GNUNET_PSYC_Slicer *slicer, + GNUNET_SOCIAL_GuestEnterCallback local_enter_cb, + GNUNET_SOCIAL_EntryDecisionCallback entry_decision_cb, + void *cls) { - struct GNUNET_SOCIAL_Guest *gst = cls; + struct GNUNET_SOCIAL_Guest *gst = GNUNET_malloc (sizeof (*gst)); struct GNUNET_SOCIAL_Place *plc = &gst->plc; - const struct GNUNET_GNSRECORD_PlaceData * - rec = (const struct GNUNET_GNSRECORD_PlaceData *) rd->data; + if (NULL == password) + password = ""; + + uint16_t app_id_size = strlen (app->id) + 1; + uint16_t gns_name_size = strlen (gns_name) + 1; + uint16_t password_size = strlen (password) + 1; + + uint16_t join_msg_size = 0; + if (NULL != join_msg) + join_msg_size = ntohs (join_msg->header.size); + + struct GuestEnterByNameRequest *greq; + plc->connect_env + = GNUNET_MQ_msg_extra (greq, app_id_size + gns_name_size + + password_size + join_msg_size, + GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_BY_NAME); + + greq->ego_pub_key = ego->pub_key; + + char *p = (char *) &greq[1]; + GNUNET_memcpy (p, app->id, app_id_size); + p += app_id_size; + GNUNET_memcpy (p, gns_name, gns_name_size); + p += gns_name_size; + GNUNET_memcpy (p, password, password_size); + p += password_size; + if (NULL != join_msg) + GNUNET_memcpy (p, join_msg, join_msg_size); + + plc->ego_pub_key = ego->pub_key; + plc->cfg = app->cfg; + plc->is_host = GNUNET_NO; + plc->slicer = slicer; - if (0 == rd_count) - { - if (NULL != gst->enter_cb) - gst->enter_cb (gst->cb_cls, GNUNET_SYSERR, 0); - return; - } + plc->op = GNUNET_OP_create (); + gst->enter_cb = local_enter_cb; + gst->entry_dcsn_cb = entry_decision_cb; + gst->cb_cls = cls; - if (rd->data_size < sizeof (*rec)) - { - GNUNET_break_op (0); - if (NULL != gst->enter_cb) - gst->enter_cb (gst->cb_cls, GNUNET_SYSERR, 0); - return; - } + guest_connect (gst); + return gst; +} - struct GuestEnterRequest * - req = (struct GuestEnterRequest *) plc->connect_msg; - uint16_t req_size = ntohs (req->header.size); - struct GNUNET_PeerIdentity *relays = NULL; - uint16_t relay_count = ntohs (rec->relay_count); +struct ReconnectContext +{ + struct GNUNET_SOCIAL_Guest *guest; + int *result; + int64_t *max_message_id; + GNUNET_SOCIAL_GuestEnterCallback enter_cb; + void *enter_cls; +}; - if (0 < relay_count) - { - uint16_t relay_size = relay_count * sizeof (struct GNUNET_PeerIdentity); - struct GuestEnterRequest * - req2 = GNUNET_malloc (req_size + relay_size); - req2->header.size = htons (req_size + relay_size); - req2->header.type = req->header.type; - req2->guest_key = req->guest_key; +static void +guest_enter_reconnect_cb (void *cls, + int result, + const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key, + uint64_t max_message_id) +{ + struct ReconnectContext *reconnect_ctx = cls; + + GNUNET_assert (NULL != reconnect_ctx); + reconnect_ctx->result = GNUNET_new (int); + *(reconnect_ctx->result) = result; + reconnect_ctx->max_message_id = GNUNET_new (int64_t); + *(reconnect_ctx->max_message_id) = max_message_id; +} - uint16_t p = sizeof (*req); - if (0 < relay_size) - { - memcpy ((char *) req2 + p, relays, relay_size); - p += relay_size; - } - memcpy ((char *) req + p, &req[1], req_size - sizeof (*req)); +static void +guest_entry_dcsn_reconnect_cb (void *cls, + int is_admitted, + const struct GNUNET_PSYC_Message *entry_resp) +{ + struct ReconnectContext *reconnect_ctx = cls; + struct GNUNET_SOCIAL_Guest *gst = reconnect_ctx->guest; - plc->connect_msg = &req2->header; - GNUNET_free (req); - req = req2; + GNUNET_assert (NULL != reconnect_ctx); + GNUNET_assert (NULL != reconnect_ctx->result); + GNUNET_assert (NULL != reconnect_ctx->max_message_id); + if (GNUNET_YES != is_admitted) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Guest was rejected after calling " + "GNUNET_SOCIAL_guest_enter_reconnect ()\n"); } - - req->place_key = rec->place_key; - req->origin = rec->origin; - req->relay_count = rec->relay_count; - memcpy (&req[1], &rec[1], - ntohl (rec->relay_count) * sizeof (struct GNUNET_PeerIdentity)); - - plc->connect_msg = &req->header; - plc->pub_key = req->place_key; - - plc->tmit = GNUNET_PSYC_transmit_create (plc->client); - plc->recv = GNUNET_PSYC_receive_create (NULL, &slicer_message, plc); - - place_send_connect_msg (plc); + else if (NULL != reconnect_ctx->enter_cb) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "guest reconnected!\n"); + reconnect_ctx->enter_cb (reconnect_ctx->enter_cls, + *(reconnect_ctx->result), + &gst->plc.pub_key, + *(reconnect_ctx->max_message_id)); + } + GNUNET_free (reconnect_ctx->result); + GNUNET_free (reconnect_ctx->max_message_id); + GNUNET_free (reconnect_ctx); } + /** - * Request entry to a place as a guest. + * Reconnect to an already entered place as guest. * - * @param cfg Configuration to contact the social service. - * @param ego Identity of the guest. - * @param address GNS name of the place to enter. Either in the form of - * 'room.friend.gnu', or 'NYMPUBKEY.zkey'. This latter case refers to - * the 'PLACE' record of the empty label ("+") in the GNS zone with the - * nym's public key 'NYMPUBKEY', and can be used to request entry to a - * pseudonym's place directly. - * @param method_name Method name for the message. - * @param env Environment containing variables for the message, or NULL. - * @param data Payload for the message to give to the enter callback. - * @param data_size Number of bytes in @a data. - * @param slicer Slicer to use for processing incoming requests from guests. + * @param gconn + * Guest connection handle. + * @see GNUNET_SOCIAL_app_connect() & GNUNET_SOCIAL_AppGuestPlaceCallback() + * @param flags + * Flags for the entry. + * @param slicer + * Slicer to use for processing incoming requests from guests. + * @param enter_cb + * Called upon re-entering is complete. + * @param entry_decision_cb + * Called upon receiving entry decision. * * @return NULL on errors, otherwise handle for the guest. */ struct GNUNET_SOCIAL_Guest * -GNUNET_SOCIAL_guest_enter_by_name (const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_IDENTITY_Ego *ego, - char *gns_name, - const struct GNUNET_PSYC_Message *join_msg, - struct GNUNET_SOCIAL_Slicer *slicer, - GNUNET_SOCIAL_GuestEnterCallback local_enter_cb, - GNUNET_SOCIAL_EntryDecisionCallback entry_decision_cb, - void *cls) +GNUNET_SOCIAL_guest_enter_reconnect (struct GNUNET_SOCIAL_GuestConnection *gconn, + enum GNUNET_PSYC_SlaveJoinFlags flags, + struct GNUNET_PSYC_Slicer *slicer, + GNUNET_SOCIAL_GuestEnterCallback enter_cb, + void *cls) { struct GNUNET_SOCIAL_Guest *gst = GNUNET_malloc (sizeof (*gst)); struct GNUNET_SOCIAL_Place *plc = &gst->plc; + struct ReconnectContext *reconnect_ctx; - gst->enter_cb = local_enter_cb; - gst->cb_cls = cls; + uint16_t app_id_size = strlen (gconn->app->id) + 1; + struct GuestEnterRequest *greq; + plc->connect_env + = GNUNET_MQ_msg_extra (greq, app_id_size, + GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER); + greq->ego_pub_key = gconn->plc_msg.ego_pub_key; + greq->place_pub_key = gconn->plc_msg.place_pub_key; + greq->flags = htonl (flags); + + GNUNET_memcpy (&greq[1], gconn->app->id, app_id_size); - plc->ego_key = *GNUNET_IDENTITY_ego_get_private_key (ego); - plc->cfg = cfg; + plc->cfg = gconn->app->cfg; plc->is_host = GNUNET_NO; plc->slicer = slicer; + plc->pub_key = gconn->plc_msg.place_pub_key; + plc->ego_pub_key = gconn->plc_msg.ego_pub_key; - struct GuestEnterRequest * - req = guest_enter_request_create (&plc->ego_key, NULL, NULL, 0, NULL, - join_msg); - plc->connect_msg = &req->header; + reconnect_ctx = GNUNET_new (struct ReconnectContext); + reconnect_ctx->guest = gst; + reconnect_ctx->enter_cb = enter_cb; + reconnect_ctx->enter_cls = cls; - /* FIXME: get the public key of the origin and relays - * by looking up the PLACE record of gns_name. - */ - if (NULL == gns) - gns = GNUNET_GNS_connect (cfg); - - plc->client = GNUNET_CLIENT_MANAGER_connect (cfg, "social", guest_handlers); - GNUNET_CLIENT_MANAGER_set_user_context_ (plc->client, gst, sizeof (*plc)); - - struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key; - GNUNET_IDENTITY_ego_get_public_key (ego, &ego_pub_key); - GNUNET_GNS_lookup (gns, gns_name, &ego_pub_key, - GNUNET_GNSRECORD_TYPE_PLACE, GNUNET_GNS_LO_DEFAULT, - NULL, gns_result_guest_enter, gst); + plc->op = GNUNET_OP_create (); + gst->enter_cb = &guest_enter_reconnect_cb; + gst->entry_dcsn_cb = &guest_entry_dcsn_reconnect_cb; + gst->cb_cls = reconnect_ctx; + guest_connect (gst); return gst; } @@ -1464,15 +2043,20 @@ GNUNET_SOCIAL_guest_enter_by_name (const struct GNUNET_CONFIGURATION_Handle *cfg struct GNUNET_SOCIAL_TalkRequest * GNUNET_SOCIAL_guest_talk (struct GNUNET_SOCIAL_Guest *gst, const char *method_name, - const struct GNUNET_ENV_Environment *env, + const struct GNUNET_PSYC_Environment *env, GNUNET_PSYC_TransmitNotifyData notify_data, void *notify_data_cls, enum GNUNET_SOCIAL_TalkFlags flags) { + struct GNUNET_SOCIAL_Place *plc = &gst->plc; + GNUNET_assert (NULL != plc->tmit); + if (GNUNET_OK == - GNUNET_PSYC_transmit_message (gst->plc.tmit, method_name, env, - NULL, notify_data, notify_data_cls, flags)); - return (struct GNUNET_SOCIAL_TalkRequest *) gst->plc.tmit; + GNUNET_PSYC_transmit_message (plc->tmit, method_name, env, + NULL, notify_data, notify_data_cls, flags)) + return (struct GNUNET_SOCIAL_TalkRequest *) plc->tmit; + else + return NULL; } @@ -1498,34 +2082,62 @@ GNUNET_SOCIAL_guest_talk_resume (struct GNUNET_SOCIAL_TalkRequest *tr) void GNUNET_SOCIAL_guest_talk_cancel (struct GNUNET_SOCIAL_TalkRequest *tr) { - GNUNET_PSYC_transmit_cancel ((struct GNUNET_PSYC_TransmitHandle *) tr); + GNUNET_PSYC_transmit_cancel ( (struct GNUNET_PSYC_TransmitHandle *) tr); } /** - * Leave a place permanently. + * Disconnect from a place. * - * Notifies the owner of the place about leaving, and destroys the place handle. + * Invalidates guest handle. * - * @param place Place to leave permanently. - * @param keep_active Keep place active after last application disconnected. + * @param gst + * The guest to disconnect. */ void -GNUNET_SOCIAL_guest_leave (struct GNUNET_SOCIAL_Guest *gst, - int keep_active, - GNUNET_ContinuationCallback leave_cb, - void *leave_cls) +GNUNET_SOCIAL_guest_disconnect (struct GNUNET_SOCIAL_Guest *gst, + GNUNET_ContinuationCallback disconnect_cb, + void *cls) { struct GNUNET_SOCIAL_Place *plc = &gst->plc; - /* FIXME: send msg to service */ + plc->disconnect_cb = disconnect_cb; + plc->disconnect_cls = cls; + place_disconnect (plc); +} + - plc->is_disconnecting = GNUNET_YES; - plc->disconnect_cb = leave_cb; - plc->disconnect_cls = leave_cls; +/** + * Leave a place temporarily or permanently. + * + * Notifies the owner of the place about leaving, and destroys the place handle. + * + * @param place + * Place to leave. + * @param keep_active + * Keep place active after last application disconnected. + * #GNUNET_YES or #GNUNET_NO + * @param env + * Optional environment for the leave message if @a keep_active + * is #GNUNET_NO. NULL if not needed. + * @param leave_cb + * Called upon disconnecting from the social service. + */ +void +GNUNET_SOCIAL_guest_leave (struct GNUNET_SOCIAL_Guest *gst, + struct GNUNET_PSYC_Environment *env, + GNUNET_ContinuationCallback disconnect_cb, + void *cls) +{ + struct GNUNET_MQ_Envelope *envelope; - GNUNET_CLIENT_MANAGER_disconnect (plc->client, GNUNET_YES, - &guest_cleanup, gst); + GNUNET_SOCIAL_guest_talk (gst, "_notice_place_leave", env, NULL, NULL, + GNUNET_SOCIAL_TALK_NONE); + gst->plc.disconnect_cb = disconnect_cb; + gst->plc.disconnect_cls = cls; + envelope = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE); + GNUNET_MQ_send (gst->plc.mq, + envelope); } @@ -1546,9 +2158,144 @@ GNUNET_SOCIAL_guest_get_place (struct GNUNET_SOCIAL_Guest *gst) /** - * A history lesson. + * Obtain the public key of a place. + * + * @param plc + * Place. + * + * @return Public key of the place. + */ +const struct GNUNET_CRYPTO_EddsaPublicKey * +GNUNET_SOCIAL_place_get_pub_key (const struct GNUNET_SOCIAL_Place *plc) +{ + return &plc->pub_key; +} + + +/** + * Set message processing @a flags for a @a method_prefix. + * + * @param plc + * Place. + * @param method_prefix + * Method prefix @a flags apply to. + * @param flags + * The flags that apply to a matching @a method_prefix. + */ +void +GNUNET_SOCIAL_place_msg_proc_set (struct GNUNET_SOCIAL_Place *plc, + const char *method_prefix, + enum GNUNET_SOCIAL_MsgProcFlags flags) +{ + GNUNET_assert (NULL != method_prefix); + struct MsgProcRequest *mpreq; + uint16_t method_size = strnlen (method_prefix, + GNUNET_MAX_MESSAGE_SIZE + - sizeof (*mpreq)) + 1; + GNUNET_assert ('\0' == method_prefix[method_size - 1]); + + struct GNUNET_MQ_Envelope * + env = GNUNET_MQ_msg_extra (mpreq, method_size, + GNUNET_MESSAGE_TYPE_SOCIAL_MSG_PROC_SET); + mpreq->flags = htonl (flags); + GNUNET_memcpy (&mpreq[1], method_prefix, method_size); + + GNUNET_MQ_send (plc->mq, env); +} + + +/** + * Clear all message processing flags previously set for this place. + */ +void +GNUNET_SOCIAL_place_msg_proc_clear (struct GNUNET_SOCIAL_Place *plc) +{ + struct GNUNET_MessageHeader *req; + struct GNUNET_MQ_Envelope * + env = GNUNET_MQ_msg (req, GNUNET_MESSAGE_TYPE_SOCIAL_MSG_PROC_CLEAR); + + GNUNET_MQ_send (plc->mq, env); +} + + +static struct GNUNET_SOCIAL_HistoryRequest * +place_history_replay (struct GNUNET_SOCIAL_Place *plc, + uint64_t start_message_id, + uint64_t end_message_id, + uint64_t message_limit, + const char *method_prefix, + uint32_t flags, + struct GNUNET_PSYC_Slicer *slicer, + GNUNET_ResultCallback result_cb, + void *cls) +{ + struct GNUNET_PSYC_HistoryRequestMessage *req; + struct GNUNET_SOCIAL_HistoryRequest *hist = GNUNET_malloc (sizeof (*hist)); + hist->plc = plc; + hist->slicer = slicer; + hist->result_cb = result_cb; + hist->cls = cls; + hist->op_id = GNUNET_OP_add (plc->op, op_recv_history_result, hist, NULL); + + GNUNET_assert (NULL != method_prefix); + uint16_t method_size = strnlen (method_prefix, + GNUNET_MAX_MESSAGE_SIZE + - sizeof (*req)) + 1; + GNUNET_assert ('\0' == method_prefix[method_size - 1]); + + struct GNUNET_MQ_Envelope * + env = GNUNET_MQ_msg_extra (req, method_size, + GNUNET_MESSAGE_TYPE_PSYC_HISTORY_REPLAY); + req->start_message_id = GNUNET_htonll (start_message_id); + req->end_message_id = GNUNET_htonll (end_message_id); + req->message_limit = GNUNET_htonll (message_limit); + req->flags = htonl (flags); + req->op_id = GNUNET_htonll (hist->op_id); + GNUNET_memcpy (&req[1], method_prefix, method_size); + + GNUNET_MQ_send (plc->mq, env); + return hist; +} + + +/** + * Learn about the history of a place. + * + * Messages are returned through the @a slicer function + * and have the #GNUNET_PSYC_MESSAGE_HISTORIC flag set. + * + * @param place + * Place we want to learn more about. + * @param start_message_id + * First historic message we are interested in. + * @param end_message_id + * Last historic message we are interested in (inclusive). + * @param method_prefix + * Only retrieve messages with this method prefix. + * @param flags + * OR'ed GNUNET_PSYC_HistoryReplayFlags + * @param slicer + * Slicer to use for retrieved messages. + * Can be the same as the slicer of the place. + * @param result_cb + * Function called after all messages retrieved. + * NULL if not needed. + * @param cls Closure for @a result_cb. */ -struct GNUNET_SOCIAL_HistoryLesson; +struct GNUNET_SOCIAL_HistoryRequest * +GNUNET_SOCIAL_place_history_replay (struct GNUNET_SOCIAL_Place *plc, + uint64_t start_message_id, + uint64_t end_message_id, + const char *method_prefix, + uint32_t flags, + struct GNUNET_PSYC_Slicer *slicer, + GNUNET_ResultCallback result_cb, + void *cls) +{ + return place_history_replay (plc, start_message_id, end_message_id, 0, + method_prefix, flags, slicer, result_cb, cls); +} + /** * Learn about the history of a place. @@ -1559,139 +2306,521 @@ struct GNUNET_SOCIAL_HistoryLesson; * * To get the latest message, use 0 for both the start and end message ID. * - * @param place Place we want to learn more about. - * @param start_message_id First historic message we are interested in. - * @param end_message_id Last historic message we are interested in (inclusive). - * @param slicer Slicer to use to process history. Can be the same as the - * slicer of the place, as the HISTORIC flag allows distinguishing - * old messages from fresh ones. - * @param finish_cb Function called after the last message in the history lesson - * is passed through the @a slicer. NULL if not needed. - * @param finish_cb_cls Closure for @a finish_cb. - * @return Handle to abort history lesson, never NULL (multiple lessons - * at the same time are allowed). + * @param place + * Place we want to learn more about. + * @param message_limit + * Maximum number of historic messages we are interested in. + * @param method_prefix + * Only retrieve messages with this method prefix. + * @param flags + * OR'ed GNUNET_PSYC_HistoryReplayFlags + * @param result_cb + * Function called after all messages retrieved. + * NULL if not needed. + * @param cls Closure for @a result_cb. */ -struct GNUNET_SOCIAL_HistoryLesson * -GNUNET_SOCIAL_place_get_history (struct GNUNET_SOCIAL_Place *place, - uint64_t start_message_id, - uint64_t end_message_id, - const struct GNUNET_SOCIAL_Slicer *slicer, - void (*finish_cb)(void *), - void *finish_cb_cls) -{ - return NULL; +struct GNUNET_SOCIAL_HistoryRequest * +GNUNET_SOCIAL_place_history_replay_latest (struct GNUNET_SOCIAL_Place *plc, + uint64_t message_limit, + const char *method_prefix, + uint32_t flags, + struct GNUNET_PSYC_Slicer *slicer, + GNUNET_ResultCallback result_cb, + void *cls) +{ + return place_history_replay (plc, 0, 0, message_limit, method_prefix, flags, + slicer, result_cb, cls); } /** - * Stop processing messages from the history lesson. - * - * Must not be called after the finish callback of the history lesson is called. + * Cancel learning about the history of a place. * - * @param hist History lesson to cancel. + * @param hist + * History lesson to cancel. */ void -GNUNET_SOCIAL_place_get_history_cancel (struct GNUNET_SOCIAL_HistoryLesson *hist) +GNUNET_SOCIAL_place_history_replay_cancel (struct GNUNET_SOCIAL_HistoryRequest *hist) { + GNUNET_OP_remove (hist->plc->op, hist->op_id); + GNUNET_free (hist); +} + +/** + * Request matching state variables. + */ +static struct GNUNET_SOCIAL_LookHandle * +place_state_get (struct GNUNET_SOCIAL_Place *plc, + uint16_t type, const char *name, + GNUNET_PSYC_StateVarCallback var_cb, + GNUNET_ResultCallback result_cb, void *cls) +{ + struct GNUNET_PSYC_StateRequestMessage *req; + struct GNUNET_SOCIAL_LookHandle *look = GNUNET_malloc (sizeof (*look)); + look->plc = plc; + look->var_cb = var_cb; + look->result_cb = result_cb; + look->cls = cls; + look->op_id = GNUNET_OP_add (plc->op, &op_recv_state_result, look, NULL); + + GNUNET_assert (NULL != name); + size_t name_size = strnlen (name, GNUNET_MAX_MESSAGE_SIZE + - sizeof (*req)) + 1; + struct GNUNET_MQ_Envelope * + env = GNUNET_MQ_msg_extra (req, name_size, type); + req->op_id = GNUNET_htonll (look->op_id); + GNUNET_memcpy (&req[1], name, name_size); + + GNUNET_MQ_send (plc->mq, env); + return look; } -struct GNUNET_SOCIAL_WatchHandle; +/** + * Look at a particular object in the place. + * + * The best matching object is returned (its name might be less specific than + * what was requested). + * + * @param place + * The place where to look. + * @param full_name + * Full name of the object. + * @param value_size + * Set to the size of the returned value. + * + * @return NULL if there is no such object at this place. + */ +struct GNUNET_SOCIAL_LookHandle * +GNUNET_SOCIAL_place_look_at (struct GNUNET_SOCIAL_Place *plc, + const char *full_name, + GNUNET_PSYC_StateVarCallback var_cb, + GNUNET_ResultCallback result_cb, + void *cls) +{ + return place_state_get (plc, GNUNET_MESSAGE_TYPE_PSYC_STATE_GET, + full_name, var_cb, result_cb, cls); +} + /** - * Watch a place for changed objects. + * Look for objects in the place with a matching name prefix. * * @param place - * Place to watch. - * @param object_filter - * Object prefix to match. - * @param state_var_cb - * Function to call when an object/state var changes. + * The place where to look. + * @param name_prefix + * Look at objects with names beginning with this value. + * @param var_cb + * Function to call for each object found. * @param cls - * Closure for callback. + * Closure for callback function. * - * @return Handle that can be used to cancel watching. + * @return Handle that can be used to stop looking at objects. */ -struct GNUNET_SOCIAL_WatchHandle * -GNUNET_SOCIAL_place_watch (struct GNUNET_SOCIAL_Place *place, - const char *object_filter, - GNUNET_PSYC_StateVarCallback state_var_cb, - void *cls) -{ - return NULL; +struct GNUNET_SOCIAL_LookHandle * +GNUNET_SOCIAL_place_look_for (struct GNUNET_SOCIAL_Place *plc, + const char *name_prefix, + GNUNET_PSYC_StateVarCallback var_cb, + GNUNET_ResultCallback result_cb, + void *cls) +{ + return place_state_get (plc, GNUNET_MESSAGE_TYPE_PSYC_STATE_GET_PREFIX, + name_prefix, var_cb, result_cb, cls); } /** - * Cancel watching a place for changed objects. + * Cancel a state request operation. * - * @param wh Watch handle to cancel. + * @param sr + * Handle for the operation to cancel. */ void -GNUNET_SOCIAL_place_watch_cancel (struct GNUNET_SOCIAL_WatchHandle *wh) +GNUNET_SOCIAL_place_look_cancel (struct GNUNET_SOCIAL_LookHandle *look) +{ + GNUNET_OP_remove (look->plc->op, look->op_id); + GNUNET_free (look); +} + + +static void +op_recv_zone_add_place_result (void *cls, int64_t result, + const void *err_msg, uint16_t err_msg_size) { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received zone add place result: %" PRId64 ".\n", result); + struct ZoneAddPlaceHandle *add_plc = cls; + if (NULL != add_plc->result_cb) + add_plc->result_cb (add_plc->result_cls, result, err_msg, err_msg_size); + + GNUNET_free (add_plc); +} + + +/** + * Advertise @e place in the GNS zone of @e ego. + * + * @param app + * Application handle. + * @param ego + * Ego. + * @param place_pub_key + * Public key of place to add. + * @param name + * The name for the PLACE record to put in the zone. + * @param password + * Password used to encrypt the record or NULL to keep it cleartext. + * @param relay_count + * Number of elements in the @a relays array. + * @param relays + * List of relays to put in the PLACE record to advertise + * as entry points to the place in addition to the origin. + * @param expiration_time + * Expiration time of the record, use 0 to remove the record. + * @param result_cb + * Function called with the result of the operation. + * @param result_cls + * Closure for @a result_cb + * + * @return #GNUNET_OK if the request was sent, + * #GNUNET_SYSERR on error, e.g. the name/password is too long. + */ +int +GNUNET_SOCIAL_zone_add_place (const struct GNUNET_SOCIAL_App *app, + const struct GNUNET_SOCIAL_Ego *ego, + const char *name, + const char *password, + const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key, + const struct GNUNET_PeerIdentity *origin, + uint32_t relay_count, + const struct GNUNET_PeerIdentity *relays, + struct GNUNET_TIME_Absolute expiration_time, + GNUNET_ResultCallback result_cb, + void *result_cls) +{ + struct ZoneAddPlaceRequest *preq; + size_t name_size = strlen (name) + 1; + size_t password_size = strlen (password) + 1; + size_t relay_size = relay_count * sizeof (*relays); + size_t payload_size = name_size + password_size + relay_size; + + if (GNUNET_MAX_MESSAGE_SIZE < sizeof (*preq) + payload_size) + return GNUNET_SYSERR; + + struct GNUNET_MQ_Envelope * + env = GNUNET_MQ_msg_extra (preq, payload_size, + GNUNET_MESSAGE_TYPE_SOCIAL_ZONE_ADD_PLACE); + preq->expiration_time = GNUNET_htonll (expiration_time.abs_value_us); + preq->ego_pub_key = ego->pub_key; + preq->place_pub_key = *place_pub_key; + preq->origin = *origin; + preq->relay_count = htonl (relay_count); + + char *p = (char *) &preq[1]; + GNUNET_memcpy (p, name, name_size); + p += name_size; + GNUNET_memcpy (p, password, password_size); + p += password_size; + GNUNET_memcpy (p, relays, relay_size); + + struct ZoneAddPlaceHandle * add_plc = GNUNET_malloc (sizeof (*add_plc)); + add_plc->result_cb = result_cb; + add_plc->result_cls = result_cls; + + preq->op_id = GNUNET_htonll (GNUNET_OP_add (app->op, + op_recv_zone_add_place_result, + add_plc, NULL)); + + GNUNET_MQ_send (app->mq, env); + return GNUNET_OK; } -struct GNUNET_SOCIAL_LookHandle; +static void +op_recv_zone_add_nym_result (void *cls, int64_t result, + const void *err_msg, uint16_t err_msg_size) +{ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received zone add nym result: %" PRId64 ".\n", result); + + struct ZoneAddNymHandle *add_nym = cls; + if (NULL != add_nym->result_cb) + add_nym->result_cb (add_nym->result_cls, result, err_msg, err_msg_size); + + GNUNET_free (add_nym); +} /** - * Look at objects in the place with a matching name prefix. + * Add nym to the GNS zone of @e ego. * - * @param place - * The place to look its objects at. - * @param name_prefix - * Look at objects with names beginning with this value. - * @param state_var_cb - * Function to call for each object found. - * @param cls - * Closure for callback function. + * @param cfg + * Configuration. + * @param ego + * Ego. + * @param name + * The name for the PKEY record to put in the zone. + * @param nym_pub_key + * Public key of nym to add. + * @param expiration_time + * Expiration time of the record, use 0 to remove the record. + * @param result_cb + * Function called with the result of the operation. + * @param result_cls + * Closure for @a result_cb + * + * @return #GNUNET_OK if the request was sent, + * #GNUNET_SYSERR on error, e.g. the name is too long. + */ +int +GNUNET_SOCIAL_zone_add_nym (const struct GNUNET_SOCIAL_App *app, + const struct GNUNET_SOCIAL_Ego *ego, + const char *name, + const struct GNUNET_CRYPTO_EcdsaPublicKey *nym_pub_key, + struct GNUNET_TIME_Absolute expiration_time, + GNUNET_ResultCallback result_cb, + void *result_cls) +{ + struct ZoneAddNymRequest *nreq; + + size_t name_size = strlen (name) + 1; + if (GNUNET_MAX_MESSAGE_SIZE < sizeof (*nreq) + name_size) + return GNUNET_SYSERR; + + struct GNUNET_MQ_Envelope * + env = GNUNET_MQ_msg_extra (nreq, name_size, + GNUNET_MESSAGE_TYPE_SOCIAL_ZONE_ADD_NYM); + nreq->expiration_time = GNUNET_htonll (expiration_time.abs_value_us); + nreq->ego_pub_key = ego->pub_key; + nreq->nym_pub_key = *nym_pub_key; + GNUNET_memcpy (&nreq[1], name, name_size); + + struct ZoneAddNymHandle *add_nym = GNUNET_malloc (sizeof (*add_nym)); + add_nym->result_cb = result_cb; + add_nym->result_cls = result_cls; + + nreq->op_id = GNUNET_htonll (GNUNET_OP_add (app->op, + op_recv_zone_add_nym_result, + add_nym, NULL)); + + GNUNET_MQ_send (app->mq, env); + return GNUNET_OK; +} + + +/*** APP ***/ + + +static void +app_connect (struct GNUNET_SOCIAL_App *app); + + +static void +app_reconnect (void *cls) +{ + app_connect (cls); +} + + +/** + * App client disconnected from service. * - * @return Handle that can be used to stop looking at objects. + * Reconnect after backoff period. */ -struct GNUNET_SOCIAL_LookHandle * -GNUNET_SOCIAL_place_look (struct GNUNET_SOCIAL_Place *place, - const char *name_prefix, - GNUNET_PSYC_StateVarCallback state_var_cb, - void *cls) +static void +app_disconnected (void *cls, enum GNUNET_MQ_Error error) { - return NULL; + struct GNUNET_SOCIAL_App *app = cls; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "App client disconnected (%d), re-connecting\n", + (int) error); + if (NULL != app->mq) + { + GNUNET_MQ_destroy (app->mq); + app->mq = NULL; + } + + app->reconnect_task = GNUNET_SCHEDULER_add_delayed (app->reconnect_delay, + app_reconnect, + app); + app->reconnect_delay = GNUNET_TIME_STD_BACKOFF (app->reconnect_delay); +} + + +static void +app_connect (struct GNUNET_SOCIAL_App *app) +{ + struct GNUNET_MQ_MessageHandler handlers[] = { + GNUNET_MQ_hd_var_size (app_ego, + GNUNET_MESSAGE_TYPE_SOCIAL_APP_EGO, + struct AppEgoMessage, + app), + GNUNET_MQ_hd_fixed_size (app_ego_end, + GNUNET_MESSAGE_TYPE_SOCIAL_APP_EGO_END, + struct GNUNET_MessageHeader, + app), + GNUNET_MQ_hd_var_size (app_place, + GNUNET_MESSAGE_TYPE_SOCIAL_APP_PLACE, + struct AppPlaceMessage, + app), + GNUNET_MQ_hd_fixed_size (app_place_end, + GNUNET_MESSAGE_TYPE_SOCIAL_APP_PLACE_END, + struct GNUNET_MessageHeader, + app), + GNUNET_MQ_hd_var_size (app_result, + GNUNET_MESSAGE_TYPE_PSYC_RESULT_CODE, + struct GNUNET_OperationResultMessage, + app), + GNUNET_MQ_handler_end () + }; + + app->mq = GNUNET_CLIENT_connect (app->cfg, "social", + handlers, app_disconnected, app); + GNUNET_assert (NULL != app->mq); + GNUNET_MQ_send_copy (app->mq, app->connect_env); } /** - * Stop looking at objects. + * Connect application to the social service. * - * @param lh Look handle to stop. + * The @host_place_cb and @guest_place_cb functions are + * initially called for each entered places, + * then later each time a new place is entered with the current application ID. + * + * @param cfg + * Configuration. + * @param id + * Application ID. + * @param ego_cb + * Function to notify about an available ego. + * @param host_cb + * Function to notify about a place entered as host. + * @param guest_cb + * Function to notify about a place entered as guest. + * @param cls + * Closure for the callbacks. + * + * @return Handle that can be used to stop listening. */ -void -GNUNET_SOCIAL_place_look_cancel (struct GNUNET_SOCIAL_LookHandle *lh) +struct GNUNET_SOCIAL_App * +GNUNET_SOCIAL_app_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *id, + GNUNET_SOCIAL_AppEgoCallback ego_cb, + GNUNET_SOCIAL_AppHostPlaceCallback host_cb, + GNUNET_SOCIAL_AppGuestPlaceCallback guest_cb, + GNUNET_SOCIAL_AppConnectedCallback connected_cb, + void *cls) { + uint16_t app_id_size = strnlen (id, GNUNET_SOCIAL_APP_MAX_ID_SIZE); + if (GNUNET_SOCIAL_APP_MAX_ID_SIZE == app_id_size) + return NULL; + app_id_size++; + + struct GNUNET_SOCIAL_App *app = GNUNET_malloc (sizeof *app); + app->cfg = cfg; + app->ego_cb = ego_cb; + app->host_cb = host_cb; + app->guest_cb = guest_cb; + app->connected_cb = connected_cb; + app->cb_cls = cls; + app->egos = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO); + app->op = GNUNET_OP_create (); + app->id = GNUNET_malloc (app_id_size); + GNUNET_memcpy (app->id, id, app_id_size); + + struct AppConnectRequest *creq; + app->connect_env = GNUNET_MQ_msg_extra (creq, app_id_size, + GNUNET_MESSAGE_TYPE_SOCIAL_APP_CONNECT); + GNUNET_memcpy (&creq[1], app->id, app_id_size); + + app_connect (app); + return app; +} + +static void +app_cleanup (struct GNUNET_SOCIAL_App *app) +{ + if (NULL != app->mq) + { + GNUNET_MQ_destroy (app->mq); + app->mq = NULL; + } + if (NULL != app->disconnect_cb) + { + app->disconnect_cb (app->disconnect_cls); + app->disconnect_cb = NULL; + } + GNUNET_free (app); } +/** + * Disconnect application. + * + * @param app + * Application handle. + * @param disconnect_cb + * Disconnect callback. + * @param disconnect_cls + * Disconnect closure. + */ +void +GNUNET_SOCIAL_app_disconnect (struct GNUNET_SOCIAL_App *app, + GNUNET_ContinuationCallback disconnect_cb, + void *disconnect_cls) +{ + if (NULL == app) return; + + app->disconnect_cb = disconnect_cb; + app->disconnect_cls = disconnect_cls; + + if (NULL != app->mq) + { + struct GNUNET_MQ_Envelope *env = GNUNET_MQ_get_last_envelope (app->mq); + if (NULL != env) + { + GNUNET_MQ_notify_sent (env, (GNUNET_SCHEDULER_TaskCallback) app_cleanup, app); + } + else + { + app_cleanup (app); + } + } + else + { + app_cleanup (app); + } +} /** - * Look at a particular object in the place. + * Detach application from a place. * - * The best matching object is returned (its name might be less specific than - * what was requested). + * Removes the place from the entered places list for this application. + * Note: this does not disconnect from the place. * - * @param place The place to look the object at. - * @param full_name Full name of the object. - * @param value_size Set to the size of the returned value. - * @return NULL if there is no such object at this place. + * @see GNUNET_SOCIAL_host_disconnect() and GNUNET_SOCIAL_guest_disconnect() + * + * @param app + * Application. + * @param plc + * Place. */ -const void * -GNUNET_SOCIAL_place_look_at (struct GNUNET_SOCIAL_Place *place, - const char *full_name, - size_t *value_size) +void +GNUNET_SOCIAL_app_detach (struct GNUNET_SOCIAL_App *app, + struct GNUNET_SOCIAL_Place *plc) { - return NULL; + struct AppDetachRequest *dreq; + struct GNUNET_MQ_Envelope * + env = GNUNET_MQ_msg (dreq, GNUNET_MESSAGE_TYPE_SOCIAL_APP_DETACH); + dreq->place_pub_key = plc->pub_key; + dreq->ego_pub_key = plc->ego_pub_key; + + GNUNET_MQ_send (app->mq, env); }