libgnunetchat

library for GNUnet Messenger
Log | Files | Refs | README | LICENSE

commit ba528dd455716f73fbfb3191401b8978e9900453
parent afd5d7cb5fdb8d2cf20f50d3942f553be78ca0ca
Author: TheJackiMonster <thejackimonster@gmail.com>
Date:   Sat, 12 Mar 2022 14:38:12 +0100

Implemented lobby functionality and uri solving to add contacts

Signed-off-by: TheJackiMonster <thejackimonster@gmail.com>

Diffstat:
MMakefile | 3+++
Minclude/gnunet_chat_lib.h | 4+++-
Msrc/gnunet_chat_context.c | 21+--------------------
Msrc/gnunet_chat_context.h | 7+------
Msrc/gnunet_chat_handle.c | 155+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/gnunet_chat_handle.h | 36++++++++++++++++++++++++++++++++++++
Msrc/gnunet_chat_handle_intern.c | 75+--------------------------------------------------------------------------
Msrc/gnunet_chat_lib.c | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/gnunet_chat_lib_intern.c | 39+++++++++++++++++++++++++++++++++++++++
Asrc/gnunet_chat_lobby.c | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/gnunet_chat_lobby.h | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/gnunet_chat_lobby_intern.c | 157+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/gnunet_chat_uri.c | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/gnunet_chat_uri.h | 48++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/gnunet_chat_util.c | 37+++++++++++++++++++++++++++++++++++++
Msrc/gnunet_chat_util.h | 15+++++++++++++++
16 files changed, 857 insertions(+), 112 deletions(-)

diff --git a/Makefile b/Makefile @@ -14,7 +14,9 @@ SOURCES = gnunet_chat_lib.c\ gnunet_chat_group.c\ gnunet_chat_handle.c\ gnunet_chat_invitation.c\ + gnunet_chat_lobby.c\ gnunet_chat_message.c\ + gnunet_chat_uri.c\ gnunet_chat_util.c HEADERS = gnunet_chat_lib.h @@ -24,6 +26,7 @@ TESTS = test_gnunet_chat_handle.c LIBRARIES = gnunetarm\ gnunetfs\ gnunetidentity\ + gnunetgns\ gnunetmessenger\ gnunetnamestore\ gnunetregex\ diff --git a/include/gnunet_chat_lib.h b/include/gnunet_chat_lib.h @@ -39,6 +39,8 @@ #include <gnunet/gnunet_time_lib.h> #include <gnunet/gnunet_util_lib.h> +#define GNUNET_CHAT_URI_PREFIX "gnunet://chat/" + /** * Enum for the different kinds of messages. */ @@ -172,7 +174,7 @@ typedef int * Method called when a lobby is opened to share with others via a chat URI. * * @param[in,out] cls Closure from #GNUNET_CHAT_lobby_open - * @param[in] uri Chat URI of the lobby + * @param[in] uri Chat URI of the lobby or NULL on error */ typedef void (*GNUNET_CHAT_LobbyCallback) (void *cls, diff --git a/src/gnunet_chat_context.c b/src/gnunet_chat_context.c @@ -329,27 +329,8 @@ context_write_records (struct GNUNET_CHAT_Context *context) count++; } - const char *type_string = "chat"; - - switch (context->type) - { - case GNUNET_CHAT_CONTEXT_TYPE_CONTACT: - type_string = "contact"; - break; - case GNUNET_CHAT_CONTEXT_TYPE_GROUP: - type_string = "group"; - break; - default: - break; - } - char *label; - GNUNET_asprintf ( - &label, - "%s_%s", - type_string, - GNUNET_h2s(hash) - ); + util_get_context_label(context->type, hash, &label); printf("WRITE: %s -> %u (%s, %s)\n", label, count, nick, topic); diff --git a/src/gnunet_chat_context.h b/src/gnunet_chat_context.h @@ -33,12 +33,7 @@ #include <gnunet/gnunet_messenger_service.h> #include <gnunet/gnunet_util_lib.h> -enum GNUNET_CHAT_ContextType -{ - GNUNET_CHAT_CONTEXT_TYPE_CONTACT = 1, - GNUNET_CHAT_CONTEXT_TYPE_GROUP = 2, - GNUNET_CHAT_CONTEXT_TYPE_UNKNOWN = 0 -}; +#include "gnunet_chat_util.h" struct GNUNET_CHAT_Handle; diff --git a/src/gnunet_chat_handle.c b/src/gnunet_chat_handle.c @@ -60,6 +60,12 @@ handle_create_from_config (const struct GNUNET_CONFIGURATION_Handle* cfg, handle->creation_op = NULL; handle->monitor = NULL; + handle->lobbies_head = NULL; + handle->lobbies_tail = NULL; + + handle->lobbies_head = NULL; + handle->lobbies_tail = NULL; + handle->files = NULL; handle->contexts = NULL; handle->contacts = NULL; @@ -84,6 +90,7 @@ handle_create_from_config (const struct GNUNET_CONFIGURATION_Handle* cfg, ); handle->fs = NULL; + handle->gns = NULL; handle->messenger = NULL; handle->public_key = NULL; @@ -218,6 +225,8 @@ handle_connect (struct GNUNET_CHAT_Handle *handle, GNUNET_free(fs_client_name); + handle->gns = GNUNET_GNS_connect(handle->cfg); + handle->messenger = GNUNET_MESSENGER_connect( handle->cfg, name, on_handle_identity, handle, @@ -250,18 +259,76 @@ handle_disconnect (struct GNUNET_CHAT_Handle *handle) handle->contexts, it_destroy_handle_contexts, NULL ); + struct GNUNET_CHAT_InternalMessages *internal; + while (handle->internal_head) + { + internal = handle->internal_head; + + if ((internal->msg) && (internal->msg->context)) + message_destroy(internal->msg); + + GNUNET_CONTAINER_DLL_remove( + handle->internal_head, + handle->internal_tail, + internal + ); + + GNUNET_free(internal); + } + if (handle->messenger) GNUNET_MESSENGER_disconnect(handle->messenger); + if (handle->gns) + GNUNET_GNS_disconnect(handle->gns); + if (handle->fs) GNUNET_FS_stop(handle->fs); + handle->fs = NULL; + handle->gns = NULL; + handle->messenger = NULL; + GNUNET_CONTAINER_multihashmap_iterate( handle->files, it_destroy_handle_files, NULL ); - handle->fs = NULL; - handle->messenger = NULL; + struct GNUNET_CHAT_InternalLobbies *lobbies; + while (handle->lobbies_head) + { + lobbies = handle->lobbies_head; + + if (lobbies->lobby) + lobby_destroy(lobbies->lobby); + + GNUNET_CONTAINER_DLL_remove( + handle->lobbies_head, + handle->lobbies_tail, + lobbies + ); + + GNUNET_free(lobbies); + } + + struct GNUNET_CHAT_UriLookups *lookups; + while (handle->lookups_head) + { + lookups = handle->lookups_head; + + if (lookups->request) + GNUNET_GNS_lookup_cancel(lookups->request); + + if (lookups->uri) + uri_destroy(lookups->uri); + + GNUNET_CONTAINER_DLL_remove( + handle->lookups_head, + handle->lookups_tail, + lookups + ); + + GNUNET_free(lookups); + } GNUNET_CONTAINER_multihashmap_destroy(handle->groups); GNUNET_CONTAINER_multishortmap_destroy(handle->contacts); @@ -493,3 +560,87 @@ handle_get_group_from_messenger (const struct GNUNET_CHAT_Handle *handle, handle->groups, key ); } + +struct GNUNET_CHAT_Context* +handle_process_records (struct GNUNET_CHAT_Handle *handle, + const char *label, + unsigned int count, + const struct GNUNET_GNSRECORD_Data *data) +{ + GNUNET_assert((handle) && (data)); + + if (count <= 0) + return NULL; + + const struct GNUNET_MESSENGER_RoomEntryRecord *record = NULL; + + for (unsigned int i = 0; i < count; i++) + { + if (GNUNET_YES == GNUNET_GNSRECORD_is_expired(data + i)) + continue; + + if ((GNUNET_GNSRECORD_TYPE_MESSENGER_ROOM_ENTRY == data[i].record_type) && + (!(GNUNET_GNSRECORD_RF_SUPPLEMENTAL & data[i].flags))) + { + record = data[i].data; + break; + } + } + + if (!record) + return NULL; + + struct GNUNET_CHAT_Context *context = GNUNET_CONTAINER_multihashmap_get( + handle->contexts, + &(record->key) + ); + + if (context) + { + context_read_records(context, label, count, data); + + printf("PATCH: %s %u %d %s\n", label, count, (int) context->type, context->topic); + + return NULL; + } + + struct GNUNET_MESSENGER_Room *room = GNUNET_MESSENGER_enter_room( + handle->messenger, + &(record->door), + &(record->key) + ); + + if (!room) + return NULL; + + context = context_create_from_room(handle, room); + context_read_records(context, label, count, data); + + printf("READ: %s %u %d %s\n", label, count, (int) context->type, context->topic); + + handle_send_room_name(handle, room); + + if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put( + handle->contexts, &(record->key), context, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + { + context_destroy(context); + GNUNET_MESSENGER_close_room(room); + return NULL; + } + + if (GNUNET_CHAT_CONTEXT_TYPE_GROUP != context->type) + return context; + + struct GNUNET_CHAT_Group *group = group_create_from_context(handle, context); + + if (context->topic) + group_publish(group); + + if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put( + handle->groups, &(record->key), group, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + group_destroy(group); + + return context; +} diff --git a/src/gnunet_chat_handle.h b/src/gnunet_chat_handle.h @@ -31,6 +31,7 @@ #include <gnunet/gnunet_container_lib.h> #include <gnunet/gnunet_arm_service.h> #include <gnunet/gnunet_fs_service.h> +#include <gnunet/gnunet_gns_service.h> #include <gnunet/gnunet_identity_service.h> #include <gnunet/gnunet_messenger_service.h> #include <gnunet/gnunet_namestore_service.h> @@ -39,7 +40,11 @@ #include "gnunet_chat_lib.h" #include "gnunet_chat_account.h" +#include "gnunet_chat_lobby.h" #include "gnunet_chat_message.h" +#include "gnunet_chat_uri.h" + +struct GNUNET_CHAT_Handle; struct GNUNET_CHAT_InternalMessages { @@ -55,6 +60,24 @@ struct GNUNET_CHAT_InternalAccounts struct GNUNET_CHAT_InternalAccounts *prev; }; +struct GNUNET_CHAT_InternalLobbies +{ + struct GNUNET_CHAT_Lobby *lobby; + struct GNUNET_CHAT_InternalLobbies *next; + struct GNUNET_CHAT_InternalLobbies *prev; +}; + +struct GNUNET_CHAT_UriLookups +{ + struct GNUNET_CHAT_Handle *handle; + + struct GNUNET_GNS_LookupRequest *request; + struct GNUNET_CHAT_Uri *uri; + + struct GNUNET_CHAT_UriLookups *next; + struct GNUNET_CHAT_UriLookups *prev; +}; + struct GNUNET_CHAT_Handle { const struct GNUNET_CONFIGURATION_Handle* cfg; @@ -75,6 +98,12 @@ struct GNUNET_CHAT_Handle struct GNUNET_IDENTITY_Operation *creation_op; struct GNUNET_NAMESTORE_ZoneMonitor *monitor; + struct GNUNET_CHAT_InternalLobbies *lobbies_head; + struct GNUNET_CHAT_InternalLobbies *lobbies_tail; + + struct GNUNET_CHAT_UriLookups *lookups_head; + struct GNUNET_CHAT_UriLookups *lookups_tail; + struct GNUNET_CONTAINER_MultiHashMap *files; struct GNUNET_CONTAINER_MultiHashMap *contexts; struct GNUNET_CONTAINER_MultiShortmap *contacts; @@ -82,6 +111,7 @@ struct GNUNET_CHAT_Handle struct GNUNET_ARM_Handle *arm; struct GNUNET_FS_Handle *fs; + struct GNUNET_GNS_Handle *gns; struct GNUNET_IDENTITY_Handle *identity; struct GNUNET_MESSENGER_Handle *messenger; struct GNUNET_NAMESTORE_Handle *namestore; @@ -137,4 +167,10 @@ struct GNUNET_CHAT_Group* handle_get_group_from_messenger (const struct GNUNET_CHAT_Handle *handle, const struct GNUNET_MESSENGER_Room *room); +struct GNUNET_CHAT_Context* +handle_process_records (struct GNUNET_CHAT_Handle *handle, + const char *label, + unsigned int count, + const struct GNUNET_GNSRECORD_Data *data); + #endif /* GNUNET_CHAT_HANDLE_H_ */ diff --git a/src/gnunet_chat_handle_intern.c b/src/gnunet_chat_handle_intern.c @@ -374,79 +374,8 @@ on_monitor_namestore_record(void *cls, { struct GNUNET_CHAT_Handle *chat = cls; - if (count <= 0) - goto skip_records; + handle_process_records(chat, label, count, data); - const struct GNUNET_MESSENGER_RoomEntryRecord *record = NULL; - - for (unsigned int i = 0; i < count; i++) - { - if (GNUNET_YES == GNUNET_GNSRECORD_is_expired(data + i)) - continue; - - if ((GNUNET_GNSRECORD_TYPE_MESSENGER_ROOM_ENTRY == data[i].record_type) && - (!(GNUNET_GNSRECORD_RF_SUPPLEMENTAL & data[i].flags))) - { - record = data[i].data; - break; - } - } - - if (!record) - goto skip_records; - - struct GNUNET_CHAT_Context *context = GNUNET_CONTAINER_multihashmap_get( - chat->contexts, - &(record->key) - ); - - if (context) - { - context_read_records(context, label, count, data); - - printf("PATCH: %s %u %d %s\n", label, count, (int) context->type, context->topic); - - goto skip_records; - } - - struct GNUNET_MESSENGER_Room *room = GNUNET_MESSENGER_enter_room( - chat->messenger, - &(record->door), - &(record->key) - ); - - if (!room) - goto skip_records; - - context = context_create_from_room(chat, room); - context_read_records(context, label, count, data); - - printf("READ: %s %u %d %s\n", label, count, (int) context->type, context->topic); - - handle_send_room_name(chat, room); - - if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put( - chat->contexts, &(record->key), context, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) - { - context_destroy(context); - goto skip_records; - } - - if (GNUNET_CHAT_CONTEXT_TYPE_GROUP != context->type) - goto skip_records; - - struct GNUNET_CHAT_Group *group = group_create_from_context(chat, context); - - if (context->topic) - group_publish(group); - - if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put( - chat->groups, &(record->key), group, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) - group_destroy(group); - -skip_records: if (chat->monitor) GNUNET_NAMESTORE_zone_monitor_next(chat->monitor, 1); } @@ -471,8 +400,6 @@ on_handle_identity(void *cls, GNUNET_assert(handle->messenger); - printf("HURRAY!\n"); - handle_send_internal_message( handle, NULL, diff --git a/src/gnunet_chat_lib.c b/src/gnunet_chat_lib.c @@ -32,7 +32,9 @@ #include "gnunet_chat_group.h" #include "gnunet_chat_handle.h" #include "gnunet_chat_invitation.h" +#include "gnunet_chat_lobby.h" #include "gnunet_chat_message.h" + #include "gnunet_chat_util.h" #include "gnunet_chat_lib_intern.c" @@ -215,9 +217,44 @@ GNUNET_CHAT_uri_parse (const char *uri, if (!uri) return NULL; - // TODO + const size_t prefix_len = strlen(GNUNET_CHAT_URI_PREFIX); - return NULL; + if (0 != strncmp(GNUNET_CHAT_URI_PREFIX, uri, prefix_len)) + { + if (emsg) + *emsg = GNUNET_strdup (_ ("CHAT URI malformed (invalid prefix)")); + + return NULL; + } + + struct GNUNET_IDENTITY_PublicKey zone; + + const char *data = uri + prefix_len; + char *end = strchr(data, '.'); + + if (!end) + { + if (emsg) + *emsg = GNUNET_strdup (_ ("CHAT URI malformed (zone key missing)")); + + return NULL; + } + + char *zone_data = GNUNET_strndup(data, (size_t) (end - data)); + + if (GNUNET_OK != GNUNET_IDENTITY_public_key_from_string(zone_data, &zone)) + { + GNUNET_free(zone_data); + + if (emsg) + *emsg = GNUNET_strdup (_ ("CHAT URI malformed (zone key invalid)")); + + return NULL; + } + + GNUNET_free(zone_data); + + return uri_create(&zone, end + 1); } @@ -227,9 +264,18 @@ GNUNET_CHAT_uri_to_string (const struct GNUNET_CHAT_Uri *uri) if (!uri) return NULL; - // TODO + char *key_string = GNUNET_IDENTITY_public_key_to_string(&(uri->zone)); - return NULL; + char *string; + GNUNET_asprintf( + &string, + "gnunet://chat/%s.%s", + key_string, + uri->label + ); + + GNUNET_free(key_string); + return string; } @@ -239,7 +285,7 @@ GNUNET_CHAT_uri_destroy (struct GNUNET_CHAT_Uri *uri) if (!uri) return; - GNUNET_free(uri); + uri_destroy(uri); } @@ -249,16 +295,53 @@ GNUNET_CHAT_lobby_open (struct GNUNET_CHAT_Handle *handle, GNUNET_CHAT_LobbyCallback callback, void *cls) { - // TODO + if (!handle) + return NULL; - return NULL; + struct GNUNET_CHAT_InternalLobbies *lobbies = GNUNET_new( + struct GNUNET_CHAT_InternalLobbies + ); + + lobbies->lobby = lobby_create(handle); + + GNUNET_CONTAINER_DLL_insert( + handle->lobbies_head, + handle->lobbies_tail, + lobbies + ); + + lobby_open(lobbies->lobby, delay, callback, cls); + + return lobbies->lobby; } void GNUNET_CHAT_lobby_close (struct GNUNET_CHAT_Lobby *lobby) { - // TODO + if (!lobby) + return; + + struct GNUNET_CHAT_InternalLobbies *lobbies = lobby->handle->lobbies_head; + + while (lobbies) + { + if (lobbies->lobby == lobby) + { + GNUNET_CONTAINER_DLL_remove( + lobby->handle->lobbies_head, + lobby->handle->lobbies_tail, + lobbies + ); + + GNUNET_free(lobbies); + break; + } + + lobbies = lobbies->next; + } + + lobby_destroy(lobby); } @@ -266,7 +349,31 @@ void GNUNET_CHAT_lobby_join (struct GNUNET_CHAT_Handle *handle, const struct GNUNET_CHAT_Uri *uri) { - // TODO + if ((!handle) || (!uri) || (!(handle->gns))) + return; + + struct GNUNET_CHAT_UriLookups *lookups = GNUNET_new( + struct GNUNET_CHAT_UriLookups + ); + + lookups->handle = handle; + lookups->uri = uri_create(&(uri->zone), uri->label); + + lookups->request = GNUNET_GNS_lookup( + handle->gns, + lookups->uri->label, + &(uri->zone), + GNUNET_GNSRECORD_TYPE_MESSENGER_ROOM_ENTRY, + GNUNET_GNS_LO_DEFAULT, + cb_lobby_lookup, + lookups + ); + + GNUNET_CONTAINER_DLL_insert( + handle->lookups_head, + handle->lookups_tail, + lookups + ); } diff --git a/src/gnunet_chat_lib_intern.c b/src/gnunet_chat_lib_intern.c @@ -41,6 +41,45 @@ cb_account_creation (void *cls, handle_send_internal_message(handle, NULL, GNUNET_CHAT_FLAG_REFRESH, NULL); } +void +cb_lobby_lookup (void *cls, + uint32_t count, + const struct GNUNET_GNSRECORD_Data *data) +{ + GNUNET_assert(cls); + + struct GNUNET_CHAT_UriLookups *lookups = (struct GNUNET_CHAT_UriLookups*) cls; + + if ((!(lookups->handle)) || (!(lookups->uri))) + goto drop_lookup; + + struct GNUNET_CHAT_Context *context = handle_process_records( + lookups->handle, + lookups->uri->label, + count, + data + ); + + if (context) + context_write_records(context); + +drop_lookup: + if (lookups->request) + GNUNET_GNS_lookup_cancel(lookups->request); + + if (lookups->uri) + uri_destroy(lookups->uri); + + if (lookups->handle) + GNUNET_CONTAINER_DLL_remove( + lookups->handle->lookups_head, + lookups->handle->lookups_tail, + lookups + ); + + GNUNET_free(lookups); +} + struct GNUNET_CHAT_HandleIterateContacts { struct GNUNET_CHAT_Handle *handle; diff --git a/src/gnunet_chat_lobby.c b/src/gnunet_chat_lobby.c @@ -0,0 +1,130 @@ +/* + This file is part of GNUnet. + Copyright (C) 2022 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 gnunet_chat_lobby.c + */ + +#include "gnunet_chat_lobby.h" + +#include "gnunet_chat_handle.h" +#include "gnunet_chat_lobby_intern.c" + +struct GNUNET_CHAT_Lobby* +lobby_create (struct GNUNET_CHAT_Handle *handle) +{ + GNUNET_assert(handle); + + struct GNUNET_CHAT_Lobby *lobby = GNUNET_new(struct GNUNET_CHAT_Lobby); + + lobby->handle = handle; + lobby->context = NULL; + lobby->uri = NULL; + + lobby->op_create = NULL; + lobby->op_delete = NULL; + + lobby->expiration = GNUNET_TIME_absolute_get_forever_(); + lobby->callback = NULL; + lobby->cls = NULL; + + return lobby; +} + +void +lobby_destroy (struct GNUNET_CHAT_Lobby *lobby) +{ + GNUNET_assert(lobby); + + if (lobby->op_create) + GNUNET_IDENTITY_cancel(lobby->op_create); + + if (lobby->op_delete) + GNUNET_IDENTITY_cancel(lobby->op_delete); + + if (lobby->uri) + uri_destroy(lobby->uri); + + if (lobby->context) + context_destroy(lobby->context); + + GNUNET_free(lobby); +} + +void +lobby_open (struct GNUNET_CHAT_Lobby *lobby, + struct GNUNET_TIME_Relative delay, + GNUNET_CHAT_LobbyCallback callback, + void *cls) +{ + GNUNET_assert(lobby); + + char *name; + + lobby->expiration = GNUNET_TIME_relative_to_absolute(delay); + lobby->callback = callback; + lobby->cls = cls; + + if (lobby->op_create) + { + GNUNET_IDENTITY_cancel(lobby->op_create); + goto open_zone; + } + + struct GNUNET_HashCode key; + GNUNET_CRYPTO_random_block(GNUNET_CRYPTO_QUALITY_WEAK, &key, sizeof(key)); + + struct GNUNET_MESSENGER_Room *room = GNUNET_MESSENGER_open_room( + lobby->handle->messenger, + &key + ); + + if (!room) + return; + + lobby->context = context_create_from_room(lobby->handle, room); + + handle_send_room_name(lobby->handle, room); + + if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put( + lobby->handle->contexts, &key, lobby->context, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + { + context_destroy(lobby->context); + lobby->context = NULL; + + GNUNET_MESSENGER_close_room(room); + return; + } + +open_zone: + util_lobby_name(&key, &name); + + lobby->op_create = GNUNET_IDENTITY_create( + lobby->handle->identity, + name, + NULL, + GNUNET_IDENTITY_TYPE_EDDSA, + cont_lobby_identity_create, + lobby + ); + + GNUNET_free(name); +} diff --git a/src/gnunet_chat_lobby.h b/src/gnunet_chat_lobby.h @@ -0,0 +1,66 @@ +/* + This file is part of GNUnet. + Copyright (C) 2022 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 gnunet_chat_lobby.h + */ + +#ifndef GNUNET_CHAT_LOBBY_H_ +#define GNUNET_CHAT_LOBBY_H_ + +#include <gnunet/platform.h> +#include <gnunet/gnunet_common.h> +#include <gnunet/gnunet_identity_service.h> +#include <gnunet/gnunet_util_lib.h> + +#include "gnunet_chat_lib.h" + +struct GNUNET_CHAT_Handle; +struct GNUNET_CHAT_Context; +struct GNUNET_CHAT_Uri; + +struct GNUNET_CHAT_Lobby +{ + struct GNUNET_CHAT_Handle *handle; + + struct GNUNET_CHAT_Context *context; + struct GNUNET_CHAT_Uri *uri; + + struct GNUNET_IDENTITY_Operation *op_create; + struct GNUNET_IDENTITY_Operation *op_delete; + + struct GNUNET_TIME_Absolute expiration; + GNUNET_CHAT_LobbyCallback callback; + void *cls; +}; + +struct GNUNET_CHAT_Lobby* +lobby_create (struct GNUNET_CHAT_Handle *handle); + +void +lobby_destroy (struct GNUNET_CHAT_Lobby *lobby); + +void +lobby_open (struct GNUNET_CHAT_Lobby *lobby, + struct GNUNET_TIME_Relative delay, + GNUNET_CHAT_LobbyCallback callback, + void *cls); + +#endif /* GNUNET_CHAT_LOBBY_H_ */ diff --git a/src/gnunet_chat_lobby_intern.c b/src/gnunet_chat_lobby_intern.c @@ -0,0 +1,157 @@ +/* + This file is part of GNUnet. + Copyright (C) 2022 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 gnunet_chat_lobby_intern.c + */ + +#include "gnunet_chat_context.h" + +void +cont_lobby_write_records (void *cls, + GNUNET_UNUSED int32_t success, + const char *emsg) +{ + struct GNUNET_CHAT_Lobby *lobby = cls; + + if (!emsg) + goto call_cb; + + handle_send_internal_message( + lobby->handle, + lobby->context, + GNUNET_CHAT_FLAG_WARNING, + emsg + ); + + if (lobby->uri) + uri_destroy(lobby->uri); + + lobby->uri = NULL; + +call_cb: + if (lobby->callback) + lobby->callback(lobby->cls, lobby->uri); +} + +void +cont_lobby_identity_delete (void *cls, + const char *emsg) +{ + struct GNUNET_CHAT_Lobby *lobby = cls; + + if (!emsg) + return; + + handle_send_internal_message( + lobby->handle, + lobby->context, + GNUNET_CHAT_FLAG_WARNING, + emsg + ); +} + +void +cont_lobby_identity_create (void *cls, + const struct GNUNET_IDENTITY_PrivateKey *zone, + const char *emsg) +{ + struct GNUNET_CHAT_Lobby *lobby = cls; + + char *name; + + if (emsg) + { + handle_send_internal_message( + lobby->handle, + NULL, + GNUNET_CHAT_FLAG_WARNING, + emsg + ); + + return; + } + + struct GNUNET_MESSENGER_Room *room = GNUNET_MESSENGER_open_room( + lobby->handle->messenger, + NULL + ); + + if (!room) + goto destroy_identity; + + const struct GNUNET_HashCode *key = GNUNET_MESSENGER_room_get_key(room); + + lobby->context = context_create_from_room(lobby->handle, room); + + handle_send_room_name(lobby->handle, room); + + if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put( + lobby->handle->contexts, key, lobby->context, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) + { + context_destroy(lobby->context); + lobby->context = NULL; + goto destroy_identity; + } + + struct GNUNET_GNSRECORD_Data data [3]; + data[0].record_type = GNUNET_GNSRECORD_TYPE_MESSENGER_ROOM_ENTRY; + data[0].data = &room; + data[0].data_size = sizeof(room); + data[0].expiration_time = lobby->expiration.abs_value_us; + data[0].flags = GNUNET_GNSRECORD_RF_NONE; + + if (lobby->uri) + uri_destroy(lobby->uri); + + struct GNUNET_IDENTITY_PublicKey public_zone; + GNUNET_IDENTITY_key_get_public(zone, &public_zone); + + char *label; + util_get_context_label(lobby->context->type, key, &label); + + lobby->uri = uri_create(&public_zone, label); + GNUNET_free(label); + + GNUNET_NAMESTORE_records_store( + lobby->handle->namestore, + zone, + lobby->uri->label, + 1, + data, + cont_lobby_write_records, + lobby + ); + + context_write_records(lobby->context); + +destroy_identity: + util_lobby_name(key, &name); + + lobby->op_delete = GNUNET_IDENTITY_delete( + lobby->handle->identity, + name, + cont_lobby_identity_delete, + lobby + ); + + GNUNET_free(name); +} diff --git a/src/gnunet_chat_uri.c b/src/gnunet_chat_uri.c @@ -0,0 +1,51 @@ +/* + This file is part of GNUnet. + Copyright (C) 2022 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 gnunet_chat_uri.c + */ + +#include "gnunet_chat_uri.h" + +struct GNUNET_CHAT_Uri* +uri_create (const struct GNUNET_IDENTITY_PublicKey *zone, + const char *label) +{ + GNUNET_assert((zone) && (label)); + + struct GNUNET_CHAT_Uri *uri = GNUNET_new(struct GNUNET_CHAT_Uri); + + GNUNET_memcpy(&(uri->zone), zone, sizeof(uri->zone)); + + uri->label = GNUNET_strdup(label); + + return uri; +} + +void +uri_destroy (struct GNUNET_CHAT_Uri *uri) +{ + GNUNET_assert(uri); + + if (uri->label) + GNUNET_free(uri->label); + + GNUNET_free(uri); +} diff --git a/src/gnunet_chat_uri.h b/src/gnunet_chat_uri.h @@ -0,0 +1,48 @@ +/* + This file is part of GNUnet. + Copyright (C) 2022 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 gnunet_chat_uri.h + */ + +#ifndef GNUNET_CHAT_URI_H_ +#define GNUNET_CHAT_URI_H_ + +#include <gnunet/platform.h> +#include <gnunet/gnunet_common.h> +#include <gnunet/gnunet_identity_service.h> +#include <gnunet/gnunet_util_lib.h> + +#include "gnunet_chat_util.h" + +struct GNUNET_CHAT_Uri +{ + struct GNUNET_IDENTITY_PublicKey zone; + char *label; +}; + +struct GNUNET_CHAT_Uri* +uri_create (const struct GNUNET_IDENTITY_PublicKey *zone, + const char *label); + +void +uri_destroy (struct GNUNET_CHAT_Uri *uri); + +#endif /* GNUNET_CHAT_URI_H_ */ diff --git a/src/gnunet_chat_util.c b/src/gnunet_chat_util.c @@ -286,3 +286,40 @@ util_get_filename (const char *directory, GNUNET_free(dirname); return result; } + +int +util_get_context_label (enum GNUNET_CHAT_ContextType type, + const struct GNUNET_HashCode *hash, + char **label) +{ + const char *type_string = "chat"; + + switch (type) + { + case GNUNET_CHAT_CONTEXT_TYPE_CONTACT: + type_string = "contact"; + break; + case GNUNET_CHAT_CONTEXT_TYPE_GROUP: + type_string = "group"; + break; + default: + break; + } + + return GNUNET_asprintf ( + label, + "%s_%s", + type_string, + GNUNET_h2s(hash) + ); +} + +int util_lobby_name (const struct GNUNET_HashCode *hash, + char **name) +{ + return GNUNET_asprintf ( + name, + "chat_lobby_%s", + GNUNET_h2s(hash) + ); +} diff --git a/src/gnunet_chat_util.h b/src/gnunet_chat_util.h @@ -32,6 +32,13 @@ #include <gnunet/gnunet_messenger_service.h> #include <gnunet/gnunet_util_lib.h> +enum GNUNET_CHAT_ContextType +{ + GNUNET_CHAT_CONTEXT_TYPE_CONTACT = 1, + GNUNET_CHAT_CONTEXT_TYPE_GROUP = 2, + GNUNET_CHAT_CONTEXT_TYPE_UNKNOWN = 0 +}; + void util_shorthash_from_member (const struct GNUNET_MESSENGER_Contact *member, struct GNUNET_ShortHashCode *shorthash); @@ -63,4 +70,12 @@ util_get_filename (const char *directory, const struct GNUNET_HashCode *hash, char **filename); +int +util_get_context_label (enum GNUNET_CHAT_ContextType type, + const struct GNUNET_HashCode *hash, + char **label); + +int util_lobby_name (const struct GNUNET_HashCode *hash, + char **name); + #endif /* GNUNET_CHAT_UTIL_H_ */