libgnunetchat

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

gnunet_chat_lib.c (72696B)


      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_lib.c
     23  */
     24 
     25 #include "gnunet_chat_lib.h"
     26 
     27 #include <gnunet/gnunet_common.h>
     28 #include <gnunet/gnunet_fs_service.h>
     29 #include <gnunet/gnunet_messenger_service.h>
     30 #include <gnunet/gnunet_reclaim_lib.h>
     31 #include <gnunet/gnunet_reclaim_service.h>
     32 #include <gnunet/gnunet_scheduler_lib.h>
     33 #include <gnunet/gnunet_time_lib.h>
     34 #include <gnunet/gnunet_util_lib.h>
     35 
     36 #include <libgen.h>
     37 #include <stdint.h>
     38 #include <string.h>
     39 #include <strings.h>
     40 #include <unistd.h>
     41 
     42 #define _(String) ((const char*) String)
     43 
     44 #include "gnunet_chat_contact.h"
     45 #include "gnunet_chat_context.h"
     46 #include "gnunet_chat_discourse.h"
     47 #include "gnunet_chat_file.h"
     48 #include "gnunet_chat_group.h"
     49 #include "gnunet_chat_handle.h"
     50 #include "gnunet_chat_invitation.h"
     51 #include "gnunet_chat_lobby.h"
     52 #include "gnunet_chat_message.h"
     53 #include "gnunet_chat_ticket.h"
     54 #include "gnunet_chat_util.h"
     55 
     56 #include "internal/gnunet_chat_tagging.h"
     57 
     58 #include "gnunet_chat_lib_intern.c"
     59 
     60 #define GNUNET_CHAT_VERSION_ASSERT() {\
     61   GNUNET_assert(\
     62     (GNUNET_MESSENGER_VERSION == ((GNUNET_CHAT_VERSION >> 16L) & 0xFFFFFFFFL))\
     63   );\
     64 }
     65 
     66 static const uint32_t block_anonymity_level = 1;
     67 static const uint32_t block_content_priority = 100;
     68 static const uint32_t block_replication_level = 1;
     69 
     70 struct GNUNET_CHAT_Handle*
     71 GNUNET_CHAT_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
     72 		               GNUNET_CHAT_ContextMessageCallback msg_cb, void *msg_cls)
     73 {
     74   GNUNET_CHAT_VERSION_ASSERT();
     75 
     76   if (!cfg)
     77     return NULL;
     78 
     79   return handle_create_from_config(
     80     cfg,
     81     msg_cb,
     82     msg_cls
     83   );
     84 }
     85 
     86 
     87 void
     88 GNUNET_CHAT_stop (struct GNUNET_CHAT_Handle *handle)
     89 {
     90   GNUNET_CHAT_VERSION_ASSERT();
     91 
     92   if ((!handle) || (handle->destruction))
     93     return;
     94 
     95   handle->destruction = GNUNET_SCHEDULER_add_with_priority(
     96     GNUNET_SCHEDULER_PRIORITY_URGENT,
     97     task_handle_destruction,
     98     handle
     99   );
    100 }
    101 
    102 
    103 enum GNUNET_GenericReturnValue
    104 GNUNET_CHAT_account_create (struct GNUNET_CHAT_Handle *handle,
    105 			                      const char* name)
    106 {
    107   GNUNET_CHAT_VERSION_ASSERT();
    108 
    109   if ((!handle) || (handle->destruction) || (!name))
    110     return GNUNET_SYSERR;
    111 
    112   char *low = util_get_lower(name);
    113 
    114   enum GNUNET_GenericReturnValue result;
    115   result = handle_create_account(handle, low);
    116 
    117   GNUNET_free(low);
    118   return result;
    119 }
    120 
    121 
    122 enum GNUNET_GenericReturnValue
    123 GNUNET_CHAT_account_delete(struct GNUNET_CHAT_Handle *handle,
    124 			                     const char *name)
    125 {
    126   GNUNET_CHAT_VERSION_ASSERT();
    127 
    128   if ((!handle) || (handle->destruction) || (!name))
    129     return GNUNET_SYSERR;
    130 
    131   const struct GNUNET_CHAT_Account *account;
    132   account = handle_get_account_by_name(handle, name, GNUNET_NO);
    133 
    134   if (!account)
    135     return GNUNET_SYSERR;
    136 
    137   return handle_delete_account(handle, account);
    138 }
    139 
    140 
    141 int
    142 GNUNET_CHAT_iterate_accounts (struct GNUNET_CHAT_Handle *handle,
    143                               GNUNET_CHAT_AccountCallback callback,
    144                               void *cls)
    145 {
    146   GNUNET_CHAT_VERSION_ASSERT();
    147 
    148   if ((!handle) || (handle->destruction))
    149     return GNUNET_SYSERR;
    150 
    151   int iterations = 0;
    152 
    153   struct GNUNET_CHAT_InternalAccounts *accounts = handle->accounts_head;
    154   while (accounts)
    155   {
    156     if ((!(accounts->account)) || (accounts->op))
    157       goto skip_account;
    158 
    159     iterations++;
    160 
    161     if ((callback) && (GNUNET_YES != callback(cls, handle, accounts->account)))
    162       break;
    163 
    164   skip_account:
    165     accounts = accounts->next;
    166   }
    167 
    168   return iterations;
    169 }
    170 
    171 
    172 struct GNUNET_CHAT_Account*
    173 GNUNET_CHAT_find_account (const struct GNUNET_CHAT_Handle *handle,
    174                           const char *name)
    175 {
    176   GNUNET_CHAT_VERSION_ASSERT();
    177 
    178   if ((!handle) || (handle->destruction))
    179     return NULL;
    180 
    181   return handle_get_account_by_name(handle, name, GNUNET_YES);
    182 }
    183 
    184 
    185 void
    186 GNUNET_CHAT_connect (struct GNUNET_CHAT_Handle *handle,
    187                      struct GNUNET_CHAT_Account *account)
    188 {
    189   GNUNET_CHAT_VERSION_ASSERT();
    190 
    191   if ((!handle) || (handle->destruction))
    192     return;
    193 
    194   if (handle->connection)
    195     GNUNET_SCHEDULER_cancel(handle->connection);
    196 
    197   if (handle->current == account)
    198   {
    199     handle->next = NULL;
    200     handle->connection = NULL;
    201     return;
    202   }
    203 
    204   if (handle->current)
    205   {
    206     handle->next = account;
    207     handle->connection = NULL;
    208     GNUNET_CHAT_disconnect(handle);
    209     return;
    210   }
    211 
    212   handle->next = account;
    213   handle->connection = GNUNET_SCHEDULER_add_now(
    214     task_handle_connection,
    215     handle
    216   );
    217 }
    218 
    219 
    220 void
    221 GNUNET_CHAT_disconnect (struct GNUNET_CHAT_Handle *handle)
    222 {
    223   GNUNET_CHAT_VERSION_ASSERT();
    224 
    225   if ((!handle) || (handle->destruction))
    226     return;
    227   
    228   if (handle->connection)
    229     GNUNET_SCHEDULER_cancel(handle->connection);
    230 
    231   if (!(handle->current))
    232   {
    233     handle->next = NULL;
    234     handle->connection = NULL;
    235     return;
    236   }
    237 
    238   handle->connection = GNUNET_SCHEDULER_add_now(
    239     task_handle_disconnection,
    240     handle
    241   );
    242 }
    243 
    244 
    245 struct GNUNET_CHAT_Account*
    246 GNUNET_CHAT_get_connected (const struct GNUNET_CHAT_Handle *handle)
    247 {
    248   GNUNET_CHAT_VERSION_ASSERT();
    249 
    250   if ((!handle) || (handle->destruction))
    251     return NULL;
    252 
    253   return handle->current;
    254 }
    255 
    256 
    257 enum GNUNET_GenericReturnValue
    258 GNUNET_CHAT_update (struct GNUNET_CHAT_Handle *handle)
    259 {
    260   GNUNET_CHAT_VERSION_ASSERT();
    261 
    262   if ((!handle) || (handle->destruction))
    263     return GNUNET_SYSERR;
    264 
    265   return handle_update(handle);
    266 }
    267 
    268 
    269 enum GNUNET_GenericReturnValue
    270 GNUNET_CHAT_set_name (struct GNUNET_CHAT_Handle *handle,
    271 		                  const char *name)
    272 {
    273   GNUNET_CHAT_VERSION_ASSERT();
    274 
    275   if ((!handle) || (handle->destruction))
    276     return GNUNET_SYSERR;
    277 
    278   if (!name)
    279     return GNUNET_NO;
    280 
    281   char *low = util_get_lower(name);
    282   enum GNUNET_GenericReturnValue result;
    283 
    284   if (handle->current)
    285     result = handle_rename_account(handle, handle->current, low);
    286   else
    287     result = GNUNET_OK;
    288 
    289   if (GNUNET_OK != result)
    290     return result;
    291 
    292   result = GNUNET_MESSENGER_set_name(handle->messenger, low);
    293 
    294   GNUNET_free(low);
    295   return result;
    296 }
    297 
    298 
    299 const char*
    300 GNUNET_CHAT_get_name (const struct GNUNET_CHAT_Handle *handle)
    301 {
    302   GNUNET_CHAT_VERSION_ASSERT();
    303 
    304   if ((!handle) || (handle->destruction))
    305     return NULL;
    306 
    307   return GNUNET_MESSENGER_get_name(handle->messenger);
    308 }
    309 
    310 
    311 const char*
    312 GNUNET_CHAT_get_key (const struct GNUNET_CHAT_Handle *handle)
    313 {
    314   GNUNET_CHAT_VERSION_ASSERT();
    315 
    316   if ((!handle) || (handle->destruction))
    317     return NULL;
    318 
    319   return handle->public_key;
    320 }
    321 
    322 
    323 void
    324 GNUNET_CHAT_set_attribute (struct GNUNET_CHAT_Handle *handle,
    325                            const char *name,
    326                            const char *value)
    327 {
    328   GNUNET_CHAT_VERSION_ASSERT();
    329 
    330   if ((!handle) || (handle->destruction))
    331     return;
    332 
    333   const struct GNUNET_CRYPTO_BlindablePrivateKey *key = handle_get_key(
    334     handle
    335   );
    336 
    337   if ((!key) || (!name))
    338     return;
    339 
    340   struct GNUNET_TIME_Relative rel;
    341   rel = GNUNET_TIME_relative_get_forever_();
    342 
    343   struct GNUNET_CHAT_AttributeProcess *attributes;
    344   attributes = internal_attributes_create_store(handle, name, rel);
    345 
    346   if (!attributes)
    347     return;
    348 
    349   if (value)
    350   {
    351     enum GNUNET_GenericReturnValue result;
    352     result = GNUNET_RECLAIM_attribute_string_to_value(
    353       GNUNET_RECLAIM_ATTRIBUTE_TYPE_STRING,
    354       value,
    355       &(attributes->data),
    356       &(attributes->attribute->data_size)
    357     );
    358 
    359     if (GNUNET_OK != result)
    360     {
    361       internal_attributes_destroy(attributes);
    362       return;
    363     }
    364 
    365     attributes->attribute->type = GNUNET_RECLAIM_ATTRIBUTE_TYPE_STRING;
    366     attributes->attribute->data = attributes->data;
    367   }
    368 
    369   attributes->iter = GNUNET_RECLAIM_get_attributes_start(
    370     handle->reclaim,
    371     key,
    372     cb_task_error_iterate_attribute,
    373     attributes,
    374     cb_store_attribute,
    375     attributes,
    376     cb_task_finish_iterate_attribute,
    377     attributes
    378   );
    379 }
    380 
    381 
    382 void
    383 GNUNET_CHAT_delete_attribute (struct GNUNET_CHAT_Handle *handle,
    384                               const char *name)
    385 {
    386   GNUNET_CHAT_VERSION_ASSERT();
    387 
    388   if ((!handle) || (handle->destruction))
    389     return;
    390 
    391   const struct GNUNET_CRYPTO_BlindablePrivateKey *key = handle_get_key(
    392     handle
    393   );
    394 
    395   if ((!key) || (!name))
    396     return;
    397 
    398   struct GNUNET_CHAT_AttributeProcess *attributes;
    399   attributes = internal_attributes_create(handle, name);
    400 
    401   if (!attributes)
    402     return;
    403 
    404   attributes->iter = GNUNET_RECLAIM_get_attributes_start(
    405     handle->reclaim,
    406     key,
    407     cb_task_error_iterate_attribute,
    408     attributes,
    409     cb_delete_attribute,
    410     attributes,
    411     cb_task_finish_iterate_attribute,
    412     attributes
    413   );
    414 }
    415 
    416 
    417 void
    418 GNUNET_CHAT_get_attributes (struct GNUNET_CHAT_Handle *handle,
    419                             GNUNET_CHAT_AttributeCallback callback,
    420                             void *cls)
    421 {
    422   GNUNET_CHAT_VERSION_ASSERT();
    423 
    424   if ((!handle) || (handle->destruction))
    425     return;
    426 
    427   const struct GNUNET_CRYPTO_BlindablePrivateKey *key = handle_get_key(
    428     handle
    429   );
    430 
    431   if (!key)
    432     return;
    433 
    434   struct GNUNET_CHAT_AttributeProcess *attributes;
    435   attributes = internal_attributes_create(handle, NULL);
    436 
    437   if (!attributes)
    438     return;
    439 
    440   attributes->callback = callback;
    441   attributes->closure = cls;
    442 
    443   attributes->iter = GNUNET_RECLAIM_get_attributes_start(
    444     handle->reclaim,
    445     key,
    446     cb_task_error_iterate_attribute,
    447     attributes,
    448     cb_iterate_attribute,
    449     attributes,
    450     cb_task_finish_iterate_attribute,
    451     attributes
    452   );
    453 }
    454 
    455 
    456 void
    457 GNUNET_CHAT_share_attribute_with (struct GNUNET_CHAT_Handle *handle,
    458                                   struct GNUNET_CHAT_Contact *contact,
    459                                   const char *name)
    460 {
    461   GNUNET_CHAT_VERSION_ASSERT();
    462 
    463   if ((!handle) || (handle->destruction) || (!contact))
    464     return;
    465 
    466   const struct GNUNET_CRYPTO_BlindablePrivateKey *key = handle_get_key(
    467     handle
    468   );
    469 
    470   const struct GNUNET_CRYPTO_BlindablePublicKey *pubkey = contact_get_key(
    471     contact
    472   );
    473 
    474   if ((!key) || (!pubkey) || (!name))
    475     return;
    476 
    477   struct GNUNET_CHAT_AttributeProcess *attributes;
    478   attributes = internal_attributes_create_share(handle, contact, name);
    479 
    480   if (!attributes)
    481     return;
    482 
    483   attributes->iter = GNUNET_RECLAIM_get_attributes_start(
    484     handle->reclaim,
    485     key,
    486     cb_task_error_iterate_attribute,
    487     attributes,
    488     cb_share_attribute,
    489     attributes,
    490     cb_task_finish_iterate_attribute,
    491     attributes
    492   );
    493 }
    494 
    495 
    496 void
    497 GNUNET_CHAT_unshare_attribute_from (struct GNUNET_CHAT_Handle *handle,
    498                                     struct GNUNET_CHAT_Contact *contact,
    499                                     const char *name)
    500 {
    501   GNUNET_CHAT_VERSION_ASSERT();
    502 
    503   if ((!handle) || (handle->destruction) || (!contact))
    504     return;
    505 
    506   const struct GNUNET_CRYPTO_BlindablePrivateKey *key = handle_get_key(
    507     handle
    508   );
    509 
    510   if ((!key) || (!name))
    511     return;
    512 
    513   struct GNUNET_CHAT_TicketProcess *tickets;
    514   tickets = internal_tickets_create(handle, contact, name);
    515 
    516   if (!tickets)
    517     return;
    518 
    519   tickets->iter = GNUNET_RECLAIM_ticket_iteration_start(
    520     handle->reclaim,
    521     key,
    522     cb_task_error_iterate_ticket,
    523     tickets,
    524     cb_iterate_ticket_check,
    525     tickets,
    526     cb_task_finish_iterate_ticket,
    527     tickets
    528   );
    529 }
    530 
    531 
    532 void
    533 GNUNET_CHAT_get_shared_attributes (struct GNUNET_CHAT_Handle *handle,
    534                                    struct GNUNET_CHAT_Contact *contact,
    535                                    GNUNET_CHAT_ContactAttributeCallback callback,
    536                                    void *cls)
    537 {
    538   GNUNET_CHAT_VERSION_ASSERT();
    539 
    540   if ((!handle) || (handle->destruction) || (!contact))
    541     return;
    542 
    543   const struct GNUNET_CRYPTO_BlindablePrivateKey *key = handle_get_key(
    544     handle
    545   );
    546 
    547   if (!key)
    548     return;
    549 
    550   struct GNUNET_CHAT_TicketProcess *tickets;
    551   tickets = internal_tickets_create(handle, contact, NULL);
    552 
    553   if (!tickets)
    554     return;
    555 
    556   tickets->callback = callback;
    557   tickets->closure = cls;
    558 
    559   tickets->iter = GNUNET_RECLAIM_ticket_iteration_start(
    560     handle->reclaim,
    561     key,
    562     cb_task_error_iterate_ticket,
    563     tickets,
    564     cb_iterate_ticket,
    565     tickets,
    566     cb_task_finish_iterate_ticket,
    567     tickets
    568   );
    569 }
    570 
    571 
    572 struct GNUNET_CHAT_Uri*
    573 GNUNET_CHAT_uri_parse (const char *uri,
    574 		                   char **emsg)
    575 {
    576   GNUNET_CHAT_VERSION_ASSERT();
    577 
    578   if (!uri)
    579     return NULL;
    580 
    581   return uri_parse_from_string(uri, emsg);
    582 }
    583 
    584 
    585 char*
    586 GNUNET_CHAT_uri_to_string (const struct GNUNET_CHAT_Uri *uri)
    587 {
    588   GNUNET_CHAT_VERSION_ASSERT();
    589 
    590   if (!uri)
    591     return NULL;
    592 
    593   return uri_to_string(uri);
    594 }
    595 
    596 
    597 enum GNUNET_CHAT_UriType
    598 GNUNET_CHAT_uri_get_type (const struct GNUNET_CHAT_Uri *uri)
    599 {
    600   GNUNET_CHAT_VERSION_ASSERT();
    601 
    602   if (!uri)
    603     return GNUNET_CHAT_URI_TYPE_UNKNOWN;
    604 
    605   return uri->type;
    606 }
    607 
    608 
    609 void
    610 GNUNET_CHAT_uri_destroy (struct GNUNET_CHAT_Uri *uri)
    611 {
    612   GNUNET_CHAT_VERSION_ASSERT();
    613 
    614   if (!uri)
    615     return;
    616 
    617   uri_destroy(uri);
    618 }
    619 
    620 
    621 struct GNUNET_CHAT_Lobby*
    622 GNUNET_CHAT_lobby_open (struct GNUNET_CHAT_Handle *handle,
    623                         unsigned int delay,
    624                         GNUNET_CHAT_LobbyCallback callback,
    625                         void *cls)
    626 {
    627   GNUNET_CHAT_VERSION_ASSERT();
    628 
    629   if ((!handle) || (handle->destruction))
    630     return NULL;
    631 
    632   struct GNUNET_TIME_Relative rel = GNUNET_TIME_relative_multiply(
    633     GNUNET_TIME_relative_get_second_(), delay
    634   );
    635 
    636   struct GNUNET_CHAT_InternalLobbies *lobbies = GNUNET_new(
    637     struct GNUNET_CHAT_InternalLobbies
    638   );
    639 
    640   lobbies->lobby = lobby_create(handle);
    641 
    642   GNUNET_CONTAINER_DLL_insert(
    643     handle->lobbies_head,
    644     handle->lobbies_tail,
    645     lobbies
    646   );
    647 
    648   lobby_open(lobbies->lobby, rel, callback, cls);
    649 
    650   return lobbies->lobby;
    651 }
    652 
    653 
    654 void
    655 GNUNET_CHAT_lobby_close (struct GNUNET_CHAT_Lobby *lobby)
    656 {
    657   GNUNET_CHAT_VERSION_ASSERT();
    658 
    659   if ((!lobby) || (lobby->destruction))
    660     return;
    661 
    662   lobby->destruction = GNUNET_SCHEDULER_add_now(
    663     task_lobby_destruction,
    664     lobby
    665   );
    666 }
    667 
    668 
    669 void
    670 GNUNET_CHAT_lobby_join (struct GNUNET_CHAT_Handle *handle,
    671 			                  const struct GNUNET_CHAT_Uri *uri)
    672 {
    673   GNUNET_CHAT_VERSION_ASSERT();
    674 
    675   if ((!handle) || (handle->destruction) || (!(handle->gns)) ||
    676       (!uri) || (GNUNET_CHAT_URI_TYPE_CHAT != uri->type))
    677     return;
    678 
    679   struct GNUNET_CHAT_UriLookups *lookups = GNUNET_new(
    680     struct GNUNET_CHAT_UriLookups
    681   );
    682 
    683   lookups->handle = handle;
    684   lookups->uri = uri_create_chat(
    685     &(uri->chat.zone),
    686     uri->chat.label
    687   );
    688 
    689   lookups->request = GNUNET_GNS_lookup(
    690     handle->gns,
    691     lookups->uri->chat.label,
    692     &(uri->chat.zone),
    693     GNUNET_GNSRECORD_TYPE_MESSENGER_ROOM_ENTRY,
    694     GNUNET_GNS_LO_DEFAULT,
    695     cb_lobby_lookup,
    696     lookups
    697   );
    698 
    699   GNUNET_CONTAINER_DLL_insert(
    700     handle->lookups_head,
    701     handle->lookups_tail,
    702     lookups
    703   );
    704 }
    705 
    706 
    707 struct GNUNET_CHAT_File*
    708 GNUNET_CHAT_request_file (struct GNUNET_CHAT_Handle *handle,
    709                           const struct GNUNET_CHAT_Uri *uri)
    710 {
    711   GNUNET_CHAT_VERSION_ASSERT();
    712 
    713   if ((!handle) || (handle->destruction) || 
    714       (!uri) || (GNUNET_CHAT_URI_TYPE_FS != uri->type))
    715     return NULL;
    716   
    717   if (!GNUNET_FS_uri_test_chk(uri->fs.uri))
    718     return NULL;
    719 
    720   const struct GNUNET_HashCode *hash = GNUNET_FS_uri_chk_get_file_hash(
    721     uri->fs.uri
    722   );
    723 
    724   if (!hash)
    725     return NULL;
    726 
    727   struct GNUNET_CHAT_File *file = GNUNET_CONTAINER_multihashmap_get(
    728     handle->files,
    729     hash
    730   );
    731 
    732   if (file)
    733     return file;
    734 
    735   file = file_create_from_chk_uri(handle, uri->fs.uri);
    736 
    737   if (!file)
    738     return NULL;
    739 
    740   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(handle->files, hash, file, 
    741       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
    742   {
    743     file_destroy(file);
    744     file = NULL;
    745   }
    746   
    747   return file;
    748 }
    749 
    750 
    751 struct GNUNET_CHAT_File*
    752 GNUNET_CHAT_upload_file (struct GNUNET_CHAT_Handle *handle,
    753                          const char *path,
    754                          GNUNET_CHAT_FileUploadCallback callback,
    755                          void *cls)
    756 {
    757   GNUNET_CHAT_VERSION_ASSERT();
    758 
    759   if ((!handle) || (handle->destruction) ||
    760       (!path))
    761     return NULL;
    762   
    763   struct GNUNET_HashCode hash;
    764   if (GNUNET_OK != util_hash_file(path, &hash))
    765     return NULL;
    766 
    767   char *filename = handle_create_file_path(
    768     handle, &hash
    769   );
    770 
    771   if (!filename)
    772     return NULL;
    773 
    774   struct GNUNET_CHAT_File *file = GNUNET_CONTAINER_multihashmap_get(
    775     handle->files,
    776     &hash
    777   );
    778 
    779   if (file)
    780     goto file_binding;
    781 
    782   if ((GNUNET_YES == GNUNET_DISK_file_test(filename)) ||
    783       (GNUNET_OK != GNUNET_DISK_directory_create_for_file(filename)) ||
    784       (GNUNET_OK != GNUNET_DISK_file_copy(path, filename)))
    785   {
    786     GNUNET_free(filename);
    787     return NULL;
    788   }
    789 
    790   char* p = GNUNET_strdup(path);
    791 
    792   file = file_create_from_disk(
    793     handle,
    794     basename(p),
    795     &hash,
    796     NULL
    797   );
    798 
    799   GNUNET_free(p);
    800 
    801   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(
    802       handle->files, &hash, file,
    803       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
    804   {
    805     file_destroy(file);
    806     GNUNET_free(filename);
    807     return NULL;
    808   }
    809 
    810   struct GNUNET_FS_BlockOptions bo;
    811 
    812   bo.anonymity_level = block_anonymity_level;
    813   bo.content_priority = block_content_priority;
    814   bo.replication_level = block_replication_level;
    815   bo.expiration_time = GNUNET_TIME_absolute_get_forever_();
    816 
    817   struct GNUNET_FS_FileInformation* fi = GNUNET_FS_file_information_create_from_file(
    818     handle->fs,
    819     file,
    820     filename,
    821     NULL,
    822     file->meta,
    823     GNUNET_YES,
    824     &bo
    825   );
    826 
    827   file->publish = GNUNET_FS_publish_start(
    828     handle->fs, fi,
    829     NULL, NULL, NULL,
    830     GNUNET_FS_PUBLISH_OPTION_NONE
    831   );
    832 
    833   if (file->publish)
    834     file->status |= GNUNET_CHAT_FILE_STATUS_PUBLISH;
    835 
    836   GNUNET_free(filename);
    837 
    838 file_binding:
    839   file_bind_upload(file, NULL, callback, cls);
    840   return file;
    841 }
    842 
    843 
    844 int
    845 GNUNET_CHAT_iterate_files (struct GNUNET_CHAT_Handle *handle,
    846                            GNUNET_CHAT_FileCallback callback,
    847                            void *cls)
    848 {
    849   GNUNET_CHAT_VERSION_ASSERT();
    850 
    851   if ((!handle) || (handle->destruction))
    852     return GNUNET_SYSERR;
    853 
    854   struct GNUNET_CHAT_IterateFiles it;
    855   it.handle = handle;
    856   it.cb = callback;
    857   it.cls = cls;
    858 
    859   return GNUNET_CONTAINER_multihashmap_iterate(
    860     handle->files,
    861     it_iterate_files,
    862     &it
    863   );
    864 }
    865 
    866 
    867 int
    868 GNUNET_CHAT_context_iterate_discourses (struct GNUNET_CHAT_Context *context,
    869                                         GNUNET_CHAT_DiscourseCallback callback,
    870                                         void *cls)
    871 {
    872   GNUNET_CHAT_VERSION_ASSERT();
    873 
    874   if ((!context) || (!(context->discourses)))
    875     return GNUNET_SYSERR;
    876 
    877   struct GNUNET_CHAT_ContextIterateDiscourses it;
    878   it.context = context;
    879   it.cb = callback;
    880   it.cls = cls;
    881 
    882   return GNUNET_CONTAINER_multishortmap_iterate(
    883     context->discourses,
    884     it_context_iterate_discourses,
    885     &it
    886   );
    887 }
    888 
    889 
    890 void
    891 GNUNET_CHAT_set_user_pointer (struct GNUNET_CHAT_Handle *handle,
    892 			                        void *user_pointer)
    893 {
    894   GNUNET_CHAT_VERSION_ASSERT();
    895 
    896   if ((!handle) || (handle->destruction))
    897     return;
    898 
    899   handle->user_pointer = user_pointer;
    900 }
    901 
    902 
    903 void*
    904 GNUNET_CHAT_get_user_pointer (const struct GNUNET_CHAT_Handle *handle)
    905 {
    906   GNUNET_CHAT_VERSION_ASSERT();
    907 
    908   if ((!handle) || (handle->destruction))
    909     return NULL;
    910 
    911   return handle->user_pointer;
    912 }
    913 
    914 
    915 int
    916 GNUNET_CHAT_iterate_contacts (struct GNUNET_CHAT_Handle *handle,
    917                               GNUNET_CHAT_ContactCallback callback,
    918                               void *cls)
    919 {
    920   GNUNET_CHAT_VERSION_ASSERT();
    921 
    922   if ((!handle) || (handle->destruction) || (!(handle->contacts)))
    923     return GNUNET_SYSERR;
    924 
    925   struct GNUNET_CHAT_HandleIterateContacts it;
    926   it.handle = handle;
    927   it.cb = callback;
    928   it.cls = cls;
    929 
    930   return GNUNET_CONTAINER_multishortmap_iterate(
    931     handle->contacts, it_handle_iterate_contacts, &it
    932   );
    933 }
    934 
    935 
    936 struct GNUNET_CHAT_Contact*
    937 GNUNET_CHAT_get_own_contact (struct GNUNET_CHAT_Handle *handle)
    938 {
    939   GNUNET_CHAT_VERSION_ASSERT();
    940 
    941   if (!(handle->own_contact))
    942     GNUNET_CHAT_iterate_contacts (handle, it_handle_find_own_contact, NULL);
    943 
    944   return handle->own_contact;
    945 }
    946 
    947 
    948 const char*
    949 GNUNET_CHAT_account_get_name (const struct GNUNET_CHAT_Account *account)
    950 {
    951   GNUNET_CHAT_VERSION_ASSERT();
    952 
    953   if (!account)
    954     return NULL;
    955 
    956   return account_get_name(account);
    957 }
    958 
    959 
    960 void
    961 GNUNET_CHAT_account_get_attributes (struct GNUNET_CHAT_Handle *handle,
    962                                     struct GNUNET_CHAT_Account *account,
    963                                     GNUNET_CHAT_AccountAttributeCallback callback,
    964                                     void *cls)
    965 {
    966   GNUNET_CHAT_VERSION_ASSERT();
    967 
    968   if ((!handle) || (handle->destruction) || (!account))
    969     return;
    970 
    971   const struct GNUNET_CRYPTO_BlindablePrivateKey *key = account_get_key(
    972     account
    973   );
    974 
    975   if (!key)
    976     return;
    977 
    978   struct GNUNET_CHAT_AttributeProcess *attributes;
    979   attributes = internal_attributes_create_request(handle, account);
    980 
    981   if (!attributes)
    982     return;
    983 
    984   attributes->account_callback = callback;
    985   attributes->closure = cls;
    986 
    987   attributes->iter = GNUNET_RECLAIM_get_attributes_start(
    988     handle->reclaim,
    989     key,
    990     cb_task_error_iterate_attribute,
    991     attributes,
    992     cb_iterate_attribute,
    993     attributes,
    994     cb_task_finish_iterate_attribute,
    995     attributes
    996   );
    997 }
    998 
    999 
   1000 void
   1001 GNUNET_CHAT_account_set_user_pointer (struct GNUNET_CHAT_Account *account,
   1002 				                              void *user_pointer)
   1003 {
   1004   GNUNET_CHAT_VERSION_ASSERT();
   1005 
   1006   if (!account)
   1007     return;
   1008 
   1009   account->user_pointer = user_pointer;
   1010 }
   1011 
   1012 
   1013 void*
   1014 GNUNET_CHAT_account_get_user_pointer (const struct GNUNET_CHAT_Account *account)
   1015 {
   1016   GNUNET_CHAT_VERSION_ASSERT();
   1017 
   1018   if (!account)
   1019     return NULL;
   1020 
   1021   return account->user_pointer;
   1022 }
   1023 
   1024 
   1025 struct GNUNET_CHAT_Group *
   1026 GNUNET_CHAT_group_create (struct GNUNET_CHAT_Handle *handle,
   1027                           const char* topic)
   1028 {
   1029   GNUNET_CHAT_VERSION_ASSERT();
   1030 
   1031   if ((!handle) || (handle->destruction) ||
   1032       (!(handle->groups)) || (!(handle->contexts)))
   1033     return NULL;
   1034 
   1035   union GNUNET_MESSENGER_RoomKey key;
   1036   GNUNET_MESSENGER_create_room_key(
   1037     &key,
   1038     topic,
   1039     topic? GNUNET_YES : GNUNET_NO,
   1040     GNUNET_YES,
   1041     GNUNET_NO
   1042   );
   1043 
   1044   if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(handle->contexts, &(key.hash)))
   1045     return NULL;
   1046 
   1047   struct GNUNET_MESSENGER_Room *room = GNUNET_MESSENGER_open_room(
   1048     handle->messenger, &key
   1049   );
   1050 
   1051   if (!room)
   1052     return NULL;
   1053 
   1054   struct GNUNET_CHAT_Context *context = context_create_from_room(handle, room);
   1055   context->type = GNUNET_CHAT_CONTEXT_TYPE_GROUP;
   1056 
   1057   util_set_name_field(topic, &(context->topic));
   1058 
   1059   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(
   1060       handle->contexts, &(key.hash), context,
   1061       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   1062     goto destroy_context;
   1063 
   1064   struct GNUNET_CHAT_Group *group = group_create_from_context(handle, context);
   1065 
   1066   if (context->topic)
   1067     group_publish(group);
   1068 
   1069   if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(
   1070       handle->groups, &(key.hash), group,
   1071       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   1072   {
   1073     context_write_records(context);
   1074     return group;
   1075   }
   1076 
   1077   group_destroy(group);
   1078 
   1079   GNUNET_CONTAINER_multihashmap_remove(handle->contexts, &(key.hash), context);
   1080 
   1081 destroy_context:
   1082   context_destroy(context);
   1083   return NULL;
   1084 }
   1085 
   1086 
   1087 int
   1088 GNUNET_CHAT_iterate_groups (struct GNUNET_CHAT_Handle *handle,
   1089                             GNUNET_CHAT_GroupCallback callback,
   1090                             void *cls)
   1091 {
   1092   GNUNET_CHAT_VERSION_ASSERT();
   1093 
   1094   if ((!handle) || (handle->destruction) || (!(handle->groups)))
   1095     return GNUNET_SYSERR;
   1096 
   1097   struct GNUNET_CHAT_HandleIterateGroups it;
   1098   it.handle = handle;
   1099   it.cb = callback;
   1100   it.cls = cls;
   1101 
   1102   return GNUNET_CONTAINER_multihashmap_iterate(
   1103     handle->groups, it_handle_iterate_groups, &it
   1104   );
   1105 }
   1106 
   1107 
   1108 void
   1109 GNUNET_CHAT_contact_delete (struct GNUNET_CHAT_Contact *contact)
   1110 {
   1111   GNUNET_CHAT_VERSION_ASSERT();
   1112 
   1113   if ((!contact) || (contact->destruction))
   1114     return;
   1115 
   1116   if (contact->context)
   1117     contact->context->deleted = GNUNET_YES;
   1118 
   1119   contact->destruction = GNUNET_SCHEDULER_add_now(
   1120     task_contact_destruction,
   1121     contact
   1122   );
   1123 }
   1124 
   1125 
   1126 void
   1127 GNUNET_CHAT_contact_set_name (struct GNUNET_CHAT_Contact *contact,
   1128 			                        const char *name)
   1129 {
   1130   GNUNET_CHAT_VERSION_ASSERT();
   1131 
   1132   if ((!contact) || (!(contact->context)) ||
   1133       (contact->context->topic))
   1134     return;
   1135 
   1136   context_update_nick(contact->context, name);
   1137 
   1138   if (contact->context->room)
   1139     context_write_records(contact->context);
   1140 }
   1141 
   1142 
   1143 const char*
   1144 GNUNET_CHAT_contact_get_name (const struct GNUNET_CHAT_Contact *contact)
   1145 {
   1146   GNUNET_CHAT_VERSION_ASSERT();
   1147 
   1148   if (!contact)
   1149     return NULL;
   1150 
   1151   if ((contact->context) && (! contact->context->topic) &&
   1152       (contact->context->nick))
   1153     return contact->context->nick;
   1154 
   1155   return GNUNET_MESSENGER_contact_get_name(contact->member);
   1156 }
   1157 
   1158 
   1159 const char*
   1160 GNUNET_CHAT_contact_get_key (const struct GNUNET_CHAT_Contact *contact)
   1161 {
   1162   GNUNET_CHAT_VERSION_ASSERT();
   1163 
   1164   if (!contact)
   1165     return NULL;
   1166 
   1167   return contact->public_key;
   1168 }
   1169 
   1170 
   1171 struct GNUNET_CHAT_Context*
   1172 GNUNET_CHAT_contact_get_context (struct GNUNET_CHAT_Contact *contact)
   1173 {
   1174   GNUNET_CHAT_VERSION_ASSERT();
   1175 
   1176   if (!contact)
   1177     return NULL;
   1178 
   1179   if (contact->context)
   1180     return contact->context;
   1181 
   1182   struct GNUNET_CHAT_Context *context = contact_find_context(
   1183     contact,
   1184     GNUNET_NO
   1185   );
   1186 
   1187   if ((context) && (GNUNET_CHAT_CONTEXT_TYPE_CONTACT == context->type))
   1188     goto attach_return;
   1189 
   1190   context = context_create_from_contact(contact->handle, contact->member);
   1191 
   1192 attach_return:
   1193   if (context)
   1194     contact->context = context;
   1195 
   1196   return context;
   1197 }
   1198 
   1199 
   1200 void
   1201 GNUNET_CHAT_contact_set_user_pointer (struct GNUNET_CHAT_Contact *contact,
   1202 				                              void *user_pointer)
   1203 {
   1204   GNUNET_CHAT_VERSION_ASSERT();
   1205 
   1206   if (!contact)
   1207     return;
   1208 
   1209   contact->user_pointer = user_pointer;
   1210 }
   1211 
   1212 
   1213 void*
   1214 GNUNET_CHAT_contact_get_user_pointer (const struct GNUNET_CHAT_Contact *contact)
   1215 {
   1216   GNUNET_CHAT_VERSION_ASSERT();
   1217 
   1218   if (!contact)
   1219     return NULL;
   1220 
   1221   return contact->user_pointer;
   1222 }
   1223 
   1224 
   1225 enum GNUNET_GenericReturnValue
   1226 GNUNET_CHAT_contact_is_owned (const struct GNUNET_CHAT_Contact *contact)
   1227 {
   1228   GNUNET_CHAT_VERSION_ASSERT();
   1229 
   1230   if (!contact)
   1231     return GNUNET_SYSERR;
   1232 
   1233   return contact->owned;
   1234 }
   1235 
   1236 
   1237 void
   1238 GNUNET_CHAT_contact_set_blocked (struct GNUNET_CHAT_Contact *contact,
   1239                                  enum GNUNET_GenericReturnValue blocked)
   1240 {
   1241   GNUNET_CHAT_VERSION_ASSERT();
   1242 
   1243   if (!contact)
   1244     return;
   1245 
   1246   struct GNUNET_CHAT_ContactIterateContexts it;
   1247   it.contact = contact;
   1248   it.tag = NULL;
   1249   
   1250   if (GNUNET_NO == blocked)
   1251     it.cb = contact_untag;
   1252   else if (GNUNET_YES == blocked)
   1253     it.cb = contact_tag;
   1254   else
   1255     return;
   1256 
   1257   GNUNET_CONTAINER_multihashmap_iterate(
   1258     contact->joined,
   1259     it_contact_iterate_contexts,
   1260     &it
   1261   );
   1262 }
   1263 
   1264 
   1265 enum GNUNET_GenericReturnValue
   1266 GNUNET_CHAT_contact_is_blocked (const struct GNUNET_CHAT_Contact *contact)
   1267 {
   1268   GNUNET_CHAT_VERSION_ASSERT();
   1269 
   1270   if (!contact)
   1271     return GNUNET_SYSERR;
   1272 
   1273   return contact_is_tagged(contact, NULL, NULL);
   1274 }
   1275 
   1276 
   1277 void
   1278 GNUNET_CHAT_contact_tag (struct GNUNET_CHAT_Contact *contact,
   1279                          const char *tag)
   1280 {
   1281   GNUNET_CHAT_VERSION_ASSERT();
   1282 
   1283   if ((!contact) || (!tag) || (!tag[0]))
   1284     return;
   1285 
   1286   struct GNUNET_CHAT_ContactIterateContexts it;
   1287   it.contact = contact;
   1288   it.tag = tag;
   1289   it.cb = contact_tag;
   1290 
   1291   GNUNET_CONTAINER_multihashmap_iterate(
   1292     contact->joined,
   1293     it_contact_iterate_contexts,
   1294     &it
   1295   );
   1296 }
   1297 
   1298 
   1299 void
   1300 GNUNET_CHAT_contact_untag (struct GNUNET_CHAT_Contact *contact,
   1301                            const char *tag)
   1302 {
   1303   GNUNET_CHAT_VERSION_ASSERT();
   1304 
   1305   if ((!contact) || (!tag) || (!tag[0]))
   1306     return;
   1307 
   1308   struct GNUNET_CHAT_ContactIterateContexts it;
   1309   it.contact = contact;
   1310   it.tag = tag;
   1311   it.cb = contact_untag;
   1312 
   1313   GNUNET_CONTAINER_multihashmap_iterate(
   1314     contact->joined,
   1315     it_contact_iterate_contexts,
   1316     &it
   1317   );
   1318 }
   1319 
   1320 
   1321 enum GNUNET_GenericReturnValue
   1322 GNUNET_CHAT_contact_is_tagged (const struct GNUNET_CHAT_Contact *contact,
   1323                                const char *tag)
   1324 {
   1325   GNUNET_CHAT_VERSION_ASSERT();
   1326 
   1327   if ((!contact) || (!tag) || (!tag[0]))
   1328     return GNUNET_SYSERR;
   1329 
   1330   return contact_is_tagged(contact, NULL, tag);
   1331 }
   1332 
   1333 
   1334 int
   1335 GNUNET_CHAT_contact_iterate_tags (struct GNUNET_CHAT_Contact *contact,
   1336                                   GNUNET_CHAT_ContactTagCallback callback,
   1337                                   void *cls)
   1338 {
   1339   GNUNET_CHAT_VERSION_ASSERT();
   1340 
   1341   if (!contact)
   1342     return GNUNET_SYSERR;
   1343 
   1344   return contact_iterate_tags(
   1345     contact,
   1346     NULL,
   1347     callback,
   1348     cls
   1349   );
   1350 }
   1351 
   1352 
   1353 void
   1354 GNUNET_CHAT_contact_get_attributes (struct GNUNET_CHAT_Contact *contact,
   1355                                     GNUNET_CHAT_ContactAttributeCallback callback,
   1356                                     void *cls)
   1357 {
   1358   GNUNET_CHAT_VERSION_ASSERT();
   1359 
   1360   if (!contact)
   1361     return;
   1362 
   1363   struct GNUNET_CHAT_InternalTickets *tickets;
   1364   tickets = contact->tickets_head;
   1365   
   1366   while (tickets)
   1367   {
   1368     ticket_consume(
   1369       tickets->ticket,
   1370       callback,
   1371       cls
   1372     );
   1373 
   1374     tickets = tickets->next;
   1375   }
   1376 }
   1377 
   1378 
   1379 enum GNUNET_GenericReturnValue
   1380 GNUNET_CHAT_group_leave (struct GNUNET_CHAT_Group *group)
   1381 {
   1382   GNUNET_CHAT_VERSION_ASSERT();
   1383 
   1384   if ((!group) || (group->destruction))
   1385     return GNUNET_SYSERR;
   1386 
   1387   group->context->deleted = GNUNET_YES;
   1388   group->destruction = GNUNET_SCHEDULER_add_now(
   1389     task_group_destruction,
   1390     group
   1391   );
   1392 
   1393   return GNUNET_OK;
   1394 }
   1395 
   1396 
   1397 void
   1398 GNUNET_CHAT_group_set_name (struct GNUNET_CHAT_Group *group,
   1399 			                      const char *name)
   1400 {
   1401   GNUNET_CHAT_VERSION_ASSERT();
   1402 
   1403   if ((!group) || (!(group->context)))
   1404     return;
   1405 
   1406   context_update_nick(group->context, name);
   1407 
   1408   if (group->context->room)
   1409     context_write_records(group->context);
   1410 }
   1411 
   1412 
   1413 const char*
   1414 GNUNET_CHAT_group_get_name (const struct GNUNET_CHAT_Group *group)
   1415 {
   1416   GNUNET_CHAT_VERSION_ASSERT();
   1417 
   1418   if ((!group) || (!(group->context)))
   1419     return NULL;
   1420 
   1421   if (group->context->nick)
   1422     return group->context->nick;
   1423 
   1424   return group->context->topic;
   1425 }
   1426 
   1427 
   1428 void
   1429 GNUNET_CHAT_group_set_user_pointer (struct GNUNET_CHAT_Group *group,
   1430 				                            void *user_pointer)
   1431 {
   1432   GNUNET_CHAT_VERSION_ASSERT();
   1433 
   1434   if (!group)
   1435     return;
   1436 
   1437   group->user_pointer = user_pointer;
   1438 }
   1439 
   1440 
   1441 void*
   1442 GNUNET_CHAT_group_get_user_pointer (const struct GNUNET_CHAT_Group *group)
   1443 {
   1444   GNUNET_CHAT_VERSION_ASSERT();
   1445 
   1446   if (!group)
   1447     return NULL;
   1448 
   1449   return group->user_pointer;
   1450 }
   1451 
   1452 
   1453 enum GNUNET_GenericReturnValue
   1454 GNUNET_CHAT_group_invite_contact (struct GNUNET_CHAT_Group *group,
   1455 				                          struct GNUNET_CHAT_Contact *contact)
   1456 {
   1457   GNUNET_CHAT_VERSION_ASSERT();
   1458 
   1459   if ((!group) || (!contact) || (!contact->member))
   1460     return GNUNET_SYSERR;
   1461 
   1462   struct GNUNET_CHAT_Context *context = contact_find_context(
   1463     contact,
   1464     GNUNET_YES
   1465   );
   1466 
   1467   if (!context)
   1468     return GNUNET_SYSERR;
   1469 
   1470   union GNUNET_MESSENGER_RoomKey key;
   1471   GNUNET_memcpy(
   1472     &(key.hash),
   1473     GNUNET_MESSENGER_room_get_key(group->context->room),
   1474     sizeof(key.hash)
   1475   );
   1476 
   1477   handle_send_room_name(group->handle, GNUNET_MESSENGER_open_room(
   1478     group->handle->messenger, &key
   1479   ));
   1480 
   1481   struct GNUNET_MESSENGER_Message msg;
   1482   memset(&msg, 0, sizeof(msg));
   1483 
   1484   msg.header.kind = GNUNET_MESSENGER_KIND_INVITE;
   1485   GNUNET_CRYPTO_get_peer_identity(group->handle->cfg, &(msg.body.invite.door));
   1486   GNUNET_memcpy(&(msg.body.invite.key), &key, sizeof(msg.body.invite.key));
   1487 
   1488   GNUNET_MESSENGER_send_message(context->room, &msg, contact->member);
   1489   return GNUNET_OK;
   1490 }
   1491 
   1492 
   1493 int
   1494 GNUNET_CHAT_group_iterate_contacts (struct GNUNET_CHAT_Group *group,
   1495                                     GNUNET_CHAT_GroupContactCallback callback,
   1496                                     void *cls)
   1497 {
   1498   GNUNET_CHAT_VERSION_ASSERT();
   1499 
   1500   if (!group)
   1501     return GNUNET_SYSERR;
   1502 
   1503   struct GNUNET_CHAT_GroupIterateContacts it;
   1504   it.group = group;
   1505   it.cb = callback;
   1506   it.cls = cls;
   1507 
   1508   return GNUNET_MESSENGER_iterate_members(
   1509     group->context->room, it_group_iterate_contacts, &it
   1510   );
   1511 }
   1512 
   1513 
   1514 void
   1515 GNUNET_CHAT_member_set_user_pointer (struct GNUNET_CHAT_Group *group,
   1516                                      const struct GNUNET_CHAT_Contact *member,
   1517                                      void *user_pointer)
   1518 {
   1519   GNUNET_CHAT_VERSION_ASSERT();
   1520 
   1521   if ((!group) || (!(group->context)) || (!member))
   1522     return;
   1523 
   1524   struct GNUNET_ShortHashCode hash;
   1525   util_shorthash_from_member(member->member, &hash);
   1526 
   1527   GNUNET_CONTAINER_multishortmap_put(
   1528     group->context->member_pointers,
   1529     &hash,
   1530     user_pointer,
   1531     GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE
   1532   );
   1533 }
   1534 
   1535 
   1536 void*
   1537 GNUNET_CHAT_member_get_user_pointer (const struct GNUNET_CHAT_Group *group,
   1538 				                             const struct GNUNET_CHAT_Contact *member)
   1539 {
   1540   GNUNET_CHAT_VERSION_ASSERT();
   1541 
   1542   if ((!group) || (!(group->context)) || (!member))
   1543     return NULL;
   1544 
   1545   struct GNUNET_ShortHashCode hash;
   1546   util_shorthash_from_member(member->member, &hash);
   1547 
   1548   return GNUNET_CONTAINER_multishortmap_get(
   1549     group->context->member_pointers,
   1550     &hash
   1551   );
   1552 }
   1553 
   1554 
   1555 struct GNUNET_CHAT_Context*
   1556 GNUNET_CHAT_group_get_context (struct GNUNET_CHAT_Group *group)
   1557 {
   1558   GNUNET_CHAT_VERSION_ASSERT();
   1559 
   1560   if (!group)
   1561     return NULL;
   1562 
   1563   return group->context;
   1564 }
   1565 
   1566 
   1567 enum GNUNET_GenericReturnValue
   1568 GNUNET_CHAT_context_get_status (struct GNUNET_CHAT_Context *context)
   1569 {
   1570   GNUNET_CHAT_VERSION_ASSERT();
   1571 
   1572   if ((!context) || (!(context->room)))
   1573     return GNUNET_SYSERR;
   1574 
   1575   switch (context->type) {
   1576     case GNUNET_CHAT_CONTEXT_TYPE_CONTACT:
   1577     {
   1578       const struct GNUNET_CHAT_Contact *contact = GNUNET_CHAT_context_get_contact(
   1579         context
   1580       );
   1581 
   1582       return contact? GNUNET_OK : GNUNET_NO;
   1583     }
   1584     case GNUNET_CHAT_CONTEXT_TYPE_GROUP:
   1585       return GNUNET_OK;
   1586     default:
   1587       return GNUNET_NO;
   1588   }
   1589 }
   1590 
   1591 
   1592 enum GNUNET_GenericReturnValue
   1593 GNUNET_CHAT_context_request (struct GNUNET_CHAT_Context *context)
   1594 {
   1595   GNUNET_CHAT_VERSION_ASSERT();
   1596 
   1597   if (!context)
   1598     return GNUNET_SYSERR;
   1599   else if (context->room)
   1600     return GNUNET_OK;
   1601 
   1602   struct GNUNET_CHAT_Handle *handle = context->handle;
   1603 
   1604   if ((!handle) || (!(context->contact)))
   1605     return GNUNET_SYSERR;
   1606 
   1607   struct GNUNET_CHAT_Contact *contact = handle_get_contact_from_messenger(
   1608     handle, context->contact
   1609   );
   1610 
   1611   if (!contact)
   1612     return GNUNET_SYSERR;
   1613 
   1614   enum GNUNET_GenericReturnValue owned = GNUNET_CHAT_contact_is_owned(
   1615     contact
   1616   );
   1617 
   1618   context->type = GNUNET_CHAT_CONTEXT_TYPE_CONTACT;
   1619 
   1620   struct GNUNET_CHAT_Context *other = contact_find_context(
   1621     contact, GNUNET_YES
   1622   );
   1623 
   1624   if (!other)
   1625     return GNUNET_SYSERR;
   1626 
   1627   union GNUNET_MESSENGER_RoomKey key;
   1628   GNUNET_MESSENGER_create_room_key(
   1629     &key,
   1630     NULL,
   1631     GNUNET_YES == owned? GNUNET_YES : GNUNET_NO,
   1632     GNUNET_NO,
   1633     GNUNET_YES == owned? GNUNET_YES : GNUNET_NO
   1634   );
   1635 
   1636   if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(
   1637       handle->contexts, &(key.hash)))
   1638     return GNUNET_SYSERR;
   1639 
   1640   struct GNUNET_MESSENGER_Room *room;
   1641   if (GNUNET_YES == owned)
   1642   {
   1643     struct GNUNET_PeerIdentity door;
   1644     if (GNUNET_OK == GNUNET_CRYPTO_get_peer_identity(
   1645           handle->cfg, &door))
   1646       room = GNUNET_MESSENGER_enter_room(
   1647         handle->messenger,
   1648         &door,
   1649         &key
   1650       );
   1651     else
   1652       room = NULL;
   1653   }
   1654   else
   1655     room = GNUNET_MESSENGER_open_room(
   1656       handle->messenger, &key
   1657     );
   1658 
   1659   if (!room)
   1660     return GNUNET_SYSERR;
   1661 
   1662   context_update_room(context, room, GNUNET_YES);
   1663 
   1664   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(
   1665       handle->contexts, &(key.hash), context,
   1666       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   1667   {
   1668     context_update_room(context, NULL, GNUNET_YES);
   1669     return GNUNET_SYSERR;
   1670   }
   1671 
   1672   if (GNUNET_YES != owned)
   1673   {
   1674     struct GNUNET_MESSENGER_Message msg;
   1675     memset(&msg, 0, sizeof(msg));
   1676 
   1677     msg.header.kind = GNUNET_MESSENGER_KIND_INVITE;
   1678     GNUNET_CRYPTO_get_peer_identity(handle->cfg, &(msg.body.invite.door));
   1679     GNUNET_memcpy(&(msg.body.invite.key), &key, sizeof(msg.body.invite.key));
   1680 
   1681     GNUNET_MESSENGER_send_message(other->room, &msg, context->contact);
   1682   }
   1683 
   1684   return GNUNET_OK;
   1685 }
   1686 
   1687 
   1688 struct GNUNET_CHAT_Contact*
   1689 GNUNET_CHAT_context_get_contact (struct GNUNET_CHAT_Context *context)
   1690 {
   1691   GNUNET_CHAT_VERSION_ASSERT();
   1692 
   1693   if ((!context) || (GNUNET_CHAT_CONTEXT_TYPE_CONTACT != context->type))
   1694     return NULL;
   1695 
   1696   if (context->contact)
   1697     return handle_get_contact_from_messenger(context->handle, context->contact);
   1698 
   1699   struct GNUNET_MESSENGER_Room *room = context->room;
   1700   struct GNUNET_CHAT_RoomFindContact find;
   1701   union GNUNET_MESSENGER_RoomKey key;
   1702 
   1703   GNUNET_memcpy(&(key.hash), GNUNET_MESSENGER_room_get_key(room), sizeof(key.hash));
   1704 
   1705   if (key.code.group_bit)
   1706     return NULL;
   1707 
   1708   if (! key.code.feed_bit)
   1709     find.ignore_key = GNUNET_MESSENGER_get_key(context->handle->messenger);
   1710   else
   1711     find.ignore_key = NULL;
   1712   
   1713   find.contact = NULL;
   1714 
   1715   int member_count = GNUNET_MESSENGER_iterate_members(
   1716     room,
   1717     it_room_find_contact,
   1718     &find
   1719   );
   1720 
   1721   if ((!find.contact) || (member_count > 2))
   1722     return NULL;
   1723 
   1724   return handle_get_contact_from_messenger(context->handle, find.contact);
   1725 }
   1726 
   1727 
   1728 struct GNUNET_CHAT_Group*
   1729 GNUNET_CHAT_context_get_group (struct GNUNET_CHAT_Context *context)
   1730 {
   1731   GNUNET_CHAT_VERSION_ASSERT();
   1732 
   1733   if ((!context) || (GNUNET_CHAT_CONTEXT_TYPE_GROUP != context->type))
   1734     return NULL;
   1735 
   1736   if (!(context->room))
   1737     return NULL;
   1738 
   1739   return handle_get_group_from_messenger(context->handle, context->room);
   1740 }
   1741 
   1742 
   1743 void
   1744 GNUNET_CHAT_context_set_user_pointer (struct GNUNET_CHAT_Context *context,
   1745 				                              void *user_pointer)
   1746 {
   1747   GNUNET_CHAT_VERSION_ASSERT();
   1748 
   1749   if (!context)
   1750     return;
   1751 
   1752   context->user_pointer = user_pointer;
   1753 }
   1754 
   1755 
   1756 void*
   1757 GNUNET_CHAT_context_get_user_pointer (const struct GNUNET_CHAT_Context *context)
   1758 {
   1759   GNUNET_CHAT_VERSION_ASSERT();
   1760 
   1761   if (!context)
   1762     return NULL;
   1763 
   1764   return context->user_pointer;
   1765 }
   1766 
   1767 
   1768 enum GNUNET_GenericReturnValue
   1769 GNUNET_CHAT_context_send_text (struct GNUNET_CHAT_Context *context,
   1770 			                         const char *text)
   1771 {
   1772   GNUNET_CHAT_VERSION_ASSERT();
   1773 
   1774   if ((!context) || (!text) || (!(context->room)))
   1775     return GNUNET_SYSERR;
   1776 
   1777   struct GNUNET_MESSENGER_Message msg;
   1778   memset(&msg, 0, sizeof(msg));
   1779 
   1780   msg.header.kind = GNUNET_MESSENGER_KIND_TEXT;
   1781   msg.body.text.text = GNUNET_strdup(text);
   1782 
   1783   GNUNET_MESSENGER_send_message(context->room, &msg, NULL);
   1784 
   1785   GNUNET_free(msg.body.text.text);
   1786   return GNUNET_OK;
   1787 }
   1788 
   1789 
   1790 enum GNUNET_GenericReturnValue
   1791 GNUNET_CHAT_context_send_read_receipt (struct GNUNET_CHAT_Context *context,
   1792 				                               struct GNUNET_CHAT_Message *message)
   1793 {
   1794   GNUNET_CHAT_VERSION_ASSERT();
   1795 
   1796   if ((!context) || (!(context->room)))
   1797     return GNUNET_SYSERR;
   1798 
   1799   char zero = '\0';
   1800   struct GNUNET_MESSENGER_Message msg;
   1801   memset(&msg, 0, sizeof(msg));
   1802 
   1803   msg.header.kind = GNUNET_MESSENGER_KIND_TEXT;
   1804   msg.body.text.text = &zero;
   1805 
   1806   const struct GNUNET_MESSENGER_Contact *receiver = NULL;
   1807 
   1808   if (!message)
   1809     goto skip_filter;
   1810 
   1811   if (GNUNET_CHAT_FLAG_NONE != message->flag)
   1812     return GNUNET_SYSERR;
   1813 
   1814   if (message->flags & GNUNET_MESSENGER_FLAG_SENT)
   1815     return GNUNET_OK;
   1816 
   1817   if (message->flags & GNUNET_MESSENGER_FLAG_PRIVATE)
   1818   {
   1819     receiver = GNUNET_MESSENGER_get_sender(context->room, &(message->hash));
   1820 
   1821     if (!receiver)
   1822       return GNUNET_SYSERR;
   1823   }
   1824 
   1825   if ((GNUNET_YES != message_has_msg(message)) ||
   1826       (GNUNET_MESSENGER_KIND_TEXT != message->msg->header.kind))
   1827     goto skip_filter;
   1828 
   1829   if ((!(message->msg->body.text.text)) ||
   1830       (!(message->msg->body.text.text[0])))
   1831     return GNUNET_SYSERR;
   1832 
   1833 skip_filter:
   1834   GNUNET_MESSENGER_send_message(context->room, &msg, receiver);
   1835   return GNUNET_OK;
   1836 }
   1837 
   1838 
   1839 struct GNUNET_CHAT_File*
   1840 GNUNET_CHAT_context_send_file (struct GNUNET_CHAT_Context *context,
   1841                                const char *path,
   1842                                GNUNET_CHAT_FileUploadCallback callback,
   1843                                void *cls)
   1844 {
   1845   GNUNET_CHAT_VERSION_ASSERT();
   1846 
   1847   if ((!context) || (!path) || (!(context->room)))
   1848     return NULL;
   1849 
   1850   struct GNUNET_HashCode hash;
   1851   if (GNUNET_OK != util_hash_file(path, &hash))
   1852     return NULL;
   1853 
   1854   char *filename = handle_create_file_path(
   1855     context->handle, &hash
   1856   );
   1857 
   1858   if (!filename)
   1859     return NULL;
   1860 
   1861   struct GNUNET_CHAT_File *file = GNUNET_CONTAINER_multihashmap_get(
   1862     context->handle->files,
   1863     &hash
   1864   );
   1865 
   1866   if (file)
   1867     goto file_binding;
   1868 
   1869   if ((GNUNET_YES == GNUNET_DISK_file_test(filename)) ||
   1870       (GNUNET_OK != GNUNET_DISK_directory_create_for_file(filename)) ||
   1871       (GNUNET_OK != GNUNET_DISK_file_copy(path, filename)))
   1872   {
   1873     GNUNET_free(filename);
   1874     return NULL;
   1875   }
   1876 
   1877   struct GNUNET_CRYPTO_SymmetricSessionKey key;
   1878   GNUNET_CRYPTO_symmetric_create_session_key(&key);
   1879 
   1880   if (GNUNET_OK != util_encrypt_file(filename, &hash, &key))
   1881   {
   1882     GNUNET_free(filename);
   1883     return NULL;
   1884   }
   1885 
   1886   char* p = GNUNET_strdup(path);
   1887 
   1888   file = file_create_from_disk(
   1889     context->handle,
   1890     basename(p),
   1891     &hash,
   1892     &key
   1893   );
   1894 
   1895   GNUNET_free(p);
   1896 
   1897   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(
   1898       context->handle->files, &hash, file,
   1899       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   1900   {
   1901     file_destroy(file);
   1902     GNUNET_free(filename);
   1903     return NULL;
   1904   }
   1905 
   1906   struct GNUNET_FS_BlockOptions bo;
   1907 
   1908   bo.anonymity_level = block_anonymity_level;
   1909   bo.content_priority = block_content_priority;
   1910   bo.replication_level = block_replication_level;
   1911   bo.expiration_time = GNUNET_TIME_absolute_get_forever_();
   1912 
   1913   struct GNUNET_FS_FileInformation* fi = GNUNET_FS_file_information_create_from_file(
   1914     context->handle->fs,
   1915     file,
   1916     filename,
   1917     NULL,
   1918     file->meta,
   1919     GNUNET_YES,
   1920     &bo
   1921   );
   1922 
   1923   file->publish = GNUNET_FS_publish_start(
   1924     context->handle->fs, fi,
   1925     NULL, NULL, NULL,
   1926     GNUNET_FS_PUBLISH_OPTION_NONE
   1927   );
   1928 
   1929   if (file->publish)
   1930     file->status |= GNUNET_CHAT_FILE_STATUS_PUBLISH;
   1931 
   1932   GNUNET_free(filename);
   1933 
   1934 file_binding:
   1935   file_bind_upload(file, context, callback, cls);
   1936   return file;
   1937 }
   1938 
   1939 
   1940 enum GNUNET_GenericReturnValue
   1941 GNUNET_CHAT_context_share_file (struct GNUNET_CHAT_Context *context,
   1942 				                        struct GNUNET_CHAT_File *file)
   1943 {
   1944   GNUNET_CHAT_VERSION_ASSERT();
   1945 
   1946   if ((!context) || (!file) || 
   1947       (!(file->name)) || (strlen(file->name) > NAME_MAX) ||
   1948       (!(file->uri)) || (!(context->room)))
   1949     return GNUNET_SYSERR;
   1950 
   1951   struct GNUNET_MESSENGER_Message msg;
   1952   memset(&msg, 0, sizeof(msg));
   1953 
   1954   msg.header.kind = GNUNET_MESSENGER_KIND_FILE;
   1955 
   1956   if (file->key)
   1957     GNUNET_memcpy(&(msg.body.file.key), file->key,
   1958                   sizeof(struct GNUNET_CRYPTO_SymmetricSessionKey));
   1959   else
   1960     memset(&(msg.body.file.key), 0, sizeof(msg.body.file.key));
   1961 
   1962   GNUNET_memcpy(&(msg.body.file.hash), &(file->hash), sizeof(file->hash));
   1963   GNUNET_strlcpy(msg.body.file.name, file->name, NAME_MAX);
   1964   msg.body.file.uri = GNUNET_FS_uri_to_string(file->uri);
   1965 
   1966   GNUNET_MESSENGER_send_message(context->room, &msg, NULL);
   1967 
   1968   GNUNET_free(msg.body.file.uri);
   1969   return GNUNET_OK;
   1970 }
   1971 
   1972 
   1973 enum GNUNET_GenericReturnValue
   1974 GNUNET_CHAT_context_send_tag (struct GNUNET_CHAT_Context *context,
   1975                               struct GNUNET_CHAT_Message *message,
   1976                               const char *tag)
   1977 {
   1978   GNUNET_CHAT_VERSION_ASSERT();
   1979 
   1980   if ((!context) || (!message) || (!tag) || (!(context->room)))
   1981     return GNUNET_SYSERR;
   1982 
   1983   char *tag_value = GNUNET_strdup(tag);
   1984 
   1985   struct GNUNET_MESSENGER_Message msg;
   1986   memset(&msg, 0, sizeof(msg));
   1987 
   1988   msg.header.kind = GNUNET_MESSENGER_KIND_TAG;
   1989   GNUNET_memcpy(&(msg.body.tag.hash), &(message->hash),
   1990     sizeof(struct GNUNET_HashCode));
   1991   msg.body.tag.tag = tag_value;
   1992 
   1993   GNUNET_MESSENGER_send_message(
   1994     context->room,
   1995     &msg,
   1996     NULL
   1997   );
   1998 
   1999   GNUNET_free(tag_value);
   2000   return GNUNET_OK;
   2001 }
   2002 
   2003 
   2004 struct GNUNET_CHAT_Discourse*
   2005 GNUNET_CHAT_context_open_discourse (struct GNUNET_CHAT_Context *context,
   2006                                     const struct GNUNET_CHAT_DiscourseId *id)
   2007 {
   2008   GNUNET_CHAT_VERSION_ASSERT();
   2009 
   2010   if ((!context) || (!(context->discourses)) || (!(context->room)) || (!id))
   2011     return NULL;
   2012 
   2013   struct GNUNET_ShortHashCode sid;
   2014   util_shorthash_from_discourse_id(id, &sid);
   2015 
   2016   struct GNUNET_CHAT_Discourse *discourse = GNUNET_CONTAINER_multishortmap_get(
   2017     context->discourses, &sid
   2018   );
   2019 
   2020   if (!discourse)
   2021   {
   2022     discourse = discourse_create(context, id);
   2023 
   2024     if (GNUNET_OK != GNUNET_CONTAINER_multishortmap_put(context->discourses,
   2025         &sid, discourse, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   2026     {
   2027       discourse_destroy(discourse);
   2028       return NULL;
   2029     }
   2030   }
   2031 
   2032   struct GNUNET_MESSENGER_Message msg;
   2033   memset(&msg, 0, sizeof(msg));
   2034 
   2035   msg.header.kind = GNUNET_MESSENGER_KIND_SUBSCRIBTION;
   2036   GNUNET_memcpy(
   2037     &(msg.body.subscription.discourse),
   2038     &sid,
   2039     sizeof(struct GNUNET_ShortHashCode)
   2040   );
   2041 
   2042   const struct GNUNET_TIME_Relative subscription_time = GNUNET_TIME_relative_multiply(
   2043     GNUNET_TIME_relative_get_second_(), 10
   2044   );
   2045 
   2046   msg.body.subscription.time = GNUNET_TIME_relative_hton(subscription_time);
   2047   msg.body.subscription.flags = GNUNET_MESSENGER_FLAG_SUBSCRIPTION_KEEP_ALIVE;
   2048 
   2049   GNUNET_MESSENGER_send_message(
   2050     context->room,
   2051     &msg,
   2052     NULL
   2053   );
   2054 
   2055   return discourse;
   2056 }
   2057 
   2058 
   2059 int
   2060 GNUNET_CHAT_context_iterate_messages (struct GNUNET_CHAT_Context *context,
   2061                                       GNUNET_CHAT_ContextMessageCallback callback,
   2062                                       void *cls)
   2063 {
   2064   GNUNET_CHAT_VERSION_ASSERT();
   2065 
   2066   if (!context)
   2067     return GNUNET_SYSERR;
   2068 
   2069   struct GNUNET_CHAT_ContextIterateMessages it;
   2070   it.context = context;
   2071   it.cb = callback;
   2072   it.cls = cls;
   2073 
   2074   return GNUNET_CONTAINER_multihashmap_iterate(
   2075     context->messages, it_context_iterate_messages, &it
   2076   );
   2077 }
   2078 
   2079 
   2080 int
   2081 GNUNET_CHAT_context_iterate_files (struct GNUNET_CHAT_Context *context,
   2082                                    GNUNET_CHAT_ContextFileCallback callback,
   2083                                    void *cls)
   2084 {
   2085   GNUNET_CHAT_VERSION_ASSERT();
   2086 
   2087   if (!context)
   2088     return GNUNET_SYSERR;
   2089 
   2090   struct GNUNET_CHAT_ContextIterateFiles it;
   2091   it.context = context;
   2092   it.cb = callback;
   2093   it.cls = cls;
   2094 
   2095   return GNUNET_CONTAINER_multihashmap_iterate(
   2096     context->files, it_context_iterate_files, &it
   2097   );
   2098 }
   2099 
   2100 
   2101 enum GNUNET_CHAT_MessageKind
   2102 GNUNET_CHAT_message_get_kind (const struct GNUNET_CHAT_Message *message)
   2103 {
   2104   GNUNET_CHAT_VERSION_ASSERT();
   2105 
   2106   if (!message)
   2107     return GNUNET_CHAT_KIND_UNKNOWN;
   2108 
   2109   switch (message->flag)
   2110   {
   2111     case GNUNET_CHAT_FLAG_WARNING:
   2112       return GNUNET_CHAT_KIND_WARNING;
   2113     case GNUNET_CHAT_FLAG_REFRESH:
   2114       return GNUNET_CHAT_KIND_REFRESH;
   2115     case GNUNET_CHAT_FLAG_LOGIN:
   2116       return GNUNET_CHAT_KIND_LOGIN;
   2117     case GNUNET_CHAT_FLAG_LOGOUT:
   2118       return GNUNET_CHAT_KIND_LOGOUT;
   2119     case GNUNET_CHAT_FLAG_CREATE_ACCOUNT:
   2120       return GNUNET_CHAT_KIND_CREATED_ACCOUNT;
   2121     case GNUNET_CHAT_FLAG_DELETE_ACCOUNT:
   2122       return GNUNET_CHAT_KIND_DELETED_ACCOUNT;
   2123     case GNUNET_CHAT_FLAG_UPDATE_ACCOUNT:
   2124       return GNUNET_CHAT_KIND_UPDATE_ACCOUNT;
   2125     case GNUNET_CHAT_FLAG_UPDATE_CONTEXT:
   2126       return GNUNET_CHAT_KIND_UPDATE_CONTEXT;
   2127     case GNUNET_CHAT_FLAG_ATTRIBUTES:
   2128       return GNUNET_CHAT_KIND_ATTRIBUTES;
   2129     case GNUNET_CHAT_FLAG_SHARE_ATTRIBUTES:
   2130       return GNUNET_CHAT_KIND_SHARED_ATTRIBUTES;
   2131     default:
   2132       break;
   2133   }
   2134 
   2135   if (GNUNET_YES != message_has_msg(message))
   2136     return GNUNET_CHAT_KIND_UNKNOWN;
   2137 
   2138   return util_message_kind_from_kind(message->msg->header.kind);
   2139 }
   2140 
   2141 
   2142 time_t
   2143 GNUNET_CHAT_message_get_timestamp (const struct GNUNET_CHAT_Message *message)
   2144 {
   2145   GNUNET_CHAT_VERSION_ASSERT();
   2146 
   2147   if ((!message) || (GNUNET_YES != message_has_msg(message)))
   2148     return ((time_t) -1);
   2149 
   2150   struct GNUNET_TIME_Absolute abs = GNUNET_TIME_absolute_ntoh(
   2151     message->msg->header.timestamp
   2152   );
   2153 
   2154   struct GNUNET_TIME_Timestamp ts = GNUNET_TIME_absolute_to_timestamp(
   2155     abs
   2156   );
   2157 
   2158   return (time_t) GNUNET_TIME_timestamp_to_s(ts);
   2159 }
   2160 
   2161 
   2162 struct GNUNET_CHAT_Contact*
   2163 GNUNET_CHAT_message_get_sender (const struct GNUNET_CHAT_Message *message)
   2164 {
   2165   GNUNET_CHAT_VERSION_ASSERT();
   2166 
   2167   if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
   2168       (!(message->context)) || (!(message->context->room)))
   2169     return NULL;
   2170 
   2171   const struct GNUNET_MESSENGER_Contact *sender = GNUNET_MESSENGER_get_sender(
   2172     message->context->room, &(message->hash)
   2173   );
   2174 
   2175   if (!sender)
   2176     return NULL;
   2177 
   2178   return handle_get_contact_from_messenger(message->context->handle, sender);
   2179 }
   2180 
   2181 
   2182 struct GNUNET_CHAT_Contact*
   2183 GNUNET_CHAT_message_get_recipient (const struct GNUNET_CHAT_Message *message)
   2184 {
   2185   GNUNET_CHAT_VERSION_ASSERT();
   2186 
   2187   if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
   2188       (!(message->context)) || (!(message->context->room)))
   2189     return NULL;
   2190 
   2191   const struct GNUNET_MESSENGER_Contact *recipient = GNUNET_MESSENGER_get_recipient(
   2192     message->context->room, &(message->hash)
   2193   );
   2194 
   2195   if (!recipient)
   2196     return NULL;
   2197 
   2198   return handle_get_contact_from_messenger(message->context->handle, recipient);
   2199 }
   2200 
   2201 
   2202 enum GNUNET_GenericReturnValue
   2203 GNUNET_CHAT_message_is_sent (const struct GNUNET_CHAT_Message *message)
   2204 {
   2205   GNUNET_CHAT_VERSION_ASSERT();
   2206 
   2207   if (!message)
   2208     return GNUNET_SYSERR;
   2209 
   2210   if (message->flags & GNUNET_MESSENGER_FLAG_SENT)
   2211     return GNUNET_YES;
   2212   else
   2213     return GNUNET_NO;
   2214 }
   2215 
   2216 
   2217 enum GNUNET_GenericReturnValue
   2218 GNUNET_CHAT_message_is_private (const struct GNUNET_CHAT_Message *message)
   2219 {
   2220   GNUNET_CHAT_VERSION_ASSERT();
   2221 
   2222   if (!message)
   2223     return GNUNET_SYSERR;
   2224 
   2225   if (message->flags & GNUNET_MESSENGER_FLAG_PRIVATE)
   2226     return GNUNET_YES;
   2227   else
   2228     return GNUNET_NO;
   2229 }
   2230 
   2231 
   2232 enum GNUNET_GenericReturnValue
   2233 GNUNET_CHAT_message_is_recent (const struct GNUNET_CHAT_Message *message)
   2234 {
   2235   GNUNET_CHAT_VERSION_ASSERT();
   2236 
   2237   if (!message)
   2238     return GNUNET_SYSERR;
   2239 
   2240   if (message->flags & GNUNET_MESSENGER_FLAG_RECENT)
   2241     return GNUNET_YES;
   2242   else
   2243     return GNUNET_NO;
   2244 }
   2245 
   2246 
   2247 enum GNUNET_GenericReturnValue
   2248 GNUNET_CHAT_message_is_update (const struct GNUNET_CHAT_Message *message)
   2249 {
   2250   GNUNET_CHAT_VERSION_ASSERT();
   2251 
   2252   if (!message)
   2253     return GNUNET_SYSERR;
   2254 
   2255   if (message->flags & GNUNET_MESSENGER_FLAG_UPDATE)
   2256     return GNUNET_YES;
   2257   else
   2258     return GNUNET_NO;
   2259 }
   2260 
   2261 
   2262 enum GNUNET_GenericReturnValue
   2263 GNUNET_CHAT_message_is_deleted (const struct GNUNET_CHAT_Message *message)
   2264 {
   2265   GNUNET_CHAT_VERSION_ASSERT();
   2266 
   2267   if (!message)
   2268     return GNUNET_SYSERR;
   2269 
   2270   if ((GNUNET_CHAT_FLAG_NONE == message->flag) &&
   2271       ((message->flags & GNUNET_MESSENGER_FLAG_DELETE) ||
   2272        (!message->msg)))
   2273     return GNUNET_YES;
   2274   else
   2275     return GNUNET_NO;
   2276 }
   2277 
   2278 
   2279 enum GNUNET_GenericReturnValue
   2280 GNUNET_CHAT_message_is_tagged (const struct GNUNET_CHAT_Message *message,
   2281                                const char *tag)
   2282 {
   2283   GNUNET_CHAT_VERSION_ASSERT();
   2284 
   2285   if ((!message) || (!(message->context)))
   2286     return GNUNET_SYSERR;
   2287 
   2288   const struct GNUNET_CHAT_InternalTagging *tagging = GNUNET_CONTAINER_multihashmap_get(
   2289     message->context->taggings, &(message->hash));
   2290   
   2291   if (!tagging)
   2292     return GNUNET_NO;
   2293 
   2294   if (internal_tagging_iterate(tagging, GNUNET_NO, tag, NULL, NULL) > 0)
   2295     return GNUNET_YES;
   2296   else
   2297     return GNUNET_NO;
   2298 }
   2299 
   2300 
   2301 int
   2302 GNUNET_CHAT_message_get_read_receipt (struct GNUNET_CHAT_Message *message,
   2303                                       GNUNET_CHAT_MessageReadReceiptCallback callback,
   2304                                       void *cls)
   2305 {
   2306   GNUNET_CHAT_VERSION_ASSERT();
   2307 
   2308   if ((!message) || (GNUNET_YES != message_has_msg(message)) || 
   2309       (!(message->context)))
   2310     return GNUNET_SYSERR;
   2311 
   2312   struct GNUNET_CHAT_MessageIterateReadReceipts it;
   2313   it.message = message;
   2314   it.cb = callback;
   2315   it.cls = cls;
   2316 
   2317   return GNUNET_MESSENGER_iterate_members(
   2318     message->context->room, it_message_iterate_read_receipts, &it
   2319   );
   2320 }
   2321 
   2322 
   2323 const char*
   2324 GNUNET_CHAT_message_get_text (const struct GNUNET_CHAT_Message *message)
   2325 {
   2326   GNUNET_CHAT_VERSION_ASSERT();
   2327 
   2328   if (!message)
   2329     return NULL;
   2330 
   2331   if (GNUNET_CHAT_FLAG_WARNING == message->flag)
   2332     return message->warning;
   2333   else if (GNUNET_CHAT_FLAG_ATTRIBUTES == message->flag)
   2334     return message->attr;
   2335 
   2336   if (GNUNET_YES != message_has_msg(message))
   2337     return NULL;
   2338 
   2339   if (GNUNET_MESSENGER_KIND_TEXT == message->msg->header.kind)
   2340     return message->msg->body.text.text;
   2341   else if (GNUNET_MESSENGER_KIND_FILE == message->msg->header.kind)
   2342     return message->msg->body.file.name;
   2343   else if (GNUNET_MESSENGER_KIND_TAG == message->msg->header.kind)
   2344     return message->msg->body.tag.tag;
   2345   else
   2346     return NULL;
   2347 }
   2348 
   2349 
   2350 void
   2351 GNUNET_CHAT_message_set_user_pointer (struct GNUNET_CHAT_Message *message,
   2352                                       void *user_pointer)
   2353 {
   2354   GNUNET_CHAT_VERSION_ASSERT();
   2355 
   2356   if (!message)
   2357     return;
   2358 
   2359   message->user_pointer = user_pointer;
   2360 }
   2361 
   2362 
   2363 void*
   2364 GNUNET_CHAT_message_get_user_pointer (const struct GNUNET_CHAT_Message *message)
   2365 {
   2366   GNUNET_CHAT_VERSION_ASSERT();
   2367 
   2368   if (!message)
   2369     return NULL;
   2370 
   2371   return message->user_pointer;
   2372 }
   2373 
   2374 
   2375 struct GNUNET_CHAT_Account*
   2376 GNUNET_CHAT_message_get_account (const struct GNUNET_CHAT_Message *message)
   2377 {
   2378   GNUNET_CHAT_VERSION_ASSERT();
   2379 
   2380   if (!message)
   2381     return NULL;
   2382 
   2383   if ((message->context) && (message->context->handle))
   2384     return message->context->handle->current;
   2385   else
   2386     return message->account;
   2387 }
   2388 
   2389 
   2390 struct GNUNET_CHAT_File*
   2391 GNUNET_CHAT_message_get_file (const struct GNUNET_CHAT_Message *message)
   2392 {
   2393   GNUNET_CHAT_VERSION_ASSERT();
   2394 
   2395   if ((!message) || (GNUNET_YES != message_has_msg(message)) || 
   2396       (!(message->context)))
   2397     return NULL;
   2398 
   2399   if (GNUNET_MESSENGER_KIND_FILE != message->msg->header.kind)
   2400     return NULL;
   2401 
   2402   return GNUNET_CONTAINER_multihashmap_get(
   2403     message->context->handle->files,
   2404     &(message->msg->body.file.hash)
   2405   );
   2406 }
   2407 
   2408 
   2409 struct GNUNET_CHAT_Invitation*
   2410 GNUNET_CHAT_message_get_invitation (const struct GNUNET_CHAT_Message *message)
   2411 {
   2412   GNUNET_CHAT_VERSION_ASSERT();
   2413 
   2414   if ((!message) || (GNUNET_YES != message_has_msg(message)) || 
   2415       (!(message->context)))
   2416     return NULL;
   2417 
   2418   if (GNUNET_MESSENGER_KIND_INVITE != message->msg->header.kind)
   2419     return NULL;
   2420 
   2421   return GNUNET_CONTAINER_multihashmap_get(
   2422     message->context->invites,
   2423     &(message->hash)
   2424   );
   2425 }
   2426 
   2427 
   2428 struct GNUNET_CHAT_Discourse*
   2429 GNUNET_CHAT_message_get_discourse (const struct GNUNET_CHAT_Message *message)
   2430 {
   2431   GNUNET_CHAT_VERSION_ASSERT();
   2432 
   2433   if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
   2434       (!(message->context)) || (!(message->context->discourses)))
   2435     return NULL;
   2436   
   2437   struct GNUNET_CHAT_Discourse *discourse;
   2438   
   2439   if (GNUNET_MESSENGER_KIND_SUBSCRIBTION == message->msg->header.kind)
   2440     discourse = GNUNET_CONTAINER_multishortmap_get(
   2441       message->context->discourses,
   2442       &(message->msg->body.subscription.discourse));
   2443   else if (GNUNET_MESSENGER_KIND_TALK == message->msg->header.kind)
   2444     discourse = GNUNET_CONTAINER_multishortmap_get(
   2445       message->context->discourses,
   2446       &(message->msg->body.talk.discourse));
   2447   else
   2448     discourse = NULL;
   2449 
   2450   return discourse;
   2451 }
   2452 
   2453 
   2454 struct GNUNET_CHAT_Message*
   2455 GNUNET_CHAT_message_get_target (const struct GNUNET_CHAT_Message *message)
   2456 {
   2457   GNUNET_CHAT_VERSION_ASSERT();
   2458 
   2459   if ((!message) || (GNUNET_YES != message_has_msg(message)) || 
   2460       (!(message->context)))
   2461     return NULL;
   2462 
   2463   struct GNUNET_CHAT_Message *target;
   2464 
   2465   if (GNUNET_MESSENGER_KIND_DELETION == message->msg->header.kind)
   2466     target = GNUNET_CONTAINER_multihashmap_get(
   2467 	message->context->messages, &(message->msg->body.deletion.hash));
   2468   else if (GNUNET_MESSENGER_KIND_TAG == message->msg->header.kind)
   2469     target = GNUNET_CONTAINER_multihashmap_get(
   2470       message->context->messages, &(message->msg->body.tag.hash));
   2471   else
   2472     target = NULL;
   2473 
   2474   return target;
   2475 }
   2476 
   2477 
   2478 enum GNUNET_GenericReturnValue
   2479 GNUNET_CHAT_message_delete (struct GNUNET_CHAT_Message *message,
   2480 			                      unsigned int delay)
   2481 {
   2482   GNUNET_CHAT_VERSION_ASSERT();
   2483 
   2484   if ((!message) || (GNUNET_YES != message_has_msg(message)) || 
   2485       (!(message->context)))
   2486     return GNUNET_SYSERR;
   2487   
   2488   struct GNUNET_TIME_Relative rel = GNUNET_TIME_relative_multiply(
   2489     GNUNET_TIME_relative_get_second_(), delay
   2490   );
   2491 
   2492   GNUNET_MESSENGER_delete_message(
   2493     message->context->room,
   2494     &(message->hash),
   2495     rel
   2496   );
   2497 
   2498   return GNUNET_OK;
   2499 }
   2500 
   2501 
   2502 int
   2503 GNUNET_CHAT_message_iterate_tags (struct GNUNET_CHAT_Message *message,
   2504                                   GNUNET_CHAT_MessageCallback callback,
   2505                                   void *cls)
   2506 {
   2507   GNUNET_CHAT_VERSION_ASSERT();
   2508 
   2509   if ((!message) || (!(message->context)))
   2510     return GNUNET_SYSERR;
   2511 
   2512   const struct GNUNET_CHAT_InternalTagging *tagging = GNUNET_CONTAINER_multihashmap_get(
   2513     message->context->taggings, &(message->hash));
   2514   
   2515   if (!tagging)
   2516     return 0;
   2517 
   2518   return internal_tagging_iterate(tagging, GNUNET_YES, NULL, callback, cls);
   2519 }
   2520 
   2521 
   2522 uint64_t
   2523 GNUNET_CHAT_message_available (const struct GNUNET_CHAT_Message *message)
   2524 {
   2525   GNUNET_CHAT_VERSION_ASSERT();
   2526 
   2527   if ((!message) || (GNUNET_YES != message_has_msg(message)))
   2528     return 0;
   2529 
   2530   if (GNUNET_MESSENGER_KIND_TALK == message->msg->header.kind)
   2531     return message->msg->body.talk.length;
   2532   else
   2533     return 0;
   2534 }
   2535 
   2536 
   2537 enum GNUNET_GenericReturnValue
   2538 GNUNET_CHAT_message_read (const struct GNUNET_CHAT_Message *message,
   2539                           char *data,
   2540                           uint64_t size)
   2541 {
   2542   GNUNET_CHAT_VERSION_ASSERT();
   2543 
   2544   if ((!message) || (GNUNET_YES != message_has_msg(message)))
   2545     return GNUNET_SYSERR;
   2546 
   2547   if (GNUNET_MESSENGER_KIND_TALK != message->msg->header.kind)
   2548     return GNUNET_SYSERR;
   2549 
   2550   const uint64_t available = message->msg->body.talk.length;
   2551 
   2552   if (available < size)
   2553     return GNUNET_NO;
   2554 
   2555   GNUNET_memcpy(
   2556     data,
   2557     message->msg->body.talk.data,
   2558     size
   2559   );
   2560 
   2561   return GNUNET_OK;
   2562 }
   2563 
   2564 
   2565 enum GNUNET_GenericReturnValue
   2566 GNUNET_CHAT_message_feed (const struct GNUNET_CHAT_Message *message,
   2567                           int fd)
   2568 {
   2569   GNUNET_CHAT_VERSION_ASSERT();
   2570 
   2571   if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
   2572       (fd == -1))
   2573     return GNUNET_SYSERR;
   2574 
   2575   if (GNUNET_MESSENGER_KIND_TALK != message->msg->header.kind)
   2576     return GNUNET_SYSERR;
   2577 
   2578   if (!(message->msg->body.talk.length))
   2579     return GNUNET_NO;
   2580 
   2581   const ssize_t written = write(
   2582     fd,
   2583     message->msg->body.talk.data,
   2584     message->msg->body.talk.length
   2585   );
   2586 
   2587   if (-1 == written)
   2588     return GNUNET_SYSERR;
   2589   else if (written != message->msg->body.talk.length)
   2590     return GNUNET_NO;
   2591   else
   2592     return GNUNET_OK;
   2593 }
   2594 
   2595 
   2596 const char*
   2597 GNUNET_CHAT_file_get_name (const struct GNUNET_CHAT_File *file)
   2598 {
   2599   GNUNET_CHAT_VERSION_ASSERT();
   2600 
   2601   if (!file)
   2602     return NULL;
   2603 
   2604   return file->name;
   2605 }
   2606 
   2607 
   2608 const char*
   2609 GNUNET_CHAT_file_get_hash (const struct GNUNET_CHAT_File *file)
   2610 {
   2611   GNUNET_CHAT_VERSION_ASSERT();
   2612 
   2613   if (!file)
   2614     return NULL;
   2615 
   2616   return GNUNET_h2s_full(&(file->hash));
   2617 }
   2618 
   2619 
   2620 uint64_t
   2621 GNUNET_CHAT_file_get_size (const struct GNUNET_CHAT_File *file)
   2622 {
   2623   GNUNET_CHAT_VERSION_ASSERT();
   2624 
   2625   if ((!file) || (!(file->uri)))
   2626     return 0;
   2627 
   2628   return GNUNET_FS_uri_chk_get_file_size(file->uri);
   2629 }
   2630 
   2631 
   2632 uint64_t
   2633 GNUNET_CHAT_file_get_local_size (const struct GNUNET_CHAT_File *file)
   2634 {
   2635   GNUNET_CHAT_VERSION_ASSERT();
   2636 
   2637   if (!file)
   2638     return 0;
   2639 
   2640   char *filename = handle_create_file_path(
   2641     file->handle, &(file->hash)
   2642   );
   2643 
   2644   if (!filename)
   2645     return 0;
   2646 
   2647   uint64_t size;
   2648   if (GNUNET_OK != GNUNET_DISK_file_size(filename, &size, GNUNET_NO, GNUNET_YES))
   2649     size = 0;
   2650 
   2651   GNUNET_free(filename);
   2652   return size;
   2653 }
   2654 
   2655 
   2656 struct GNUNET_CHAT_Uri*
   2657 GNUNET_CHAT_file_get_uri (const struct GNUNET_CHAT_File *file)
   2658 {
   2659   GNUNET_CHAT_VERSION_ASSERT();
   2660 
   2661   if ((!file) || (!(file->uri)))
   2662     return NULL;
   2663 
   2664   return uri_create_file(file->uri);
   2665 }
   2666 
   2667 
   2668 enum GNUNET_GenericReturnValue
   2669 GNUNET_CHAT_file_is_uploading (const struct GNUNET_CHAT_File *file)
   2670 {
   2671   GNUNET_CHAT_VERSION_ASSERT();
   2672 
   2673   if ((!file) || (0 == (file->status & GNUNET_CHAT_FILE_STATUS_PUBLISH)))
   2674     return GNUNET_NO;
   2675   else
   2676     return GNUNET_YES;
   2677 }
   2678 
   2679 
   2680 enum GNUNET_GenericReturnValue
   2681 GNUNET_CHAT_file_is_ready (const struct GNUNET_CHAT_File *file)
   2682 {
   2683   GNUNET_CHAT_VERSION_ASSERT();
   2684 
   2685   if ((!file) || (file->status & GNUNET_CHAT_FILE_STATUS_MASK))
   2686     return GNUNET_NO;
   2687 
   2688   const uint64_t size = GNUNET_CHAT_file_get_size(file);
   2689   const uint64_t local_size = GNUNET_CHAT_file_get_local_size(file);
   2690 
   2691   if (size != local_size)
   2692     return GNUNET_NO;
   2693   else
   2694     return GNUNET_YES;
   2695 }
   2696 
   2697 
   2698 const char*
   2699 GNUNET_CHAT_file_open_preview (struct GNUNET_CHAT_File *file)
   2700 {
   2701   GNUNET_CHAT_VERSION_ASSERT();
   2702 
   2703   if (!file)
   2704     return NULL;
   2705 
   2706   if (file->preview)
   2707     return file->preview;
   2708 
   2709   char *filename = handle_create_file_path(
   2710     file->handle, &(file->hash)
   2711   );
   2712 
   2713   if (!filename)
   2714     return NULL;
   2715 
   2716   if (GNUNET_YES != GNUNET_DISK_file_test(filename))
   2717     goto free_filename;
   2718 
   2719   if (!(file->key))
   2720   {
   2721     file->preview = filename;
   2722     return file->preview;
   2723   }
   2724 
   2725   file->preview = GNUNET_DISK_mktemp(
   2726     file->name? file->name : ""
   2727   );
   2728 
   2729   if (!(file->preview))
   2730     goto free_filename;
   2731 
   2732   remove(file->preview);
   2733 
   2734   if ((GNUNET_OK != GNUNET_DISK_file_copy(filename, file->preview)) ||
   2735       (GNUNET_OK != util_decrypt_file(file->preview,
   2736       &(file->hash), file->key)))
   2737   {
   2738     GNUNET_free(file->preview);
   2739     file->preview = NULL;
   2740   }
   2741 
   2742 free_filename:
   2743   GNUNET_free(filename);
   2744   return file->preview;
   2745 }
   2746 
   2747 
   2748 void
   2749 GNUNET_CHAT_file_close_preview (struct GNUNET_CHAT_File *file)
   2750 {
   2751   GNUNET_CHAT_VERSION_ASSERT();
   2752 
   2753   if ((!file) || (!(file->preview)))
   2754     return;
   2755 
   2756   if (!(file->key))
   2757     goto skip_filename;
   2758 
   2759   char *filename = handle_create_file_path(
   2760     file->handle, &(file->hash)
   2761   );
   2762 
   2763   if (!filename)
   2764     goto skip_filename;
   2765 
   2766   if (0 != strcmp(filename, file->preview))
   2767     remove(file->preview);
   2768 
   2769   GNUNET_free(filename);
   2770 
   2771 skip_filename:
   2772   GNUNET_free(file->preview);
   2773   file->preview = NULL;
   2774 }
   2775 
   2776 
   2777 void
   2778 GNUNET_CHAT_file_set_user_pointer (struct GNUNET_CHAT_File *file,
   2779 				                           void *user_pointer)
   2780 {
   2781   GNUNET_CHAT_VERSION_ASSERT();
   2782 
   2783   if (!file)
   2784     return;
   2785 
   2786   file->user_pointer = user_pointer;
   2787 }
   2788 
   2789 
   2790 void*
   2791 GNUNET_CHAT_file_get_user_pointer (const struct GNUNET_CHAT_File *file)
   2792 {
   2793   GNUNET_CHAT_VERSION_ASSERT();
   2794 
   2795   if (!file)
   2796     return NULL;
   2797 
   2798   return file->user_pointer;
   2799 }
   2800 
   2801 
   2802 enum GNUNET_GenericReturnValue
   2803 GNUNET_CHAT_file_is_downloading (const struct GNUNET_CHAT_File *file)
   2804 {
   2805   GNUNET_CHAT_VERSION_ASSERT();
   2806 
   2807   if ((!file) || (0 == (file->status & GNUNET_CHAT_FILE_STATUS_DOWNLOAD)))
   2808     return GNUNET_NO;
   2809   else
   2810     return GNUNET_YES;
   2811 }
   2812 
   2813 
   2814 enum GNUNET_GenericReturnValue
   2815 GNUNET_CHAT_file_start_download (struct GNUNET_CHAT_File *file,
   2816                                  GNUNET_CHAT_FileDownloadCallback callback,
   2817                                  void *cls)
   2818 {
   2819   GNUNET_CHAT_VERSION_ASSERT();
   2820 
   2821   if ((!file) || (!(file->uri)))
   2822     return GNUNET_SYSERR;
   2823 
   2824   if (file->download)
   2825   {
   2826     file_bind_downlaod(file, callback, cls);
   2827 
   2828     GNUNET_FS_download_resume(file->download);
   2829     return GNUNET_OK;
   2830   }
   2831 
   2832   char *filename = handle_create_file_path(
   2833     file->handle, &(file->hash)
   2834   );
   2835 
   2836   if (!filename)
   2837     return GNUNET_SYSERR;
   2838 
   2839   const uint64_t size = GNUNET_FS_uri_chk_get_file_size(file->uri);
   2840 
   2841   uint64_t offset;
   2842   if (GNUNET_OK != GNUNET_DISK_file_size(filename, &offset, 
   2843       GNUNET_NO, GNUNET_YES))
   2844     offset = 0;
   2845 
   2846   if (offset >= size)
   2847   {
   2848     if (callback)
   2849       callback(cls, file, size, size);
   2850 
   2851     goto free_filename;
   2852   }
   2853 
   2854   file_bind_downlaod(file, callback, cls);
   2855 
   2856   const uint64_t remaining = (size - offset);
   2857 
   2858   file->download = GNUNET_FS_download_start(
   2859     file->handle->fs,
   2860     file->uri,
   2861     file->meta,
   2862     filename,
   2863     NULL,
   2864     offset,
   2865     remaining,
   2866     1,
   2867     GNUNET_FS_DOWNLOAD_OPTION_NONE,
   2868     file,
   2869     NULL
   2870   );
   2871 
   2872   if (file->download)
   2873     file->status |= GNUNET_CHAT_FILE_STATUS_DOWNLOAD;
   2874 
   2875 free_filename:
   2876   GNUNET_free(filename);
   2877   return GNUNET_OK;
   2878 }
   2879 
   2880 
   2881 enum GNUNET_GenericReturnValue
   2882 GNUNET_CHAT_file_pause_download (struct GNUNET_CHAT_File *file)
   2883 {
   2884   GNUNET_CHAT_VERSION_ASSERT();
   2885 
   2886   if (!file)
   2887     return GNUNET_SYSERR;
   2888 
   2889   GNUNET_FS_download_suspend(file->download);
   2890   return GNUNET_OK;
   2891 }
   2892 
   2893 
   2894 enum GNUNET_GenericReturnValue
   2895 GNUNET_CHAT_file_resume_download (struct GNUNET_CHAT_File *file)
   2896 {
   2897   GNUNET_CHAT_VERSION_ASSERT();
   2898 
   2899   if (!file)
   2900     return GNUNET_SYSERR;
   2901 
   2902   GNUNET_FS_download_resume(file->download);
   2903   return GNUNET_OK;
   2904 }
   2905 
   2906 
   2907 enum GNUNET_GenericReturnValue
   2908 GNUNET_CHAT_file_stop_download (struct GNUNET_CHAT_File *file)
   2909 {
   2910   GNUNET_CHAT_VERSION_ASSERT();
   2911 
   2912   if (!file)
   2913     return GNUNET_SYSERR;
   2914 
   2915   GNUNET_FS_download_stop(file->download, GNUNET_YES);
   2916   file->download = NULL;
   2917   return GNUNET_OK;
   2918 }
   2919 
   2920 
   2921 enum GNUNET_GenericReturnValue
   2922 GNUNET_CHAT_file_is_unindexing (const struct GNUNET_CHAT_File *file)
   2923 {
   2924   GNUNET_CHAT_VERSION_ASSERT();
   2925 
   2926   if ((!file) || (0 == (file->status & GNUNET_CHAT_FILE_STATUS_UNINDEX)))
   2927     return GNUNET_NO;
   2928   else
   2929     return GNUNET_YES;
   2930 }
   2931 
   2932 
   2933 enum GNUNET_GenericReturnValue
   2934 GNUNET_CHAT_file_unindex (struct GNUNET_CHAT_File *file,
   2935                           GNUNET_CHAT_FileUnindexCallback callback,
   2936                           void *cls)
   2937 {
   2938   GNUNET_CHAT_VERSION_ASSERT();
   2939 
   2940   if (!file)
   2941     return GNUNET_SYSERR;
   2942 
   2943   if (file->publish)
   2944   {
   2945     GNUNET_FS_publish_stop(file->publish);
   2946     file->publish = NULL;
   2947     return GNUNET_OK;
   2948   }
   2949 
   2950   file_bind_unindex(file, callback, cls);
   2951 
   2952   if (file->unindex)
   2953     return GNUNET_OK;
   2954 
   2955   char *filename = handle_create_file_path(
   2956     file->handle, &(file->hash)
   2957   );
   2958 
   2959   if (!filename)
   2960     return GNUNET_SYSERR;
   2961 
   2962   file->unindex = GNUNET_FS_unindex_start(
   2963     file->handle->fs, filename, file
   2964   );
   2965 
   2966   if (file->unindex)
   2967     file->status |= GNUNET_CHAT_FILE_STATUS_UNINDEX;
   2968 
   2969   GNUNET_free(filename);
   2970   return GNUNET_OK;
   2971 }
   2972 
   2973 
   2974 void
   2975 GNUNET_CHAT_invitation_accept (struct GNUNET_CHAT_Invitation *invitation)
   2976 {
   2977   GNUNET_CHAT_VERSION_ASSERT();
   2978 
   2979   if (!invitation)
   2980     return;
   2981 
   2982   struct GNUNET_CHAT_Handle *handle;
   2983   handle = invitation->context->handle;
   2984 
   2985   if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(
   2986       handle->contexts, &(invitation->key.hash)))
   2987     return;
   2988 
   2989   struct GNUNET_PeerIdentity door;
   2990   GNUNET_PEER_resolve(invitation->door, &door);
   2991 
   2992   struct GNUNET_MESSENGER_Room *room;
   2993   room = GNUNET_MESSENGER_enter_room(
   2994     invitation->context->handle->messenger,
   2995     &door, &(invitation->key)
   2996   );
   2997 
   2998   if (!room)
   2999     return;
   3000 
   3001   struct GNUNET_CHAT_Context *context;
   3002   context = context_create_from_room(handle, room);
   3003 
   3004   if (!context)
   3005     return;
   3006 
   3007   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(
   3008       handle->contexts, &(invitation->key.hash), context,
   3009       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   3010     goto destroy_context;
   3011   
   3012   if (GNUNET_CHAT_CONTEXT_TYPE_GROUP != context->type)
   3013   {
   3014     context_write_records(context);
   3015     return;
   3016   }
   3017 
   3018   struct GNUNET_CHAT_Group *group;
   3019   group = group_create_from_context(handle, context);
   3020 
   3021   if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(
   3022       handle->groups, &(invitation->key.hash), group,
   3023       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   3024   {
   3025     context_write_records(context);
   3026     return;
   3027   }
   3028 
   3029   group_destroy(group);
   3030 
   3031   GNUNET_CONTAINER_multihashmap_remove(
   3032     handle->contexts, &(invitation->key.hash), context);
   3033 
   3034 destroy_context:
   3035   context_destroy(context);
   3036 }
   3037 
   3038 
   3039 void
   3040 GNUNET_CHAT_invitation_reject (struct GNUNET_CHAT_Invitation *invitation)
   3041 {
   3042   GNUNET_CHAT_VERSION_ASSERT();
   3043 
   3044   if (!invitation)
   3045     return;
   3046 
   3047   const struct GNUNET_MESSENGER_Contact *sender = GNUNET_MESSENGER_get_sender(
   3048     invitation->context->room, &(invitation->hash)
   3049   );
   3050 
   3051   if (!sender)
   3052     return;
   3053 
   3054   struct GNUNET_MESSENGER_Message msg;
   3055   memset(&msg, 0, sizeof(msg));
   3056 
   3057   msg.header.kind = GNUNET_MESSENGER_KIND_TAG;
   3058   GNUNET_memcpy(&(msg.body.tag.hash), &(invitation->hash),
   3059                 sizeof(struct GNUNET_HashCode));
   3060   msg.body.tag.tag = NULL;
   3061 
   3062   GNUNET_MESSENGER_send_message(invitation->context->room, &msg, sender);
   3063 }
   3064 
   3065 
   3066 enum GNUNET_GenericReturnValue
   3067 GNUNET_CHAT_invitation_is_accepted (const struct GNUNET_CHAT_Invitation *invitation)
   3068 {
   3069   GNUNET_CHAT_VERSION_ASSERT();
   3070 
   3071   if (!invitation)
   3072     return GNUNET_NO;
   3073 
   3074   return GNUNET_CONTAINER_multihashmap_contains(
   3075     invitation->context->handle->contexts,
   3076     &(invitation->key.hash)
   3077   );
   3078 }
   3079 
   3080 
   3081 enum GNUNET_GenericReturnValue
   3082 GNUNET_CHAT_invitation_is_rejected (const struct GNUNET_CHAT_Invitation *invitation)
   3083 {
   3084   GNUNET_CHAT_VERSION_ASSERT();
   3085 
   3086   if (!invitation)
   3087     return GNUNET_NO;
   3088 
   3089   const struct GNUNET_CHAT_InternalTagging *tagging = GNUNET_CONTAINER_multihashmap_get(
   3090     invitation->context->taggings, &(invitation->hash));
   3091   
   3092   if (!tagging)
   3093     return GNUNET_NO;
   3094   
   3095   if (internal_tagging_iterate(tagging, GNUNET_NO, NULL, NULL, NULL) > 0)
   3096     return GNUNET_YES;
   3097   else
   3098     return GNUNET_NO;
   3099 }
   3100 
   3101 
   3102 enum GNUNET_GenericReturnValue
   3103 GNUNET_CHAT_invitation_is_direct (const struct GNUNET_CHAT_Invitation *invitation)
   3104 {
   3105   GNUNET_CHAT_VERSION_ASSERT();
   3106 
   3107   if ((invitation->key.code.public_bit) ||
   3108       (invitation->key.code.group_bit) ||
   3109       (invitation->key.code.feed_bit))
   3110     return GNUNET_NO;
   3111   else
   3112     return GNUNET_YES;
   3113 }
   3114 
   3115 
   3116 const struct GNUNET_CHAT_DiscourseId*
   3117 GNUNET_CHAT_discourse_get_id (const struct GNUNET_CHAT_Discourse *discourse)
   3118 {
   3119   GNUNET_CHAT_VERSION_ASSERT();
   3120 
   3121   if (!discourse)
   3122     return NULL;
   3123 
   3124   return &(discourse->id);
   3125 }
   3126 
   3127 
   3128 enum GNUNET_GenericReturnValue
   3129 GNUNET_CHAT_discourse_is_open (const struct GNUNET_CHAT_Discourse *discourse)
   3130 {
   3131   GNUNET_CHAT_VERSION_ASSERT();
   3132 
   3133   if (!discourse)
   3134     return GNUNET_SYSERR;
   3135 
   3136   struct GNUNET_CHAT_DiscourseSubscription *sub;
   3137   for (sub = discourse->head; sub; sub = sub->next)
   3138   {
   3139     if (GNUNET_TIME_absolute_cmp(sub->end, <, GNUNET_TIME_absolute_get()))
   3140       continue;
   3141 
   3142     if (GNUNET_YES == sub->contact->owned)
   3143       return GNUNET_YES;
   3144   }
   3145 
   3146   return GNUNET_NO;
   3147 }
   3148 
   3149 
   3150 void
   3151 GNUNET_CHAT_discourse_set_user_pointer (struct GNUNET_CHAT_Discourse *discourse,
   3152                                         void *user_pointer)
   3153 {
   3154   GNUNET_CHAT_VERSION_ASSERT();
   3155 
   3156   if (!discourse)
   3157     return;
   3158 
   3159   discourse->user_pointer = user_pointer;
   3160 }
   3161 
   3162 
   3163 void*
   3164 GNUNET_CHAT_discourse_get_user_pointer (const struct GNUNET_CHAT_Discourse *discourse)
   3165 {
   3166   GNUNET_CHAT_VERSION_ASSERT();
   3167 
   3168   if (!discourse)
   3169     return NULL;
   3170 
   3171   return discourse->user_pointer;
   3172 }
   3173 
   3174 
   3175 void
   3176 GNUNET_CHAT_discourse_close (struct GNUNET_CHAT_Discourse *discourse)
   3177 {
   3178   GNUNET_CHAT_VERSION_ASSERT();
   3179 
   3180   if ((!discourse) || (!(discourse->context)) || (!(discourse->context->room)))
   3181     return;
   3182   
   3183   struct GNUNET_MESSENGER_Message msg;
   3184   memset(&msg, 0, sizeof(msg));
   3185 
   3186   msg.header.kind = GNUNET_MESSENGER_KIND_SUBSCRIBTION;
   3187 
   3188   util_shorthash_from_discourse_id(
   3189     &(discourse->id),
   3190     &(msg.body.subscription.discourse)
   3191   );
   3192 
   3193   msg.body.subscription.time = GNUNET_TIME_relative_hton(GNUNET_TIME_relative_get_zero_());
   3194   msg.body.subscription.flags = GNUNET_MESSENGER_FLAG_SUBSCRIPTION_UNSUBSCRIBE;
   3195 
   3196   GNUNET_MESSENGER_send_message(
   3197     discourse->context->room,
   3198     &msg,
   3199     NULL
   3200   );
   3201 }
   3202 
   3203 
   3204 enum GNUNET_GenericReturnValue
   3205 GNUNET_CHAT_discourse_write (struct GNUNET_CHAT_Discourse *discourse,
   3206                              const char *data,
   3207                              uint64_t size)
   3208 {
   3209   GNUNET_CHAT_VERSION_ASSERT();
   3210 
   3211   if ((!discourse) || (!data) || (!(discourse->context)) || 
   3212       (!(discourse->context->room)))
   3213     return GNUNET_SYSERR;
   3214   
   3215   static const uint64_t max_size = (uint16_t) (
   3216     GNUNET_MAX_MESSAGE_SIZE - GNUNET_MIN_MESSAGE_SIZE -
   3217     sizeof (struct GNUNET_MESSENGER_Message)
   3218   );
   3219 
   3220   struct GNUNET_MESSENGER_Message msg;
   3221   memset(&msg, 0, sizeof(msg));
   3222 
   3223   msg.header.kind = GNUNET_MESSENGER_KIND_TALK;
   3224   msg.body.talk.data = GNUNET_malloc(size > max_size? max_size : size);
   3225 
   3226   util_shorthash_from_discourse_id(
   3227     &(discourse->id),
   3228     &(msg.body.talk.discourse)
   3229   );
   3230 
   3231   while (size > 0)
   3232   {
   3233     msg.body.talk.length = (uint16_t) (size > max_size? max_size : size);
   3234 
   3235     GNUNET_memcpy(
   3236       msg.body.talk.data,
   3237       data,
   3238       msg.body.talk.length
   3239     );
   3240 
   3241     size -= msg.body.talk.length;
   3242     data += msg.body.talk.length;
   3243 
   3244     GNUNET_MESSENGER_send_message(discourse->context->room, &msg, NULL);
   3245   }
   3246 
   3247   GNUNET_free(msg.body.talk.data);
   3248   return GNUNET_OK;
   3249 }
   3250 
   3251 
   3252 int
   3253 GNUNET_CHAT_discourse_get_fd (const struct GNUNET_CHAT_Discourse *discourse)
   3254 {
   3255   GNUNET_CHAT_VERSION_ASSERT();
   3256 
   3257   if (! discourse)
   3258     return GNUNET_SYSERR;
   3259 
   3260   return discourse->pipe[1];
   3261 }
   3262 
   3263 
   3264 int
   3265 GNUNET_CHAT_discourse_iterate_contacts (struct GNUNET_CHAT_Discourse *discourse,
   3266                                         GNUNET_CHAT_DiscourseContactCallback callback,
   3267                                         void *cls)
   3268 {
   3269   GNUNET_CHAT_VERSION_ASSERT();
   3270 
   3271   if (! discourse)
   3272     return GNUNET_SYSERR;
   3273 
   3274   int iterations = 0;
   3275 
   3276   struct GNUNET_CHAT_DiscourseSubscription *sub;
   3277   for (sub = discourse->head; sub; sub = sub->next)
   3278   {
   3279     if (GNUNET_TIME_absolute_cmp(sub->end, <, GNUNET_TIME_absolute_get()))
   3280       continue;
   3281 
   3282     if (callback)
   3283       callback(cls, discourse, sub->contact);
   3284 
   3285     iterations++;
   3286   }
   3287 
   3288   return iterations;
   3289 }