libgnunetchat

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

gnunet_chat_handle.c (26674B)


      1 /*
      2    This file is part of GNUnet.
      3    Copyright (C) 2021--2025 GNUnet e.V.
      4 
      5    GNUnet is free software: you can redistribute it and/or modify it
      6    under the terms of the GNU Affero General Public License as published
      7    by the Free Software Foundation, either version 3 of the License,
      8    or (at your option) any later version.
      9 
     10    GNUnet is distributed in the hope that it will be useful, but
     11    WITHOUT ANY WARRANTY; without even the implied warranty of
     12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13    Affero General Public License for more details.
     14 
     15    You should have received a copy of the GNU Affero General Public License
     16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
     17 
     18    SPDX-License-Identifier: AGPL3.0-or-later
     19  */
     20 /*
     21  * @author Tobias Frisch
     22  * @file gnunet_chat_handle.c
     23  */
     24 
     25 #include "gnunet_chat_handle.h"
     26 
     27 #include "gnunet_chat_account.h"
     28 #include "gnunet_chat_handle_intern.c"
     29 #include "gnunet_chat_message.h"
     30 #include <gnunet/gnunet_arm_service.h>
     31 #include <gnunet/gnunet_common.h>
     32 #include <gnunet/gnunet_messenger_service.h>
     33 #include <gnunet/gnunet_reclaim_service.h>
     34 #include <gnunet/gnunet_scheduler_lib.h>
     35 #include <gnunet/gnunet_util_lib.h>
     36 
     37 static const unsigned int initial_map_size_of_handle = 8;
     38 static const unsigned int minimum_amount_of_other_members_in_group = 2;
     39 
     40 struct GNUNET_CHAT_Handle*
     41 handle_create_from_config (const struct GNUNET_CONFIGURATION_Handle* cfg,
     42                            GNUNET_CHAT_ContextMessageCallback msg_cb,
     43                            void *msg_cls)
     44 {
     45   GNUNET_assert(cfg);
     46 
     47   struct GNUNET_CHAT_Handle* handle = GNUNET_new(struct GNUNET_CHAT_Handle);
     48 
     49   handle->cfg = cfg;
     50   handle->shutdown_hook = GNUNET_SCHEDULER_add_shutdown(
     51     on_handle_shutdown, handle
     52   );
     53 
     54   handle->destruction = NULL;
     55 
     56   handle->services_head = NULL;
     57   handle->services_tail = NULL;
     58 
     59   handle->internal_head = NULL;
     60   handle->internal_tail = NULL;
     61 
     62   handle->directory = NULL;
     63 
     64   char *dir_path = NULL;
     65   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename(cfg,
     66 		     GNUNET_MESSENGER_SERVICE_NAME,
     67 		     "MESSENGER_DIR",
     68 		     &dir_path))
     69   {
     70     if (dir_path)
     71       GNUNET_free(dir_path);
     72   }
     73   else if ((GNUNET_YES != GNUNET_DISK_directory_test(dir_path, GNUNET_YES)) &&
     74 	         (GNUNET_OK != GNUNET_DISK_directory_create(dir_path)))
     75   {
     76     GNUNET_free(dir_path);
     77   }
     78   else
     79   {
     80     char *chat_directory = NULL;
     81     util_get_dirname(dir_path, "chat", &chat_directory);
     82     GNUNET_free(dir_path);
     83 
     84     if ((GNUNET_YES != GNUNET_DISK_directory_test(chat_directory, GNUNET_YES)) &&
     85     	  (GNUNET_OK != GNUNET_DISK_directory_create(chat_directory)))
     86       GNUNET_free(chat_directory);
     87     else
     88       handle->directory = chat_directory;
     89   }
     90 
     91   handle->msg_cb = msg_cb;
     92   handle->msg_cls = msg_cls;
     93 
     94   handle->accounts_head = NULL;
     95   handle->accounts_tail = NULL;
     96 
     97   handle->refreshing = GNUNET_NO;
     98   handle->own_contact = NULL;
     99 
    100   handle->next = NULL;
    101   handle->current = NULL;
    102   handle->monitor = NULL;
    103 
    104   handle->lobbies_head = NULL;
    105   handle->lobbies_tail = NULL;
    106 
    107   handle->lookups_head = NULL;
    108   handle->lookups_tail = NULL;
    109 
    110   handle->attributes_head = NULL;
    111   handle->attributes_tail = NULL;
    112 
    113   handle->tickets_head = NULL;
    114   handle->tickets_tail = NULL;
    115 
    116   handle->files = GNUNET_CONTAINER_multihashmap_create(
    117     initial_map_size_of_handle, GNUNET_NO);
    118   
    119   handle->contexts = NULL;
    120   handle->contacts = NULL;
    121   handle->groups = NULL;
    122   handle->invitations = NULL;
    123 
    124   handle->arm = GNUNET_ARM_connect(
    125     handle->cfg,
    126     on_handle_arm_connection, 
    127     handle
    128   );
    129 
    130   if (handle->arm)
    131     on_handle_arm_connection(handle, GNUNET_NO);
    132 
    133   handle->identity = GNUNET_IDENTITY_connect(
    134     handle->cfg,
    135     on_handle_gnunet_identity,
    136     handle
    137   );
    138 
    139   handle->fs = GNUNET_FS_start(
    140     handle->cfg, "libgnunetchat",
    141     notify_handle_fs_progress, handle,
    142     GNUNET_FS_FLAGS_NONE,
    143     GNUNET_FS_OPTIONS_END
    144   );
    145 
    146   handle->gns = NULL;
    147   handle->messenger = NULL;
    148 
    149   handle->namestore = GNUNET_NAMESTORE_connect(
    150     handle->cfg
    151   );
    152 
    153   handle->reclaim = GNUNET_RECLAIM_connect(
    154     handle->cfg
    155   );
    156 
    157   handle->public_key = NULL;
    158   handle->user_pointer = NULL;
    159   return handle;
    160 }
    161 
    162 void
    163 handle_update_key (struct GNUNET_CHAT_Handle *handle)
    164 {
    165   GNUNET_assert(handle);
    166 
    167   if (handle->public_key)
    168     GNUNET_free(handle->public_key);
    169 
    170   handle->public_key = NULL;
    171   handle->own_contact = NULL;
    172 
    173   if (!(handle->messenger))
    174     return;
    175 
    176   const struct GNUNET_CRYPTO_BlindablePublicKey *pubkey;
    177   pubkey = GNUNET_MESSENGER_get_key(handle->messenger);
    178 
    179   if (pubkey)
    180     handle->public_key = GNUNET_CRYPTO_blindable_public_key_to_string(pubkey);
    181 }
    182 
    183 void
    184 handle_destroy (struct GNUNET_CHAT_Handle *handle)
    185 {
    186   GNUNET_assert(handle);
    187 
    188   if (handle->shutdown_hook)
    189     GNUNET_SCHEDULER_cancel(handle->shutdown_hook);
    190   if (handle->destruction)
    191     GNUNET_SCHEDULER_cancel(handle->destruction);
    192   if (handle->connection)
    193     GNUNET_SCHEDULER_cancel(handle->connection);
    194   if (handle->refresh)
    195     GNUNET_SCHEDULER_cancel(handle->refresh);
    196 
    197   if (handle->monitor)
    198     GNUNET_NAMESTORE_zone_monitor_stop(handle->monitor);
    199 
    200   handle->connection = NULL;
    201 
    202   if (handle->current)
    203     handle_disconnect(handle);
    204 
    205   GNUNET_CONTAINER_multihashmap_iterate(
    206     handle->files, it_destroy_handle_files, NULL
    207   );
    208 
    209   while (handle->attributes_head)
    210     internal_attributes_destroy(handle->attributes_head);
    211 
    212   if (handle->reclaim)
    213     GNUNET_RECLAIM_disconnect(handle->reclaim);
    214 
    215   if (handle->namestore)
    216     GNUNET_NAMESTORE_disconnect(handle->namestore);
    217 
    218   struct GNUNET_CHAT_InternalAccounts *accounts;
    219   while (handle->accounts_head)
    220   {
    221     accounts = handle->accounts_head;
    222 
    223     internal_accounts_stop_method(accounts);
    224 
    225     if (accounts->account)
    226       account_destroy(accounts->account);
    227 
    228     internal_accounts_destroy(accounts);
    229   }
    230 
    231   if (handle->fs)
    232     GNUNET_FS_stop(handle->fs);
    233 
    234   if (handle->identity)
    235     GNUNET_IDENTITY_disconnect(handle->identity);
    236 
    237   struct GNUNET_CHAT_InternalServices *services;
    238   while (handle->services_head)
    239   {
    240     services = handle->services_head;
    241 
    242     if (services->op)
    243       GNUNET_ARM_operation_cancel(services->op);
    244 
    245     GNUNET_CONTAINER_DLL_remove(
    246       handle->services_head,
    247       handle->services_tail,
    248       services
    249     );
    250 
    251     GNUNET_free(services);
    252   }
    253 
    254   if (handle->arm)
    255     GNUNET_ARM_disconnect(handle->arm);
    256 
    257   GNUNET_CONTAINER_multihashmap_destroy(handle->files);
    258 
    259   if (handle->directory)
    260     GNUNET_free(handle->directory);
    261 
    262   struct GNUNET_CHAT_InternalMessages *internal;
    263   while (handle->internal_head)
    264   {
    265     internal = handle->internal_head;
    266 
    267     if (internal->msg)
    268       message_destroy(internal->msg);
    269 
    270     if (internal->task)
    271       GNUNET_SCHEDULER_cancel(internal->task);
    272 
    273     GNUNET_CONTAINER_DLL_remove(
    274       handle->internal_head,
    275       handle->internal_tail,
    276       internal
    277     );
    278 
    279     GNUNET_free(internal);
    280   }
    281 
    282   GNUNET_free(handle);
    283 }
    284 
    285 static void
    286 handle_update_identity(struct GNUNET_CHAT_Handle *handle)
    287 {
    288   GNUNET_assert(
    289     (handle) &&
    290     (handle->current) &&
    291 		(handle->contexts) &&
    292 		(handle->groups) &&
    293 		(handle->contacts)
    294   );
    295 
    296   handle_update_key(handle);
    297 
    298   if ((0 < GNUNET_CONTAINER_multihashmap_size(handle->contexts)) ||
    299       (0 < GNUNET_CONTAINER_multihashmap_size(handle->groups)) ||
    300       (0 < GNUNET_CONTAINER_multishortmap_size(handle->contacts)))
    301     return;
    302 
    303   GNUNET_assert(handle->messenger);
    304 
    305   handle_send_internal_message(
    306     handle,
    307     handle->current,
    308     NULL,
    309     GNUNET_CHAT_FLAG_LOGIN,
    310     NULL,
    311     GNUNET_NO
    312   );
    313 
    314   const struct GNUNET_CRYPTO_BlindablePrivateKey *zone = handle_get_key(handle);
    315 
    316   if ((!zone) || (handle->monitor))
    317     return;
    318 
    319   handle->monitor = GNUNET_NAMESTORE_zone_monitor_start(
    320     handle->cfg,
    321     zone,
    322     GNUNET_YES,
    323     NULL,
    324     NULL,
    325     on_monitor_namestore_record,
    326     handle,
    327     NULL,
    328     NULL
    329   );
    330 }
    331 
    332 void
    333 handle_connect (struct GNUNET_CHAT_Handle *handle,
    334 		            struct GNUNET_CHAT_Account *account)
    335 {
    336   GNUNET_assert(
    337     (handle) && (account) &&
    338 		(!(handle->current)) &&
    339 		(!(handle->contexts)) &&
    340     (!(handle->contacts)) &&
    341     (!(handle->groups)) &&
    342     (!(handle->invitations)) &&
    343 		(handle->files)
    344   );
    345 
    346   if (handle->monitor)
    347   {
    348     GNUNET_NAMESTORE_zone_monitor_stop(handle->monitor);
    349     handle->monitor = NULL;
    350   }
    351 
    352   handle->contexts = GNUNET_CONTAINER_multihashmap_create(
    353     initial_map_size_of_handle, GNUNET_NO);
    354   handle->contacts = GNUNET_CONTAINER_multishortmap_create(
    355     initial_map_size_of_handle, GNUNET_NO);
    356   handle->groups = GNUNET_CONTAINER_multihashmap_create(
    357     initial_map_size_of_handle, GNUNET_NO);
    358   handle->invitations = GNUNET_CONTAINER_multihashmap_create(
    359     initial_map_size_of_handle, GNUNET_NO);
    360 
    361   handle->gns = GNUNET_GNS_connect(handle->cfg);
    362 
    363   const struct GNUNET_CRYPTO_BlindablePrivateKey *key;
    364   key = account_get_key(account);
    365 
    366   const char *name = account_get_name(account);
    367 
    368   handle->messenger = GNUNET_MESSENGER_connect(
    369     handle->cfg, name, key,
    370     on_handle_message,
    371     handle
    372   );
    373 
    374   handle->next = NULL;
    375   handle->current = account;
    376   handle_update_identity(handle);
    377 }
    378 
    379 void
    380 handle_disconnect (struct GNUNET_CHAT_Handle *handle)
    381 {
    382   GNUNET_assert(
    383     (handle) &&
    384 		(handle->current) &&
    385 		(handle->groups) &&
    386 		(handle->contacts) &&
    387 		(handle->contexts) &&
    388 		(handle->files)
    389   );
    390 
    391   handle_send_internal_message(
    392     handle,
    393     handle->current,
    394     NULL,
    395     GNUNET_CHAT_FLAG_LOGOUT,
    396     NULL,
    397     GNUNET_YES
    398   );
    399 
    400   handle->own_contact = NULL;
    401 
    402   while (handle->attributes_head)
    403     internal_attributes_destroy(handle->attributes_head);
    404 
    405   while (handle->tickets_head)
    406     internal_tickets_destroy(handle->tickets_head);
    407 
    408   GNUNET_CONTAINER_multihashmap_iterate(
    409     handle->groups, it_destroy_handle_groups, NULL
    410   );
    411 
    412   GNUNET_CONTAINER_multishortmap_iterate(
    413     handle->contacts, it_destroy_handle_contacts, NULL
    414   );
    415 
    416   GNUNET_CONTAINER_multihashmap_iterate(
    417     handle->contexts, it_destroy_handle_contexts, NULL
    418   );
    419 
    420   struct GNUNET_CHAT_InternalMessages *internal;
    421   while (handle->internal_head)
    422   {
    423     internal = handle->internal_head;
    424 
    425     if (!(internal->msg->context))
    426       break;
    427 
    428     if (internal->msg)
    429       message_destroy(internal->msg);
    430 
    431     if (internal->task)
    432       GNUNET_SCHEDULER_cancel(internal->task);
    433 
    434     GNUNET_CONTAINER_DLL_remove(
    435       handle->internal_head,
    436       handle->internal_tail,
    437       internal
    438     );
    439 
    440     GNUNET_free(internal);
    441   }
    442 
    443   if (handle->messenger)
    444     GNUNET_MESSENGER_disconnect(handle->messenger);
    445 
    446   struct GNUNET_CHAT_UriLookups *lookups;
    447   while (handle->lookups_head)
    448   {
    449     lookups = handle->lookups_head;
    450 
    451     if (lookups->request)
    452       GNUNET_GNS_lookup_cancel(lookups->request);
    453 
    454     if (lookups->uri)
    455       uri_destroy(lookups->uri);
    456 
    457     GNUNET_CONTAINER_DLL_remove(
    458       handle->lookups_head,
    459       handle->lookups_tail,
    460       lookups
    461     );
    462 
    463     GNUNET_free(lookups);
    464   }
    465 
    466   if (handle->gns)
    467     GNUNET_GNS_disconnect(handle->gns);
    468 
    469   GNUNET_CONTAINER_multihashmap_iterate(
    470     handle->files, it_destroy_handle_files, NULL
    471   );
    472 
    473   handle->gns = NULL;
    474   handle->messenger = NULL;
    475 
    476   struct GNUNET_CHAT_InternalLobbies *lobbies;
    477   while (handle->lobbies_head)
    478   {
    479     lobbies = handle->lobbies_head;
    480 
    481     if (lobbies->lobby)
    482       lobby_destroy(lobbies->lobby);
    483 
    484     GNUNET_CONTAINER_DLL_remove(
    485       handle->lobbies_head,
    486       handle->lobbies_tail,
    487       lobbies
    488     );
    489 
    490     GNUNET_free(lobbies);
    491   }
    492 
    493   handle->own_contact = NULL;
    494 
    495   GNUNET_CONTAINER_multihashmap_destroy(handle->invitations);
    496   GNUNET_CONTAINER_multihashmap_destroy(handle->groups);
    497   GNUNET_CONTAINER_multishortmap_destroy(handle->contacts);
    498   GNUNET_CONTAINER_multihashmap_destroy(handle->contexts);
    499   GNUNET_CONTAINER_multihashmap_clear(handle->files);
    500 
    501   handle->contexts = NULL;
    502   handle->contacts = NULL;
    503   handle->groups = NULL;
    504   handle->invitations = NULL;
    505 
    506   if (handle->connection)
    507     GNUNET_SCHEDULER_cancel(handle->connection);
    508 
    509   handle->current = NULL;
    510   handle->connection = NULL;
    511 
    512   handle_update_key(handle);
    513 }
    514 
    515 static struct GNUNET_CHAT_InternalAccounts*
    516 find_accounts_by_name (const struct GNUNET_CHAT_Handle *handle,
    517 		                   const char *name,
    518                        enum GNUNET_GenericReturnValue skip_op)
    519 {
    520   GNUNET_assert((handle) && (name));
    521 
    522   struct GNUNET_CHAT_InternalAccounts *accounts = handle->accounts_head;
    523   const char *account_name;
    524 
    525   while (accounts)
    526   {
    527     if ((!(accounts->account)) || ((GNUNET_YES == skip_op) &&
    528         (accounts->op)))
    529       goto skip_account;
    530 
    531     account_name = account_get_name(
    532       accounts->account
    533     );
    534 
    535     if ((account_name) && (0 == strcmp(account_name, name)))
    536       break;
    537 
    538   skip_account:
    539     accounts = accounts->next;
    540   }
    541 
    542   return accounts;
    543 }
    544 
    545 struct GNUNET_CHAT_Account*
    546 handle_get_account_by_name (const struct GNUNET_CHAT_Handle *handle,
    547 		                        const char *name,
    548                             enum GNUNET_GenericReturnValue skip_op)
    549 {
    550   GNUNET_assert((handle) && (name));
    551 
    552   struct GNUNET_CHAT_InternalAccounts *accounts;
    553   accounts = find_accounts_by_name(handle, name, skip_op);
    554 
    555   if (!accounts)
    556     return NULL;
    557 
    558   return accounts->account;
    559 }
    560 
    561 static enum GNUNET_GenericReturnValue
    562 update_accounts_operation (struct GNUNET_CHAT_InternalAccounts **out_accounts,
    563                            struct GNUNET_CHAT_Handle *handle,
    564                            const char *name,
    565                            enum GNUNET_CHAT_AccountMethod method)
    566 {
    567   GNUNET_assert(handle);
    568 
    569   struct GNUNET_CHAT_InternalAccounts *accounts = *out_accounts;
    570 
    571   if (accounts)
    572     internal_accounts_stop_method(accounts);
    573   else
    574   {
    575     accounts = internal_accounts_create(handle, NULL);
    576 
    577     if (!accounts)
    578       return GNUNET_SYSERR;
    579   }
    580 
    581   internal_accounts_start_method(accounts, method, name);
    582 
    583   *out_accounts = accounts;
    584 
    585   return GNUNET_OK;
    586 }
    587 
    588 enum GNUNET_GenericReturnValue
    589 handle_create_account (struct GNUNET_CHAT_Handle *handle,
    590 		                   const char *name)
    591 {
    592   GNUNET_assert((handle) && (name));
    593 
    594   struct GNUNET_CHAT_InternalAccounts *accounts;
    595   accounts = find_accounts_by_name(handle, name, GNUNET_NO);
    596 
    597   if (accounts)
    598     return GNUNET_SYSERR;
    599 
    600   enum GNUNET_GenericReturnValue result;
    601   result = update_accounts_operation(
    602     &accounts, 
    603     handle, 
    604     name, 
    605     GNUNET_CHAT_ACCOUNT_CREATION
    606   );
    607 
    608   if (GNUNET_OK != result)
    609     return result;
    610 
    611   accounts->op = GNUNET_IDENTITY_create(
    612     handle->identity,
    613     name,
    614     NULL,
    615     GNUNET_PUBLIC_KEY_TYPE_ECDSA,
    616     cb_account_creation,
    617     accounts
    618   );
    619 
    620   if (!accounts->op)
    621     return GNUNET_SYSERR;
    622 
    623   return result;
    624 }
    625 
    626 enum GNUNET_GenericReturnValue
    627 handle_delete_account (struct GNUNET_CHAT_Handle *handle,
    628 		                   const struct GNUNET_CHAT_Account *account)
    629 {
    630   GNUNET_assert((handle) && (account));
    631 
    632   struct GNUNET_CHAT_InternalAccounts *accounts;
    633   accounts = handle->accounts_head;
    634 
    635   while (accounts)
    636     if (account == accounts->account)
    637       break;
    638     else
    639       accounts = accounts->next;
    640 
    641   if (!accounts)
    642     return GNUNET_SYSERR;
    643 
    644   enum GNUNET_GenericReturnValue result;
    645   result = update_accounts_operation(
    646     &accounts, 
    647     handle, 
    648     NULL, 
    649     GNUNET_CHAT_ACCOUNT_DELETION
    650   );
    651 
    652   if (GNUNET_OK != result)
    653     return result;
    654 
    655   const char *name = account_get_name(account);
    656 
    657   accounts->op = GNUNET_IDENTITY_delete(
    658     handle->identity,
    659     name,
    660     cb_account_deletion,
    661     accounts
    662   );
    663 
    664   if (!accounts->op)
    665     return GNUNET_SYSERR;
    666 
    667   return result;
    668 }
    669 
    670 enum GNUNET_GenericReturnValue
    671 handle_rename_account (struct GNUNET_CHAT_Handle *handle,
    672                        const struct GNUNET_CHAT_Account *account,
    673                        const char *new_name)
    674 {
    675   GNUNET_assert((handle) && (account) && (new_name));
    676 
    677   struct GNUNET_CHAT_InternalAccounts *accounts;
    678   accounts = handle->accounts_head;
    679 
    680   while (accounts)
    681     if (account == accounts->account)
    682       break;
    683     else
    684       accounts = accounts->next;
    685 
    686   if (!accounts)
    687     return GNUNET_SYSERR;
    688 
    689   if (find_accounts_by_name(handle, new_name, GNUNET_NO))
    690     return GNUNET_SYSERR;
    691 
    692   const char *old_name = account_get_name(account);
    693 
    694   if (0 == strcmp(old_name, new_name))
    695     return GNUNET_OK;
    696 
    697   enum GNUNET_GenericReturnValue result;
    698   result = update_accounts_operation(
    699     &accounts, 
    700     handle, 
    701     NULL, 
    702     GNUNET_CHAT_ACCOUNT_RENAMING
    703   );
    704 
    705   if (GNUNET_OK != result)
    706     return result;
    707 
    708   accounts->op = GNUNET_IDENTITY_rename(
    709     handle->identity,
    710     old_name,
    711     new_name,
    712     cb_account_rename,
    713     accounts
    714   );
    715 
    716   if (!accounts->op)
    717     return GNUNET_SYSERR;
    718 
    719   return result;
    720 }
    721 
    722 enum GNUNET_GenericReturnValue
    723 handle_delete_lobby (struct GNUNET_CHAT_Handle *handle,
    724                      const struct GNUNET_CHAT_Lobby *lobby)
    725 {
    726   GNUNET_assert((handle) && (lobby));
    727 
    728   if (!(lobby->context))
    729     return GNUNET_SYSERR;
    730 
    731   const struct GNUNET_HashCode *key = GNUNET_MESSENGER_room_get_key(
    732     lobby->context->room
    733   );
    734 
    735   if (!key)
    736     return GNUNET_SYSERR;
    737 
    738   struct GNUNET_CHAT_InternalAccounts *accounts = NULL;
    739   enum GNUNET_GenericReturnValue result;
    740   result = update_accounts_operation(
    741     &accounts, 
    742     handle, 
    743     NULL, 
    744     GNUNET_CHAT_ACCOUNT_DELETION
    745   );
    746 
    747   if (GNUNET_OK != result)
    748     return result;
    749 
    750   char *name;
    751   util_lobby_name(key, &name);
    752 
    753   accounts->op = GNUNET_IDENTITY_delete(
    754     handle->identity,
    755     name,
    756     cb_lobby_deletion,
    757     accounts
    758   );
    759 
    760   GNUNET_free(name);
    761 
    762   if (!accounts->op)
    763     return GNUNET_SYSERR;
    764 
    765   return result;
    766 }
    767 
    768 const char*
    769 handle_get_directory (const struct GNUNET_CHAT_Handle *handle)
    770 {
    771   GNUNET_assert(handle);
    772 
    773   return handle->directory;
    774 }
    775 
    776 char*
    777 handle_create_file_path (const struct GNUNET_CHAT_Handle *handle,
    778                          const struct GNUNET_HashCode *hash)
    779 {
    780   GNUNET_assert((handle) && (hash));
    781 
    782   const char *directory = handle_get_directory(handle);
    783 
    784   if (!directory)
    785     return NULL;
    786 
    787   char *filename;
    788   util_get_filename (
    789     directory, "files", hash, &filename
    790   );
    791 
    792   return filename;
    793 }
    794 
    795 enum GNUNET_GenericReturnValue
    796 handle_update (struct GNUNET_CHAT_Handle *handle)
    797 {
    798   GNUNET_assert((handle) && (handle->current));
    799 
    800   struct GNUNET_CHAT_InternalAccounts *accounts;
    801   accounts = handle->accounts_head;
    802 
    803   while (accounts)
    804     if (handle->current == accounts->account)
    805       break;
    806     else
    807       accounts = accounts->next;
    808 
    809   if (!accounts)
    810     return GNUNET_SYSERR;
    811 
    812   const char *name = account_get_name(handle->current);
    813 
    814   enum GNUNET_GenericReturnValue result;
    815   result = update_accounts_operation(
    816     &accounts, 
    817     handle, 
    818     name, 
    819     GNUNET_CHAT_ACCOUNT_UPDATING
    820   );
    821 
    822   if (GNUNET_OK != result)
    823     return result;
    824 
    825   accounts->op = GNUNET_IDENTITY_delete(
    826     handle->identity,
    827     name,
    828     cb_account_update,
    829     accounts
    830   );
    831 
    832   if (!accounts->op)
    833     return GNUNET_SYSERR;
    834 
    835   return result;
    836 }
    837 
    838 const struct GNUNET_CRYPTO_BlindablePrivateKey*
    839 handle_get_key (const struct GNUNET_CHAT_Handle *handle)
    840 {
    841   GNUNET_assert(handle);
    842 
    843   if (!(handle->current))
    844     return NULL;
    845 
    846   return account_get_key(handle->current);
    847 }
    848 
    849 void
    850 handle_send_internal_message (struct GNUNET_CHAT_Handle *handle,
    851                               struct GNUNET_CHAT_Account *account,
    852                               struct GNUNET_CHAT_Context *context,
    853                               enum GNUNET_CHAT_MessageFlag flag,
    854                               const char *warning,
    855                               enum GNUNET_GenericReturnValue feedback)
    856 {
    857   GNUNET_assert((handle) && (GNUNET_CHAT_FLAG_NONE != flag));
    858 
    859   if ((handle->destruction) || (!(handle->msg_cb)))
    860     return;
    861 
    862   struct GNUNET_CHAT_InternalMessages *internal = GNUNET_new(
    863     struct GNUNET_CHAT_InternalMessages
    864   );
    865 
    866   internal->chat = handle;
    867   internal->msg = message_create_internally(
    868     account, context, flag, warning
    869   );
    870 
    871   if (!(internal->msg))
    872   {
    873     GNUNET_free(internal);
    874     return;
    875   }
    876 
    877   if (GNUNET_YES != feedback)
    878     internal->task = GNUNET_SCHEDULER_add_now(
    879       on_handle_internal_message_callback,
    880       internal
    881     );
    882   else
    883   {
    884     internal->task = NULL;
    885     if (handle->msg_cb)
    886       handle->msg_cb(handle->msg_cls, context, internal->msg);
    887   }
    888 
    889   if (context)
    890     GNUNET_CONTAINER_DLL_insert(
    891       handle->internal_head,
    892       handle->internal_tail,
    893       internal
    894     );
    895   else
    896     GNUNET_CONTAINER_DLL_insert_tail(
    897     	handle->internal_head,
    898     	handle->internal_tail,
    899     	internal
    900     );
    901 }
    902 
    903 void
    904 handle_send_room_name (struct GNUNET_CHAT_Handle *handle,
    905 		                   struct GNUNET_MESSENGER_Room *room)
    906 {
    907   GNUNET_assert((handle) && (handle->messenger) && (room));
    908 
    909   if (handle->destruction)
    910     return;
    911 
    912   const char *name = GNUNET_MESSENGER_get_name(handle->messenger);
    913 
    914   if (!name)
    915     return;
    916 
    917   struct GNUNET_MESSENGER_Message msg;
    918   memset(&msg, 0, sizeof(msg));
    919 
    920   msg.header.kind = GNUNET_MESSENGER_KIND_NAME;
    921   msg.body.name.name = GNUNET_strdup(name);
    922 
    923   GNUNET_MESSENGER_send_message(room, &msg, NULL);
    924 
    925   GNUNET_free(msg.body.name.name);
    926 }
    927 
    928 enum GNUNET_GenericReturnValue
    929 handle_request_context_by_room (struct GNUNET_CHAT_Handle *handle,
    930 				                        struct GNUNET_MESSENGER_Room *room)
    931 {
    932   GNUNET_assert(
    933     (handle) &&
    934 		(handle->contexts) &&
    935 		(room)
    936   );
    937 
    938   const struct GNUNET_HashCode *key = GNUNET_MESSENGER_room_get_key(room);
    939 
    940   struct GNUNET_CHAT_Context *context = GNUNET_CONTAINER_multihashmap_get(
    941     handle->contexts, key
    942   );
    943 
    944   struct GNUNET_CHAT_CheckHandleRoomMembers check;
    945 
    946   if (!context)
    947     goto new_context;
    948 
    949   if ((GNUNET_CHAT_CONTEXT_TYPE_UNKNOWN == context->type) &&
    950       (GNUNET_YES != context->deleted))
    951     goto check_type;
    952   
    953   return GNUNET_OK;
    954 
    955 new_context:
    956   context = context_create_from_room(handle, room);
    957 
    958   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(
    959       handle->contexts, key, context,
    960       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
    961   {
    962     context_destroy(context);
    963     return GNUNET_SYSERR;
    964   }
    965 
    966   if (GNUNET_CHAT_CONTEXT_TYPE_GROUP == context->type)
    967     goto setup_group;
    968 
    969 check_type:
    970   check.ignore_key = GNUNET_MESSENGER_get_key(handle->messenger);
    971   check.contact = NULL;
    972 
    973   const int checks = GNUNET_MESSENGER_iterate_members(
    974     room, check_handle_room_members, &check
    975   );
    976 
    977   if ((check.contact) &&
    978       (GNUNET_OK == intern_provide_contact_for_member(handle,
    979 						      check.contact,
    980 						      context)))
    981   {
    982     context_delete(context, GNUNET_NO);
    983 
    984     context->type = GNUNET_CHAT_CONTEXT_TYPE_CONTACT;
    985     context->deleted = GNUNET_NO;
    986 
    987     context_write_records(context);
    988   }
    989   else if (checks >= minimum_amount_of_other_members_in_group)
    990   {
    991     context_delete(context, GNUNET_NO);
    992 
    993     context->type = GNUNET_CHAT_CONTEXT_TYPE_GROUP;
    994     context->deleted = GNUNET_NO;
    995 
    996     if (context->contact)
    997     {
    998       struct GNUNET_CHAT_Contact *contact = handle_get_contact_from_messenger(
    999 	      handle, check.contact
   1000       );
   1001 
   1002       if ((contact) && (contact->context == context))
   1003 	      contact->context = NULL;
   1004 
   1005       context->contact = NULL;
   1006     }
   1007 
   1008     goto setup_group;
   1009   }
   1010 
   1011   return GNUNET_OK;
   1012 
   1013 setup_group:
   1014   GNUNET_MESSENGER_iterate_members(room, scan_handle_room_members, handle);
   1015 
   1016   struct GNUNET_CHAT_Group *group = group_create_from_context(
   1017     handle, context
   1018   );
   1019 
   1020   if (context->topic)
   1021     group_publish(group);
   1022 
   1023   if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(
   1024       handle->groups, key, group,
   1025       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   1026   {
   1027     handle_send_internal_message(
   1028       handle,
   1029       NULL,
   1030       context,
   1031       GNUNET_CHAT_FLAG_UPDATE_CONTEXT,
   1032       NULL,
   1033       GNUNET_NO
   1034     );
   1035 
   1036     context_write_records(context);
   1037     return GNUNET_OK;
   1038   }
   1039 
   1040   group_destroy(group);
   1041 
   1042   GNUNET_CONTAINER_multihashmap_remove(handle->contexts, key, context);
   1043   context_destroy(context);
   1044   return GNUNET_SYSERR;
   1045 }
   1046 
   1047 struct GNUNET_CHAT_Contact*
   1048 handle_get_contact_from_messenger (const struct GNUNET_CHAT_Handle *handle,
   1049 				                           const struct GNUNET_MESSENGER_Contact *contact)
   1050 {
   1051   GNUNET_assert((handle) && (handle->contacts) && (contact));
   1052 
   1053   struct GNUNET_ShortHashCode shorthash;
   1054   util_shorthash_from_member(contact, &shorthash);
   1055 
   1056   return GNUNET_CONTAINER_multishortmap_get(
   1057     handle->contacts, &shorthash
   1058   );
   1059 }
   1060 
   1061 struct GNUNET_CHAT_Group*
   1062 handle_get_group_from_messenger (const struct GNUNET_CHAT_Handle *handle,
   1063 				                         const struct GNUNET_MESSENGER_Room *room)
   1064 {
   1065   GNUNET_assert((handle) && (handle->groups) && (room));
   1066 
   1067   const struct GNUNET_HashCode *key = GNUNET_MESSENGER_room_get_key(room);
   1068 
   1069   if (!key)
   1070     return NULL;
   1071 
   1072   return GNUNET_CONTAINER_multihashmap_get(
   1073     handle->groups, key
   1074   );
   1075 }
   1076 
   1077 struct GNUNET_CHAT_Context*
   1078 handle_process_records (struct GNUNET_CHAT_Handle *handle,
   1079                         const char *label,
   1080                         unsigned int count,
   1081                         const struct GNUNET_GNSRECORD_Data *data)
   1082 {
   1083   GNUNET_assert((handle) && (data));
   1084 
   1085   if (!count)
   1086     return NULL;
   1087 
   1088   const struct GNUNET_MESSENGER_RoomEntryRecord *record = NULL;
   1089 
   1090   for (unsigned int i = 0; i < count; i++)
   1091   {
   1092     if (GNUNET_YES == GNUNET_GNSRECORD_is_expired(data + i))
   1093       continue;
   1094 
   1095     if ((GNUNET_GNSRECORD_TYPE_MESSENGER_ROOM_ENTRY == data[i].record_type) &&
   1096       (!(GNUNET_GNSRECORD_RF_SUPPLEMENTAL & data[i].flags)))
   1097     {
   1098       record = data[i].data;
   1099       break;
   1100     }
   1101   }
   1102 
   1103   if (!record)
   1104     return NULL;
   1105 
   1106   union GNUNET_MESSENGER_RoomKey key;
   1107   GNUNET_memcpy (&(key.hash), &(record->key), sizeof(key));
   1108 
   1109   struct GNUNET_CHAT_Context *context = GNUNET_CONTAINER_multihashmap_get(
   1110     handle->contexts,
   1111     &(key.hash)
   1112   );
   1113 
   1114   if ((context) && (context->room))
   1115   {
   1116     context_read_records(context, label, count, data);
   1117     return NULL;
   1118   }
   1119 
   1120   struct GNUNET_MESSENGER_Room *room = GNUNET_MESSENGER_enter_room(
   1121     handle->messenger,
   1122     &(record->door),
   1123     &key
   1124   );
   1125 
   1126   if (!room)
   1127     return NULL;
   1128   else if (context)
   1129   {
   1130     context_update_room(context, room, GNUNET_NO);
   1131     context_read_records(context, label, count, data);
   1132     return NULL;
   1133   }
   1134 
   1135   context = context_create_from_room(handle, room);
   1136   context_read_records(context, label, count, data);
   1137 
   1138   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(
   1139       handle->contexts, &(key.hash), context,
   1140       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   1141   {
   1142     context_destroy(context);
   1143     GNUNET_MESSENGER_close_room(room);
   1144     return NULL;
   1145   }
   1146 
   1147   if (GNUNET_CHAT_CONTEXT_TYPE_GROUP != context->type)
   1148     return context;
   1149 
   1150   struct GNUNET_CHAT_Group *group = group_create_from_context(handle, context);
   1151 
   1152   if (context->topic)
   1153     group_publish(group);
   1154 
   1155   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(
   1156       handle->groups, &(key.hash), group,
   1157       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   1158     group_destroy(group);
   1159 
   1160   return context;
   1161 }