+ chn->max_message_id = max_message_id;
+ chn->max_state_message_id = max_state_message_id;
+ slv->member
+ = GNUNET_MULTICAST_member_join (cfg, &chn->pub_key, &slv->priv_key,
+ &slv->origin,
+ slv->relay_count, slv->relays,
+ &slv->join_msg->header,
+ mcast_recv_join_request,
+ mcast_recv_join_decision,
+ mcast_recv_replay_fragment,
+ mcast_recv_replay_message,
+ mcast_recv_message, chn);
+ if (NULL != slv->join_msg)
+ {
+ GNUNET_free (slv->join_msg);
+ slv->join_msg = NULL;
+ }
+ }
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "%p GNUNET_PSYCSTORE_counters_get() "
+ "returned %d for channel %s.\n",
+ chn, result, GNUNET_h2s (&chn->pub_key_hash));
+ }
+
+ client_send_msg (chn, &res.header);
+}
+
+
+static void
+channel_init (struct Channel *chn)
+{
+ chn->recv_msgs
+ = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
+ chn->recv_frags = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
+}
+
+
+/**
+ * Handle a connecting client starting a channel master.
+ */
+static void
+client_recv_master_start (void *cls, struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *msg)
+{
+ const struct MasterStartRequest *req
+ = (const struct MasterStartRequest *) msg;
+
+ struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
+ struct GNUNET_HashCode pub_key_hash;
+
+ GNUNET_CRYPTO_eddsa_key_get_public (&req->channel_key, &pub_key);
+ GNUNET_CRYPTO_hash (&pub_key, sizeof (pub_key), &pub_key_hash);
+
+ struct Master *
+ mst = GNUNET_CONTAINER_multihashmap_get (masters, &pub_key_hash);
+ struct Channel *chn;
+
+ if (NULL == mst)
+ {
+ mst = GNUNET_new (struct Master);
+ mst->policy = ntohl (req->policy);
+ mst->priv_key = req->channel_key;
+ mst->join_reqs = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
+
+ chn = &mst->chn;
+ chn->is_master = GNUNET_YES;
+ chn->pub_key = pub_key;
+ chn->pub_key_hash = pub_key_hash;
+ channel_init (chn);
+
+ GNUNET_CONTAINER_multihashmap_put (masters, &chn->pub_key_hash, chn,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+ chn->store_op = GNUNET_PSYCSTORE_counters_get (store, &chn->pub_key,
+ store_recv_master_counters, mst);
+ }
+ else
+ {
+ chn = &mst->chn;
+
+ struct GNUNET_PSYC_CountersResultMessage res;
+ res.header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_MASTER_START_ACK);
+ res.header.size = htons (sizeof (res));
+ res.result_code = htonl (GNUNET_OK);
+ res.max_message_id = GNUNET_htonll (mst->max_message_id);
+
+ GNUNET_SERVER_notification_context_add (nc, client);
+ GNUNET_SERVER_notification_context_unicast (nc, client, &res.header,
+ GNUNET_NO);
+ }
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "%p Client connected as master to channel %s.\n",
+ mst, GNUNET_h2s (&chn->pub_key_hash));
+
+ struct Client *cli = GNUNET_new (struct Client);
+ cli->client = client;
+ GNUNET_CONTAINER_DLL_insert (chn->clients_head, chn->clients_tail, cli);
+
+ GNUNET_SERVER_client_set_user_context (client, chn);
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+}
+
+
+/**
+ * Handle a connecting client joining as a channel slave.
+ */
+static void
+client_recv_slave_join (void *cls, struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *msg)
+{
+ const struct SlaveJoinRequest *req
+ = (const struct SlaveJoinRequest *) msg;
+ uint16_t req_size = ntohs (req->header.size);
+
+ struct GNUNET_CRYPTO_EcdsaPublicKey slv_pub_key;
+ struct GNUNET_HashCode pub_key_hash, slv_pub_hash;
+
+ GNUNET_CRYPTO_ecdsa_key_get_public (&req->slave_key, &slv_pub_key);
+ GNUNET_CRYPTO_hash (&slv_pub_key, sizeof (slv_pub_key), &slv_pub_hash);
+ GNUNET_CRYPTO_hash (&req->channel_pub_key, sizeof (req->channel_pub_key), &pub_key_hash);
+
+ struct GNUNET_CONTAINER_MultiHashMap *
+ chn_slv = GNUNET_CONTAINER_multihashmap_get (channel_slaves, &pub_key_hash);
+ struct Slave *slv = NULL;
+ struct Channel *chn;
+
+ if (NULL != chn_slv)
+ {
+ slv = GNUNET_CONTAINER_multihashmap_get (chn_slv, &slv_pub_hash);
+ }
+ if (NULL == slv)
+ {
+ slv = GNUNET_new (struct Slave);
+ slv->priv_key = req->slave_key;
+ slv->pub_key = slv_pub_key;
+ slv->pub_key_hash = slv_pub_hash;
+ slv->origin = req->origin;
+ slv->relay_count = ntohl (req->relay_count);
+ slv->join_flags = ntohl (req->flags);
+
+ const struct GNUNET_PeerIdentity *
+ relays = (const struct GNUNET_PeerIdentity *) &req[1];
+ uint16_t relay_size = slv->relay_count * sizeof (*relays);
+ uint16_t join_msg_size = 0;
+
+ if (sizeof (*req) + relay_size + sizeof (struct GNUNET_MessageHeader)
+ <= req_size)
+ {
+ struct GNUNET_PSYC_Message *
+ join_msg = (struct GNUNET_PSYC_Message *) (((char *) &req[1]) + relay_size);
+ join_msg_size = ntohs (join_msg->header.size);
+ slv->join_msg = GNUNET_malloc (join_msg_size);
+ memcpy (slv->join_msg, join_msg, join_msg_size);
+ }
+ if (sizeof (*req) + relay_size + join_msg_size != req_size)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "%u + %u + %u != %u\n",
+ sizeof (*req), relay_size, join_msg_size, req_size);
+ GNUNET_break (0);
+ GNUNET_SERVER_client_disconnect (client);
+ GNUNET_free (slv);
+ return;
+ }
+ if (0 < slv->relay_count)
+ {
+ slv->relays = GNUNET_malloc (relay_size);
+ memcpy (slv->relays, &req[1], relay_size);
+ }
+
+ chn = &slv->chn;
+ chn->is_master = GNUNET_NO;
+ chn->pub_key = req->channel_pub_key;
+ chn->pub_key_hash = pub_key_hash;
+ channel_init (chn);
+
+ if (NULL == chn_slv)
+ {
+ chn_slv = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
+ GNUNET_CONTAINER_multihashmap_put (channel_slaves, &chn->pub_key_hash, chn_slv,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+ }
+ GNUNET_CONTAINER_multihashmap_put (chn_slv, &slv->pub_key_hash, chn,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+ GNUNET_CONTAINER_multihashmap_put (slaves, &chn->pub_key_hash, chn,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+ chn->store_op = GNUNET_PSYCSTORE_counters_get (store, &chn->pub_key,
+ &store_recv_slave_counters, slv);
+ }
+ else
+ {
+ chn = &slv->chn;
+
+ struct GNUNET_PSYC_CountersResultMessage res;
+ res.header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_SLAVE_JOIN_ACK);
+ res.header.size = htons (sizeof (res));
+ res.result_code = htonl (GNUNET_OK);
+ res.max_message_id = GNUNET_htonll (chn->max_message_id);
+
+ GNUNET_SERVER_notification_context_add (nc, client);
+ GNUNET_SERVER_notification_context_unicast (nc, client, &res.header,
+ GNUNET_NO);
+
+ if (GNUNET_PSYC_SLAVE_JOIN_LOCAL & slv->join_flags)
+ {
+ mcast_recv_join_decision (slv, GNUNET_YES,
+ NULL, 0, NULL, NULL);
+ }
+ else if (NULL == slv->member)
+ {
+ slv->member
+ = GNUNET_MULTICAST_member_join (cfg, &chn->pub_key, &slv->priv_key,
+ &slv->origin,
+ slv->relay_count, slv->relays,
+ &slv->join_msg->header,
+ &mcast_recv_join_request,
+ &mcast_recv_join_decision,
+ &mcast_recv_replay_fragment,
+ &mcast_recv_replay_message,
+ &mcast_recv_message, chn);
+ if (NULL != slv->join_msg)
+ {
+ GNUNET_free (slv->join_msg);
+ slv->join_msg = NULL;
+ }
+ }
+ else if (NULL != slv->join_dcsn)
+ {
+ GNUNET_SERVER_notification_context_add (nc, client);
+ GNUNET_SERVER_notification_context_unicast (nc, client,
+ &slv->join_dcsn->header,
+ GNUNET_NO);
+ }