libgnunetchat

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

gnunet_chat_lib.c (74424B)


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