libgnunetchat

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

gnunet_chat_lib.c (74464B)


      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   const struct GNUNET_PeerIdentity *pid = context->handle->pid;
   1493 
   1494   if (!pid)
   1495     return GNUNET_SYSERR;
   1496 
   1497   union GNUNET_MESSENGER_RoomKey key;
   1498   GNUNET_memcpy(
   1499     &(key.hash),
   1500     GNUNET_MESSENGER_room_get_key(group->context->room),
   1501     sizeof(key.hash)
   1502   );
   1503 
   1504   handle_send_room_name(group->handle, GNUNET_MESSENGER_open_room(
   1505     group->handle->messenger, &key
   1506   ));
   1507 
   1508   struct GNUNET_MESSENGER_Message msg;
   1509   memset(&msg, 0, sizeof(msg));
   1510 
   1511   msg.header.kind = GNUNET_MESSENGER_KIND_INVITE;
   1512   GNUNET_memcpy(&(msg.body.invite.door), pid, sizeof(msg.body.invite.door));
   1513   GNUNET_memcpy(&(msg.body.invite.key), &key, sizeof(msg.body.invite.key));
   1514 
   1515   GNUNET_MESSENGER_send_message(context->room, &msg, contact->member);
   1516   return GNUNET_OK;
   1517 }
   1518 
   1519 
   1520 int
   1521 GNUNET_CHAT_group_iterate_contacts (struct GNUNET_CHAT_Group *group,
   1522                                     GNUNET_CHAT_GroupContactCallback callback,
   1523                                     void *cls)
   1524 {
   1525   GNUNET_CHAT_VERSION_ASSERT();
   1526 
   1527   if (!group)
   1528     return GNUNET_SYSERR;
   1529 
   1530   struct GNUNET_CHAT_GroupIterateContacts it;
   1531   it.group = group;
   1532   it.cb = callback;
   1533   it.cls = cls;
   1534 
   1535   return GNUNET_MESSENGER_iterate_members(
   1536     group->context->room, it_group_iterate_contacts, &it
   1537   );
   1538 }
   1539 
   1540 
   1541 void
   1542 GNUNET_CHAT_member_set_user_pointer (struct GNUNET_CHAT_Group *group,
   1543                                      const struct GNUNET_CHAT_Contact *member,
   1544                                      void *user_pointer)
   1545 {
   1546   GNUNET_CHAT_VERSION_ASSERT();
   1547 
   1548   if ((!group) || (!(group->context)) || (!member))
   1549     return;
   1550 
   1551   struct GNUNET_ShortHashCode hash;
   1552   util_shorthash_from_member(member->member, &hash);
   1553 
   1554   GNUNET_CONTAINER_multishortmap_put(
   1555     group->context->member_pointers,
   1556     &hash,
   1557     user_pointer,
   1558     GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE
   1559   );
   1560 }
   1561 
   1562 
   1563 void*
   1564 GNUNET_CHAT_member_get_user_pointer (const struct GNUNET_CHAT_Group *group,
   1565 				                             const struct GNUNET_CHAT_Contact *member)
   1566 {
   1567   GNUNET_CHAT_VERSION_ASSERT();
   1568 
   1569   if ((!group) || (!(group->context)) || (!member))
   1570     return NULL;
   1571 
   1572   struct GNUNET_ShortHashCode hash;
   1573   util_shorthash_from_member(member->member, &hash);
   1574 
   1575   return GNUNET_CONTAINER_multishortmap_get(
   1576     group->context->member_pointers,
   1577     &hash
   1578   );
   1579 }
   1580 
   1581 
   1582 struct GNUNET_CHAT_Context*
   1583 GNUNET_CHAT_group_get_context (struct GNUNET_CHAT_Group *group)
   1584 {
   1585   GNUNET_CHAT_VERSION_ASSERT();
   1586 
   1587   if (!group)
   1588     return NULL;
   1589 
   1590   return group->context;
   1591 }
   1592 
   1593 
   1594 enum GNUNET_GenericReturnValue
   1595 GNUNET_CHAT_context_get_status (struct GNUNET_CHAT_Context *context)
   1596 {
   1597   GNUNET_CHAT_VERSION_ASSERT();
   1598 
   1599   if ((!context) || (!(context->room)))
   1600     return GNUNET_SYSERR;
   1601 
   1602   switch (context->type) {
   1603     case GNUNET_CHAT_CONTEXT_TYPE_CONTACT:
   1604     {
   1605       const struct GNUNET_CHAT_Contact *contact = GNUNET_CHAT_context_get_contact(
   1606         context
   1607       );
   1608 
   1609       return contact? GNUNET_OK : GNUNET_NO;
   1610     }
   1611     case GNUNET_CHAT_CONTEXT_TYPE_GROUP:
   1612       return GNUNET_OK;
   1613     default:
   1614       return GNUNET_NO;
   1615   }
   1616 }
   1617 
   1618 
   1619 enum GNUNET_GenericReturnValue
   1620 GNUNET_CHAT_context_request (struct GNUNET_CHAT_Context *context)
   1621 {
   1622   GNUNET_CHAT_VERSION_ASSERT();
   1623 
   1624   if (!context)
   1625     return GNUNET_SYSERR;
   1626   else if (context->room)
   1627     return GNUNET_OK;
   1628 
   1629   struct GNUNET_CHAT_Handle *handle = context->handle;
   1630 
   1631   if ((!handle) || (!(context->contact)))
   1632     return GNUNET_SYSERR;
   1633 
   1634   struct GNUNET_CHAT_Contact *contact = handle_get_contact_from_messenger(
   1635     handle, context->contact
   1636   );
   1637 
   1638   if (!contact)
   1639     return GNUNET_SYSERR;
   1640 
   1641   enum GNUNET_GenericReturnValue owned = GNUNET_CHAT_contact_is_owned(
   1642     contact
   1643   );
   1644 
   1645   context->type = GNUNET_CHAT_CONTEXT_TYPE_CONTACT;
   1646 
   1647   struct GNUNET_CHAT_Context *other = contact_find_context(
   1648     contact, GNUNET_YES
   1649   );
   1650 
   1651   if (!other)
   1652     return GNUNET_SYSERR;
   1653 
   1654   union GNUNET_MESSENGER_RoomKey key;
   1655   GNUNET_MESSENGER_create_room_key(
   1656     &key,
   1657     NULL,
   1658     GNUNET_YES == owned? GNUNET_YES : GNUNET_NO,
   1659     GNUNET_NO,
   1660     GNUNET_YES == owned? GNUNET_YES : GNUNET_NO
   1661   );
   1662 
   1663   if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(
   1664       handle->contexts, &(key.hash)))
   1665     return GNUNET_SYSERR;
   1666   
   1667   const struct GNUNET_PeerIdentity *pid = handle->pid;
   1668 
   1669   if (!pid)
   1670     return GNUNET_SYSERR;
   1671 
   1672   struct GNUNET_MESSENGER_Room *room;
   1673   if (GNUNET_YES == owned)
   1674   {
   1675     room = GNUNET_MESSENGER_enter_room(
   1676       handle->messenger,
   1677       pid,
   1678       &key
   1679     );
   1680   }
   1681   else
   1682     room = GNUNET_MESSENGER_open_room(
   1683       handle->messenger, &key
   1684     );
   1685 
   1686   if (!room)
   1687     return GNUNET_SYSERR;
   1688 
   1689   context_update_room(context, room, GNUNET_YES);
   1690 
   1691   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(
   1692       handle->contexts, &(key.hash), context,
   1693       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   1694   {
   1695     context_update_room(context, NULL, GNUNET_YES);
   1696     return GNUNET_SYSERR;
   1697   }
   1698 
   1699   if (GNUNET_YES != owned)
   1700   {
   1701     struct GNUNET_MESSENGER_Message msg;
   1702     memset(&msg, 0, sizeof(msg));
   1703 
   1704     msg.header.kind = GNUNET_MESSENGER_KIND_INVITE;
   1705     GNUNET_memcpy(&(msg.body.invite.door), pid, sizeof(msg.body.invite.door));
   1706     GNUNET_memcpy(&(msg.body.invite.key), &key, sizeof(msg.body.invite.key));
   1707 
   1708     GNUNET_MESSENGER_send_message(other->room, &msg, context->contact);
   1709   }
   1710 
   1711   return GNUNET_OK;
   1712 }
   1713 
   1714 
   1715 struct GNUNET_CHAT_Contact*
   1716 GNUNET_CHAT_context_get_contact (struct GNUNET_CHAT_Context *context)
   1717 {
   1718   GNUNET_CHAT_VERSION_ASSERT();
   1719 
   1720   if ((!context) || (GNUNET_CHAT_CONTEXT_TYPE_CONTACT != context->type))
   1721     return NULL;
   1722 
   1723   if (context->contact)
   1724     return handle_get_contact_from_messenger(context->handle, context->contact);
   1725 
   1726   struct GNUNET_MESSENGER_Room *room = context->room;
   1727   struct GNUNET_CHAT_RoomFindContact find;
   1728   union GNUNET_MESSENGER_RoomKey key;
   1729 
   1730   GNUNET_memcpy(&(key.hash), GNUNET_MESSENGER_room_get_key(room), sizeof(key.hash));
   1731 
   1732   if (key.code.group_bit)
   1733     return NULL;
   1734 
   1735   if (! key.code.feed_bit)
   1736     find.ignore_key = GNUNET_MESSENGER_get_key(context->handle->messenger);
   1737   else
   1738     find.ignore_key = NULL;
   1739   
   1740   find.contact = NULL;
   1741 
   1742   int member_count = GNUNET_MESSENGER_iterate_members(
   1743     room,
   1744     it_room_find_contact,
   1745     &find
   1746   );
   1747 
   1748   if ((!find.contact) || (member_count > 2))
   1749     return NULL;
   1750 
   1751   return handle_get_contact_from_messenger(context->handle, find.contact);
   1752 }
   1753 
   1754 
   1755 struct GNUNET_CHAT_Group*
   1756 GNUNET_CHAT_context_get_group (struct GNUNET_CHAT_Context *context)
   1757 {
   1758   GNUNET_CHAT_VERSION_ASSERT();
   1759 
   1760   if ((!context) || (GNUNET_CHAT_CONTEXT_TYPE_GROUP != context->type))
   1761     return NULL;
   1762 
   1763   if (!(context->room))
   1764     return NULL;
   1765 
   1766   return handle_get_group_from_messenger(context->handle, context->room);
   1767 }
   1768 
   1769 
   1770 void
   1771 GNUNET_CHAT_context_set_user_pointer (struct GNUNET_CHAT_Context *context,
   1772 				                              void *user_pointer)
   1773 {
   1774   GNUNET_CHAT_VERSION_ASSERT();
   1775 
   1776   if (!context)
   1777     return;
   1778 
   1779   context->user_pointer = user_pointer;
   1780 }
   1781 
   1782 
   1783 void*
   1784 GNUNET_CHAT_context_get_user_pointer (const struct GNUNET_CHAT_Context *context)
   1785 {
   1786   GNUNET_CHAT_VERSION_ASSERT();
   1787 
   1788   if (!context)
   1789     return NULL;
   1790 
   1791   return context->user_pointer;
   1792 }
   1793 
   1794 
   1795 enum GNUNET_GenericReturnValue
   1796 GNUNET_CHAT_context_send_text (struct GNUNET_CHAT_Context *context,
   1797 			                         const char *text)
   1798 {
   1799   GNUNET_CHAT_VERSION_ASSERT();
   1800 
   1801   if ((!context) || (!text) || (!(context->room)))
   1802     return GNUNET_SYSERR;
   1803 
   1804   struct GNUNET_MESSENGER_Message msg;
   1805   memset(&msg, 0, sizeof(msg));
   1806 
   1807   msg.header.kind = GNUNET_MESSENGER_KIND_TEXT;
   1808   msg.body.text.text = GNUNET_strdup(text);
   1809 
   1810   GNUNET_MESSENGER_send_message(context->room, &msg, NULL);
   1811 
   1812   GNUNET_free(msg.body.text.text);
   1813   return GNUNET_OK;
   1814 }
   1815 
   1816 
   1817 enum GNUNET_GenericReturnValue
   1818 GNUNET_CHAT_context_send_read_receipt (struct GNUNET_CHAT_Context *context,
   1819 				                               struct GNUNET_CHAT_Message *message)
   1820 {
   1821   GNUNET_CHAT_VERSION_ASSERT();
   1822 
   1823   if ((!context) || (!(context->room)))
   1824     return GNUNET_SYSERR;
   1825 
   1826   char zero = '\0';
   1827   struct GNUNET_MESSENGER_Message msg;
   1828   memset(&msg, 0, sizeof(msg));
   1829 
   1830   msg.header.kind = GNUNET_MESSENGER_KIND_TEXT;
   1831   msg.body.text.text = &zero;
   1832 
   1833   const struct GNUNET_MESSENGER_Contact *receiver = NULL;
   1834 
   1835   if (!message)
   1836     goto skip_filter;
   1837 
   1838   if (GNUNET_CHAT_FLAG_NONE != message->flag)
   1839     return GNUNET_SYSERR;
   1840 
   1841   if (message->flags & GNUNET_MESSENGER_FLAG_SENT)
   1842     return GNUNET_OK;
   1843 
   1844   if (message->flags & GNUNET_MESSENGER_FLAG_PRIVATE)
   1845   {
   1846     receiver = GNUNET_MESSENGER_get_sender(context->room, &(message->hash));
   1847 
   1848     if (!receiver)
   1849       return GNUNET_SYSERR;
   1850   }
   1851 
   1852   if ((GNUNET_YES != message_has_msg(message)) ||
   1853       (GNUNET_MESSENGER_KIND_TEXT != message->msg->header.kind))
   1854     goto skip_filter;
   1855 
   1856   if ((!(message->msg->body.text.text)) ||
   1857       (!(message->msg->body.text.text[0])))
   1858     return GNUNET_SYSERR;
   1859 
   1860 skip_filter:
   1861   GNUNET_MESSENGER_send_message(context->room, &msg, receiver);
   1862   return GNUNET_OK;
   1863 }
   1864 
   1865 
   1866 struct GNUNET_CHAT_File*
   1867 GNUNET_CHAT_context_send_file (struct GNUNET_CHAT_Context *context,
   1868                                const char *path,
   1869                                GNUNET_CHAT_FileUploadCallback callback,
   1870                                void *cls)
   1871 {
   1872   GNUNET_CHAT_VERSION_ASSERT();
   1873 
   1874   if ((!context) || (!path) || (!(context->room)))
   1875     return NULL;
   1876 
   1877   struct GNUNET_HashCode hash;
   1878   if (GNUNET_OK != util_hash_file(path, &hash))
   1879     return NULL;
   1880 
   1881   char *filename = handle_create_file_path(
   1882     context->handle, &hash
   1883   );
   1884 
   1885   if (!filename)
   1886     return NULL;
   1887 
   1888   struct GNUNET_CHAT_File *file = GNUNET_CONTAINER_multihashmap_get(
   1889     context->handle->files,
   1890     &hash
   1891   );
   1892 
   1893   if (file)
   1894     goto file_binding;
   1895 
   1896   if ((GNUNET_YES == GNUNET_DISK_file_test(filename)) ||
   1897       (GNUNET_OK != GNUNET_DISK_directory_create_for_file(filename)) ||
   1898       (GNUNET_OK != GNUNET_DISK_file_copy(path, filename)))
   1899   {
   1900     GNUNET_free(filename);
   1901     return NULL;
   1902   }
   1903 
   1904   struct GNUNET_CRYPTO_SymmetricSessionKey key;
   1905   GNUNET_CRYPTO_symmetric_create_session_key(&key);
   1906 
   1907   if (GNUNET_OK != util_encrypt_file(filename, &hash, &key))
   1908   {
   1909     GNUNET_free(filename);
   1910     return NULL;
   1911   }
   1912 
   1913   char* p = GNUNET_strdup(path);
   1914 
   1915   file = file_create_from_disk(
   1916     context->handle,
   1917     basename(p),
   1918     &hash,
   1919     &key
   1920   );
   1921 
   1922   GNUNET_free(p);
   1923 
   1924   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(
   1925       context->handle->files, &hash, file,
   1926       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   1927   {
   1928     file_destroy(file);
   1929     GNUNET_free(filename);
   1930     return NULL;
   1931   }
   1932 
   1933   struct GNUNET_FS_BlockOptions bo;
   1934 
   1935   bo.anonymity_level = block_anonymity_level;
   1936   bo.content_priority = block_content_priority;
   1937   bo.replication_level = block_replication_level;
   1938   bo.expiration_time = GNUNET_TIME_absolute_get_forever_();
   1939 
   1940   struct GNUNET_FS_FileInformation* fi = GNUNET_FS_file_information_create_from_file(
   1941     context->handle->fs,
   1942     file,
   1943     filename,
   1944     NULL,
   1945     file->meta,
   1946     GNUNET_YES,
   1947     &bo
   1948   );
   1949 
   1950   file->publish = GNUNET_FS_publish_start(
   1951     context->handle->fs, fi,
   1952     NULL, NULL, NULL,
   1953     GNUNET_FS_PUBLISH_OPTION_NONE
   1954   );
   1955 
   1956   if (file->publish)
   1957     file->status |= GNUNET_CHAT_FILE_STATUS_PUBLISH;
   1958 
   1959   GNUNET_free(filename);
   1960 
   1961 file_binding:
   1962   file_bind_upload(file, context, callback, cls);
   1963   return file;
   1964 }
   1965 
   1966 
   1967 enum GNUNET_GenericReturnValue
   1968 GNUNET_CHAT_context_share_file (struct GNUNET_CHAT_Context *context,
   1969 				                        struct GNUNET_CHAT_File *file)
   1970 {
   1971   GNUNET_CHAT_VERSION_ASSERT();
   1972 
   1973   if ((!context) || (!file) || 
   1974       (!(file->name)) || (strlen(file->name) > NAME_MAX) ||
   1975       (!(file->uri)) || (!(context->room)))
   1976     return GNUNET_SYSERR;
   1977 
   1978   struct GNUNET_MESSENGER_Message msg;
   1979   memset(&msg, 0, sizeof(msg));
   1980 
   1981   msg.header.kind = GNUNET_MESSENGER_KIND_FILE;
   1982 
   1983   if (file->key)
   1984     GNUNET_memcpy(&(msg.body.file.key), file->key,
   1985                   sizeof(struct GNUNET_CRYPTO_SymmetricSessionKey));
   1986   else
   1987     memset(&(msg.body.file.key), 0, sizeof(msg.body.file.key));
   1988 
   1989   GNUNET_memcpy(&(msg.body.file.hash), &(file->hash), sizeof(file->hash));
   1990   GNUNET_strlcpy(msg.body.file.name, file->name, NAME_MAX);
   1991   msg.body.file.uri = GNUNET_FS_uri_to_string(file->uri);
   1992 
   1993   GNUNET_MESSENGER_send_message(context->room, &msg, NULL);
   1994 
   1995   GNUNET_free(msg.body.file.uri);
   1996   return GNUNET_OK;
   1997 }
   1998 
   1999 
   2000 enum GNUNET_GenericReturnValue
   2001 GNUNET_CHAT_context_send_tag (struct GNUNET_CHAT_Context *context,
   2002                               struct GNUNET_CHAT_Message *message,
   2003                               const char *tag)
   2004 {
   2005   GNUNET_CHAT_VERSION_ASSERT();
   2006 
   2007   if ((!context) || (!message) || (!tag) || (!(context->room)))
   2008     return GNUNET_SYSERR;
   2009 
   2010   char *tag_value = GNUNET_strdup(tag);
   2011 
   2012   struct GNUNET_MESSENGER_Message msg;
   2013   memset(&msg, 0, sizeof(msg));
   2014 
   2015   msg.header.kind = GNUNET_MESSENGER_KIND_TAG;
   2016   GNUNET_memcpy(&(msg.body.tag.hash), &(message->hash),
   2017     sizeof(struct GNUNET_HashCode));
   2018   msg.body.tag.tag = tag_value;
   2019 
   2020   GNUNET_MESSENGER_send_message(
   2021     context->room,
   2022     &msg,
   2023     NULL
   2024   );
   2025 
   2026   GNUNET_free(tag_value);
   2027   return GNUNET_OK;
   2028 }
   2029 
   2030 
   2031 struct GNUNET_CHAT_Discourse*
   2032 GNUNET_CHAT_context_open_discourse (struct GNUNET_CHAT_Context *context,
   2033                                     const struct GNUNET_CHAT_DiscourseId *id)
   2034 {
   2035   GNUNET_CHAT_VERSION_ASSERT();
   2036 
   2037   if ((!context) || (!(context->discourses)) || (!(context->room)) || (!id))
   2038     return NULL;
   2039 
   2040   struct GNUNET_ShortHashCode sid;
   2041   util_shorthash_from_discourse_id(id, &sid);
   2042 
   2043   struct GNUNET_CHAT_Discourse *discourse = GNUNET_CONTAINER_multishortmap_get(
   2044     context->discourses, &sid
   2045   );
   2046 
   2047   if (!discourse)
   2048   {
   2049     discourse = discourse_create(context, id);
   2050 
   2051     if (GNUNET_OK != GNUNET_CONTAINER_multishortmap_put(context->discourses,
   2052         &sid, discourse, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   2053     {
   2054       discourse_destroy(discourse);
   2055       return NULL;
   2056     }
   2057   }
   2058 
   2059   struct GNUNET_MESSENGER_Message msg;
   2060   memset(&msg, 0, sizeof(msg));
   2061 
   2062   msg.header.kind = GNUNET_MESSENGER_KIND_SUBSCRIBTION;
   2063   GNUNET_memcpy(
   2064     &(msg.body.subscription.discourse),
   2065     &sid,
   2066     sizeof(struct GNUNET_ShortHashCode)
   2067   );
   2068 
   2069   const struct GNUNET_TIME_Relative subscription_time = GNUNET_TIME_relative_multiply(
   2070     GNUNET_TIME_relative_get_second_(), 10
   2071   );
   2072 
   2073   msg.body.subscription.time = GNUNET_TIME_relative_hton(subscription_time);
   2074   msg.body.subscription.flags = GNUNET_MESSENGER_FLAG_SUBSCRIPTION_KEEP_ALIVE;
   2075 
   2076   GNUNET_MESSENGER_send_message(
   2077     context->room,
   2078     &msg,
   2079     NULL
   2080   );
   2081 
   2082   return discourse;
   2083 }
   2084 
   2085 
   2086 int
   2087 GNUNET_CHAT_context_iterate_messages (struct GNUNET_CHAT_Context *context,
   2088                                       GNUNET_CHAT_ContextMessageCallback callback,
   2089                                       void *cls)
   2090 {
   2091   GNUNET_CHAT_VERSION_ASSERT();
   2092 
   2093   if (!context)
   2094     return GNUNET_SYSERR;
   2095 
   2096   struct GNUNET_CHAT_ContextIterateMessages it;
   2097   it.context = context;
   2098   it.cb = callback;
   2099   it.cls = cls;
   2100 
   2101   return GNUNET_CONTAINER_multihashmap_iterate(
   2102     context->messages, it_context_iterate_messages, &it
   2103   );
   2104 }
   2105 
   2106 
   2107 int
   2108 GNUNET_CHAT_context_iterate_files (struct GNUNET_CHAT_Context *context,
   2109                                    GNUNET_CHAT_ContextFileCallback callback,
   2110                                    void *cls)
   2111 {
   2112   GNUNET_CHAT_VERSION_ASSERT();
   2113 
   2114   if (!context)
   2115     return GNUNET_SYSERR;
   2116 
   2117   struct GNUNET_CHAT_ContextIterateFiles it;
   2118   it.context = context;
   2119   it.cb = callback;
   2120   it.cls = cls;
   2121 
   2122   return GNUNET_CONTAINER_multihashmap_iterate(
   2123     context->files, it_context_iterate_files, &it
   2124   );
   2125 }
   2126 
   2127 
   2128 enum GNUNET_CHAT_MessageKind
   2129 GNUNET_CHAT_message_get_kind (const struct GNUNET_CHAT_Message *message)
   2130 {
   2131   GNUNET_CHAT_VERSION_ASSERT();
   2132 
   2133   if (!message)
   2134     return GNUNET_CHAT_KIND_UNKNOWN;
   2135 
   2136   switch (message->flag)
   2137   {
   2138     case GNUNET_CHAT_FLAG_WARNING:
   2139       return GNUNET_CHAT_KIND_WARNING;
   2140     case GNUNET_CHAT_FLAG_REFRESH:
   2141       return GNUNET_CHAT_KIND_REFRESH;
   2142     case GNUNET_CHAT_FLAG_LOGIN:
   2143       return GNUNET_CHAT_KIND_LOGIN;
   2144     case GNUNET_CHAT_FLAG_LOGOUT:
   2145       return GNUNET_CHAT_KIND_LOGOUT;
   2146     case GNUNET_CHAT_FLAG_CREATE_ACCOUNT:
   2147       return GNUNET_CHAT_KIND_CREATED_ACCOUNT;
   2148     case GNUNET_CHAT_FLAG_DELETE_ACCOUNT:
   2149       return GNUNET_CHAT_KIND_DELETED_ACCOUNT;
   2150     case GNUNET_CHAT_FLAG_UPDATE_ACCOUNT:
   2151       return GNUNET_CHAT_KIND_UPDATE_ACCOUNT;
   2152     case GNUNET_CHAT_FLAG_UPDATE_CONTEXT:
   2153       return GNUNET_CHAT_KIND_UPDATE_CONTEXT;
   2154     case GNUNET_CHAT_FLAG_ATTRIBUTES:
   2155       return GNUNET_CHAT_KIND_ATTRIBUTES;
   2156     case GNUNET_CHAT_FLAG_SHARE_ATTRIBUTES:
   2157       return GNUNET_CHAT_KIND_SHARED_ATTRIBUTES;
   2158     default:
   2159       break;
   2160   }
   2161 
   2162   if (GNUNET_YES != message_has_msg(message))
   2163     return GNUNET_CHAT_KIND_UNKNOWN;
   2164 
   2165   return util_message_kind_from_kind(message->msg->header.kind);
   2166 }
   2167 
   2168 
   2169 time_t
   2170 GNUNET_CHAT_message_get_timestamp (const struct GNUNET_CHAT_Message *message)
   2171 {
   2172   GNUNET_CHAT_VERSION_ASSERT();
   2173 
   2174   if ((!message) || (GNUNET_YES != message_has_msg(message)))
   2175     return ((time_t) -1);
   2176 
   2177   struct GNUNET_TIME_Absolute abs = GNUNET_TIME_absolute_ntoh(
   2178     message->msg->header.timestamp
   2179   );
   2180 
   2181   struct GNUNET_TIME_Timestamp ts = GNUNET_TIME_absolute_to_timestamp(
   2182     abs
   2183   );
   2184 
   2185   return (time_t) GNUNET_TIME_timestamp_to_s(ts);
   2186 }
   2187 
   2188 
   2189 struct GNUNET_CHAT_Contact*
   2190 GNUNET_CHAT_message_get_sender (const struct GNUNET_CHAT_Message *message)
   2191 {
   2192   GNUNET_CHAT_VERSION_ASSERT();
   2193 
   2194   if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
   2195       (!(message->context)) || (!(message->context->room)))
   2196     return NULL;
   2197 
   2198   const struct GNUNET_MESSENGER_Contact *sender = GNUNET_MESSENGER_get_sender(
   2199     message->context->room, &(message->hash)
   2200   );
   2201 
   2202   if (!sender)
   2203     return NULL;
   2204 
   2205   return handle_get_contact_from_messenger(message->context->handle, sender);
   2206 }
   2207 
   2208 
   2209 struct GNUNET_CHAT_Contact*
   2210 GNUNET_CHAT_message_get_recipient (const struct GNUNET_CHAT_Message *message)
   2211 {
   2212   GNUNET_CHAT_VERSION_ASSERT();
   2213 
   2214   if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
   2215       (!(message->context)) || (!(message->context->room)))
   2216     return NULL;
   2217 
   2218   const struct GNUNET_MESSENGER_Contact *recipient = GNUNET_MESSENGER_get_recipient(
   2219     message->context->room, &(message->hash)
   2220   );
   2221 
   2222   if (!recipient)
   2223     return NULL;
   2224 
   2225   return handle_get_contact_from_messenger(message->context->handle, recipient);
   2226 }
   2227 
   2228 
   2229 enum GNUNET_GenericReturnValue
   2230 GNUNET_CHAT_message_is_sent (const struct GNUNET_CHAT_Message *message)
   2231 {
   2232   GNUNET_CHAT_VERSION_ASSERT();
   2233 
   2234   if (!message)
   2235     return GNUNET_SYSERR;
   2236 
   2237   if (message->flags & GNUNET_MESSENGER_FLAG_SENT)
   2238     return GNUNET_YES;
   2239   else
   2240     return GNUNET_NO;
   2241 }
   2242 
   2243 
   2244 enum GNUNET_GenericReturnValue
   2245 GNUNET_CHAT_message_is_private (const struct GNUNET_CHAT_Message *message)
   2246 {
   2247   GNUNET_CHAT_VERSION_ASSERT();
   2248 
   2249   if (!message)
   2250     return GNUNET_SYSERR;
   2251 
   2252   if (message->flags & GNUNET_MESSENGER_FLAG_PRIVATE)
   2253     return GNUNET_YES;
   2254   else
   2255     return GNUNET_NO;
   2256 }
   2257 
   2258 
   2259 enum GNUNET_GenericReturnValue
   2260 GNUNET_CHAT_message_is_recent (const struct GNUNET_CHAT_Message *message)
   2261 {
   2262   GNUNET_CHAT_VERSION_ASSERT();
   2263 
   2264   if (!message)
   2265     return GNUNET_SYSERR;
   2266 
   2267   if (message->flags & GNUNET_MESSENGER_FLAG_RECENT)
   2268     return GNUNET_YES;
   2269   else
   2270     return GNUNET_NO;
   2271 }
   2272 
   2273 
   2274 enum GNUNET_GenericReturnValue
   2275 GNUNET_CHAT_message_is_update (const struct GNUNET_CHAT_Message *message)
   2276 {
   2277   GNUNET_CHAT_VERSION_ASSERT();
   2278 
   2279   if (!message)
   2280     return GNUNET_SYSERR;
   2281 
   2282   if (message->flags & GNUNET_MESSENGER_FLAG_UPDATE)
   2283     return GNUNET_YES;
   2284   else
   2285     return GNUNET_NO;
   2286 }
   2287 
   2288 
   2289 enum GNUNET_GenericReturnValue
   2290 GNUNET_CHAT_message_is_deleted (const struct GNUNET_CHAT_Message *message)
   2291 {
   2292   GNUNET_CHAT_VERSION_ASSERT();
   2293 
   2294   if (!message)
   2295     return GNUNET_SYSERR;
   2296 
   2297   if ((GNUNET_CHAT_FLAG_NONE == message->flag) &&
   2298       ((message->flags & GNUNET_MESSENGER_FLAG_DELETE) ||
   2299        (!message->msg)))
   2300     return GNUNET_YES;
   2301   else
   2302     return GNUNET_NO;
   2303 }
   2304 
   2305 
   2306 enum GNUNET_GenericReturnValue
   2307 GNUNET_CHAT_message_is_tagged (const struct GNUNET_CHAT_Message *message,
   2308                                const char *tag)
   2309 {
   2310   GNUNET_CHAT_VERSION_ASSERT();
   2311 
   2312   if ((!message) || (!(message->context)))
   2313     return GNUNET_SYSERR;
   2314 
   2315   const struct GNUNET_CHAT_InternalTagging *tagging = GNUNET_CONTAINER_multihashmap_get(
   2316     message->context->taggings, &(message->hash));
   2317   
   2318   if (!tagging)
   2319     return GNUNET_NO;
   2320 
   2321   if (internal_tagging_iterate(tagging, GNUNET_NO, tag, NULL, NULL) > 0)
   2322     return GNUNET_YES;
   2323   else
   2324     return GNUNET_NO;
   2325 }
   2326 
   2327 
   2328 int
   2329 GNUNET_CHAT_message_get_read_receipt (struct GNUNET_CHAT_Message *message,
   2330                                       GNUNET_CHAT_MessageReadReceiptCallback callback,
   2331                                       void *cls)
   2332 {
   2333   GNUNET_CHAT_VERSION_ASSERT();
   2334 
   2335   if ((!message) || (GNUNET_YES != message_has_msg(message)) || 
   2336       (!(message->context)))
   2337     return GNUNET_SYSERR;
   2338 
   2339   struct GNUNET_CHAT_MessageIterateReadReceipts it;
   2340   it.message = message;
   2341   it.cb = callback;
   2342   it.cls = cls;
   2343 
   2344   return GNUNET_MESSENGER_iterate_members(
   2345     message->context->room, it_message_iterate_read_receipts, &it
   2346   );
   2347 }
   2348 
   2349 
   2350 const char*
   2351 GNUNET_CHAT_message_get_text (const struct GNUNET_CHAT_Message *message)
   2352 {
   2353   GNUNET_CHAT_VERSION_ASSERT();
   2354 
   2355   if (!message)
   2356     return NULL;
   2357 
   2358   if (GNUNET_CHAT_FLAG_WARNING == message->flag)
   2359     return message->warning;
   2360   else if (GNUNET_CHAT_FLAG_UPDATE_ACCOUNT == message->flag)
   2361     return message->warning;
   2362   else if (GNUNET_CHAT_FLAG_ATTRIBUTES == message->flag)
   2363     return message->attr;
   2364 
   2365   if (GNUNET_YES != message_has_msg(message))
   2366     return NULL;
   2367 
   2368   if (GNUNET_MESSENGER_KIND_TEXT == message->msg->header.kind)
   2369     return message->msg->body.text.text;
   2370   else if (GNUNET_MESSENGER_KIND_FILE == message->msg->header.kind)
   2371     return message->msg->body.file.name;
   2372   else if (GNUNET_MESSENGER_KIND_TAG == message->msg->header.kind)
   2373     return message->msg->body.tag.tag;
   2374   else
   2375     return NULL;
   2376 }
   2377 
   2378 
   2379 void
   2380 GNUNET_CHAT_message_set_user_pointer (struct GNUNET_CHAT_Message *message,
   2381                                       void *user_pointer)
   2382 {
   2383   GNUNET_CHAT_VERSION_ASSERT();
   2384 
   2385   if (!message)
   2386     return;
   2387 
   2388   message->user_pointer = user_pointer;
   2389 }
   2390 
   2391 
   2392 void*
   2393 GNUNET_CHAT_message_get_user_pointer (const struct GNUNET_CHAT_Message *message)
   2394 {
   2395   GNUNET_CHAT_VERSION_ASSERT();
   2396 
   2397   if (!message)
   2398     return NULL;
   2399 
   2400   return message->user_pointer;
   2401 }
   2402 
   2403 
   2404 struct GNUNET_CHAT_Account*
   2405 GNUNET_CHAT_message_get_account (const struct GNUNET_CHAT_Message *message)
   2406 {
   2407   GNUNET_CHAT_VERSION_ASSERT();
   2408 
   2409   if (!message)
   2410     return NULL;
   2411 
   2412   if ((message->context) && (message->context->handle))
   2413     return message->context->handle->current;
   2414   else
   2415     return message->account;
   2416 }
   2417 
   2418 
   2419 struct GNUNET_CHAT_File*
   2420 GNUNET_CHAT_message_get_file (const struct GNUNET_CHAT_Message *message)
   2421 {
   2422   GNUNET_CHAT_VERSION_ASSERT();
   2423 
   2424   if ((!message) || (GNUNET_YES != message_has_msg(message)) || 
   2425       (!(message->context)))
   2426     return NULL;
   2427 
   2428   if (GNUNET_MESSENGER_KIND_FILE != message->msg->header.kind)
   2429     return NULL;
   2430 
   2431   return GNUNET_CONTAINER_multihashmap_get(
   2432     message->context->handle->files,
   2433     &(message->msg->body.file.hash)
   2434   );
   2435 }
   2436 
   2437 
   2438 struct GNUNET_CHAT_Invitation*
   2439 GNUNET_CHAT_message_get_invitation (const struct GNUNET_CHAT_Message *message)
   2440 {
   2441   GNUNET_CHAT_VERSION_ASSERT();
   2442 
   2443   if ((!message) || (GNUNET_YES != message_has_msg(message)) || 
   2444       (!(message->context)))
   2445     return NULL;
   2446 
   2447   if (GNUNET_MESSENGER_KIND_INVITE != message->msg->header.kind)
   2448     return NULL;
   2449 
   2450   return GNUNET_CONTAINER_multihashmap_get(
   2451     message->context->invites,
   2452     &(message->hash)
   2453   );
   2454 }
   2455 
   2456 
   2457 struct GNUNET_CHAT_Discourse*
   2458 GNUNET_CHAT_message_get_discourse (const struct GNUNET_CHAT_Message *message)
   2459 {
   2460   GNUNET_CHAT_VERSION_ASSERT();
   2461 
   2462   if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
   2463       (!(message->context)) || (!(message->context->discourses)))
   2464     return NULL;
   2465   
   2466   struct GNUNET_CHAT_Discourse *discourse;
   2467   
   2468   if (GNUNET_MESSENGER_KIND_SUBSCRIBTION == message->msg->header.kind)
   2469     discourse = GNUNET_CONTAINER_multishortmap_get(
   2470       message->context->discourses,
   2471       &(message->msg->body.subscription.discourse));
   2472   else if (GNUNET_MESSENGER_KIND_TALK == message->msg->header.kind)
   2473     discourse = GNUNET_CONTAINER_multishortmap_get(
   2474       message->context->discourses,
   2475       &(message->msg->body.talk.discourse));
   2476   else
   2477     discourse = NULL;
   2478 
   2479   return discourse;
   2480 }
   2481 
   2482 
   2483 struct GNUNET_CHAT_Message*
   2484 GNUNET_CHAT_message_get_target (const struct GNUNET_CHAT_Message *message)
   2485 {
   2486   GNUNET_CHAT_VERSION_ASSERT();
   2487 
   2488   if ((!message) || (GNUNET_YES != message_has_msg(message)) || 
   2489       (!(message->context)))
   2490     return NULL;
   2491 
   2492   struct GNUNET_CHAT_Message *target;
   2493 
   2494   if (GNUNET_MESSENGER_KIND_DELETION == message->msg->header.kind)
   2495     target = GNUNET_CONTAINER_multihashmap_get(
   2496 	message->context->messages, &(message->msg->body.deletion.hash));
   2497   else if (GNUNET_MESSENGER_KIND_TAG == message->msg->header.kind)
   2498     target = GNUNET_CONTAINER_multihashmap_get(
   2499       message->context->messages, &(message->msg->body.tag.hash));
   2500   else
   2501     target = NULL;
   2502 
   2503   return target;
   2504 }
   2505 
   2506 
   2507 enum GNUNET_GenericReturnValue
   2508 GNUNET_CHAT_message_delete (struct GNUNET_CHAT_Message *message,
   2509 			                      unsigned int delay)
   2510 {
   2511   GNUNET_CHAT_VERSION_ASSERT();
   2512 
   2513   if ((!message) || (GNUNET_YES != message_has_msg(message)) || 
   2514       (!(message->context)))
   2515     return GNUNET_SYSERR;
   2516   
   2517   struct GNUNET_TIME_Relative rel = GNUNET_TIME_relative_multiply(
   2518     GNUNET_TIME_relative_get_second_(), delay
   2519   );
   2520 
   2521   GNUNET_MESSENGER_delete_message(
   2522     message->context->room,
   2523     &(message->hash),
   2524     rel
   2525   );
   2526 
   2527   return GNUNET_OK;
   2528 }
   2529 
   2530 
   2531 int
   2532 GNUNET_CHAT_message_iterate_tags (struct GNUNET_CHAT_Message *message,
   2533                                   GNUNET_CHAT_MessageCallback callback,
   2534                                   void *cls)
   2535 {
   2536   GNUNET_CHAT_VERSION_ASSERT();
   2537 
   2538   if ((!message) || (!(message->context)))
   2539     return GNUNET_SYSERR;
   2540 
   2541   const struct GNUNET_CHAT_InternalTagging *tagging = GNUNET_CONTAINER_multihashmap_get(
   2542     message->context->taggings, &(message->hash));
   2543   
   2544   if (!tagging)
   2545     return 0;
   2546 
   2547   return internal_tagging_iterate(tagging, GNUNET_YES, NULL, callback, cls);
   2548 }
   2549 
   2550 
   2551 uint64_t
   2552 GNUNET_CHAT_message_available (const struct GNUNET_CHAT_Message *message)
   2553 {
   2554   GNUNET_CHAT_VERSION_ASSERT();
   2555 
   2556   if ((!message) || (GNUNET_YES != message_has_msg(message)))
   2557     return 0;
   2558 
   2559   if (GNUNET_MESSENGER_KIND_TALK == message->msg->header.kind)
   2560     return message->msg->body.talk.length;
   2561   else
   2562     return 0;
   2563 }
   2564 
   2565 
   2566 enum GNUNET_GenericReturnValue
   2567 GNUNET_CHAT_message_read (const struct GNUNET_CHAT_Message *message,
   2568                           char *data,
   2569                           uint64_t size)
   2570 {
   2571   GNUNET_CHAT_VERSION_ASSERT();
   2572 
   2573   if ((!message) || (GNUNET_YES != message_has_msg(message)))
   2574     return GNUNET_SYSERR;
   2575 
   2576   if (GNUNET_MESSENGER_KIND_TALK != message->msg->header.kind)
   2577     return GNUNET_SYSERR;
   2578 
   2579   const uint64_t available = message->msg->body.talk.length;
   2580 
   2581   if (available < size)
   2582     return GNUNET_NO;
   2583 
   2584   GNUNET_memcpy(
   2585     data,
   2586     message->msg->body.talk.data,
   2587     size
   2588   );
   2589 
   2590   return GNUNET_OK;
   2591 }
   2592 
   2593 
   2594 enum GNUNET_GenericReturnValue
   2595 GNUNET_CHAT_message_feed (const struct GNUNET_CHAT_Message *message,
   2596                           int fd)
   2597 {
   2598   GNUNET_CHAT_VERSION_ASSERT();
   2599 
   2600   if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
   2601       (fd == -1))
   2602     return GNUNET_SYSERR;
   2603 
   2604   if (GNUNET_MESSENGER_KIND_TALK != message->msg->header.kind)
   2605     return GNUNET_SYSERR;
   2606 
   2607   if (!(message->msg->body.talk.length))
   2608     return GNUNET_NO;
   2609 
   2610   const ssize_t written = write(
   2611     fd,
   2612     message->msg->body.talk.data,
   2613     message->msg->body.talk.length
   2614   );
   2615 
   2616   if (-1 == written)
   2617     return GNUNET_SYSERR;
   2618   else if (written != message->msg->body.talk.length)
   2619     return GNUNET_NO;
   2620   else
   2621     return GNUNET_OK;
   2622 }
   2623 
   2624 
   2625 const char*
   2626 GNUNET_CHAT_file_get_name (const struct GNUNET_CHAT_File *file)
   2627 {
   2628   GNUNET_CHAT_VERSION_ASSERT();
   2629 
   2630   if (!file)
   2631     return NULL;
   2632 
   2633   return file->name;
   2634 }
   2635 
   2636 
   2637 const char*
   2638 GNUNET_CHAT_file_get_hash (const struct GNUNET_CHAT_File *file)
   2639 {
   2640   GNUNET_CHAT_VERSION_ASSERT();
   2641 
   2642   if (!file)
   2643     return NULL;
   2644 
   2645   return GNUNET_h2s_full(&(file->hash));
   2646 }
   2647 
   2648 
   2649 uint64_t
   2650 GNUNET_CHAT_file_get_size (const struct GNUNET_CHAT_File *file)
   2651 {
   2652   GNUNET_CHAT_VERSION_ASSERT();
   2653 
   2654   if ((!file) || (!(file->uri)))
   2655     return 0;
   2656 
   2657   return GNUNET_FS_uri_chk_get_file_size(file->uri);
   2658 }
   2659 
   2660 
   2661 uint64_t
   2662 GNUNET_CHAT_file_get_local_size (const struct GNUNET_CHAT_File *file)
   2663 {
   2664   GNUNET_CHAT_VERSION_ASSERT();
   2665 
   2666   if (!file)
   2667     return 0;
   2668 
   2669   char *filename = handle_create_file_path(
   2670     file->handle, &(file->hash)
   2671   );
   2672 
   2673   if (!filename)
   2674     return 0;
   2675 
   2676   uint64_t size;
   2677   if (GNUNET_OK != GNUNET_DISK_file_size(filename, &size, GNUNET_NO, GNUNET_YES))
   2678     size = 0;
   2679 
   2680   GNUNET_free(filename);
   2681   return size;
   2682 }
   2683 
   2684 
   2685 struct GNUNET_CHAT_Uri*
   2686 GNUNET_CHAT_file_get_uri (const struct GNUNET_CHAT_File *file)
   2687 {
   2688   GNUNET_CHAT_VERSION_ASSERT();
   2689 
   2690   if ((!file) || (!(file->uri)))
   2691     return NULL;
   2692 
   2693   return uri_create_file(file->uri);
   2694 }
   2695 
   2696 
   2697 enum GNUNET_GenericReturnValue
   2698 GNUNET_CHAT_file_is_uploading (const struct GNUNET_CHAT_File *file)
   2699 {
   2700   GNUNET_CHAT_VERSION_ASSERT();
   2701 
   2702   if ((!file) || (0 == (file->status & GNUNET_CHAT_FILE_STATUS_PUBLISH)))
   2703     return GNUNET_NO;
   2704   else
   2705     return GNUNET_YES;
   2706 }
   2707 
   2708 
   2709 enum GNUNET_GenericReturnValue
   2710 GNUNET_CHAT_file_is_ready (const struct GNUNET_CHAT_File *file)
   2711 {
   2712   GNUNET_CHAT_VERSION_ASSERT();
   2713 
   2714   if ((!file) || (file->status & GNUNET_CHAT_FILE_STATUS_MASK))
   2715     return GNUNET_NO;
   2716 
   2717   const uint64_t size = GNUNET_CHAT_file_get_size(file);
   2718   const uint64_t local_size = GNUNET_CHAT_file_get_local_size(file);
   2719 
   2720   if (size != local_size)
   2721     return GNUNET_NO;
   2722   else
   2723     return GNUNET_YES;
   2724 }
   2725 
   2726 
   2727 const char*
   2728 GNUNET_CHAT_file_open_preview (struct GNUNET_CHAT_File *file)
   2729 {
   2730   GNUNET_CHAT_VERSION_ASSERT();
   2731 
   2732   if (!file)
   2733     return NULL;
   2734 
   2735   if (file->preview)
   2736     return file->preview;
   2737 
   2738   char *filename = handle_create_file_path(
   2739     file->handle, &(file->hash)
   2740   );
   2741 
   2742   if (!filename)
   2743     return NULL;
   2744 
   2745   if (GNUNET_YES != GNUNET_DISK_file_test(filename))
   2746     goto free_filename;
   2747 
   2748   if (!(file->key))
   2749   {
   2750     file->preview = filename;
   2751     return file->preview;
   2752   }
   2753 
   2754   file->preview = GNUNET_DISK_mktemp(
   2755     file->name? file->name : ""
   2756   );
   2757 
   2758   if (!(file->preview))
   2759     goto free_filename;
   2760 
   2761   remove(file->preview);
   2762 
   2763   if ((GNUNET_OK != GNUNET_DISK_file_copy(filename, file->preview)) ||
   2764       (GNUNET_OK != util_decrypt_file(file->preview,
   2765       &(file->hash), file->key)))
   2766   {
   2767     GNUNET_free(file->preview);
   2768     file->preview = NULL;
   2769   }
   2770 
   2771 free_filename:
   2772   GNUNET_free(filename);
   2773   return file->preview;
   2774 }
   2775 
   2776 
   2777 void
   2778 GNUNET_CHAT_file_close_preview (struct GNUNET_CHAT_File *file)
   2779 {
   2780   GNUNET_CHAT_VERSION_ASSERT();
   2781 
   2782   if ((!file) || (!(file->preview)))
   2783     return;
   2784 
   2785   if (!(file->key))
   2786     goto skip_filename;
   2787 
   2788   char *filename = handle_create_file_path(
   2789     file->handle, &(file->hash)
   2790   );
   2791 
   2792   if (!filename)
   2793     goto skip_filename;
   2794 
   2795   if (0 != strcmp(filename, file->preview))
   2796     remove(file->preview);
   2797 
   2798   GNUNET_free(filename);
   2799 
   2800 skip_filename:
   2801   GNUNET_free(file->preview);
   2802   file->preview = NULL;
   2803 }
   2804 
   2805 
   2806 void
   2807 GNUNET_CHAT_file_set_user_pointer (struct GNUNET_CHAT_File *file,
   2808 				                           void *user_pointer)
   2809 {
   2810   GNUNET_CHAT_VERSION_ASSERT();
   2811 
   2812   if (!file)
   2813     return;
   2814 
   2815   file->user_pointer = user_pointer;
   2816 }
   2817 
   2818 
   2819 void*
   2820 GNUNET_CHAT_file_get_user_pointer (const struct GNUNET_CHAT_File *file)
   2821 {
   2822   GNUNET_CHAT_VERSION_ASSERT();
   2823 
   2824   if (!file)
   2825     return NULL;
   2826 
   2827   return file->user_pointer;
   2828 }
   2829 
   2830 
   2831 enum GNUNET_GenericReturnValue
   2832 GNUNET_CHAT_file_is_downloading (const struct GNUNET_CHAT_File *file)
   2833 {
   2834   GNUNET_CHAT_VERSION_ASSERT();
   2835 
   2836   if ((!file) || (0 == (file->status & GNUNET_CHAT_FILE_STATUS_DOWNLOAD)))
   2837     return GNUNET_NO;
   2838   else
   2839     return GNUNET_YES;
   2840 }
   2841 
   2842 
   2843 enum GNUNET_GenericReturnValue
   2844 GNUNET_CHAT_file_start_download (struct GNUNET_CHAT_File *file,
   2845                                  GNUNET_CHAT_FileDownloadCallback callback,
   2846                                  void *cls)
   2847 {
   2848   GNUNET_CHAT_VERSION_ASSERT();
   2849 
   2850   if ((!file) || (!(file->uri)))
   2851     return GNUNET_SYSERR;
   2852 
   2853   if (file->download)
   2854   {
   2855     file_bind_downlaod(file, callback, cls);
   2856 
   2857     GNUNET_FS_download_resume(file->download);
   2858     return GNUNET_OK;
   2859   }
   2860 
   2861   char *filename = handle_create_file_path(
   2862     file->handle, &(file->hash)
   2863   );
   2864 
   2865   if (!filename)
   2866     return GNUNET_SYSERR;
   2867 
   2868   const uint64_t size = GNUNET_FS_uri_chk_get_file_size(file->uri);
   2869 
   2870   uint64_t offset;
   2871   if (GNUNET_OK != GNUNET_DISK_file_size(filename, &offset, 
   2872       GNUNET_NO, GNUNET_YES))
   2873     offset = 0;
   2874 
   2875   if (offset >= size)
   2876   {
   2877     if (callback)
   2878       callback(cls, file, size, size);
   2879 
   2880     goto free_filename;
   2881   }
   2882 
   2883   file_bind_downlaod(file, callback, cls);
   2884 
   2885   const uint64_t remaining = (size - offset);
   2886 
   2887   file->download = GNUNET_FS_download_start(
   2888     file->handle->fs,
   2889     file->uri,
   2890     file->meta,
   2891     filename,
   2892     NULL,
   2893     offset,
   2894     remaining,
   2895     1,
   2896     GNUNET_FS_DOWNLOAD_OPTION_NONE,
   2897     file,
   2898     NULL
   2899   );
   2900 
   2901   if (file->download)
   2902     file->status |= GNUNET_CHAT_FILE_STATUS_DOWNLOAD;
   2903 
   2904 free_filename:
   2905   GNUNET_free(filename);
   2906   return GNUNET_OK;
   2907 }
   2908 
   2909 
   2910 enum GNUNET_GenericReturnValue
   2911 GNUNET_CHAT_file_pause_download (struct GNUNET_CHAT_File *file)
   2912 {
   2913   GNUNET_CHAT_VERSION_ASSERT();
   2914 
   2915   if (!file)
   2916     return GNUNET_SYSERR;
   2917 
   2918   GNUNET_FS_download_suspend(file->download);
   2919   return GNUNET_OK;
   2920 }
   2921 
   2922 
   2923 enum GNUNET_GenericReturnValue
   2924 GNUNET_CHAT_file_resume_download (struct GNUNET_CHAT_File *file)
   2925 {
   2926   GNUNET_CHAT_VERSION_ASSERT();
   2927 
   2928   if (!file)
   2929     return GNUNET_SYSERR;
   2930 
   2931   GNUNET_FS_download_resume(file->download);
   2932   return GNUNET_OK;
   2933 }
   2934 
   2935 
   2936 enum GNUNET_GenericReturnValue
   2937 GNUNET_CHAT_file_stop_download (struct GNUNET_CHAT_File *file)
   2938 {
   2939   GNUNET_CHAT_VERSION_ASSERT();
   2940 
   2941   if (!file)
   2942     return GNUNET_SYSERR;
   2943 
   2944   GNUNET_FS_download_stop(file->download, GNUNET_YES);
   2945   file->download = NULL;
   2946   return GNUNET_OK;
   2947 }
   2948 
   2949 
   2950 enum GNUNET_GenericReturnValue
   2951 GNUNET_CHAT_file_is_unindexing (const struct GNUNET_CHAT_File *file)
   2952 {
   2953   GNUNET_CHAT_VERSION_ASSERT();
   2954 
   2955   if ((!file) || (0 == (file->status & GNUNET_CHAT_FILE_STATUS_UNINDEX)))
   2956     return GNUNET_NO;
   2957   else
   2958     return GNUNET_YES;
   2959 }
   2960 
   2961 
   2962 enum GNUNET_GenericReturnValue
   2963 GNUNET_CHAT_file_unindex (struct GNUNET_CHAT_File *file,
   2964                           GNUNET_CHAT_FileUnindexCallback callback,
   2965                           void *cls)
   2966 {
   2967   GNUNET_CHAT_VERSION_ASSERT();
   2968 
   2969   if (!file)
   2970     return GNUNET_SYSERR;
   2971 
   2972   if (file->publish)
   2973   {
   2974     GNUNET_FS_publish_stop(file->publish);
   2975     file->publish = NULL;
   2976     return GNUNET_OK;
   2977   }
   2978 
   2979   file_bind_unindex(file, callback, cls);
   2980 
   2981   if (file->unindex)
   2982     return GNUNET_OK;
   2983 
   2984   char *filename = handle_create_file_path(
   2985     file->handle, &(file->hash)
   2986   );
   2987 
   2988   if (!filename)
   2989     return GNUNET_SYSERR;
   2990 
   2991   file->unindex = GNUNET_FS_unindex_start(
   2992     file->handle->fs, filename, file
   2993   );
   2994 
   2995   if (file->unindex)
   2996     file->status |= GNUNET_CHAT_FILE_STATUS_UNINDEX;
   2997 
   2998   GNUNET_free(filename);
   2999   return GNUNET_OK;
   3000 }
   3001 
   3002 
   3003 void
   3004 GNUNET_CHAT_invitation_accept (struct GNUNET_CHAT_Invitation *invitation)
   3005 {
   3006   GNUNET_CHAT_VERSION_ASSERT();
   3007 
   3008   if (!invitation)
   3009     return;
   3010 
   3011   struct GNUNET_CHAT_Handle *handle;
   3012   handle = invitation->context->handle;
   3013 
   3014   if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(
   3015       handle->contexts, &(invitation->key.hash)))
   3016     return;
   3017 
   3018   struct GNUNET_PeerIdentity door;
   3019   GNUNET_PEER_resolve(invitation->door, &door);
   3020 
   3021   struct GNUNET_MESSENGER_Room *room;
   3022   room = GNUNET_MESSENGER_enter_room(
   3023     invitation->context->handle->messenger,
   3024     &door, &(invitation->key)
   3025   );
   3026 
   3027   if (!room)
   3028     return;
   3029 
   3030   struct GNUNET_CHAT_Context *context;
   3031   context = context_create_from_room(handle, room);
   3032 
   3033   if (!context)
   3034     return;
   3035 
   3036   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(
   3037       handle->contexts, &(invitation->key.hash), context,
   3038       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   3039     goto destroy_context;
   3040   
   3041   if (GNUNET_CHAT_CONTEXT_TYPE_GROUP != context->type)
   3042   {
   3043     context_write_records(context);
   3044     return;
   3045   }
   3046 
   3047   struct GNUNET_CHAT_Group *group;
   3048   group = group_create_from_context(handle, context);
   3049 
   3050   if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(
   3051       handle->groups, &(invitation->key.hash), group,
   3052       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   3053   {
   3054     context_write_records(context);
   3055     return;
   3056   }
   3057 
   3058   group_destroy(group);
   3059 
   3060   GNUNET_CONTAINER_multihashmap_remove(
   3061     handle->contexts, &(invitation->key.hash), context);
   3062 
   3063 destroy_context:
   3064   context_destroy(context);
   3065 }
   3066 
   3067 
   3068 void
   3069 GNUNET_CHAT_invitation_reject (struct GNUNET_CHAT_Invitation *invitation)
   3070 {
   3071   GNUNET_CHAT_VERSION_ASSERT();
   3072 
   3073   if (!invitation)
   3074     return;
   3075 
   3076   const struct GNUNET_MESSENGER_Contact *sender = GNUNET_MESSENGER_get_sender(
   3077     invitation->context->room, &(invitation->hash)
   3078   );
   3079 
   3080   if (!sender)
   3081     return;
   3082 
   3083   struct GNUNET_MESSENGER_Message msg;
   3084   memset(&msg, 0, sizeof(msg));
   3085 
   3086   msg.header.kind = GNUNET_MESSENGER_KIND_TAG;
   3087   GNUNET_memcpy(&(msg.body.tag.hash), &(invitation->hash),
   3088                 sizeof(struct GNUNET_HashCode));
   3089   msg.body.tag.tag = NULL;
   3090 
   3091   GNUNET_MESSENGER_send_message(invitation->context->room, &msg, sender);
   3092 }
   3093 
   3094 
   3095 enum GNUNET_GenericReturnValue
   3096 GNUNET_CHAT_invitation_is_accepted (const struct GNUNET_CHAT_Invitation *invitation)
   3097 {
   3098   GNUNET_CHAT_VERSION_ASSERT();
   3099 
   3100   if (!invitation)
   3101     return GNUNET_NO;
   3102 
   3103   return GNUNET_CONTAINER_multihashmap_contains(
   3104     invitation->context->handle->contexts,
   3105     &(invitation->key.hash)
   3106   );
   3107 }
   3108 
   3109 
   3110 enum GNUNET_GenericReturnValue
   3111 GNUNET_CHAT_invitation_is_rejected (const struct GNUNET_CHAT_Invitation *invitation)
   3112 {
   3113   GNUNET_CHAT_VERSION_ASSERT();
   3114 
   3115   if (!invitation)
   3116     return GNUNET_NO;
   3117 
   3118   const struct GNUNET_CHAT_InternalTagging *tagging = GNUNET_CONTAINER_multihashmap_get(
   3119     invitation->context->taggings, &(invitation->hash));
   3120   
   3121   if (!tagging)
   3122     return GNUNET_NO;
   3123   
   3124   if (internal_tagging_iterate(tagging, GNUNET_NO, NULL, NULL, NULL) > 0)
   3125     return GNUNET_YES;
   3126   else
   3127     return GNUNET_NO;
   3128 }
   3129 
   3130 
   3131 enum GNUNET_GenericReturnValue
   3132 GNUNET_CHAT_invitation_is_direct (const struct GNUNET_CHAT_Invitation *invitation)
   3133 {
   3134   GNUNET_CHAT_VERSION_ASSERT();
   3135 
   3136   if ((invitation->key.code.public_bit) ||
   3137       (invitation->key.code.group_bit) ||
   3138       (invitation->key.code.feed_bit))
   3139     return GNUNET_NO;
   3140   else
   3141     return GNUNET_YES;
   3142 }
   3143 
   3144 
   3145 const struct GNUNET_CHAT_DiscourseId*
   3146 GNUNET_CHAT_discourse_get_id (const struct GNUNET_CHAT_Discourse *discourse)
   3147 {
   3148   GNUNET_CHAT_VERSION_ASSERT();
   3149 
   3150   if (!discourse)
   3151     return NULL;
   3152 
   3153   return &(discourse->id);
   3154 }
   3155 
   3156 
   3157 enum GNUNET_GenericReturnValue
   3158 GNUNET_CHAT_discourse_is_open (const struct GNUNET_CHAT_Discourse *discourse)
   3159 {
   3160   GNUNET_CHAT_VERSION_ASSERT();
   3161 
   3162   if (!discourse)
   3163     return GNUNET_SYSERR;
   3164 
   3165   struct GNUNET_CHAT_DiscourseSubscription *sub;
   3166   for (sub = discourse->head; sub; sub = sub->next)
   3167   {
   3168     if (GNUNET_TIME_absolute_cmp(sub->end, <, GNUNET_TIME_absolute_get()))
   3169       continue;
   3170 
   3171     if (GNUNET_YES == sub->contact->owned)
   3172       return GNUNET_YES;
   3173   }
   3174 
   3175   return GNUNET_NO;
   3176 }
   3177 
   3178 
   3179 void
   3180 GNUNET_CHAT_discourse_set_user_pointer (struct GNUNET_CHAT_Discourse *discourse,
   3181                                         void *user_pointer)
   3182 {
   3183   GNUNET_CHAT_VERSION_ASSERT();
   3184 
   3185   if (!discourse)
   3186     return;
   3187 
   3188   discourse->user_pointer = user_pointer;
   3189 }
   3190 
   3191 
   3192 void*
   3193 GNUNET_CHAT_discourse_get_user_pointer (const struct GNUNET_CHAT_Discourse *discourse)
   3194 {
   3195   GNUNET_CHAT_VERSION_ASSERT();
   3196 
   3197   if (!discourse)
   3198     return NULL;
   3199 
   3200   return discourse->user_pointer;
   3201 }
   3202 
   3203 
   3204 void
   3205 GNUNET_CHAT_discourse_close (struct GNUNET_CHAT_Discourse *discourse)
   3206 {
   3207   GNUNET_CHAT_VERSION_ASSERT();
   3208 
   3209   if ((!discourse) || (!(discourse->context)) || (!(discourse->context->room)))
   3210     return;
   3211   
   3212   struct GNUNET_MESSENGER_Message msg;
   3213   memset(&msg, 0, sizeof(msg));
   3214 
   3215   msg.header.kind = GNUNET_MESSENGER_KIND_SUBSCRIBTION;
   3216 
   3217   util_shorthash_from_discourse_id(
   3218     &(discourse->id),
   3219     &(msg.body.subscription.discourse)
   3220   );
   3221 
   3222   msg.body.subscription.time = GNUNET_TIME_relative_hton(GNUNET_TIME_relative_get_zero_());
   3223   msg.body.subscription.flags = GNUNET_MESSENGER_FLAG_SUBSCRIPTION_UNSUBSCRIBE;
   3224 
   3225   GNUNET_MESSENGER_send_message(
   3226     discourse->context->room,
   3227     &msg,
   3228     NULL
   3229   );
   3230 }
   3231 
   3232 
   3233 enum GNUNET_GenericReturnValue
   3234 GNUNET_CHAT_discourse_write (struct GNUNET_CHAT_Discourse *discourse,
   3235                              const char *data,
   3236                              uint64_t size)
   3237 {
   3238   GNUNET_CHAT_VERSION_ASSERT();
   3239 
   3240   if ((!discourse) || (!data) || (!(discourse->context)) || 
   3241       (!(discourse->context->room)))
   3242     return GNUNET_SYSERR;
   3243   
   3244   static const uint64_t max_size = (uint16_t) (
   3245     GNUNET_MAX_MESSAGE_SIZE - GNUNET_MIN_MESSAGE_SIZE -
   3246     sizeof (struct GNUNET_MESSENGER_Message)
   3247   );
   3248 
   3249   struct GNUNET_MESSENGER_Message msg;
   3250   memset(&msg, 0, sizeof(msg));
   3251 
   3252   msg.header.kind = GNUNET_MESSENGER_KIND_TALK;
   3253   msg.body.talk.data = GNUNET_malloc(size > max_size? max_size : size);
   3254 
   3255   util_shorthash_from_discourse_id(
   3256     &(discourse->id),
   3257     &(msg.body.talk.discourse)
   3258   );
   3259 
   3260   while (size > 0)
   3261   {
   3262     msg.body.talk.length = (uint16_t) (size > max_size? max_size : size);
   3263 
   3264     GNUNET_memcpy(
   3265       msg.body.talk.data,
   3266       data,
   3267       msg.body.talk.length
   3268     );
   3269 
   3270     size -= msg.body.talk.length;
   3271     data += msg.body.talk.length;
   3272 
   3273     GNUNET_MESSENGER_send_message(discourse->context->room, &msg, NULL);
   3274   }
   3275 
   3276   GNUNET_free(msg.body.talk.data);
   3277   return GNUNET_OK;
   3278 }
   3279 
   3280 
   3281 int
   3282 GNUNET_CHAT_discourse_get_fd (const struct GNUNET_CHAT_Discourse *discourse)
   3283 {
   3284   GNUNET_CHAT_VERSION_ASSERT();
   3285 
   3286   if (! discourse)
   3287     return GNUNET_SYSERR;
   3288 
   3289   return discourse->pipe[1];
   3290 }
   3291 
   3292 
   3293 int
   3294 GNUNET_CHAT_discourse_iterate_contacts (struct GNUNET_CHAT_Discourse *discourse,
   3295                                         GNUNET_CHAT_DiscourseContactCallback callback,
   3296                                         void *cls)
   3297 {
   3298   GNUNET_CHAT_VERSION_ASSERT();
   3299 
   3300   if (! discourse)
   3301     return GNUNET_SYSERR;
   3302 
   3303   int iterations = 0;
   3304 
   3305   struct GNUNET_CHAT_DiscourseSubscription *sub;
   3306   for (sub = discourse->head; sub; sub = sub->next)
   3307   {
   3308     if (GNUNET_TIME_absolute_cmp(sub->end, <, GNUNET_TIME_absolute_get()))
   3309       continue;
   3310 
   3311     if (callback)
   3312       callback(cls, discourse, sub->contact);
   3313 
   3314     iterations++;
   3315   }
   3316 
   3317   return iterations;
   3318 }
   3319 
   3320 enum GNUNET_GenericReturnValue
   3321 GNUNET_CHAT_generate_secret (char *secret,
   3322                              uint32_t secret_len)
   3323 {
   3324   GNUNET_CHAT_VERSION_ASSERT();
   3325 
   3326   if (secret_len <= 0)
   3327     return GNUNET_SYSERR;
   3328 
   3329   const uint32_t requested = secret_len * 5 / 8 + 1;
   3330   const uint32_t size = ((requested*8) + (((requested*8) % 5) > 0 ? 5 - ((requested*8) % 5) : 0)) / 5;
   3331 
   3332   char *raw_secret = GNUNET_malloc(requested);
   3333   char *buf;
   3334 
   3335   if (!raw_secret)
   3336     return GNUNET_SYSERR;
   3337 
   3338   if (size > secret_len)
   3339   {
   3340     buf = GNUNET_malloc(size);
   3341 
   3342     if (!buf)
   3343     {
   3344       GNUNET_free(raw_secret);
   3345       return GNUNET_SYSERR;
   3346     }
   3347   }
   3348   else
   3349     buf = secret;
   3350 
   3351   GNUNET_CRYPTO_random_block(
   3352     GNUNET_CRYPTO_QUALITY_STRONG,
   3353     raw_secret,
   3354     requested
   3355   );
   3356 
   3357   enum GNUNET_GenericReturnValue result;
   3358   result = GNUNET_STRINGS_data_to_string(
   3359     raw_secret,
   3360     requested,
   3361     buf,
   3362     size
   3363   ) == NULL? GNUNET_SYSERR : GNUNET_OK;
   3364 
   3365   GNUNET_CRYPTO_zero_keys(raw_secret, requested);
   3366   GNUNET_free(raw_secret);
   3367 
   3368   if (buf != secret)
   3369   {
   3370     GNUNET_memcpy(secret, buf, secret_len);
   3371     GNUNET_CRYPTO_zero_keys(buf, size);
   3372     GNUNET_free(buf);
   3373   }
   3374 
   3375   return result;
   3376 }