libgnunetchat

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

gnunet_chat_handle_intern.c (31186B)


      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_intern.c
     23  */
     24 
     25 #include "gnunet_chat_contact.h"
     26 #include "gnunet_chat_context.h"
     27 #include "gnunet_chat_discourse.h"
     28 #include "gnunet_chat_file.h"
     29 #include "gnunet_chat_group.h"
     30 #include "gnunet_chat_handle.h"
     31 #include "gnunet_chat_invitation.h"
     32 #include "gnunet_chat_lobby.h"
     33 #include "gnunet_chat_message.h"
     34 #include "gnunet_chat_ticket.h"
     35 #include "gnunet_chat_util.h"
     36 
     37 #include "internal/gnunet_chat_accounts.h"
     38 #include "internal/gnunet_chat_tagging.h"
     39 
     40 #include <gnunet/gnunet_arm_service.h>
     41 #include <gnunet/gnunet_common.h>
     42 #include <gnunet/gnunet_hello_uri_lib.h>
     43 #include <gnunet/gnunet_identity_service.h>
     44 #include <gnunet/gnunet_messenger_service.h>
     45 #include <gnunet/gnunet_reclaim_service.h>
     46 #include <gnunet/gnunet_scheduler_lib.h>
     47 #include <gnunet/gnunet_time_lib.h>
     48 #include <gnunet/gnunet_util_lib.h>
     49 
     50 #include <stdio.h>
     51 #include <string.h>
     52 
     53 #define GNUNET_UNUSED __attribute__ ((unused))
     54 
     55 static const char gnunet_service_name_arm [] = "arm";
     56 static const char gnunet_service_name_fs [] = "fs";
     57 static const char gnunet_service_name_gns [] = "gns";
     58 static const char gnunet_service_name_identity [] = "identity";
     59 static const char gnunet_service_name_messenger [] = "messenger";
     60 static const char gnunet_service_name_namestore [] = "namestore";
     61 static const char gnunet_service_name_pils [] = "pils";
     62 static const char gnunet_service_name_reclaim [] = "reclaim";
     63 
     64 void
     65 on_handle_shutdown(void *cls)
     66 {
     67   struct GNUNET_CHAT_Handle *chat = cls;
     68 
     69   GNUNET_assert((chat) && (chat->shutdown_hook));
     70   chat->shutdown_hook = NULL;
     71 
     72   handle_destroy(chat);
     73 }
     74 
     75 void
     76 on_handle_service_request(void *cls, 
     77                           enum GNUNET_ARM_RequestStatus status, 
     78                           enum GNUNET_ARM_Result result)
     79 {
     80   GNUNET_assert(cls);
     81 
     82   struct GNUNET_CHAT_InternalServices *services = cls;
     83   services->op = NULL;
     84 
     85   if (status != GNUNET_ARM_REQUEST_SENT_OK)
     86     return;
     87 
     88   struct GNUNET_CHAT_Handle *chat = services->chat;
     89 
     90   GNUNET_CONTAINER_DLL_remove(
     91     chat->services_head,
     92     chat->services_tail,
     93     services
     94   );
     95 
     96   GNUNET_free(services);
     97 }
     98 
     99 static void
    100 _request_service_via_arm(struct GNUNET_CHAT_Handle *chat,
    101                          const char *service_name)
    102 {
    103   GNUNET_assert((chat) && (chat->arm) && (service_name));
    104 
    105   struct GNUNET_CHAT_InternalServices *services = GNUNET_new(
    106     struct GNUNET_CHAT_InternalServices
    107   );
    108 
    109   if (! services)
    110     return;
    111 
    112   services->chat = chat;
    113   services->op = GNUNET_ARM_request_service_start(
    114     chat->arm,
    115     service_name,
    116     GNUNET_OS_INHERIT_STD_NONE,
    117     on_handle_service_request,
    118     services
    119   );
    120 
    121   GNUNET_CONTAINER_DLL_insert(
    122     chat->services_head,
    123     chat->services_tail,
    124     services
    125   );
    126 }
    127 
    128 void
    129 on_handle_arm_connection(void *cls,
    130 			                   int connected)
    131 {
    132   struct GNUNET_CHAT_Handle *chat = cls;
    133 
    134   GNUNET_assert((chat) && (chat->arm));
    135 
    136   if (GNUNET_YES == connected)
    137   {
    138     _request_service_via_arm(chat, gnunet_service_name_identity);
    139     _request_service_via_arm(chat, gnunet_service_name_messenger);
    140     _request_service_via_arm(chat, gnunet_service_name_fs);
    141     _request_service_via_arm(chat, gnunet_service_name_gns);
    142     _request_service_via_arm(chat, gnunet_service_name_namestore);
    143     _request_service_via_arm(chat, gnunet_service_name_pils);
    144     _request_service_via_arm(chat, gnunet_service_name_reclaim);
    145   }
    146   else
    147   {
    148     _request_service_via_arm(chat, gnunet_service_name_arm);
    149   }
    150 }
    151 
    152 void*
    153 notify_handle_fs_progress(void* cls,
    154 			                    const struct GNUNET_FS_ProgressInfo* info)
    155 {
    156   struct GNUNET_CHAT_Handle *chat = cls;
    157 
    158   GNUNET_assert(info);
    159 
    160   if (!chat)
    161     return NULL;
    162 
    163   switch (info->status) {
    164     case GNUNET_FS_STATUS_PUBLISH_START: {
    165       struct GNUNET_CHAT_File *file = info->value.publish.cctx;
    166 
    167       file_update_upload(
    168         file,
    169         0,
    170         info->value.publish.size
    171       );
    172 
    173       return file;
    174     } case GNUNET_FS_STATUS_PUBLISH_PROGRESS: {
    175       struct GNUNET_CHAT_File *file = info->value.publish.cctx;
    176 
    177       file_update_upload(
    178         file,
    179         info->value.publish.completed,
    180         info->value.publish.size
    181       );
    182 
    183       return file;
    184     } case GNUNET_FS_STATUS_PUBLISH_COMPLETED: {
    185       struct GNUNET_CHAT_File *file = info->value.publish.cctx;
    186 
    187       file->uri = GNUNET_FS_uri_dup(
    188 	      info->value.publish.specifics.completed.chk_uri
    189       );
    190 
    191       file_update_upload(
    192         file,
    193         info->value.publish.size,
    194         info->value.publish.size
    195       );
    196 
    197       file->publish = NULL;
    198       break;
    199     } case GNUNET_FS_STATUS_PUBLISH_ERROR: {
    200       break;
    201     } case GNUNET_FS_STATUS_DOWNLOAD_START: {
    202       struct GNUNET_CHAT_File *file = info->value.download.cctx;
    203 
    204       file_update_download(
    205 	      file,
    206         0,
    207         info->value.download.size
    208       );
    209 
    210       return file;
    211     } case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE: {
    212       return info->value.download.cctx;
    213     } case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE: {
    214       return info->value.download.cctx;
    215     } case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS: {
    216       struct GNUNET_CHAT_File *file = info->value.download.cctx;
    217 
    218       file_update_download(
    219         file,
    220         info->value.download.completed,
    221         info->value.download.size
    222       );
    223 
    224       return file;
    225     } case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED: {
    226       struct GNUNET_CHAT_File *file = info->value.download.cctx;
    227 
    228       file_update_download(
    229         file,
    230         info->value.download.size,
    231         info->value.download.size
    232       );
    233 
    234       file->download = NULL;
    235       break;
    236     } case GNUNET_FS_STATUS_DOWNLOAD_ERROR: {
    237       break;
    238     } case GNUNET_FS_STATUS_UNINDEX_START: {
    239       struct GNUNET_CHAT_File *file = info->value.unindex.cctx;
    240 
    241       file_update_unindex(
    242 	      file,
    243         0,
    244         info->value.unindex.size
    245       );
    246 
    247       return file;
    248     } case GNUNET_FS_STATUS_UNINDEX_PROGRESS: {
    249       struct GNUNET_CHAT_File *file = info->value.unindex.cctx;
    250 
    251       file_update_unindex(
    252         file,
    253         info->value.unindex.completed,
    254         info->value.unindex.size
    255       );
    256 
    257       return file;
    258     } case GNUNET_FS_STATUS_UNINDEX_COMPLETED: {
    259       struct GNUNET_CHAT_File *file = info->value.unindex.cctx;
    260 
    261       file_update_unindex(
    262 	      file,
    263         info->value.unindex.size,
    264         info->value.unindex.size
    265       );
    266 
    267       file->unindex = NULL;
    268       char *filename = handle_create_file_path(
    269         chat, &(file->hash)
    270       );
    271 
    272       if (!filename)
    273         break;
    274 
    275       if (GNUNET_YES == GNUNET_DISK_file_test_read(filename))
    276         remove(filename);
    277 
    278       GNUNET_free(filename);
    279       break;
    280     } default: {
    281       break;
    282     }
    283   }
    284 
    285   return NULL;
    286 }
    287 
    288 static void
    289 on_handle_refresh (void *cls)
    290 {
    291   GNUNET_assert(cls);
    292 
    293   struct GNUNET_CHAT_Handle* handle = cls;
    294 
    295   handle->refresh = NULL;
    296 
    297   handle_send_internal_message(
    298     handle,
    299     NULL,
    300     NULL,
    301     GNUNET_CHAT_FLAG_REFRESH,
    302     NULL,
    303     GNUNET_YES
    304   );
    305 }
    306 
    307 void
    308 on_handle_gnunet_identity (void *cls,
    309                            struct GNUNET_IDENTITY_Ego *ego,
    310                            void **ctx,
    311                            const char *name)
    312 {
    313   GNUNET_assert(cls);
    314 
    315   if ((name) && (GNUNET_YES == util_is_lobby_name(name)))
    316     return;
    317 
    318   struct GNUNET_CHAT_Handle* handle = cls;
    319 
    320   if ((!ctx) || (!ego))
    321   {
    322     handle->refreshing = GNUNET_YES;
    323     goto send_refresh;
    324   }
    325 
    326   struct GNUNET_CHAT_InternalAccounts *accounts = handle->accounts_head;
    327 
    328   while (accounts)
    329   {
    330     if (!(accounts->account))
    331       goto skip_account;
    332 
    333     if (ego != accounts->account->ego)
    334       goto check_matching_name;
    335 
    336     if ((name) && ((!(accounts->account->name)) ||
    337         (0 != strcmp(accounts->account->name, name))))
    338     {
    339       const char *old_name = account_get_name(accounts->account);
    340       char *name_buffer = old_name? GNUNET_strdup(old_name) : NULL;
    341       
    342       util_set_name_field(name, &(accounts->account->name));
    343 
    344       handle_send_internal_message(
    345         handle,
    346         accounts->account,
    347         NULL,
    348         GNUNET_CHAT_FLAG_UPDATE_ACCOUNT,
    349         name_buffer,
    350         GNUNET_YES
    351       );
    352 
    353       if (name_buffer)
    354         GNUNET_free(name_buffer);
    355     }
    356     else if ((!name) && (!(accounts->op)))
    357     {
    358       if (handle->current == accounts->account)
    359 	      handle_disconnect(handle);
    360 
    361       account_destroy(accounts->account);
    362       internal_accounts_destroy(accounts);
    363     }
    364     else if (!name)
    365       account_update_ego(accounts->account, handle, NULL);
    366 
    367     goto send_refresh;
    368 
    369 check_matching_name:
    370     if ((name) && (accounts->account->name) && (ego) &&
    371 	      (0 == strcmp(accounts->account->name, name)))
    372     {
    373       account_update_ego(accounts->account, handle, ego);
    374       goto send_refresh;
    375     }
    376 
    377 skip_account:
    378     accounts = accounts->next;
    379   }
    380 
    381   if (!name)
    382     return;
    383 
    384   accounts = internal_accounts_create(
    385     handle,
    386     account_create_from_ego(handle, ego, name)
    387   );
    388 
    389 send_refresh:
    390   if ((GNUNET_YES != handle->refreshing) ||
    391       (handle->refresh))
    392     return;
    393   
    394   handle->refresh = GNUNET_SCHEDULER_add_with_priority(
    395     GNUNET_SCHEDULER_PRIORITY_IDLE,
    396     on_handle_refresh,
    397     handle
    398   );
    399 }
    400 
    401 void
    402 cb_account_creation (void *cls,
    403                      const struct GNUNET_CRYPTO_BlindablePrivateKey *key,
    404                      enum GNUNET_ErrorCode ec)
    405 {
    406   GNUNET_assert(cls);
    407 
    408   struct GNUNET_CHAT_InternalAccounts *accounts = (
    409     (struct GNUNET_CHAT_InternalAccounts*) cls
    410   );
    411 
    412   accounts->op = NULL;
    413 
    414   if ((!(accounts->account)) && (accounts->identifier))
    415     accounts->account = account_create(
    416       accounts->handle, accounts->identifier
    417     );
    418   
    419   internal_accounts_stop_method(accounts);
    420   
    421   if (GNUNET_EC_NONE == ec)
    422     return;
    423 
    424   handle_send_internal_message(
    425     accounts->handle,
    426     accounts->account,
    427     NULL,
    428     GNUNET_CHAT_FLAG_WARNING,
    429     GNUNET_ErrorCode_get_hint(ec),
    430     GNUNET_YES
    431   );
    432 }
    433 
    434 void
    435 cb_account_deletion (void *cls,
    436 		                 enum GNUNET_ErrorCode ec)
    437 {
    438   GNUNET_assert(cls);
    439 
    440   struct GNUNET_CHAT_InternalAccounts *accounts = (
    441     (struct GNUNET_CHAT_InternalAccounts*) cls
    442   );
    443 
    444   accounts->op = NULL;
    445 
    446   internal_accounts_stop_method(accounts);
    447 
    448   if (accounts->handle->current == accounts->account)
    449 	  handle_disconnect(accounts->handle);
    450 
    451   if (GNUNET_EC_NONE != ec)
    452     handle_send_internal_message(
    453       accounts->handle,
    454       accounts->account,
    455       NULL,
    456       GNUNET_CHAT_FLAG_WARNING,
    457       GNUNET_ErrorCode_get_hint(ec),
    458       GNUNET_YES
    459     );
    460   else
    461   {
    462     handle_send_internal_message(
    463       accounts->handle,
    464       accounts->account,
    465       NULL,
    466       GNUNET_CHAT_FLAG_DELETE_ACCOUNT,
    467       NULL,
    468       GNUNET_YES
    469     );
    470 
    471     account_delete(accounts->account);
    472   }
    473 
    474   account_destroy(accounts->account);
    475   internal_accounts_destroy(accounts);
    476 }
    477 
    478 void
    479 cb_account_rename (void *cls,
    480 		               enum GNUNET_ErrorCode ec)
    481 {
    482   GNUNET_assert(cls);
    483 
    484   struct GNUNET_CHAT_InternalAccounts *accounts = (
    485     (struct GNUNET_CHAT_InternalAccounts*) cls
    486   );
    487 
    488   accounts->op = NULL;
    489 
    490   internal_accounts_stop_method(accounts);
    491 
    492   if (GNUNET_EC_NONE == ec)
    493     return;
    494 
    495   handle_send_internal_message(
    496     accounts->handle,
    497     accounts->account,
    498     NULL,
    499     GNUNET_CHAT_FLAG_WARNING,
    500     GNUNET_ErrorCode_get_hint(ec),
    501     GNUNET_YES
    502   );
    503 }
    504 
    505 void
    506 cb_lobby_deletion (void *cls,
    507 		               enum GNUNET_ErrorCode ec)
    508 {
    509   GNUNET_assert(cls);
    510 
    511   struct GNUNET_CHAT_InternalAccounts *accounts = (
    512     (struct GNUNET_CHAT_InternalAccounts*) cls
    513   );
    514 
    515   accounts->op = NULL;
    516 
    517   internal_accounts_stop_method(accounts);
    518 
    519   if (GNUNET_EC_NONE != ec)
    520     handle_send_internal_message(
    521       accounts->handle,
    522       accounts->account,
    523       NULL,
    524       GNUNET_CHAT_FLAG_WARNING,
    525       GNUNET_ErrorCode_get_hint(ec),
    526       GNUNET_YES
    527     );
    528 
    529   internal_accounts_destroy(accounts);
    530 }
    531 
    532 static void
    533 cb_account_update_completion (void *cls,
    534                               const struct GNUNET_CRYPTO_BlindablePrivateKey *key,
    535                               enum GNUNET_ErrorCode ec)
    536 {
    537   GNUNET_assert(cls);
    538 
    539   struct GNUNET_CHAT_InternalAccounts *accounts = (
    540     (struct GNUNET_CHAT_InternalAccounts*) cls
    541   );
    542 
    543   accounts->op = NULL;
    544 
    545   cb_account_creation(cls, key, ec);
    546 }
    547 
    548 void
    549 cb_account_update (void *cls,
    550 		               enum GNUNET_ErrorCode ec)
    551 {
    552   GNUNET_assert(cls);
    553 
    554   struct GNUNET_CHAT_InternalAccounts *accounts = (
    555     (struct GNUNET_CHAT_InternalAccounts*) cls
    556   );
    557 
    558   if ((GNUNET_EC_NONE != ec) || (!(accounts->identifier)))
    559   {
    560     cb_account_deletion(cls, ec);
    561     return;
    562   }
    563   
    564   accounts->op = GNUNET_IDENTITY_create(
    565     accounts->handle->identity,
    566     accounts->identifier,
    567     NULL,
    568     GNUNET_PUBLIC_KEY_TYPE_ECDSA,
    569     cb_account_update_completion,
    570     accounts
    571   );
    572 }
    573 
    574 int
    575 intern_provide_contact_for_member(struct GNUNET_CHAT_Handle *handle,
    576                                   const struct GNUNET_MESSENGER_Contact *member,
    577                                   struct GNUNET_CHAT_Context *context)
    578 {
    579   GNUNET_assert((handle) && (handle->contacts));
    580 
    581   if (!member)
    582     return GNUNET_SYSERR;
    583 
    584   struct GNUNET_ShortHashCode shorthash;
    585   util_shorthash_from_member(member, &shorthash);
    586 
    587   struct GNUNET_CHAT_Contact *contact = GNUNET_CONTAINER_multishortmap_get(
    588     handle->contacts, &shorthash
    589   );
    590 
    591   if (contact)
    592   {
    593     if ((context) && (NULL == contact->context))
    594     {
    595       contact->context = context;
    596       context->contact = member;
    597     }
    598 
    599     return GNUNET_OK;
    600   }
    601 
    602   contact = contact_create_from_member(
    603     handle, member
    604   );
    605 
    606   if (context)
    607   {
    608     contact->context = context;
    609     context->contact = member;
    610   }
    611 
    612   if (GNUNET_OK == GNUNET_CONTAINER_multishortmap_put(
    613       handle->contacts, &shorthash, contact,
    614     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
    615     return GNUNET_OK;
    616 
    617   if (context)
    618     context->contact = NULL;
    619 
    620   contact_destroy(contact);
    621   return GNUNET_SYSERR;
    622 }
    623 
    624 struct GNUNET_CHAT_CheckHandleRoomMembers
    625 {
    626   const struct GNUNET_CRYPTO_BlindablePublicKey *ignore_key;
    627   const struct GNUNET_MESSENGER_Contact *contact;
    628 };
    629 
    630 int
    631 check_handle_room_members (void* cls,
    632 			                     GNUNET_UNUSED struct GNUNET_MESSENGER_Room *room,
    633                            const struct GNUNET_MESSENGER_Contact *member)
    634 {
    635   struct GNUNET_CHAT_CheckHandleRoomMembers *check = cls;
    636 
    637   GNUNET_assert((check) && (member));
    638 
    639   const struct GNUNET_CRYPTO_BlindablePublicKey *member_key = (
    640     GNUNET_MESSENGER_contact_get_key(member)
    641   );
    642 
    643   if ((member_key) && (check->ignore_key) &&
    644       (0 == GNUNET_memcmp(member_key, check->ignore_key)))
    645     return GNUNET_YES;
    646 
    647   if (check->contact)
    648   {
    649     check->contact = NULL;
    650     return GNUNET_NO;
    651   }
    652 
    653   check->contact = member;
    654   return GNUNET_YES;
    655 }
    656 
    657 int
    658 scan_handle_room_members (void* cls,
    659 			                    GNUNET_UNUSED struct GNUNET_MESSENGER_Room *room,
    660                           const struct GNUNET_MESSENGER_Contact *member)
    661 {
    662   struct GNUNET_CHAT_Handle *handle = cls;
    663 
    664   GNUNET_assert((handle) && (member));
    665 
    666   if (GNUNET_OK == intern_provide_contact_for_member(handle, member, NULL))
    667     return GNUNET_YES;
    668   else
    669     return GNUNET_NO;
    670 }
    671 
    672 void
    673 on_monitor_namestore_record(void *cls,
    674                             GNUNET_UNUSED const
    675                             struct GNUNET_CRYPTO_BlindablePrivateKey *zone,
    676                             const char *label,
    677                             unsigned int count,
    678                             const struct GNUNET_GNSRECORD_Data *data)
    679 {
    680   struct GNUNET_CHAT_Handle *handle = cls;
    681 
    682   GNUNET_assert((handle) && (label) && (data));
    683 
    684   if (handle->destruction)
    685   {
    686     GNUNET_NAMESTORE_zone_monitor_stop(handle->monitor);
    687     handle->monitor = NULL;
    688     return;
    689   }
    690 
    691   handle_process_records(handle, label, count, data);
    692 
    693   if (handle->monitor)
    694     GNUNET_NAMESTORE_zone_monitor_next(handle->monitor, 1);
    695 }
    696 
    697 void
    698 on_pils_identity_changed(void *cls,
    699                          const struct GNUNET_HELLO_Parser *parser,
    700                          GNUNET_UNUSED const struct GNUNET_HashCode *hash)
    701 {
    702   struct GNUNET_CHAT_Handle *handle = cls;
    703 
    704   GNUNET_assert((handle) && (parser));
    705 
    706   const struct GNUNET_PeerIdentity *id = GNUNET_HELLO_parser_get_id(parser);
    707 
    708   if (!id)
    709     return;
    710 
    711   if (!handle->pid)
    712     handle->pid = GNUNET_new(struct GNUNET_PeerIdentity);
    713 
    714   GNUNET_memcpy(handle->pid, id, sizeof(struct GNUNET_PeerIdentity));
    715 }
    716 
    717 void
    718 on_handle_message_callback(void *cls);
    719 
    720 static enum GNUNET_GenericReturnValue
    721 it_context_iterate_dependencies(void *cls,
    722                                 const struct GNUNET_HashCode *key,
    723                                 void *value)
    724 {
    725   struct GNUNET_CHAT_Message *message = (struct GNUNET_CHAT_Message*) value;
    726 
    727   if ((message) && (!message->task))
    728     message->task = GNUNET_SCHEDULER_add_now(
    729       on_handle_message_callback, message
    730     );
    731 
    732   return GNUNET_YES;
    733 }
    734 
    735 void
    736 on_handle_internal_message_callback(void *cls)
    737 {
    738   struct GNUNET_CHAT_InternalMessages *internal = cls;
    739 
    740   GNUNET_assert(
    741     (internal) &&
    742     (internal->chat) &&
    743     (internal->msg) &&
    744     (internal->task)
    745   );
    746 
    747   internal->task = NULL;
    748 
    749   struct GNUNET_CHAT_Handle *handle = internal->chat;
    750   struct GNUNET_CHAT_Context *context = internal->msg->context;
    751 
    752   if (!(handle->msg_cb))
    753     return;
    754 
    755   handle->msg_cb(handle->msg_cls, context, internal->msg);
    756 }
    757 
    758 static enum GNUNET_GenericReturnValue
    759 it_invitation_update (GNUNET_UNUSED void *cls,
    760                       GNUNET_UNUSED const struct GNUNET_HashCode *key,
    761                       void *value)
    762 {
    763   struct GNUNET_CHAT_Invitation *invitation = (struct GNUNET_CHAT_Invitation*) value;
    764 
    765   if (invitation)
    766     invitation_update(invitation);
    767 
    768   return GNUNET_YES;
    769 }
    770 
    771 void
    772 on_handle_message_callback(void *cls)
    773 {
    774   struct GNUNET_CHAT_Message *message = (struct GNUNET_CHAT_Message*) cls;
    775 
    776   GNUNET_assert(
    777     (message) &&
    778 		(message->context) &&
    779 		(message->context->handle)
    780   );
    781 
    782   message->task = NULL;
    783 
    784   if (GNUNET_YES != message_has_msg(message))
    785     return;
    786 
    787   const struct GNUNET_TIME_Absolute timestamp = GNUNET_TIME_absolute_ntoh(
    788     message->msg->header.timestamp
    789   );
    790 
    791   struct GNUNET_TIME_Relative task_delay;
    792   switch (message->msg->header.kind)
    793   {
    794     case GNUNET_MESSENGER_KIND_DELETION:
    795     {
    796       const struct GNUNET_TIME_Relative delay = GNUNET_TIME_relative_ntoh(
    797 	      message->msg->body.deletion.delay
    798       );
    799 
    800       task_delay = GNUNET_TIME_absolute_get_difference(
    801         GNUNET_TIME_absolute_get(),
    802         GNUNET_TIME_absolute_add(timestamp, delay)
    803       );
    804 
    805       break;
    806     }
    807     default:
    808     {
    809       task_delay = GNUNET_TIME_relative_get_zero_();
    810       break;
    811     }
    812   }
    813 
    814   if (! GNUNET_TIME_relative_is_zero(task_delay))
    815   {
    816     message->task = GNUNET_SCHEDULER_add_delayed(
    817       task_delay,
    818       on_handle_message_callback,
    819       message
    820     );
    821 
    822     return;
    823   }
    824 
    825   struct GNUNET_CHAT_Context *context = message->context;
    826   struct GNUNET_CHAT_Handle *handle = context->handle;
    827   const struct GNUNET_MESSENGER_Contact *sender;
    828 
    829   if (GNUNET_MESSENGER_FLAG_DELETE & message->flags)
    830     goto skip_msg_handing;
    831 
    832   switch (message->msg->header.kind)
    833   {
    834     case GNUNET_MESSENGER_KIND_INVITE:
    835     {
    836       if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(context->invites, 
    837                                                                &(message->hash)))
    838         break;
    839       
    840       struct GNUNET_CHAT_Invitation *invitation = invitation_create_from_message(
    841 	      context, &(message->hash), &(message->msg->body.invite)
    842       );
    843 
    844       if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(
    845         context->invites, &(message->hash), invitation,
    846         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
    847 	      invitation_destroy(invitation);
    848       else
    849         GNUNET_CONTAINER_multihashmap_put(
    850           handle->invitations, &(invitation->key.hash), invitation,
    851           GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
    852       break;
    853     }
    854     case GNUNET_MESSENGER_KIND_FILE:
    855     {
    856       if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(context->files, 
    857                                                                &(message->hash)))
    858         break;
    859 
    860       GNUNET_CONTAINER_multihashmap_put(
    861         context->files, &(message->hash), NULL,
    862         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST
    863       );
    864 
    865       struct GNUNET_CHAT_File *file = GNUNET_CONTAINER_multihashmap_get(
    866         context->handle->files, &(message->msg->body.file.hash)
    867       );
    868 
    869       if (file)
    870 	      break;
    871 
    872       file = file_create_from_message(
    873 	      context->handle, &(message->msg->body.file)
    874       );
    875 
    876       if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(
    877           context->handle->files, &(file->hash), file,
    878           GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
    879 	      file_destroy(file);
    880       break;
    881     }
    882     case GNUNET_MESSENGER_KIND_TAG:
    883     {
    884       struct GNUNET_CHAT_InternalTagging *tagging = GNUNET_CONTAINER_multihashmap_get(
    885         context->taggings, &(message->msg->body.tag.hash));
    886       
    887       if (!tagging)
    888       {
    889         tagging = internal_tagging_create();
    890 
    891         if (!tagging)
    892           break;
    893 
    894         if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(
    895             context->taggings, &(message->msg->body.tag.hash), tagging,
    896             GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
    897         {
    898           internal_tagging_destroy(tagging);
    899           break;
    900         }
    901       }
    902 
    903       internal_tagging_add(tagging, message);
    904       break;
    905     }
    906     default:
    907       break;
    908   }
    909 
    910 skip_msg_handing:
    911   sender = GNUNET_MESSENGER_get_sender(context->room, &(message->hash));
    912 
    913   if (!sender)
    914     goto clear_dependencies;
    915 
    916   struct GNUNET_ShortHashCode shorthash;
    917   util_shorthash_from_member(sender, &shorthash);
    918 
    919   struct GNUNET_CHAT_Contact *contact = GNUNET_CONTAINER_multishortmap_get(
    920     handle->contacts, &shorthash
    921   );
    922 
    923   if (!contact)
    924     goto clear_dependencies;
    925 
    926   if (GNUNET_MESSENGER_FLAG_DELETE & message->flags)
    927     goto skip_sender_handing;
    928 
    929   switch (message->msg->header.kind)
    930   {
    931     case GNUNET_MESSENGER_KIND_JOIN:
    932     {
    933       contact_update_join(contact, context, 
    934         &(message->hash), message->flags);
    935       
    936       GNUNET_CONTAINER_multihashmap_get_multiple(
    937         handle->invitations,
    938         GNUNET_MESSENGER_room_get_key(context->room),
    939         it_invitation_update, handle);
    940       
    941       if ((GNUNET_MESSENGER_FLAG_SENT & message->flags) &&
    942           (GNUNET_MESSENGER_FLAG_RECENT & message->flags))
    943         handle_send_room_name(handle, context->room);
    944       
    945       break;
    946     }
    947     case GNUNET_MESSENGER_KIND_LEAVE:
    948     {
    949       GNUNET_CONTAINER_multihashmap_get_multiple(
    950         handle->invitations,
    951         GNUNET_MESSENGER_room_get_key(context->room),
    952         it_invitation_update, handle);
    953       
    954       break;
    955     }
    956     case GNUNET_MESSENGER_KIND_KEY:
    957     {
    958       contact_update_key(contact);
    959       break;
    960     }
    961     case GNUNET_MESSENGER_KIND_TICKET:
    962     {
    963       struct GNUNET_CHAT_InternalTickets *tickets = contact->tickets_head;
    964       while (tickets)
    965       {
    966         if (0 == strncmp(tickets->ticket->ticket.gns_name, 
    967                          message->msg->body.ticket.identifier,
    968                          sizeof(tickets->ticket->ticket.gns_name)))
    969           break;
    970 
    971         tickets = tickets->next;
    972       }
    973 
    974       if (tickets)
    975         break;
    976       
    977       tickets = GNUNET_new(
    978         struct GNUNET_CHAT_InternalTickets
    979       );
    980 
    981       if (!tickets)
    982         break;
    983 
    984       tickets->ticket = ticket_create_from_message(
    985 	      handle, contact, &(message->msg->body.ticket)
    986       );
    987 
    988       if (!tickets->ticket)
    989       {
    990         GNUNET_free(tickets);
    991         break;
    992       }
    993 
    994       GNUNET_CONTAINER_DLL_insert_tail(
    995         contact->tickets_head,
    996         contact->tickets_tail,
    997         tickets
    998       );
    999       break;
   1000     }
   1001     case GNUNET_MESSENGER_KIND_SUBSCRIBTION:
   1002     {
   1003       const struct GNUNET_ShortHashCode *sid = &(message->msg->body.subscription.discourse);
   1004       struct GNUNET_CHAT_Discourse *discourse = GNUNET_CONTAINER_multishortmap_get(
   1005         context->discourses, sid
   1006       );
   1007 
   1008       if (! discourse)
   1009       {
   1010         struct GNUNET_CHAT_DiscourseId id;
   1011         util_discourse_id_from_shorthash(sid, &id);
   1012 
   1013         discourse = discourse_create(context, &id);
   1014 
   1015         if (GNUNET_OK != GNUNET_CONTAINER_multishortmap_put(context->discourses,
   1016           sid, discourse, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   1017         {
   1018           discourse_destroy(discourse);
   1019           break;
   1020         }
   1021       }
   1022 
   1023       enum GNUNET_GenericReturnValue subscription_update = GNUNET_NO;
   1024 
   1025       if (GNUNET_MESSENGER_FLAG_SUBSCRIPTION_UNSUBSCRIBE & message->msg->body.subscription.flags)
   1026         discourse_unsubscribe(
   1027           discourse,
   1028           contact,
   1029           GNUNET_TIME_absolute_ntoh(message->msg->header.timestamp),
   1030           GNUNET_TIME_relative_ntoh(message->msg->body.subscription.time)
   1031         );
   1032       else
   1033         subscription_update = discourse_subscribe(
   1034           discourse,
   1035           contact,
   1036           GNUNET_TIME_absolute_ntoh(message->msg->header.timestamp),
   1037           GNUNET_TIME_relative_ntoh(message->msg->body.subscription.time)
   1038         );
   1039       
   1040       if (GNUNET_YES == subscription_update)
   1041         message->flags |= GNUNET_MESSENGER_FLAG_UPDATE;
   1042 
   1043       break;
   1044     }
   1045     default:
   1046       break;
   1047   }
   1048 
   1049 skip_sender_handing:
   1050   if (!(handle->msg_cb))
   1051     goto clear_dependencies;
   1052 
   1053   handle->msg_cb(handle->msg_cls, context, message);
   1054 
   1055 clear_dependencies:
   1056   GNUNET_CONTAINER_multihashmap_get_multiple(context->dependencies,
   1057                                              &(message->hash),
   1058                                              it_context_iterate_dependencies,
   1059                                              NULL);
   1060   GNUNET_CONTAINER_multihashmap_remove_all(context->dependencies,
   1061                                            &(message->hash));
   1062 }
   1063 
   1064 void
   1065 on_handle_message (void *cls,
   1066                    struct GNUNET_MESSENGER_Room *room,
   1067                    const struct GNUNET_MESSENGER_Contact *sender,
   1068                    const struct GNUNET_MESSENGER_Contact *recipient,
   1069                    const struct GNUNET_MESSENGER_Message *msg,
   1070                    const struct GNUNET_HashCode *hash,
   1071                    enum GNUNET_MESSENGER_MessageFlags flags)
   1072 {
   1073   struct GNUNET_CHAT_Handle *handle = cls;
   1074 
   1075   GNUNET_assert(
   1076     (handle) &&
   1077 		(room) &&
   1078 		(msg) &&
   1079 		(hash)
   1080   );
   1081 
   1082   if ((handle->destruction) ||
   1083       (GNUNET_OK != handle_request_context_by_room(handle, room)))
   1084     return;
   1085   
   1086   struct GNUNET_CHAT_Context *context = GNUNET_CONTAINER_multihashmap_get(
   1087     handle->contexts, GNUNET_MESSENGER_room_get_key(room)
   1088   );
   1089 
   1090   if (GNUNET_MESSENGER_KIND_MERGE == msg->header.kind)
   1091     context_request_message(context, &(msg->body.merge.previous));
   1092 
   1093   context_request_message(context, &(msg->header.previous));
   1094 
   1095   if ((GNUNET_CHAT_KIND_UNKNOWN == util_message_kind_from_kind(msg->header.kind)) ||
   1096       (GNUNET_OK != intern_provide_contact_for_member(handle, sender, NULL)))
   1097     return;
   1098 
   1099   const struct GNUNET_TIME_Absolute timestamp = GNUNET_TIME_absolute_ntoh(
   1100     msg->header.timestamp
   1101   );
   1102 
   1103   struct GNUNET_ShortHashCode shorthash;
   1104   util_shorthash_from_member(sender, &shorthash);
   1105 
   1106   struct GNUNET_CHAT_Contact *contact = GNUNET_CONTAINER_multishortmap_get(
   1107     handle->contacts, &shorthash
   1108   );
   1109 
   1110   if (flags & GNUNET_MESSENGER_FLAG_SENT)
   1111     contact->owned = GNUNET_YES;
   1112 
   1113   struct GNUNET_TIME_Absolute *time = GNUNET_CONTAINER_multishortmap_get(
   1114     context->timestamps, &shorthash
   1115   );
   1116 
   1117   if (!time)
   1118   {
   1119     time = GNUNET_new(struct GNUNET_TIME_Absolute);
   1120     *time = timestamp;
   1121 
   1122     if (GNUNET_OK != GNUNET_CONTAINER_multishortmap_put(
   1123         context->timestamps, &shorthash, time,
   1124         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   1125       GNUNET_free(time);
   1126   }
   1127   else
   1128   {
   1129     const struct GNUNET_TIME_Relative delta = GNUNET_TIME_absolute_get_difference(
   1130 	    timestamp, *time
   1131     );
   1132 
   1133     if (GNUNET_TIME_relative_is_zero(delta))
   1134       *time = timestamp;
   1135   }
   1136 
   1137   const struct GNUNET_HashCode *dependency = NULL;
   1138 
   1139   struct GNUNET_CHAT_Message *message = GNUNET_CONTAINER_multihashmap_get(
   1140     context->messages, hash
   1141   );
   1142 
   1143   if (message)
   1144   {
   1145     if (message->flags & GNUNET_MESSENGER_FLAG_DELETE)
   1146       return;
   1147 
   1148     message_update_msg (message, flags, msg);
   1149 
   1150     if (0 == (message->flags & GNUNET_MESSENGER_FLAG_UPDATE))
   1151       return;
   1152 
   1153     goto handle_callback;
   1154   }
   1155 
   1156   message = message_create_from_msg(context, hash, flags, msg);
   1157 
   1158   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(
   1159       context->messages, hash, message,
   1160       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   1161   {
   1162     message_destroy(message);
   1163     return;
   1164   }
   1165 
   1166 handle_callback:
   1167   switch (msg->header.kind)
   1168   {
   1169     case GNUNET_MESSENGER_KIND_DELETION:
   1170     {
   1171       dependency = &(msg->body.deletion.hash);
   1172       break;
   1173     }
   1174     case GNUNET_MESSENGER_KIND_TRANSCRIPT:
   1175     {
   1176       dependency = &(msg->body.transcript.hash);
   1177       break;
   1178     }
   1179     case GNUNET_MESSENGER_KIND_TAG:
   1180     {
   1181       dependency = &(msg->body.tag.hash);
   1182       break;
   1183     }
   1184     default:
   1185       break;
   1186   }
   1187 
   1188   if ((dependency) && 
   1189       (GNUNET_YES != GNUNET_CONTAINER_multihashmap_contains(context->messages, dependency)))
   1190   {
   1191     GNUNET_CONTAINER_multihashmap_put(
   1192       context->dependencies,
   1193       dependency,
   1194       message,
   1195       GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE
   1196     );
   1197 
   1198     GNUNET_MESSENGER_get_message(room, dependency);
   1199     return;
   1200   }
   1201 
   1202   on_handle_message_callback(message);
   1203 }
   1204 
   1205 int
   1206 it_destroy_handle_groups (GNUNET_UNUSED void *cls,
   1207                           GNUNET_UNUSED const struct GNUNET_HashCode *key,
   1208                           void *value)
   1209 {
   1210   GNUNET_assert(value);
   1211 
   1212   struct GNUNET_CHAT_Group *group = value;
   1213   group_destroy(group);
   1214   return GNUNET_YES;
   1215 }
   1216 
   1217 int
   1218 it_destroy_handle_contacts (GNUNET_UNUSED void *cls,
   1219                             GNUNET_UNUSED const struct GNUNET_ShortHashCode *key,
   1220                             void *value)
   1221 {
   1222   GNUNET_assert(value);
   1223 
   1224   struct GNUNET_CHAT_Contact *contact = value;
   1225   contact_destroy(contact);
   1226   return GNUNET_YES;
   1227 }
   1228 
   1229 int
   1230 it_destroy_handle_contexts (GNUNET_UNUSED void *cls,
   1231                             GNUNET_UNUSED const struct GNUNET_HashCode *key,
   1232                             void *value)
   1233 {
   1234   GNUNET_assert(value);
   1235 
   1236   struct GNUNET_CHAT_Context *context = value;
   1237   context_destroy(context);
   1238   return GNUNET_YES;
   1239 }
   1240 
   1241 int
   1242 it_destroy_handle_files (GNUNET_UNUSED void *cls,
   1243                          GNUNET_UNUSED const struct GNUNET_HashCode *key,
   1244                          void *value)
   1245 {
   1246   GNUNET_assert(value);
   1247 
   1248   struct GNUNET_CHAT_File *file = value;
   1249   file_destroy(file);
   1250   return GNUNET_YES;
   1251 }