summaryrefslogtreecommitdiff
path: root/src/messenger/gnunet-service-messenger_room.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/messenger/gnunet-service-messenger_room.c')
-rw-r--r--src/messenger/gnunet-service-messenger_room.c1051
1 files changed, 1051 insertions, 0 deletions
diff --git a/src/messenger/gnunet-service-messenger_room.c b/src/messenger/gnunet-service-messenger_room.c
new file mode 100644
index 000000000..7383e1d20
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_room.c
@@ -0,0 +1,1051 @@
+/*
+ This file is part of GNUnet.
+ Copyright (C) 2020 GNUnet e.V.
+
+ GNUnet is free software: you can redistribute it and/or modify it
+ under the terms of the GNU Affero General Public License as published
+ by the Free Software Foundation, either version 3 of the License,
+ or (at your option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ SPDX-License-Identifier: AGPL3.0-or-later
+ */
+/**
+ * @author Tobias Frisch
+ * @file src/messenger/gnunet-service-messenger_room.c
+ * @brief GNUnet MESSENGER service
+ */
+
+#include "gnunet-service-messenger_room.h"
+
+#include "gnunet-service-messenger_message_kind.h"
+
+#include "gnunet-service-messenger_service.h"
+#include "gnunet-service-messenger_util.h"
+
+static void
+idle_request_room_messages (void *cls);
+
+struct GNUNET_MESSENGER_SrvRoom*
+create_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct GNUNET_HashCode *key)
+{
+ GNUNET_assert((handle) && (key));
+
+ struct GNUNET_MESSENGER_SrvRoom *room = GNUNET_new(struct GNUNET_MESSENGER_SrvRoom);
+
+ room->service = handle->service;
+ room->host = handle;
+ room->port = NULL;
+
+ GNUNET_memcpy(&(room->key), key, sizeof(struct GNUNET_HashCode));
+
+ room->tunnels = GNUNET_CONTAINER_multipeermap_create (8, GNUNET_NO);
+ room->members = GNUNET_CONTAINER_multishortmap_create (8, GNUNET_NO);
+ room->member_infos = GNUNET_CONTAINER_multishortmap_create (8, GNUNET_NO);
+
+ init_message_store (&(room->store));
+ room->requested = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
+
+ init_list_tunnels (&(room->basement));
+ init_list_messages (&(room->last_messages));
+
+ room->peer_message = NULL;
+
+ init_list_messages (&(room->handling));
+ room->idle = NULL;
+
+ room->strict_access = GNUNET_NO;
+
+ if (room->service->dir)
+ load_service_room_and_messages (room->service, room);
+
+ room->idle = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE, idle_request_room_messages, room);
+
+ return room;
+}
+
+static int
+iterate_destroy_tunnels (void *cls, const struct GNUNET_PeerIdentity *key, void *value)
+{
+ struct GNUNET_MESSENGER_SrvTunnel *tunnel = value;
+ destroy_tunnel (tunnel);
+ return GNUNET_YES;
+}
+
+static int
+iterate_clear_members (void *cls, const struct GNUNET_ShortHashCode *key, void *value)
+{
+ struct GNUNET_MESSENGER_SrvContact *contact = value;
+
+ if (GNUNET_YES == decrease_contact_rc (contact))
+ {
+ struct GNUNET_MESSENGER_SrvRoom *room = cls;
+
+ const struct GNUNET_HashCode *id = get_contact_id_from_key (contact);
+
+ if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (room->service->contacts, id, contact))
+ destroy_contact (contact);
+ }
+
+ return GNUNET_YES;
+}
+
+static int
+iterate_destroy_member_infos (void *cls, const struct GNUNET_ShortHashCode *key, void *value)
+{
+ struct GNUNET_MESSENGER_MemberInfo *info = value;
+
+ clear_list_messages (&(info->session_messages));
+
+ GNUNET_free(info);
+ return GNUNET_YES;
+}
+
+void
+destroy_room (struct GNUNET_MESSENGER_SrvRoom *room)
+{
+ GNUNET_assert(room);
+
+ if (room->idle)
+ {
+ GNUNET_SCHEDULER_cancel (room->idle);
+
+ room->idle = NULL;
+ }
+
+ if (room->port)
+ GNUNET_CADET_close_port (room->port);
+
+ merge_room_last_messages (room, room->host);
+
+ GNUNET_CONTAINER_multipeermap_iterate (room->tunnels, iterate_destroy_tunnels,
+ NULL);
+
+ handle_room_messages (room);
+
+ if (room->service->dir)
+ save_service_room_and_messages (room->service, room);
+
+ GNUNET_CONTAINER_multishortmap_iterate (room->members, iterate_clear_members, room);
+ GNUNET_CONTAINER_multishortmap_iterate (room->member_infos, iterate_destroy_member_infos, NULL);
+
+ clear_message_store (&(room->store));
+
+ GNUNET_CONTAINER_multihashmap_destroy (room->requested);
+
+ GNUNET_CONTAINER_multipeermap_destroy (room->tunnels);
+ GNUNET_CONTAINER_multishortmap_destroy (room->members);
+ GNUNET_CONTAINER_multishortmap_destroy (room->member_infos);
+
+ clear_list_tunnels (&(room->basement));
+ clear_list_messages (&(room->last_messages));
+
+ if (room->peer_message)
+ GNUNET_free(room->peer_message);
+
+ GNUNET_free(room);
+}
+
+struct GNUNET_MESSENGER_SrvContact*
+get_room_contact (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *id)
+{
+ GNUNET_assert((room) && (room->members));
+
+ return GNUNET_CONTAINER_multishortmap_get (room->members, id);
+}
+
+void
+add_room_contact (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *id,
+ const struct GNUNET_IDENTITY_PublicKey *pubkey)
+{
+ struct GNUNET_MESSENGER_SrvContact *contact = get_service_contact_by_pubkey (room->service, pubkey);
+
+ if (GNUNET_OK == GNUNET_CONTAINER_multishortmap_put (room->members, id, contact,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
+ increase_contact_rc (contact);
+}
+
+struct GNUNET_MESSENGER_MemberInfo*
+get_room_member_info (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *id)
+{
+ GNUNET_assert((room) && (room->member_infos));
+
+ return GNUNET_CONTAINER_multishortmap_get (room->member_infos, id);
+}
+
+struct GNUNET_ShortHashCode*
+generate_room_member_id (const struct GNUNET_MESSENGER_SrvRoom *room)
+{
+ struct GNUNET_ShortHashCode *unique_id = GNUNET_new(struct GNUNET_ShortHashCode);
+
+ GNUNET_assert(room);
+
+ if (GNUNET_YES == generate_free_member_id (unique_id, room->members))
+ return unique_id;
+ else
+ {
+ GNUNET_free(unique_id);
+ return NULL;
+ }
+}
+
+const struct GNUNET_ShortHashCode*
+get_room_host_id (const struct GNUNET_MESSENGER_SrvRoom *room)
+{
+ GNUNET_assert(room);
+
+ return get_handle_member_id (room->host, &(room->key));
+}
+
+void
+change_room_host_id (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *unique_id)
+{
+ GNUNET_assert(room);
+
+ change_handle_member_id (room->host, &(room->key), unique_id);
+}
+
+static int
+send_room_info (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle,
+ struct GNUNET_MESSENGER_SrvTunnel *tunnel)
+{
+ if (!handle)
+ return GNUNET_NO;
+
+ merge_room_last_messages (room, handle);
+
+ if (!is_tunnel_connected (tunnel))
+ return GNUNET_NO;
+
+ struct GNUNET_MESSENGER_Message *message = create_message_info (get_handle_ego(handle), room->members);
+
+ if (!message)
+ return GNUNET_NO;
+
+ if ((tunnel->peer_message) && (tunnel->contact_id))
+ {
+ GNUNET_memcpy(&(message->body.info.unique_id), &(tunnel->contact_id), sizeof(struct GNUNET_ShortHashCode));
+ GNUNET_free(tunnel->contact_id);
+
+ tunnel->contact_id = NULL;
+ }
+
+ struct GNUNET_HashCode hash;
+
+ send_tunnel_message (tunnel, handle, message, &hash);
+ destroy_message (message);
+
+ if (tunnel->contact_id)
+ {
+ GNUNET_free(tunnel->contact_id);
+
+ tunnel->contact_id = NULL;
+ }
+
+ return GNUNET_YES;
+}
+
+static void*
+callback_room_connect (void *cls, struct GNUNET_CADET_Channel *channel, const struct GNUNET_PeerIdentity *source)
+{
+ struct GNUNET_MESSENGER_SrvRoom *room = cls;
+
+ struct GNUNET_MESSENGER_SrvTunnel *tunnel = GNUNET_CONTAINER_multipeermap_get (room->tunnels, source);
+
+ if (tunnel)
+ {
+ if (GNUNET_YES == bind_tunnel (tunnel, channel))
+ {
+ if (GNUNET_YES == send_room_info (room, room->host, tunnel))
+ return tunnel;
+ else
+ {
+ disconnect_tunnel (tunnel);
+ return NULL;
+ }
+ }
+ else
+ {
+ delayed_disconnect_channel (channel);
+ return NULL;
+ }
+ }
+ else
+ {
+ tunnel = create_tunnel (room, source);
+
+ if ((GNUNET_YES == bind_tunnel (tunnel, channel)) && (GNUNET_OK
+ == GNUNET_CONTAINER_multipeermap_put (room->tunnels, source, tunnel,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)))
+ {
+ if (GNUNET_YES == send_room_info (room, room->host, tunnel))
+ return tunnel;
+ else
+ {
+ GNUNET_CONTAINER_multipeermap_remove (room->tunnels, source, tunnel);
+
+ disconnect_tunnel (tunnel);
+ destroy_tunnel (tunnel);
+ return NULL;
+ }
+ }
+ else
+ {
+ tunnel->channel = NULL;
+ destroy_tunnel (tunnel);
+
+ delayed_disconnect_channel (channel);
+ return NULL;
+ }
+ }
+}
+
+static int
+join_room (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle,
+ const struct GNUNET_ShortHashCode *member_id)
+{
+ GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Joining room: %s (%s)\n", GNUNET_h2s(get_room_key(room)), GNUNET_sh2s(member_id));
+
+ struct GNUNET_MESSENGER_Message *message = create_message_join (get_handle_ego(handle));
+
+ if (!message)
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Your join message could not be created!\n");
+
+ return GNUNET_NO;
+ }
+
+ struct GNUNET_HashCode hash;
+
+ send_room_message (room, handle, message, &hash);
+ destroy_message (message);
+
+ struct GNUNET_MESSENGER_MemberInfo *info = GNUNET_new(struct GNUNET_MESSENGER_MemberInfo);
+
+ info->access = GNUNET_MESSENGER_MEMBER_ALLOWED;
+ init_list_messages (&(info->session_messages));
+
+ if (GNUNET_YES == GNUNET_CONTAINER_multishortmap_put (room->member_infos, member_id, info,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
+ {
+ change_handle_member_id (handle, &(room->key), member_id);
+
+ add_to_list_messages (&(info->session_messages), &hash);
+ return GNUNET_YES;
+ }
+ else
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Your member information could not be registered!\n");
+
+ GNUNET_free(info);
+ return GNUNET_NO;
+ }
+}
+
+static int
+join_room_locally (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle)
+{
+ const struct GNUNET_ShortHashCode *member_id = get_handle_member_id (handle, &(room->key));
+
+ struct GNUNET_MESSENGER_MemberInfo *info = GNUNET_CONTAINER_multishortmap_get (room->member_infos, member_id);
+
+ if ((!info) && (GNUNET_NO == join_room (room, handle, member_id)))
+ return GNUNET_NO;
+
+ return GNUNET_YES;
+}
+
+extern int
+check_tunnel_message (void *cls, const struct GNUNET_MessageHeader *header);
+extern void
+handle_tunnel_message (void *cls, const struct GNUNET_MessageHeader *header);
+
+extern void
+callback_tunnel_disconnect (void *cls, const struct GNUNET_CADET_Channel *channel);
+
+int
+open_room (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle)
+{
+ if (room->port)
+ return join_room_locally (room, handle);
+
+ struct GNUNET_CADET_Handle *cadet = get_room_cadet (room);
+ struct GNUNET_HashCode *key = get_room_key (room);
+
+ struct GNUNET_MQ_MessageHandler handlers[] = { GNUNET_MQ_hd_var_size(tunnel_message, GNUNET_MESSAGE_TYPE_CADET_CLI,
+ struct GNUNET_MessageHeader, NULL),
+ GNUNET_MQ_handler_end() };
+
+ room->port = GNUNET_CADET_open_port (cadet, key, callback_room_connect, room, NULL,
+ callback_tunnel_disconnect, handlers);
+
+ const struct GNUNET_ShortHashCode *member_id = get_handle_member_id (handle, &(room->key));
+
+ struct GNUNET_MESSENGER_MemberInfo *info = GNUNET_CONTAINER_multishortmap_get (room->member_infos, member_id);
+
+ if ((!info) && (GNUNET_NO == join_room (room, handle, member_id)) && (room->port))
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "You could not join the room, therefore it keeps closed!\n");
+
+ GNUNET_CADET_close_port (room->port);
+ room->port = NULL;
+
+ return GNUNET_NO;
+ }
+
+ struct GNUNET_MESSENGER_Message *message = create_message_peer (room->service);
+
+ if (message)
+ {
+ struct GNUNET_HashCode hash;
+
+ send_room_message (room, handle, message, &hash);
+ destroy_message (message);
+ }
+
+ return (room->port ? GNUNET_YES : GNUNET_NO);
+}
+
+int
+entry_room_at (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle,
+ const struct GNUNET_PeerIdentity *door)
+{
+ if (room->peer_message)
+ {
+ const struct GNUNET_MESSENGER_Message *msg = get_room_message (room, handle, room->peer_message, GNUNET_NO);
+
+ if (0 == GNUNET_memcmp(&(msg->body.peer.peer), door))
+ return join_room_locally (room, handle);
+ }
+
+ struct GNUNET_MESSENGER_SrvTunnel *tunnel = GNUNET_CONTAINER_multipeermap_get (room->tunnels, door);
+
+ if (tunnel)
+ {
+ switch (connect_tunnel (tunnel))
+ {
+ case GNUNET_YES:
+ return GNUNET_YES;
+ case GNUNET_NO:
+ return join_room_locally (room, handle);
+ default:
+ return GNUNET_NO;
+ }
+ }
+
+ tunnel = create_tunnel (room, door);
+
+ if ((GNUNET_YES == connect_tunnel (tunnel)) &&
+ (GNUNET_OK == GNUNET_CONTAINER_multipeermap_put (room->tunnels, door, tunnel,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)))
+ return GNUNET_YES;
+ else
+ {
+ GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "You could not connect to that door!\n");
+
+ destroy_tunnel (tunnel);
+ return GNUNET_NO;
+ }
+}
+
+struct GNUNET_MESSENGER_SrvTunnelFinder
+{
+ const struct GNUNET_ShortHashCode *needle;
+ struct GNUNET_MESSENGER_SrvTunnel *tunnel;
+};
+
+static int
+iterate_find_tunnel (void *cls, const struct GNUNET_PeerIdentity *peer, void *value)
+{
+ struct GNUNET_MESSENGER_SrvTunnel *tunnel = value;
+ struct GNUNET_MESSENGER_SrvTunnelFinder *finder = cls;
+
+ if ((tunnel->contact_id) && (0 == GNUNET_memcmp(tunnel->contact_id, finder->needle)))
+ {
+ finder->tunnel = tunnel;
+ return GNUNET_NO;
+ }
+
+ return GNUNET_YES;
+}
+
+struct GNUNET_MESSENGER_SrvTunnel*
+find_room_tunnel_to (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *contact_id)
+{
+ struct GNUNET_MESSENGER_SrvTunnelFinder finder;
+
+ finder.needle = contact_id;
+ finder.tunnel = NULL;
+
+ GNUNET_CONTAINER_multipeermap_iterate (room->tunnels, iterate_find_tunnel, &finder);
+
+ return finder.tunnel;
+}
+
+struct GNUNET_MQ_Envelope*
+pack_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle,
+ struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode *hash, int mode)
+{
+ message->header.timestamp = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get ());
+
+ const struct GNUNET_ShortHashCode *id = get_handle_member_id (handle, &(room->key));
+
+ GNUNET_assert(id);
+
+ GNUNET_memcpy(&(message->header.sender_id), id, sizeof(struct GNUNET_ShortHashCode));
+
+ if (room->last_messages.head)
+ GNUNET_memcpy(&(message->header.previous), &(room->last_messages.head->hash), sizeof(struct GNUNET_HashCode));
+ else
+ memset (&(message->header.previous), 0, sizeof(struct GNUNET_HashCode));
+
+ return pack_message (message, hash, get_handle_ego (handle), mode);
+}
+
+struct GNUNET_MESSENGER_ClosureSendRoom
+{
+ struct GNUNET_MESSENGER_SrvRoom *room;
+ struct GNUNET_MESSENGER_SrvHandle *handle;
+ struct GNUNET_MESSENGER_SrvTunnel *exclude;
+ struct GNUNET_MESSENGER_Message *message;
+ struct GNUNET_HashCode *hash;
+ int packed;
+};
+
+static int
+iterate_send_room_message (void *cls, const struct GNUNET_PeerIdentity *key, void *value)
+{
+ struct GNUNET_MESSENGER_SrvTunnel *tunnel = value;
+
+ if ((!is_tunnel_connected (tunnel)) || (!tunnel->contact_id))
+ return GNUNET_YES;
+
+ struct GNUNET_MESSENGER_ClosureSendRoom *closure = cls;
+
+ if (tunnel == closure->exclude)
+ return GNUNET_YES;
+
+ struct GNUNET_MQ_Envelope *env = NULL;
+
+ if (closure->packed == GNUNET_NO)
+ {
+ env = pack_room_message (closure->room, closure->handle, closure->message, closure->hash,
+ GNUNET_MESSENGER_PACK_MODE_ENVELOPE);
+
+ if (env)
+ {
+ closure->message = copy_message (closure->message);
+ closure->packed = GNUNET_YES;
+ }
+ }
+ else
+ {
+ env = pack_message (closure->message, NULL, NULL,
+ GNUNET_MESSENGER_PACK_MODE_ENVELOPE);
+ }
+
+ if (env)
+ send_tunnel_envelope (tunnel, closure->handle, env, closure->message, closure->hash);
+
+ return GNUNET_YES;
+}
+
+void
+callback_room_sent (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, void *cls,
+ struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash);
+
+void
+send_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle,
+ struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode *hash)
+{
+ struct GNUNET_MESSENGER_ClosureSendRoom closure;
+
+ closure.room = room;
+ closure.handle = handle;
+ closure.exclude = NULL;
+ closure.message = message;
+ closure.hash = hash;
+ closure.packed = GNUNET_NO;
+
+ GNUNET_CONTAINER_multipeermap_iterate (room->tunnels, iterate_send_room_message, &closure);
+
+ if ((GNUNET_NO == closure.packed) && (closure.message == message))
+ {
+ pack_room_message (room, handle, message, hash,
+ GNUNET_MESSENGER_PACK_MODE_UNKNOWN);
+
+ callback_room_sent (room, handle, NULL, copy_message (message), hash);
+ }
+}
+
+void
+send_room_message_ext (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle,
+ struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode *hash,
+ struct GNUNET_MESSENGER_SrvTunnel *tunnel)
+{
+ struct GNUNET_MESSENGER_ClosureSendRoom closure;
+
+ closure.room = room;
+ closure.handle = handle;
+ closure.exclude = tunnel;
+ closure.message = message;
+ closure.hash = hash;
+ closure.packed = GNUNET_NO;
+
+ GNUNET_CONTAINER_multipeermap_iterate (room->tunnels, iterate_send_room_message, &closure);
+
+ if ((GNUNET_NO == closure.packed) && (closure.message == message))
+ {
+ pack_room_message (room, handle, message, hash,
+ GNUNET_MESSENGER_PACK_MODE_UNKNOWN);
+
+ callback_room_sent (room, handle, NULL, copy_message (message), hash);
+ }
+}
+
+void
+forward_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvTunnel *tunnel,
+ const struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash)
+{
+ struct GNUNET_MESSENGER_ClosureSendRoom closure;
+ struct GNUNET_HashCode message_hash;
+
+ GNUNET_memcpy(&message_hash, hash, sizeof(struct GNUNET_HashCode));
+
+ closure.room = room;
+ closure.handle = NULL;
+ closure.exclude = tunnel;
+ closure.message = copy_message (message);
+ closure.hash = &message_hash;
+ closure.packed = GNUNET_YES;
+
+ GNUNET_CONTAINER_multipeermap_iterate (room->tunnels, iterate_send_room_message, &closure);
+}
+
+void
+merge_room_last_messages (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle)
+{
+ if (!handle)
+ return;
+
+ if (!room->last_messages.head)
+ return;
+
+ while (room->last_messages.head != room->last_messages.tail)
+ {
+ struct GNUNET_MESSENGER_ListMessage *element = room->last_messages.tail;
+
+ struct GNUNET_MESSENGER_Message *message = create_message_merge (&(element->hash));
+
+ if (message)
+ {
+ struct GNUNET_HashCode hash;
+
+ send_room_message (room, handle, message, &hash);
+ destroy_message (message);
+ }
+
+ if (element->prev)
+ GNUNET_CONTAINER_DLL_remove(room->last_messages.head, room->last_messages.tail, element);
+ }
+}
+
+struct GNUNET_CADET_Handle*
+get_room_cadet (struct GNUNET_MESSENGER_SrvRoom *room)
+{
+ return room->service->cadet;
+}
+
+struct GNUNET_HashCode*
+get_room_key (struct GNUNET_MESSENGER_SrvRoom *room)
+{
+ return &(room->key);
+}
+
+const struct GNUNET_MESSENGER_SrvTunnel*
+get_room_tunnel (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_PeerIdentity *peer)
+{
+ return GNUNET_CONTAINER_multipeermap_get (room->tunnels, peer);
+}
+
+const struct GNUNET_MESSENGER_Message*
+get_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle,
+ const struct GNUNET_HashCode *hash, int request)
+{
+ const struct GNUNET_MESSENGER_Message *message = get_store_message (&(room->store), hash);
+
+ if ((message) || (!handle) || (GNUNET_YES != request)
+ || (GNUNET_NO != GNUNET_CONTAINER_multihashmap_contains (room->requested, hash)))
+ return message;
+
+ struct GNUNET_MESSENGER_Message *request_msg = create_message_request (hash);
+
+ if (request_msg)
+ {
+ if (GNUNET_CONTAINER_multihashmap_put (room->requested, hash, NULL, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST) == GNUNET_OK)
+ {
+ struct GNUNET_HashCode request_hash;
+
+ send_room_message (room, handle, request_msg, &request_hash);
+ }
+
+ destroy_message (request_msg);
+ }
+
+ return message;
+}
+
+void
+callback_room_disconnect (struct GNUNET_MESSENGER_SrvRoom *room, void *cls)
+{
+ struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls;
+
+ if (!room->host)
+ return;
+
+ struct GNUNET_PeerIdentity identity;
+
+ GNUNET_PEER_resolve (tunnel->peer, &identity);
+
+ if (GNUNET_YES == contains_list_tunnels (&(room->basement), &identity))
+ {
+ struct GNUNET_MESSENGER_Message *message = create_message_miss (&identity);
+
+ if (message)
+ {
+ struct GNUNET_HashCode hash;
+
+ send_room_message (room, room->host, message, &hash);
+ destroy_message (message);
+ }
+ }
+}
+
+int
+callback_verify_room_message (struct GNUNET_MESSENGER_SrvRoom *room, void *cls,
+ struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode *hash)
+{
+ if (GNUNET_MESSENGER_KIND_UNKNOWN == message->header.kind)
+ return GNUNET_SYSERR;
+
+ struct GNUNET_MESSENGER_SrvContact *contact = GNUNET_CONTAINER_multishortmap_get (room->members,
+ &(message->header.sender_id));
+
+ if (!contact)
+ {
+ if (GNUNET_MESSENGER_KIND_INFO == message->header.kind)
+ contact = get_service_contact_by_pubkey (room->service, &(message->body.info.host_key));
+ else if (GNUNET_MESSENGER_KIND_JOIN == message->header.kind)
+ contact = get_service_contact_by_pubkey (room->service, &(message->body.join.key));
+ }
+
+ if ((!contact) || (GNUNET_SYSERR == verify_message (message, hash, get_contact_key (contact))))
+ return GNUNET_SYSERR;
+
+ if (GNUNET_YES == room->strict_access)
+ {
+ struct GNUNET_MESSENGER_MemberInfo *info = GNUNET_CONTAINER_multishortmap_get (room->member_infos,
+ &(message->header.sender_id));
+
+ if ((info) && (GNUNET_MESSENGER_MEMBER_BLOCKED == info->access))
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_YES == contains_store_message (&(room->store), hash))
+ return GNUNET_NO;
+
+ return GNUNET_YES;
+}
+
+static void
+search_room_for_message (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_HashCode *hash)
+{
+ const struct GNUNET_MESSENGER_Message *message = get_room_message (room, room->host, hash, GNUNET_YES);
+
+ if (!message)
+ return;
+
+ if (GNUNET_MESSENGER_KIND_MERGE == message->header.kind)
+ search_room_for_message (room, &(message->body.merge.previous));
+
+ search_room_for_message (room, &(message->header.previous));
+}
+
+static void
+idle_request_room_messages (void *cls)
+{
+ struct GNUNET_MESSENGER_SrvRoom *room = cls;
+
+ room->idle = NULL;
+
+ struct GNUNET_MESSENGER_ListMessage *element = room->last_messages.head;
+
+ while (element)
+ {
+ search_room_for_message (room, &(element->hash));
+
+ element = element->next;
+ }
+
+ merge_room_last_messages (room, room->host);
+
+ room->idle = GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_relative_get_second_ (),
+ GNUNET_SCHEDULER_PRIORITY_IDLE, idle_request_room_messages,
+ cls);
+}
+
+void
+update_room_last_messages (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_MESSENGER_Message *message,
+ const struct GNUNET_HashCode *hash)
+{
+ struct GNUNET_MESSENGER_ListMessage *element = room->last_messages.head;
+ struct GNUNET_MESSENGER_ListMessage *merging = NULL;
+
+ if (GNUNET_MESSENGER_KIND_MERGE == message->header.kind)
+ {
+ merging = room->last_messages.head;
+
+ while (merging)
+ {
+ if (0 == GNUNET_CRYPTO_hash_cmp (&(merging->hash), &(message->body.merge.previous)))
+ break;
+
+ merging = merging->next;
+ }
+
+ if (merging)
+ element = merging->next;
+ }
+
+ while (element)
+ {
+ if (0 == GNUNET_CRYPTO_hash_cmp (&(element->hash), &(message->header.previous)))
+ break;
+
+ element = element->next;
+ }
+
+ if ((merging) && (!element))
+ {
+ element = merging;
+ merging = NULL;
+ }
+
+ if (element)
+ {
+ GNUNET_memcpy(&(element->hash), hash, sizeof(struct GNUNET_HashCode));
+
+ if (merging)
+ GNUNET_CONTAINER_DLL_remove(room->last_messages.head, room->last_messages.tail, merging);
+ }
+ else
+ add_to_list_messages (&(room->last_messages), hash);
+
+ if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (room->requested, hash))
+ GNUNET_CONTAINER_multihashmap_remove_all (room->requested, hash);
+}
+
+void
+switch_room_member_id (struct GNUNET_MESSENGER_SrvRoom *room, const struct GNUNET_ShortHashCode *old_id,
+ const struct GNUNET_ShortHashCode *new_id, const struct GNUNET_HashCode *hash)
+{
+ struct GNUNET_MESSENGER_SrvContact *contact = GNUNET_CONTAINER_multishortmap_get (room->members, old_id);
+
+ if ((contact) && (GNUNET_YES == GNUNET_CONTAINER_multishortmap_remove (room->members, old_id, contact)))
+ GNUNET_CONTAINER_multishortmap_put (room->members, new_id, contact,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+
+ struct GNUNET_MESSENGER_MemberInfo *info = GNUNET_CONTAINER_multishortmap_get (room->member_infos, old_id);
+
+ if ((!info) || (GNUNET_YES != GNUNET_CONTAINER_multishortmap_remove (room->member_infos, old_id, contact))
+ || (GNUNET_YES != GNUNET_CONTAINER_multishortmap_put (room->member_infos, new_id, contact,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)))
+ return;
+
+ if (hash)
+ add_to_list_messages (&(info->session_messages), hash);
+}
+
+void
+rebuild_room_basement_structure (struct GNUNET_MESSENGER_SrvRoom *room)
+{
+ struct GNUNET_PeerIdentity peer;
+ size_t src;
+
+ if ((GNUNET_OK != get_service_peer_identity (room->service, &peer)) || (!find_list_tunnels (&(room->basement), &peer,
+ &src)))
+ return;
+
+ size_t count = count_of_tunnels (&(room->basement));
+
+ struct GNUNET_MESSENGER_ListTunnel *element = room->basement.head;
+ struct GNUNET_MESSENGER_SrvTunnel *tunnel;
+
+ size_t dst = 0;
+
+ while (element)
+ {
+ GNUNET_PEER_resolve (element->peer, &peer);
+
+ tunnel = GNUNET_CONTAINER_multipeermap_get (room->tunnels, &peer);
+
+ if (!tunnel)
+ {
+ element = remove_from_list_tunnels (&(room->basement), element);
+ continue;
+ }
+
+ if (GNUNET_YES == required_connection_between (count, src, dst))
+ {
+ if (GNUNET_SYSERR == connect_tunnel (tunnel))
+ {
+ element = remove_from_list_tunnels (&(room->basement), element);
+ continue;
+ }
+ }
+ else
+ disconnect_tunnel (tunnel);
+
+ element = element->next;
+ dst++;
+ }
+}
+
+void
+handle_room_messages (struct GNUNET_MESSENGER_SrvRoom *room)
+{
+ while (room->handling.head)
+ {
+ struct GNUNET_MESSENGER_ListMessage *element = room->handling.head;
+
+ const struct GNUNET_MESSENGER_Message *msg = get_room_message (room, room->host, &(element->hash), GNUNET_NO);
+
+ if (msg)
+ handle_service_message (room->service, room, msg, &(element->hash));
+
+ GNUNET_CONTAINER_DLL_remove(room->handling.head, room->handling.tail, element);
+ GNUNET_free(element);
+ }
+}
+
+#include "gnunet-service-messenger_message_recv.h"
+
+void
+callback_room_recv (struct GNUNET_MESSENGER_SrvRoom *room, void *cls, struct GNUNET_MESSENGER_Message *message,
+ const struct GNUNET_HashCode *hash)
+{
+ struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls;
+
+ if (GNUNET_OK != put_store_message (&(room->store), hash, message))
+ return;
+
+ update_room_last_messages (room, message, hash);
+
+ if (GNUNET_MESSENGER_KIND_INFO != message->header.kind)
+ forward_room_message (room, tunnel, message, hash);
+
+ const int start_handle = room->handling.head ? GNUNET_NO : GNUNET_YES;
+
+ add_to_list_messages (&(room->handling), hash);
+
+ switch (message->header.kind)
+ {
+ case GNUNET_MESSENGER_KIND_INFO:
+ recv_message_info (room, tunnel, message, hash);
+ break;
+ case GNUNET_MESSENGER_KIND_JOIN:
+ recv_message_join (room, tunnel, message, hash);
+ break;
+ case GNUNET_MESSENGER_KIND_LEAVE:
+ recv_message_leave (room, tunnel, message, hash);
+ break;
+ case GNUNET_MESSENGER_KIND_NAME:
+ recv_message_name (room, tunnel, message, hash);
+ break;
+ case GNUNET_MESSENGER_KIND_KEY:
+ recv_message_key (room, tunnel, message, hash);
+ break;
+ case GNUNET_MESSENGER_KIND_PEER:
+ recv_message_peer (room, tunnel, message, hash);
+ break;
+ case GNUNET_MESSENGER_KIND_ID:
+ recv_message_id (room, tunnel, message, hash);
+ break;
+ case GNUNET_MESSENGER_KIND_MISS:
+ recv_message_miss (room, tunnel, message, hash);
+ break;
+ case GNUNET_MESSENGER_KIND_REQUEST:
+ recv_message_request (room, tunnel, message, hash);
+ break;
+ default:
+ break;
+ }
+
+ if (GNUNET_YES == start_handle)
+ handle_room_messages (room);
+}
+
+#include "gnunet-service-messenger_message_send.h"
+
+void
+callback_room_sent (struct GNUNET_MESSENGER_SrvRoom *room, struct GNUNET_MESSENGER_SrvHandle *handle, void *cls,
+ struct GNUNET_MESSENGER_Message *message, const struct GNUNET_HashCode *hash)
+{
+ const struct GNUNET_MESSENGER_Message *old_message = get_room_message (room, handle, hash, GNUNET_NO);
+
+ if ((old_message) || (GNUNET_OK != put_store_message (&(room->store), hash, message)))
+ {
+ if (old_message != message)
+ GNUNET_free(message);
+ }
+ else
+ {
+ struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls; // may be NULL
+
+ update_room_last_messages (room, message, hash);
+
+ const int start_handle = room->handling.head ? GNUNET_NO : GNUNET_YES;
+
+ add_to_list_messages (&(room->handling), hash);
+
+ switch (message->header.kind)
+ {
+ case GNUNET_MESSENGER_KIND_INFO:
+ send_message_info (room, handle, tunnel, message, hash);
+ break;
+ case GNUNET_MESSENGER_KIND_JOIN:
+ send_message_join (room, handle, tunnel, message, hash);
+ break;
+ case GNUNET_MESSENGER_KIND_LEAVE:
+ send_message_leave (room, handle, tunnel, message, hash);
+ break;
+ case GNUNET_MESSENGER_KIND_NAME:
+ send_message_name (room, handle, tunnel, message, hash);
+ break;
+ case GNUNET_MESSENGER_KIND_KEY:
+ send_message_key (room, handle, tunnel, message, hash);
+ break;
+ case GNUNET_MESSENGER_KIND_PEER:
+ send_message_peer (room, handle, tunnel, message, hash);
+ break;
+ case GNUNET_MESSENGER_KIND_ID:
+ send_message_id (room, handle, tunnel, message, hash);
+ break;
+ case GNUNET_MESSENGER_KIND_MISS:
+ send_message_miss (room, handle, tunnel, message, hash);
+ break;
+ default:
+ break;
+ }
+
+ if (GNUNET_YES == start_handle)
+ handle_room_messages (room);
+ }
+}