gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnunet] branch master updated: -remerge branch 'jacki/messenger'


From: gnunet
Subject: [gnunet] branch master updated: -remerge branch 'jacki/messenger'
Date: Thu, 12 Nov 2020 21:10:34 +0100

This is an automated email from the git hooks/post-receive script.

thejackimonster pushed a commit to branch master
in repository gnunet.

The following commit(s) were added to refs/heads/master by this push:
     new ca912f85d -remerge branch 'jacki/messenger'
ca912f85d is described below

commit ca912f85dae6b61dd80ab02d0e3f0b20a556da7c
Author: TheJackiMonster <thejackimonster@gmail.com>
AuthorDate: Thu Nov 12 20:58:07 2020 +0100

    -remerge branch 'jacki/messenger'
    
    This reverts commit e11d1e59e4ae5f7d89c33df3ae9ca8f1ece990cf.
---
 README.1st                                         |    1 +
 configure.ac                                       |    2 +
 pkgconfig/gnunetmessenger.pc.in                    |   12 +
 po/POTFILES.in                                     |   23 +
 src/Makefile.am                                    |    3 +-
 src/identity/identity_api.c                        |   22 +-
 src/include/Makefile.am                            |    1 +
 src/include/gnunet_messenger_service.h             |  436 ++++++++
 src/include/gnunet_protocols.h                     |   43 +-
 src/messenger/.gitignore                           |    4 +
 src/messenger/Makefile.am                          |  131 +++
 src/messenger/gnunet-messenger.c                   |  306 ++++++
 src/messenger/gnunet-service-messenger.c           |  306 ++++++
 src/messenger/gnunet-service-messenger.h           |  121 +++
 src/messenger/gnunet-service-messenger_basement.c  |   58 ++
 src/messenger/gnunet-service-messenger_basement.h  |   66 ++
 src/messenger/gnunet-service-messenger_contact.c   |   96 ++
 src/messenger/gnunet-service-messenger_contact.h   |  112 +++
 src/messenger/gnunet-service-messenger_handle.c    |  503 ++++++++++
 src/messenger/gnunet-service-messenger_handle.h    |  216 ++++
 .../gnunet-service-messenger_list_handles.c        |   95 ++
 .../gnunet-service-messenger_list_handles.h        |   96 ++
 .../gnunet-service-messenger_list_messages.c       |   76 ++
 .../gnunet-service-messenger_list_messages.h       |   81 ++
 .../gnunet-service-messenger_message_handle.c      |  130 +++
 .../gnunet-service-messenger_message_handle.h      |  128 +++
 .../gnunet-service-messenger_message_kind.c        |  192 ++++
 .../gnunet-service-messenger_message_kind.h        |  160 +++
 .../gnunet-service-messenger_message_recv.c        |  204 ++++
 .../gnunet-service-messenger_message_recv.h        |  159 +++
 .../gnunet-service-messenger_message_send.c        |  118 +++
 .../gnunet-service-messenger_message_send.h        |  155 +++
 .../gnunet-service-messenger_message_store.c       |  282 ++++++
 .../gnunet-service-messenger_message_store.h       |  120 +++
 src/messenger/gnunet-service-messenger_room.c      | 1051 ++++++++++++++++++++
 src/messenger/gnunet-service-messenger_room.h      |  378 +++++++
 src/messenger/gnunet-service-messenger_service.c   |  516 ++++++++++
 src/messenger/gnunet-service-messenger_service.h   |  259 +++++
 src/messenger/gnunet-service-messenger_tunnel.c    |  300 ++++++
 src/messenger/gnunet-service-messenger_tunnel.h    |  155 +++
 src/messenger/gnunet-service-messenger_util.c      |   64 ++
 src/messenger/gnunet-service-messenger_util.h      |   53 +
 src/messenger/messenger.conf.in                    |   13 +
 src/messenger/messenger_api.c                      |  568 +++++++++++
 src/messenger/messenger_api_contact.c              |   78 ++
 src/messenger/messenger_api_contact.h              |   93 ++
 src/messenger/messenger_api_ego.h                  |   38 +
 src/messenger/messenger_api_handle.c               |  213 ++++
 src/messenger/messenger_api_handle.h               |  174 ++++
 src/messenger/messenger_api_list_tunnels.c         |  112 +++
 src/messenger/messenger_api_list_tunnels.h         |  112 +++
 src/messenger/messenger_api_message.c              |  602 +++++++++++
 src/messenger/messenger_api_message.h              |  190 ++++
 src/messenger/messenger_api_room.c                 |  189 ++++
 src/messenger/messenger_api_room.h                 |   95 ++
 src/messenger/test_messenger.c                     |  187 ++++
 src/messenger/test_messenger_anonymous.c           |  179 ++++
 src/messenger/test_messenger_comm0.c               |  252 +++++
 58 files changed, 10315 insertions(+), 14 deletions(-)

diff --git a/README.1st b/README.1st
index 3d000a23f..c25992351 100644
--- a/README.1st
+++ b/README.1st
@@ -253,4 +253,5 @@ We reserve judgement (due to lack of data) on:
 * credential
 * abe
 * rest
+* messenger
 
diff --git a/configure.ac b/configure.ac
index 581764b52..a9594a56c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1975,6 +1975,8 @@ src/zonemaster/zonemaster.conf
 src/rest/Makefile
 src/abe/Makefile
 src/reclaim/Makefile
+src/messenger/Makefile
+src/messenger/messenger.conf
 pkgconfig/Makefile
 pkgconfig/gnunetarm.pc
 pkgconfig/gnunetats.pc
diff --git a/pkgconfig/gnunetmessenger.pc.in b/pkgconfig/gnunetmessenger.pc.in
new file mode 100644
index 000000000..465663d0c
--- /dev/null
+++ b/pkgconfig/gnunetmessenger.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: GNUnet MESSENGER
+Description: Instant messaging based on the CADET subsystem
+URL: https://gnunet.org
+Version: @VERSION@
+Requires:
+Libs: -L${libdir} -lgnunetmessenger_common -lgnunetmessenger
+Cflags: -I${includedir}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 35bd71771..00bdd7cbc 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -204,6 +204,28 @@ src/json/json.c
 src/json/json_generator.c
 src/json/json_helper.c
 src/json/json_mhd.c
+src/messenger/gnunet-messenger.c
+src/messenger/gnunet-service-messenger.c
+src/messenger/gnunet-service-messenger_basement.c
+src/messenger/gnunet-service-messenger_contact.c
+src/messenger/gnunet-service-messenger_handle.c
+src/messenger/gnunet-service-messenger_list_handles.c
+src/messenger/gnunet-service-messenger_list_messages.c
+src/messenger/gnunet-service-messenger_message_handle.c
+src/messenger/gnunet-service-messenger_message_kind.c
+src/messenger/gnunet-service-messenger_message_recv.c
+src/messenger/gnunet-service-messenger_message_send.c
+src/messenger/gnunet-service-messenger_message_store.c
+src/messenger/gnunet-service-messenger_room.c
+src/messenger/gnunet-service-messenger_service.c
+src/messenger/gnunet-service-messenger_tunnel.c
+src/messenger/gnunet-service-messenger_util.c
+src/messenger/messenger_api.c
+src/messenger/messenger_api_contact.c
+src/messenger/messenger_api_handle.c
+src/messenger/messenger_api_list_tunnels.c
+src/messenger/messenger_api_message.c
+src/messenger/messenger_api_room.c
 src/my/my.c
 src/my/my_query_helper.c
 src/my/my_result_helper.c
@@ -524,5 +546,6 @@ src/zonemaster/gnunet-service-zonemaster.c
 src/zonemaster/gnunet-service-zonemaster-monitor.c
 src/fs/fs_api.h
 src/include/gnunet_identity_service.h
+src/include/gnunet_messenger_service.h
 src/testbed/testbed_api.h
 src/testbed/testbed_api_operations.h
diff --git a/src/Makefile.am b/src/Makefile.am
index f98cb96df..d8a869acb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -11,7 +11,8 @@ endif
 
 if HAVE_EXPERIMENTAL
  EXP_DIR = \
-  rps
+  rps \
+  messenger
   #abd FTBFS
 if HAVE_ABE
  EXP_DIR += \
diff --git a/src/identity/identity_api.c b/src/identity/identity_api.c
index d44e8da96..64c088923 100644
--- a/src/identity/identity_api.c
+++ b/src/identity/identity_api.c
@@ -1138,11 +1138,11 @@ GNUNET_IDENTITY_signature_verify_ (uint32_t purpose,
 
 
 ssize_t
-GNUNET_IDENTITY_public_key_encrypt (const void *block,
-                                    size_t size,
-                                    const struct GNUNET_IDENTITY_PublicKey 
*pub,
-                                    struct GNUNET_CRYPTO_EcdhePublicKey *ecc,
-                                    void *result)
+GNUNET_IDENTITY_encrypt (const void *block,
+                         size_t size,
+                         const struct GNUNET_IDENTITY_PublicKey *pub,
+                         struct GNUNET_CRYPTO_EcdhePublicKey *ecc,
+                         void *result)
 {
   struct GNUNET_CRYPTO_EcdhePrivateKey pk;
   GNUNET_CRYPTO_ecdhe_key_create (&pk);
@@ -1177,13 +1177,11 @@ GNUNET_IDENTITY_public_key_encrypt (const void *block,
 
 
 ssize_t
-GNUNET_IDENTITY_private_key_decrypt (const void *block,
-                                     size_t size,
-                                     const struct
-                                     GNUNET_IDENTITY_PrivateKey *priv,
-                                     const struct
-                                     GNUNET_CRYPTO_EcdhePublicKey *ecc,
-                                     void *result)
+GNUNET_IDENTITY_decrypt (const void *block,
+                         size_t size,
+                         const struct GNUNET_IDENTITY_PrivateKey *priv,
+                         const struct GNUNET_CRYPTO_EcdhePublicKey *ecc,
+                         void *result)
 {
   struct GNUNET_HashCode hash;
   switch (ntohl (priv->type))
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
index 202abb7ac..fc3d745a6 100644
--- a/src/include/Makefile.am
+++ b/src/include/Makefile.am
@@ -62,6 +62,7 @@ gnunetinclude_HEADERS = \
   gnunet_json_lib.h \
   gnunet_load_lib.h \
   gnunet_cadet_service.h \
+  gnunet_messenger_service.h \
   gnunet_mhd_compat.h \
   gnunet_microphone_lib.h \
   gnunet_mst_lib.h \
diff --git a/src/include/gnunet_messenger_service.h 
b/src/include/gnunet_messenger_service.h
new file mode 100644
index 000000000..8f5315c30
--- /dev/null
+++ b/src/include/gnunet_messenger_service.h
@@ -0,0 +1,436 @@
+/*
+   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
+ * MESSENGER service; manages decentralized chat groups
+ *
+ * @defgroup messenger  MESSENGER service
+ * Instant messaging based on the CADET subsystem
+ *
+ * @{
+ */
+
+#ifndef GNUNET_MESSENGER_SERVICE_H
+#define GNUNET_MESSENGER_SERVICE_H
+
+#ifdef __cplusplus
+extern "C" {
+#if 0 /* keep Emacsens' auto-indent happy */
+}
+#endif
+#endif
+
+#include "platform.h"
+#include "gnunet_configuration_lib.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_identity_service.h"
+#include "gnunet_mq_lib.h"
+#include "gnunet_protocols.h"
+#include "gnunet_scheduler_lib.h"
+#include "gnunet_time_lib.h"
+
+#define GNUNET_MESSENGER_SERVICE_NAME "messenger"
+
+/**
+ * Opaque handle to the messenger
+ */
+struct GNUNET_MESSENGER_Handle;
+
+/**
+ * Opaque handle to a room
+ */
+struct GNUNET_MESSENGER_Room;
+
+/**
+ * Opaque handle to a contact
+ */
+struct GNUNET_MESSENGER_Contact;
+
+/**
+ * Enum for the different supported kinds of messages
+ */
+enum GNUNET_MESSENGER_MessageKind
+{
+  GNUNET_MESSENGER_KIND_INFO = 1,
+
+  GNUNET_MESSENGER_KIND_JOIN = 2,
+  GNUNET_MESSENGER_KIND_LEAVE = 3,
+
+  GNUNET_MESSENGER_KIND_NAME = 4,
+  GNUNET_MESSENGER_KIND_KEY = 5,
+  GNUNET_MESSENGER_KIND_PEER = 6,
+  GNUNET_MESSENGER_KIND_ID = 7,
+
+  GNUNET_MESSENGER_KIND_MISS = 8,
+  GNUNET_MESSENGER_KIND_MERGE = 9,
+  GNUNET_MESSENGER_KIND_REQUEST = 10,
+
+  GNUNET_MESSENGER_KIND_INVITE = 11,
+  GNUNET_MESSENGER_KIND_TEXT = 12,
+  GNUNET_MESSENGER_KIND_FILE = 13,
+
+  GNUNET_MESSENGER_KIND_PRIVATE = 14,
+
+  GNUNET_MESSENGER_KIND_UNKNOWN = 0
+}__attribute__((__packed__));
+
+/**
+ * Get the name of a message <i>kind</i>.
+ *
+ * @param kind Kind of a message
+ * @return Name of that kind
+ */
+const char*
+GNUNET_MESSENGER_name_of_kind (enum GNUNET_MESSENGER_MessageKind kind);
+
+struct GNUNET_MESSENGER_MessageHeader
+{
+  struct GNUNET_IDENTITY_Signature signature;
+
+  struct GNUNET_TIME_AbsoluteNBO timestamp;
+
+  struct GNUNET_ShortHashCode sender_id;
+  struct GNUNET_HashCode previous;
+
+  enum GNUNET_MESSENGER_MessageKind kind;
+};
+
+struct GNUNET_MESSENGER_MessageInfo
+{
+  struct GNUNET_IDENTITY_PublicKey host_key;
+  struct GNUNET_ShortHashCode unique_id;
+};
+
+struct GNUNET_MESSENGER_MessageJoin
+{
+  struct GNUNET_IDENTITY_PublicKey key;
+};
+
+struct GNUNET_MESSENGER_MessageLeave
+{
+};
+
+struct GNUNET_MESSENGER_MessageName
+{
+  char *name;
+};
+
+struct GNUNET_MESSENGER_MessageKey
+{
+  struct GNUNET_IDENTITY_PublicKey key;
+};
+
+struct GNUNET_MESSENGER_MessagePeer
+{
+  struct GNUNET_PeerIdentity peer;
+};
+
+struct GNUNET_MESSENGER_MessageId
+{
+  struct GNUNET_ShortHashCode id;
+};
+
+struct GNUNET_MESSENGER_MessageMiss
+{
+  struct GNUNET_PeerIdentity peer;
+};
+
+struct GNUNET_MESSENGER_MessageMerge
+{
+  struct GNUNET_HashCode previous;
+};
+
+struct GNUNET_MESSENGER_MessageRequest
+{
+  struct GNUNET_HashCode hash;
+};
+
+struct GNUNET_MESSENGER_MessageInvite
+{
+  struct GNUNET_PeerIdentity door;
+  struct GNUNET_HashCode key;
+};
+
+struct GNUNET_MESSENGER_MessageText
+{
+  char *text;
+};
+
+struct GNUNET_MESSENGER_MessageFile
+{
+  struct GNUNET_CRYPTO_SymmetricSessionKey key;
+  struct GNUNET_HashCode hash;
+  char name[NAME_MAX];
+  char *uri;
+};
+
+struct GNUNET_MESSENGER_MessagePrivate
+{
+  struct GNUNET_CRYPTO_EcdhePublicKey key;
+
+  uint16_t length;
+  char *data;
+};
+
+struct GNUNET_MESSENGER_MessageBody
+{
+  union
+  {
+    struct GNUNET_MESSENGER_MessageInfo info;
+
+    struct GNUNET_MESSENGER_MessageJoin join;
+    struct GNUNET_MESSENGER_MessageLeave leave;
+
+    struct GNUNET_MESSENGER_MessageName name;
+    struct GNUNET_MESSENGER_MessageKey key;
+    struct GNUNET_MESSENGER_MessagePeer peer;
+    struct GNUNET_MESSENGER_MessageId id;
+
+    struct GNUNET_MESSENGER_MessageMiss miss;
+    struct GNUNET_MESSENGER_MessageMerge merge;
+    struct GNUNET_MESSENGER_MessageRequest request;
+
+    struct GNUNET_MESSENGER_MessageInvite invite;
+    struct GNUNET_MESSENGER_MessageText text;
+    struct GNUNET_MESSENGER_MessageFile file;
+
+    struct GNUNET_MESSENGER_MessagePrivate private;
+  };
+};
+
+/**
+ * Struct to a message
+ */
+struct GNUNET_MESSENGER_Message
+{
+  struct GNUNET_MESSENGER_MessageHeader header;
+  struct GNUNET_MESSENGER_MessageBody body;
+};
+
+/**
+ * Method called whenever the EGO of a <i>handle</i> changes or if the first 
connection fails
+ * to load a valid EGO and the anonymous keypair will be used instead.
+ *
+ * @param cls Closure from <i>GNUNET_MESSENGER_connect</i>
+ * @param handle Messenger handle
+ */
+typedef void
+(*GNUNET_MESSENGER_IdentityCallback) (void *cls, struct 
GNUNET_MESSENGER_Handle *handle);
+
+/**
+ * Method called whenever a message is sent or received from a <i>room</i>.
+ *
+ * @param cls Closure from <i>GNUNET_MESSENGER_connect</i>
+ * @param room Room handle
+ * @param message Newly received or sent message
+ * @param hash Hash identifying the message
+ */
+typedef void
+(*GNUNET_MESSENGER_MessageCallback) (void *cls, const struct 
GNUNET_MESSENGER_Room *room,
+                                     const struct GNUNET_MESSENGER_Message 
*message, const struct GNUNET_HashCode *hash);
+
+/**
+ * Set up a handle for the messenger related functions and connects to all 
necessary services. It will look up the ego
+ * key identified by its <i>name</i> and use it for signing all messages from 
the handle.
+ *
+ * @param cfg Configuration to use
+ * @param name Name to look up an ego or NULL to stay anonymous
+ * @param identity_callback Function called when the EGO of the handle changes
+ * @param identity_cls Closure for the <i>identity_callback</i> handler
+ * @param msg_callback Function called when a new message is sent or received
+ * @param msg_cls Closure for the <i>msg_callback</i> handler
+ * @return Messenger handle to use, NULL on error
+ */
+struct GNUNET_MESSENGER_Handle*
+GNUNET_MESSENGER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, const 
char *name,
+                          GNUNET_MESSENGER_IdentityCallback identity_callback, 
void *identity_cls,
+                          GNUNET_MESSENGER_MessageCallback msg_callback, void 
*msg_cls);
+
+/**
+ * Update a handle of the messenger to use a different ego key and replace the 
old one with a newly generated one. All
+ * participated rooms get informed about the key renewal. The handle requires 
a set name for this function to work and
+ * it needs to be unused by other egos.
+ *
+ * Keep in mind that this will fully delete the old ego key (if any is used) 
even if any other service wants to use it
+ * as default.
+ *
+ * @param handle Messenger handle to use
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure
+ */
+int
+GNUNET_MESSENGER_update (struct GNUNET_MESSENGER_Handle *handle);
+
+/**
+ * Disconnect all of the messengers used services and clears up its used 
memory.
+ *
+ * @param handle Messenger handle to use
+ */
+void
+GNUNET_MESSENGER_disconnect (struct GNUNET_MESSENGER_Handle *handle);
+
+/**
+ * Get the name (if specified, otherwise NULL) used by the messenger.
+ *
+ * @param handle Messenger handle to use
+ * @return Name used by the messenger or NULL
+ */
+const char*
+GNUNET_MESSENGER_get_name (const struct GNUNET_MESSENGER_Handle *handle);
+
+/**
+ * Set the name for the messenger. This will rename the currently used ego and 
move all stored files related to the current
+ * name to its new directory. If anything fails during this process the 
function returns GNUNET_NO and the name for
+ * the messenger won't change as specified.
+ *
+ * @param handle Messenger handle to use
+ * @param name Name for the messenger to change to
+ * @return GNUNET_YES on success, GNUNET_NO on failure and GNUNET_SYSERR if 
<i>handle</i> is NULL
+ */
+int
+GNUNET_MESSENGER_set_name (struct GNUNET_MESSENGER_Handle *handle, const char 
*name);
+
+/**
+ * Get the public key used by the messenger.
+ *
+ * @param handle Messenger handle to use
+ * @return Used ego's public key
+ */
+const struct GNUNET_IDENTITY_PublicKey*
+GNUNET_MESSENGER_get_key (const struct GNUNET_MESSENGER_Handle *handle);
+
+/**
+ * Open a room to send and receive messages. The room will use the specified 
<i>key</i> as port for the underlying cadet
+ * service. Opening a room results in opening the port for incoming 
connections as possible <b>door</b>.
+ *
+ * Notice that there can only be one room related to a specific <i>key</i>. So 
trying to open two rooms with the same
+ * <i>key</i> will result in opening the room once but returning the handle 
both times because the room stays open.
+ *
+ * You can also open a room after entering it through a <b>door</b> using 
<i>GNUNET_MESSENGER_entry_room(...)</i>. This
+ * will notify all entered <b>doors</b> to list you as new <b>door</b>.
+ *
+ * ( All <b>doors</b> form a ring structured network to shorten the latency 
sending and receiving messages. )
+ *
+ * @param handle Messenger handle to use
+ * @param key Hash identifying the port
+ * @return Room handle, NULL on error
+ */
+struct GNUNET_MESSENGER_Room*
+GNUNET_MESSENGER_open_room (struct GNUNET_MESSENGER_Handle *handle, const 
struct GNUNET_HashCode *key);
+
+/**
+ * Enter a room to send and receive messages through a <b>door</b> opened 
using <i>GNUNET_MESSENGER_open_room(...)</i>.
+ *
+ * Notice that there can only be one room related to a specific <i>key</i>. So 
trying to enter two rooms with the same
+ * <i>key</i> will result in entering the room once but returning the handle 
both times because the room stays entered.
+ * You can however enter a room through multiple <b>doors</b> in parallel 
which results in connecting both ends. But
+ * entering the room through the same <b>door</b> won't have any effect after 
the first time.
+ *
+ * You can also enter a room through a <b>door</b> after opening it using 
<i>GNUNET_MESSENGER_open_room(...)</i>. But the
+ * <b>door</b> may not be your own peer identity.
+ *
+ * ( All <b>doors</b> form a ring structured network to shorten the latency 
sending and receiving messages. )
+ *
+ * @param handle Messenger handle to use
+ * @param door Peer identity of an open <b>door</b>
+ * @param key Hash identifying the port
+ * @return Room handle, NULL on error
+ */
+struct GNUNET_MESSENGER_Room*
+GNUNET_MESSENGER_entry_room (struct GNUNET_MESSENGER_Handle *handle, const 
struct GNUNET_PeerIdentity *door,
+                             const struct GNUNET_HashCode *key);
+
+/**
+ * Close a room which was entered, opened or both in various order and 
variety. Closing a room will destroy all connections
+ * from your peer to another and the other way around.
+ *
+ * ( After a member closes a <b>door</b>, all members entered through that 
specific <b>door</b> have to use another one
+ * or open the room on their own. )
+ *
+ * @param room Room handle
+ */
+void
+GNUNET_MESSENGER_close_room (struct GNUNET_MESSENGER_Room *room);
+
+/**
+ * Get the contact of a member in a <i>room</i> identified by their <i>id</i>.
+ *
+ * Notice that contacts are independent of rooms but will be removed if all 
rooms containing these contacts get closed.
+ *
+ * @param room Room handle
+ * @param id Hash identifying a member
+ * @return Contact handle, NULL if <i>id</i> is not in use
+ */
+struct GNUNET_MESSENGER_Contact*
+GNUNET_MESSENGER_get_member (const struct GNUNET_MESSENGER_Room *room, const 
struct GNUNET_ShortHashCode *id);
+
+/**
+ * Get the name used by the <i>contact</i>.
+ *
+ * @param contact Contact handle
+ * @return Name of <i>contact</i> or NULL
+ */
+const char*
+GNUNET_MESSENGER_contact_get_name (const struct GNUNET_MESSENGER_Contact 
*contact);
+
+/**
+ * Get the public key used by the <i>contact</i>.
+ *
+ * @param contact Contact handle
+ * @return Public key of the ego used by <i>contact</i>
+ */
+const struct GNUNET_IDENTITY_PublicKey*
+GNUNET_MESSENGER_contact_get_key (const struct GNUNET_MESSENGER_Contact 
*contact);
+
+/**
+ * Send a <i>message</i> into a </i>room</i>. If you opened the <i>room</i> 
all entered members will receive the
+ * <i>message</i>. If you entered the <i>room</i> through a <b>door</b> all so 
entered <b>doors</b> will receive the
+ * <i>message</i> as well. All members receiving the <i>message</i> will also 
propagate this <i>message</i> recursively
+ * as long as the <i>message</i> is unknown to them.
+ *
+ * Notice that all messages sent and received are also stored and can be 
propagated to new members entering the room.
+ *
+ * @param room Room handle
+ * @param message New message to send
+ */
+void
+GNUNET_MESSENGER_send_message (struct GNUNET_MESSENGER_Room *room, const 
struct GNUNET_MESSENGER_Message *message);
+
+/**
+ * Get the message in a <i>room</i> identified by its <i>hash</i>.
+ *
+ * @param room Room handle
+ * @param hash Hash identifying a message
+ * @return Message struct or NULL if no message with that hash is known
+ */
+const struct GNUNET_MESSENGER_Message*
+GNUNET_MESSENGER_get_message (const struct GNUNET_MESSENGER_Room *room, const 
struct GNUNET_HashCode *hash);
+
+#if 0 /* keep Emacsens' auto-indent happy */
+{
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+#endif //GNUNET_MESSENGER_SERVICE_H
+
+/** @} *//* end of group */
diff --git a/src/include/gnunet_protocols.h b/src/include/gnunet_protocols.h
index e9b81a654..9aa029e9d 100644
--- a/src/include/gnunet_protocols.h
+++ b/src/include/gnunet_protocols.h
@@ -1,6 +1,6 @@
 /*
      This file is part of GNUnet.
-     Copyright (C) 2001--2018 GNUnet e.V.
+     Copyright (C) 2001--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
@@ -20,6 +20,7 @@
 
 /**
  * @author Christian Grothoff
+ * @author Tobias Frisch
  *
  * @file
  * Constants for network protocols
@@ -3508,6 +3509,46 @@ extern "C" {
 #define GNUNET_MESSAGE_TYPE_RECLAIM_REFERENCE_RESULT 1501
 
 
+/*********************************************************************************/
+/**********************************  MESSENGER  
**********************************/
+/*********************************************************************************/
+/* MESSENGER: message types 1600-1629
+ * 1600-1609 Connection-level Messages
+ * 1610-1619 Room-level Messages
+ */
+
+/*********************************  Connection  
**********************************/
+
+#define GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_CREATE 1600
+
+#define GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_UPDATE 1601
+
+#define GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_DESTROY 1602
+
+#define GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_GET_NAME 1603
+
+#define GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_SET_NAME 1604
+
+#define GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_GET_KEY 1605
+
+#define GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_MEMBER_ID 1606
+
+/************************************  Room  
*************************************/
+
+#define GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_OPEN 1610
+
+#define GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_ENTRY 1611
+
+#define GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_CLOSE 1612
+
+#define GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_SEND_MESSAGE 1614
+
+#define GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_RECV_MESSAGE 1615
+
+#define GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_GET_MESSAGE 1616
+
+/*********************************************************************************/
+
 /**
  * Type used to match 'all' message types.
  */
diff --git a/src/messenger/.gitignore b/src/messenger/.gitignore
new file mode 100644
index 000000000..9de3fb304
--- /dev/null
+++ b/src/messenger/.gitignore
@@ -0,0 +1,4 @@
+gnunet-service-messenger
+gnunet-messenger
+test_messenger_api
+test_messenger_anonymous
diff --git a/src/messenger/Makefile.am b/src/messenger/Makefile.am
new file mode 100644
index 000000000..ebe08290e
--- /dev/null
+++ b/src/messenger/Makefile.am
@@ -0,0 +1,131 @@
+# This Makefile.am is in the public domain
+AM_CPPFLAGS = -I$(top_srcdir)/src/include
+
+if USE_COVERAGE
+  AM_CFLAGS = --coverage -O0
+  XLIB = -lgcov
+endif
+
+pkgcfgdir= $(pkgdatadir)/config.d/
+
+libexecdir= $(pkglibdir)/libexec/
+
+pkgcfg_DATA = \
+  messenger.conf
+
+plugindir = $(libdir)/gnunet
+
+AM_CLFAGS = -g
+
+libexec_PROGRAMS = \
+ gnunet-service-messenger \
+ $(EXP_LIBEXEC)
+
+bin_PROGRAMS = \
+ gnunet-messenger
+
+lib_LTLIBRARIES = \
+  libgnunetmessenger_common.la \
+  libgnunetmessenger.la \
+  $(EXP_LIB)
+
+libgnunetmessenger_common_la_SOURCES = \
+  messenger_api_ego.h \
+  messenger_api_message.c messenger_api_message.h \
+  messenger_api_list_tunnels.c messenger_api_list_tunnels.h
+libgnunetmessenger_common_la_LIBADD = \
+  $(top_builddir)/src/util/libgnunetutil.la \
+  $(top_builddir)/src/identity/libgnunetidentity.la \
+  $(XLIB) \
+  $(LTLIBINTL)
+libgnunetmessenger_common_la_LDFLAGS = \
+  $(GN_LIB_LDFLAGS)  \
+  -version-info 0:0:0
+
+libgnunetmessenger_la_SOURCES = \
+  messenger_api.c \
+  messenger_api_contact.c messenger_api_contact.h \
+  messenger_api_handle.c messenger_api_handle.h \
+  messenger_api_room.c messenger_api_room.h
+libgnunetmessenger_la_LIBADD = \
+  $(top_builddir)/src/util/libgnunetutil.la \
+  $(top_builddir)/src/identity/libgnunetidentity.la \
+  libgnunetmessenger_common.la \
+  $(XLIB) \
+  $(LTLIBINTL)
+libgnunetmessenger_la_LDFLAGS = \
+  $(GN_LIB_LDFLAGS)  \
+  -version-info 0:0:0
+
+gnunet_messenger_SOURCES = \
+ gnunet-messenger.c
+gnunet_messenger_LDADD = \
+ libgnunetmessenger_common.la \
+ libgnunetmessenger.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+gnunet_messenger_LDFLAGS = \
+  $(GN_LIBINTL)
+
+gnunet_service_messenger_SOURCES = \
+  gnunet-service-messenger.c gnunet-service-messenger.h \
+  gnunet-service-messenger_service.c gnunet-service-messenger_service.h \
+  gnunet-service-messenger_list_handles.c 
gnunet-service-messenger_list_handles.h \
+  gnunet-service-messenger_list_messages.c 
gnunet-service-messenger_list_messages.h \
+  gnunet-service-messenger_message_handle.c 
gnunet-service-messenger_message_handle.h \
+  gnunet-service-messenger_message_kind.c 
gnunet-service-messenger_message_kind.h \
+  gnunet-service-messenger_message_recv.c 
gnunet-service-messenger_message_recv.h \
+  gnunet-service-messenger_message_send.c 
gnunet-service-messenger_message_send.h \
+  gnunet-service-messenger_message_store.c 
gnunet-service-messenger_message_store.h \
+  gnunet-service-messenger_basement.c gnunet-service-messenger_basement.h \
+  gnunet-service-messenger_contact.c gnunet-service-messenger_contact.h \
+  gnunet-service-messenger_handle.c gnunet-service-messenger_handle.h \
+  gnunet-service-messenger_room.c gnunet-service-messenger_room.h \
+  gnunet-service-messenger_tunnel.c gnunet-service-messenger_tunnel.h \
+  gnunet-service-messenger_util.c gnunet-service-messenger_util.h
+gnunet_service_messenger_LDADD = \
+  $(top_builddir)/src/util/libgnunetutil.la \
+  $(top_builddir)/src/cadet/libgnunetcadet.la \
+  $(top_builddir)/src/identity/libgnunetidentity.la \
+  libgnunetmessenger_common.la \
+  libgnunetmessenger.la \
+  $(GN_LIBINTL)
+
+check_PROGRAMS = \
+  test_messenger_api \
+  test_messenger_anonymous \
+  test_messenger_comm0
+
+if ENABLE_TEST_RUN
+AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export 
PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset 
XDG_CONFIG_HOME;
+TESTS = \
+ $(check_PROGRAMS)
+endif
+
+test_messenger_api_SOURCES = \
+  test_messenger.c
+test_messenger_api_LDADD = \
+  libgnunetmessenger_common.la \
+  libgnunetmessenger.la \
+  $(top_builddir)/src/testing/libgnunettesting.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+test_messenger_anonymous_SOURCES = \
+  test_messenger_anonymous.c
+test_messenger_anonymous_LDADD = \
+  libgnunetmessenger_common.la \
+  libgnunetmessenger.la \
+  $(top_builddir)/src/testing/libgnunettesting.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+test_messenger_comm0_SOURCES = \
+  test_messenger_comm0.c
+test_messenger_comm0_LDADD = \
+  libgnunetmessenger_common.la \
+  libgnunetmessenger.la \
+  $(top_builddir)/src/testbed/libgnunettestbed.la \
+  $(top_builddir)/src/testbed-logger/libgnunettestbedlogger.la \
+  $(top_builddir)/src/testing/libgnunettesting.la \
+  $(top_builddir)/src/util/libgnunetutil.la
+
+EXTRA_DIST = \
+  test_messenger_api.conf
diff --git a/src/messenger/gnunet-messenger.c b/src/messenger/gnunet-messenger.c
new file mode 100644
index 000000000..579e5c3ad
--- /dev/null
+++ b/src/messenger/gnunet-messenger.c
@@ -0,0 +1,306 @@
+/*
+   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-messenger.c
+ * @brief Print information about messenger groups.
+ */
+
+#include <stdio.h>
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_messenger_service.h"
+
+struct GNUNET_MESSENGER_Handle *messenger;
+
+/**
+ * Function called whenever a message is received or sent.
+ *
+ * @param cls Closure
+ * @param room Room
+ * @param message Message
+ * @param hash Hash of message
+ */
+void
+on_message (void *cls, const struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_MESSENGER_Message *message,
+            const struct GNUNET_HashCode *hash)
+{
+  struct GNUNET_MESSENGER_Contact *sender = GNUNET_MESSENGER_get_member (room, 
&(message->header.sender_id));
+
+  const char *sender_name = GNUNET_MESSENGER_contact_get_name (sender);
+
+  if (!sender_name)
+    sender_name = "anonymous";
+
+  switch (message->header.kind)
+  {
+  case GNUNET_MESSENGER_KIND_JOIN:
+    {
+      printf ("* '%s' joined the room! [ %u %u %u %u ]\n", sender_name, 
message->body.join.key.ecdsa_key.q_y[0],
+              message->body.join.key.ecdsa_key.q_y[1], 
message->body.join.key.ecdsa_key.q_y[2],
+              message->body.join.key.ecdsa_key.q_y[3]);
+      break;
+    }
+  case GNUNET_MESSENGER_KIND_LEAVE:
+    {
+      printf ("* '%s' leaves the room!\n", sender_name);
+      break;
+    }
+  case GNUNET_MESSENGER_KIND_PEER:
+    {
+      printf ("* '%s' opened the room on: %s\n", sender_name, GNUNET_i2s_full 
(&(message->body.peer.peer)));
+      break;
+    }
+  case GNUNET_MESSENGER_KIND_TEXT:
+    {
+      printf ("* '%s' says: \"%s\"\n", sender_name, message->body.text.text);
+      break;
+    }
+  default:
+    {
+      break;
+    }
+  }
+}
+
+struct GNUNET_SCHEDULER_Task *read_task;
+
+/**
+ * Task to shut down this application.
+ *
+ * @param cls Closure
+ */
+static void
+shutdown_hook (void *cls)
+{
+  struct GNUNET_MESSENGER_Room *room = cls;
+
+  if (read_task)
+    GNUNET_SCHEDULER_cancel (read_task);
+
+  if (room)
+    GNUNET_MESSENGER_close_room (room);
+
+  if (messenger)
+    GNUNET_MESSENGER_disconnect (messenger);
+}
+
+static void
+listen_stdio (void *cls);
+
+#define MAX_BUFFER_SIZE 60000
+
+/**
+ * Task run in stdio mode, after some data is available at stdin.
+ *
+ * @param cls Closure
+ */
+static void
+read_stdio (void *cls)
+{
+  read_task = NULL;
+
+  char buffer[MAX_BUFFER_SIZE];
+  ssize_t length;
+
+  length = read (0, buffer, MAX_BUFFER_SIZE);
+
+  if ((length <= 0) || (length >= MAX_BUFFER_SIZE))
+  {
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+
+  if (buffer[length - 1] == '\n')
+    buffer[length - 1] = '\0';
+  else
+    buffer[length] = '\0';
+
+  struct GNUNET_MESSENGER_Room *room = cls;
+
+  struct GNUNET_MESSENGER_Message message;
+  message.header.kind = GNUNET_MESSENGER_KIND_TEXT;
+  message.body.text.text = buffer;
+
+  GNUNET_MESSENGER_send_message (room, &message);
+
+  read_task = GNUNET_SCHEDULER_add_now (listen_stdio, cls);
+}
+
+/**
+ * Wait for input on STDIO and send it out over the #ch.
+ *
+ * @param cls Closure
+ */
+static void
+listen_stdio (void *cls)
+{
+  read_task = NULL;
+
+  struct GNUNET_NETWORK_FDSet *rs = GNUNET_NETWORK_fdset_create ();
+
+  GNUNET_NETWORK_fdset_set_native (rs, 0);
+
+  read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+  GNUNET_TIME_UNIT_FOREVER_REL,
+                                           rs,
+                                           NULL,
+                                           &read_stdio, cls);
+
+  GNUNET_NETWORK_fdset_destroy (rs);
+}
+
+/**
+ * Initial task to startup application.
+ *
+ * @param cls Closure
+ */
+static void
+idle (void *cls)
+{
+  struct GNUNET_MESSENGER_Room *room = cls;
+
+  printf ("* You joined the room.\n");
+
+  read_task = GNUNET_SCHEDULER_add_now (listen_stdio, room);
+}
+
+char *door_id;
+char *ego_name;
+char *room_key;
+
+struct GNUNET_SCHEDULER_Task *shutdown_task;
+
+/**
+ * Function called when an identity is retrieved.
+ *
+ * @param cls Closure
+ * @param handle Handle of messenger service
+ */
+static void
+on_identity (void *cls, struct GNUNET_MESSENGER_Handle *handle)
+{
+  struct GNUNET_HashCode key;
+  memset (&key, 0, sizeof(key));
+
+  if (room_key)
+    GNUNET_CRYPTO_hash (room_key, strlen (room_key), &key);
+
+  struct GNUNET_PeerIdentity *door = NULL;
+
+  if (door_id)
+  {
+    door = GNUNET_new(struct GNUNET_PeerIdentity);
+
+    if (GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (door_id, 
strlen (door_id), &(door->public_key)))
+    {
+      GNUNET_free(door);
+      door = NULL;
+    }
+  }
+
+  const char *name = GNUNET_MESSENGER_get_name (handle);
+
+  if (!name)
+    name = "anonymous";
+
+  printf ("* Welcome to the messenger, '%s'!\n", name);
+
+  struct GNUNET_MESSENGER_Room *room;
+
+  if (door)
+  {
+    printf ("* You try to entry a room...\n");
+
+    room = GNUNET_MESSENGER_entry_room (messenger, door, &key);
+  }
+  else
+  {
+    printf ("* You try to open a room...\n");
+
+    room = GNUNET_MESSENGER_open_room (messenger, &key);
+  }
+
+  GNUNET_SCHEDULER_cancel (shutdown_task);
+
+  shutdown_task = GNUNET_SCHEDULER_add_shutdown (shutdown_hook, room);
+
+  if (!room)
+    GNUNET_SCHEDULER_shutdown ();
+  else
+    GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_relative_get_zero_ 
(), GNUNET_SCHEDULER_PRIORITY_IDLE, idle,
+                                                room);
+}
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be 
NULL!)
+ * @param cfg configuration
+ */
+static void
+run (void *cls, char *const*args, const char *cfgfile, const struct 
GNUNET_CONFIGURATION_Handle *cfg)
+{
+  messenger = GNUNET_MESSENGER_connect (cfg, ego_name, &on_identity, NULL, 
&on_message, NULL);
+
+  shutdown_task = GNUNET_SCHEDULER_add_shutdown (shutdown_hook, NULL);
+}
+
+/**
+ * The main function to obtain messenger information.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main (int argc, char **argv)
+{
+  const char *description = "Open and connect to rooms using the MESSENGER to 
chat.";
+
+  struct GNUNET_GETOPT_CommandLineOption options[] = {
+    GNUNET_GETOPT_option_string ('d',
+                                 "door",
+                                 "PEERIDENTITY",
+                                 "peer identity to entry into the room",
+                                 &door_id),
+    GNUNET_GETOPT_option_string ('e',
+                                 "ego",
+                                 "IDENTITY",
+                                 "identity to use for messaging",
+                                 &ego_name),
+    GNUNET_GETOPT_option_string ('r',
+                                 "room",
+                                 "ROOMKEY",
+                                 "key of the room to connect to",
+                                 &room_key),
+    GNUNET_GETOPT_OPTION_END };
+
+  return (GNUNET_OK == GNUNET_PROGRAM_run (argc,
+                                           argv,
+                                           "gnunet-messenger\0",
+                                           gettext_noop(description),
+                                           options,
+                                           &run,
+                                           NULL) ? EXIT_SUCCESS : 
EXIT_FAILURE);
+}
diff --git a/src/messenger/gnunet-service-messenger.c 
b/src/messenger/gnunet-service-messenger.c
new file mode 100644
index 000000000..2c92305c4
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger.c
@@ -0,0 +1,306 @@
+/*
+   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.c
+ * @brief GNUnet MESSENGER service
+ */
+
+#include "gnunet-service-messenger.h"
+
+#include "gnunet-service-messenger_service.h"
+#include "messenger_api_message.h"
+
+struct GNUNET_MESSENGER_Client
+{
+  struct GNUNET_SERVICE_Client *client;
+  struct GNUNET_MESSENGER_SrvHandle *handle;
+};
+
+struct GNUNET_MESSENGER_Service *messenger;
+
+static int
+check_create (void *cls, const struct GNUNET_MESSENGER_CreateMessage *msg)
+{
+  GNUNET_MQ_check_zero_termination(msg);
+  return GNUNET_OK;
+}
+
+static void
+handle_create (void *cls, const struct GNUNET_MESSENGER_CreateMessage *msg)
+{
+  struct GNUNET_MESSENGER_Client *msg_client = cls;
+
+  const char *name = ((const char*) msg) + sizeof(*msg);
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Handle created with name: %s\n", name);
+
+  setup_handle_name (msg_client->handle, strlen (name) > 0? name : NULL);
+
+  GNUNET_SERVICE_client_continue (msg_client->client);
+}
+
+static void
+handle_update (void *cls, const struct GNUNET_MESSENGER_UpdateMessage *msg)
+{
+  struct GNUNET_MESSENGER_Client *msg_client = cls;
+
+  if (GNUNET_OK != update_handle (msg_client->handle))
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Name is required to update key!\n");
+
+  GNUNET_SERVICE_client_continue (msg_client->client);
+}
+
+static void
+handle_destroy (void *cls, const struct GNUNET_MESSENGER_DestroyMessage *msg)
+{
+  struct GNUNET_MESSENGER_Client *msg_client = cls;
+
+  GNUNET_SERVICE_client_drop (msg_client->client);
+}
+
+static int
+check_set_name (void *cls, const struct GNUNET_MESSENGER_NameMessage *msg)
+{
+  GNUNET_MQ_check_zero_termination(msg);
+  return GNUNET_OK;
+}
+
+static void
+handle_set_name (void *cls, const struct GNUNET_MESSENGER_NameMessage *msg)
+{
+  struct GNUNET_MESSENGER_Client *msg_client = cls;
+
+  const char *name = ((const char*) msg) + sizeof(*msg);
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Handles name is now: %s\n", name);
+
+  if (GNUNET_YES != set_handle_name (msg_client->handle, name))
+    GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "No valid name: %s\n", name);
+
+  GNUNET_SERVICE_client_continue (msg_client->client);
+}
+
+static void
+handle_room_open (void *cls, const struct GNUNET_MESSENGER_RoomMessage *msg)
+{
+  struct GNUNET_MESSENGER_Client *msg_client = cls;
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Opening room: %s\n",
+             GNUNET_h2s (&(msg->key)));
+
+  if (GNUNET_YES == open_handle_room (msg_client->handle, &(msg->key)))
+  {
+    const struct GNUNET_ShortHashCode* member_id = 
get_handle_member_id(msg_client->handle, &(msg->key));
+
+    GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Opening room with member id: %s\n",
+               GNUNET_sh2s (member_id));
+
+    struct GNUNET_MESSENGER_RoomMessage *response;
+    struct GNUNET_MQ_Envelope *env;
+
+    env = GNUNET_MQ_msg(response, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_OPEN);
+    GNUNET_memcpy(&(response->key), &(msg->key), sizeof(msg->key));
+    GNUNET_MQ_send (msg_client->handle->mq, env);
+  }
+  else
+    GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Opening room failed: %s\n",
+               GNUNET_h2s (&(msg->key)));
+
+  GNUNET_SERVICE_client_continue (msg_client->client);
+}
+
+static void
+handle_room_entry (void *cls, const struct GNUNET_MESSENGER_RoomMessage *msg)
+{
+  struct GNUNET_MESSENGER_Client *msg_client = cls;
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Entering room: %s, %s\n",
+             GNUNET_h2s (&(msg->key)), GNUNET_i2s (&(msg->door)));
+
+  if (GNUNET_YES == entry_handle_room (msg_client->handle, &(msg->door), 
&(msg->key)))
+  {
+    const struct GNUNET_ShortHashCode* member_id = 
get_handle_member_id(msg_client->handle, &(msg->key));
+
+    GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Entering room with member id: %s\n",
+               GNUNET_sh2s (member_id));
+
+    struct GNUNET_MESSENGER_RoomMessage *response;
+    struct GNUNET_MQ_Envelope *env;
+
+    env = GNUNET_MQ_msg(response, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_ENTRY);
+    GNUNET_memcpy(&(response->door), &(msg->door), sizeof(msg->door));
+    GNUNET_memcpy(&(response->key), &(msg->key), sizeof(msg->key));
+    GNUNET_MQ_send (msg_client->handle->mq, env);
+  }
+  else
+    GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Entrance into room failed: %s, %s\n",
+               GNUNET_h2s (&(msg->key)), GNUNET_i2s (&(msg->door)));
+
+  GNUNET_SERVICE_client_continue (msg_client->client);
+}
+
+static void
+handle_room_close (void *cls, const struct GNUNET_MESSENGER_RoomMessage *msg)
+{
+  struct GNUNET_MESSENGER_Client *msg_client = cls;
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Closing room: %s\n", GNUNET_h2s 
(&(msg->key)));
+
+  if (GNUNET_YES == close_handle_room (msg_client->handle, &(msg->key)))
+  {
+    const struct GNUNET_ShortHashCode* member_id = 
get_handle_member_id(msg_client->handle, &(msg->key));
+
+    GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Closing room with member id: %s\n",
+               GNUNET_sh2s (member_id));
+
+    struct GNUNET_MESSENGER_RoomMessage *response;
+    struct GNUNET_MQ_Envelope *env;
+
+    env = GNUNET_MQ_msg(response, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_CLOSE);
+    GNUNET_memcpy(&(response->key), &(msg->key), sizeof(msg->key));
+    GNUNET_MQ_send (msg_client->handle->mq, env);
+  }
+  else
+    GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Closing room failed: %s\n", 
GNUNET_h2s (&(msg->key)));
+
+  GNUNET_SERVICE_client_continue (msg_client->client);
+}
+
+static int
+check_send_message (void *cls, const struct GNUNET_MESSENGER_SendMessage *msg)
+{
+  const uint16_t full_length = ntohs (msg->header.size) - sizeof(msg->header);
+
+  if (full_length < sizeof(msg->key))
+    return GNUNET_NO;
+
+  const uint16_t length = full_length - sizeof(msg->key);
+  const char *buffer = ((const char*) msg) + sizeof(*msg);
+
+  struct GNUNET_MESSENGER_Message message;
+
+  if (GNUNET_YES != decode_message (&message, length, buffer))
+    return GNUNET_NO;
+
+  return GNUNET_OK;
+}
+
+static void
+handle_send_message (void *cls, const struct GNUNET_MESSENGER_SendMessage *msg)
+{
+  struct GNUNET_MESSENGER_Client *msg_client = cls;
+
+  const struct GNUNET_HashCode *key = &(msg->key);
+  const char *buffer = ((const char*) msg) + sizeof(*msg);
+
+  const uint16_t length = ntohs (msg->header.size) - sizeof(*msg);
+
+  struct GNUNET_MESSENGER_Message message;
+  decode_message (&message, length, buffer);
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Sending message: %s to %s\n",
+             GNUNET_MESSENGER_name_of_kind (message.header.kind),
+             GNUNET_h2s (key));
+
+  if (GNUNET_YES != send_handle_message (msg_client->handle, key, &message))
+    GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Sending message failed: %s to %s\n",
+               GNUNET_MESSENGER_name_of_kind (message.header.kind),
+               GNUNET_h2s (key));
+
+  GNUNET_SERVICE_client_continue (msg_client->client);
+}
+
+static void
+handle_get_message (void *cls, const struct GNUNET_MESSENGER_RecvMessage *msg)
+{
+  struct GNUNET_MESSENGER_Client *msg_client = cls;
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Requesting message from room: %s\n",
+             GNUNET_h2s (&(msg->key)));
+
+  struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (messenger, 
&(msg->key));
+
+  if (room)
+    get_room_message (room, msg_client->handle, &(msg->hash), GNUNET_YES);
+  else
+    GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Room not found: %s\n",
+               GNUNET_h2s (&(msg->key)));
+
+  GNUNET_SERVICE_client_continue (msg_client->client);
+}
+
+static void*
+callback_client_connect (void *cls, struct GNUNET_SERVICE_Client *client, 
struct GNUNET_MQ_Handle *mq)
+{
+  struct GNUNET_MESSENGER_Client *msg_client = GNUNET_new(struct 
GNUNET_MESSENGER_Client);
+
+  msg_client->client = client;
+  msg_client->handle = add_service_handle (messenger, mq);
+
+  return msg_client;
+}
+
+static void
+callback_client_disconnect (void *cls, struct GNUNET_SERVICE_Client *client, 
void *internal_cls)
+{
+  struct GNUNET_MESSENGER_Client *msg_client = internal_cls;
+
+  remove_service_handle (messenger, msg_client->handle);
+
+  GNUNET_free(msg_client);
+}
+
+/**
+ * Setup MESSENGER internals.
+ *
+ * @param cls closure
+ * @param config configuration to use
+ * @param service the initialized service
+ */
+static void
+run (void *cls, const struct GNUNET_CONFIGURATION_Handle *config, struct 
GNUNET_SERVICE_Handle *service)
+{
+  messenger = create_service (config, service);
+
+  if ((!messenger) || (!messenger->cadet) || (!messenger->identity))
+    GNUNET_SCHEDULER_shutdown ();
+}
+
+/**
+ * Define "main" method using service macro.
+ */
+GNUNET_SERVICE_MAIN(
+    GNUNET_MESSENGER_SERVICE_NAME,
+    GNUNET_SERVICE_OPTION_NONE,
+    &run,
+    &callback_client_connect,
+    &callback_client_disconnect,
+    NULL,
+    GNUNET_MQ_hd_var_size( create, 
GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_CREATE, struct 
GNUNET_MESSENGER_CreateMessage, NULL ),
+    GNUNET_MQ_hd_fixed_size( update, 
GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_UPDATE, struct 
GNUNET_MESSENGER_UpdateMessage, NULL ),
+    GNUNET_MQ_hd_fixed_size( destroy, 
GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_DESTROY, struct 
GNUNET_MESSENGER_DestroyMessage, NULL ),
+    GNUNET_MQ_hd_var_size( set_name, 
GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_SET_NAME, struct 
GNUNET_MESSENGER_NameMessage, NULL ),
+    GNUNET_MQ_hd_fixed_size( room_open, 
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_OPEN, struct GNUNET_MESSENGER_RoomMessage, 
NULL ),
+    GNUNET_MQ_hd_fixed_size( room_entry, 
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_ENTRY, struct GNUNET_MESSENGER_RoomMessage, 
NULL ),
+    GNUNET_MQ_hd_fixed_size( room_close, 
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_CLOSE, struct GNUNET_MESSENGER_RoomMessage, 
NULL ),
+    GNUNET_MQ_hd_var_size( send_message, 
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_SEND_MESSAGE, struct 
GNUNET_MESSENGER_SendMessage, NULL ),
+    GNUNET_MQ_hd_fixed_size( get_message, 
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_GET_MESSAGE, struct 
GNUNET_MESSENGER_RecvMessage, NULL ),
+    GNUNET_MQ_handler_end());
diff --git a/src/messenger/gnunet-service-messenger.h 
b/src/messenger/gnunet-service-messenger.h
new file mode 100644
index 000000000..85a1d2549
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger.h
@@ -0,0 +1,121 @@
+/*
+   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.h
+ * @brief GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_SERVICE_MESSENGER_H
+#define GNUNET_SERVICE_MESSENGER_H
+
+#include "platform.h"
+#include "gnunet_cadet_service.h"
+#include "gnunet_container_lib.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_identity_service.h"
+#include "gnunet_mq_lib.h"
+#include "gnunet_peer_lib.h"
+#include "gnunet_protocols.h"
+#include "gnunet_util_lib.h"
+
+/**
+ * Message to create a handle for a client
+ */
+struct GNUNET_MESSENGER_CreateMessage
+{
+  struct GNUNET_MessageHeader header;
+};
+
+/**
+ * Message to update the handle (its EGO key) for a client
+ */
+struct GNUNET_MESSENGER_UpdateMessage
+{
+  struct GNUNET_MessageHeader header;
+};
+
+/**
+ * Message to destroy the handle for a client
+ */
+struct GNUNET_MESSENGER_DestroyMessage
+{
+  struct GNUNET_MessageHeader header;
+};
+
+/**
+ * Message to receive the current name of a handle
+ */
+struct GNUNET_MESSENGER_NameMessage
+{
+  struct GNUNET_MessageHeader header;
+};
+
+/**
+ * Message to receive the current public key of a handle
+ */
+struct GNUNET_MESSENGER_KeyMessage
+{
+  struct GNUNET_MessageHeader header;
+  struct GNUNET_IDENTITY_PublicKey pubkey;
+};
+
+/**
+ * General message to confirm interaction with a room
+ */
+struct GNUNET_MESSENGER_RoomMessage
+{
+  struct GNUNET_MessageHeader header;
+
+  struct GNUNET_PeerIdentity door;
+  struct GNUNET_HashCode key;
+};
+
+/**
+ * Message to receive the current member id of a handle in room
+ */
+struct GNUNET_MESSENGER_MemberMessage
+{
+  struct GNUNET_MessageHeader header;
+
+  struct GNUNET_HashCode key;
+  struct GNUNET_ShortHashCode id;
+};
+
+/**
+ * Message to send something into a room
+ */
+struct GNUNET_MESSENGER_SendMessage
+{
+  struct GNUNET_MessageHeader header;
+  struct GNUNET_HashCode key;
+};
+
+/**
+ * Message to receive something from a room
+ */
+struct GNUNET_MESSENGER_RecvMessage
+{
+  struct GNUNET_MessageHeader header;
+  struct GNUNET_HashCode key;
+  struct GNUNET_HashCode hash;
+};
+
+#endif //GNUNET_SERVICE_MESSENGER_H
diff --git a/src/messenger/gnunet-service-messenger_basement.c 
b/src/messenger/gnunet-service-messenger_basement.c
new file mode 100644
index 000000000..190cf2de5
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_basement.c
@@ -0,0 +1,58 @@
+/*
+   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_basement.c
+ * @brief GNUnet MESSENGER service
+ */
+
+#include "gnunet-service-messenger_basement.h"
+
+size_t
+count_of_tunnels (const struct GNUNET_MESSENGER_ListTunnels *tunnels)
+{
+  const struct GNUNET_MESSENGER_ListTunnel *element;
+  size_t count = 0;
+
+  for (element = tunnels->head; element; element = element->next)
+    count++;
+
+  return count;
+}
+
+int
+should_connect_tunnel_to (size_t count, size_t src, size_t dst)
+{
+  if ((src + 1) % count == dst % count)
+    return GNUNET_YES;
+
+  return GNUNET_NO;
+}
+
+int
+required_connection_between (size_t count, size_t src, size_t dst)
+{
+  if (GNUNET_YES == should_connect_tunnel_to (count, src, dst))
+    return GNUNET_YES;
+  if (GNUNET_YES == should_connect_tunnel_to (count, dst, src))
+    return GNUNET_YES;
+
+  return GNUNET_NO;
+}
diff --git a/src/messenger/gnunet-service-messenger_basement.h 
b/src/messenger/gnunet-service-messenger_basement.h
new file mode 100644
index 000000000..0a1a9b126
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_basement.h
@@ -0,0 +1,66 @@
+/*
+   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_basement.h
+ * @brief GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_SERVICE_MESSENGER_BASEMENT_H
+#define GNUNET_SERVICE_MESSENGER_BASEMENT_H
+
+#include "messenger_api_list_tunnels.h"
+
+/**
+ * Returns the count of peers in a list (typically from the basement of a 
room).
+ *
+ * @param tunnels List of peer identities
+ * @return Count of the entries in the list
+ */
+size_t
+count_of_tunnels (const struct GNUNET_MESSENGER_ListTunnels *tunnels);
+
+/**
+ * Returns GNUNET_YES or GNUNET_NO to determine if the peer at index 
<i>src</i> should
+ * or should not connect outgoing to the peer at index <i>dst</i> to construct 
a complete
+ * basement with a given <i>count</i> of peers.
+ *
+ * @param count Count of peers
+ * @param src Source index
+ * @param dst Destination index
+ * @return GNUNET_YES or GNUNET_NO based on topologic requirement
+ */
+int
+should_connect_tunnel_to (size_t count, size_t src, size_t dst);
+
+/**
+ * Returns GNUNET_YES or GNUNET_NO to determine if the peers of index 
<i>src</i> and
+ * index <i>dst</i> should be connected in any direction to construct a 
complete
+ * basement with a given <i>count</i> of peers.
+ *
+ * @param count Count of peers
+ * @param src Source index
+ * @param dst Destination index
+ * @return GNUNET_YES or GNUNET_NO based on topologic requirement
+ */
+int
+required_connection_between (size_t count, size_t src, size_t dst);
+
+#endif //GNUNET_SERVICE_MESSENGER_BASEMENT_H
diff --git a/src/messenger/gnunet-service-messenger_contact.c 
b/src/messenger/gnunet-service-messenger_contact.c
new file mode 100644
index 000000000..1ec125402
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_contact.c
@@ -0,0 +1,96 @@
+/*
+   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_contact.c
+ * @brief GNUnet MESSENGER service
+ */
+
+#include "gnunet-service-messenger_contact.h"
+
+struct GNUNET_MESSENGER_SrvContact*
+create_contact (const struct GNUNET_IDENTITY_PublicKey *key)
+{
+  struct GNUNET_MESSENGER_SrvContact *contact = GNUNET_new(struct 
GNUNET_MESSENGER_SrvContact);
+
+  contact->name = NULL;
+  contact->rc = 0;
+
+  GNUNET_memcpy(&(contact->public_key), key, sizeof(contact->public_key));
+
+  return contact;
+}
+
+void
+destroy_contact (struct GNUNET_MESSENGER_SrvContact *contact)
+{
+  if (contact->name)
+    GNUNET_free(contact->name);
+
+  GNUNET_free(contact);
+}
+
+const char*
+get_contact_name (const struct GNUNET_MESSENGER_SrvContact *contact)
+{
+  return contact->name;
+}
+
+void
+set_contact_name (struct GNUNET_MESSENGER_SrvContact *contact, const char 
*name)
+{
+  GNUNET_assert(name);
+
+  if (contact->name)
+    GNUNET_free(contact->name);
+
+  contact->name = GNUNET_strdup(name);
+}
+
+const struct GNUNET_IDENTITY_PublicKey*
+get_contact_key (const struct GNUNET_MESSENGER_SrvContact *contact)
+{
+  return &(contact->public_key);
+}
+
+void
+increase_contact_rc (struct GNUNET_MESSENGER_SrvContact *contact)
+{
+  contact->rc++;
+}
+
+int
+decrease_contact_rc (struct GNUNET_MESSENGER_SrvContact *contact)
+{
+  if (contact->rc > 0)
+    contact->rc--;
+
+  return contact->rc ? GNUNET_NO : GNUNET_YES;
+}
+
+const struct GNUNET_HashCode*
+get_contact_id_from_key (const struct GNUNET_MESSENGER_SrvContact *contact)
+{
+  static struct GNUNET_HashCode id;
+
+  GNUNET_CRYPTO_hash (&(contact->public_key), sizeof(contact->public_key), 
&id);
+
+  return &id;
+}
diff --git a/src/messenger/gnunet-service-messenger_contact.h 
b/src/messenger/gnunet-service-messenger_contact.h
new file mode 100644
index 000000000..4a4f8bf0f
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_contact.h
@@ -0,0 +1,112 @@
+/*
+   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_contact.h
+ * @brief GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_SERVICE_MESSENGER_CONTACT_H
+#define GNUNET_SERVICE_MESSENGER_CONTACT_H
+
+#include "platform.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_identity_service.h"
+
+struct GNUNET_MESSENGER_SrvContact
+{
+  char *name;
+  size_t rc;
+
+  struct GNUNET_IDENTITY_PublicKey public_key;
+};
+
+/**
+ * Creates and allocates a new contact with a given public <i>key</i> from an 
EGO.
+ *
+ * @param key Public key
+ * @return New contact
+ */
+struct GNUNET_MESSENGER_SrvContact*
+create_contact (const struct GNUNET_IDENTITY_PublicKey *key);
+
+/**
+ * Destroys a contact and frees its memory fully.
+ *
+ * @param contact Contact
+ */
+void
+destroy_contact (struct GNUNET_MESSENGER_SrvContact *contact);
+
+/**
+ * Returns the current name of a given <i>contact</i> or NULL if no valid name 
was assigned yet.
+ *
+ * @param contact Contact
+ * @return Name of the contact or NULL
+ */
+const char*
+get_contact_name (const struct GNUNET_MESSENGER_SrvContact *contact);
+
+/**
+ * Changes the current name of a given <i>contact</i> by copying it from the 
parameter <i>name</i>.
+ *
+ * @param contact Contact
+ * @param name Valid name (may not be NULL!)
+ */
+void
+set_contact_name (struct GNUNET_MESSENGER_SrvContact *contact, const char 
*name);
+
+/**
+ * Returns the public key of a given <i>contact</i>.
+ *
+ * @param contact Contact
+ * @return Public key of the contact
+ */
+const struct GNUNET_IDENTITY_PublicKey*
+get_contact_key (const struct GNUNET_MESSENGER_SrvContact *contact);
+
+/**
+ * Increases the reference counter of a given <i>contact</i> which is zero as 
default.
+ *
+ * @param contact Contact
+ */
+void
+increase_contact_rc (struct GNUNET_MESSENGER_SrvContact *contact);
+
+/**
+ * Decreases the reference counter if possible (can not underflow!) of a given 
<i>contact</i>
+ * and returns GNUNET_YES if the counter is equal to zero, otherwise GNUNET_NO.
+ *
+ * @param contact Contact
+ * @return GNUNET_YES or GNUNET_NO depending on the reference counter
+ */
+int
+decrease_contact_rc (struct GNUNET_MESSENGER_SrvContact *contact);
+
+/**
+ * Returns the resulting hashcode of the public key from a given 
<i>contact</i>.
+ *
+ * @param contact Contact
+ * @return Hash of the contacts public key
+ */
+const struct GNUNET_HashCode*
+get_contact_id_from_key (const struct GNUNET_MESSENGER_SrvContact *contact);
+
+#endif //GNUNET_SERVICE_MESSENGER_CONTACT_H
diff --git a/src/messenger/gnunet-service-messenger_handle.c 
b/src/messenger/gnunet-service-messenger_handle.c
new file mode 100644
index 000000000..38ad6fbb4
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_handle.c
@@ -0,0 +1,503 @@
+/*
+   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_handle.c
+ * @brief GNUnet MESSENGER service
+ */
+
+#include "gnunet-service-messenger_handle.h"
+
+#include "gnunet-service-messenger.h"
+#include "gnunet-service-messenger_message_kind.h"
+
+struct GNUNET_MESSENGER_SrvHandle*
+create_handle (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MQ_Handle *mq)
+{
+  struct GNUNET_MESSENGER_SrvHandle *handle = GNUNET_new(struct 
GNUNET_MESSENGER_SrvHandle);
+
+  handle->service = service;
+  handle->mq = mq;
+
+  handle->name = NULL;
+
+  handle->operation = NULL;
+
+  handle->ego = NULL;
+
+  handle->member_ids = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
+
+  return handle;
+}
+
+int
+iterate_free_member_ids (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  GNUNET_free(value);
+
+  return GNUNET_YES;
+}
+
+void
+destroy_handle (struct GNUNET_MESSENGER_SrvHandle *handle)
+{
+  if (handle->service->dir)
+   save_handle_configuration(handle);
+
+  if (handle->operation)
+    GNUNET_IDENTITY_cancel (handle->operation);
+
+  if (handle->name)
+    GNUNET_free(handle->name);
+
+  GNUNET_CONTAINER_multihashmap_iterate (handle->member_ids, 
iterate_free_member_ids, NULL);
+  GNUNET_CONTAINER_multihashmap_destroy (handle->member_ids);
+
+  GNUNET_free(handle);
+}
+
+void
+get_handle_data_subdir (struct GNUNET_MESSENGER_SrvHandle *handle, const char 
*name, char **dir)
+{
+  if (name)
+    GNUNET_asprintf (dir, "%s%s%c%s%c", handle->service->dir, "identities",
+                     DIR_SEPARATOR, name, DIR_SEPARATOR);
+  else
+    GNUNET_asprintf (dir, "%s%s%c", handle->service->dir, "anonymous",
+                     DIR_SEPARATOR);
+}
+
+static int
+create_handle_member_id (const struct GNUNET_MESSENGER_SrvHandle *handle, 
const struct GNUNET_HashCode *key)
+{
+  struct GNUNET_ShortHashCode *random_id = generate_service_new_member_id 
(handle->service, key);
+
+  if (!random_id)
+    return GNUNET_NO;
+
+  if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (handle->member_ids, key, 
random_id,
+                                                      
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
+  {
+    GNUNET_free(random_id);
+    return GNUNET_NO;
+  }
+
+  GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Created a new member id (%s) for room: 
%s\n",
+             GNUNET_sh2s(random_id), GNUNET_h2s(key));
+
+  return GNUNET_YES;
+}
+
+const struct GNUNET_ShortHashCode*
+get_handle_member_id (const struct GNUNET_MESSENGER_SrvHandle *handle, const 
struct GNUNET_HashCode *key)
+{
+  return GNUNET_CONTAINER_multihashmap_get (handle->member_ids, key);
+}
+
+void
+change_handle_member_id (struct GNUNET_MESSENGER_SrvHandle *handle, const 
struct GNUNET_HashCode *key,
+                         const struct GNUNET_ShortHashCode *unique_id)
+{
+  struct GNUNET_ShortHashCode *member_id = GNUNET_CONTAINER_multihashmap_get 
(handle->member_ids, key);
+
+  if (member_id)
+  {
+    GNUNET_log(GNUNET_ERROR_TYPE_INFO, "Changed a member id (%s) for room (%s) 
",
+               GNUNET_sh2s(member_id), GNUNET_h2s(key));
+    GNUNET_log(GNUNET_ERROR_TYPE_INFO, "into (%s).\n",
+               GNUNET_sh2s(unique_id));
+
+    GNUNET_memcpy(member_id, unique_id, sizeof(*unique_id));
+
+    struct GNUNET_MESSENGER_MemberMessage *msg;
+    struct GNUNET_MQ_Envelope *env;
+
+    env = GNUNET_MQ_msg(msg, 
GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_MEMBER_ID);
+
+    GNUNET_memcpy(&(msg->key), key, sizeof(*key));
+    GNUNET_memcpy(&(msg->id), member_id, sizeof(*member_id));
+
+    GNUNET_MQ_send (handle->mq, env);
+  }
+  else
+  {
+    member_id = GNUNET_new(struct GNUNET_ShortHashCode);
+    GNUNET_memcpy(member_id, unique_id, sizeof(*member_id));
+
+    if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (handle->member_ids, 
key, member_id,
+                                                        
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
+      GNUNET_free(member_id);
+  }
+}
+
+static void
+change_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char 
*name)
+{
+  if (handle->name)
+    GNUNET_free(handle->name);
+
+  handle->name = name ? GNUNET_strdup(name) : NULL;
+
+  const uint16_t name_len = handle->name ? strlen (handle->name) : 0;
+
+  struct GNUNET_MESSENGER_NameMessage *msg;
+  struct GNUNET_MQ_Envelope *env;
+
+  env = GNUNET_MQ_msg_extra(msg, name_len + 1, 
GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_GET_NAME);
+
+  char *extra = ((char*) msg) + sizeof(*msg);
+
+  if (name_len)
+    GNUNET_memcpy(extra, handle->name, name_len);
+
+  extra[name_len] = '\0';
+
+  GNUNET_MQ_send (handle->mq, env);
+}
+
+static void
+change_handle_ego (struct GNUNET_MESSENGER_SrvHandle *handle, struct 
GNUNET_MESSENGER_Ego *ego)
+{
+  handle->ego = ego;
+
+  ego = get_handle_ego(handle);
+
+  struct GNUNET_MESSENGER_KeyMessage *msg;
+  struct GNUNET_MQ_Envelope *env;
+
+  env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_GET_KEY);
+
+  GNUNET_memcpy(&(msg->pubkey), &(ego->pub), sizeof(ego->pub));
+
+  GNUNET_MQ_send (handle->mq, env);
+}
+
+struct GNUNET_MESSENGER_Ego*
+get_handle_ego (struct GNUNET_MESSENGER_SrvHandle *handle)
+{
+  static struct GNUNET_MESSENGER_Ego anonymous;
+  static int read_keys = 0;
+
+  if (handle->ego)
+    return handle->ego;
+
+  if (!read_keys)
+  {
+    struct GNUNET_IDENTITY_Ego* ego = GNUNET_IDENTITY_ego_get_anonymous ();
+    GNUNET_memcpy(&(anonymous.priv), GNUNET_IDENTITY_ego_get_private_key(ego), 
sizeof(anonymous.priv));
+    GNUNET_IDENTITY_ego_get_public_key(ego, &(anonymous.pub));
+    read_keys = 1;
+  }
+
+  return &anonymous;
+}
+
+void
+setup_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name)
+{
+  change_handle_name (handle, name);
+  change_handle_ego (handle, handle->name? lookup_service_ego(handle->service, 
handle->name) : NULL);
+
+  if (handle->service->dir)
+   load_handle_configuration(handle);
+}
+
+struct GNUNET_MESSENGER_MessageHandle
+{
+  struct GNUNET_MESSENGER_SrvHandle *handle;
+  struct GNUNET_MESSENGER_Message *message;
+};
+
+static int
+iterate_send_message (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct GNUNET_MESSENGER_MessageHandle *msg_handle = cls;
+
+  send_handle_message (msg_handle->handle, key, msg_handle->message);
+
+  return GNUNET_YES;
+}
+
+static void
+callback_ego_create (void *cls, const struct GNUNET_IDENTITY_PrivateKey *key, 
const char *emsg)
+{
+  struct GNUNET_MESSENGER_SrvHandle *handle = cls;
+
+  handle->operation = NULL;
+
+  if (emsg)
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "%s\n", emsg);
+
+  if (key)
+  {
+    struct GNUNET_MESSENGER_MessageHandle msg_handle;
+
+    msg_handle.handle = handle;
+    msg_handle.message = create_message_key (key);
+
+    GNUNET_CONTAINER_multihashmap_iterate (handle->member_ids, 
iterate_send_message, &msg_handle);
+
+    destroy_message (msg_handle.message);
+
+    update_service_ego(handle->service, handle->name, key);
+
+    change_handle_ego (handle, lookup_service_ego(handle->service, 
handle->name));
+  }
+}
+
+int
+update_handle (struct GNUNET_MESSENGER_SrvHandle *handle)
+{
+  GNUNET_assert(handle);
+
+  if (!handle->name)
+    return GNUNET_SYSERR;
+
+  struct GNUNET_MESSENGER_Ego *ego = lookup_service_ego(handle->service, 
handle->name);
+
+  if (!ego)
+    handle->operation = GNUNET_IDENTITY_create (handle->service->identity, 
handle->name, NULL,
+                                                GNUNET_IDENTITY_TYPE_ECDSA, 
callback_ego_create, handle);
+  else
+    change_handle_ego (handle, ego);
+
+  return GNUNET_OK;
+}
+
+int
+set_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name)
+{
+  GNUNET_assert(handle);
+
+  if ((name) && (lookup_service_ego(handle->service, name)))
+    return GNUNET_NO;
+
+  struct GNUNET_IDENTITY_Operation *operation = handle->operation;
+
+  if (handle->name)
+    handle->operation = GNUNET_IDENTITY_rename (handle->service->identity, 
handle->name, name, NULL, NULL);
+
+  char *old_dir;
+  get_handle_data_subdir (handle, handle->name, &old_dir);
+
+  char *new_dir;
+  get_handle_data_subdir (handle, name, &new_dir);
+
+  int result = 0;
+
+  if (GNUNET_YES == GNUNET_DISK_directory_test (old_dir, GNUNET_YES))
+  {
+    GNUNET_DISK_directory_create_for_file (new_dir);
+
+    result = rename (old_dir, new_dir);
+  }
+  else if (GNUNET_YES == GNUNET_DISK_directory_test (new_dir, GNUNET_NO))
+    result = -1;
+
+  if (0 == result)
+  {
+    struct GNUNET_MESSENGER_MessageHandle msg_handle;
+
+    msg_handle.handle = handle;
+    msg_handle.message = create_message_name (name);
+
+    GNUNET_CONTAINER_multihashmap_iterate (handle->member_ids, 
iterate_send_message, &msg_handle);
+
+    destroy_message (msg_handle.message);
+
+    change_handle_name (handle, name);
+
+    if (operation)
+      GNUNET_IDENTITY_cancel (operation);
+  }
+  else
+  {
+    if (handle->operation)
+    {
+      GNUNET_IDENTITY_cancel (handle->operation);
+
+      handle->operation = operation;
+    }
+  }
+
+  GNUNET_free(old_dir);
+  GNUNET_free(new_dir);
+
+  return (result == 0 ? GNUNET_OK : GNUNET_NO);
+}
+
+int
+open_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct 
GNUNET_HashCode *key)
+{
+  if ((!get_handle_member_id (handle, key)) && (GNUNET_YES != 
create_handle_member_id (handle, key)))
+    return GNUNET_NO;
+
+  return open_service_room (handle->service, handle, key);
+}
+
+int
+entry_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct 
GNUNET_PeerIdentity *door,
+                   const struct GNUNET_HashCode *key)
+{
+  if ((!get_handle_member_id (handle, key)) && (GNUNET_YES != 
create_handle_member_id (handle, key)))
+    return GNUNET_NO;
+
+  return entry_service_room (handle->service, handle, door, key);
+}
+
+int
+close_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct 
GNUNET_HashCode *key)
+{
+  if (!get_handle_member_id (handle, key))
+    return GNUNET_NO;
+
+  return close_service_room (handle->service, handle, key);
+}
+
+int
+send_handle_message (struct GNUNET_MESSENGER_SrvHandle *handle, const struct 
GNUNET_HashCode *key,
+                     struct GNUNET_MESSENGER_Message *message)
+{
+  const struct GNUNET_ShortHashCode *id = get_handle_member_id (handle, key);
+
+  if (!id)
+  {
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "It is required to be a member of a 
room to send messages!\n");
+    return GNUNET_NO;
+  }
+
+  struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (handle->service, 
key);
+
+  if (!room)
+  {
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "The room (%s) is unknown!\n", 
GNUNET_h2s (key));
+    return GNUNET_NO;
+  }
+
+  struct GNUNET_HashCode hash;
+
+  GNUNET_memcpy(&(message->header.sender_id), id, sizeof(*id));
+
+  send_room_message (room, handle, message, &hash);
+  return GNUNET_YES;
+}
+
+static int callback_scan_for_rooms(void* cls, const char *filename) {
+  struct GNUNET_MESSENGER_SrvHandle* handle = cls;
+
+  struct GNUNET_CONFIGURATION_Handle* cfg = GNUNET_CONFIGURATION_create();
+
+  if ((GNUNET_YES == GNUNET_DISK_file_test(filename)) &&
+      (GNUNET_OK == GNUNET_CONFIGURATION_parse(cfg, filename)))
+  {
+    struct GNUNET_HashCode key;
+    struct GNUNET_ShortHashCode member_id;
+
+    if ((GNUNET_OK == GNUNET_CONFIGURATION_get_data(cfg, "room", "key", &key, 
sizeof(key))) &&
+        (GNUNET_OK == GNUNET_CONFIGURATION_get_data(cfg, "room", "member_id", 
&member_id, sizeof(member_id))))
+      change_handle_member_id(handle, &key, &member_id);
+  }
+
+  GNUNET_CONFIGURATION_destroy(cfg);
+  return GNUNET_OK;
+}
+
+void load_handle_configuration(struct GNUNET_MESSENGER_SrvHandle *handle) {
+  char* id_dir;
+  get_handle_data_subdir(handle, handle->name, &id_dir);
+
+  if (GNUNET_YES == GNUNET_DISK_directory_test(id_dir, GNUNET_YES))
+  {
+    char* scan_dir;
+    GNUNET_asprintf(&scan_dir, "%s%s%c", id_dir, "rooms", DIR_SEPARATOR);
+
+    if (GNUNET_OK == GNUNET_DISK_directory_test(scan_dir, GNUNET_YES))
+      GNUNET_DISK_directory_scan(scan_dir, callback_scan_for_rooms, handle);
+
+    GNUNET_free(scan_dir);
+  }
+
+  GNUNET_free(id_dir);
+}
+
+static int
+iterate_save_rooms(void* cls, const struct GNUNET_HashCode* key, void* value)
+{
+  struct GNUNET_MESSENGER_SrvHandle* handle = cls;
+  struct GNUNET_ShortHashCode* member_id = value;
+
+  char* id_dir;
+  get_handle_data_subdir(handle, handle->name, &id_dir);
+
+  char* filename;
+  GNUNET_asprintf(&filename, "%s%s%c%s.cfg",
+                  id_dir, "rooms", DIR_SEPARATOR,
+                  GNUNET_h2s(key));
+
+  GNUNET_free(id_dir);
+
+  struct GNUNET_CONFIGURATION_Handle* cfg = GNUNET_CONFIGURATION_create();
+
+  char* key_data = GNUNET_STRINGS_data_to_string_alloc(key, sizeof(*key));
+
+  if (key_data)
+  {
+    GNUNET_CONFIGURATION_set_value_string(cfg, "room", "key", key_data);
+
+    GNUNET_free(key_data);
+  }
+
+  char* member_id_data = GNUNET_STRINGS_data_to_string_alloc(member_id, 
sizeof(*member_id));
+
+  if (member_id_data)
+  {
+    GNUNET_CONFIGURATION_set_value_string(cfg, "room", "member_id", 
member_id_data);
+
+    GNUNET_free(member_id_data);
+  }
+
+  GNUNET_CONFIGURATION_write(cfg, filename);
+  GNUNET_CONFIGURATION_destroy(cfg);
+
+  GNUNET_free(filename);
+
+  return GNUNET_YES;
+}
+
+void save_handle_configuration(struct GNUNET_MESSENGER_SrvHandle *handle)
+{
+  char* id_dir;
+  get_handle_data_subdir(handle, handle->name, &id_dir);
+
+  if ((GNUNET_YES == GNUNET_DISK_directory_test(id_dir, GNUNET_NO)) ||
+      (GNUNET_OK == GNUNET_DISK_directory_create(id_dir)))
+  {
+    char* save_dir;
+    GNUNET_asprintf(&save_dir, "%s%s%c", id_dir, "rooms", DIR_SEPARATOR);
+
+    if ((GNUNET_YES == GNUNET_DISK_directory_test(save_dir, GNUNET_NO)) ||
+        (GNUNET_OK == GNUNET_DISK_directory_create(save_dir)))
+      GNUNET_CONTAINER_multihashmap_iterate(handle->member_ids, 
iterate_save_rooms, handle);
+
+    GNUNET_free(save_dir);
+  }
+
+  GNUNET_free(id_dir);
+}
diff --git a/src/messenger/gnunet-service-messenger_handle.h 
b/src/messenger/gnunet-service-messenger_handle.h
new file mode 100644
index 000000000..81cf377a8
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_handle.h
@@ -0,0 +1,216 @@
+/*
+   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_handle.h
+ * @brief GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_SERVICE_MESSENGER_HANDLE_H
+#define GNUNET_SERVICE_MESSENGER_HANDLE_H
+
+#include "platform.h"
+#include "gnunet_cadet_service.h"
+#include "gnunet_container_lib.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_identity_service.h"
+#include "gnunet_peer_lib.h"
+#include "gnunet_mq_lib.h"
+
+#include "gnunet-service-messenger_service.h"
+
+#include "messenger_api_ego.h"
+#include "messenger_api_message.h"
+
+struct GNUNET_MESSENGER_SrvHandle
+{
+  struct GNUNET_MESSENGER_Service *service;
+  struct GNUNET_MQ_Handle *mq;
+
+  char *name;
+
+  struct GNUNET_IDENTITY_Operation *operation;
+
+  struct GNUNET_MESSENGER_Ego *ego;
+
+  struct GNUNET_CONTAINER_MultiHashMap *member_ids;
+};
+
+/**
+ * Creates and allocates a new handle related to a <i>service</i> and using a 
given <i>mq</i> (message queue).
+ *
+ * @param service MESSENGER Service
+ * @param mq Message queue
+ * @return New handle
+ */
+struct GNUNET_MESSENGER_SrvHandle*
+create_handle (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MQ_Handle *mq);
+
+/**
+ * Destroys a handle and frees its memory fully.
+ *
+ * @param handle Handle
+ */
+void
+destroy_handle (struct GNUNET_MESSENGER_SrvHandle *handle);
+
+/**
+ * Writes the path of the directory for a given <i>handle</i> using a specific 
<i>name</i> to the parameter
+ * <i>dir</i>. This directory will be used to store data regarding the handle 
and its messages.
+ *
+ * @param handle Handle
+ * @param name Potential name of the handle
+ * @param dir[out] Path to store data
+ */
+void
+get_handle_data_subdir (struct GNUNET_MESSENGER_SrvHandle *handle, const char 
*name, char **dir);
+
+/**
+ * Returns the member id of a given <i>handle</i> in a specific <i>room</i>.
+ *
+ * If the handle is not a member of the specific <i>room</i>, NULL gets 
returned.
+ *
+ * @param handle Handle
+ * @param key Key of a room
+ * @return Member id or NULL
+ */
+const struct GNUNET_ShortHashCode*
+get_handle_member_id (const struct GNUNET_MESSENGER_SrvHandle *handle, const 
struct GNUNET_HashCode *key);
+
+/**
+ * Changes the member id of a given <i>handle</i> in a specific <i>room</i> to 
match a <i>unique_id</i>.
+ *
+ * The client connected to the <i>handle</i> will be informed afterwards 
automatically.
+ *
+ * @param handle Handle
+ * @param key Key of a room
+ * @param unique_id Unique member id
+ */
+void
+change_handle_member_id (struct GNUNET_MESSENGER_SrvHandle *handle, const 
struct GNUNET_HashCode *key,
+                         const struct GNUNET_ShortHashCode *unique_id);
+
+/**
+ * Returns the EGO used by a given <i>handle</i>.
+ *
+ * @param handle Handle
+ * @return EGO keypair
+ */
+struct GNUNET_MESSENGER_Ego*
+get_handle_ego (struct GNUNET_MESSENGER_SrvHandle *handle);
+
+/**
+ * Tries to set the name and EGO key of a <i>handle</i> initially by looking 
up a specific <i>name</i>.
+ *
+ * @param handle Handle
+ * @param name Name (optionally: valid EGO name)
+ */
+void
+setup_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char 
*name);
+
+/**
+ * Tries to change the keypair of an EGO of a <i>handle</i> under the same 
name and informs all rooms
+ * about the change automatically.
+ *
+ * @param handle Handle
+ * @return GNUNET_OK on success, otherwise GNUNET_SYSERR
+ */
+int
+update_handle (struct GNUNET_MESSENGER_SrvHandle *handle);
+
+/**
+ * Tries to rename the handle which implies renaming the EGO its using and 
moving all related data into
+ * the directory fitting to the changed <i>name</i>.
+ *
+ * The client connected to the <i>handle</i> will be informed afterwards 
automatically.
+ *
+ * @param handle Handle
+ * @param name New name
+ * @return GNUNET_OK on success, otherwise GNUNET_NO
+ */
+int
+set_handle_name (struct GNUNET_MESSENGER_SrvHandle *handle, const char *name);
+
+/**
+ * Makes a given <i>handle</i> a member of the room using a specific 
<i>key</i> and opens the
+ * room from the handles service.
+ *
+ * @param handle Handle
+ * @param key Key of a room
+ * @return GNUNET_YES on success, otherwise GNUNET_NO
+ */
+int
+open_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct 
GNUNET_HashCode *key);
+
+/**
+ * Makes a given <i>handle</i> a member of the room using a specific 
<i>key</i> and enters the room
+ * through a tunnel to a peer identified by a given <i>door</i> (peer 
identity).
+ *
+ * @param handle Handle
+ * @param door Peer identity
+ * @param key Key of a room
+ * @return GNUNET_YES on success, otherwise GNUNET_NO
+ */
+int
+entry_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct 
GNUNET_PeerIdentity *door,
+                   const struct GNUNET_HashCode *key);
+
+/**
+ * Removes the membership of the room using a specific <i>key</i> and closes 
it if no other handle
+ * from this service is still a member of it.
+ *
+ * @param handle Handle
+ * @param key Key of a room
+ * @return GNUNET_YES on success, otherwise GNUNET_NO
+ */
+int
+close_handle_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct 
GNUNET_HashCode *key);
+
+/**
+ * Sends a <i>message</i> from a given <i>handle</i> to the room using a 
specific <i>key</i>.
+ *
+ * @param handle Handle
+ * @param key Key of a room
+ * @param message Message
+ * @return GNUNET_YES on success, otherwise GNUNET_NO
+ */
+int
+send_handle_message (struct GNUNET_MESSENGER_SrvHandle *handle, const struct 
GNUNET_HashCode *key,
+                     struct GNUNET_MESSENGER_Message *message);
+
+/**
+ * Loads member ids and other potential configuration from a given 
<i>handle</i> which
+ * depends on the given name the <i>handle</i> uses.
+ *
+ * @param handle Handle
+ */
+void
+load_handle_configuration(struct GNUNET_MESSENGER_SrvHandle *handle);
+
+/**
+ * Saves member ids and other potential configuration from a given 
<i>handle</i> which
+ * depends on the given name the <i>handle</i> uses.
+ *
+ * @param handle Handle
+ */
+void
+save_handle_configuration(struct GNUNET_MESSENGER_SrvHandle *handle);
+
+#endif //GNUNET_SERVICE_MESSENGER_HANDLE_H
diff --git a/src/messenger/gnunet-service-messenger_list_handles.c 
b/src/messenger/gnunet-service-messenger_list_handles.c
new file mode 100644
index 000000000..16a160dea
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_list_handles.c
@@ -0,0 +1,95 @@
+/*
+   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_list_handles.c
+ * @brief GNUnet MESSENGER service
+ */
+
+#include "gnunet-service-messenger_list_handles.h"
+
+#include "gnunet-service-messenger_handle.h"
+
+void
+init_list_handles (struct GNUNET_MESSENGER_ListHandles *handles)
+{
+  GNUNET_assert(handles);
+
+  handles->head = NULL;
+  handles->tail = NULL;
+}
+
+void
+clear_list_handles (struct GNUNET_MESSENGER_ListHandles *handles)
+{
+  GNUNET_assert(handles);
+
+  while (handles->head)
+  {
+    struct GNUNET_MESSENGER_ListHandle *element = handles->head;
+
+    GNUNET_CONTAINER_DLL_remove(handles->head, handles->tail, element);
+    destroy_handle (element->handle);
+    GNUNET_free(element);
+  }
+
+  handles->head = NULL;
+  handles->tail = NULL;
+}
+
+void
+add_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, void *handle)
+{
+  struct GNUNET_MESSENGER_ListHandle *element = GNUNET_new(struct 
GNUNET_MESSENGER_ListHandle);
+
+  element->handle = handle;
+
+  GNUNET_CONTAINER_DLL_insert_tail(handles->head, handles->tail, element);
+}
+
+int
+remove_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, void *handle)
+{
+  struct GNUNET_MESSENGER_ListHandle *element;
+
+  for (element = handles->head; element; element = element->next)
+    if (element->handle == handle)
+      break;
+
+  if (!element)
+    return GNUNET_NO;
+
+  GNUNET_CONTAINER_DLL_remove(handles->head, handles->tail, element);
+  GNUNET_free(element);
+
+  return GNUNET_YES;
+}
+
+void*
+find_list_handle_by_member (struct GNUNET_MESSENGER_ListHandles *handles, 
const struct GNUNET_HashCode *key)
+{
+  struct GNUNET_MESSENGER_ListHandle *element;
+
+  for (element = handles->head; element; element = element->next)
+    if (get_handle_member_id ((struct GNUNET_MESSENGER_SrvHandle*) 
element->handle, key))
+      return element->handle;
+
+  return NULL;
+}
diff --git a/src/messenger/gnunet-service-messenger_list_handles.h 
b/src/messenger/gnunet-service-messenger_list_handles.h
new file mode 100644
index 000000000..fe92cc58a
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_list_handles.h
@@ -0,0 +1,96 @@
+/*
+   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_list_handles.h
+ * @brief GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_SERVICE_MESSENGER_LIST_HANDLES_H
+#define GNUNET_SERVICE_MESSENGER_LIST_HANDLES_H
+
+#include "platform.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_container_lib.h"
+
+struct GNUNET_MESSENGER_ListHandle
+{
+  struct GNUNET_MESSENGER_ListHandle *prev;
+  struct GNUNET_MESSENGER_ListHandle *next;
+
+  void *handle;
+};
+
+struct GNUNET_MESSENGER_ListHandles
+{
+  struct GNUNET_MESSENGER_ListHandle *head;
+  struct GNUNET_MESSENGER_ListHandle *tail;
+};
+
+/**
+ * Initializes list of handles as empty list.
+ *
+ * @param handles List of handles
+ */
+void
+init_list_handles (struct GNUNET_MESSENGER_ListHandles *handles);
+
+/**
+ * Destroys remaining handles and clears the list.
+ *
+ * @param handles List of handles
+ */
+void
+clear_list_handles (struct GNUNET_MESSENGER_ListHandles *handles);
+
+/**
+ * Adds a specific <i>handle</i> to the end of the list.
+ *
+ * @param handles List of handles
+ * @param handle Handle
+ */
+void
+add_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, void *handle);
+
+/**
+ * Removes the first entry matching with a specific <i>handle</i> from the 
list and
+ * returns GNUNET_YES on success or GNUNET_NO on failure.
+ *
+ * @param handles List of handles
+ * @param handle Handle
+ * @return GNUNET_YES on success, otherwise GNUNET_NO
+ */
+int
+remove_list_handle (struct GNUNET_MESSENGER_ListHandles *handles, void 
*handle);
+
+/**
+ * Searches linearly through the list of handles for members of a specific room
+ * which is identified by a given <i>key</i>.
+ *
+ * If no handle is found which is a current member, NULL gets returned.
+ *
+ * @param handles List of handles
+ * @param key Common key of a room
+ * @return First handle which is a current member
+ */
+void*
+find_list_handle_by_member (struct GNUNET_MESSENGER_ListHandles *handles, 
const struct GNUNET_HashCode *key);
+
+#endif //GNUNET_SERVICE_MESSENGER_LIST_HANDLES_H
diff --git a/src/messenger/gnunet-service-messenger_list_messages.c 
b/src/messenger/gnunet-service-messenger_list_messages.c
new file mode 100644
index 000000000..c4f1f7043
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_list_messages.c
@@ -0,0 +1,76 @@
+/*
+   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_list_messages.c
+ * @brief GNUnet MESSENGER service
+ */
+
+#include "gnunet-service-messenger_list_messages.h"
+
+void
+init_list_messages (struct GNUNET_MESSENGER_ListMessages *messages)
+{
+  GNUNET_assert(messages);
+
+  messages->head = NULL;
+  messages->tail = NULL;
+}
+
+void
+clear_list_messages (struct GNUNET_MESSENGER_ListMessages *messages)
+{
+  GNUNET_assert(messages);
+
+  while (messages->head)
+  {
+    struct GNUNET_MESSENGER_ListMessage *element = messages->head;
+
+    GNUNET_CONTAINER_DLL_remove(messages->head, messages->tail, element);
+    GNUNET_free(element);
+  }
+
+  messages->head = NULL;
+  messages->tail = NULL;
+}
+
+void
+add_to_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const 
struct GNUNET_HashCode *hash)
+{
+  struct GNUNET_MESSENGER_ListMessage *element = GNUNET_new(struct 
GNUNET_MESSENGER_ListMessage);
+
+  GNUNET_memcpy(&(element->hash), hash, sizeof(struct GNUNET_HashCode));
+
+  GNUNET_CONTAINER_DLL_insert_tail(messages->head, messages->tail, element);
+}
+
+void
+remove_from_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, 
const struct GNUNET_HashCode *hash)
+{
+  struct GNUNET_MESSENGER_ListMessage *element;
+
+  for (element = messages->head; element; element = element->next)
+    if (0 == GNUNET_CRYPTO_hash_cmp (&(element->hash), hash))
+    {
+      GNUNET_CONTAINER_DLL_remove(messages->head, messages->tail, element);
+      GNUNET_free(element);
+      break;
+    }
+}
diff --git a/src/messenger/gnunet-service-messenger_list_messages.h 
b/src/messenger/gnunet-service-messenger_list_messages.h
new file mode 100644
index 000000000..266c30ec6
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_list_messages.h
@@ -0,0 +1,81 @@
+/*
+   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_list_messages.h
+ * @brief GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_SERVICE_MESSENGER_LIST_MESSAGES_H
+#define GNUNET_SERVICE_MESSENGER_LIST_MESSAGES_H
+
+#include "platform.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_container_lib.h"
+
+struct GNUNET_MESSENGER_ListMessage
+{
+  struct GNUNET_MESSENGER_ListMessage *prev;
+  struct GNUNET_MESSENGER_ListMessage *next;
+
+  struct GNUNET_HashCode hash;
+};
+
+struct GNUNET_MESSENGER_ListMessages
+{
+  struct GNUNET_MESSENGER_ListMessage *head;
+  struct GNUNET_MESSENGER_ListMessage *tail;
+};
+
+/**
+ * Initializes list of message hashes as empty list.
+ *
+ * @param messages List of hashes
+ */
+void
+init_list_messages (struct GNUNET_MESSENGER_ListMessages *messages);
+
+/**
+ * Clears the list of message hashes.
+ *
+ * @param messages List of hashes
+ */
+void
+clear_list_messages (struct GNUNET_MESSENGER_ListMessages *messages);
+
+/**
+ * Adds a specific <i>hash</i> from a message to the end of the list.
+ *
+ * @param messages List of hashes
+ * @param hash Hash of message
+ */
+void
+add_to_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, const 
struct GNUNET_HashCode *hash);
+
+/**
+ * Removes the first entry with a matching <i>hash</i> from the list.
+ *
+ * @param messages List of hashes
+ * @param hash Hash of message
+ */
+void
+remove_from_list_messages (struct GNUNET_MESSENGER_ListMessages *messages, 
const struct GNUNET_HashCode *hash);
+
+#endif //GNUNET_SERVICE_MESSENGER_LIST_MESSAGES_H
diff --git a/src/messenger/gnunet-service-messenger_message_handle.c 
b/src/messenger/gnunet-service-messenger_message_handle.c
new file mode 100644
index 000000000..1652435c8
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_message_handle.c
@@ -0,0 +1,130 @@
+/*
+   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_message_handle.c
+ * @brief GNUnet MESSENGER service
+ */
+
+#include "gnunet-service-messenger_message_handle.h"
+
+void
+handle_message_join (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                     struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+{
+  struct GNUNET_MESSENGER_SrvContact *contact = get_room_contact (room, 
&(message->header.sender_id));
+
+  if (!contact)
+    add_room_contact (room, &(message->header.sender_id), 
&(message->body.join.key));
+
+  struct GNUNET_MESSENGER_MemberInfo *info = get_room_member_info (room, 
&(message->header.sender_id));
+
+  if (!info)
+  {
+    info = GNUNET_new(struct GNUNET_MESSENGER_MemberInfo);
+
+    info->access = GNUNET_MESSENGER_MEMBER_UNKNOWN;
+    init_list_messages (&(info->session_messages));
+  }
+  else
+    clear_list_messages (&(info->session_messages));
+
+  if (GNUNET_YES == GNUNET_CONTAINER_multishortmap_put (room->member_infos, 
&(message->header.sender_id), info,
+                                                        
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
+    add_to_list_messages (&(info->session_messages), hash);
+}
+
+void
+handle_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                      struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+{
+  struct GNUNET_MESSENGER_MemberInfo *info = get_room_member_info (room, 
&(message->header.sender_id));
+
+  if (info)
+    clear_list_messages (&(info->session_messages));
+}
+
+void
+handle_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                     struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+{
+  struct GNUNET_MESSENGER_SrvContact *contact = get_room_contact (room, 
&(message->header.sender_id));
+
+  if (contact)
+    set_contact_name (contact, message->body.name.name);
+
+  struct GNUNET_MESSENGER_MemberInfo *info = get_room_member_info (room, 
&(message->header.sender_id));
+
+  if (info)
+    add_to_list_messages (&(info->session_messages), hash);
+}
+
+void
+handle_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                    struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+{
+  struct GNUNET_MESSENGER_SrvContact *contact = get_room_contact (room, 
&(message->header.sender_id));
+
+  if (contact)
+    swap_service_contact_by_pubkey (room->service, contact, 
&(message->body.key.key));
+
+  struct GNUNET_MESSENGER_MemberInfo *info = get_room_member_info (room, 
&(message->header.sender_id));
+
+  if (info)
+    add_to_list_messages (&(info->session_messages), hash);
+}
+
+void
+handle_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                     struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+{
+  if (GNUNET_NO == contains_list_tunnels (&(room->basement), 
&(message->body.peer.peer)))
+    add_to_list_tunnels (&(room->basement), &(message->body.peer.peer));
+
+  if (room->peer_message)
+    rebuild_room_basement_structure (room);
+}
+
+void
+handle_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+{
+  struct GNUNET_MESSENGER_MemberInfo *info = get_room_member_info (room, 
&(message->header.sender_id));
+
+  if (info)
+    add_to_list_messages (&(info->session_messages), hash);
+
+  switch_room_member_id (room, &(message->header.sender_id), 
&(message->body.id.id), hash);
+}
+
+void
+handle_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                     struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+{
+  struct GNUNET_MESSENGER_ListTunnel *element = find_list_tunnels 
(&(room->basement), &(message->body.peer.peer), NULL);
+
+  if (!element)
+    return;
+
+  remove_from_list_tunnels (&(room->basement), element);
+
+  if (room->peer_message)
+    rebuild_room_basement_structure (room);
+}
diff --git a/src/messenger/gnunet-service-messenger_message_handle.h 
b/src/messenger/gnunet-service-messenger_message_handle.h
new file mode 100644
index 000000000..d091e1d11
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_message_handle.h
@@ -0,0 +1,128 @@
+/*
+   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_message_handle.h
+ * @brief GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_SERVICE_MESSENGER_MESSAGE_HANDLE_H
+#define GNUNET_SERVICE_MESSENGER_MESSAGE_HANDLE_H
+
+#include "platform.h"
+#include "gnunet_crypto_lib.h"
+
+#include "gnunet-service-messenger_message_kind.h"
+
+#include "gnunet-service-messenger_tunnel.h"
+#include "messenger_api_message.h"
+
+/**
+ * Handles a received or sent join message to make changes of current member 
information.
+ * (add matching member and clear member info)
+ *
+ * @param room Room of the message
+ * @param tunnel Receiving/sending connection (may be NULL)
+ * @param message JOIN-Message
+ * @param hash Hash of the message
+ */
+void
+handle_message_join (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                     struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+
+/**
+ * Handles a received or sent leave message to make changes of current member 
information.
+ * (remove matching member and clear member info)
+ *
+ * @param room Room of the message
+ * @param tunnel Receiving/sending connection (may be NULL)
+ * @param message LEAVE-Message
+ * @param hash Hash of the message
+ */
+void
+handle_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                      struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+
+/**
+ * Handles a received or sent name message to rename a current member.
+ * (change name of matching member)
+ *
+ * @param room Room of the message
+ * @param tunnel Receiving/sending connection (may be NULL)
+ * @param message NAME-Message
+ * @param hash Hash of the message
+ */
+void
+handle_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                     struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+
+/**
+ * Handles a received or sent key message to change the key of a member and 
rearrange the contacts accordingly.
+ * (move the member in the contacts and change its key)
+ *
+ * @param room Room of the message
+ * @param tunnel Receiving/sending connection (may be NULL)
+ * @param message KEY-Message
+ * @param hash Hash of the message
+ */
+void
+handle_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                    struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+
+/**
+ * Handles a received or sent peer message to make changes of the basement in 
the room.
+ * (add a new peer to the basement and restructure connections based on 
updated list of peers)
+ *
+ * @param room Room of the message
+ * @param tunnel Receiving/sending connection (may be NULL)
+ * @param message PEER-Message
+ * @param hash Hash of the message
+ */
+void
+handle_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                     struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+
+/**
+ * Handles a received or sent id message to change a members id.
+ * (change id of matching member)
+ *
+ * @param room Room of the message
+ * @param tunnel Receiving/sending connection (may be NULL)
+ * @param message ID-Message
+ * @param hash Hash of the message
+ */
+void
+handle_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+
+/**
+ * Handles a received or sent miss message to drop a peer from the basement in 
the room.
+ * (remove a peer from the basement and restructure connections based on 
updated list of peers)
+ *
+ * @param room Room of the message
+ * @param tunnel Receiving/sending connection (may be NULL)
+ * @param message MISS-Message
+ * @param hash Hash of the message
+ */
+void
+handle_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                     struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+
+#endif //GNUNET_SERVICE_MESSENGER_MESSAGE_HANDLE_H
diff --git a/src/messenger/gnunet-service-messenger_message_kind.c 
b/src/messenger/gnunet-service-messenger_message_kind.c
new file mode 100644
index 000000000..9c829fe09
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_message_kind.c
@@ -0,0 +1,192 @@
+/*
+   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_message_kind.c
+ * @brief GNUnet MESSENGER service
+ */
+
+#include "gnunet-service-messenger_message_kind.h"
+#include "gnunet-service-messenger_util.h"
+
+struct GNUNET_MESSENGER_Message*
+create_message_info (struct GNUNET_MESSENGER_Ego *ego, struct 
GNUNET_CONTAINER_MultiShortmap *members)
+{
+  struct GNUNET_MESSENGER_Message *message = create_message 
(GNUNET_MESSENGER_KIND_INFO);
+
+  if (!message)
+    return NULL;
+
+  GNUNET_memcpy(&(message->body.info.host_key), &(ego->pub), sizeof(ego->pub));
+
+  if (GNUNET_YES == generate_free_member_id (&(message->body.info.unique_id), 
members))
+    return message;
+  else
+  {
+    destroy_message (message);
+    return NULL;
+  }
+}
+
+struct GNUNET_MESSENGER_Message*
+create_message_join (struct GNUNET_MESSENGER_Ego *ego)
+{
+  struct GNUNET_MESSENGER_Message *message = create_message 
(GNUNET_MESSENGER_KIND_JOIN);
+
+  if (!message)
+    return NULL;
+
+  GNUNET_memcpy(&(message->body.join.key), &(ego->pub), sizeof(ego->pub));
+
+  return message;
+}
+
+struct GNUNET_MESSENGER_Message*
+create_message_leave ()
+{
+  return create_message (GNUNET_MESSENGER_KIND_LEAVE);
+}
+
+struct GNUNET_MESSENGER_Message*
+create_message_name (const char *name)
+{
+  struct GNUNET_MESSENGER_Message *message = create_message 
(GNUNET_MESSENGER_KIND_NAME);
+
+  if (!message)
+    return NULL;
+
+  message->body.name.name = GNUNET_strdup(name);
+  return message;
+}
+
+struct GNUNET_MESSENGER_Message*
+create_message_key (const struct GNUNET_IDENTITY_PrivateKey *key)
+{
+  struct GNUNET_MESSENGER_Message *message = create_message 
(GNUNET_MESSENGER_KIND_KEY);
+
+  if (!message)
+    return NULL;
+
+  GNUNET_IDENTITY_key_get_public (key, &(message->body.key.key));
+  return message;
+}
+
+struct GNUNET_MESSENGER_Message*
+create_message_peer (const struct GNUNET_MESSENGER_Service *service)
+{
+  struct GNUNET_MESSENGER_Message *message = create_message 
(GNUNET_MESSENGER_KIND_PEER);
+
+  if (!message)
+    return NULL;
+
+  if (GNUNET_OK == get_service_peer_identity (service, 
&(message->body.peer.peer)))
+    return message;
+  else
+  {
+    destroy_message (message);
+    return NULL;
+  }
+}
+
+struct GNUNET_MESSENGER_Message*
+create_message_id (const struct GNUNET_ShortHashCode *unique_id)
+{
+  struct GNUNET_MESSENGER_Message *message = create_message 
(GNUNET_MESSENGER_KIND_ID);
+
+  if (!message)
+    return NULL;
+
+  GNUNET_memcpy(&(message->body.id.id), unique_id, sizeof(struct 
GNUNET_ShortHashCode));
+
+  return message;
+}
+
+struct GNUNET_MESSENGER_Message*
+create_message_miss (const struct GNUNET_PeerIdentity *peer)
+{
+  struct GNUNET_MESSENGER_Message *message = create_message 
(GNUNET_MESSENGER_KIND_MISS);
+
+  if (!message)
+  {
+    return NULL;
+  }
+
+  GNUNET_memcpy(&(message->body.miss.peer), peer, sizeof(struct 
GNUNET_PeerIdentity));
+
+  return message;
+}
+
+struct GNUNET_MESSENGER_Message*
+create_message_merge (const struct GNUNET_HashCode *previous)
+{
+  struct GNUNET_MESSENGER_Message *message = create_message 
(GNUNET_MESSENGER_KIND_MERGE);
+
+  if (!message)
+    return NULL;
+
+  GNUNET_memcpy(&(message->body.merge.previous), previous, sizeof(struct 
GNUNET_HashCode));
+
+  return message;
+}
+
+struct GNUNET_MESSENGER_Message*
+create_message_request (const struct GNUNET_HashCode *hash)
+{
+  struct GNUNET_HashCode zero;
+  memset (&zero, 0, sizeof(zero));
+
+  if (0 == GNUNET_CRYPTO_hash_cmp (hash, &zero))
+    return NULL;
+
+  struct GNUNET_MESSENGER_Message *message = create_message 
(GNUNET_MESSENGER_KIND_REQUEST);
+
+  if (!message)
+    return NULL;
+
+  GNUNET_memcpy(&(message->body.request.hash), hash, sizeof(struct 
GNUNET_HashCode));
+
+  return message;
+}
+
+struct GNUNET_MESSENGER_Message*
+create_message_invite (const struct GNUNET_PeerIdentity *door, const struct 
GNUNET_HashCode *key)
+{
+  struct GNUNET_MESSENGER_Message *message = create_message 
(GNUNET_MESSENGER_KIND_INVITE);
+
+  if (!message)
+    return NULL;
+
+  GNUNET_memcpy(&(message->body.invite.door), door, sizeof(struct 
GNUNET_PeerIdentity));
+  GNUNET_memcpy(&(message->body.invite.key), key, sizeof(struct 
GNUNET_HashCode));
+
+  return message;
+}
+
+struct GNUNET_MESSENGER_Message*
+create_message_text (const char *text)
+{
+  struct GNUNET_MESSENGER_Message *message = create_message 
(GNUNET_MESSENGER_KIND_TEXT);
+
+  if (!message)
+    return NULL;
+
+  message->body.text.text = GNUNET_strdup(text);
+  return message;
+}
diff --git a/src/messenger/gnunet-service-messenger_message_kind.h 
b/src/messenger/gnunet-service-messenger_message_kind.h
new file mode 100644
index 000000000..dd89d0b2f
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_message_kind.h
@@ -0,0 +1,160 @@
+/*
+   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_message_kind.h
+ * @brief GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_SERVICE_MESSENGER_MESSAGE_KIND_H
+#define GNUNET_SERVICE_MESSENGER_MESSAGE_KIND_H
+
+#include "platform.h"
+#include "gnunet_container_lib.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_identity_service.h"
+
+#include "messenger_api_message.h"
+#include "gnunet-service-messenger_service.h"
+#include "messenger_api_ego.h"
+
+/**
+ * Creates and allocates a new info message containing the hosts public key 
and a newly generated unique member id.
+ * (all values are stored as copy)
+ *
+ * @param ego EGO of the host
+ * @param members Map of all assigned member ids
+ * @return New message
+ */
+struct GNUNET_MESSENGER_Message*
+create_message_info (struct GNUNET_MESSENGER_Ego *ego, struct 
GNUNET_CONTAINER_MultiShortmap *members);
+
+/**
+ * Creates and allocates a new join message containing the clients public key.
+ * (all values are stored as copy)
+ *
+ * @param ego EGO of the client
+ * @return New message
+ */
+struct GNUNET_MESSENGER_Message*
+create_message_join (struct GNUNET_MESSENGER_Ego *ego);
+
+/**
+ * Creates and allocates a new leave message.
+ *
+ * @return New message
+ */
+struct GNUNET_MESSENGER_Message*
+create_message_leave ();
+
+/**
+ * Creates and allocates a new name message containing the <i>name</i> to 
change to.
+ * (all values are stored as copy)
+ *
+ * @param name New name
+ * @return New message
+ */
+struct GNUNET_MESSENGER_Message*
+create_message_name (const char *name);
+
+/**
+ * Creates and allocates a new key message containing the public key to change 
to derived
+ * from its private counterpart. (all values are stored as copy)
+ *
+ * @param key Private key of EGO
+ * @return New message
+ */
+struct GNUNET_MESSENGER_Message*
+create_message_key (const struct GNUNET_IDENTITY_PrivateKey *key);
+
+/**
+ * Creates and allocates a new peer message containing a services peer 
identity.
+ * (all values are stored as copy)
+ *
+ * @param service Service
+ * @return New message
+ */
+struct GNUNET_MESSENGER_Message*
+create_message_peer (const struct GNUNET_MESSENGER_Service *service);
+
+/**
+ * Creates and allocates a new id message containing the unique member id to 
change to.
+ * (all values are stored as copy)
+ *
+ * @param unique_id Unique member id
+ * @return New message
+ */
+struct GNUNET_MESSENGER_Message*
+create_message_id (const struct GNUNET_ShortHashCode *unique_id);
+
+/**
+ * Creates and allocates a new miss message containing the missing peer 
identity.
+ * (all values are stored as copy)
+ *
+ * @param peer Missing peer identity
+ * @return New message
+ */
+struct GNUNET_MESSENGER_Message*
+create_message_miss (const struct GNUNET_PeerIdentity *peer);
+
+/**
+ * Creates and allocates a new merge message containing the hash of a second 
previous message
+ * besides the regular previous message mentioned in a messages header.
+ * (all values are stored as copy)
+ *
+ * @param previous Hash of message
+ * @return New message
+ */
+struct GNUNET_MESSENGER_Message*
+create_message_merge (const struct GNUNET_HashCode *previous);
+
+/**
+ * Creates and allocates a new request message containing the hash of a 
missing message.
+ * (all values are stored as copy)
+ *
+ * @param hash Hash of message
+ * @return New message
+ */
+struct GNUNET_MESSENGER_Message*
+create_message_request (const struct GNUNET_HashCode *hash);
+
+/**
+ * Creates and allocates a new invite message containing the peer identity of 
an entrance peer
+ * to a room using a given <i>key</i> as shared secret for communication.
+ * (all values are stored as copy)
+ *
+ * @param door Peer identity
+ * @param key Shared secret of a room
+ * @return New message
+ */
+struct GNUNET_MESSENGER_Message*
+create_message_invite (const struct GNUNET_PeerIdentity *door, const struct 
GNUNET_HashCode *key);
+
+/**
+ * Creates and allocates a new text message containing a string representing 
text.
+ * (all values are stored as copy)
+ *
+ * @param text Text
+ * @return New message
+ */
+struct GNUNET_MESSENGER_Message*
+create_message_text (const char *text);
+
+#endif //GNUNET_SERVICE_MESSENGER_MESSAGE_KIND_H
diff --git a/src/messenger/gnunet-service-messenger_message_recv.c 
b/src/messenger/gnunet-service-messenger_message_recv.c
new file mode 100644
index 000000000..aa28a36ea
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_message_recv.c
@@ -0,0 +1,204 @@
+/*
+   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_message_recv.c
+ * @brief GNUnet MESSENGER service
+ */
+
+#include "gnunet-service-messenger_message_recv.h"
+#include "gnunet-service-messenger_message_handle.h"
+
+void
+recv_message_info (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+{
+  int conflict = GNUNET_CONTAINER_multishortmap_contains (room->members, 
&(message->body.info.unique_id));
+
+  if (GNUNET_NO == conflict)
+  {
+    struct GNUNET_MESSENGER_Message *sync_message = create_message_id 
(&(message->body.info.unique_id));
+    struct GNUNET_HashCode sync_hash;
+
+    send_room_message_ext (room, room->host, sync_message, &sync_hash, tunnel);
+    destroy_message (sync_message);
+
+    switch_room_member_id (room, get_room_host_id (room), 
&(message->body.info.unique_id), NULL);
+
+    change_room_host_id (room, &(message->body.info.unique_id));
+  }
+
+  if (!tunnel->contact_id)
+    tunnel->contact_id = GNUNET_new(struct GNUNET_ShortHashCode);
+
+  GNUNET_memcpy(tunnel->contact_id, &(message->header.sender_id), 
sizeof(struct GNUNET_ShortHashCode));
+
+  struct GNUNET_ShortHashCode original_id;
+
+  if (GNUNET_YES == conflict)
+  {
+    GNUNET_memcpy(&original_id, get_room_host_id (room), sizeof(struct 
GNUNET_ShortHashCode));
+
+    change_room_host_id (room, &(message->body.info.unique_id));
+  }
+
+  {
+    struct GNUNET_MESSENGER_Message *join_message = create_message_join 
(room->host->ego);
+    struct GNUNET_HashCode join_hash;
+
+    send_tunnel_message (tunnel, room->host, join_message, &join_hash);
+    destroy_message (join_message);
+  }
+
+  if ((GNUNET_YES == conflict) && (0 != GNUNET_memcmp(&original_id, 
get_room_host_id (room))))
+  {
+    struct GNUNET_MESSENGER_Message *sync_message = create_message_id 
(&original_id);
+    struct GNUNET_HashCode sync_hash;
+
+    send_tunnel_message (tunnel, room->host, sync_message, &sync_hash);
+    destroy_message (sync_message);
+  }
+}
+
+struct GNUNET_MESSENGER_MemberInfoSpread
+{
+  struct GNUNET_MESSENGER_SrvRoom *room;
+  struct GNUNET_MESSENGER_SrvTunnel *tunnel;
+};
+
+static int
+iterate_send_member_infos (void *cls, const struct GNUNET_ShortHashCode *key, 
void *value)
+{
+  struct GNUNET_MESSENGER_MemberInfo *info = value;
+  struct GNUNET_MESSENGER_MemberInfoSpread *spread = cls;
+
+  struct GNUNET_MESSENGER_ListMessage *element = info->session_messages.head;
+
+  while (element)
+  {
+    const struct GNUNET_MESSENGER_Message *message = get_room_message 
(spread->room, spread->room->host,
+                                                                       
&(element->hash), GNUNET_NO);
+
+    if (message)
+      forward_tunnel_message (spread->tunnel, message, &(element->hash));
+
+    element = element->next;
+  }
+
+  return GNUNET_YES;
+}
+
+void
+recv_message_join (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+{
+  const struct GNUNET_MESSENGER_Message *info_msg = get_room_message (room, 
room->host, &(message->header.previous),
+                                                                      
GNUNET_NO);
+
+  if ((info_msg) && (0 == GNUNET_memcmp(&(info_msg->header.sender_id), 
get_room_host_id (room)))
+      && (GNUNET_MESSENGER_KIND_INFO == info_msg->header.kind))
+  {
+    struct GNUNET_MESSENGER_MemberInfoSpread spread;
+
+    spread.room = room;
+
+    if ((tunnel) && (tunnel->contact_id) && (0 == 
GNUNET_memcmp(tunnel->contact_id, &(message->header.sender_id))))
+      spread.tunnel = tunnel;
+    else
+      spread.tunnel = find_room_tunnel_to (room, &(message->header.sender_id));
+
+    if (spread.tunnel)
+      GNUNET_CONTAINER_multishortmap_iterate (room->member_infos, 
iterate_send_member_infos, &spread);
+  }
+
+  handle_message_join (room, tunnel, message, hash);
+}
+
+void
+recv_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                    struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+{
+  handle_message_leave (room, tunnel, message, hash);
+}
+
+void
+recv_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+{
+  handle_message_name (room, tunnel, message, hash);
+}
+
+void
+recv_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                  struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+{
+  handle_message_key (room, tunnel, message, hash);
+}
+
+void
+recv_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+{
+  struct GNUNET_PeerIdentity peer;
+  GNUNET_PEER_resolve (tunnel->peer, &peer);
+
+  if (0 == GNUNET_memcmp(&peer, &(message->body.peer.peer)))
+  {
+    if (!tunnel->peer_message)
+      tunnel->peer_message = GNUNET_new(struct GNUNET_HashCode);
+
+    GNUNET_memcpy(tunnel->peer_message, hash, sizeof(struct GNUNET_HashCode));
+
+    if (!tunnel->contact_id)
+      tunnel->contact_id = GNUNET_new(struct GNUNET_ShortHashCode);
+
+    GNUNET_memcpy(tunnel->contact_id, &(message->header.sender_id), 
sizeof(struct GNUNET_ShortHashCode));
+  }
+
+  handle_message_peer (room, tunnel, message, hash);
+}
+
+void
+recv_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                 struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+{
+  if ((tunnel->contact_id) && (0 == GNUNET_memcmp(tunnel->contact_id, 
&(message->header.sender_id))))
+    GNUNET_memcpy(tunnel->contact_id, &(message->body.id.id), sizeof(struct 
GNUNET_ShortHashCode));
+
+  handle_message_id (room, tunnel, message, hash);
+}
+
+void
+recv_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+{
+  handle_message_miss (room, tunnel, message, hash);
+}
+
+void
+recv_message_request (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                      struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+{
+  const struct GNUNET_MESSENGER_Message *msg = get_room_message (room, 
room->host, &(message->body.request.hash),
+                                                                 GNUNET_NO);
+
+  if (msg)
+    forward_tunnel_message (tunnel, msg, &(message->body.request.hash));
+}
diff --git a/src/messenger/gnunet-service-messenger_message_recv.h 
b/src/messenger/gnunet-service-messenger_message_recv.h
new file mode 100644
index 000000000..245612cb0
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_message_recv.h
@@ -0,0 +1,159 @@
+/*
+   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_message_recv.h
+ * @brief GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_SERVICE_MESSENGER_MESSAGE_RECV_H
+#define GNUNET_SERVICE_MESSENGER_MESSAGE_RECV_H
+
+#include "platform.h"
+#include "gnunet_crypto_lib.h"
+
+#include "gnunet-service-messenger_tunnel.h"
+#include "messenger_api_message.h"
+
+/**
+ * Handles a received info message to change the current member id to the one 
generated by
+ * the host connected to. (all current tunnels will be informed about the id 
change)
+ *
+ * @param room Room of the message
+ * @param tunnel Receiving connection
+ * @param message INFO-Message
+ * @param hash Hash of the message
+ */
+void
+recv_message_info (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+
+/**
+ * Handles a received join message to forward all member information to the 
new member if the message was
+ * the direct reaction to a previous info message from this peer.
+ *
+ * @param room Room of the message
+ * @param tunnel Receiving connection
+ * @param message JOIN-Message
+ * @param hash Hash of the message
+ */
+void
+recv_message_join (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+
+/**
+ * Handles a received leave message.
+ * @see handle_message_leave()
+ *
+ * @param room Room of the message
+ * @param tunnel Receiving connection
+ * @param message LEAVE-Message
+ * @param hash Hash of the message
+ */
+void
+recv_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                    struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+
+/**
+ * Handles a received name message.
+ * @see handle_message_name()
+ *
+ * @param room Room of the message
+ * @param tunnel Receiving connection
+ * @param message NAME-Message
+ * @param hash Hash of the message
+ */
+void
+recv_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+
+/**
+ * Handles a received key message.
+ * @see handle_message_key()
+ *
+ * @param room Room of the message
+ * @param tunnel Receiving connection
+ * @param message KEY-Message
+ * @param hash Hash of the message
+ */
+void
+recv_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                  struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+
+/**
+ * Handles a received peer message to link it to its origin tunnel if the peer 
identity matches.
+ * (the peer message and the member id can potentially be linked to the tunnel)
+ *
+ * TODO: This handling will only check the one given tunnel!
+ *
+ * @param room Room of the message
+ * @param tunnel Receiving connection
+ * @param message PEER-Message
+ * @param hash Hash of the message
+ */
+void
+recv_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+
+/**
+ * Handles a received id message to change the tunnels linked member id if 
necessary.
+ * (the tunnels linked member id will be changed if the sender id is matching)
+ *
+ * TODO: This handling will only check the one given tunnel!
+ *
+ * @param room Room of the message
+ * @param tunnel Receiving connection
+ * @param message ID-Message
+ * @param hash Hash of the message
+ */
+void
+recv_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                 struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+
+/**
+ * Handles a received miss message.
+ * @see handle_message_miss()
+ *
+ * @param room Room of the message
+ * @param tunnel Receiving connection
+ * @param message MISS-Message
+ * @param hash Hash of the message
+ */
+void
+recv_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                   struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+
+/**
+ * Handles a received request message by checking for the requested message 
and forwarding it back
+ * if the message was found.
+ * (this can also cause this peer to send a new request instead of only 
forwarding the received one)
+ *
+ * TODO: Requests can cause exponentially more requests!
+ *
+ * @param room Room of the message
+ * @param tunnel Receiving connection
+ * @param message REQUEST-Message
+ * @param hash Hash of the message
+ */
+void
+recv_message_request (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvTunnel *tunnel,
+                      struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+
+#endif //GNUNET_SERVICE_MESSENGER_MESSAGE_RECV_H
diff --git a/src/messenger/gnunet-service-messenger_message_send.c 
b/src/messenger/gnunet-service-messenger_message_send.c
new file mode 100644
index 000000000..86cf9b888
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_message_send.c
@@ -0,0 +1,118 @@
+/*
+   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_message_send.c
+ * @brief GNUnet MESSENGER service
+ */
+
+#include "gnunet-service-messenger_message_send.h"
+#include "gnunet-service-messenger_message_handle.h"
+
+void
+send_message_info (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                   struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
+                   const struct GNUNET_HashCode *hash)
+{
+  if (!tunnel->contact_id)
+  {
+    tunnel->contact_id = GNUNET_new(struct GNUNET_ShortHashCode);
+
+    GNUNET_memcpy(tunnel->contact_id, &(message->body.info.unique_id), 
sizeof(struct GNUNET_ShortHashCode));
+  }
+  else
+  {
+    disconnect_tunnel (tunnel);
+  }
+}
+
+void
+send_message_join (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                   struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
+                   const struct GNUNET_HashCode *hash)
+{
+  handle_message_join (room, tunnel, message, hash);
+
+  if (room->peer_message)
+  {
+    const struct GNUNET_MESSENGER_Message *peer_message = get_room_message 
(room, handle, room->peer_message,
+                                                                            
GNUNET_NO);
+
+    if ((peer_message) && (tunnel))
+    {
+      forward_tunnel_message (tunnel, peer_message, room->peer_message);
+    }
+  }
+}
+
+void
+send_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                    struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
+                    const struct GNUNET_HashCode *hash)
+{
+  handle_message_leave (room, tunnel, message, hash);
+}
+
+void
+send_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                   struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
+                   const struct GNUNET_HashCode *hash)
+{
+  handle_message_name (room, tunnel, message, hash);
+}
+
+void
+send_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                  struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
+                  const struct GNUNET_HashCode *hash)
+{
+  handle_message_key (room, tunnel, message, hash);
+}
+
+void
+send_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                   struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
+                   const struct GNUNET_HashCode *hash)
+{
+  if (!room->peer_message)
+  {
+    room->peer_message = GNUNET_new(struct GNUNET_HashCode);
+  }
+
+  GNUNET_memcpy(room->peer_message, hash, sizeof(struct GNUNET_HashCode));
+
+  handle_message_peer (room, tunnel, message, hash);
+}
+
+void
+send_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                 struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
+                 const struct GNUNET_HashCode *hash)
+{
+  handle_message_id (room, tunnel, message, hash);
+}
+
+void
+send_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                   struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
+                   const struct GNUNET_HashCode *hash)
+{
+  handle_message_miss (room, tunnel, message, hash);
+}
diff --git a/src/messenger/gnunet-service-messenger_message_send.h 
b/src/messenger/gnunet-service-messenger_message_send.h
new file mode 100644
index 000000000..c1096205a
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_message_send.h
@@ -0,0 +1,155 @@
+/*
+   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_message_send.h
+ * @brief GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_SERVICE_MESSENGER_MESSAGE_SEND_H
+#define GNUNET_SERVICE_MESSENGER_MESSAGE_SEND_H
+
+#include "platform.h"
+#include "gnunet_crypto_lib.h"
+
+#include "gnunet-service-messenger_tunnel.h"
+#include "messenger_api_message.h"
+
+/**
+ * Handles a sent info message to setup a tunnels linked member id.
+ * (if a tunnel has already got a member id linked to it, the connection will 
be closed)
+ *
+ * @param room Room of the message
+ * @param handle Sending handle
+ * @param tunnel Sending connection (may be NULL)
+ * @param message INFO-Message
+ * @param hash Hash of the message
+ */
+void
+send_message_info (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                   struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
+                   const struct GNUNET_HashCode *hash);
+
+/**
+ * Handles a sent join message to ensure growth of the decentralized room 
structure.
+ * (if the service provides a peer message for this room currently, it will be 
forwarded)
+ *
+ * @param room Room of the message
+ * @param handle Sending handle
+ * @param tunnel Sending connection (may be NULL)
+ * @param message JOIN-Message
+ * @param hash Hash of the message
+ */
+void
+send_message_join (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                   struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
+                   const struct GNUNET_HashCode *hash);
+
+/**
+ * Handles a sent leave message.
+ * @see handle_message_leave()
+ *
+ * @param room Room of the message
+ * @param handle Sending handle
+ * @param tunnel Sending connection (may be NULL)
+ * @param message LEAVE-Message
+ * @param hash Hash of the message
+ */
+void
+send_message_leave (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                    struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
+                    const struct GNUNET_HashCode *hash);
+
+/**
+ * Handles a sent name message.
+ * @see handle_message_name()
+ *
+ * @param room Room of the message
+ * @param handle Sending handle
+ * @param tunnel Sending connection (may be NULL)
+ * @param message NAME-Message
+ * @param hash Hash of the message
+ */
+void
+send_message_name (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                   struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
+                   const struct GNUNET_HashCode *hash);
+
+/**
+ * Handles a sent key message.
+ * @see handle_message_key()
+ *
+ * @param room Room of the message
+ * @param handle Sending handle
+ * @param tunnel Sending connection (may be NULL)
+ * @param message KEY-Message
+ * @param hash Hash of the message
+ */
+void
+send_message_key (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                  struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
+                  const struct GNUNET_HashCode *hash);
+
+/**
+ * Handles a sent peer message to update the rooms peer message of this 
service.
+ * (a set peer message indicates this service being a part of the 
decentralized room structure)
+ *
+ * @param room Room of the message
+ * @param handle Sending handle
+ * @param tunnel Sending connection (may be NULL)
+ * @param message PEER-Message
+ * @param hash Hash of the message
+ */
+void
+send_message_peer (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                   struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
+                   const struct GNUNET_HashCode *hash);
+
+/**
+ * Handles a sent id message.
+ * @see handle_message_id()
+ *
+ * @param room Room of the message
+ * @param handle Sending handle
+ * @param tunnel Sending connection (may be NULL)
+ * @param message ID-Message
+ * @param hash Hash of the message
+ */
+void
+send_message_id (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                 struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
+                 const struct GNUNET_HashCode *hash);
+
+/**
+ * Handles a sent miss message.
+ * @see handle_message_miss()
+ *
+ * @param room Room of the message
+ * @param handle Sending handle
+ * @param tunnel Sending connection (may be NULL)
+ * @param message MISS-Message
+ * @param hash Hash of the message
+ */
+void
+send_message_miss (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                   struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_MESSENGER_Message *message,
+                   const struct GNUNET_HashCode *hash);
+
+#endif //GNUNET_SERVICE_MESSENGER_MESSAGE_SEND_H
diff --git a/src/messenger/gnunet-service-messenger_message_store.c 
b/src/messenger/gnunet-service-messenger_message_store.c
new file mode 100644
index 000000000..5933d6390
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_message_store.c
@@ -0,0 +1,282 @@
+/*
+   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_message_store.c
+ * @brief GNUnet MESSENGER service
+ */
+
+#include "gnunet-service-messenger_message_store.h"
+#include "messenger_api_message.h"
+
+void
+init_message_store (struct GNUNET_MESSENGER_MessageStore *store)
+{
+  store->storage_messages = NULL;
+
+  store->entries = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
+  store->messages = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
+}
+
+static int
+iterate_destroy_entries (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct GNUNET_MESSENGER_MessageEntry *entry = value;
+
+  GNUNET_free(entry);
+
+  return GNUNET_YES;
+}
+
+static int
+iterate_destroy_messages (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct GNUNET_MESSENGER_Message *message = value;
+
+  destroy_message (message);
+
+  return GNUNET_YES;
+}
+
+void
+clear_message_store (struct GNUNET_MESSENGER_MessageStore *store)
+{
+  if (store->storage_messages)
+  {
+    GNUNET_DISK_file_close (store->storage_messages);
+
+    store->storage_messages = NULL;
+  }
+
+  GNUNET_CONTAINER_multihashmap_iterate (store->entries, 
iterate_destroy_entries, NULL);
+  GNUNET_CONTAINER_multihashmap_iterate (store->messages, 
iterate_destroy_messages, NULL);
+
+  GNUNET_CONTAINER_multihashmap_destroy (store->entries);
+  GNUNET_CONTAINER_multihashmap_destroy (store->messages);
+}
+
+struct GNUNET_MESSENGER_MessageEntryStorage
+{
+  struct GNUNET_HashCode hash;
+  struct GNUNET_MESSENGER_MessageEntry entry;
+};
+
+void
+load_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char 
*directory)
+{
+  enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ 
| GNUNET_DISK_PERM_USER_WRITE);
+
+  if (store->storage_messages)
+    GNUNET_DISK_file_close (store->storage_messages);
+
+  char *filename;
+  GNUNET_asprintf (&filename, "%s%s", directory, "messages.store");
+
+  if (GNUNET_YES == GNUNET_DISK_file_test (filename))
+    store->storage_messages = GNUNET_DISK_file_open (filename, 
GNUNET_DISK_OPEN_READ, permission);
+  else
+    store->storage_messages = NULL;
+
+  GNUNET_free(filename);
+
+  if (!store->storage_messages)
+    return;
+
+  GNUNET_asprintf (&filename, "%s%s", directory, "entries.store");
+
+  if (GNUNET_YES != GNUNET_DISK_file_test (filename))
+    goto free_filename;
+
+  struct GNUNET_DISK_FileHandle *entries = GNUNET_DISK_file_open (filename, 
GNUNET_DISK_OPEN_READ, permission);
+
+  if (!entries)
+    goto free_filename;
+
+  struct GNUNET_MESSENGER_MessageEntryStorage storage;
+  struct GNUNET_MESSENGER_MessageEntry *entry;
+
+  do
+  {
+    entry = GNUNET_new(struct GNUNET_MESSENGER_MessageEntry);
+
+    if (GNUNET_DISK_file_read (entries, &storage, sizeof(storage)) == 
sizeof(storage))
+    {
+      GNUNET_memcpy(entry, &(storage.entry), sizeof(*entry));
+
+      if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (store->entries, 
&(storage.hash), entry,
+                                                          
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
+        GNUNET_free(entry);
+    }
+    else
+    {
+      GNUNET_free(entry);
+
+      entry = NULL;
+    }
+  }
+  while (entry);
+
+  GNUNET_DISK_file_close (entries);
+
+free_filename:
+  GNUNET_free(filename);
+}
+
+struct GNUNET_MESSENGER_MessageSave
+{
+  struct GNUNET_MESSENGER_MessageStore *store;
+
+  struct GNUNET_DISK_FileHandle *storage_entries;
+};
+
+static int
+iterate_save_messages (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct GNUNET_MESSENGER_MessageSave *save = cls;
+
+  if (GNUNET_NO != GNUNET_CONTAINER_multihashmap_contains 
(save->store->entries, key))
+    return GNUNET_YES;
+
+  struct GNUNET_MESSENGER_Message *message = value;
+  struct GNUNET_MESSENGER_MessageEntryStorage storage;
+
+  GNUNET_memcpy(&(storage.hash), key, sizeof(storage.hash));
+
+  storage.entry.length = get_message_size (message);
+  storage.entry.offset = GNUNET_DISK_file_seek (save->store->storage_messages, 
0, GNUNET_DISK_SEEK_END);
+
+  if ((GNUNET_SYSERR == storage.entry.offset) ||
+      (sizeof(storage) != GNUNET_DISK_file_write (save->storage_entries, 
&storage, sizeof(storage))))
+    return GNUNET_YES;
+
+  char *buffer = GNUNET_malloc(storage.entry.length);
+
+  encode_message (message, storage.entry.length, buffer);
+
+  GNUNET_DISK_file_write (save->store->storage_messages, buffer, 
storage.entry.length);
+
+  GNUNET_free(buffer);
+
+  return GNUNET_YES;
+}
+
+void
+save_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char 
*directory)
+{
+  struct GNUNET_MESSENGER_MessageSave save;
+
+  enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ 
| GNUNET_DISK_PERM_USER_WRITE);
+
+  char *filename;
+  GNUNET_asprintf (&filename, "%s%s", directory, "entries.store");
+
+  save.store = store;
+  save.storage_entries = GNUNET_DISK_file_open (filename, 
GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE, permission);
+
+  GNUNET_free(filename);
+
+  if (!save.storage_entries)
+    return;
+
+  if (GNUNET_SYSERR == GNUNET_DISK_file_seek (save.storage_entries, 0, 
GNUNET_DISK_SEEK_END))
+    goto close_entries;
+
+  if (store->storage_messages)
+    GNUNET_DISK_file_close (store->storage_messages);
+
+  GNUNET_asprintf (&filename, "%s%s", directory, "messages.store");
+
+  store->storage_messages = GNUNET_DISK_file_open (filename, 
GNUNET_DISK_OPEN_READWRITE | GNUNET_DISK_OPEN_CREATE,
+                                                   permission);
+
+  GNUNET_free(filename);
+
+  if (store->storage_messages)
+  {
+    GNUNET_CONTAINER_multihashmap_iterate (store->messages, 
iterate_save_messages, &save);
+
+    GNUNET_DISK_file_sync (store->storage_messages);
+    GNUNET_DISK_file_sync (save.storage_entries);
+  }
+
+close_entries:
+  GNUNET_DISK_file_close (save.storage_entries);
+}
+
+int
+contains_store_message (struct GNUNET_MESSENGER_MessageStore *store, const 
struct GNUNET_HashCode *hash)
+{
+  if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (store->messages, 
hash))
+    return GNUNET_YES;
+
+  return GNUNET_CONTAINER_multihashmap_contains (store->entries, hash);
+}
+
+const struct GNUNET_MESSENGER_Message*
+get_store_message (struct GNUNET_MESSENGER_MessageStore *store, const struct 
GNUNET_HashCode *hash)
+{
+  struct GNUNET_MESSENGER_Message *message = GNUNET_CONTAINER_multihashmap_get 
(store->messages, hash);
+
+  if (message)
+    return message;
+
+  if (!store->storage_messages)
+    return NULL;
+
+  const struct GNUNET_MESSENGER_MessageEntry *entry = 
GNUNET_CONTAINER_multihashmap_get (store->entries, hash);
+
+  if (!entry)
+    return NULL;
+
+  if (entry->offset != GNUNET_DISK_file_seek (store->storage_messages, 
entry->offset, GNUNET_DISK_SEEK_SET))
+    return message;
+
+  char *buffer = GNUNET_malloc(entry->length);
+
+  if (GNUNET_DISK_file_read (store->storage_messages, buffer, entry->length) 
!= entry->length)
+    goto free_buffer;
+
+
+  message = create_message (GNUNET_MESSENGER_KIND_UNKNOWN);
+
+  if ((GNUNET_YES != decode_message (message, entry->length, buffer)) || 
(GNUNET_OK
+      != GNUNET_CONTAINER_multihashmap_put (store->messages, hash, message,
+                                            
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)))
+  {
+    destroy_message (message);
+
+    message = NULL;
+
+    GNUNET_CONTAINER_multihashmap_remove (store->entries, hash, entry);
+  }
+
+free_buffer:
+  GNUNET_free(buffer);
+
+  return message;
+}
+
+int
+put_store_message (struct GNUNET_MESSENGER_MessageStore *store, const struct 
GNUNET_HashCode *hash,
+                   struct GNUNET_MESSENGER_Message *message)
+{
+  return GNUNET_CONTAINER_multihashmap_put (store->messages, hash, message,
+                                            
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+}
diff --git a/src/messenger/gnunet-service-messenger_message_store.h 
b/src/messenger/gnunet-service-messenger_message_store.h
new file mode 100644
index 000000000..e58459b21
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_message_store.h
@@ -0,0 +1,120 @@
+/*
+   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_message_store.h
+ * @brief GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_SERVICE_MESSENGER_MESSAGE_STORE_H
+#define GNUNET_SERVICE_MESSENGER_MESSAGE_STORE_H
+
+#include "platform.h"
+#include "gnunet_container_lib.h"
+#include "gnunet_disk_lib.h"
+
+struct GNUNET_MESSENGER_MessageEntry
+{
+  off_t offset;
+  uint16_t length;
+};
+
+struct GNUNET_MESSENGER_MessageStore
+{
+  struct GNUNET_DISK_FileHandle *storage_messages;
+
+  struct GNUNET_CONTAINER_MultiHashMap *entries;
+  struct GNUNET_CONTAINER_MultiHashMap *messages;
+};
+
+/**
+ * Initializes a message store as fully empty.
+ *
+ * @param store Message store
+ */
+void
+init_message_store (struct GNUNET_MESSENGER_MessageStore *store);
+
+/**
+ * Clears a message store, wipes its content and deallocates its memory.
+ *
+ * @param store Message store
+ */
+void
+clear_message_store (struct GNUNET_MESSENGER_MessageStore *store);
+
+/**
+ * Loads messages from a directory into a message store.
+ *
+ * @param store Message store
+ * @param directory Path to a directory
+ */
+void
+load_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char 
*directory);
+
+/**
+ * Saves messages from a message store into a directory.
+ *
+ * @param store Message store
+ * @param directory Path to a directory
+ */
+void
+save_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char 
*directory);
+
+/**
+ * Checks if a message matching a given <i>hash</i> is stored in a message 
store. The function returns
+ * GNUNET_YES if a match is found, GNUNET_NO otherwise.
+ *
+ * The message has not to be loaded from disk into memory for this check!
+ *
+ * @param store Message store
+ * @param hash Hash of message
+ * @return GNUNET_YES on match, otherwise GNUNET_NO
+ */
+int
+contains_store_message (struct GNUNET_MESSENGER_MessageStore *store, const 
struct GNUNET_HashCode *hash);
+
+/**
+ * Returns the message from a message store matching a given <i>hash</i>. If 
no matching message is found,
+ * NULL gets returned.
+ *
+ * This function requires the message to be loaded into memory!
+ * @see contains_store_message()
+ *
+ * @param store Message store
+ * @param hash Hash of message
+ * @return Message or NULL
+ */
+const struct GNUNET_MESSENGER_Message*
+get_store_message (struct GNUNET_MESSENGER_MessageStore *store, const struct 
GNUNET_HashCode *hash);
+
+/**
+ * Stores a message into the message store. The result indicates if the 
operation was successful.
+ *
+ * @param store Message store
+ * @param hash Hash of message
+ * @param message Message
+ * @return GNUNET_OK on success, otherwise GNUNET_NO
+ */
+int
+put_store_message (struct GNUNET_MESSENGER_MessageStore *store, const struct 
GNUNET_HashCode *hash,
+                   struct GNUNET_MESSENGER_Message *message);
+
+#endif //GNUNET_SERVICE_MESSENGER_MESSAGE_STORE_H
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);
+  }
+}
diff --git a/src/messenger/gnunet-service-messenger_room.h 
b/src/messenger/gnunet-service-messenger_room.h
new file mode 100644
index 000000000..36c9e8cf5
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_room.h
@@ -0,0 +1,378 @@
+/*
+   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.h
+ * @brief GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_SERVICE_MESSENGER_ROOM_H
+#define GNUNET_SERVICE_MESSENGER_ROOM_H
+
+#include "platform.h"
+#include "gnunet_cadet_service.h"
+#include "gnunet_container_lib.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_identity_service.h"
+#include "gnunet_mq_lib.h"
+
+#include "gnunet-service-messenger_contact.h"
+
+#include "gnunet_messenger_service.h"
+#include "gnunet-service-messenger_basement.h"
+#include "gnunet-service-messenger_handle.h"
+#include "gnunet-service-messenger_tunnel.h"
+
+#include "gnunet-service-messenger_list_messages.h"
+#include "messenger_api_list_tunnels.h"
+
+#include "gnunet-service-messenger_message_store.h"
+#include "messenger_api_ego.h"
+
+enum GNUNET_MESSENGER_MemberAccess
+{
+  GNUNET_MESSENGER_MEMBER_ALLOWED = 1,
+  GNUNET_MESSENGER_MEMBER_BLOCKED = 1,
+
+  GNUNET_MESSENGER_MEMBER_UNKNOWN = 0
+};
+
+struct GNUNET_MESSENGER_MemberInfo
+{
+  enum GNUNET_MESSENGER_MemberAccess access;
+
+  struct GNUNET_MESSENGER_ListMessages session_messages;
+};
+
+struct GNUNET_MESSENGER_SrvRoom
+{
+  struct GNUNET_MESSENGER_Service *service;
+  struct GNUNET_MESSENGER_SrvHandle *host;
+  struct GNUNET_CADET_Port *port;
+
+  struct GNUNET_HashCode key;
+
+  struct GNUNET_CONTAINER_MultiPeerMap *tunnels;
+  struct GNUNET_CONTAINER_MultiShortmap *members;
+  struct GNUNET_CONTAINER_MultiShortmap *member_infos;
+
+  struct GNUNET_MESSENGER_MessageStore store;
+  struct GNUNET_CONTAINER_MultiHashMap *requested;
+
+  struct GNUNET_MESSENGER_ListTunnels basement;
+  struct GNUNET_MESSENGER_ListMessages last_messages;
+
+  struct GNUNET_HashCode *peer_message;
+
+  struct GNUNET_MESSENGER_ListMessages handling;
+  struct GNUNET_SCHEDULER_Task *idle;
+
+  int strict_access;
+};
+
+/**
+ * Creates and allocates a new room for a <i>handle</i> with a given 
<i>key</i>.
+ *
+ * @param handle Handle
+ * @param key Key of room
+ * @return New room
+ */
+struct GNUNET_MESSENGER_SrvRoom*
+create_room (struct GNUNET_MESSENGER_SrvHandle *handle, const struct 
GNUNET_HashCode *key);
+
+/**
+ * Destroys a room and frees its memory fully.
+ *
+ * @param room Room
+ */
+void
+destroy_room (struct GNUNET_MESSENGER_SrvRoom *room);
+
+/**
+ * Returns the contact of a member in a <i>room</i> identified by a given 
<i>id</i>. If the <i>room</i>
+ * does not contain a member with the given <i>id</i>, NULL gets returned.
+ *
+ * @param room Room
+ * @param id Member id
+ * @return Contact or NULL
+ */
+struct GNUNET_MESSENGER_SrvContact*
+get_room_contact (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_ShortHashCode *id);
+
+/**
+ * Adds a contact from the service to a <i>room</i> under a specific <i>id</i> 
with a given public key.
+ *
+ * @param room Room
+ * @param id Member id
+ * @param pubkey Public key of EGO
+ */
+void
+add_room_contact (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_ShortHashCode *id,
+                  const struct GNUNET_IDENTITY_PublicKey *pubkey);
+
+/**
+ * Returns the member information of a member in a <i>room</i> identified by a 
given <i>id</i>. If the <i>room</i>
+ * does not contain a member with the given <i>id</i>, NULL gets returned.
+ *
+ * @param room Room
+ * @param id Member id
+ * @return Member information or NULL
+ */
+struct GNUNET_MESSENGER_MemberInfo*
+get_room_member_info (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_ShortHashCode *id);
+
+/**
+ * Tries to generate and allocate a new unique member id checking all current 
members for possible
+ * duplicates. If the function fails, NULL gets returned.
+ *
+ * @param room Room
+ * @return New member id or NULL
+ */
+struct GNUNET_ShortHashCode*
+generate_room_member_id (const struct GNUNET_MESSENGER_SrvRoom *room);
+
+/**
+ * Returns the member id of the member representing the handle currently 
hosting this <i>room</i>.
+ *
+ * @param room Room
+ * @return Host member id or NULL
+ */
+const struct GNUNET_ShortHashCode*
+get_room_host_id (const struct GNUNET_MESSENGER_SrvRoom *room);
+
+/**
+ * Changes the member id of the member representing the handle currently 
hosting this <i>room</i>.
+ *
+ * @param room Room
+ * @param unique_id Unique member id
+ */
+void
+change_room_host_id (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_ShortHashCode *unique_id);
+
+/**
+ * Tries to open a <i>room</i> for a given <i>handle</i>. If the room has 
already been opened, the handle
+ * will locally join the room.
+ *
+ * Calling this method should result in joining a room and sending a peer 
message as well for this peer.
+ *
+ * If the function returns GNUNET_YES the port for this room is guranteed to 
be open for incoming connections.
+ *
+ * @param room Room
+ * @param handle Handle
+ * @return GNUNET_YES on success, GNUNET_NO on failure.
+ */
+int
+open_room (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle);
+
+/**
+ * Connects a tunnel to a hosting peer of a <i>room</i> through a so called 
<i>door</i> which is represented by
+ * a peer identity of a hosting peer. During the connection the handle will 
join the room as a member, waiting for
+ * an info message from the selected host.
+ *
+ * @param room Room
+ * @param handle Handle
+ * @param door Peer identity
+ * @return GNUNET_YES on success, GNUNET_NO on failure.
+ */
+int
+entry_room_at (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+               const struct GNUNET_PeerIdentity *door);
+
+/**
+ * Returns a tunnel granting a direct connection to a specific member in a 
<i>room</i>. The member gets identified
+ * by an <i>id</i>. If no tunnel has been linked to the selected id, NULL gets 
returned.
+ *
+ * @param room Room
+ * @param contact_id Member id
+ * @return Tunnel to the member or NULL
+ */
+struct GNUNET_MESSENGER_SrvTunnel*
+find_room_tunnel_to (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_ShortHashCode *contact_id);
+
+/**
+ * Packs a <i>message</i> depending on the selected <i>mode</i> into a newly 
allocated envelope. It will set the
+ * timestamp of the message, the sender id and the previous messages hash 
automatically before packing. The message
+ * will be signed by the handles EGO.
+ *
+ * If the optional <i>hash</i> parameter is a valid pointer, its value will be 
overriden by the signed messages hash.
+ *
+ * If <i>mode</i> is set to GNUNET_MESSENGER_PACK_MODE_ENVELOPE, the function 
returns a valid envelope to send
+ * through a message queue, otherwise NULL.
+ *
+ * @param room Room
+ * @param handle Handle
+ * @param message Message
+ * @param[out] hash Hash of message
+ * @param mode Packing mode
+ * @return New envelope or NULL
+ */
+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);
+
+/**
+ * Sends a <i>message</i> from a given <i>handle</i> into a <i>room</i>. The 
<i>hash</i> parameter will be
+ * updated with the hash-value resulting from the sent message.
+ *
+ * The function handles packing the message automatically and will call linked 
message-events locally even if
+ * the message won't be sent to another peer.
+ *
+ * @param room Room
+ * @param handle Handle
+ * @param message Message
+ * @param[out] hash Hash of message
+ */
+void
+send_room_message (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                   struct GNUNET_MESSENGER_Message *message, struct 
GNUNET_HashCode *hash);
+
+/**
+ * Sends a <i>message</i> from a given <i>handle</i> into a <i>room</i> 
excluding one specific <i>tunnel</i>.
+ * The <i>hash</i> parameter will be updated with the hash-value resulting 
from the sent message.
+ *
+ * The function handles packing the message automatically and will call linked 
message-events locally even if
+ * the message won't be sent to another peer.
+ *
+ * @param room Room
+ * @param handle Handle
+ * @param message Message
+ * @param[out] hash Hash of message
+ * @param tunnel Tunnel
+ */
+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);
+
+/**
+ * Forwards a <i>message</i> with a given <i>hash</i> to a specific 
<i>tunnel</i> inside of a <i>room</i>.
+ *
+ * @param room Room
+ * @param tunnel Tunnel
+ * @param message Message
+ * @param hash Hash of message
+ */
+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);
+
+/**
+ * Reduces all current forks inside of the message history of a <i>room</i> to 
one remaining last message
+ * by merging them down. All merge messages will be sent from a given 
<i>handle</i>.
+ *
+ * @param room Room
+ * @param handle Handle
+ */
+void
+merge_room_last_messages (struct GNUNET_MESSENGER_SrvRoom *room, struct 
GNUNET_MESSENGER_SrvHandle *handle);
+
+/**
+ * Returns the CADET handle from a rooms service.
+ *
+ * @param room Room
+ * @return CADET handle
+ */
+struct GNUNET_CADET_Handle*
+get_room_cadet (struct GNUNET_MESSENGER_SrvRoom *room);
+
+/**
+ * Returns the shared secret you need to access a <i>room</i>.
+ *
+ * @param room Room
+ * @return Shared secret
+ */
+struct GNUNET_HashCode*
+get_room_key (struct GNUNET_MESSENGER_SrvRoom *room);
+
+/**
+ * Returns a tunnel inside of a <i>room</i> leading towards a given 
<i>peer</i> if such a tunnel exists,
+ * otherwise NULL.
+ *
+ * @param room Room
+ * @param peer Peer identity
+ * @return Tunnel or NULL
+ */
+const struct GNUNET_MESSENGER_SrvTunnel*
+get_room_tunnel (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_PeerIdentity *peer);
+
+/**
+ * Returns a message from a <i>room</i> identified by a given <i>hash</i>. If 
no matching message is
+ * found and <i>request</i> is set to GNUNET_YES, the <i>handle</i> will 
request the missing message
+ * automatically.
+ *
+ * The function uses the optimized check for a message via its hash from the 
message store.
+ * @see contains_store_message()
+ *
+ * If a message is missing independent of the following request, NULL gets 
returned instead of the
+ * matching message.
+ *
+ * @param room Room
+ * @param handle Handle
+ * @param hash Hash of message
+ * @param request Flag to request a message
+ * @return Message or NULL
+ */
+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);
+
+/**
+ * Updates the last messages of a <i>room</i> by replacing them if the 
previous hash of a given <i>message</i>
+ * matches with one of the latest messages.
+ *
+ * @param room Room
+ * @param message Message
+ * @param hash Hash of message
+ */
+void
+update_room_last_messages (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_MESSENGER_Message *message,
+                           const struct GNUNET_HashCode *hash);
+
+/**
+ * Changes an id of a current member from an old id to a new one and adds 
optionally the <i>hash</i> of an
+ * id message to the members information.
+ *
+ * @param room Room
+ * @param old_id Old member id
+ * @param new_id New member id
+ * @param hash Hash of id message
+ */
+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);
+
+/**
+ * Rebuilds the decentralized structure for a <i>room</i> by ensuring all 
required connections are made
+ * depending on the amount of peers and this peers index in the list of them.
+ *
+ * @param room Room
+ */
+void
+rebuild_room_basement_structure (struct GNUNET_MESSENGER_SrvRoom *room);
+
+/**
+ * Handles all queued up messages of a room to handle in correct order.
+ *
+ * @param room Room
+ */
+void
+handle_room_messages (struct GNUNET_MESSENGER_SrvRoom *room);
+
+#endif //GNUNET_SERVICE_MESSENGER_ROOM_H
diff --git a/src/messenger/gnunet-service-messenger_service.c 
b/src/messenger/gnunet-service-messenger_service.c
new file mode 100644
index 000000000..963314fd8
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_service.c
@@ -0,0 +1,516 @@
+/*
+   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_service.c
+ * @brief GNUnet MESSENGER service
+ */
+
+#include "gnunet-service-messenger_service.h"
+
+#include "gnunet-service-messenger_message_kind.h"
+
+#include "gnunet-service-messenger.h"
+#include "gnunet-service-messenger_util.h"
+
+static void
+callback_shutdown_service (void *cls)
+{
+  struct GNUNET_MESSENGER_Service *service = cls;
+
+  if (service)
+  {
+    service->shutdown = NULL;
+
+    destroy_service (service);
+  }
+}
+
+static void
+callback_update_ego (void *cls,
+                     struct GNUNET_IDENTITY_Ego *ego,
+                     void **ctx,
+                     const char *identifier)
+{
+  if ((!ego) || (!identifier))
+    return;
+
+  struct GNUNET_MESSENGER_Service *service = cls;
+
+  update_service_ego(service, identifier, 
GNUNET_IDENTITY_ego_get_private_key(ego));
+}
+
+struct GNUNET_MESSENGER_Service*
+create_service (const struct GNUNET_CONFIGURATION_Handle *config, struct 
GNUNET_SERVICE_Handle *service_handle)
+{
+  struct GNUNET_MESSENGER_Service *service = GNUNET_new(struct 
GNUNET_MESSENGER_Service);
+
+  service->config = config;
+  service->service = service_handle;
+
+  service->shutdown = GNUNET_SCHEDULER_add_shutdown 
(&callback_shutdown_service, service);
+
+  service->dir = NULL;
+
+  if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (service->config,
+  GNUNET_MESSENGER_SERVICE_NAME,
+                                                            "MESSENGER_DIR", 
&(service->dir)))
+  {
+    if (service->dir)
+      GNUNET_free(service->dir);
+
+    service->dir = NULL;
+  }
+  else
+  {
+    if ((GNUNET_YES != GNUNET_DISK_directory_test (service->dir, GNUNET_YES)) 
&& (GNUNET_OK
+        != GNUNET_DISK_directory_create (service->dir)))
+    {
+      GNUNET_free(service->dir);
+
+      service->dir = NULL;
+    }
+  }
+
+  service->cadet = GNUNET_CADET_connect (service->config);
+  service->identity = GNUNET_IDENTITY_connect (service->config, 
&callback_update_ego, service);
+
+  service->egos = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
+
+  init_list_handles (&(service->handles));
+
+  service->contacts = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
+  service->rooms = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
+
+  return service;
+}
+
+static int
+iterate_destroy_egos (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct GNUNET_MESSENGER_Ego *ego = value;
+  GNUNET_free(ego);
+  return GNUNET_YES;
+}
+
+static int
+iterate_destroy_rooms (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct GNUNET_MESSENGER_SrvRoom *room = value;
+  destroy_room (room);
+  return GNUNET_YES;
+}
+
+static int
+iterate_destroy_contacts (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct GNUNET_MESSENGER_SrvContact *contact = value;
+  destroy_contact (contact);
+  return GNUNET_YES;
+}
+
+void
+destroy_service (struct GNUNET_MESSENGER_Service *service)
+{
+  if (service->shutdown)
+  {
+    GNUNET_SCHEDULER_cancel (service->shutdown);
+
+    service->shutdown = NULL;
+  }
+
+  GNUNET_CONTAINER_multihashmap_iterate (service->egos, iterate_destroy_egos, 
NULL);
+
+  clear_list_handles (&(service->handles));
+
+  GNUNET_CONTAINER_multihashmap_iterate (service->rooms, 
iterate_destroy_rooms, NULL);
+  GNUNET_CONTAINER_multihashmap_iterate (service->contacts, 
iterate_destroy_contacts, NULL);
+
+  GNUNET_CONTAINER_multihashmap_destroy (service->egos);
+  GNUNET_CONTAINER_multihashmap_destroy (service->rooms);
+  GNUNET_CONTAINER_multihashmap_destroy (service->contacts);
+
+  if (service->cadet)
+  {
+    GNUNET_CADET_disconnect (service->cadet);
+
+    service->cadet = NULL;
+  }
+
+  if (service->identity)
+  {
+    GNUNET_IDENTITY_disconnect (service->identity);
+
+    service->identity = NULL;
+  }
+
+  if (service->dir)
+  {
+    GNUNET_free(service->dir);
+
+    service->dir = NULL;
+  }
+
+  GNUNET_SERVICE_shutdown (service->service);
+
+  GNUNET_free(service);
+}
+
+struct GNUNET_MESSENGER_Ego*
+lookup_service_ego (struct GNUNET_MESSENGER_Service *service, const char 
*identifier)
+{
+  GNUNET_assert(identifier);
+
+  struct GNUNET_HashCode hash;
+
+  GNUNET_CRYPTO_hash(identifier, strlen(identifier), &hash);
+  return GNUNET_CONTAINER_multihashmap_get(service->egos, &hash);
+}
+
+void
+update_service_ego (struct GNUNET_MESSENGER_Service *service, const char 
*identifier,
+                    const struct GNUNET_IDENTITY_PrivateKey* key)
+{
+  GNUNET_assert((identifier) && (key));
+
+  struct GNUNET_HashCode hash;
+
+  GNUNET_CRYPTO_hash(identifier, strlen(identifier), &hash);
+
+  struct GNUNET_MESSENGER_Ego* ego = 
GNUNET_CONTAINER_multihashmap_get(service->egos, &hash);
+
+  if (!ego)
+  {
+    ego = GNUNET_new(struct GNUNET_MESSENGER_Ego);
+    GNUNET_CONTAINER_multihashmap_put(service->egos, &hash, ego, 
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+  }
+
+  GNUNET_memcpy(&(ego->priv), key, sizeof(*key));
+
+  if (GNUNET_OK != GNUNET_IDENTITY_key_get_public(key, &(ego->pub)))
+    GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Updating invalid ego key 
failed!\n");
+}
+
+struct GNUNET_MESSENGER_SrvHandle*
+add_service_handle (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MQ_Handle *mq)
+{
+  struct GNUNET_MESSENGER_SrvHandle *handle = create_handle (service, mq);
+
+  if (handle)
+  {
+    add_list_handle (&(service->handles), handle);
+  }
+
+  return handle;
+}
+
+void
+remove_service_handle (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MESSENGER_SrvHandle *handle)
+{
+  if (!handle)
+    return;
+
+  if (GNUNET_YES == remove_list_handle (&(service->handles), handle))
+    destroy_handle (handle);
+}
+
+int
+get_service_peer_identity (const struct GNUNET_MESSENGER_Service *service, 
struct GNUNET_PeerIdentity *peer)
+{
+  return GNUNET_CRYPTO_get_peer_identity (service->config, peer);
+}
+
+struct GNUNET_MESSENGER_SrvContact*
+get_service_contact_by_pubkey (struct GNUNET_MESSENGER_Service *service, const 
struct GNUNET_IDENTITY_PublicKey *pubkey)
+{
+  struct GNUNET_HashCode hash;
+
+  GNUNET_CRYPTO_hash (pubkey, sizeof(*pubkey), &hash);
+
+  struct GNUNET_MESSENGER_SrvContact *contact = 
GNUNET_CONTAINER_multihashmap_get (service->contacts, &hash);
+
+  if (contact)
+    return contact;
+
+  contact = create_contact (pubkey);
+
+  if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (service->contacts, 
&hash, contact,
+                                                      
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
+    return contact;
+
+  destroy_contact (contact);
+  return NULL;
+}
+
+void
+swap_service_contact_by_pubkey (struct GNUNET_MESSENGER_Service *service, 
struct GNUNET_MESSENGER_SrvContact *contact,
+                                const struct GNUNET_IDENTITY_PublicKey *pubkey)
+{
+  const struct GNUNET_HashCode *hash = get_contact_id_from_key (contact);
+
+  if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (service->contacts, 
hash, contact))
+  {
+    GNUNET_memcpy(&(contact->public_key), pubkey, sizeof(*pubkey));
+
+    hash = get_contact_id_from_key (contact);
+
+    GNUNET_CONTAINER_multihashmap_put (service->contacts, hash, contact,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+  }
+}
+
+struct GNUNET_ShortHashCode*
+generate_service_new_member_id (struct GNUNET_MESSENGER_Service *service, 
const struct GNUNET_HashCode *key)
+{
+  struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (service, key);
+
+  if (room)
+  {
+    return generate_room_member_id (room);
+  }
+  else
+  {
+    struct GNUNET_ShortHashCode *random_id = GNUNET_new(struct 
GNUNET_ShortHashCode);
+    generate_free_member_id (random_id, NULL);
+    return random_id;
+  }
+}
+
+struct GNUNET_MESSENGER_SrvRoom*
+get_service_room (struct GNUNET_MESSENGER_Service *service, const struct 
GNUNET_HashCode *key)
+{
+  return GNUNET_CONTAINER_multihashmap_get (service->rooms, key);
+}
+
+int
+open_service_room (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                   const struct GNUNET_HashCode *key)
+{
+  struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (service, key);
+
+  if (room)
+    return open_room (room, handle);
+
+  room = create_room (handle, key);
+
+  if ((GNUNET_YES == open_room (room, handle)) && (GNUNET_OK
+      == GNUNET_CONTAINER_multihashmap_put (service->rooms, key, room, 
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)))
+    return GNUNET_YES;
+
+  destroy_room (room);
+  return GNUNET_NO;
+}
+
+int
+entry_service_room (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                    const struct GNUNET_PeerIdentity *door, const struct 
GNUNET_HashCode *key)
+{
+  struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (service, key);
+
+  if (room)
+  {
+    if (GNUNET_YES == entry_room_at (room, handle, door))
+      return GNUNET_YES;
+    else
+      return GNUNET_NO;
+  }
+
+  room = create_room (handle, key);
+
+  if ((GNUNET_YES == entry_room_at (room, handle, door)) && (GNUNET_OK
+      == GNUNET_CONTAINER_multihashmap_put (service->rooms, key, room, 
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)))
+  {
+    return GNUNET_YES;
+  }
+  else
+  {
+    destroy_room (room);
+    return GNUNET_NO;
+  }
+
+}
+
+int
+close_service_room (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                    const struct GNUNET_HashCode *key)
+{
+  struct GNUNET_MESSENGER_SrvRoom *room = get_service_room (service, key);
+
+  if (!room)
+    return GNUNET_NO;
+
+  struct GNUNET_MESSENGER_Message *message = create_message_leave ();
+
+  if (message)
+  {
+    struct GNUNET_HashCode hash;
+
+    send_room_message (room, handle, message, &hash);
+    destroy_message (message);
+  }
+
+  const struct GNUNET_ShortHashCode *id = get_handle_member_id (handle, key);
+
+  GNUNET_assert(id);
+
+  if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_remove (handle->member_ids, 
key, id))
+    return GNUNET_NO;
+
+  struct GNUNET_MESSENGER_SrvHandle *member_handle = (struct 
GNUNET_MESSENGER_SrvHandle*) find_list_handle_by_member (
+      &(service->handles), key);
+
+  if (!member_handle)
+  {
+    if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_remove (service->rooms, 
key, room))
+    {
+      destroy_room (room);
+      return GNUNET_YES;
+    }
+    else
+      return GNUNET_NO;
+  }
+
+  if (room->host == handle)
+    room->host = member_handle;
+
+  return GNUNET_YES;
+}
+
+static void
+get_room_data_subdir (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MESSENGER_SrvRoom *room, char **dir)
+{
+  GNUNET_asprintf (dir, "%s%s%c%s%c", service->dir, "rooms", DIR_SEPARATOR, 
GNUNET_h2s (&(room->key)), DIR_SEPARATOR);
+}
+
+void
+load_service_room_and_messages (struct GNUNET_MESSENGER_Service *service, 
struct GNUNET_MESSENGER_SrvRoom *room)
+{
+  char *room_dir;
+  get_room_data_subdir (service, room, &room_dir);
+
+  if (GNUNET_YES == GNUNET_DISK_directory_test (room_dir, GNUNET_YES))
+  {
+    load_message_store (&room->store, room_dir);
+
+    char *config_file;
+    GNUNET_asprintf (&config_file, "%s%s", room_dir, "room.cfg");
+
+    struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create ();
+
+    if ((GNUNET_YES == GNUNET_DISK_file_test (config_file)) && (GNUNET_OK
+        == GNUNET_CONFIGURATION_parse (cfg, config_file)))
+    {
+      unsigned long long access;
+
+      if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_number (cfg, "room", 
"access-rule", &access))
+        room->strict_access = (int) (access);
+
+      char *message_string;
+
+      if ((GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, "room", 
"last-message", &message_string)) && (message_string))
+      {
+        struct GNUNET_HashCode hash;
+
+        GNUNET_CRYPTO_hash_from_string(message_string, &hash);
+
+        const struct GNUNET_MESSENGER_Message *message = get_room_message 
(room, room->host, &hash, GNUNET_NO);
+
+        if (message)
+          update_room_last_messages (room, message, &hash);
+
+        GNUNET_free(message_string);
+      }
+    }
+
+    GNUNET_CONFIGURATION_destroy (cfg);
+
+    GNUNET_free(config_file);
+  }
+
+  GNUNET_free(room_dir);
+}
+
+void
+save_service_room_and_messages (struct GNUNET_MESSENGER_Service *service, 
struct GNUNET_MESSENGER_SrvRoom *room)
+{
+  if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_contains (service->rooms, 
&(room->key)))
+  {
+    return;
+  }
+
+  char *room_dir;
+  get_room_data_subdir (service, room, &room_dir);
+
+  if ((GNUNET_YES == GNUNET_DISK_directory_test (room_dir, GNUNET_NO)) || 
(GNUNET_OK
+      == GNUNET_DISK_directory_create (room_dir)))
+  {
+    save_message_store (&room->store, room_dir);
+
+    char *config_file;
+    GNUNET_asprintf (&config_file, "%s%s", room_dir, "room.cfg");
+
+    struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create ();
+
+    GNUNET_CONFIGURATION_set_value_number (cfg, "room", "access-rule", 
room->strict_access);
+
+    if (room->last_messages.head)
+      GNUNET_CONFIGURATION_set_value_string (cfg, "room", "last-message",
+                                             GNUNET_h2s_full 
(&(room->last_messages.head->hash)));
+
+    GNUNET_CONFIGURATION_write (cfg, config_file);
+    GNUNET_CONFIGURATION_destroy (cfg);
+
+    GNUNET_free(config_file);
+  }
+
+  GNUNET_free(room_dir);
+}
+
+void
+handle_service_message (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MESSENGER_SrvRoom *room,
+                        const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash)
+{
+  struct GNUNET_MESSENGER_ListHandle *element = service->handles.head;
+
+  const uint16_t length = get_message_size (message);
+
+  while (element)
+  {
+    struct GNUNET_MESSENGER_SrvHandle *handle = (struct 
GNUNET_MESSENGER_SrvHandle*) element->handle;
+
+    if ((handle->mq) && (get_handle_member_id (handle, &(room->key))))
+    {
+      struct GNUNET_MESSENGER_RecvMessage *msg;
+      struct GNUNET_MQ_Envelope *env;
+
+      env = GNUNET_MQ_msg_extra(msg, length, 
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_RECV_MESSAGE);
+
+      GNUNET_memcpy(&(msg->key), &(room->key), sizeof(room->key));
+      GNUNET_memcpy(&(msg->hash), hash, sizeof(*hash));
+
+      char *buffer = ((char*) msg) + sizeof(*msg);
+      encode_message (message, length, buffer);
+
+      GNUNET_MQ_send (handle->mq, env);
+    }
+
+    element = element->next;
+  }
+}
diff --git a/src/messenger/gnunet-service-messenger_service.h 
b/src/messenger/gnunet-service-messenger_service.h
new file mode 100644
index 000000000..246c74771
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_service.h
@@ -0,0 +1,259 @@
+/*
+   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_service.h
+ * @brief GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_SERVICE_MESSENGER_SERVICE_H
+#define GNUNET_SERVICE_MESSENGER_SERVICE_H
+
+#include "platform.h"
+#include "gnunet_configuration_lib.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_container_lib.h"
+#include "gnunet_disk_lib.h"
+#include "gnunet_identity_service.h"
+
+#include "messenger_api_ego.h"
+
+#include "gnunet-service-messenger_list_handles.h"
+
+#include "gnunet-service-messenger_contact.h"
+#include "gnunet-service-messenger_room.h"
+
+struct GNUNET_MESSENGER_Service
+{
+  const struct GNUNET_CONFIGURATION_Handle *config;
+  struct GNUNET_SERVICE_Handle *service;
+
+  struct GNUNET_SCHEDULER_Task *shutdown;
+
+  char *dir;
+
+  struct GNUNET_CADET_Handle *cadet;
+  struct GNUNET_IDENTITY_Handle *identity;
+
+  struct GNUNET_CONTAINER_MultiHashMap *egos;
+
+  struct GNUNET_MESSENGER_ListHandles handles;
+
+  struct GNUNET_CONTAINER_MultiHashMap *contacts;
+  struct GNUNET_CONTAINER_MultiHashMap *rooms;
+};
+
+/**
+ * Creates and allocates a new service using a given <i>config</i> and a 
GNUnet service handle.
+ *
+ * @param config Configuration
+ * @param service_handle GNUnet service handle
+ * @return New service
+ */
+struct GNUNET_MESSENGER_Service*
+create_service (const struct GNUNET_CONFIGURATION_Handle *config, struct 
GNUNET_SERVICE_Handle *service_handle);
+
+/**
+ * Destroys a <i>service</i> and frees its memory fully.
+ *
+ * @param service Service
+ */
+void
+destroy_service (struct GNUNET_MESSENGER_Service *service);
+
+/**
+ * Lookups an EGO which was registered to a <i>service</i> under
+ * a specific <i>identifier</i>.
+ *
+ * @param service Service
+ * @param identifier Identifier string
+ * @return EGO or NULL
+ */
+struct GNUNET_MESSENGER_Ego*
+lookup_service_ego (struct GNUNET_MESSENGER_Service *service, const char 
*identifier);
+
+/**
+ * Updates the registration of an EGO to a <i>service</i> under
+ * a specific <i>identifier</i> with a new <i>key</i>.
+ *
+ * @param service Service
+ * @param identifier Identifier string
+ * @param key Private EGO key
+ */
+void
+update_service_ego (struct GNUNET_MESSENGER_Service *service, const char 
*identifier,
+                    const struct GNUNET_IDENTITY_PrivateKey* key);
+
+/**
+ * Creates and adds a new handle to a <i>service</i> using a given message 
queue.
+ *
+ * @param service Service
+ * @param mq Message queue
+ * @return New handle
+ */
+struct GNUNET_MESSENGER_SrvHandle*
+add_service_handle (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MQ_Handle *mq);
+
+/**
+ * Removes a <i>handle</i> from a <i>service</i> and destroys it.
+ *
+ * @param service Service
+ * @param handle Handle
+ */
+void
+remove_service_handle (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MESSENGER_SrvHandle *handle);
+
+/**
+ * Tries to write the peer identity of the peer running a <i>service</i> on to 
the <i>peer</i>
+ * parameter. The functions returns GNUNET_OK on success, otherwise 
GNUNET_SYSERR.
+ *
+ * @param service Service
+ * @param[out] peer Peer identity
+ * @return GNUNET_OK on success, otherwise GNUNET_SYSERR
+ */
+int
+get_service_peer_identity (const struct GNUNET_MESSENGER_Service *service, 
struct GNUNET_PeerIdentity *peer);
+
+/**
+ * Returns a contact of a <i>service</i> identified by a given public key. If 
no matching contact exists,
+ * it will tried to create one with the specific public key. If the function 
still fails to do so,
+ * NULL gets returned.
+ *
+ * @param service Service
+ * @param pubkey Public key of EGO
+ * @return Contact
+ */
+struct GNUNET_MESSENGER_SrvContact*
+get_service_contact_by_pubkey (struct GNUNET_MESSENGER_Service *service, const 
struct GNUNET_IDENTITY_PublicKey *pubkey);
+
+/**
+ * Changes the public key for a <i>contact</i> known to a <i>service</i> to a 
specific public key and
+ * updates local map entries to access the contact by its updated key.
+ *
+ * @param service Service
+ * @param contact Contact
+ * @param pubkey Public key of EGO
+ */
+void
+swap_service_contact_by_pubkey (struct GNUNET_MESSENGER_Service *service, 
struct GNUNET_MESSENGER_SrvContact *contact,
+                                const struct GNUNET_IDENTITY_PublicKey 
*pubkey);
+
+/**
+ * Tries to generate and allocate a new unique member id for a given room of a 
service identified by its <i>key</i>.
+ * If the generation fails caused by too many tries of duplicates, it returns 
NULL.
+ *
+ * @param service Service
+ * @param key Key of room
+ * @return Newly generated member id or NULL
+ */
+struct GNUNET_ShortHashCode*
+generate_service_new_member_id (struct GNUNET_MESSENGER_Service *service, 
const struct GNUNET_HashCode *key);
+
+/**
+ * Returns the room identified by a given <i>key</i> for a <i>service</i>. If 
the service doesn't know any room
+ * using the given key, NULL gets returned.
+ *
+ * @param service Service
+ * @param key Key of room
+ * @return Room or NULL
+ */
+struct GNUNET_MESSENGER_SrvRoom*
+get_service_room (struct GNUNET_MESSENGER_Service *service, const struct 
GNUNET_HashCode *key);
+
+/**
+ * Tries to open a room using a given <i>key</i> for a <i>service</i> by a 
specific <i>handle</i>. The room will be
+ * created if necessary. If the function is successful, it returns GNUNET_YES, 
otherwise GNUNET_NO.
+ *
+ * @param service Service
+ * @param handle Handle
+ * @param key Key of room
+ * @return GNUNET_YES on success, otherwise GNUNET_NO
+ */
+int
+open_service_room (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                   const struct GNUNET_HashCode *key);
+
+/**
+ * Tries to enter a room using a given <i>key</i> for a <i>service</i> by a 
specific <i>handle</i>. The room will
+ * be created if necessary. If the function is successful, it returns 
GNUNET_YES, otherwise GNUNET_NO.
+ *
+ * The room will be entered through the peer identitied by the peer identity 
provided as <i>door</i> parameter and
+ * a new connection will be made.
+ *
+ * @param service Service
+ * @param handle Handle
+ * @param door Peer identity
+ * @param key Key of room
+ * @return GNUNET_YES on success, otherwise GNUNET_NO
+ */
+int
+entry_service_room (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                    const struct GNUNET_PeerIdentity *door, const struct 
GNUNET_HashCode *key);
+
+/**
+ * Tries to close a room using a given <i>key</i> for a <i>service</i> by a 
specific <i>handle</i>. The room will
+ * be created if necessary. If the function is successful, it returns 
GNUNET_YES, otherwise GNUNET_NO.
+ *
+ * If the specific handle is currently the host of the room for this service, 
a new handle which is a member will
+ * take its place. Otherwise the room will be destroyed for this service.
+ *
+ * @param service Service
+ * @param handle Handle
+ * @param key Key of room
+ * @return GNUNET_YES on success, otherwise GNUNET_NO
+ */
+int
+close_service_room (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MESSENGER_SrvHandle *handle,
+                    const struct GNUNET_HashCode *key);
+
+/**
+ * Loads the local configuration for a given <i>room</i> of a <i>service</i> 
which contains the last messages hash
+ * and the ruleset for general access of new members.
+ *
+ * @param service Service
+ * @param room Room
+ */
+void
+load_service_room_and_messages (struct GNUNET_MESSENGER_Service *service, 
struct GNUNET_MESSENGER_SrvRoom *room);
+
+/**
+ * Saves the configuration for a given <i>room</i> of a <i>service</i> which 
contains the last messages hash
+ * and the ruleset for general access of new members locally.
+ *
+ * @param service Service
+ * @param room Room
+ */
+void
+save_service_room_and_messages (struct GNUNET_MESSENGER_Service *service, 
struct GNUNET_MESSENGER_SrvRoom *room);
+
+/**
+ * Sends a received or sent <i>message</i> with a given <i>hash</i> to each 
handle of a <i>service</i> which
+ * is currently member of a specific <i>room</i> for handling it in the client 
API.
+ *
+ * @param service Service
+ * @param room Room
+ * @param message Message
+ * @param hash Hash of message
+ */
+void
+handle_service_message (struct GNUNET_MESSENGER_Service *service, struct 
GNUNET_MESSENGER_SrvRoom *room,
+                        const struct GNUNET_MESSENGER_Message *message, const 
struct GNUNET_HashCode *hash);
+
+#endif //GNUNET_SERVICE_MESSENGER_SERVICE_H
diff --git a/src/messenger/gnunet-service-messenger_tunnel.c 
b/src/messenger/gnunet-service-messenger_tunnel.c
new file mode 100644
index 000000000..df9e5c4c7
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_tunnel.c
@@ -0,0 +1,300 @@
+/*
+   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_tunnel.c
+ * @brief GNUnet MESSENGER service
+ */
+
+#include "gnunet-service-messenger_tunnel.h"
+
+#include "gnunet-service-messenger_handle.h"
+#include "gnunet-service-messenger_util.h"
+
+struct GNUNET_MESSENGER_SrvTunnel*
+create_tunnel (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_PeerIdentity *door)
+{
+  GNUNET_assert((room) && (door));
+
+  struct GNUNET_MESSENGER_SrvTunnel *tunnel = GNUNET_new(struct 
GNUNET_MESSENGER_SrvTunnel);
+
+  tunnel->room = room;
+  tunnel->channel = NULL;
+
+  tunnel->peer = GNUNET_PEER_intern (door);
+  tunnel->contact_id = NULL;
+
+  tunnel->peer_message = NULL;
+  tunnel->last_message = NULL;
+
+  return tunnel;
+}
+
+void
+destroy_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel)
+{
+  GNUNET_assert(tunnel);
+
+  if (tunnel->channel)
+    GNUNET_CADET_channel_destroy (tunnel->channel);
+
+  GNUNET_PEER_change_rc (tunnel->peer, -1);
+
+  if (tunnel->contact_id)
+    GNUNET_free(tunnel->contact_id);
+
+  if (tunnel->peer_message)
+    GNUNET_free(tunnel->peer_message);
+
+  if (tunnel->last_message)
+    GNUNET_free(tunnel->last_message);
+
+  GNUNET_free(tunnel);
+}
+
+int
+bind_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_CADET_Channel *channel)
+{
+  GNUNET_assert(tunnel);
+
+  if (tunnel->channel)
+  {
+    if (tunnel->contact_id)
+      return GNUNET_NO;
+
+    delayed_disconnect_channel (tunnel->channel);
+  }
+
+  tunnel->channel = channel;
+
+  return GNUNET_YES;
+}
+
+extern void
+callback_room_disconnect (struct GNUNET_MESSENGER_SrvRoom *room, void *cls);
+
+void
+callback_tunnel_disconnect (void *cls, const struct GNUNET_CADET_Channel 
*channel)
+{
+  struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls;
+
+  if (tunnel)
+  {
+    tunnel->channel = NULL;
+
+    callback_room_disconnect (tunnel->room, cls);
+  }
+}
+
+extern int
+callback_verify_room_message (struct GNUNET_MESSENGER_SrvRoom *room, void *cls,
+                              struct GNUNET_MESSENGER_Message *message, struct 
GNUNET_HashCode *hash);
+
+int
+check_tunnel_message (void *cls, const struct GNUNET_MessageHeader *header)
+{
+  struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls;
+
+  if (!tunnel)
+    return GNUNET_NO;
+
+  const uint16_t length = ntohs (header->size) - sizeof(*header);
+  const char *buffer = (const char*) &header[1];
+
+  struct GNUNET_MESSENGER_Message message;
+
+  if (length < sizeof(message.header))
+    return GNUNET_NO;
+
+  if (GNUNET_YES != decode_message (&message, length, buffer))
+    return GNUNET_NO;
+
+  struct GNUNET_HashCode hash;
+  hash_message (length, buffer, &hash);
+
+  int result = callback_verify_room_message (tunnel->room, cls, &message, 
&hash);
+
+  if (GNUNET_MESSENGER_KIND_PEER == message.header.kind)
+  {
+    struct GNUNET_PeerIdentity identity;
+
+    GNUNET_PEER_resolve (tunnel->peer, &identity);
+
+    if (0 == GNUNET_memcmp(&(message.body.peer.peer), &(identity)))
+    {
+      if (tunnel->contact_id)
+      {
+        if (0 != GNUNET_memcmp(tunnel->contact_id, 
&(message.header.sender_id)))
+          result = GNUNET_SYSERR;
+      }
+      else
+      {
+        tunnel->contact_id = GNUNET_new(struct GNUNET_ShortHashCode);
+
+        GNUNET_memcpy(tunnel->contact_id, &(message.header.sender_id), 
sizeof(struct GNUNET_ShortHashCode));
+      }
+    }
+  }
+
+  return (result == GNUNET_YES ? GNUNET_OK : GNUNET_NO);
+}
+
+extern void
+callback_room_recv (struct GNUNET_MESSENGER_SrvRoom *room, void *cls, struct 
GNUNET_MESSENGER_Message *message,
+                    const struct GNUNET_HashCode *hash);
+
+void
+handle_tunnel_message (void *cls, const struct GNUNET_MessageHeader *header)
+{
+  struct GNUNET_MESSENGER_SrvTunnel *tunnel = cls;
+
+  const uint16_t length = ntohs (header->size) - sizeof(*header);
+  const char *buffer = (const char*) &header[1];
+
+  struct GNUNET_MESSENGER_Message message;
+  struct GNUNET_HashCode hash;
+
+  decode_message (&message, length, buffer);
+  hash_message (length, buffer, &hash);
+
+  if (tunnel)
+  {
+    if (!tunnel->last_message)
+      tunnel->last_message = GNUNET_new(struct GNUNET_HashCode);
+
+    GNUNET_memcpy(tunnel->last_message, &hash, sizeof(struct GNUNET_HashCode));
+
+    callback_room_recv (tunnel->room, cls, copy_message (&message), &hash);
+  }
+
+  GNUNET_CADET_receive_done (tunnel->channel);
+}
+
+int
+connect_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel)
+{
+  GNUNET_assert(tunnel);
+
+  if (tunnel->channel)
+    return GNUNET_NO;
+
+  const struct GNUNET_PeerIdentity *door = GNUNET_PEER_resolve2 (tunnel->peer);
+
+  struct GNUNET_CADET_Handle *cadet = get_room_cadet (tunnel->room);
+  struct GNUNET_HashCode *key = get_room_key (tunnel->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() };
+
+  tunnel->channel = GNUNET_CADET_channel_create (cadet, tunnel, door, key, 
NULL, callback_tunnel_disconnect, handlers);
+
+  return GNUNET_YES;
+}
+
+void
+disconnect_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel)
+{
+  if (tunnel->channel)
+  {
+    delayed_disconnect_channel (tunnel->channel);
+
+    tunnel->channel = NULL;
+  }
+}
+
+int
+is_tunnel_connected (const struct GNUNET_MESSENGER_SrvTunnel *tunnel)
+{
+  return (tunnel->channel ? GNUNET_YES : GNUNET_NO);
+}
+
+struct GNUNET_MESSENGER_MessageSent
+{
+  struct GNUNET_MESSENGER_SrvTunnel *tunnel;
+  struct GNUNET_HashCode hash;
+};
+
+extern 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);
+
+static void
+callback_tunnel_sent (void *cls)
+{
+  struct GNUNET_MESSENGER_MessageSent *sent = cls;
+
+  if (sent->tunnel)
+  {
+    if (!sent->tunnel->last_message)
+      sent->tunnel->last_message = GNUNET_new(struct GNUNET_HashCode);
+
+    GNUNET_memcpy(sent->tunnel->last_message, &(sent->hash), sizeof(struct 
GNUNET_HashCode));
+  }
+
+  GNUNET_free(sent);
+}
+
+void
+send_tunnel_envelope (struct GNUNET_MESSENGER_SrvTunnel *tunnel, void *handle, 
struct GNUNET_MQ_Envelope *env,
+                      struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash)
+{
+  struct GNUNET_MQ_Handle *mq = GNUNET_CADET_get_mq (tunnel->channel);
+
+  struct GNUNET_MESSENGER_MessageSent *sent = GNUNET_new(struct 
GNUNET_MESSENGER_MessageSent);
+
+  GNUNET_memcpy(&(sent->hash), hash, sizeof(struct GNUNET_HashCode));
+
+  sent->tunnel = tunnel;
+
+  GNUNET_MQ_notify_sent (env, callback_tunnel_sent, sent);
+  GNUNET_MQ_send (mq, env);
+
+  callback_room_sent (tunnel->room, (struct GNUNET_MESSENGER_SrvHandle*) 
handle, tunnel, message, hash);
+}
+
+void
+send_tunnel_message (struct GNUNET_MESSENGER_SrvTunnel *tunnel, void *handle, 
struct GNUNET_MESSENGER_Message *message,
+                     struct GNUNET_HashCode *hash)
+{
+  struct GNUNET_MQ_Envelope *env = pack_room_message (tunnel->room, (struct 
GNUNET_MESSENGER_SrvHandle*) handle,
+                                                      message, hash,
+                                                      
GNUNET_MESSENGER_PACK_MODE_ENVELOPE);
+
+  if (env)
+    send_tunnel_envelope (tunnel, handle, env, copy_message (message), hash);
+}
+
+void
+forward_tunnel_message (struct GNUNET_MESSENGER_SrvTunnel *tunnel, const 
struct GNUNET_MESSENGER_Message *message,
+                        const struct GNUNET_HashCode *hash)
+{
+  struct GNUNET_MESSENGER_Message *clone = copy_message (message);
+  struct GNUNET_MQ_Envelope *env = pack_message (clone, NULL, NULL, 
GNUNET_MESSENGER_PACK_MODE_ENVELOPE);
+
+  if (env)
+    send_tunnel_envelope (tunnel, NULL, env, clone, hash);
+}
+
+const struct GNUNET_HashCode*
+get_tunnel_peer_message (const struct GNUNET_MESSENGER_SrvTunnel *tunnel)
+{
+  return tunnel->peer_message;
+}
diff --git a/src/messenger/gnunet-service-messenger_tunnel.h 
b/src/messenger/gnunet-service-messenger_tunnel.h
new file mode 100644
index 000000000..e6efb226d
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_tunnel.h
@@ -0,0 +1,155 @@
+/*
+   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_tunnel.h
+ * @brief GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_SERVICE_MESSENGER_TUNNEL_H
+#define GNUNET_SERVICE_MESSENGER_TUNNEL_H
+
+#include "platform.h"
+#include "gnunet_cadet_service.h"
+#include "gnunet_peer_lib.h"
+#include "gnunet_crypto_lib.h"
+
+#include "gnunet-service-messenger_room.h"
+
+struct GNUNET_MESSENGER_SrvTunnel
+{
+  struct GNUNET_MESSENGER_SrvRoom *room;
+  struct GNUNET_CADET_Channel *channel;
+
+  GNUNET_PEER_Id peer;
+  struct GNUNET_ShortHashCode *contact_id;
+
+  struct GNUNET_HashCode *peer_message;
+  struct GNUNET_HashCode *last_message;
+};
+
+/**
+ * Creates and allocates a tunnel of a <i>room</i> to a specific peer identity.
+ *
+ * @param room Room
+ * @param door Peer identity
+ * @return New tunnel
+ */
+struct GNUNET_MESSENGER_SrvTunnel*
+create_tunnel (struct GNUNET_MESSENGER_SrvRoom *room, const struct 
GNUNET_PeerIdentity *door);
+
+/**
+ * Destroys a <i>tunnel</i> and frees its memory fully.
+ *
+ * @param tunnel
+ */
+void
+destroy_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel);
+
+/**
+ * Binds a CADET <i>channel</i> to a <i>tunnel</i> on returns GNUNET_YES only 
if
+ * the bounds channel was replaced successfully, otherwise GNUNET_NO gets 
returned.
+ *
+ * @param tunnel Tunnel
+ * @param channel CADET channel
+ * @return GNUNET_YES on success, otherwise GNUNET_NO
+ */
+int
+bind_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel, struct 
GNUNET_CADET_Channel *channel);
+
+/**
+ * Tries to connect a <i>tunnel</i> by creating a new CADET channel and 
binding it.
+ * The function returns GNUNET_YES on success, otherwise GNUNET_NO.
+ *
+ * @param tunnel Tunnel
+ * @return GNUNET_YES on success, otherwise GNUNET_NO
+ */
+int
+connect_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel);
+
+/**
+ * Disconnects and unbinds a channel from a <i>tunnel</i>. The actual 
disconnection
+ * will be asynchronous.
+ *
+ * @param tunnel Tunnel
+ */
+void
+disconnect_tunnel (struct GNUNET_MESSENGER_SrvTunnel *tunnel);
+
+/**
+ * Returns the status of a currently bound channel of a <i>tunnel</i>.
+ *
+ * @param tunnel Tunnel
+ * @return GNUNET_YES or GNUNET_NO
+ */
+int
+is_tunnel_connected (const struct GNUNET_MESSENGER_SrvTunnel *tunnel);
+
+/**
+ * Sends an envelope containing a <i>message</i> with a given <i>hash</i> 
through
+ * a <i>tunnel</i> by a given <i>handle</i>.
+ *
+ * @param tunnel Tunnel
+ * @param handle Handle
+ * @param env Envelope
+ * @param message Message
+ * @param hash Hash of message
+ */
+void
+send_tunnel_envelope (struct GNUNET_MESSENGER_SrvTunnel *tunnel, void *handle, 
struct GNUNET_MQ_Envelope *env,
+                      struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash);
+
+/**
+ * Sends a <i>message</i> by packing it automatically into an envelope and 
passing it
+ * through the <i>tunnel</i>. The used <i>handle</i> will sign the message and
+ * the <i>hash</i> will be calculated and stored.
+ *
+ * @param tunnel Tunnel
+ * @param handle Handle
+ * @param[out] message Message
+ * @param[out] hash Hash of message
+ */
+void
+send_tunnel_message (struct GNUNET_MESSENGER_SrvTunnel *tunnel, void *handle, 
struct GNUNET_MESSENGER_Message *message,
+                     struct GNUNET_HashCode *hash);
+
+/**
+ * Forwards a given <i>message</i> with a known <i>hash</i> through a 
<i>tunnel</i>.
+ *
+ * @param tunnel Tunnel
+ * @param message Message
+ * @param hash Hash of message
+ */
+void
+forward_tunnel_message (struct GNUNET_MESSENGER_SrvTunnel *tunnel, const 
struct GNUNET_MESSENGER_Message *message,
+                        const struct GNUNET_HashCode *hash);
+
+/**
+ * Returns the hash of the latest peer message published through a given 
<i>tunnel</i>
+ * and matching the tunnels peer identity. If no peer message has been linked 
to the tunnel
+ * yet, NULL gets returned.
+ *
+ * @param tunnel Tunnel
+ * @return Hash of peer message or NULL
+ */
+const struct GNUNET_HashCode*
+get_tunnel_peer_message (const struct GNUNET_MESSENGER_SrvTunnel *tunnel);
+
+#endif //GNUNET_SERVICE_MESSENGER_TUNNEL_H
diff --git a/src/messenger/gnunet-service-messenger_util.c 
b/src/messenger/gnunet-service-messenger_util.c
new file mode 100644
index 000000000..94fc9469d
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_util.c
@@ -0,0 +1,64 @@
+/*
+   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_util.c
+ * @brief GNUnet MESSENGER service
+ */
+
+#include "gnunet-service-messenger_util.h"
+
+static void
+callback_close_channel (void *cls)
+{
+  struct GNUNET_CADET_Channel *channel = cls;
+
+  if (channel)
+    GNUNET_CADET_channel_destroy (channel);
+}
+
+void
+delayed_disconnect_channel (struct GNUNET_CADET_Channel *channel)
+{
+  GNUNET_SCHEDULER_add_delayed_with_priority (GNUNET_TIME_relative_get_zero_ 
(), GNUNET_SCHEDULER_PRIORITY_URGENT,
+                                              callback_close_channel, channel);
+}
+
+int
+generate_free_member_id (struct GNUNET_ShortHashCode *id, const struct 
GNUNET_CONTAINER_MultiShortmap *members)
+{
+  size_t counter = 1 + (members ? GNUNET_CONTAINER_multishortmap_size 
(members) : 0);
+
+  do
+  {
+    GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, id, 
sizeof(struct GNUNET_ShortHashCode));
+
+    if ((members) && (GNUNET_YES == GNUNET_CONTAINER_multishortmap_contains 
(members, id)))
+      counter--;
+    else
+      break;
+  }
+  while (counter > 0);
+
+  if (counter)
+    return GNUNET_YES;
+
+  return GNUNET_NO;
+}
diff --git a/src/messenger/gnunet-service-messenger_util.h 
b/src/messenger/gnunet-service-messenger_util.h
new file mode 100644
index 000000000..20f8f0afe
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_util.h
@@ -0,0 +1,53 @@
+/*
+   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_util.h
+ * @brief GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_SERVICE_MESSENGER_UTIL_H
+#define GNUNET_SERVICE_MESSENGER_UTIL_H
+
+#include "platform.h"
+#include "gnunet_cadet_service.h"
+#include "gnunet_container_lib.h"
+#include "gnunet_crypto_lib.h"
+
+/**
+ * Starts an urgent task to close a CADET channel asynchronously.
+ *
+ * @param channel Channel
+ */
+void
+delayed_disconnect_channel (struct GNUNET_CADET_Channel *channel);
+
+/**
+ * Tries to generate an unused member id and store it into the <i>id</i> 
parameter. A map containing all currently
+ * used member ids is used to check against.
+ *
+ * @param[out] id New member id
+ * @param members Map of member ids
+ * @return GNUNET_YES on success, GNUNET_NO on failure
+ */
+int
+generate_free_member_id (struct GNUNET_ShortHashCode *id, const struct 
GNUNET_CONTAINER_MultiShortmap *members);
+
+#endif //GNUNET_SERVICE_MESSENGER_UTIL_H
diff --git a/src/messenger/messenger.conf.in b/src/messenger/messenger.conf.in
new file mode 100644
index 000000000..59e11b166
--- /dev/null
+++ b/src/messenger/messenger.conf.in
@@ -0,0 +1,13 @@
+[messenger]
+START_ON_DEMAND = YES
+PORT = 2097
+HOSTNAME = localhost
+BINARY = gnunet-service-messenger
+ACCEPT_FROM = 127.0.0.1;
+ACCEPT_FROM6 = ::1;
+UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-messenger.sock
+UNIX_MATCH_UID = NO
+UNIX_MATCH_GID = YES
+
+# Directory to store messages and contacts
+MESSENGER_DIR = $GNUNET_DATA_HOME/messenger/
\ No newline at end of file
diff --git a/src/messenger/messenger_api.c b/src/messenger/messenger_api.c
new file mode 100644
index 000000000..6401b18d7
--- /dev/null
+++ b/src/messenger/messenger_api.c
@@ -0,0 +1,568 @@
+/*
+   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/messenger_api.c
+ * @brief messenger api: client implementation of GNUnet MESSENGER service
+ */
+
+#include "gnunet_messenger_service.h"
+
+#include "gnunet-service-messenger.h"
+
+#include "messenger_api_handle.h"
+#include "messenger_api_message.h"
+
+const char*
+GNUNET_MESSENGER_name_of_kind (enum GNUNET_MESSENGER_MessageKind kind)
+{
+  switch (kind)
+  {
+  case GNUNET_MESSENGER_KIND_INFO:
+    return "INFO";
+  case GNUNET_MESSENGER_KIND_JOIN:
+    return "JOIN";
+  case GNUNET_MESSENGER_KIND_LEAVE:
+    return "LEAVE";
+  case GNUNET_MESSENGER_KIND_NAME:
+    return "NAME";
+  case GNUNET_MESSENGER_KIND_KEY:
+    return "KEY";
+  case GNUNET_MESSENGER_KIND_PEER:
+    return "PEER";
+  case GNUNET_MESSENGER_KIND_ID:
+    return "ID";
+  case GNUNET_MESSENGER_KIND_MISS:
+    return "MISS";
+  case GNUNET_MESSENGER_KIND_MERGE:
+    return "MERGE";
+  case GNUNET_MESSENGER_KIND_REQUEST:
+    return "REQUEST";
+  case GNUNET_MESSENGER_KIND_INVITE:
+    return "INVITE";
+  case GNUNET_MESSENGER_KIND_TEXT:
+    return "TEXT";
+  case GNUNET_MESSENGER_KIND_FILE:
+    return "FILE";
+  default:
+    return "UNKNOWN";
+  }
+}
+
+static int
+check_get_name (void *cls, const struct GNUNET_MESSENGER_NameMessage *msg)
+{
+  GNUNET_MQ_check_zero_termination(msg);
+  return GNUNET_OK;
+}
+
+static void
+handle_get_name (void *cls, const struct GNUNET_MESSENGER_NameMessage *msg)
+{
+  struct GNUNET_MESSENGER_Handle *handle = cls;
+
+  const char *name = ((const char*) msg) + sizeof(*msg);
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Set name of handle: %s\n", name);
+
+  set_handle_name (handle, strlen(name) > 0? name : NULL);
+}
+
+static void
+handle_get_key (void *cls, const struct GNUNET_MESSENGER_KeyMessage *msg)
+{
+  struct GNUNET_MESSENGER_Handle *handle = cls;
+
+  const struct GNUNET_IDENTITY_PublicKey *pubkey = &(msg->pubkey);
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Set key of handle: %s\n", 
GNUNET_IDENTITY_public_key_to_string (pubkey));
+
+  set_handle_key (handle, pubkey);
+
+  if (handle->identity_callback)
+    handle->identity_callback (handle->identity_cls, handle);
+}
+
+static void
+handle_member_id (void *cls, const struct GNUNET_MESSENGER_MemberMessage *msg)
+{
+  struct GNUNET_MESSENGER_Handle *handle = cls;
+
+  const struct GNUNET_HashCode *key = &(msg->key);
+  const struct GNUNET_ShortHashCode *id = &(msg->id);
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Set id of handle in room: %s\n", 
GNUNET_h2s (key));
+
+  struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get 
(handle->rooms, key);
+
+  if (room)
+  {
+    if (!room->contact_id)
+      room->contact_id = GNUNET_new(struct GNUNET_ShortHashCode);
+
+    GNUNET_memcpy(room->contact_id, id, sizeof(*id));
+  }
+}
+
+static void
+handle_room_open (void *cls, const struct GNUNET_MESSENGER_RoomMessage *msg)
+{
+  struct GNUNET_MESSENGER_Handle *handle = cls;
+
+  const struct GNUNET_HashCode *key = &(msg->key);
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Opened room: %s\n", GNUNET_h2s (key));
+
+  open_handle_room (handle, key);
+}
+
+static void
+handle_room_entry (void *cls, const struct GNUNET_MESSENGER_RoomMessage *msg)
+{
+  struct GNUNET_MESSENGER_Handle *handle = cls;
+
+  const struct GNUNET_PeerIdentity *door = &(msg->door);
+  const struct GNUNET_HashCode *key = &(msg->key);
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Entered room: %s\n", GNUNET_h2s (key));
+
+  entry_handle_room_at (handle, door, key);
+}
+
+static void
+handle_room_close (void *cls, const struct GNUNET_MESSENGER_RoomMessage *msg)
+{
+  struct GNUNET_MESSENGER_Handle *handle = cls;
+
+  const struct GNUNET_HashCode *key = &(msg->key);
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Closed room: %s\n", GNUNET_h2s (key));
+
+  close_handle_room (handle, key);
+}
+
+static int
+check_recv_message (void *cls, const struct GNUNET_MESSENGER_RecvMessage *msg)
+{
+  const uint16_t full_length = ntohs (msg->header.size) - sizeof(msg->header);
+
+  if (full_length < sizeof(msg->hash))
+    return GNUNET_NO;
+
+  const uint16_t length = full_length - sizeof(msg->hash);
+  const char *buffer = ((const char*) msg) + sizeof(*msg);
+
+  struct GNUNET_MESSENGER_Message message;
+
+  if (length < sizeof(message.header))
+    return GNUNET_NO;
+
+  if (GNUNET_YES != decode_message (&message, length, buffer))
+    return GNUNET_NO;
+
+  return GNUNET_OK;
+}
+
+static void
+handle_recv_message (void *cls, const struct GNUNET_MESSENGER_RecvMessage *msg)
+{
+  struct GNUNET_MESSENGER_Handle *handle = cls;
+
+  const struct GNUNET_HashCode *key = &(msg->key);
+  const struct GNUNET_HashCode *hash = &(msg->hash);
+
+  const char *buffer = ((const char*) msg) + sizeof(*msg);
+
+  const uint16_t length = ntohs (msg->header.size) - sizeof(*msg);
+
+  struct GNUNET_MESSENGER_Message message;
+  decode_message (&message, length, buffer);
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Receiving message: %s\n", 
GNUNET_MESSENGER_name_of_kind (message.header.kind));
+
+  struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get 
(handle->rooms, key);
+
+  if (room)
+  {
+    handle_room_message (room, &message, hash);
+
+    if (handle->msg_callback)
+      handle->msg_callback (handle->msg_cls, room, &message, hash);
+  }
+  else
+    GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "MESSENGER ERROR: Room not found\n");
+}
+
+static void
+reconnect (struct GNUNET_MESSENGER_Handle *handle);
+
+static void
+send_open_room (struct GNUNET_MESSENGER_Handle *handle, struct 
GNUNET_MESSENGER_Room *room)
+{
+  struct GNUNET_MESSENGER_RoomMessage *msg;
+  struct GNUNET_MQ_Envelope *env;
+
+  env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_OPEN);
+  GNUNET_memcpy(&(msg->key), &(room->key), sizeof(room->key));
+  GNUNET_MQ_send (handle->mq, env);
+}
+
+static void
+send_entry_room (struct GNUNET_MESSENGER_Handle *handle, struct 
GNUNET_MESSENGER_Room *room,
+                 const struct GNUNET_PeerIdentity *door)
+{
+  struct GNUNET_MESSENGER_RoomMessage *msg;
+  struct GNUNET_MQ_Envelope *env;
+
+  env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_ENTRY);
+  GNUNET_memcpy(&(msg->door), door, sizeof(*door));
+  GNUNET_memcpy(&(msg->key), &(room->key), sizeof(room->key));
+  GNUNET_MQ_send (handle->mq, env);
+}
+
+static void
+send_close_room (struct GNUNET_MESSENGER_Handle *handle, struct 
GNUNET_MESSENGER_Room *room)
+{
+  struct GNUNET_MESSENGER_RoomMessage *msg;
+  struct GNUNET_MQ_Envelope *env;
+
+  env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_CLOSE);
+  GNUNET_memcpy(&(msg->key), &(room->key), sizeof(room->key));
+  GNUNET_MQ_send (handle->mq, env);
+}
+
+static int
+iterate_reset_room (void *cls, const struct GNUNET_HashCode *key, void *value)
+{
+  struct GNUNET_MESSENGER_Handle *handle = cls;
+  struct GNUNET_MESSENGER_Room *room = value;
+
+  if (GNUNET_YES == room->opened)
+    send_open_room (handle, room);
+
+  struct GNUNET_MESSENGER_ListTunnel *entry = room->entries.head;
+
+  struct GNUNET_PeerIdentity door;
+
+  while (entry)
+  {
+    GNUNET_PEER_resolve (entry->peer, &door);
+
+    send_entry_room (handle, room, &door);
+
+    entry = entry->next;
+  }
+
+  return GNUNET_YES;
+}
+
+static void
+callback_reconnect (void *cls)
+{
+  struct GNUNET_MESSENGER_Handle *handle = cls;
+
+  handle->reconnect_task = NULL;
+  handle->reconnect_time = GNUNET_TIME_STD_BACKOFF(handle->reconnect_time)
+  ;
+
+  reconnect (handle);
+
+  GNUNET_CONTAINER_multihashmap_iterate (handle->rooms, iterate_reset_room, 
handle);
+}
+
+static int
+iterate_close_room (void *cls, const struct GNUNET_HashCode *key, void *value)
+{
+  struct GNUNET_MESSENGER_Handle *handle = cls;
+  struct GNUNET_MESSENGER_Room *room = value;
+
+  send_close_room (handle, room);
+
+  return GNUNET_YES;
+}
+
+static void
+callback_mq_error (void *cls, enum GNUNET_MQ_Error error)
+{
+  struct GNUNET_MESSENGER_Handle *handle = cls;
+
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "MQ ERROR: %u\n", error);
+
+  GNUNET_CONTAINER_multihashmap_iterate (handle->rooms, iterate_close_room, 
handle);
+
+  if (handle->mq)
+  {
+    GNUNET_MQ_destroy (handle->mq);
+    handle->mq = NULL;
+  }
+
+  handle->reconnect_task = GNUNET_SCHEDULER_add_delayed 
(handle->reconnect_time, &callback_reconnect, handle);
+}
+
+static void
+reconnect (struct GNUNET_MESSENGER_Handle *handle)
+{
+  const struct GNUNET_MQ_MessageHandler handlers[] = { GNUNET_MQ_hd_var_size(
+      get_name, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_GET_NAME, struct 
GNUNET_MESSENGER_NameMessage, handle),
+                                                       GNUNET_MQ_hd_fixed_size(
+                                                           get_key, 
GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_GET_KEY,
+                                                           struct 
GNUNET_MESSENGER_KeyMessage, handle),
+                                                       GNUNET_MQ_hd_fixed_size(
+                                                           member_id,
+                                                           
GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_MEMBER_ID,
+                                                           struct 
GNUNET_MESSENGER_MemberMessage, handle),
+                                                       
GNUNET_MQ_hd_fixed_size(room_open,
+                                                                               
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_OPEN,
+                                                                               
struct GNUNET_MESSENGER_RoomMessage,
+                                                                               
handle),
+                                                       
GNUNET_MQ_hd_fixed_size(room_entry,
+                                                                               
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_ENTRY,
+                                                                               
struct GNUNET_MESSENGER_RoomMessage,
+                                                                               
handle),
+                                                       
GNUNET_MQ_hd_fixed_size(room_close,
+                                                                               
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_CLOSE,
+                                                                               
struct GNUNET_MESSENGER_RoomMessage,
+                                                                               
handle),
+                                                       GNUNET_MQ_hd_var_size(
+                                                           recv_message,
+                                                           
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_RECV_MESSAGE,
+                                                           struct 
GNUNET_MESSENGER_RecvMessage, handle),
+                                                       GNUNET_MQ_handler_end() 
};
+
+  handle->mq = GNUNET_CLIENT_connect (handle->cfg,
+  GNUNET_MESSENGER_SERVICE_NAME,
+                                      handlers, &callback_mq_error, handle);
+}
+
+struct GNUNET_MESSENGER_Handle*
+GNUNET_MESSENGER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, const 
char *name,
+                          GNUNET_MESSENGER_IdentityCallback identity_callback, 
void *identity_cls,
+                          GNUNET_MESSENGER_MessageCallback msg_callback, void 
*msg_cls)
+{
+  struct GNUNET_MESSENGER_Handle *handle = create_handle (cfg, 
identity_callback, identity_cls, msg_callback, msg_cls);
+
+  reconnect (handle);
+
+  if (handle->mq)
+  {
+    const uint16_t name_len = name ? strlen (name) : 0;
+
+    struct GNUNET_MESSENGER_CreateMessage *msg;
+    struct GNUNET_MQ_Envelope *env;
+
+    env = GNUNET_MQ_msg_extra(msg, name_len + 1, 
GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_CREATE);
+
+    char *extra = ((char*) msg) + sizeof(*msg);
+
+    if (name_len)
+      GNUNET_memcpy(extra, name, name_len);
+
+    extra[name_len] = '\0';
+
+    GNUNET_MQ_send (handle->mq, env);
+    return handle;
+  }
+  else
+  {
+    destroy_handle (handle);
+    return NULL;
+  }
+}
+
+int
+GNUNET_MESSENGER_update (struct GNUNET_MESSENGER_Handle *handle)
+{
+  if ((!handle) || (!get_handle_name(handle)))
+    return GNUNET_SYSERR;
+
+  struct GNUNET_MESSENGER_UpdateMessage *msg;
+  struct GNUNET_MQ_Envelope *env;
+
+  env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_UPDATE);
+  GNUNET_MQ_send (handle->mq, env);
+  return GNUNET_OK;
+}
+
+void
+GNUNET_MESSENGER_disconnect (struct GNUNET_MESSENGER_Handle *handle)
+{
+  if (!handle)
+    return;
+
+  struct GNUNET_MESSENGER_DestroyMessage *msg;
+  struct GNUNET_MQ_Envelope *env;
+
+  env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_DESTROY);
+  GNUNET_MQ_send (handle->mq, env);
+
+  destroy_handle (handle);
+}
+
+const char*
+GNUNET_MESSENGER_get_name (const struct GNUNET_MESSENGER_Handle *handle)
+{
+  if (!handle)
+    return NULL;
+
+  return get_handle_name (handle);
+}
+
+int
+GNUNET_MESSENGER_set_name (struct GNUNET_MESSENGER_Handle *handle, const char 
*name)
+{
+  if (!handle)
+    return GNUNET_SYSERR;
+
+  const uint16_t name_len = name ? strlen (name) : 0;
+
+  struct GNUNET_MESSENGER_NameMessage *msg;
+  struct GNUNET_MQ_Envelope *env;
+
+  env = GNUNET_MQ_msg_extra(msg, name_len + 1, 
GNUNET_MESSAGE_TYPE_MESSENGER_CONNECTION_SET_NAME);
+
+  char *extra = ((char*) msg) + sizeof(*msg);
+
+  if (name_len)
+    GNUNET_memcpy(extra, name, name_len);
+
+  extra[name_len] = '\0';
+
+  GNUNET_MQ_send (handle->mq, env);
+  return GNUNET_YES;
+}
+
+const struct GNUNET_IDENTITY_PublicKey*
+GNUNET_MESSENGER_get_key (const struct GNUNET_MESSENGER_Handle *handle)
+{
+  if (!handle)
+    return NULL;
+
+  return get_handle_key (handle);
+}
+
+struct GNUNET_MESSENGER_Room*
+GNUNET_MESSENGER_open_room (struct GNUNET_MESSENGER_Handle *handle, const 
struct GNUNET_HashCode *key)
+{
+  struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get 
(handle->rooms, key);
+
+  if (!room)
+  {
+    room = create_room (handle, key);
+
+    if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (handle->rooms, key, 
room,
+                                                        
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
+    {
+      destroy_room (room);
+      return NULL;
+    }
+  }
+
+  send_open_room (handle, room);
+  return room;
+}
+
+struct GNUNET_MESSENGER_Room*
+GNUNET_MESSENGER_entry_room (struct GNUNET_MESSENGER_Handle *handle, const 
struct GNUNET_PeerIdentity *door,
+                             const struct GNUNET_HashCode *key)
+{
+  struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get 
(handle->rooms, key);
+
+  if (!room)
+  {
+    room = create_room (handle, key);
+
+    if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (handle->rooms, key, 
room,
+                                                        
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
+    {
+      destroy_room (room);
+      return NULL;
+    }
+  }
+
+  send_entry_room (handle, room, door);
+  return room;
+}
+
+void
+GNUNET_MESSENGER_close_room (struct GNUNET_MESSENGER_Room *room)
+{
+  send_close_room (room->handle, room);
+}
+
+struct GNUNET_MESSENGER_Contact*
+GNUNET_MESSENGER_get_member (const struct GNUNET_MESSENGER_Room *room, const 
struct GNUNET_ShortHashCode *id)
+{
+  return GNUNET_CONTAINER_multishortmap_get (room->members, id);
+}
+
+const char*
+GNUNET_MESSENGER_contact_get_name (const struct GNUNET_MESSENGER_Contact 
*contact)
+{
+  if (!contact)
+    return NULL;
+
+  return get_contact_name (contact);
+}
+
+const struct GNUNET_IDENTITY_PublicKey*
+GNUNET_MESSENGER_contact_get_key (const struct GNUNET_MESSENGER_Contact 
*contact)
+{
+  if (!contact)
+    return NULL;
+
+  return get_contact_key (contact);
+}
+
+void
+GNUNET_MESSENGER_send_message (struct GNUNET_MESSENGER_Room *room, const 
struct GNUNET_MESSENGER_Message *message)
+{
+  const uint16_t length = get_message_size (message);
+
+  struct GNUNET_MESSENGER_SendMessage *msg;
+  struct GNUNET_MQ_Envelope *env;
+
+  env = GNUNET_MQ_msg_extra(msg, length, 
GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_SEND_MESSAGE);
+
+  GNUNET_memcpy(&(msg->key), &(room->key), sizeof(room->key));
+
+  char *buffer = ((char*) msg) + sizeof(*msg);
+  encode_message (message, length, buffer);
+
+  GNUNET_MQ_send (room->handle->mq, env);
+}
+
+const struct GNUNET_MESSENGER_Message*
+GNUNET_MESSENGER_get_message (const struct GNUNET_MESSENGER_Room *room, const 
struct GNUNET_HashCode *hash)
+{
+  const struct GNUNET_MESSENGER_Message *message = get_room_message (room, 
hash);
+
+  if (!message)
+  {
+    struct GNUNET_MESSENGER_RecvMessage *msg;
+    struct GNUNET_MQ_Envelope *env;
+
+    env = GNUNET_MQ_msg(msg, GNUNET_MESSAGE_TYPE_MESSENGER_ROOM_GET_MESSAGE);
+    GNUNET_memcpy(&(msg->key), &(room->key), sizeof(room->key));
+    GNUNET_memcpy(&(msg->hash), hash, sizeof(*hash));
+    GNUNET_MQ_send (room->handle->mq, env);
+  }
+
+  return message;
+}
diff --git a/src/messenger/messenger_api_contact.c 
b/src/messenger/messenger_api_contact.c
new file mode 100644
index 000000000..9a242aa00
--- /dev/null
+++ b/src/messenger/messenger_api_contact.c
@@ -0,0 +1,78 @@
+/*
+   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/messenger_api_contact.c
+ * @brief messenger api: client implementation of GNUnet MESSENGER service
+ */
+
+#include "messenger_api_contact.h"
+
+struct GNUNET_MESSENGER_Contact*
+create_contact (const struct GNUNET_IDENTITY_PublicKey *key)
+{
+  struct GNUNET_MESSENGER_Contact *contact = GNUNET_new(struct 
GNUNET_MESSENGER_Contact);
+
+  contact->name = NULL;
+
+  GNUNET_memcpy(&(contact->public_key), key, sizeof(contact->public_key));
+
+  return contact;
+}
+
+void
+destroy_contact (struct GNUNET_MESSENGER_Contact *contact)
+{
+  if (contact->name)
+    GNUNET_free(contact->name);
+
+  GNUNET_free(contact);
+}
+
+const char*
+get_contact_name (const struct GNUNET_MESSENGER_Contact *contact)
+{
+  return contact->name;
+}
+
+void
+set_contact_name (struct GNUNET_MESSENGER_Contact *contact, const char *name)
+{
+  if (contact->name)
+    GNUNET_free(contact->name);
+
+  contact->name = name? GNUNET_strdup(name) : NULL;
+}
+
+const struct GNUNET_IDENTITY_PublicKey*
+get_contact_key (const struct GNUNET_MESSENGER_Contact *contact)
+{
+  return &(contact->public_key);
+}
+
+const struct GNUNET_HashCode*
+get_contact_id_from_key (const struct GNUNET_MESSENGER_Contact *contact)
+{
+  static struct GNUNET_HashCode id;
+
+  GNUNET_CRYPTO_hash (&(contact->public_key), sizeof(contact->public_key), 
&id);
+
+  return &id;
+}
diff --git a/src/messenger/messenger_api_contact.h 
b/src/messenger/messenger_api_contact.h
new file mode 100644
index 000000000..0673b9b85
--- /dev/null
+++ b/src/messenger/messenger_api_contact.h
@@ -0,0 +1,93 @@
+/*
+   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/messenger_api_contact.h
+ * @brief messenger api: client implementation of GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_MESSENGER_API_CONTACT_H
+#define GNUNET_MESSENGER_API_CONTACT_H
+
+#include "platform.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_identity_service.h"
+
+struct GNUNET_MESSENGER_Contact
+{
+  char *name;
+
+  struct GNUNET_IDENTITY_PublicKey public_key;
+};
+
+/**
+ * Creates and allocates a new contact with a given public <i>key</i> from an 
EGO.
+ *
+ * @param key Public key
+ * @return New contact
+ */
+struct GNUNET_MESSENGER_Contact*
+create_contact (const struct GNUNET_IDENTITY_PublicKey *key);
+
+/**
+ * Destroys a contact and frees its memory fully.
+ *
+ * @param contact Contact
+ */
+void
+destroy_contact (struct GNUNET_MESSENGER_Contact *contact);
+
+/**
+ * Returns the current name of a given <i>contact</i> or NULL if no valid name 
was assigned yet.
+ *
+ * @param contact Contact
+ * @return Name of the contact or NULL
+ */
+const char*
+get_contact_name (const struct GNUNET_MESSENGER_Contact *contact);
+
+/**
+ * Changes the current name of a given <i>contact</i> by copying it from the 
parameter <i>name</i>.
+ *
+ * @param contact Contact
+ * @param name Valid name (may not be NULL!)
+ */
+void
+set_contact_name (struct GNUNET_MESSENGER_Contact *contact, const char *name);
+
+/**
+ * Returns the public key of a given <i>contact</i>.
+ *
+ * @param contact Contact
+ * @return Public key of the contact
+ */
+const struct GNUNET_IDENTITY_PublicKey*
+get_contact_key (const struct GNUNET_MESSENGER_Contact *contact);
+
+/**
+ * Returns the resulting hashcode of the public key from a given 
<i>contact</i>.
+ *
+ * @param contact Contact
+ * @return Hash of the contacts public key
+ */
+const struct GNUNET_HashCode*
+get_contact_id_from_key (const struct GNUNET_MESSENGER_Contact *contact);
+
+#endif //GNUNET_MESSENGER_API_CONTACT_H
diff --git a/src/messenger/messenger_api_ego.h 
b/src/messenger/messenger_api_ego.h
new file mode 100644
index 000000000..c60eeac50
--- /dev/null
+++ b/src/messenger/messenger_api_ego.h
@@ -0,0 +1,38 @@
+/*
+   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/messenger_api_ego.h
+ * @brief GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_MESSENGER_API_EGO_H
+#define GNUNET_MESSENGER_API_EGO_H
+
+#include "platform.h"
+#include "gnunet_identity_service.h"
+
+struct GNUNET_MESSENGER_Ego
+{
+  struct GNUNET_IDENTITY_PrivateKey priv;
+  struct GNUNET_IDENTITY_PublicKey pub;
+};
+
+#endif //GNUNET_MESSENGER_API_EGO_H
diff --git a/src/messenger/messenger_api_handle.c 
b/src/messenger/messenger_api_handle.c
new file mode 100644
index 000000000..20ef77254
--- /dev/null
+++ b/src/messenger/messenger_api_handle.c
@@ -0,0 +1,213 @@
+/*
+   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/messenger_api_handle.c
+ * @brief messenger api: client implementation of GNUnet MESSENGER service
+ */
+
+#include "messenger_api_handle.h"
+
+struct GNUNET_MESSENGER_Handle*
+create_handle (const struct GNUNET_CONFIGURATION_Handle *cfg, 
GNUNET_MESSENGER_IdentityCallback identity_callback,
+               void *identity_cls, GNUNET_MESSENGER_MessageCallback 
msg_callback, void *msg_cls)
+{
+  struct GNUNET_MESSENGER_Handle *handle = GNUNET_new(struct 
GNUNET_MESSENGER_Handle);
+
+  handle->cfg = cfg;
+  handle->mq = NULL;
+
+  handle->identity_callback = identity_callback;
+  handle->identity_cls = identity_cls;
+
+  handle->msg_callback = msg_callback;
+  handle->msg_cls = msg_cls;
+
+  handle->name = NULL;
+  handle->pubkey = NULL;
+
+  handle->reconnect_time = GNUNET_TIME_relative_get_zero_ ();
+  handle->reconnect_task = NULL;
+
+  handle->rooms = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
+  handle->contacts = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
+
+  return handle;
+}
+
+static int
+iterate_destroy_room (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct GNUNET_MESSENGER_Room *room = value;
+
+  destroy_room (room);
+
+  return GNUNET_YES;
+}
+
+static int
+iterate_destroy_contact (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct GNUNET_MESSENGER_Contact *contact = value;
+
+  destroy_contact (contact);
+
+  return GNUNET_YES;
+}
+
+void
+destroy_handle (struct GNUNET_MESSENGER_Handle *handle)
+{
+  if (handle->reconnect_task)
+    GNUNET_SCHEDULER_cancel (handle->reconnect_task);
+
+  if (handle->mq)
+    GNUNET_MQ_destroy (handle->mq);
+
+  if (handle->name)
+    GNUNET_free(handle->name);
+
+  if (handle->pubkey)
+    GNUNET_free(handle->pubkey);
+
+  if (handle->rooms)
+  {
+    GNUNET_CONTAINER_multihashmap_iterate (handle->rooms, 
iterate_destroy_room, NULL);
+
+    GNUNET_CONTAINER_multihashmap_destroy (handle->rooms);
+  }
+
+  if (handle->contacts)
+  {
+    GNUNET_CONTAINER_multihashmap_iterate (handle->contacts, 
iterate_destroy_contact, NULL);
+
+    GNUNET_CONTAINER_multihashmap_destroy (handle->contacts);
+  }
+
+  GNUNET_free(handle->name);
+}
+
+void
+set_handle_name (struct GNUNET_MESSENGER_Handle *handle, const char *name)
+{
+  if (handle->name)
+    GNUNET_free(handle->name);
+
+  handle->name = name? GNUNET_strdup(name) : NULL;
+}
+
+const char*
+get_handle_name (const struct GNUNET_MESSENGER_Handle *handle)
+{
+  return handle->name;
+}
+
+void
+set_handle_key (struct GNUNET_MESSENGER_Handle *handle, const struct 
GNUNET_IDENTITY_PublicKey *pubkey)
+{
+  if (!handle->pubkey)
+    handle->pubkey = GNUNET_new(struct GNUNET_IDENTITY_PublicKey);
+
+  GNUNET_memcpy(handle->pubkey, pubkey, sizeof(*pubkey));
+}
+
+const struct GNUNET_IDENTITY_PublicKey*
+get_handle_key (const struct GNUNET_MESSENGER_Handle *handle)
+{
+  if (!handle->pubkey)
+  {
+    struct GNUNET_IDENTITY_Ego *anonymous = GNUNET_IDENTITY_ego_get_anonymous 
();
+    static struct GNUNET_IDENTITY_PublicKey pubkey;
+
+    GNUNET_IDENTITY_ego_get_public_key (anonymous, &pubkey);
+
+    return &pubkey;
+  }
+
+  return handle->pubkey;
+}
+
+struct GNUNET_MESSENGER_Contact*
+get_handle_contact_by_pubkey (const struct GNUNET_MESSENGER_Handle *handle,
+                              const struct GNUNET_IDENTITY_PublicKey *pubkey)
+{
+  struct GNUNET_HashCode hash;
+
+  GNUNET_CRYPTO_hash (pubkey, sizeof(*pubkey), &hash);
+
+  struct GNUNET_MESSENGER_Contact *contact = GNUNET_CONTAINER_multihashmap_get 
(handle->contacts, &hash);
+
+  if (contact)
+    return contact;
+
+  contact = create_contact (pubkey);
+
+  if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (handle->contacts, &hash, 
contact,
+                                                      
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
+    return contact;
+
+  destroy_contact (contact);
+  return NULL;
+}
+
+void
+swap_handle_contact_by_pubkey (struct GNUNET_MESSENGER_Handle *handle, struct 
GNUNET_MESSENGER_Contact *contact,
+                               const struct GNUNET_IDENTITY_PublicKey *pubkey)
+{
+  const struct GNUNET_HashCode *hash = get_contact_id_from_key (contact);
+
+  if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (handle->contacts, 
hash, contact))
+  {
+    GNUNET_memcpy(&(contact->public_key), pubkey, sizeof(*pubkey));
+
+    hash = get_contact_id_from_key (contact);
+
+    GNUNET_CONTAINER_multihashmap_put (handle->contacts, hash, contact,
+                                       
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+  }
+}
+
+void
+open_handle_room (struct GNUNET_MESSENGER_Handle *handle, const struct 
GNUNET_HashCode *key)
+{
+  struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get 
(handle->rooms, key);
+
+  if (room)
+    room->opened = GNUNET_YES;
+}
+
+void
+entry_handle_room_at (struct GNUNET_MESSENGER_Handle *handle, const struct 
GNUNET_PeerIdentity *door,
+                      const struct GNUNET_HashCode *key)
+{
+  struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get 
(handle->rooms, key);
+
+  if (room)
+    add_to_list_tunnels (&(room->entries), door);
+}
+
+void
+close_handle_room (struct GNUNET_MESSENGER_Handle *handle, const struct 
GNUNET_HashCode *key)
+{
+  struct GNUNET_MESSENGER_Room *room = GNUNET_CONTAINER_multihashmap_get 
(handle->rooms, key);
+
+  if ((room) && (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove 
(handle->rooms, key, room)))
+    destroy_room (room);
+}
diff --git a/src/messenger/messenger_api_handle.h 
b/src/messenger/messenger_api_handle.h
new file mode 100644
index 000000000..d6cde0106
--- /dev/null
+++ b/src/messenger/messenger_api_handle.h
@@ -0,0 +1,174 @@
+/*
+   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/messenger_api_handle.h
+ * @brief messenger api: client implementation of GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_MESSENGER_API_HANDLE_H
+#define GNUNET_MESSENGER_API_HANDLE_H
+
+#include "platform.h"
+#include "gnunet_cadet_service.h"
+#include "gnunet_container_lib.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_identity_service.h"
+#include "gnunet_peer_lib.h"
+
+#include "gnunet_messenger_service.h"
+
+#include "messenger_api_contact.h"
+#include "messenger_api_room.h"
+
+struct GNUNET_MESSENGER_Handle
+{
+  const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+  struct GNUNET_MQ_Handle *mq;
+
+  GNUNET_MESSENGER_IdentityCallback identity_callback;
+  void *identity_cls;
+
+  GNUNET_MESSENGER_MessageCallback msg_callback;
+  void *msg_cls;
+
+  char *name;
+  struct GNUNET_IDENTITY_PublicKey *pubkey;
+
+  struct GNUNET_TIME_Relative reconnect_time;
+  struct GNUNET_SCHEDULER_Task *reconnect_task;
+
+  struct GNUNET_CONTAINER_MultiHashMap *rooms;
+  struct GNUNET_CONTAINER_MultiHashMap *contacts;
+};
+
+/**
+ * Creates and allocates a new handle using a given configuration and a custom 
message callback
+ * with a given closure for the client API.
+ *
+ * @param cfg Configuration
+ * @param msg_callback Message callback
+ * @param msg_cls Closure
+ * @return New handle
+ */
+struct GNUNET_MESSENGER_Handle*
+create_handle (const struct GNUNET_CONFIGURATION_Handle *cfg, 
GNUNET_MESSENGER_IdentityCallback identity_callback,
+               void *identity_cls, GNUNET_MESSENGER_MessageCallback 
msg_callback, void *msg_cls);
+
+/**
+ * Destroys a <i>handle</i> and frees its memory fully from the client API.
+ *
+ * @param handle Handle
+ */
+void
+destroy_handle (struct GNUNET_MESSENGER_Handle *handle);
+
+/**
+ * Sets the name of a <i>handle</i> to a specific <i>name</i>.
+ *
+ * @param handle Handle
+ * @param name New name
+ */
+void
+set_handle_name (struct GNUNET_MESSENGER_Handle *handle, const char *name);
+
+/**
+ * Returns the current name of a given <i>handle</i> or NULL if no valid name 
was assigned yet.
+ *
+ * @param handle Handle
+ * @return Name of the handle or NULL
+ */
+const char*
+get_handle_name (const struct GNUNET_MESSENGER_Handle *handle);
+
+/**
+ * Sets the public key of a given <i>handle</i> to a specific public key.
+ *
+ * @param handle Handle
+ * @param pubkey Public key
+ */
+void
+set_handle_key (struct GNUNET_MESSENGER_Handle *handle, const struct 
GNUNET_IDENTITY_PublicKey *pubkey);
+
+/**
+ * Returns the public key of a given <i>handle</i>.
+ *
+ * @param handle Handle
+ * @return Public key of the handle
+ */
+const struct GNUNET_IDENTITY_PublicKey*
+get_handle_key (const struct GNUNET_MESSENGER_Handle *handle);
+
+/**
+ * Returns a contact known to a <i>handle</i> identified by a given public 
key. If not matching
+ * contact is found, NULL gets returned.
+ *
+ * @param handle Handle
+ * @param pubkey Public key of EGO
+ * @return Contact or NULL
+ */
+struct GNUNET_MESSENGER_Contact*
+get_handle_contact_by_pubkey (const struct GNUNET_MESSENGER_Handle *handle,
+                              const struct GNUNET_IDENTITY_PublicKey *pubkey);
+
+/**
+ * Changes the public key for a <i>contact</i> known to a <i>handle</i> to a 
specific public key and
+ * updates local map entries to access the contact by its updated key.
+ *
+ * @param handle Handle
+ * @param contact Contact
+ * @param pubkey Public key of EGO
+ */
+void
+swap_handle_contact_by_pubkey (struct GNUNET_MESSENGER_Handle *handle, struct 
GNUNET_MESSENGER_Contact *contact,
+                               const struct GNUNET_IDENTITY_PublicKey *pubkey);
+
+/**
+ * Marks a room known to a <i>handle</i> identified by a given <i>key</i> as 
open.
+ *
+ * @param handle Handle
+ * @param key Key of room
+ */
+void
+open_handle_room (struct GNUNET_MESSENGER_Handle *handle, const struct 
GNUNET_HashCode *key);
+
+/**
+ * Adds a tunnel for a room known to a <i>handle</i> identified by a given 
<i>key</i> to a
+ * list of opened connections.
+ *
+ * @param handle Handle
+ * @param door Peer identity
+ * @param key Key of room
+ */
+void
+entry_handle_room_at (struct GNUNET_MESSENGER_Handle *handle, const struct 
GNUNET_PeerIdentity *door,
+                      const struct GNUNET_HashCode *key);
+
+/**
+ * Destroys and so implicitly closes a room known to a <i>handle</i> 
identified by a given <i>key</i>.
+ *
+ * @param handle Handle
+ * @param key Key of room
+ */
+void
+close_handle_room (struct GNUNET_MESSENGER_Handle *handle, const struct 
GNUNET_HashCode *key);
+
+#endif //GNUNET_MESSENGER_API_HANDLE_H
diff --git a/src/messenger/messenger_api_list_tunnels.c 
b/src/messenger/messenger_api_list_tunnels.c
new file mode 100644
index 000000000..13d8c1906
--- /dev/null
+++ b/src/messenger/messenger_api_list_tunnels.c
@@ -0,0 +1,112 @@
+/*
+   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/messenger_api_list_tunnels.c
+ * @brief messenger api: client and service implementation of GNUnet MESSENGER 
service
+ */
+
+#include "messenger_api_list_tunnels.h"
+
+void
+init_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels)
+{
+  GNUNET_assert(tunnels);
+
+  tunnels->head = NULL;
+  tunnels->tail = NULL;
+}
+
+void
+clear_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels)
+{
+  GNUNET_assert(tunnels);
+
+  struct GNUNET_MESSENGER_ListTunnel *element;
+
+  for (element = tunnels->head; element; element = tunnels->head)
+  {
+    GNUNET_CONTAINER_DLL_remove(tunnels->head, tunnels->tail, element);
+    GNUNET_PEER_change_rc (element->peer, -1);
+    GNUNET_free(element);
+  }
+
+  tunnels->head = NULL;
+  tunnels->tail = NULL;
+}
+
+static int
+compare_list_tunnels (void *cls, struct GNUNET_MESSENGER_ListTunnel *element0,
+                      struct GNUNET_MESSENGER_ListTunnel *element1)
+{
+  return ((int) element0->peer) - ((int) element1->peer);
+}
+
+void
+add_to_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const 
struct GNUNET_PeerIdentity *peer)
+{
+  struct GNUNET_MESSENGER_ListTunnel *element = GNUNET_new(struct 
GNUNET_MESSENGER_ListTunnel);
+
+  element->peer = GNUNET_PEER_intern (peer);
+
+  GNUNET_CONTAINER_DLL_insert_sorted(struct GNUNET_MESSENGER_ListTunnel, 
compare_list_tunnels, NULL, tunnels->head,
+                                     tunnels->tail, element);
+}
+
+struct GNUNET_MESSENGER_ListTunnel*
+find_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const struct 
GNUNET_PeerIdentity *peer, size_t *index)
+{
+  struct GNUNET_MESSENGER_ListTunnel *element;
+  struct GNUNET_PeerIdentity pid;
+
+  if (index)
+    *index = 0;
+
+  for (element = tunnels->head; element; element = element->next)
+  {
+    GNUNET_PEER_resolve (element->peer, &pid);
+
+    if (0 == GNUNET_memcmp(&pid, peer))
+      return element;
+
+    if (index)
+      (*index) = (*index) + 1;
+  }
+
+  return NULL;
+}
+
+int
+contains_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const 
struct GNUNET_PeerIdentity *peer)
+{
+  return find_list_tunnels (tunnels, peer, NULL) != NULL ? GNUNET_YES : 
GNUNET_NO;
+}
+
+struct GNUNET_MESSENGER_ListTunnel*
+remove_from_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, struct 
GNUNET_MESSENGER_ListTunnel *element)
+{
+  struct GNUNET_MESSENGER_ListTunnel *next = element->next;
+
+  GNUNET_CONTAINER_DLL_remove(tunnels->head, tunnels->tail, element);
+  GNUNET_PEER_change_rc (element->peer, -1);
+  GNUNET_free(element);
+
+  return next;
+}
diff --git a/src/messenger/messenger_api_list_tunnels.h 
b/src/messenger/messenger_api_list_tunnels.h
new file mode 100644
index 000000000..0240fceb8
--- /dev/null
+++ b/src/messenger/messenger_api_list_tunnels.h
@@ -0,0 +1,112 @@
+/*
+   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/messenger_api_list_tunnels.h
+ * @brief messenger api: client and service implementation of GNUnet MESSENGER 
service
+ */
+
+#ifndef GNUNET_MESSENGER_API_LIST_TUNNELS_H
+#define GNUNET_MESSENGER_API_LIST_TUNNELS_H
+
+#include "platform.h"
+#include "gnunet_peer_lib.h"
+#include "gnunet_container_lib.h"
+
+struct GNUNET_MESSENGER_ListTunnel
+{
+  struct GNUNET_MESSENGER_ListTunnel *prev;
+  struct GNUNET_MESSENGER_ListTunnel *next;
+
+  GNUNET_PEER_Id peer;
+};
+
+struct GNUNET_MESSENGER_ListTunnels
+{
+  struct GNUNET_MESSENGER_ListTunnel *head;
+  struct GNUNET_MESSENGER_ListTunnel *tail;
+};
+
+/**
+ * Initializes list of tunnels peer identities as empty list.
+ *
+ * @param tunnels List of peer identities
+ */
+void
+init_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels);
+
+/**
+ * Clears the list of tunnels peer identities.
+ *
+ * @param tunnels List of peer identities
+ */
+void
+clear_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels);
+
+/**
+ * Adds a specific <i>peer</i> from a tunnel to the end of the list.
+ *
+ * @param tunnels List of peer identities
+ * @param peer Peer identity of tunnel
+ */
+void
+add_to_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const 
struct GNUNET_PeerIdentity *peer);
+
+/**
+ * Searches linearly through the list of tunnels peer identities for matching a
+ * specific <i>peer</i> identity and returns the matching element of the list.
+ *
+ * If no matching element is found, NULL gets returned.
+ *
+ * If <i>index</i> is not NULL, <i>index</i> will be overriden with the 
numeric index of
+ * the found element in the list. If no matching element is found, 
<i>index</i> will
+ * contain the total amount of elements in the list.
+ *
+ * @param tunnels List of peer identities
+ * @param peer Peer identity of tunnel
+ * @param[out] index Index of found element (optional)
+ * @return Element in the list with matching peer identity
+ */
+struct GNUNET_MESSENGER_ListTunnel*
+find_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const struct 
GNUNET_PeerIdentity *peer, size_t *index);
+
+/**
+ * Tests linearly if the list of tunnels peer identities contains a specific
+ * <i>peer</i> identity and returns GNUNET_YES on success, otherwise GNUNET_NO.
+ *
+ * @param tunnels List of peer identities
+ * @param peer Peer identity of tunnel
+ * @return GNUNET_YES on success, otherwise GNUNET_NO
+ */
+int
+contains_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, const 
struct GNUNET_PeerIdentity *peer);
+
+/**
+ * Removes a specific <i>element</i> from the list of tunnels peer identities 
and returns
+ * the next element in the list.
+ *
+ * @param tunnels List of peer identities
+ * @param element Element of the list
+ * @return Next element in the list
+ */
+struct GNUNET_MESSENGER_ListTunnel*
+remove_from_list_tunnels (struct GNUNET_MESSENGER_ListTunnels *tunnels, struct 
GNUNET_MESSENGER_ListTunnel *element);
+
+#endif //GNUNET_MESSENGER_API_LIST_TUNNELS_H
diff --git a/src/messenger/messenger_api_message.c 
b/src/messenger/messenger_api_message.c
new file mode 100644
index 000000000..fdab60eef
--- /dev/null
+++ b/src/messenger/messenger_api_message.c
@@ -0,0 +1,602 @@
+/*
+   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/messenger_api_message.c
+ * @brief messenger api: client and service implementation of GNUnet MESSENGER 
service
+ */
+
+#include "messenger_api_message.h"
+
+struct GNUNET_MESSENGER_MessageSignature
+{
+  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+  struct GNUNET_HashCode hash;
+};
+
+struct GNUNET_MESSENGER_ShortMessage
+{
+  enum GNUNET_MESSENGER_MessageKind kind;
+  struct GNUNET_MESSENGER_MessageBody body;
+};
+
+struct GNUNET_MESSENGER_Message*
+create_message (enum GNUNET_MESSENGER_MessageKind kind)
+{
+  struct GNUNET_MESSENGER_Message *message = GNUNET_new(struct 
GNUNET_MESSENGER_Message);
+
+  message->header.kind = kind;
+
+  switch (message->header.kind)
+  {
+  case GNUNET_MESSENGER_KIND_NAME:
+    message->body.name.name = NULL;
+    break;
+  case GNUNET_MESSENGER_KIND_TEXT:
+    message->body.text.text = NULL;
+    break;
+  case GNUNET_MESSENGER_KIND_FILE:
+    message->body.file.uri = NULL;
+    break;
+  case GNUNET_MESSENGER_KIND_PRIVATE:
+    message->body.private.length = 0;
+    message->body.private.data = NULL;
+    break;
+  default:
+    break;
+  }
+
+  return message;
+}
+
+struct GNUNET_MESSENGER_Message*
+copy_message (const struct GNUNET_MESSENGER_Message *message)
+{
+  struct GNUNET_MESSENGER_Message *copy = GNUNET_new(struct 
GNUNET_MESSENGER_Message);
+
+  GNUNET_memcpy(copy, message, sizeof(struct GNUNET_MESSENGER_Message));
+
+  switch (message->header.kind)
+  {
+  case GNUNET_MESSENGER_KIND_NAME:
+    copy->body.name.name = GNUNET_strdup(message->body.name.name);
+    break;
+  case GNUNET_MESSENGER_KIND_TEXT:
+    copy->body.text.text = GNUNET_strdup(message->body.text.text);
+    break;
+  case GNUNET_MESSENGER_KIND_FILE:
+    copy->body.file.uri = GNUNET_strdup(message->body.file.uri);
+    break;
+  case GNUNET_MESSENGER_KIND_PRIVATE:
+    copy->body.private.data = copy->body.private.length ? 
GNUNET_malloc(copy->body.private.length) : NULL;
+
+    if (copy->body.private.data)
+    {
+      GNUNET_memcpy(copy->body.private.data, message->body.private.data, 
copy->body.private.length);
+    }
+
+    break;
+  default:
+    break;
+  }
+
+  return copy;
+}
+
+static void
+destroy_message_body (enum GNUNET_MESSENGER_MessageKind kind, struct 
GNUNET_MESSENGER_MessageBody *body)
+{
+  switch (kind)
+  {
+  case GNUNET_MESSENGER_KIND_NAME:
+    GNUNET_free(body->name.name);
+    break;
+  case GNUNET_MESSENGER_KIND_TEXT:
+    GNUNET_free(body->text.text);
+    break;
+  case GNUNET_MESSENGER_KIND_FILE:
+    GNUNET_free(body->file.uri);
+    break;
+  case GNUNET_MESSENGER_KIND_PRIVATE:
+    GNUNET_free(body->private.data);
+    break;
+  default:
+    break;
+  }
+}
+
+void
+destroy_message (struct GNUNET_MESSENGER_Message *message)
+{
+  destroy_message_body (message->header.kind, &(message->body));
+
+  GNUNET_free(message);
+}
+
+static void
+fold_short_message (const struct GNUNET_MESSENGER_Message *message, struct 
GNUNET_MESSENGER_ShortMessage *shortened)
+{
+  shortened->kind = message->header.kind;
+
+  GNUNET_memcpy(&(shortened->body), &(message->body), sizeof(struct 
GNUNET_MESSENGER_MessageBody));
+}
+
+static void
+unfold_short_message (struct GNUNET_MESSENGER_ShortMessage *shortened, struct 
GNUNET_MESSENGER_Message *message)
+{
+  destroy_message_body (message->header.kind, &(message->body));
+
+  message->header.kind = shortened->kind;
+
+  GNUNET_memcpy(&(message->body), &(shortened->body), sizeof(struct 
GNUNET_MESSENGER_MessageBody));
+}
+
+#define member_size(type, member) sizeof(((type*) NULL)->member)
+
+static uint16_t
+get_message_body_kind_size (enum GNUNET_MESSENGER_MessageKind kind)
+{
+  uint16_t length = 0;
+
+  switch (kind)
+  {
+  case GNUNET_MESSENGER_KIND_INFO:
+    length += member_size(struct GNUNET_MESSENGER_Message, body.info.host_key);
+    length += member_size(struct GNUNET_MESSENGER_Message, 
body.info.unique_id);
+    break;
+  case GNUNET_MESSENGER_KIND_JOIN:
+    length += member_size(struct GNUNET_MESSENGER_Message, body.join.key);
+    break;
+  case GNUNET_MESSENGER_KIND_LEAVE:
+    break;
+  case GNUNET_MESSENGER_KIND_NAME:
+    break;
+  case GNUNET_MESSENGER_KIND_KEY:
+    length += member_size(struct GNUNET_MESSENGER_Message, body.key.key);
+    break;
+  case GNUNET_MESSENGER_KIND_PEER:
+    length += member_size(struct GNUNET_MESSENGER_Message, body.peer.peer);
+    break;
+  case GNUNET_MESSENGER_KIND_ID:
+    length += member_size(struct GNUNET_MESSENGER_Message, body.id.id);
+    break;
+  case GNUNET_MESSENGER_KIND_MISS:
+    length += member_size(struct GNUNET_MESSENGER_Message, body.miss.peer);
+    break;
+  case GNUNET_MESSENGER_KIND_MERGE:
+    length += member_size(struct GNUNET_MESSENGER_Message, 
body.merge.previous);
+    break;
+  case GNUNET_MESSENGER_KIND_REQUEST:
+    length += member_size(struct GNUNET_MESSENGER_Message, body.request.hash);
+    break;
+  case GNUNET_MESSENGER_KIND_INVITE:
+    length += member_size(struct GNUNET_MESSENGER_Message, body.invite.door);
+    length += member_size(struct GNUNET_MESSENGER_Message, body.invite.key);
+    break;
+  case GNUNET_MESSENGER_KIND_TEXT:
+    break;
+  case GNUNET_MESSENGER_KIND_FILE:
+    length += member_size(struct GNUNET_MESSENGER_Message, body.file.key);
+    length += member_size(struct GNUNET_MESSENGER_Message, body.file.hash);
+    length += NAME_MAX;
+    break;
+  case GNUNET_MESSENGER_KIND_PRIVATE:
+    length += member_size(struct GNUNET_MESSENGER_Message, body.private.key);
+    break;
+  default:
+    break;
+  }
+
+  return length;
+}
+
+uint16_t
+get_message_kind_size (enum GNUNET_MESSENGER_MessageKind kind)
+{
+  uint16_t length = 0;
+
+  length += member_size(struct GNUNET_MESSENGER_Message, header.signature);
+  length += member_size(struct GNUNET_MESSENGER_Message, header.timestamp);
+  length += member_size(struct GNUNET_MESSENGER_Message, header.sender_id);
+  length += member_size(struct GNUNET_MESSENGER_Message, header.previous);
+  length += member_size(struct GNUNET_MESSENGER_Message, header.kind);
+
+  return length + get_message_body_kind_size (kind);
+}
+
+static uint16_t
+get_message_body_size (enum GNUNET_MESSENGER_MessageKind kind, const struct 
GNUNET_MESSENGER_MessageBody *body)
+{
+  uint16_t length = 0;
+
+  switch (kind)
+  {
+  case GNUNET_MESSENGER_KIND_NAME:
+    length += (body->name.name? strlen (body->name.name) : 0);
+    break;
+  case GNUNET_MESSENGER_KIND_TEXT:
+    length += strlen (body->text.text);
+    break;
+  case GNUNET_MESSENGER_KIND_FILE:
+    length += strlen (body->file.uri);
+    break;
+  case GNUNET_MESSENGER_KIND_PRIVATE:
+    length += body->private.length;
+    break;
+  default:
+    break;
+  }
+
+  return length;
+}
+
+uint16_t
+get_message_size (const struct GNUNET_MESSENGER_Message *message)
+{
+  return get_message_kind_size (message->header.kind) + get_message_body_size 
(message->header.kind, &(message->body));
+}
+
+static uint16_t
+get_short_message_size (const struct GNUNET_MESSENGER_ShortMessage *message)
+{
+  if (message)
+    return sizeof(message->kind) + get_message_body_kind_size (message->kind)
+           + get_message_body_size (message->kind, &(message->body));
+  else
+    return sizeof(message->kind);
+}
+
+#define min(x, y) (x < y? x : y)
+
+#define encode_step_ext(dst, offset, src, size) do { \
+       GNUNET_memcpy(dst + offset, src, size);                        \
+       offset += size;                                                \
+} while (0)
+
+#define encode_step(dst, offset, src) do {         \
+  encode_step_ext(dst, offset, src, sizeof(*src)); \
+} while(0)
+
+static void
+encode_message_body (enum GNUNET_MESSENGER_MessageKind kind, const struct 
GNUNET_MESSENGER_MessageBody *body,
+                     uint16_t length, char *buffer, uint16_t offset)
+{
+  switch (kind)
+  {
+  case GNUNET_MESSENGER_KIND_INFO:
+    encode_step(buffer, offset, &(body->info.host_key));
+    encode_step(buffer, offset, &(body->info.unique_id));
+    break;
+  case GNUNET_MESSENGER_KIND_JOIN:
+    encode_step(buffer, offset, &(body->join.key));
+    break;
+  case GNUNET_MESSENGER_KIND_LEAVE:
+    break;
+  case GNUNET_MESSENGER_KIND_NAME:
+    if (body->name.name)
+      encode_step_ext(buffer, offset, body->name.name, min(length - offset, 
strlen(body->name.name)));
+    break;
+  case GNUNET_MESSENGER_KIND_KEY:
+    encode_step(buffer, offset, &(body->key.key));
+    break;
+  case GNUNET_MESSENGER_KIND_PEER:
+    encode_step(buffer, offset, &(body->peer.peer));
+    break;
+  case GNUNET_MESSENGER_KIND_ID:
+    encode_step(buffer, offset, &(body->id.id));
+    break;
+  case GNUNET_MESSENGER_KIND_MISS:
+    encode_step(buffer, offset, &(body->miss.peer));
+    break;
+  case GNUNET_MESSENGER_KIND_MERGE:
+    encode_step(buffer, offset, &(body->merge.previous));
+    break;
+  case GNUNET_MESSENGER_KIND_REQUEST:
+    encode_step(buffer, offset, &(body->request.hash));
+    break;
+  case GNUNET_MESSENGER_KIND_INVITE:
+    encode_step(buffer, offset, &(body->invite.door));
+    encode_step(buffer, offset, &(body->invite.key));
+    break;
+  case GNUNET_MESSENGER_KIND_TEXT:
+    encode_step_ext(buffer, offset, body->text.text, min(length - offset, 
strlen(body->text.text)));
+    break;
+  case GNUNET_MESSENGER_KIND_FILE:
+    encode_step(buffer, offset, &(body->file.key));
+    encode_step(buffer, offset, &(body->file.hash));
+    encode_step_ext(buffer, offset, body->file.name, NAME_MAX);
+    encode_step_ext(buffer, offset, body->file.uri, min(length - offset, 
strlen(body->file.uri)));
+    break;
+  case GNUNET_MESSENGER_KIND_PRIVATE:
+    encode_step(buffer, offset, &(body->private.key));
+    encode_step_ext(buffer, offset, body->private.data, min(length - offset, 
body->private.length));
+    break;
+  default:
+    break;
+  }
+}
+
+void
+encode_message (const struct GNUNET_MESSENGER_Message *message, uint16_t 
length, char *buffer)
+{
+  uint16_t offset = 0;
+
+  encode_step(buffer, offset, &(message->header.signature));
+  encode_step(buffer, offset, &(message->header.timestamp));
+  encode_step(buffer, offset, &(message->header.sender_id));
+  encode_step(buffer, offset, &(message->header.previous));
+  encode_step(buffer, offset, &(message->header.kind));
+
+  encode_message_body (message->header.kind, &(message->body), length, buffer, 
offset);
+}
+
+static void
+encode_short_message (const struct GNUNET_MESSENGER_ShortMessage *message, 
uint16_t length, char *buffer)
+{
+  uint16_t offset = 0;
+
+  encode_step(buffer, offset, &(message->kind));
+
+  encode_message_body (message->kind, &(message->body), length, buffer, 
offset);
+}
+
+#define decode_step_ext(src, offset, dst, size) do { \
+       GNUNET_memcpy(dst, src + offset, size);                              \
+       offset += size;                                                      \
+} while (0)
+
+#define decode_step(src, offset, dst) do {                              \
+  decode_step_ext(src, offset, dst, sizeof(*dst)); \
+} while (0)
+
+#define decode_step_malloc(src, offset, dst, size, zero) do {  \
+       dst = GNUNET_malloc(size + zero);                           \
+  if (zero) dst[size] = 0;                                                     
                                  \
+       decode_step_ext(src, offset, dst, size);                                
                  \
+} while (0)
+
+static void
+decode_message_body (enum GNUNET_MESSENGER_MessageKind *kind, struct 
GNUNET_MESSENGER_MessageBody *body,
+                     uint16_t length, const char *buffer, uint16_t offset)
+{
+  switch (*kind)
+  {
+  case GNUNET_MESSENGER_KIND_INFO:
+    decode_step(buffer, offset, &(body->info.host_key));
+    decode_step(buffer, offset, &(body->info.unique_id));
+    break;
+  case GNUNET_MESSENGER_KIND_JOIN:
+    decode_step(buffer, offset, &(body->join.key));
+    break;
+  case GNUNET_MESSENGER_KIND_LEAVE:
+    break;
+  case GNUNET_MESSENGER_KIND_NAME:
+    if (length - offset > 0)
+      decode_step_malloc(buffer, offset, body->name.name, length - offset, 1);
+    else
+      body->name.name = NULL;
+    break;
+  case GNUNET_MESSENGER_KIND_KEY:
+    decode_step(buffer, offset, &(body->key.key));
+    break;
+  case GNUNET_MESSENGER_KIND_PEER:
+    decode_step(buffer, offset, &(body->peer.peer));
+    break;
+  case GNUNET_MESSENGER_KIND_ID:
+    decode_step(buffer, offset, &(body->id.id));
+    break;
+  case GNUNET_MESSENGER_KIND_MISS:
+    decode_step(buffer, offset, &(body->miss.peer));
+    break;
+  case GNUNET_MESSENGER_KIND_MERGE:
+    decode_step(buffer, offset, &(body->merge.previous));
+    break;
+  case GNUNET_MESSENGER_KIND_REQUEST:
+    decode_step(buffer, offset, &(body->request.hash));
+    break;
+  case GNUNET_MESSENGER_KIND_INVITE:
+    decode_step(buffer, offset, &(body->invite.door));
+    decode_step(buffer, offset, &(body->invite.key));
+    break;
+  case GNUNET_MESSENGER_KIND_TEXT:
+    decode_step_malloc(buffer, offset, body->text.text, length - offset, 1);
+    break;
+  case GNUNET_MESSENGER_KIND_FILE:
+    decode_step(buffer, offset, &(body->file.key));
+    decode_step(buffer, offset, &(body->file.hash));
+    decode_step_ext(buffer, offset, body->file.name, NAME_MAX);
+    decode_step_malloc(buffer, offset, body->file.uri, length - offset, 1);
+    break;
+  case GNUNET_MESSENGER_KIND_PRIVATE:
+    decode_step(buffer, offset, &(body->private.key));
+
+    body->private.length = (length - offset);
+    decode_step_malloc(buffer, offset, body->private.data, length - offset, 0);
+    break;
+  default:
+    *kind = GNUNET_MESSENGER_KIND_UNKNOWN;
+    break;
+  }
+}
+
+int
+decode_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, 
const char *buffer)
+{
+  uint16_t offset = 0;
+
+  if (length < get_message_kind_size (GNUNET_MESSENGER_KIND_UNKNOWN))
+    return GNUNET_NO;
+
+  decode_step(buffer, offset, &(message->header.signature));
+  decode_step(buffer, offset, &(message->header.timestamp));
+  decode_step(buffer, offset, &(message->header.sender_id));
+  decode_step(buffer, offset, &(message->header.previous));
+  decode_step(buffer, offset, &(message->header.kind));
+
+  if (length < get_message_kind_size (message->header.kind))
+    return GNUNET_NO;
+
+  decode_message_body (&(message->header.kind), &(message->body), length, 
buffer, offset);
+
+  return GNUNET_YES;
+}
+
+static int
+decode_short_message (struct GNUNET_MESSENGER_ShortMessage *message, uint16_t 
length, const char *buffer)
+{
+  uint16_t offset = 0;
+
+  if (length < get_short_message_size (NULL))
+    return GNUNET_NO;
+
+  decode_step(buffer, offset, &(message->kind));
+
+  if (length < get_short_message_size (message))
+    return GNUNET_NO;
+
+  decode_message_body (&(message->kind), &(message->body), length, buffer, 
offset);
+
+  return GNUNET_YES;
+}
+
+void
+hash_message (uint16_t length, const char *buffer, struct GNUNET_HashCode 
*hash)
+{
+  GNUNET_CRYPTO_hash (buffer + sizeof(struct GNUNET_CRYPTO_EcdsaSignature),
+                      length - sizeof(struct GNUNET_CRYPTO_EcdsaSignature), 
hash);
+}
+
+void
+sign_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, char 
*buffer,
+              const struct GNUNET_HashCode *hash, const struct 
GNUNET_MESSENGER_Ego *ego)
+{
+  struct GNUNET_MESSENGER_MessageSignature signature;
+
+  signature.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CHAT_MESSAGE);
+  signature.purpose.size = htonl (sizeof(signature));
+
+  GNUNET_memcpy(&(signature.hash), hash, sizeof(struct GNUNET_HashCode));
+
+  GNUNET_IDENTITY_sign(&(ego->priv), &signature, &(message->header.signature));
+  GNUNET_memcpy(buffer, &(message->header.signature), sizeof(struct 
GNUNET_CRYPTO_EcdsaSignature));
+}
+
+int
+verify_message (const struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash,
+                const struct GNUNET_IDENTITY_PublicKey *key)
+{
+  struct GNUNET_MESSENGER_MessageSignature signature;
+
+  signature.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CHAT_MESSAGE);
+  signature.purpose.size = htonl (sizeof(signature));
+
+  GNUNET_memcpy(&(signature.hash), hash, sizeof(struct GNUNET_HashCode));
+
+  return 
GNUNET_IDENTITY_signature_verify(GNUNET_SIGNATURE_PURPOSE_CHAT_MESSAGE, 
&signature,
+                                          &(message->header.signature), key);
+}
+
+int
+encrypt_message (struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_IDENTITY_PublicKey *key)
+{
+  struct GNUNET_MESSENGER_ShortMessage shortened;
+
+  fold_short_message (message, &shortened);
+
+  const uint16_t length = get_short_message_size (&shortened);
+
+  message->header.kind = GNUNET_MESSENGER_KIND_PRIVATE;
+  message->body.private.data = GNUNET_malloc(length);
+
+  encode_short_message (&shortened, length, message->body.private.data);
+
+  if (GNUNET_IDENTITY_encrypt (message->body.private.data, length, key, 
&(message->body.private.key),
+                               message->body.private.data)
+      == length)
+  {
+    destroy_message_body (shortened.kind, &(shortened.body));
+    return GNUNET_YES;
+  }
+  else
+  {
+    unfold_short_message (&shortened, message);
+    return GNUNET_NO;
+  }
+}
+
+int
+decrypt_message (struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_IDENTITY_PrivateKey *key)
+{
+  if (message->body.private.length != GNUNET_IDENTITY_decrypt 
(message->body.private.data,
+                                                               
message->body.private.length, key,
+                                                               
&(message->body.private.key),
+                                                               
message->body.private.data))
+    return GNUNET_NO;
+
+  struct GNUNET_MESSENGER_ShortMessage shortened;
+
+  if (GNUNET_YES != decode_short_message (&shortened, 
message->body.private.length, message->body.private.data))
+    return GNUNET_NO;
+
+  unfold_short_message (&shortened, message);
+  return GNUNET_YES;
+}
+
+struct GNUNET_MQ_Envelope*
+pack_message (struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode 
*hash,
+              const struct GNUNET_MESSENGER_Ego *ego, int mode)
+{
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Packing message: %u\n", 
message->header.kind);
+
+  struct GNUNET_MessageHeader *header;
+
+  uint16_t length = get_message_size (message);
+
+  struct GNUNET_MQ_Envelope *env;
+  char *buffer;
+
+  if (GNUNET_MESSENGER_PACK_MODE_ENVELOPE == mode)
+  {
+    env = GNUNET_MQ_msg_extra(header, length, GNUNET_MESSAGE_TYPE_CADET_CLI);
+
+    buffer = (char*) &(header[1]);
+  }
+  else
+  {
+    env = NULL;
+
+    buffer = GNUNET_malloc(length);
+  }
+
+  encode_message (message, length, buffer);
+
+  if (hash)
+  {
+    hash_message (length, buffer, hash);
+
+    if (ego)
+      sign_message (message, length, buffer, hash, ego);
+  }
+
+  if (GNUNET_MESSENGER_PACK_MODE_ENVELOPE != mode)
+    GNUNET_free(buffer);
+
+  return env;
+}
diff --git a/src/messenger/messenger_api_message.h 
b/src/messenger/messenger_api_message.h
new file mode 100644
index 000000000..0f0a97e9c
--- /dev/null
+++ b/src/messenger/messenger_api_message.h
@@ -0,0 +1,190 @@
+/*
+   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/messenger_api_message.h
+ * @brief messenger api: client and service implementation of GNUnet MESSENGER 
service
+ */
+
+#ifndef GNUNET_MESSENGER_API_MESSAGE_H
+#define GNUNET_MESSENGER_API_MESSAGE_H
+
+#include "platform.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_identity_service.h"
+#include "gnunet_mq_lib.h"
+#include "gnunet_signatures.h"
+
+#include "gnunet_messenger_service.h"
+
+#include "messenger_api_ego.h"
+
+/**
+ * Creates and allocates a new message with a specific <i>kind</i>.
+ *
+ * @param kind Kind of message
+ * @return New message
+ */
+struct GNUNET_MESSENGER_Message*
+create_message (enum GNUNET_MESSENGER_MessageKind kind);
+
+/**
+ * Creates and allocates a copy of a given <i>message</i>.
+ *
+ * @param message Message
+ * @return New message
+ */
+struct GNUNET_MESSENGER_Message*
+copy_message (const struct GNUNET_MESSENGER_Message *message);
+
+/**
+ * Destroys a message and frees its memory fully.
+ *
+ * @param message Message
+ */
+void
+destroy_message (struct GNUNET_MESSENGER_Message *message);
+
+/**
+ * Returns the minimal size in bytes to encode a message of a specific 
<i>kind</i>.
+ *
+ * @param kind Kind of message
+ * @return Minimal size to encode
+ */
+uint16_t
+get_message_kind_size (enum GNUNET_MESSENGER_MessageKind kind);
+
+/**
+ * Returns the exact size in bytes to encode a given <i>message</i>.
+ *
+ * @param message Message
+ * @return Size to encode
+ */
+uint16_t
+get_message_size (const struct GNUNET_MESSENGER_Message *message);
+
+/**
+ * Encodes a given <i>message</i> into a <i>buffer</i> of a maximal 
<i>length</i> in bytes.
+ *
+ * @param message Message
+ * @param length Maximal length to encode
+ * @param[out] buffer Buffer
+ */
+void
+encode_message (const struct GNUNET_MESSENGER_Message *message, uint16_t 
length, char *buffer);
+
+/**
+ * Decodes a <i>message</i> from a given <i>buffer</i> of a maximal 
<i>length</i> in bytes.
+ *
+ * If the buffer is too small for a message of its decoded kind the function 
fails with
+ * resulting GNUNET_NO after decoding only the messages header.
+ *
+ * On success the function returns GNUNET_YES.
+ *
+ * @param[out] message Message
+ * @param length Maximal length to decode
+ * @param buffer Buffer
+ * @return GNUNET_YES on success, otherwise GNUNET_NO
+ */
+int
+decode_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, 
const char *buffer);
+
+/**
+ * Calculates a <i>hash</i> of a given <i>buffer</i> of a <i>length</i> in 
bytes.
+ *
+ * @param length Length of buffer
+ * @param buffer Buffer
+ * @param[out] hash Hash
+ */
+void
+hash_message (uint16_t length, const char *buffer, struct GNUNET_HashCode 
*hash);
+
+/**
+ * Signs the <i>hash</i> of a <i>message</i> with a given <i>ego</i> and 
writes the signature
+ * into the <i>buffer</i> as well.
+ *
+ * @param[out] message Message
+ * @param length Length of buffer
+ * @param[out] buffer Buffer
+ * @param hash Hash of message
+ * @param ego EGO
+ */
+void
+sign_message (struct GNUNET_MESSENGER_Message *message, uint16_t length, char 
*buffer,
+              const struct GNUNET_HashCode *hash, const struct 
GNUNET_MESSENGER_Ego *ego);
+
+/**
+ * Verifies the signature of a given <i>message</i> and its <i>hash</i> with a 
specific
+ * public key. The function returns GNUNET_OK if the signature was valid, 
otherwise
+ * GNUNET_SYSERR.
+ *
+ * @param message Message
+ * @param hash Hash of message
+ * @param key Public key of EGO
+ * @return GNUNET_OK on success, otherwise GNUNET_SYSERR
+ */
+int
+verify_message (const struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_HashCode *hash,
+                const struct GNUNET_IDENTITY_PublicKey *key);
+
+/**
+ * Encrypts a <i>message</i> using a given public <i>key</i> and replaces its 
body
+ * and kind with the now private encrypted <i>message</i>. The function returns
+ * GNUNET_YES if the operation succeeded, otherwise GNUNET_NO.
+ *
+ * @param message Message
+ * @param key Public key of EGO
+ * @return GNUNET_YES on success, otherwise GNUNET_NO
+ */
+int
+encrypt_message (struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_IDENTITY_PublicKey *key);
+
+/**
+ * Decrypts a private <i>message</i> using a given private <i>key</i> and 
replaces its body
+ * and kind with the inner encrypted message. The function returns GNUNET_YES 
if the
+ * operation succeeded, otherwise GNUNET_NO.
+ *
+ * @param message Message
+ * @param key Private key of EGO
+ * @return GNUNET_YES on success, otherwise GNUNET_NO
+ */
+int
+decrypt_message (struct GNUNET_MESSENGER_Message *message, const struct 
GNUNET_IDENTITY_PrivateKey *key);
+
+#define GNUNET_MESSENGER_PACK_MODE_ENVELOPE 0x1
+#define GNUNET_MESSENGER_PACK_MODE_UNKNOWN 0x0
+
+/**
+ * Encodes the <i>message</i> to pack it into a newly allocated envelope if 
<i>mode</i>
+ * is equal to GNUNET_MESSENGER_PACK_MODE_ENVELOPE. Independent of the mode 
the message
+ * will be hashed if <i>hash</i> is not NULL and it will be signed if the 
<i>ego</i> is
+ * not NULL.
+ *
+ * @param[out] message Message
+ * @param[out] hash Hash of message
+ * @param ego EGO to sign
+ * @param mode Mode of packing
+ * @return Envelope or NULL
+ */
+struct GNUNET_MQ_Envelope*
+pack_message (struct GNUNET_MESSENGER_Message *message, struct GNUNET_HashCode 
*hash,
+              const struct GNUNET_MESSENGER_Ego *ego, int mode);
+
+#endif //GNUNET_MESSENGER_API_MESSAGE_H
diff --git a/src/messenger/messenger_api_room.c 
b/src/messenger/messenger_api_room.c
new file mode 100644
index 000000000..5fedf1a78
--- /dev/null
+++ b/src/messenger/messenger_api_room.c
@@ -0,0 +1,189 @@
+/*
+   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/messenger_api_room.c
+ * @brief messenger api: client implementation of GNUnet MESSENGER service
+ */
+
+#include "messenger_api_room.h"
+
+#include "messenger_api_handle.h"
+
+struct GNUNET_MESSENGER_Room*
+create_room (struct GNUNET_MESSENGER_Handle *handle, const struct 
GNUNET_HashCode *key)
+{
+  struct GNUNET_MESSENGER_Room *room = GNUNET_new(struct 
GNUNET_MESSENGER_Room);
+
+  room->handle = handle;
+  GNUNET_memcpy(&(room->key), key, sizeof(*key));
+
+  room->opened = GNUNET_NO;
+  room->contact_id = NULL;
+
+  room->members = GNUNET_CONTAINER_multishortmap_create (8, GNUNET_NO);
+
+  init_list_tunnels (&(room->entries));
+
+  room->messages = GNUNET_CONTAINER_multihashmap_create (8, GNUNET_NO);
+
+  return room;
+}
+
+static int
+iterate_destroy_message (void *cls, const struct GNUNET_HashCode *key, void 
*value)
+{
+  struct GNUNET_MESSENGER_Message *message = value;
+
+  destroy_message (message);
+
+  return GNUNET_YES;
+}
+
+void
+destroy_room (struct GNUNET_MESSENGER_Room *room)
+{
+  if (room->members)
+    GNUNET_CONTAINER_multishortmap_destroy (room->members);
+
+  clear_list_tunnels (&(room->entries));
+
+  if (room->messages)
+  {
+    GNUNET_CONTAINER_multihashmap_iterate (room->messages, 
iterate_destroy_message, NULL);
+
+    GNUNET_CONTAINER_multihashmap_destroy (room->messages);
+  }
+
+  if (room->contact_id)
+    GNUNET_free(room->contact_id);
+
+  GNUNET_free(room);
+}
+
+const struct GNUNET_MESSENGER_Message*
+get_room_message (const struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_HashCode *hash)
+{
+  return GNUNET_CONTAINER_multihashmap_get (room->messages, hash);
+}
+
+static void
+handle_join_message (struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_MESSENGER_Message *message,
+                     const struct GNUNET_HashCode *hash)
+{
+  struct GNUNET_MESSENGER_Contact *contact = get_handle_contact_by_pubkey 
(room->handle, &(message->body.join.key));
+
+  if (contact)
+    GNUNET_CONTAINER_multishortmap_put (room->members, 
&(message->header.sender_id), contact,
+                                        
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
+}
+
+static void
+handle_leave_message (struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_MESSENGER_Message *message,
+                      const struct GNUNET_HashCode *hash)
+{
+  GNUNET_CONTAINER_multishortmap_remove_all (room->members, 
&(message->header.sender_id));
+}
+
+static void
+handle_name_message (struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_MESSENGER_Message *message,
+                     const struct GNUNET_HashCode *hash)
+{
+  struct GNUNET_MESSENGER_Contact *contact = 
GNUNET_CONTAINER_multishortmap_get (room->members,
+                                                                               
  &(message->header.sender_id));
+
+  if (contact)
+    set_contact_name (contact, message->body.name.name);
+}
+
+static void
+handle_key_message (struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_MESSENGER_Message *message,
+                    const struct GNUNET_HashCode *hash)
+{
+  struct GNUNET_MESSENGER_Contact *contact = 
GNUNET_CONTAINER_multishortmap_get (room->members,
+                                                                               
  &(message->header.sender_id));
+
+  if (contact)
+    swap_handle_contact_by_pubkey (room->handle, contact, 
&(message->body.key.key));
+}
+
+static void
+handle_id_message (struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_MESSENGER_Message *message,
+                   const struct GNUNET_HashCode *hash)
+{
+  struct GNUNET_MESSENGER_Contact *contact = 
GNUNET_CONTAINER_multishortmap_get (room->members,
+                                                                               
  &(message->header.sender_id));
+
+  if ((contact) && (GNUNET_OK
+      == GNUNET_CONTAINER_multishortmap_put (room->members, 
&(message->body.id.id), contact,
+                                             
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)))
+    GNUNET_CONTAINER_multishortmap_remove (room->members, 
&(message->header.sender_id), contact);
+}
+
+static void
+handle_miss_message (struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_MESSENGER_Message *message,
+                     const struct GNUNET_HashCode *hash)
+{
+  if ((room->contact_id) && (0 == GNUNET_memcmp(&(message->header.sender_id), 
room->contact_id)))
+  {
+    struct GNUNET_MESSENGER_ListTunnel *match = find_list_tunnels 
(&(room->entries), &(message->body.miss.peer), NULL);
+
+    if (match)
+      remove_from_list_tunnels (&(room->entries), match);
+  }
+}
+
+void
+handle_room_message (struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_MESSENGER_Message *message,
+                     const struct GNUNET_HashCode *hash)
+{
+  if (GNUNET_NO != GNUNET_CONTAINER_multihashmap_contains (room->messages, 
hash))
+    return;
+
+  switch (message->header.kind)
+  {
+  case GNUNET_MESSENGER_KIND_JOIN:
+    handle_join_message (room, message, hash);
+    break;
+  case GNUNET_MESSENGER_KIND_LEAVE:
+    handle_leave_message (room, message, hash);
+    break;
+  case GNUNET_MESSENGER_KIND_NAME:
+    handle_name_message (room, message, hash);
+    break;
+  case GNUNET_MESSENGER_KIND_KEY:
+    handle_key_message (room, message, hash);
+    break;
+  case GNUNET_MESSENGER_KIND_ID:
+    handle_id_message (room, message, hash);
+    break;
+  case GNUNET_MESSENGER_KIND_MISS:
+    handle_miss_message (room, message, hash);
+    break;
+  default:
+    break;
+  }
+
+  struct GNUNET_MESSENGER_Message *clone = copy_message (message);
+
+  if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (room->messages, hash, 
clone,
+                                                      
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
+    destroy_message (clone);
+}
diff --git a/src/messenger/messenger_api_room.h 
b/src/messenger/messenger_api_room.h
new file mode 100644
index 000000000..0038128d8
--- /dev/null
+++ b/src/messenger/messenger_api_room.h
@@ -0,0 +1,95 @@
+/*
+   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/messenger_api_room.h
+ * @brief messenger api: client implementation of GNUnet MESSENGER service
+ */
+
+#ifndef GNUNET_MESSENGER_API_ROOM_H
+#define GNUNET_MESSENGER_API_ROOM_H
+
+#include "platform.h"
+#include "gnunet_container_lib.h"
+#include "gnunet_crypto_lib.h"
+
+#include "gnunet_messenger_service.h"
+
+#include "messenger_api_list_tunnels.h"
+#include "messenger_api_contact.h"
+#include "messenger_api_message.h"
+
+struct GNUNET_MESSENGER_Room
+{
+  struct GNUNET_MESSENGER_Handle *handle;
+  struct GNUNET_HashCode key;
+
+  int opened;
+
+  struct GNUNET_ShortHashCode *contact_id;
+
+  struct GNUNET_CONTAINER_MultiShortmap *members;
+  struct GNUNET_MESSENGER_ListTunnels entries;
+
+  struct GNUNET_CONTAINER_MultiHashMap *messages;
+};
+
+/**
+ * Creates and allocates a new room for a <i>handle</i> with a given 
<i>key</i> for the client API.
+ *
+ * @param handle Handle
+ * @param key Key of room
+ * @return New room
+ */
+struct GNUNET_MESSENGER_Room*
+create_room (struct GNUNET_MESSENGER_Handle *handle, const struct 
GNUNET_HashCode *key);
+
+/**
+ * Destroys a room and frees its memory fully from the client API.
+ *
+ * @param room Room
+ */
+void
+destroy_room (struct GNUNET_MESSENGER_Room *room);
+
+/**
+ * Returns a message locally stored from a map for a given <i>hash</i> in a 
<i>room</i>. If no matching
+ * message is found, NULL gets returned.
+ *
+ * @param room Room
+ * @param hash Hash of message
+ * @return Message or NULL
+ */
+const struct GNUNET_MESSENGER_Message*
+get_room_message (const struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_HashCode *hash);
+
+/**
+ * Handles a <i>message</i> with a given <i>hash</i> in a <i>room</i> for the 
client API to update
+ * members and its information. The function also stores the message in map 
locally for access afterwards.
+ *
+ * @param room Room
+ * @param message Message
+ * @param hash Hash of message
+ */
+void
+handle_room_message (struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_MESSENGER_Message *message,
+                     const struct GNUNET_HashCode *hash);
+
+#endif //GNUNET_MESSENGER_API_ROOM_H
diff --git a/src/messenger/test_messenger.c b/src/messenger/test_messenger.c
new file mode 100644
index 000000000..b42dfe6d9
--- /dev/null
+++ b/src/messenger/test_messenger.c
@@ -0,0 +1,187 @@
+/*
+   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
+ */
+/**
+ * @file messenger/test_messenger.c
+ * @author Tobias Frisch
+ * @brief Test for the messenger service using cadet API.
+ */
+#include <stdio.h>
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_testing_lib.h"
+#include "gnunet_messenger_service.h"
+
+/**
+ * How long until we really give up on a particular testcase portion?
+ */
+#define TOTAL_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 
\
+                                                     60)
+
+/**
+ * How long until we give up on any particular operation (and retry)?
+ */
+#define BASE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 
5)
+
+#define TESTER_NAME "tester"
+
+static int status = 1;
+
+static struct GNUNET_SCHEDULER_Task *die_task = NULL;
+static struct GNUNET_SCHEDULER_Task *op_task = NULL;
+
+struct GNUNET_MESSENGER_Handle *messenger = NULL;
+
+static void
+end (void *cls)
+{
+  die_task = NULL;
+
+  if (op_task)
+  {
+    GNUNET_SCHEDULER_cancel (op_task);
+    op_task = NULL;
+  }
+
+  if (messenger)
+  {
+    GNUNET_MESSENGER_disconnect(messenger);
+    messenger = NULL;
+  }
+
+  status = 0;
+}
+
+
+static void
+end_badly (void *cls)
+{
+  fprintf (stderr, "Testcase failed (timeout).\n");
+
+  end (NULL);
+  status = 1;
+}
+
+static void
+end_operation (void *cls)
+{
+  op_task = NULL;
+
+  fprintf (stderr, "Testcase failed (operation: '%s').\n", cls? (const char*) 
cls : "unknown");
+
+  if (die_task)
+    GNUNET_SCHEDULER_cancel (die_task);
+
+  end (NULL);
+  status = 1;
+}
+
+static int identity_counter = 0;
+
+/**
+ * Function called when an identity is retrieved.
+ *
+ * @param cls Closure
+ * @param handle Handle of messenger service
+ */
+static void
+on_identity (void *cls, struct GNUNET_MESSENGER_Handle *handle)
+{
+  if (op_task)
+  {
+    GNUNET_SCHEDULER_cancel (op_task);
+    op_task = NULL;
+  }
+
+  const char* name = GNUNET_MESSENGER_get_name(handle);
+
+  if (0 != strcmp(name, TESTER_NAME))
+  {
+    op_task = GNUNET_SCHEDULER_add_now (&end_operation, "name");
+    return;
+  }
+
+  struct GNUNET_IDENTITY_Ego* ego = GNUNET_IDENTITY_ego_get_anonymous();
+  struct GNUNET_IDENTITY_PublicKey anonymous_key;
+
+  GNUNET_IDENTITY_ego_get_public_key(ego, &anonymous_key);
+
+  const struct GNUNET_IDENTITY_PublicKey* key = 
GNUNET_MESSENGER_get_key(handle);
+
+  if (((!identity_counter) && (0 != GNUNET_memcmp(key, (&anonymous_key)))) ||
+      ((identity_counter) && (0 == GNUNET_memcmp(key, (&anonymous_key)))))
+  {
+    op_task = GNUNET_SCHEDULER_add_now (&end_operation, "key");
+    return;
+  }
+
+  if (identity_counter) {
+    GNUNET_MESSENGER_disconnect(handle);
+
+    op_task = NULL;
+    messenger = NULL;
+
+    if (die_task)
+      GNUNET_SCHEDULER_cancel (die_task);
+
+    die_task = GNUNET_SCHEDULER_add_now (&end, NULL);
+    return;
+  }
+
+  GNUNET_MESSENGER_update(messenger);
+  identity_counter++;
+}
+
+/**
+ * Main function for testcase.
+ *
+ * @param cls Closure
+ * @param cfg Configuration
+ * @param peer Peer for testing
+ */
+static void
+run (void *cls,
+     const struct GNUNET_CONFIGURATION_Handle *cfg,
+     struct GNUNET_TESTING_Peer *peer)
+{
+  die_task = GNUNET_SCHEDULER_add_delayed (TOTAL_TIMEOUT, &end_badly, NULL);
+
+  identity_counter = 0;
+
+  op_task = GNUNET_SCHEDULER_add_delayed (BASE_TIMEOUT, &end_operation, 
"connect");
+  messenger = GNUNET_MESSENGER_connect(cfg, TESTER_NAME, &on_identity, NULL, 
NULL, NULL);
+}
+
+/**
+ * The main function.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main(int argc, char **argv)
+{
+  if (0 != GNUNET_TESTING_peer_run("test-messenger",
+                                   "test_messenger_api.conf",
+                                   &run, NULL))
+    return 1;
+
+  return status;
+}
diff --git a/src/messenger/test_messenger_anonymous.c 
b/src/messenger/test_messenger_anonymous.c
new file mode 100644
index 000000000..e2057acc4
--- /dev/null
+++ b/src/messenger/test_messenger_anonymous.c
@@ -0,0 +1,179 @@
+/*
+   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
+ */
+/**
+ * @file messenger/test_messenger_anonymous.c
+ * @author Tobias Frisch
+ * @brief Test for the messenger service using cadet API.
+ */
+#include <stdio.h>
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_testing_lib.h"
+#include "gnunet_messenger_service.h"
+
+/**
+ * How long until we really give up on a particular testcase portion?
+ */
+#define TOTAL_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 
\
+                                                     60)
+
+/**
+ * How long until we give up on any particular operation (and retry)?
+ */
+#define BASE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 
5)
+
+static int status = 1;
+
+static struct GNUNET_SCHEDULER_Task *die_task = NULL;
+static struct GNUNET_SCHEDULER_Task *op_task = NULL;
+
+struct GNUNET_MESSENGER_Handle *messenger = NULL;
+
+static void
+end (void *cls)
+{
+  die_task = NULL;
+
+  if (op_task)
+  {
+    GNUNET_SCHEDULER_cancel (op_task);
+    op_task = NULL;
+  }
+
+  if (messenger)
+  {
+    GNUNET_MESSENGER_disconnect(messenger);
+    messenger = NULL;
+  }
+
+  status = 0;
+}
+
+
+static void
+end_badly (void *cls)
+{
+  fprintf (stderr, "Testcase failed (timeout).\n");
+
+  end (NULL);
+  status = 1;
+}
+
+static void
+end_operation (void *cls)
+{
+  op_task = NULL;
+
+  fprintf (stderr, "Testcase failed (operation: '%s').\n", cls? (const char*) 
cls : "unknown");
+
+  if (die_task)
+    GNUNET_SCHEDULER_cancel (die_task);
+
+  end (NULL);
+  status = 1;
+}
+
+/**
+ * Function called when an identity is retrieved.
+ *
+ * @param cls Closure
+ * @param handle Handle of messenger service
+ */
+static void
+on_identity (void *cls, struct GNUNET_MESSENGER_Handle *handle)
+{
+  if (op_task)
+  {
+    GNUNET_SCHEDULER_cancel (op_task);
+    op_task = NULL;
+  }
+
+  const char* name = GNUNET_MESSENGER_get_name(handle);
+
+  if (NULL != name)
+  {
+    op_task = GNUNET_SCHEDULER_add_now (&end_operation, "name-anonymous");
+    return;
+  }
+
+  if (GNUNET_SYSERR != GNUNET_MESSENGER_update(handle))
+  {
+    op_task = GNUNET_SCHEDULER_add_now (&end_operation, "update-fail");
+    return;
+  }
+
+  struct GNUNET_IDENTITY_Ego* ego = GNUNET_IDENTITY_ego_get_anonymous();
+  struct GNUNET_IDENTITY_PublicKey anonymous_key;
+
+  GNUNET_IDENTITY_ego_get_public_key(ego, &anonymous_key);
+
+  const struct GNUNET_IDENTITY_PublicKey* key = 
GNUNET_MESSENGER_get_key(handle);
+
+  if (0 != GNUNET_memcmp(key, (&anonymous_key)))
+  {
+    op_task = GNUNET_SCHEDULER_add_now (&end_operation, "key-anonymous");
+    return;
+  }
+
+  GNUNET_MESSENGER_disconnect(handle);
+
+  messenger = NULL;
+
+  if (die_task)
+    GNUNET_SCHEDULER_cancel (die_task);
+
+  die_task = GNUNET_SCHEDULER_add_now (&end, NULL);
+}
+
+/**
+ * Main function for testcase.
+ *
+ * @param cls Closure
+ * @param cfg Configuration
+ * @param peer Peer for testing
+ */
+static void
+run (void *cls,
+     const struct GNUNET_CONFIGURATION_Handle *cfg,
+     struct GNUNET_TESTING_Peer *peer)
+{
+  die_task = GNUNET_SCHEDULER_add_delayed (TOTAL_TIMEOUT, &end_badly, NULL);
+
+  op_task = GNUNET_SCHEDULER_add_delayed (BASE_TIMEOUT, &end_operation, 
"connect");
+  messenger = GNUNET_MESSENGER_connect(cfg, NULL, &on_identity, NULL, NULL, 
NULL);
+}
+
+/**
+ * The main function.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main(int argc, char **argv)
+{
+  if (0 != GNUNET_TESTING_peer_run("test-messenger",
+                                   "test_messenger_api.conf",
+                                   &run, NULL))
+    return 1;
+
+  return status;
+}
diff --git a/src/messenger/test_messenger_comm0.c 
b/src/messenger/test_messenger_comm0.c
new file mode 100644
index 000000000..631b5b2c9
--- /dev/null
+++ b/src/messenger/test_messenger_comm0.c
@@ -0,0 +1,252 @@
+/*
+   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
+ */
+/**
+ * @file messenger/test_messenger_comm0.c
+ * @author Tobias Frisch
+ * @brief Test for the messenger service using cadet API.
+ */
+#include <stdio.h>
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_testbed_logger_service.h"
+#include "gnunet_testbed_service.h"
+#include "gnunet_testing_lib.h"
+#include "gnunet_messenger_service.h"
+
+/**
+ * How long until we really give up on a particular testcase portion?
+ */
+#define TOTAL_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 
\
+                                                     60)
+
+/**
+ * How long until we give up on any particular operation (and retry)?
+ */
+#define BASE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 
5)
+
+static int status = 1;
+
+static struct GNUNET_SCHEDULER_Task *die_task = NULL;
+static struct GNUNET_SCHEDULER_Task *op_task = NULL;
+
+static void
+end (void *cls)
+{
+  die_task = NULL;
+
+  if (op_task)
+  {
+    GNUNET_SCHEDULER_cancel (op_task);
+    op_task = NULL;
+  }
+
+  GNUNET_SCHEDULER_shutdown ();
+  status = 0;
+}
+
+
+static void
+end_badly (void *cls)
+{
+  fprintf (stderr, "Testcase failed (timeout).\n");
+
+  end (NULL);
+  status = 1;
+}
+
+static void
+end_operation (void *cls)
+{
+  op_task = NULL;
+
+  fprintf (stderr, "Testcase failed (operation: '%s').\n", cls? (const char*) 
cls : "unknown");
+
+  if (die_task)
+    GNUNET_SCHEDULER_cancel (die_task);
+
+  end (NULL);
+  status = 1;
+}
+
+static void
+end_error (void *cls)
+{
+  op_task = NULL;
+
+  fprintf (stderr, "Testcase failed (error: '%s').\n", cls? (const char*) cls 
: "unknown");
+  GNUNET_free(cls);
+
+  if (die_task)
+    GNUNET_SCHEDULER_cancel (die_task);
+
+  end (NULL);
+  status = 1;
+}
+
+/**
+ * Function called whenever a message is received or sent.
+ *
+ * @param cls Closure
+ * @param room Room
+ * @param message Message
+ * @param hash Hash of message
+ */
+static void
+on_message (void *cls, const struct GNUNET_MESSENGER_Room *room, const struct 
GNUNET_MESSENGER_Message *message,
+            const struct GNUNET_HashCode *hash)
+{
+  // TODO
+}
+
+/**
+ * Function called when an identity is retrieved.
+ *
+ * @param cls Closure
+ * @param handle Handle of messenger service
+ */
+static void
+on_identity (void *cls, struct GNUNET_MESSENGER_Handle *handle)
+{
+  // TODO
+}
+
+static void
+on_peer (void *cb_cls, struct GNUNET_TESTBED_Operation *op,
+         const struct GNUNET_TESTBED_PeerInformation *pinfo,
+         const char *emsg)
+{
+  if (emsg)
+  {
+    op_task = GNUNET_SCHEDULER_add_now (&end_error, GNUNET_strdup(emsg));
+    return;
+  }
+
+  if (pinfo->pit != GNUNET_TESTBED_PIT_CONFIGURATION)
+  {
+    op_task = GNUNET_SCHEDULER_add_now (&end_operation, "config");
+    return;
+  }
+
+  struct GNUNET_MESSENGER_Handle *handle;
+  struct GNUNET_MESSENGER_Room *room;
+
+  fprintf (stderr, "MSG: connect\n");
+
+  handle = GNUNET_MESSENGER_connect(pinfo->result.cfg, "tester", &on_identity, 
NULL, &on_message, NULL);
+
+  struct GNUNET_HashCode hash;
+  GNUNET_CRYPTO_hash("test", 4, &hash);
+
+  fprintf (stderr, "MSG: open\n");
+
+  room = GNUNET_MESSENGER_open_room(handle, &hash);
+
+  fprintf (stderr, "MSG: close\n");
+
+  GNUNET_MESSENGER_close_room(room);
+
+  fprintf (stderr, "MSG: disconnect\n");
+
+  GNUNET_MESSENGER_disconnect(handle);
+
+  GNUNET_TESTBED_operation_done(op);
+
+}
+
+/**
+ * Main function for a peer of the testcase.
+ *
+ * @param cls Closure
+ * @param event Information about the event
+ */
+static void
+run (void *cls, const struct GNUNET_TESTBED_EventInformation *event)
+{
+  if (GNUNET_TESTBED_ET_PEER_START != event->type)
+  {
+    op_task = GNUNET_SCHEDULER_add_now (&end_operation, "start");
+    return;
+  }
+
+  GNUNET_TESTBED_peer_get_information(event->details.peer_start.peer,
+                                      GNUNET_TESTBED_PIT_CONFIGURATION,
+                                      on_peer, event->details.peer_start.peer);
+
+  fprintf (stderr, "MSG: barrier\n");
+
+  GNUNET_TESTBED_barrier_wait("exit", NULL, NULL);
+
+  fprintf (stderr, "MSG: exit\n");
+}
+
+static void
+exit_status (void *cls, const char *name,
+             struct GNUNET_TESTBED_Barrier *barrier,
+             enum GNUNET_TESTBED_BarrierStatus status,
+             const char *emsg)
+{
+  if (emsg)
+  {
+    op_task = GNUNET_SCHEDULER_add_now (&end_error, GNUNET_strdup(emsg));
+    return;
+  }
+
+  if (GNUNET_TESTBED_BARRIERSTATUS_ERROR == status)
+  {
+    op_task = GNUNET_SCHEDULER_add_now (&end_operation, "exit");
+    return;
+  }
+  else if (GNUNET_TESTBED_BARRIERSTATUS_CROSSED == status)
+    GNUNET_SCHEDULER_add_now(&end, NULL);
+}
+
+static void
+init (void *cls, struct GNUNET_TESTBED_RunHandle *h, unsigned int num_peers,
+      struct GNUNET_TESTBED_Peer **peers, unsigned int links_succeeded,
+      unsigned int links_failed)
+{
+  die_task = GNUNET_SCHEDULER_add_delayed (TOTAL_TIMEOUT, &end_badly, NULL);
+
+  struct GNUNET_TESTBED_Controller *controller;
+
+  controller = GNUNET_TESTBED_run_get_controller_handle(h);
+
+  GNUNET_TESTBED_barrier_init(controller, "exit", num_peers, exit_status, 
NULL);
+}
+
+/**
+ * The main function.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ */
+int
+main(int argc, char **argv)
+{
+  if (GNUNET_OK != GNUNET_TESTBED_test_run("test-messenger-comm0",
+                                           "test_messenger_api.conf",
+                                           2, 0,
+                                           &run, NULL,
+                                           &init, NULL))
+    return 1;
+
+  return status;
+}

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]