+static void
+origin_to_all (struct GNUNET_MULTICAST_Origin *orig);
+
+static void
+member_to_origin (struct GNUNET_MULTICAST_Member *mem);
+
+
+/**
+ * Check join request message.
+ */
+static int
+check_group_join_request (void *cls,
+ const struct MulticastJoinRequestMessage *jreq)
+{
+ uint16_t size = ntohs (jreq->header.size);
+
+ if (sizeof (*jreq) == size)
+ return GNUNET_OK;
+
+ if (sizeof (*jreq) + sizeof (struct GNUNET_MessageHeader) <= size)
+ return GNUNET_OK;
+
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Receive join request from service.
+ */
+static void
+handle_group_join_request (void *cls,
+ const struct MulticastJoinRequestMessage *jreq)
+{
+ struct GNUNET_MULTICAST_Group *grp = cls;
+ struct GNUNET_MULTICAST_JoinHandle *jh;
+ const struct GNUNET_MessageHeader *jmsg = NULL;
+
+ if (NULL == grp)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ if (NULL == grp->join_req_cb)
+ return;
+
+ if (sizeof (*jreq) + sizeof (*jmsg) <= ntohs (jreq->header.size))
+ jmsg = (const struct GNUNET_MessageHeader *) &jreq[1];
+
+ jh = GNUNET_malloc (sizeof (*jh));
+ jh->group = grp;
+ jh->member_pub_key = jreq->member_pub_key;
+ jh->peer = jreq->peer;
+ grp->join_req_cb (grp->cb_cls, &jreq->member_pub_key, jmsg, jh);
+
+ grp->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
+}
+
+
+/**
+ * Check multicast message.
+ */
+static int
+check_group_message (void *cls,
+ const struct GNUNET_MULTICAST_MessageHeader *mmsg)
+{
+ return GNUNET_OK;
+}
+
+
+/**
+ * Receive multicast message from service.
+ */
+static void
+handle_group_message (void *cls,
+ const struct GNUNET_MULTICAST_MessageHeader *mmsg)
+{
+ struct GNUNET_MULTICAST_Group *grp = cls;
+
+ if (GNUNET_YES == grp->is_disconnecting)
+ return;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Calling message callback with a message of size %u.\n",
+ ntohs (mmsg->header.size));
+
+ if (NULL != grp->message_cb)
+ grp->message_cb (grp->cb_cls, mmsg);
+
+ grp->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
+}
+
+
+/**
+ * Receive message/request fragment acknowledgement from service.
+ */
+static void
+handle_group_fragment_ack (void *cls,
+ const struct GNUNET_MessageHeader *msg)
+{
+ struct GNUNET_MULTICAST_Group *grp = cls;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "%p Got fragment ACK. in_transmit=%u, acks_pending=%u\n",
+ grp, grp->in_transmit, grp->acks_pending);
+
+ if (0 == grp->acks_pending)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "%p Ignoring extraneous fragment ACK.\n", grp);
+ return;
+ }
+ grp->acks_pending--;
+
+ if (GNUNET_YES != grp->in_transmit)
+ return;
+
+ if (GNUNET_YES == grp->is_origin)
+ origin_to_all ((struct GNUNET_MULTICAST_Origin *) grp);
+ else
+ member_to_origin ((struct GNUNET_MULTICAST_Member *) grp);
+
+ grp->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
+}
+
+
+/**
+ * Check unicast request.
+ */
+static int
+check_origin_request (void *cls,
+ const struct GNUNET_MULTICAST_RequestHeader *req)
+{
+ return GNUNET_OK;
+}
+
+
+/**
+ * Origin receives unicast request from a member.
+ */
+static void
+handle_origin_request (void *cls,
+ const struct GNUNET_MULTICAST_RequestHeader *req)
+{
+ struct GNUNET_MULTICAST_Group *grp;
+ struct GNUNET_MULTICAST_Origin *orig = cls;
+ grp = &orig->grp;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Calling request callback with a request of size %u.\n",
+ ntohs (req->header.size));
+
+ if (NULL != orig->request_cb)
+ orig->request_cb (grp->cb_cls, req);
+
+ grp->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
+}
+
+
+/**
+ * Receive multicast replay request from service.
+ */
+static void
+handle_group_replay_request (void *cls,
+ const struct MulticastReplayRequestMessage *rep)
+
+{
+ struct GNUNET_MULTICAST_Group *grp = cls;
+
+ if (GNUNET_YES == grp->is_disconnecting)
+ return;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got replay request.\n");
+
+ if (0 != rep->fragment_id)
+ {
+ if (NULL != grp->replay_frag_cb)
+ {
+ struct GNUNET_MULTICAST_ReplayHandle * rh = GNUNET_malloc (sizeof (*rh));
+ rh->grp = grp;
+ rh->req = *rep;
+ grp->replay_frag_cb (grp->cb_cls, &rep->member_pub_key,
+ GNUNET_ntohll (rep->fragment_id),
+ GNUNET_ntohll (rep->flags), rh);
+ }
+ }
+ else if (0 != rep->message_id)
+ {
+ if (NULL != grp->replay_msg_cb)
+ {
+ struct GNUNET_MULTICAST_ReplayHandle * rh = GNUNET_malloc (sizeof (*rh));
+ rh->grp = grp;
+ rh->req = *rep;
+ grp->replay_msg_cb (grp->cb_cls, &rep->member_pub_key,
+ GNUNET_ntohll (rep->message_id),
+ GNUNET_ntohll (rep->fragment_offset),
+ GNUNET_ntohll (rep->flags), rh);
+ }
+ }
+
+ grp->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
+}
+
+
+/**
+ * Check replay response.
+ */
+static int
+check_member_replay_response (void *cls,
+ const struct MulticastReplayResponseMessage *res)
+{
+ uint16_t size = ntohs (res->header.size);
+
+ if (sizeof (*res) == size)
+ return GNUNET_OK;
+
+ if (sizeof (*res) + sizeof (struct GNUNET_MULTICAST_MessageHeader) <= size)
+ return GNUNET_OK;
+
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Receive replay response from service.
+ */
+static void
+handle_member_replay_response (void *cls,
+ const struct MulticastReplayResponseMessage *res)
+{
+ struct GNUNET_MULTICAST_Group *grp;
+ struct GNUNET_MULTICAST_Member *mem = cls;
+ grp = &mem->grp;
+
+ if (GNUNET_YES == grp->is_disconnecting)
+ return;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got replay response.\n");
+
+ // FIXME: return result
+}
+
+
+/**
+ * Check join decision.
+ */
+static int
+check_member_join_decision (void *cls,
+ const struct MulticastJoinDecisionMessageHeader *hdcsn)
+{
+ return GNUNET_OK; // checked in handle below
+}
+
+
+/**
+ * Member receives join decision.
+ */
+static void
+handle_member_join_decision (void *cls,
+ const struct MulticastJoinDecisionMessageHeader *hdcsn)
+{
+ struct GNUNET_MULTICAST_Group *grp;
+ struct GNUNET_MULTICAST_Member *mem = cls;
+ grp = &mem->grp;
+
+ const struct MulticastJoinDecisionMessage *
+ dcsn = (const struct MulticastJoinDecisionMessage *) &hdcsn[1];
+
+ uint16_t dcsn_size = ntohs (dcsn->header.size);
+ int is_admitted = ntohl (dcsn->is_admitted);
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "%p Member got join decision from multicast: %d\n",
+ mem, is_admitted);
+
+ const struct GNUNET_MessageHeader *join_resp = NULL;
+ uint16_t join_resp_size = 0;
+
+ uint16_t relay_count = ntohl (dcsn->relay_count);
+ const struct GNUNET_PeerIdentity *relays = NULL;
+ uint16_t relay_size = relay_count * sizeof (*relays);
+ if (0 < relay_count)
+ {
+ if (dcsn_size < sizeof (*dcsn) + relay_size)
+ {
+ GNUNET_break_op (0);
+ is_admitted = GNUNET_SYSERR;
+ }
+ else
+ {
+ relays = (struct GNUNET_PeerIdentity *) &dcsn[1];
+ }
+ }
+
+ if (sizeof (*dcsn) + relay_size + sizeof (*join_resp) <= dcsn_size)
+ {
+ join_resp = (const struct GNUNET_MessageHeader *) ((char *) &dcsn[1] + relay_size);
+ join_resp_size = ntohs (join_resp->size);
+ }
+ if (dcsn_size < sizeof (*dcsn) + relay_size + join_resp_size)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received invalid join decision message from multicast: %u < %u + %u + %u\n",
+ dcsn_size , sizeof (*dcsn), relay_size, join_resp_size);
+ GNUNET_break_op (0);
+ is_admitted = GNUNET_SYSERR;
+ }
+
+ if (NULL != mem->join_dcsn_cb)
+ mem->join_dcsn_cb (grp->cb_cls, is_admitted, &hdcsn->peer,
+ relay_count, relays, join_resp);
+
+ // FIXME:
+ //if (GNUNET_YES != is_admitted)
+ // GNUNET_MULTICAST_member_part (mem);
+
+ grp->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
+}
+
+
+static void
+group_cleanup (struct GNUNET_MULTICAST_Group *grp)
+{
+ if (NULL != grp->connect_env)
+ {
+ GNUNET_MQ_discard (grp->connect_env);
+ grp->connect_env = NULL;
+ }
+ if (NULL != grp->mq)
+ {
+ GNUNET_MQ_destroy (grp->mq);
+ grp->mq = NULL;
+ }
+ if (NULL != grp->disconnect_cb)
+ {
+ grp->disconnect_cb (grp->disconnect_cls);
+ grp->disconnect_cb = NULL;
+ }
+ GNUNET_free (grp);
+}
+
+
+static void
+group_disconnect (struct GNUNET_MULTICAST_Group *grp,
+ GNUNET_ContinuationCallback cb,
+ void *cls)
+{
+ grp->is_disconnecting = GNUNET_YES;
+ grp->disconnect_cb = cb;
+ grp->disconnect_cls = cls;
+
+ if (NULL != grp->mq)
+ {
+ struct GNUNET_MQ_Envelope *last = GNUNET_MQ_get_last_envelope (grp->mq);
+ if (NULL != last)
+ {
+ GNUNET_MQ_notify_sent (last,
+ (GNUNET_MQ_NotifyCallback) group_cleanup, grp);
+ }
+ else
+ {
+ group_cleanup (grp);
+ }
+ }
+ else
+ {
+ group_cleanup (grp);
+ }
+}
+
+