libgnunetchat

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

gnunet_chat_handle.c (27314B)


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