libgnunetchat

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

gnunet_chat_handle_intern.c (30163B)


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