+/**
+ * Tunnel ID for the next created tunnel (global tunnel number)
+ */
+static MESH_TunnelNumber next_tid;
+
+/******************************************************************************/
+/****************** GENERAL HELPER FUNCTIONS ************************/
+/******************************************************************************/
+
+/**
+ * Retrieve the MeshPeerInfo stucture associated with the peer, create one
+ * and inster it in the appropiate structures if the peer is not known yet.
+ * @param peer Identity of the peer
+ * @return Existing or newly created peer info
+ */
+static struct MeshPeerInfo *
+get_peer_info (const struct GNUNET_PeerIdentity *peer)
+{
+ struct MeshPeerInfo * peer_info;
+
+ peer_info = GNUNET_CONTAINER_multihashmap_get(peers,
+ &peer->hashPubKey);
+ if (NULL == peer_info) {
+ peer_info = (struct MeshPeerInfo *)
+ GNUNET_malloc(sizeof(struct MeshPeerInfo));
+ GNUNET_CONTAINER_multihashmap_put(peers,
+ &peer->hashPubKey,
+ peer_info,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
+ peer_info->id = GNUNET_PEER_intern(peer);
+ peer_info->state = MESH_PEER_SEARCHING;
+ }
+
+ return peer_info;
+}
+
+/**
+ * Find the first peer whom to send a packet to go down this path
+ * @param path The path to use
+ * @return short id of the next peer, myid in case of local delivery,
+ * or 0 in case of error
+ */
+static GNUNET_PEER_Id
+get_first_hop (struct MeshPath *path)
+{
+ unsigned int i;
+
+ if (NULL == path) return 0;
+ if (path->in_use == 0) return 0;
+
+ for (i = 0; i < path->length; i++) {
+ if (path->peers[i] == myid) {
+ if (i < path->length - 1) {
+ return path->peers[i+1];
+ } else {
+ return myid;
+ }
+ }
+ }
+ return 0;
+}
+
+
+/**
+ * Get the cost of the path.
+ * @param path The path to analyze
+ * @return Number of hops to reach destination, UINT_MAX in case the peer is not
+ * in the path
+ */
+static unsigned int
+get_path_cost(struct MeshPath *path)
+{
+ unsigned int i;
+
+ if (NULL == path) return UINT_MAX;
+ for (i = 0; i < path->length; i++) {
+ if (path->peers[i] == myid) {
+ return path->length - i;
+ }
+ }
+ return UINT_MAX;
+}
+
+
+/**
+ * Add the path to the peer and update the path used to reach it in case this
+ * is the shortest.
+ * @param peer_info Destination peer to add the path to.
+ * @param path New path to add. Last peer must be the peer in arg 1.
+ */
+static void
+add_path_to_peer(struct MeshPeerInfo *peer_info, struct MeshPath *path)
+{
+ unsigned int i;
+ unsigned int new_cost;
+ unsigned int best_cost;
+ struct MeshPath *aux;
+ struct MeshPath *best;
+
+ if (NULL == peer_info || NULL == path) return;
+
+ new_cost = get_path_cost(path);
+ best_cost = UINT_MAX;
+ best = NULL;
+ for (aux = peer_info->path; aux != NULL; aux = aux->next) {
+ if ((i = get_path_cost(aux)) < best_cost) {
+ best = aux;
+ best_cost = i;
+ }
+ }
+ if (best_cost < new_cost) {
+ path->in_use = 0;
+ GNUNET_CONTAINER_DLL_insert_tail(peer_info->path,
+ peer_info->path_tail,
+ path);
+ } else {
+ if (NULL != best) best->in_use = 0;
+ path->in_use = 1;
+ GNUNET_CONTAINER_DLL_insert(peer_info->path,
+ peer_info->path_tail,
+ path);
+ }
+ return;
+}
+
+
+/**
+ * Check if client has registered with the service and has not disconnected
+ * @param client the client to check
+ * @return non-NULL if client exists in the global DLL
+ */
+static struct MeshClient *
+retrieve_client (struct GNUNET_SERVER_Client *client)
+{
+ struct MeshClient *c;
+
+ c = clients;
+ while (NULL != c) {
+ if (c->handle == client) return c;
+ c = c->next;
+ }
+ return NULL;
+}
+
+
+/**
+ * Search for a tunnel among the tunnels for a client
+ * @param client the client whose tunnels to search in
+ * @param tid the local id of the tunnel
+ * @return tunnel handler, NULL if doesn't exist
+ */
+static struct MeshTunnel *
+retrieve_tunnel_by_local_id (struct MeshClient *c, MESH_TunnelNumber tid)
+{
+ GNUNET_HashCode hash;
+
+ GNUNET_CRYPTO_hash(&tid, sizeof(MESH_TunnelNumber), &hash);
+ return GNUNET_CONTAINER_multihashmap_get(c->tunnels, &hash);
+}
+
+/**
+ * Search for a tunnel by global ID using PEER_ID
+ * @param pi owner of the tunnel
+ * @param tid global tunnel number
+ * @return tunnel handler, NULL if doesn't exist
+ */
+static struct MeshTunnel *
+retrieve_tunnel_by_pi (GNUNET_PEER_Id pi, MESH_TunnelNumber tid)
+{
+ struct MESH_TunnelID id;
+ GNUNET_HashCode hash;
+
+ id.oid = pi;
+ id.tid = tid;
+
+ GNUNET_CRYPTO_hash(&id, sizeof(struct MESH_TunnelID), &hash);
+ return GNUNET_CONTAINER_multihashmap_get(tunnels, &hash);
+}
+
+
+
+/**
+ * Search for a tunnel by global ID using full PeerIdentities
+ * @param oid owner of the tunnel
+ * @param tid global tunnel number
+ * @return tunnel handler, NULL if doesn't exist
+ */
+static struct MeshTunnel *
+retrieve_tunnel (struct GNUNET_PeerIdentity *oid, MESH_TunnelNumber tid)
+{
+ GNUNET_PEER_Id pi;
+
+ pi = GNUNET_PEER_intern(oid);
+ GNUNET_PEER_change_rc(pi, -1);
+ return retrieve_tunnel_by_pi(pi, tid);
+}
+
+
+/**
+ * Destroy the path and free any allocated resources linked to it
+ * @param t tunnel the path belongs to
+ * @param p the path to destroy
+ * @return GNUNET_OK on success
+ */
+static int
+destroy_path(struct MeshPath *p)
+{
+ GNUNET_PEER_decrement_rcs(p->peers, p->length);
+ GNUNET_free(p->peers);
+ GNUNET_free(p);
+ return GNUNET_OK;
+}
+
+#if LATER
+/**
+ * Destroy the peer_info and free any allocated resources linked to it
+ * @param t tunnel the path belongs to
+ * @param pi the peer_info to destroy
+ * @return GNUNET_OK on success
+ */
+static int
+destroy_peer_info(struct MeshPeerInfo *pi)
+{
+ GNUNET_HashCode hash;
+ struct GNUNET_PeerIdentity id;
+
+ GNUNET_PEER_resolve(pi->id, &id);
+ GNUNET_PEER_change_rc(pi->id, -1);
+ GNUNET_CRYPTO_hash(&id, sizeof(struct GNUNET_PeerIdentity), &hash);
+
+ GNUNET_CONTAINER_multihashmap_remove(peers, &hash, pi);
+ GNUNET_free(pi);
+ return GNUNET_OK;
+}
+#endif
+
+
+/**
+ * Destroy the tunnel and free any allocated resources linked to it
+ * @param c client the tunnel belongs to
+ * @param t the tunnel to destroy
+ * @return GNUNET_OK on success
+ */
+static int
+destroy_tunnel(struct MeshTunnel *t)
+{
+// struct MeshPath *path;
+ struct MeshClient *c;
+ GNUNET_HashCode hash;
+ int r;
+
+ if (NULL == t) return GNUNET_OK;
+
+ c = t->client;
+
+ GNUNET_CRYPTO_hash(&t->id, sizeof(struct MESH_TunnelID), &hash);
+ if(GNUNET_YES != GNUNET_CONTAINER_multihashmap_remove(tunnels, &hash, t)) {
+ r = GNUNET_SYSERR;
+ }
+
+ GNUNET_CRYPTO_hash(&t->local_tid, sizeof(MESH_TunnelNumber), &hash);
+ if(GNUNET_YES !=
+ GNUNET_CONTAINER_multihashmap_remove(c->tunnels, &hash, t))
+ {
+ r = GNUNET_SYSERR;
+ }
+ GNUNET_free(t);
+ return r;
+}
+
+/******************************************************************************/
+/**************** MESH NETWORK HANDLER HELPERS ***********************/
+/******************************************************************************/
+
+/**
+ * Function called to notify a client about the socket
+ * being ready to queue more data. "buf" will be
+ * NULL and "size" zero if the socket was closed for
+ * writing in the meantime.
+ *
+ * @param cls closure
+ * @param size number of bytes available in buf
+ * @param buf where the callee should write the message
+ * @return number of bytes written to buf
+ */
+static size_t
+send_core_create_path_for_peer (void *cls, size_t size, void *buf)
+{
+ struct MeshPeerInfo *peer_info = cls;
+ struct GNUNET_MESH_ManipulatePath *msg;
+ struct MeshPath *p;
+ struct GNUNET_PeerIdentity *peer_ptr;
+ struct GNUNET_PeerIdentity id;
+ size_t size_needed;
+ int i;
+
+ if (0 == size && NULL == buf) {
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Retransmitting create path\n");
+ GNUNET_PEER_resolve(get_first_hop(peer_info->path), &id);
+ GNUNET_CORE_notify_transmit_ready(core_handle,
+ 0,
+ 0,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ &id,
+ sizeof(struct GNUNET_MESH_ManipulatePath)
+ + (peer_info->path->length
+ * sizeof (struct GNUNET_PeerIdentity)),
+ &send_core_create_path_for_peer,
+ peer_info);
+ return 0;
+ }
+ p = peer_info->path;
+ while (NULL != p) {
+ if (p->in_use) {
+ break;
+ }
+ p = p->next;
+ }
+ if (p == NULL) return 0; // TODO Notify ERROR Path not found
+
+ size_needed = sizeof(struct GNUNET_MESH_ManipulatePath)
+ + p->length * sizeof(struct GNUNET_PeerIdentity);
+ if (size < size_needed) {
+ // TODO retry? cancel?
+ return 0;
+ }
+
+ msg = (struct GNUNET_MESH_ManipulatePath *) buf;
+ msg->header.size = htons(size_needed);
+ msg->header.type = htons(GNUNET_MESSAGE_TYPE_MESH_PATH_CREATE);
+
+ peer_ptr = (struct GNUNET_PeerIdentity *) &msg[1];
+ for (i = 0; i < p->length; i++) {
+ GNUNET_PEER_resolve(p->peers[i], peer_ptr++);
+ }
+
+ peer_info->state = MESH_PEER_WAITING; // TODO maybe already ready?
+
+ return size_needed;
+}
+
+#if LATER
+/**
+ * Send another peer a notification to destroy a tunnel
+ * @param cls The tunnel to destroy
+ * @param size Size in the buffer
+ * @param buf Memory where to put the data to transmit
+ * @return Size of data put in buffer
+ */
+static size_t
+send_p2p_tunnel_destroy(void *cls, size_t size, void *buf)
+{
+ struct MeshTunnel *t = cls;
+ struct MeshClient *c;
+ struct GNUNET_MESH_TunnelMessage *msg;
+
+ c = t->client;
+ msg = buf;
+ msg->header.type = htons(GNUNET_MESSAGE_TYPE_MESH_LOCAL_TUNNEL_DESTROY); /*FIXME*/
+ msg->header.size = htons(sizeof(struct GNUNET_MESH_TunnelMessage));
+ msg->tunnel_id = htonl(t->id.tid);
+
+ destroy_tunnel(c, t);
+ return sizeof(struct GNUNET_MESH_TunnelMessage);
+}
+#endif
+
+