/* This file is part of GNUnet. Copyright (C) 2021--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 . SPDX-License-Identifier: AGPL3.0-or-later */ /* * @author Tobias Frisch * @file gnunet_chat_context.c */ #include "gnunet_chat_context.h" #include "gnunet_chat_group.h" #include "gnunet_chat_handle.h" #include "gnunet_chat_util.h" #include "gnunet_chat_context_intern.c" struct GNUNET_CHAT_Context* context_create_from_room (struct GNUNET_CHAT_Handle *handle, struct GNUNET_MESSENGER_Room *room) { GNUNET_assert((handle) && (room)); struct GNUNET_CHAT_Context* context = GNUNET_new(struct GNUNET_CHAT_Context); context->handle = handle; context->type = GNUNET_CHAT_CONTEXT_TYPE_UNKNOWN; context->nick[0] = '\0'; context->topic = NULL; context->deleted = GNUNET_NO; context->timestamps = GNUNET_CONTAINER_multishortmap_create(8, GNUNET_NO); context->messages = GNUNET_CONTAINER_multihashmap_create(8, GNUNET_NO); context->invites = GNUNET_CONTAINER_multihashmap_create(8, GNUNET_NO); context->files = GNUNET_CONTAINER_multihashmap_create(8, GNUNET_NO); context->room = room; context->contact = NULL; context->user_pointer = NULL; context->member_pointers = GNUNET_CONTAINER_multishortmap_create( 8, GNUNET_NO ); context->query = NULL; return context; } struct GNUNET_CHAT_Context* context_create_from_contact (struct GNUNET_CHAT_Handle *handle, const struct GNUNET_MESSENGER_Contact *contact) { GNUNET_assert((handle) && (contact)); struct GNUNET_CHAT_Context* context = GNUNET_new(struct GNUNET_CHAT_Context); context->handle = handle; context->type = GNUNET_CHAT_CONTEXT_TYPE_CONTACT; context->nick[0] = '\0'; context->topic = NULL; context->deleted = GNUNET_NO; context->timestamps = GNUNET_CONTAINER_multishortmap_create(4, GNUNET_NO); context->messages = GNUNET_CONTAINER_multihashmap_create(4, GNUNET_NO); context->invites = GNUNET_CONTAINER_multihashmap_create(4, GNUNET_NO); context->files = GNUNET_CONTAINER_multihashmap_create(4, GNUNET_NO); context->room = NULL; context->contact = contact; context->user_pointer = NULL; context->member_pointers = GNUNET_CONTAINER_multishortmap_create( 8, GNUNET_NO ); context->query = NULL; return context; } void context_destroy (struct GNUNET_CHAT_Context *context) { GNUNET_assert((context) && (context->timestamps) && (context->messages) && (context->invites) && (context->files)); if (context->query) GNUNET_NAMESTORE_cancel(context->query); GNUNET_CONTAINER_multishortmap_iterate( context->timestamps, it_destroy_context_timestamps, NULL ); GNUNET_CONTAINER_multihashmap_iterate( context->messages, it_destroy_context_messages, NULL ); GNUNET_CONTAINER_multihashmap_iterate( context->invites, it_destroy_context_invites, NULL ); GNUNET_CONTAINER_multishortmap_destroy(context->member_pointers); GNUNET_CONTAINER_multishortmap_destroy(context->timestamps); GNUNET_CONTAINER_multihashmap_destroy(context->messages); GNUNET_CONTAINER_multihashmap_destroy(context->invites); GNUNET_CONTAINER_multihashmap_destroy(context->files); if (context->topic) GNUNET_free(context->topic); GNUNET_free(context); } void context_update_room (struct GNUNET_CHAT_Context *context, struct GNUNET_MESSENGER_Room *room) { GNUNET_assert(context); if (room == context->room) return; GNUNET_CONTAINER_multishortmap_iterate( context->timestamps, it_destroy_context_timestamps, NULL ); GNUNET_CONTAINER_multihashmap_iterate( context->messages, it_destroy_context_messages, NULL ); GNUNET_CONTAINER_multihashmap_iterate( context->invites, it_destroy_context_invites, NULL ); GNUNET_CONTAINER_multishortmap_destroy(context->timestamps); context->timestamps = GNUNET_CONTAINER_multishortmap_create(8, GNUNET_NO); GNUNET_CONTAINER_multihashmap_clear(context->messages); GNUNET_CONTAINER_multihashmap_clear(context->invites); GNUNET_CONTAINER_multihashmap_clear(context->files); context->room = room; if (!(context->room)) return; context_write_records(context); } void context_update_nick (struct GNUNET_CHAT_Context *context, const char *nick) { GNUNET_assert(context); size_t len = nick? strlen(nick) : 0; if (len >= sizeof(context->nick)) len = sizeof(context->nick) - 1; GNUNET_memcpy(context->nick, nick, len); context->nick[len] = '\0'; if (!(context->handle)) return; handle_send_internal_message( context->handle, context, GNUNET_CHAT_FLAG_UPDATE, NULL ); } void context_read_records (struct GNUNET_CHAT_Context *context, const char *label, unsigned int count, const struct GNUNET_GNSRECORD_Data *data) { GNUNET_assert((context) && (context->room)); char *nick = NULL; char *topic = NULL; uint32_t flags = 0; for (unsigned int i = 0; i < count; i++) { if (!(GNUNET_GNSRECORD_RF_SUPPLEMENTAL & data[i].flags)) continue; if (GNUNET_GNSRECORD_TYPE_MESSENGER_ROOM_DETAILS == data[i].record_type) { if (nick) continue; const struct GNUNET_MESSENGER_RoomDetailsRecord *record = data[i].data; nick = GNUNET_strndup(record->name, sizeof(record->name)); flags = record->flags; } if (GNUNET_DNSPARSER_TYPE_TXT == data[i].record_type) { if (topic) continue; topic = GNUNET_strndup(data[i].data, data[i].data_size); } } context_update_nick(context, nick); if (nick) GNUNET_free(nick); if (topic) { struct GNUNET_HashCode topic_hash; GNUNET_CRYPTO_hash(topic, strlen(topic), &topic_hash); const struct GNUNET_HashCode *hash = GNUNET_MESSENGER_room_get_key( context->room ); if (0 != GNUNET_CRYPTO_hash_cmp(&topic_hash, hash)) { GNUNET_free(topic); topic = NULL; } } util_set_name_field(topic, &(context->topic)); if (topic) GNUNET_free(topic); if (0 == strncmp(label, "group_", 6)) context->type = GNUNET_CHAT_CONTEXT_TYPE_GROUP; else if (0 == strncmp(label, "contact_", 8)) context->type = GNUNET_CHAT_CONTEXT_TYPE_CONTACT; else context->type = GNUNET_CHAT_CONTEXT_TYPE_UNKNOWN; } void context_write_records (struct GNUNET_CHAT_Context *context) { GNUNET_assert((context) && (context->handle) && (context->room)); const struct GNUNET_IDENTITY_PrivateKey *zone = handle_get_key( context->handle ); if (!zone) return; const struct GNUNET_HashCode *hash = GNUNET_MESSENGER_room_get_key( context->room ); struct GNUNET_TIME_Absolute expiration = GNUNET_TIME_absolute_get_forever_(); struct GNUNET_MESSENGER_RoomEntryRecord room_entry; GNUNET_CRYPTO_get_peer_identity(context->handle->cfg, &(room_entry.door)); GNUNET_memcpy( &(room_entry.key), hash, sizeof(room_entry.key) ); struct GNUNET_MESSENGER_RoomDetailsRecord room_details; const char *topic = context->topic; if (topic) { struct GNUNET_HashCode topic_hash; GNUNET_CRYPTO_hash(topic, strlen(topic), &topic_hash); if (0 != GNUNET_CRYPTO_hash_cmp(&topic_hash, hash)) topic = NULL; } char *label; util_get_context_label(context->type, hash, &label); unsigned int count = 0; struct GNUNET_GNSRECORD_Data data [3]; if (GNUNET_YES == context->deleted) goto skip_record_data; data[count].record_type = GNUNET_GNSRECORD_TYPE_MESSENGER_ROOM_ENTRY; data[count].data = &room_entry; data[count].data_size = sizeof(room_entry); data[count].expiration_time = expiration.abs_value_us; data[count].flags = GNUNET_GNSRECORD_RF_PRIVATE; count++; if (context->nick) { GNUNET_memcpy(room_details.name, context->nick, sizeof(room_details.name)); room_details.flags = 0; data[count].record_type = GNUNET_GNSRECORD_TYPE_MESSENGER_ROOM_DETAILS; data[count].data = &room_details; data[count].data_size = sizeof(room_details); data[count].expiration_time = expiration.abs_value_us; data[count].flags = ( GNUNET_GNSRECORD_RF_PRIVATE | GNUNET_GNSRECORD_RF_SUPPLEMENTAL ); count++; } if (topic) { data[count].record_type = GNUNET_DNSPARSER_TYPE_TXT; data[count].data = topic; data[count].data_size = strlen(topic); data[count].expiration_time = expiration.abs_value_us; data[count].flags = ( GNUNET_GNSRECORD_RF_PRIVATE | GNUNET_GNSRECORD_RF_SUPPLEMENTAL ); count++; } skip_record_data: if (context->query) GNUNET_NAMESTORE_cancel(context->query); context->query = GNUNET_NAMESTORE_records_store( context->handle->namestore, zone, label, count, data, cont_context_write_records, context ); GNUNET_free(label); }