libgnunetchat

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

gnunet_chat_lib.c (74410B)


      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                         enum GNUNET_CHAT_LobbyType type,
    646                         unsigned int delay,
    647                         GNUNET_CHAT_LobbyCallback callback,
    648                         void *cls)
    649 {
    650   GNUNET_CHAT_VERSION_ASSERT();
    651 
    652   if ((!handle) || (handle->destruction))
    653     return NULL;
    654 
    655   struct GNUNET_TIME_Relative rel = GNUNET_TIME_relative_multiply(
    656     GNUNET_TIME_relative_get_second_(), delay
    657   );
    658 
    659   struct GNUNET_CHAT_InternalLobbies *lobbies = GNUNET_new(
    660     struct GNUNET_CHAT_InternalLobbies
    661   );
    662 
    663   lobbies->lobby = lobby_create(handle);
    664 
    665   GNUNET_CONTAINER_DLL_insert(
    666     handle->lobbies_head,
    667     handle->lobbies_tail,
    668     lobbies
    669   );
    670 
    671   lobby_open(lobbies->lobby, type, rel, callback, cls);
    672 
    673   return lobbies->lobby;
    674 }
    675 
    676 
    677 void
    678 GNUNET_CHAT_lobby_close (struct GNUNET_CHAT_Lobby *lobby)
    679 {
    680   GNUNET_CHAT_VERSION_ASSERT();
    681 
    682   if ((!lobby) || (lobby->destruction))
    683     return;
    684 
    685   lobby->destruction = GNUNET_SCHEDULER_add_now(
    686     task_lobby_destruction,
    687     lobby
    688   );
    689 }
    690 
    691 
    692 void
    693 GNUNET_CHAT_lobby_join (struct GNUNET_CHAT_Handle *handle,
    694 			                  const struct GNUNET_CHAT_Uri *uri)
    695 {
    696   GNUNET_CHAT_VERSION_ASSERT();
    697 
    698   if ((!handle) || (handle->destruction) || (!(handle->gns)) ||
    699       (!uri) || (GNUNET_CHAT_URI_TYPE_CHAT != uri->type))
    700     return;
    701 
    702   struct GNUNET_CHAT_UriLookups *lookups = GNUNET_new(
    703     struct GNUNET_CHAT_UriLookups
    704   );
    705 
    706   lookups->handle = handle;
    707   lookups->uri = uri_create_chat(
    708     &(uri->chat.zone),
    709     uri->chat.label
    710   );
    711 
    712   lookups->request = GNUNET_GNS_lookup(
    713     handle->gns,
    714     lookups->uri->chat.label,
    715     &(uri->chat.zone),
    716     GNUNET_GNSRECORD_TYPE_MESSENGER_ROOM_ENTRY,
    717     GNUNET_GNS_LO_DEFAULT,
    718     cb_lobby_lookup,
    719     lookups
    720   );
    721 
    722   GNUNET_CONTAINER_DLL_insert(
    723     handle->lookups_head,
    724     handle->lookups_tail,
    725     lookups
    726   );
    727 }
    728 
    729 
    730 struct GNUNET_CHAT_File*
    731 GNUNET_CHAT_request_file (struct GNUNET_CHAT_Handle *handle,
    732                           const struct GNUNET_CHAT_Uri *uri)
    733 {
    734   GNUNET_CHAT_VERSION_ASSERT();
    735 
    736   if ((!handle) || (handle->destruction) ||
    737       (!uri) || (GNUNET_CHAT_URI_TYPE_FS != uri->type))
    738     return NULL;
    739 
    740   if (!GNUNET_FS_uri_test_chk(uri->fs.uri))
    741     return NULL;
    742 
    743   const struct GNUNET_HashCode *hash = GNUNET_FS_uri_chk_get_file_hash(
    744     uri->fs.uri
    745   );
    746 
    747   if (!hash)
    748     return NULL;
    749 
    750   struct GNUNET_CHAT_File *file = GNUNET_CONTAINER_multihashmap_get(
    751     handle->files,
    752     hash
    753   );
    754 
    755   if (file)
    756     return file;
    757 
    758   file = file_create_from_chk_uri(handle, uri->fs.uri);
    759 
    760   if (!file)
    761     return NULL;
    762 
    763   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(handle->files, hash, file,
    764       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
    765   {
    766     file_destroy(file);
    767     file = NULL;
    768   }
    769 
    770   return file;
    771 }
    772 
    773 
    774 struct GNUNET_CHAT_File*
    775 GNUNET_CHAT_upload_file (struct GNUNET_CHAT_Handle *handle,
    776                          const char *path,
    777                          GNUNET_CHAT_FileUploadCallback callback,
    778                          void *cls)
    779 {
    780   GNUNET_CHAT_VERSION_ASSERT();
    781 
    782   if ((!handle) || (handle->destruction) ||
    783       (!path))
    784     return NULL;
    785 
    786   struct GNUNET_HashCode hash;
    787   if (GNUNET_OK != util_hash_file(path, &hash))
    788     return NULL;
    789 
    790   char *filename = handle_create_file_path(
    791     handle, &hash
    792   );
    793 
    794   if (!filename)
    795     return NULL;
    796 
    797   struct GNUNET_CHAT_File *file = GNUNET_CONTAINER_multihashmap_get(
    798     handle->files,
    799     &hash
    800   );
    801 
    802   if (file)
    803     goto file_binding;
    804 
    805   if ((GNUNET_YES == GNUNET_DISK_file_test(filename)) ||
    806       (GNUNET_OK != GNUNET_DISK_directory_create_for_file(filename)) ||
    807       (GNUNET_OK != GNUNET_DISK_file_copy(path, filename)))
    808   {
    809     GNUNET_free(filename);
    810     return NULL;
    811   }
    812 
    813   char* p = GNUNET_strdup(path);
    814 
    815   file = file_create_from_disk(
    816     handle,
    817     basename(p),
    818     &hash
    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   char* p = GNUNET_strdup(path);
   1905 
   1906   file = file_create_from_disk(
   1907     context->handle,
   1908     basename(p),
   1909     &hash
   1910   );
   1911 
   1912   GNUNET_free(p);
   1913 
   1914   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(
   1915       context->handle->files, &hash, file,
   1916       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   1917   {
   1918     file_destroy(file);
   1919     GNUNET_free(filename);
   1920     return NULL;
   1921   }
   1922 
   1923   struct GNUNET_FS_BlockOptions bo;
   1924 
   1925   bo.anonymity_level = block_anonymity_level;
   1926   bo.content_priority = block_content_priority;
   1927   bo.replication_level = block_replication_level;
   1928   bo.expiration_time = GNUNET_TIME_absolute_get_forever_();
   1929 
   1930   struct GNUNET_FS_FileInformation* fi = GNUNET_FS_file_information_create_from_file(
   1931     context->handle->fs,
   1932     file,
   1933     filename,
   1934     NULL,
   1935     file->meta,
   1936     GNUNET_YES,
   1937     &bo
   1938   );
   1939 
   1940   file->publish = GNUNET_FS_publish_start(
   1941     context->handle->fs, fi,
   1942     NULL, NULL, NULL,
   1943     GNUNET_FS_PUBLISH_OPTION_NONE
   1944   );
   1945 
   1946   if (file->publish)
   1947     file->status |= GNUNET_CHAT_FILE_STATUS_PUBLISH;
   1948 
   1949   GNUNET_free(filename);
   1950 
   1951 file_binding:
   1952   file_bind_upload(file, context, callback, cls);
   1953   return file;
   1954 }
   1955 
   1956 
   1957 enum GNUNET_GenericReturnValue
   1958 GNUNET_CHAT_context_share_file (struct GNUNET_CHAT_Context *context,
   1959 				                        struct GNUNET_CHAT_File *file)
   1960 {
   1961   GNUNET_CHAT_VERSION_ASSERT();
   1962 
   1963   if ((!context) || (!file) ||
   1964       (!(file->name)) || (strlen(file->name) > NAME_MAX) ||
   1965       (!(file->uri)) || (!(context->room)))
   1966     return GNUNET_SYSERR;
   1967 
   1968   struct GNUNET_MESSENGER_Message msg;
   1969   memset(&msg, 0, sizeof(msg));
   1970 
   1971   msg.header.kind = GNUNET_MESSENGER_KIND_FILE;
   1972 
   1973   GNUNET_memcpy(&(msg.body.file.hash), &(file->hash), sizeof(file->hash));
   1974   GNUNET_strlcpy(msg.body.file.name, file->name, NAME_MAX);
   1975   msg.body.file.uri = GNUNET_FS_uri_to_string(file->uri);
   1976 
   1977   GNUNET_MESSENGER_send_message(context->room, &msg, NULL);
   1978 
   1979   GNUNET_free(msg.body.file.uri);
   1980   return GNUNET_OK;
   1981 }
   1982 
   1983 
   1984 enum GNUNET_GenericReturnValue
   1985 GNUNET_CHAT_context_send_tag (struct GNUNET_CHAT_Context *context,
   1986                               struct GNUNET_CHAT_Message *message,
   1987                               const char *tag)
   1988 {
   1989   GNUNET_CHAT_VERSION_ASSERT();
   1990 
   1991   if ((!context) || (!message) || (!tag) || (!(context->room)))
   1992     return GNUNET_SYSERR;
   1993 
   1994   char *tag_value = GNUNET_strdup(tag);
   1995 
   1996   struct GNUNET_MESSENGER_Message msg;
   1997   memset(&msg, 0, sizeof(msg));
   1998 
   1999   msg.header.kind = GNUNET_MESSENGER_KIND_TAG;
   2000   GNUNET_memcpy(&(msg.body.tag.hash), &(message->hash),
   2001     sizeof(struct GNUNET_HashCode));
   2002   msg.body.tag.tag = tag_value;
   2003 
   2004   GNUNET_MESSENGER_send_message(
   2005     context->room,
   2006     &msg,
   2007     NULL
   2008   );
   2009 
   2010   GNUNET_free(tag_value);
   2011   return GNUNET_OK;
   2012 }
   2013 
   2014 
   2015 struct GNUNET_CHAT_Discourse*
   2016 GNUNET_CHAT_context_open_discourse (struct GNUNET_CHAT_Context *context,
   2017                                     const struct GNUNET_CHAT_DiscourseId *id)
   2018 {
   2019   GNUNET_CHAT_VERSION_ASSERT();
   2020 
   2021   if ((!context) || (!(context->discourses)) || (!(context->room)) || (!id))
   2022     return NULL;
   2023 
   2024   struct GNUNET_ShortHashCode sid;
   2025   util_shorthash_from_discourse_id(id, &sid);
   2026 
   2027   struct GNUNET_CHAT_Discourse *discourse = GNUNET_CONTAINER_multishortmap_get(
   2028     context->discourses, &sid
   2029   );
   2030 
   2031   if (!discourse)
   2032   {
   2033     discourse = discourse_create(context, id);
   2034 
   2035     if (GNUNET_OK != GNUNET_CONTAINER_multishortmap_put(context->discourses,
   2036         &sid, discourse, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   2037     {
   2038       discourse_destroy(discourse);
   2039       return NULL;
   2040     }
   2041   }
   2042 
   2043   struct GNUNET_MESSENGER_Message msg;
   2044   memset(&msg, 0, sizeof(msg));
   2045 
   2046   msg.header.kind = GNUNET_MESSENGER_KIND_SUBSCRIBTION;
   2047   GNUNET_memcpy(
   2048     &(msg.body.subscription.discourse),
   2049     &sid,
   2050     sizeof(struct GNUNET_ShortHashCode)
   2051   );
   2052 
   2053   const struct GNUNET_TIME_Relative subscription_time = GNUNET_TIME_relative_multiply(
   2054     GNUNET_TIME_relative_get_second_(), 10
   2055   );
   2056 
   2057   msg.body.subscription.time = GNUNET_TIME_relative_hton(subscription_time);
   2058   msg.body.subscription.flags = GNUNET_MESSENGER_FLAG_SUBSCRIPTION_KEEP_ALIVE;
   2059 
   2060   GNUNET_MESSENGER_send_message(
   2061     context->room,
   2062     &msg,
   2063     NULL
   2064   );
   2065 
   2066   return discourse;
   2067 }
   2068 
   2069 
   2070 int
   2071 GNUNET_CHAT_context_iterate_messages (struct GNUNET_CHAT_Context *context,
   2072                                       GNUNET_CHAT_ContextMessageCallback callback,
   2073                                       void *cls)
   2074 {
   2075   GNUNET_CHAT_VERSION_ASSERT();
   2076 
   2077   if (!context)
   2078     return GNUNET_SYSERR;
   2079 
   2080   struct GNUNET_CHAT_ContextIterateMessages it;
   2081   it.context = context;
   2082   it.cb = callback;
   2083   it.cls = cls;
   2084 
   2085   return GNUNET_CONTAINER_multihashmap_iterate(
   2086     context->messages, it_context_iterate_messages, &it
   2087   );
   2088 }
   2089 
   2090 
   2091 int
   2092 GNUNET_CHAT_context_iterate_files (struct GNUNET_CHAT_Context *context,
   2093                                    GNUNET_CHAT_ContextFileCallback callback,
   2094                                    void *cls)
   2095 {
   2096   GNUNET_CHAT_VERSION_ASSERT();
   2097 
   2098   if (!context)
   2099     return GNUNET_SYSERR;
   2100 
   2101   struct GNUNET_CHAT_ContextIterateFiles it;
   2102   it.context = context;
   2103   it.cb = callback;
   2104   it.cls = cls;
   2105 
   2106   return GNUNET_CONTAINER_multihashmap_iterate(
   2107     context->files, it_context_iterate_files, &it
   2108   );
   2109 }
   2110 
   2111 
   2112 enum GNUNET_CHAT_MessageKind
   2113 GNUNET_CHAT_message_get_kind (const struct GNUNET_CHAT_Message *message)
   2114 {
   2115   GNUNET_CHAT_VERSION_ASSERT();
   2116 
   2117   if (!message)
   2118     return GNUNET_CHAT_KIND_UNKNOWN;
   2119 
   2120   switch (message->flag)
   2121   {
   2122     case GNUNET_CHAT_FLAG_WARNING:
   2123       return GNUNET_CHAT_KIND_WARNING;
   2124     case GNUNET_CHAT_FLAG_REFRESH:
   2125       return GNUNET_CHAT_KIND_REFRESH;
   2126     case GNUNET_CHAT_FLAG_LOGIN:
   2127       return GNUNET_CHAT_KIND_LOGIN;
   2128     case GNUNET_CHAT_FLAG_LOGOUT:
   2129       return GNUNET_CHAT_KIND_LOGOUT;
   2130     case GNUNET_CHAT_FLAG_CREATE_ACCOUNT:
   2131       return GNUNET_CHAT_KIND_CREATED_ACCOUNT;
   2132     case GNUNET_CHAT_FLAG_DELETE_ACCOUNT:
   2133       return GNUNET_CHAT_KIND_DELETED_ACCOUNT;
   2134     case GNUNET_CHAT_FLAG_UPDATE_ACCOUNT:
   2135       return GNUNET_CHAT_KIND_UPDATE_ACCOUNT;
   2136     case GNUNET_CHAT_FLAG_UPDATE_CONTEXT:
   2137       return GNUNET_CHAT_KIND_UPDATE_CONTEXT;
   2138     case GNUNET_CHAT_FLAG_ATTRIBUTES:
   2139       return GNUNET_CHAT_KIND_ATTRIBUTES;
   2140     case GNUNET_CHAT_FLAG_SHARE_ATTRIBUTES:
   2141       return GNUNET_CHAT_KIND_SHARED_ATTRIBUTES;
   2142     default:
   2143       break;
   2144   }
   2145 
   2146   if (GNUNET_YES != message_has_msg(message))
   2147     return GNUNET_CHAT_KIND_UNKNOWN;
   2148 
   2149   return util_message_kind_from_kind(message->msg->header.kind);
   2150 }
   2151 
   2152 
   2153 time_t
   2154 GNUNET_CHAT_message_get_timestamp (const struct GNUNET_CHAT_Message *message)
   2155 {
   2156   GNUNET_CHAT_VERSION_ASSERT();
   2157 
   2158   if ((!message) || (GNUNET_YES != message_has_msg(message)))
   2159     return ((time_t) -1);
   2160 
   2161   struct GNUNET_TIME_Absolute abs = GNUNET_TIME_absolute_ntoh(
   2162     message->msg->header.timestamp
   2163   );
   2164 
   2165   struct GNUNET_TIME_Timestamp ts = GNUNET_TIME_absolute_to_timestamp(
   2166     abs
   2167   );
   2168 
   2169   return (time_t) GNUNET_TIME_timestamp_to_s(ts);
   2170 }
   2171 
   2172 
   2173 struct GNUNET_CHAT_Contact*
   2174 GNUNET_CHAT_message_get_sender (const struct GNUNET_CHAT_Message *message)
   2175 {
   2176   GNUNET_CHAT_VERSION_ASSERT();
   2177 
   2178   if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
   2179       (!(message->context)) || (!(message->context->room)))
   2180     return NULL;
   2181 
   2182   const struct GNUNET_MESSENGER_Contact *sender = GNUNET_MESSENGER_get_sender(
   2183     message->context->room, &(message->hash)
   2184   );
   2185 
   2186   if (!sender)
   2187     return NULL;
   2188 
   2189   return handle_get_contact_from_messenger(message->context->handle, sender);
   2190 }
   2191 
   2192 
   2193 struct GNUNET_CHAT_Contact*
   2194 GNUNET_CHAT_message_get_recipient (const struct GNUNET_CHAT_Message *message)
   2195 {
   2196   GNUNET_CHAT_VERSION_ASSERT();
   2197 
   2198   if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
   2199       (!(message->context)) || (!(message->context->room)))
   2200     return NULL;
   2201 
   2202   const struct GNUNET_MESSENGER_Contact *recipient = GNUNET_MESSENGER_get_recipient(
   2203     message->context->room, &(message->hash)
   2204   );
   2205 
   2206   if (!recipient)
   2207     return NULL;
   2208 
   2209   return handle_get_contact_from_messenger(message->context->handle, recipient);
   2210 }
   2211 
   2212 
   2213 enum GNUNET_GenericReturnValue
   2214 GNUNET_CHAT_message_is_sent (const struct GNUNET_CHAT_Message *message)
   2215 {
   2216   GNUNET_CHAT_VERSION_ASSERT();
   2217 
   2218   if (!message)
   2219     return GNUNET_SYSERR;
   2220 
   2221   if (message->flags & GNUNET_MESSENGER_FLAG_SENT)
   2222     return GNUNET_YES;
   2223   else
   2224     return GNUNET_NO;
   2225 }
   2226 
   2227 
   2228 enum GNUNET_GenericReturnValue
   2229 GNUNET_CHAT_message_is_private (const struct GNUNET_CHAT_Message *message)
   2230 {
   2231   GNUNET_CHAT_VERSION_ASSERT();
   2232 
   2233   if (!message)
   2234     return GNUNET_SYSERR;
   2235 
   2236   if (message->flags & GNUNET_MESSENGER_FLAG_PRIVATE)
   2237     return GNUNET_YES;
   2238   else
   2239     return GNUNET_NO;
   2240 }
   2241 
   2242 
   2243 enum GNUNET_GenericReturnValue
   2244 GNUNET_CHAT_message_is_recent (const struct GNUNET_CHAT_Message *message)
   2245 {
   2246   GNUNET_CHAT_VERSION_ASSERT();
   2247 
   2248   if (!message)
   2249     return GNUNET_SYSERR;
   2250 
   2251   if (message->flags & GNUNET_MESSENGER_FLAG_RECENT)
   2252     return GNUNET_YES;
   2253   else
   2254     return GNUNET_NO;
   2255 }
   2256 
   2257 
   2258 enum GNUNET_GenericReturnValue
   2259 GNUNET_CHAT_message_is_update (const struct GNUNET_CHAT_Message *message)
   2260 {
   2261   GNUNET_CHAT_VERSION_ASSERT();
   2262 
   2263   if (!message)
   2264     return GNUNET_SYSERR;
   2265 
   2266   if (message->flags & GNUNET_MESSENGER_FLAG_UPDATE)
   2267     return GNUNET_YES;
   2268   else
   2269     return GNUNET_NO;
   2270 }
   2271 
   2272 
   2273 enum GNUNET_GenericReturnValue
   2274 GNUNET_CHAT_message_is_deleted (const struct GNUNET_CHAT_Message *message)
   2275 {
   2276   GNUNET_CHAT_VERSION_ASSERT();
   2277 
   2278   if (!message)
   2279     return GNUNET_SYSERR;
   2280 
   2281   if ((GNUNET_CHAT_FLAG_NONE == message->flag) &&
   2282       ((message->flags & GNUNET_MESSENGER_FLAG_DELETE) ||
   2283        (!message->msg)))
   2284     return GNUNET_YES;
   2285   else
   2286     return GNUNET_NO;
   2287 }
   2288 
   2289 
   2290 enum GNUNET_GenericReturnValue
   2291 GNUNET_CHAT_message_is_tagged (const struct GNUNET_CHAT_Message *message,
   2292                                const char *tag)
   2293 {
   2294   GNUNET_CHAT_VERSION_ASSERT();
   2295 
   2296   if ((!message) || (!(message->context)))
   2297     return GNUNET_SYSERR;
   2298 
   2299   const struct GNUNET_CHAT_InternalTagging *tagging = GNUNET_CONTAINER_multihashmap_get(
   2300     message->context->taggings, &(message->hash));
   2301 
   2302   if (!tagging)
   2303     return GNUNET_NO;
   2304 
   2305   if (internal_tagging_iterate(tagging, GNUNET_NO, tag, NULL, NULL) > 0)
   2306     return GNUNET_YES;
   2307   else
   2308     return GNUNET_NO;
   2309 }
   2310 
   2311 
   2312 int
   2313 GNUNET_CHAT_message_get_read_receipt (struct GNUNET_CHAT_Message *message,
   2314                                       GNUNET_CHAT_MessageReadReceiptCallback callback,
   2315                                       void *cls)
   2316 {
   2317   GNUNET_CHAT_VERSION_ASSERT();
   2318 
   2319   if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
   2320       (!(message->context)))
   2321     return GNUNET_SYSERR;
   2322 
   2323   struct GNUNET_CHAT_MessageIterateReadReceipts it;
   2324   it.message = message;
   2325   it.cb = callback;
   2326   it.cls = cls;
   2327 
   2328   return GNUNET_MESSENGER_iterate_members(
   2329     message->context->room, it_message_iterate_read_receipts, &it
   2330   );
   2331 }
   2332 
   2333 
   2334 const char*
   2335 GNUNET_CHAT_message_get_text (const struct GNUNET_CHAT_Message *message)
   2336 {
   2337   GNUNET_CHAT_VERSION_ASSERT();
   2338 
   2339   if (!message)
   2340     return NULL;
   2341 
   2342   if (GNUNET_CHAT_FLAG_WARNING == message->flag)
   2343     return message->warning;
   2344   else if (GNUNET_CHAT_FLAG_UPDATE_ACCOUNT == message->flag)
   2345     return message->warning;
   2346   else if (GNUNET_CHAT_FLAG_ATTRIBUTES == message->flag)
   2347     return message->attr;
   2348 
   2349   if (GNUNET_YES != message_has_msg(message))
   2350     return NULL;
   2351 
   2352   if (GNUNET_MESSENGER_KIND_TEXT == message->msg->header.kind)
   2353     return message->msg->body.text.text;
   2354   else if (GNUNET_MESSENGER_KIND_FILE == message->msg->header.kind)
   2355     return message->msg->body.file.name;
   2356   else if (GNUNET_MESSENGER_KIND_TAG == message->msg->header.kind)
   2357     return message->msg->body.tag.tag;
   2358   else
   2359     return NULL;
   2360 }
   2361 
   2362 
   2363 void
   2364 GNUNET_CHAT_message_set_user_pointer (struct GNUNET_CHAT_Message *message,
   2365                                       void *user_pointer)
   2366 {
   2367   GNUNET_CHAT_VERSION_ASSERT();
   2368 
   2369   if (!message)
   2370     return;
   2371 
   2372   message->user_pointer = user_pointer;
   2373 }
   2374 
   2375 
   2376 void*
   2377 GNUNET_CHAT_message_get_user_pointer (const struct GNUNET_CHAT_Message *message)
   2378 {
   2379   GNUNET_CHAT_VERSION_ASSERT();
   2380 
   2381   if (!message)
   2382     return NULL;
   2383 
   2384   return message->user_pointer;
   2385 }
   2386 
   2387 
   2388 struct GNUNET_CHAT_Account*
   2389 GNUNET_CHAT_message_get_account (const struct GNUNET_CHAT_Message *message)
   2390 {
   2391   GNUNET_CHAT_VERSION_ASSERT();
   2392 
   2393   if (!message)
   2394     return NULL;
   2395 
   2396   if ((message->context) && (message->context->handle))
   2397     return message->context->handle->current;
   2398   else
   2399     return message->account;
   2400 }
   2401 
   2402 
   2403 struct GNUNET_CHAT_File*
   2404 GNUNET_CHAT_message_get_file (const struct GNUNET_CHAT_Message *message)
   2405 {
   2406   GNUNET_CHAT_VERSION_ASSERT();
   2407 
   2408   if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
   2409       (!(message->context)))
   2410     return NULL;
   2411 
   2412   if (GNUNET_MESSENGER_KIND_FILE != message->msg->header.kind)
   2413     return NULL;
   2414 
   2415   return GNUNET_CONTAINER_multihashmap_get(
   2416     message->context->handle->files,
   2417     &(message->msg->body.file.hash)
   2418   );
   2419 }
   2420 
   2421 
   2422 struct GNUNET_CHAT_Invitation*
   2423 GNUNET_CHAT_message_get_invitation (const struct GNUNET_CHAT_Message *message)
   2424 {
   2425   GNUNET_CHAT_VERSION_ASSERT();
   2426 
   2427   if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
   2428       (!(message->context)))
   2429     return NULL;
   2430 
   2431   if (GNUNET_MESSENGER_KIND_INVITE != message->msg->header.kind)
   2432     return NULL;
   2433 
   2434   return GNUNET_CONTAINER_multihashmap_get(
   2435     message->context->invites,
   2436     &(message->hash)
   2437   );
   2438 }
   2439 
   2440 
   2441 struct GNUNET_CHAT_Discourse*
   2442 GNUNET_CHAT_message_get_discourse (const struct GNUNET_CHAT_Message *message)
   2443 {
   2444   GNUNET_CHAT_VERSION_ASSERT();
   2445 
   2446   if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
   2447       (!(message->context)) || (!(message->context->discourses)))
   2448     return NULL;
   2449 
   2450   struct GNUNET_CHAT_Discourse *discourse;
   2451 
   2452   if (GNUNET_MESSENGER_KIND_SUBSCRIBTION == message->msg->header.kind)
   2453     discourse = GNUNET_CONTAINER_multishortmap_get(
   2454       message->context->discourses,
   2455       &(message->msg->body.subscription.discourse));
   2456   else if (GNUNET_MESSENGER_KIND_TALK == message->msg->header.kind)
   2457     discourse = GNUNET_CONTAINER_multishortmap_get(
   2458       message->context->discourses,
   2459       &(message->msg->body.talk.discourse));
   2460   else
   2461     discourse = NULL;
   2462 
   2463   return discourse;
   2464 }
   2465 
   2466 
   2467 struct GNUNET_CHAT_Message*
   2468 GNUNET_CHAT_message_get_target (const struct GNUNET_CHAT_Message *message)
   2469 {
   2470   GNUNET_CHAT_VERSION_ASSERT();
   2471 
   2472   if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
   2473       (!(message->context)))
   2474     return NULL;
   2475 
   2476   struct GNUNET_CHAT_Message *target;
   2477 
   2478   if (GNUNET_MESSENGER_KIND_DELETION == message->msg->header.kind)
   2479     target = GNUNET_CONTAINER_multihashmap_get(
   2480 	message->context->messages, &(message->msg->body.deletion.hash));
   2481   else if (GNUNET_MESSENGER_KIND_TAG == message->msg->header.kind)
   2482     target = GNUNET_CONTAINER_multihashmap_get(
   2483       message->context->messages, &(message->msg->body.tag.hash));
   2484   else
   2485     target = NULL;
   2486 
   2487   return target;
   2488 }
   2489 
   2490 
   2491 enum GNUNET_GenericReturnValue
   2492 GNUNET_CHAT_message_delete (struct GNUNET_CHAT_Message *message,
   2493 			                      unsigned int delay)
   2494 {
   2495   GNUNET_CHAT_VERSION_ASSERT();
   2496 
   2497   if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
   2498       (!(message->context)))
   2499     return GNUNET_SYSERR;
   2500 
   2501   struct GNUNET_TIME_Relative rel = GNUNET_TIME_relative_multiply(
   2502     GNUNET_TIME_relative_get_second_(), delay
   2503   );
   2504 
   2505   GNUNET_MESSENGER_delete_message(
   2506     message->context->room,
   2507     &(message->hash),
   2508     rel
   2509   );
   2510 
   2511   return GNUNET_OK;
   2512 }
   2513 
   2514 
   2515 int
   2516 GNUNET_CHAT_message_iterate_tags (struct GNUNET_CHAT_Message *message,
   2517                                   GNUNET_CHAT_MessageCallback callback,
   2518                                   void *cls)
   2519 {
   2520   GNUNET_CHAT_VERSION_ASSERT();
   2521 
   2522   if ((!message) || (!(message->context)))
   2523     return GNUNET_SYSERR;
   2524 
   2525   const struct GNUNET_CHAT_InternalTagging *tagging = GNUNET_CONTAINER_multihashmap_get(
   2526     message->context->taggings, &(message->hash));
   2527 
   2528   if (!tagging)
   2529     return 0;
   2530 
   2531   return internal_tagging_iterate(tagging, GNUNET_YES, NULL, callback, cls);
   2532 }
   2533 
   2534 
   2535 uint64_t
   2536 GNUNET_CHAT_message_available (const struct GNUNET_CHAT_Message *message)
   2537 {
   2538   GNUNET_CHAT_VERSION_ASSERT();
   2539 
   2540   if ((!message) || (GNUNET_YES != message_has_msg(message)))
   2541     return 0;
   2542 
   2543   if (GNUNET_MESSENGER_KIND_TALK == message->msg->header.kind)
   2544     return message->msg->body.talk.length;
   2545   else
   2546     return 0;
   2547 }
   2548 
   2549 
   2550 enum GNUNET_GenericReturnValue
   2551 GNUNET_CHAT_message_read (const struct GNUNET_CHAT_Message *message,
   2552                           char *data,
   2553                           uint64_t size)
   2554 {
   2555   GNUNET_CHAT_VERSION_ASSERT();
   2556 
   2557   if ((!message) || (GNUNET_YES != message_has_msg(message)))
   2558     return GNUNET_SYSERR;
   2559 
   2560   if (GNUNET_MESSENGER_KIND_TALK != message->msg->header.kind)
   2561     return GNUNET_SYSERR;
   2562 
   2563   const uint64_t available = message->msg->body.talk.length;
   2564 
   2565   if (available < size)
   2566     return GNUNET_NO;
   2567 
   2568   GNUNET_memcpy(
   2569     data,
   2570     message->msg->body.talk.data,
   2571     size
   2572   );
   2573 
   2574   return GNUNET_OK;
   2575 }
   2576 
   2577 
   2578 enum GNUNET_GenericReturnValue
   2579 GNUNET_CHAT_message_feed (const struct GNUNET_CHAT_Message *message,
   2580                           int fd)
   2581 {
   2582   GNUNET_CHAT_VERSION_ASSERT();
   2583 
   2584   if ((!message) || (GNUNET_YES != message_has_msg(message)) ||
   2585       (fd == -1))
   2586     return GNUNET_SYSERR;
   2587 
   2588   if (GNUNET_MESSENGER_KIND_TALK != message->msg->header.kind)
   2589     return GNUNET_SYSERR;
   2590 
   2591   if (!(message->msg->body.talk.length))
   2592     return GNUNET_NO;
   2593 
   2594   const ssize_t written = write(
   2595     fd,
   2596     message->msg->body.talk.data,
   2597     message->msg->body.talk.length
   2598   );
   2599 
   2600   if (-1 == written)
   2601     return GNUNET_SYSERR;
   2602   else if (written != message->msg->body.talk.length)
   2603     return GNUNET_NO;
   2604   else
   2605     return GNUNET_OK;
   2606 }
   2607 
   2608 
   2609 const char*
   2610 GNUNET_CHAT_file_get_name (const struct GNUNET_CHAT_File *file)
   2611 {
   2612   GNUNET_CHAT_VERSION_ASSERT();
   2613 
   2614   if (!file)
   2615     return NULL;
   2616 
   2617   return file->name;
   2618 }
   2619 
   2620 
   2621 const char*
   2622 GNUNET_CHAT_file_get_hash (const struct GNUNET_CHAT_File *file)
   2623 {
   2624   GNUNET_CHAT_VERSION_ASSERT();
   2625 
   2626   if (!file)
   2627     return NULL;
   2628 
   2629   return GNUNET_h2s_full(&(file->hash));
   2630 }
   2631 
   2632 
   2633 uint64_t
   2634 GNUNET_CHAT_file_get_size (const struct GNUNET_CHAT_File *file)
   2635 {
   2636   GNUNET_CHAT_VERSION_ASSERT();
   2637 
   2638   if ((!file) || (!(file->uri)))
   2639     return 0;
   2640 
   2641   return GNUNET_FS_uri_chk_get_file_size(file->uri);
   2642 }
   2643 
   2644 
   2645 uint64_t
   2646 GNUNET_CHAT_file_get_local_size (const struct GNUNET_CHAT_File *file)
   2647 {
   2648   GNUNET_CHAT_VERSION_ASSERT();
   2649 
   2650   if (!file)
   2651     return 0;
   2652 
   2653   char *filename = handle_create_file_path(
   2654     file->handle, &(file->hash)
   2655   );
   2656 
   2657   if (!filename)
   2658     return 0;
   2659 
   2660   uint64_t size;
   2661   if (GNUNET_OK != GNUNET_DISK_file_size(filename, &size, GNUNET_NO, GNUNET_YES))
   2662     size = 0;
   2663 
   2664   GNUNET_free(filename);
   2665   return size;
   2666 }
   2667 
   2668 
   2669 struct GNUNET_CHAT_Uri*
   2670 GNUNET_CHAT_file_get_uri (const struct GNUNET_CHAT_File *file)
   2671 {
   2672   GNUNET_CHAT_VERSION_ASSERT();
   2673 
   2674   if ((!file) || (!(file->uri)))
   2675     return NULL;
   2676 
   2677   return uri_create_file(file->uri);
   2678 }
   2679 
   2680 
   2681 enum GNUNET_GenericReturnValue
   2682 GNUNET_CHAT_file_is_uploading (const struct GNUNET_CHAT_File *file)
   2683 {
   2684   GNUNET_CHAT_VERSION_ASSERT();
   2685 
   2686   if ((!file) || (0 == (file->status & GNUNET_CHAT_FILE_STATUS_PUBLISH)))
   2687     return GNUNET_NO;
   2688   else
   2689     return GNUNET_YES;
   2690 }
   2691 
   2692 
   2693 enum GNUNET_GenericReturnValue
   2694 GNUNET_CHAT_file_stop_upload (struct GNUNET_CHAT_File *file)
   2695 {
   2696   GNUNET_CHAT_VERSION_ASSERT();
   2697 
   2698   if (!file)
   2699     return GNUNET_SYSERR;
   2700 
   2701   if (!file->publish)
   2702     return GNUNET_SYSERR;
   2703 
   2704   GNUNET_FS_publish_stop(file->publish);
   2705   file->publish = NULL;
   2706   return GNUNET_OK;
   2707 }
   2708 
   2709 
   2710 enum GNUNET_GenericReturnValue
   2711 GNUNET_CHAT_file_is_ready (const struct GNUNET_CHAT_File *file)
   2712 {
   2713   GNUNET_CHAT_VERSION_ASSERT();
   2714 
   2715   if ((!file) || (file->status & GNUNET_CHAT_FILE_STATUS_MASK))
   2716     return GNUNET_NO;
   2717 
   2718   const uint64_t size = GNUNET_CHAT_file_get_size(file);
   2719   const uint64_t local_size = GNUNET_CHAT_file_get_local_size(file);
   2720 
   2721   if (size != local_size)
   2722     return GNUNET_NO;
   2723   else
   2724     return GNUNET_YES;
   2725 }
   2726 
   2727 
   2728 enum GNUNET_GenericReturnValue
   2729 GNUNET_CHAT_file_is_valid (const struct GNUNET_CHAT_File *file)
   2730 {
   2731   GNUNET_CHAT_VERSION_ASSERT();
   2732 
   2733   if ((!file) || (file->status & GNUNET_CHAT_FILE_STATUS_MASK))
   2734     return GNUNET_SYSERR;
   2735 
   2736   char *filename = handle_create_file_path(
   2737     file->handle, &(file->hash)
   2738   );
   2739 
   2740   if (!filename)
   2741     return GNUNET_SYSERR;
   2742 
   2743   struct GNUNET_HashCode hash;
   2744   enum GNUNET_GenericReturnValue result;
   2745 
   2746   if (GNUNET_OK == util_hash_file(filename, &hash))
   2747   {
   2748     if (0 == GNUNET_CRYPTO_hash_cmp(&hash, &(file->hash)))
   2749       result = GNUNET_YES;
   2750     else
   2751       result = GNUNET_NO;
   2752   }
   2753   else
   2754     result = GNUNET_SYSERR;
   2755 
   2756   GNUNET_free(filename);
   2757   return result;
   2758 }
   2759 
   2760 
   2761 const char*
   2762 GNUNET_CHAT_file_open_preview (struct GNUNET_CHAT_File *file)
   2763 {
   2764   GNUNET_CHAT_VERSION_ASSERT();
   2765 
   2766   if (!file)
   2767     return NULL;
   2768 
   2769   if (file->preview)
   2770     return file->preview;
   2771 
   2772   char *filename = handle_create_file_path(
   2773     file->handle, &(file->hash)
   2774   );
   2775 
   2776   if (!filename)
   2777     return NULL;
   2778 
   2779   if (GNUNET_YES != GNUNET_DISK_file_test(filename))
   2780   {
   2781     GNUNET_free(filename);
   2782     filename = NULL;
   2783   }
   2784 
   2785   file->preview = filename;
   2786   return file->preview;
   2787 }
   2788 
   2789 
   2790 void
   2791 GNUNET_CHAT_file_close_preview (struct GNUNET_CHAT_File *file)
   2792 {
   2793   GNUNET_CHAT_VERSION_ASSERT();
   2794 
   2795   if ((!file) || (!(file->preview)))
   2796     return;
   2797 
   2798   GNUNET_free(file->preview);
   2799   file->preview = NULL;
   2800 }
   2801 
   2802 
   2803 void
   2804 GNUNET_CHAT_file_set_user_pointer (struct GNUNET_CHAT_File *file,
   2805 				                           void *user_pointer)
   2806 {
   2807   GNUNET_CHAT_VERSION_ASSERT();
   2808 
   2809   if (!file)
   2810     return;
   2811 
   2812   file->user_pointer = user_pointer;
   2813 }
   2814 
   2815 
   2816 void*
   2817 GNUNET_CHAT_file_get_user_pointer (const struct GNUNET_CHAT_File *file)
   2818 {
   2819   GNUNET_CHAT_VERSION_ASSERT();
   2820 
   2821   if (!file)
   2822     return NULL;
   2823 
   2824   return file->user_pointer;
   2825 }
   2826 
   2827 
   2828 enum GNUNET_GenericReturnValue
   2829 GNUNET_CHAT_file_is_downloading (const struct GNUNET_CHAT_File *file)
   2830 {
   2831   GNUNET_CHAT_VERSION_ASSERT();
   2832 
   2833   if ((!file) || (0 == (file->status & GNUNET_CHAT_FILE_STATUS_DOWNLOAD)))
   2834     return GNUNET_NO;
   2835   else
   2836     return GNUNET_YES;
   2837 }
   2838 
   2839 
   2840 enum GNUNET_GenericReturnValue
   2841 GNUNET_CHAT_file_start_download (struct GNUNET_CHAT_File *file,
   2842                                  GNUNET_CHAT_FileDownloadCallback callback,
   2843                                  void *cls)
   2844 {
   2845   GNUNET_CHAT_VERSION_ASSERT();
   2846 
   2847   if ((!file) || (!(file->uri)))
   2848     return GNUNET_SYSERR;
   2849 
   2850   if (file->download)
   2851   {
   2852     file_bind_downlaod(file, callback, cls);
   2853 
   2854     GNUNET_FS_download_resume(file->download);
   2855     return GNUNET_OK;
   2856   }
   2857 
   2858   char *filename = handle_create_file_path(
   2859     file->handle, &(file->hash)
   2860   );
   2861 
   2862   if (!filename)
   2863     return GNUNET_SYSERR;
   2864 
   2865   const uint64_t size = GNUNET_FS_uri_chk_get_file_size(file->uri);
   2866 
   2867   uint64_t offset;
   2868   if (GNUNET_OK != GNUNET_DISK_file_size(filename, &offset,
   2869       GNUNET_NO, GNUNET_YES))
   2870     offset = 0;
   2871 
   2872   if (offset >= size)
   2873   {
   2874     if (callback)
   2875       callback(cls, file, size, size);
   2876 
   2877     goto free_filename;
   2878   }
   2879 
   2880   file_bind_downlaod(file, callback, cls);
   2881 
   2882   const uint64_t remaining = (size - offset);
   2883 
   2884   file->download = GNUNET_FS_download_start(
   2885     file->handle->fs,
   2886     file->uri,
   2887     file->meta,
   2888     filename,
   2889     NULL,
   2890     offset,
   2891     remaining,
   2892     1,
   2893     GNUNET_FS_DOWNLOAD_OPTION_NONE,
   2894     file,
   2895     NULL
   2896   );
   2897 
   2898   if (file->download)
   2899     file->status |= GNUNET_CHAT_FILE_STATUS_DOWNLOAD;
   2900 
   2901 free_filename:
   2902   GNUNET_free(filename);
   2903   return GNUNET_OK;
   2904 }
   2905 
   2906 
   2907 enum GNUNET_GenericReturnValue
   2908 GNUNET_CHAT_file_pause_download (struct GNUNET_CHAT_File *file)
   2909 {
   2910   GNUNET_CHAT_VERSION_ASSERT();
   2911 
   2912   if (!file)
   2913     return GNUNET_SYSERR;
   2914 
   2915   if (!file->download)
   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   if (!file->download)
   2932     return GNUNET_SYSERR;
   2933 
   2934   GNUNET_FS_download_resume(file->download);
   2935   return GNUNET_OK;
   2936 }
   2937 
   2938 
   2939 enum GNUNET_GenericReturnValue
   2940 GNUNET_CHAT_file_stop_download (struct GNUNET_CHAT_File *file)
   2941 {
   2942   GNUNET_CHAT_VERSION_ASSERT();
   2943 
   2944   if (!file)
   2945     return GNUNET_SYSERR;
   2946 
   2947   if (!file->download)
   2948     return GNUNET_SYSERR;
   2949 
   2950   GNUNET_FS_download_stop(file->download, GNUNET_YES);
   2951   file->download = NULL;
   2952   return GNUNET_OK;
   2953 }
   2954 
   2955 
   2956 enum GNUNET_GenericReturnValue
   2957 GNUNET_CHAT_file_is_unindexing (const struct GNUNET_CHAT_File *file)
   2958 {
   2959   GNUNET_CHAT_VERSION_ASSERT();
   2960 
   2961   if ((!file) || (0 == (file->status & GNUNET_CHAT_FILE_STATUS_UNINDEX)))
   2962     return GNUNET_NO;
   2963   else
   2964     return GNUNET_YES;
   2965 }
   2966 
   2967 
   2968 enum GNUNET_GenericReturnValue
   2969 GNUNET_CHAT_file_unindex (struct GNUNET_CHAT_File *file,
   2970                           GNUNET_CHAT_FileUnindexCallback callback,
   2971                           void *cls)
   2972 {
   2973   GNUNET_CHAT_VERSION_ASSERT();
   2974 
   2975   if (!file)
   2976     return GNUNET_SYSERR;
   2977 
   2978   if (file->publish)
   2979   {
   2980     GNUNET_FS_publish_stop(file->publish);
   2981     file->publish = NULL;
   2982     return GNUNET_OK;
   2983   }
   2984 
   2985   file_bind_unindex(file, callback, cls);
   2986 
   2987   if (file->unindex)
   2988     return GNUNET_OK;
   2989 
   2990   char *filename = handle_create_file_path(
   2991     file->handle, &(file->hash)
   2992   );
   2993 
   2994   if (!filename)
   2995     return GNUNET_SYSERR;
   2996 
   2997   file->unindex = GNUNET_FS_unindex_start(
   2998     file->handle->fs, filename, file
   2999   );
   3000 
   3001   if (file->unindex)
   3002     file->status |= GNUNET_CHAT_FILE_STATUS_UNINDEX;
   3003 
   3004   GNUNET_free(filename);
   3005   return GNUNET_OK;
   3006 }
   3007 
   3008 
   3009 void
   3010 GNUNET_CHAT_invitation_accept (struct GNUNET_CHAT_Invitation *invitation)
   3011 {
   3012   GNUNET_CHAT_VERSION_ASSERT();
   3013 
   3014   if (!invitation)
   3015     return;
   3016 
   3017   struct GNUNET_CHAT_Handle *handle;
   3018   handle = invitation->context->handle;
   3019 
   3020   if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(
   3021       handle->contexts, &(invitation->key.hash)))
   3022     return;
   3023 
   3024   struct GNUNET_PeerIdentity door;
   3025   GNUNET_PEER_resolve(invitation->door, &door);
   3026 
   3027   struct GNUNET_MESSENGER_Room *room;
   3028   room = GNUNET_MESSENGER_enter_room(
   3029     invitation->context->handle->messenger,
   3030     &door, &(invitation->key)
   3031   );
   3032 
   3033   if (!room)
   3034     return;
   3035 
   3036   struct GNUNET_CHAT_Context *context;
   3037   context = context_create_from_room(handle, room);
   3038 
   3039   if (!context)
   3040     return;
   3041 
   3042   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(
   3043       handle->contexts, &(invitation->key.hash), context,
   3044       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   3045     goto destroy_context;
   3046 
   3047   if (GNUNET_CHAT_CONTEXT_TYPE_GROUP != context->type)
   3048   {
   3049     context_write_records(context);
   3050     return;
   3051   }
   3052 
   3053   struct GNUNET_CHAT_Group *group;
   3054   group = group_create_from_context(handle, context);
   3055 
   3056   if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(
   3057       handle->groups, &(invitation->key.hash), group,
   3058       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
   3059   {
   3060     context_write_records(context);
   3061     return;
   3062   }
   3063 
   3064   group_destroy(group);
   3065 
   3066   GNUNET_CONTAINER_multihashmap_remove(
   3067     handle->contexts, &(invitation->key.hash), context);
   3068 
   3069 destroy_context:
   3070   context_destroy(context);
   3071 }
   3072 
   3073 
   3074 void
   3075 GNUNET_CHAT_invitation_reject (struct GNUNET_CHAT_Invitation *invitation)
   3076 {
   3077   GNUNET_CHAT_VERSION_ASSERT();
   3078 
   3079   if (!invitation)
   3080     return;
   3081 
   3082   const struct GNUNET_MESSENGER_Contact *sender = GNUNET_MESSENGER_get_sender(
   3083     invitation->context->room, &(invitation->hash)
   3084   );
   3085 
   3086   if (!sender)
   3087     return;
   3088 
   3089   struct GNUNET_MESSENGER_Message msg;
   3090   memset(&msg, 0, sizeof(msg));
   3091 
   3092   msg.header.kind = GNUNET_MESSENGER_KIND_TAG;
   3093   GNUNET_memcpy(&(msg.body.tag.hash), &(invitation->hash),
   3094                 sizeof(struct GNUNET_HashCode));
   3095   msg.body.tag.tag = NULL;
   3096 
   3097   GNUNET_MESSENGER_send_message(invitation->context->room, &msg, sender);
   3098 }
   3099 
   3100 
   3101 enum GNUNET_GenericReturnValue
   3102 GNUNET_CHAT_invitation_is_accepted (const struct GNUNET_CHAT_Invitation *invitation)
   3103 {
   3104   GNUNET_CHAT_VERSION_ASSERT();
   3105 
   3106   if (!invitation)
   3107     return GNUNET_NO;
   3108 
   3109   return GNUNET_CONTAINER_multihashmap_contains(
   3110     invitation->context->handle->contexts,
   3111     &(invitation->key.hash)
   3112   );
   3113 }
   3114 
   3115 
   3116 enum GNUNET_GenericReturnValue
   3117 GNUNET_CHAT_invitation_is_rejected (const struct GNUNET_CHAT_Invitation *invitation)
   3118 {
   3119   GNUNET_CHAT_VERSION_ASSERT();
   3120 
   3121   if (!invitation)
   3122     return GNUNET_NO;
   3123 
   3124   const struct GNUNET_CHAT_InternalTagging *tagging = GNUNET_CONTAINER_multihashmap_get(
   3125     invitation->context->taggings, &(invitation->hash));
   3126 
   3127   if (!tagging)
   3128     return GNUNET_NO;
   3129 
   3130   if (internal_tagging_iterate(tagging, GNUNET_NO, NULL, NULL, NULL) > 0)
   3131     return GNUNET_YES;
   3132   else
   3133     return GNUNET_NO;
   3134 }
   3135 
   3136 
   3137 enum GNUNET_GenericReturnValue
   3138 GNUNET_CHAT_invitation_is_direct (const struct GNUNET_CHAT_Invitation *invitation)
   3139 {
   3140   GNUNET_CHAT_VERSION_ASSERT();
   3141 
   3142   if ((invitation->key.code.public_bit) ||
   3143       (invitation->key.code.group_bit) ||
   3144       (invitation->key.code.feed_bit))
   3145     return GNUNET_NO;
   3146   else
   3147     return GNUNET_YES;
   3148 }
   3149 
   3150 
   3151 const struct GNUNET_CHAT_DiscourseId*
   3152 GNUNET_CHAT_discourse_get_id (const struct GNUNET_CHAT_Discourse *discourse)
   3153 {
   3154   GNUNET_CHAT_VERSION_ASSERT();
   3155 
   3156   if (!discourse)
   3157     return NULL;
   3158 
   3159   return &(discourse->id);
   3160 }
   3161 
   3162 
   3163 enum GNUNET_GenericReturnValue
   3164 GNUNET_CHAT_discourse_is_open (const struct GNUNET_CHAT_Discourse *discourse)
   3165 {
   3166   GNUNET_CHAT_VERSION_ASSERT();
   3167 
   3168   if (!discourse)
   3169     return GNUNET_SYSERR;
   3170 
   3171   struct GNUNET_CHAT_DiscourseSubscription *sub;
   3172   for (sub = discourse->head; sub; sub = sub->next)
   3173   {
   3174     if (GNUNET_TIME_absolute_cmp(sub->end, <, GNUNET_TIME_absolute_get()))
   3175       continue;
   3176 
   3177     if (GNUNET_YES == sub->contact->owned)
   3178       return GNUNET_YES;
   3179   }
   3180 
   3181   return GNUNET_NO;
   3182 }
   3183 
   3184 
   3185 void
   3186 GNUNET_CHAT_discourse_set_user_pointer (struct GNUNET_CHAT_Discourse *discourse,
   3187                                         void *user_pointer)
   3188 {
   3189   GNUNET_CHAT_VERSION_ASSERT();
   3190 
   3191   if (!discourse)
   3192     return;
   3193 
   3194   discourse->user_pointer = user_pointer;
   3195 }
   3196 
   3197 
   3198 void*
   3199 GNUNET_CHAT_discourse_get_user_pointer (const struct GNUNET_CHAT_Discourse *discourse)
   3200 {
   3201   GNUNET_CHAT_VERSION_ASSERT();
   3202 
   3203   if (!discourse)
   3204     return NULL;
   3205 
   3206   return discourse->user_pointer;
   3207 }
   3208 
   3209 
   3210 void
   3211 GNUNET_CHAT_discourse_close (struct GNUNET_CHAT_Discourse *discourse)
   3212 {
   3213   GNUNET_CHAT_VERSION_ASSERT();
   3214 
   3215   if ((!discourse) || (!(discourse->context)) || (!(discourse->context->room)))
   3216     return;
   3217 
   3218   struct GNUNET_MESSENGER_Message msg;
   3219   memset(&msg, 0, sizeof(msg));
   3220 
   3221   msg.header.kind = GNUNET_MESSENGER_KIND_SUBSCRIBTION;
   3222 
   3223   util_shorthash_from_discourse_id(
   3224     &(discourse->id),
   3225     &(msg.body.subscription.discourse)
   3226   );
   3227 
   3228   msg.body.subscription.time = GNUNET_TIME_relative_hton(GNUNET_TIME_relative_get_zero_());
   3229   msg.body.subscription.flags = GNUNET_MESSENGER_FLAG_SUBSCRIPTION_UNSUBSCRIBE;
   3230 
   3231   GNUNET_MESSENGER_send_message(
   3232     discourse->context->room,
   3233     &msg,
   3234     NULL
   3235   );
   3236 }
   3237 
   3238 
   3239 enum GNUNET_GenericReturnValue
   3240 GNUNET_CHAT_discourse_write (struct GNUNET_CHAT_Discourse *discourse,
   3241                              const char *data,
   3242                              uint64_t size)
   3243 {
   3244   GNUNET_CHAT_VERSION_ASSERT();
   3245 
   3246   if ((!discourse) || (!data) || (!(discourse->context)) ||
   3247       (!(discourse->context->room)))
   3248     return GNUNET_SYSERR;
   3249 
   3250   static const uint64_t max_size = (uint16_t) (
   3251     GNUNET_MAX_MESSAGE_SIZE - GNUNET_MIN_MESSAGE_SIZE -
   3252     sizeof (struct GNUNET_MESSENGER_Message)
   3253   );
   3254 
   3255   struct GNUNET_MESSENGER_Message msg;
   3256   memset(&msg, 0, sizeof(msg));
   3257 
   3258   msg.header.kind = GNUNET_MESSENGER_KIND_TALK;
   3259   msg.body.talk.data = GNUNET_malloc(size > max_size? max_size : size);
   3260 
   3261   util_shorthash_from_discourse_id(
   3262     &(discourse->id),
   3263     &(msg.body.talk.discourse)
   3264   );
   3265 
   3266   while (size > 0)
   3267   {
   3268     msg.body.talk.length = (uint16_t) (size > max_size? max_size : size);
   3269 
   3270     GNUNET_memcpy(
   3271       msg.body.talk.data,
   3272       data,
   3273       msg.body.talk.length
   3274     );
   3275 
   3276     size -= msg.body.talk.length;
   3277     data += msg.body.talk.length;
   3278 
   3279     GNUNET_MESSENGER_send_message(discourse->context->room, &msg, NULL);
   3280   }
   3281 
   3282   GNUNET_free(msg.body.talk.data);
   3283   return GNUNET_OK;
   3284 }
   3285 
   3286 
   3287 int
   3288 GNUNET_CHAT_discourse_get_fd (const struct GNUNET_CHAT_Discourse *discourse)
   3289 {
   3290   GNUNET_CHAT_VERSION_ASSERT();
   3291 
   3292   if (! discourse)
   3293     return GNUNET_SYSERR;
   3294 
   3295   return discourse->pipe[1];
   3296 }
   3297 
   3298 
   3299 int
   3300 GNUNET_CHAT_discourse_iterate_contacts (struct GNUNET_CHAT_Discourse *discourse,
   3301                                         GNUNET_CHAT_DiscourseContactCallback callback,
   3302                                         void *cls)
   3303 {
   3304   GNUNET_CHAT_VERSION_ASSERT();
   3305 
   3306   if (! discourse)
   3307     return GNUNET_SYSERR;
   3308 
   3309   int iterations = 0;
   3310 
   3311   struct GNUNET_CHAT_DiscourseSubscription *sub;
   3312   for (sub = discourse->head; sub; sub = sub->next)
   3313   {
   3314     if (GNUNET_TIME_absolute_cmp(sub->end, <, GNUNET_TIME_absolute_get()))
   3315       continue;
   3316 
   3317     if (callback)
   3318       callback(cls, discourse, sub->contact);
   3319 
   3320     iterations++;
   3321   }
   3322 
   3323   return iterations;
   3324 }
   3325 
   3326 enum GNUNET_GenericReturnValue
   3327 GNUNET_CHAT_generate_secret (char *secret,
   3328                              uint32_t secret_len)
   3329 {
   3330   GNUNET_CHAT_VERSION_ASSERT();
   3331 
   3332   if (secret_len <= 0)
   3333     return GNUNET_SYSERR;
   3334 
   3335   const uint32_t requested = secret_len * 5 / 8 + 1;
   3336   const uint32_t size = ((requested*8) + (((requested*8) % 5) > 0 ? 5 - ((requested*8) % 5) : 0)) / 5;
   3337 
   3338   char *raw_secret = GNUNET_malloc(requested);
   3339   char *buf;
   3340 
   3341   if (!raw_secret)
   3342     return GNUNET_SYSERR;
   3343 
   3344   if (size > secret_len)
   3345   {
   3346     buf = GNUNET_malloc(size);
   3347 
   3348     if (!buf)
   3349     {
   3350       GNUNET_free(raw_secret);
   3351       return GNUNET_SYSERR;
   3352     }
   3353   }
   3354   else
   3355     buf = secret;
   3356 
   3357   GNUNET_CRYPTO_random_block(
   3358     raw_secret,
   3359     requested
   3360   );
   3361 
   3362   enum GNUNET_GenericReturnValue result;
   3363   result = GNUNET_STRINGS_data_to_string(
   3364     raw_secret,
   3365     requested,
   3366     buf,
   3367     size
   3368   ) == NULL? GNUNET_SYSERR : GNUNET_OK;
   3369 
   3370   GNUNET_CRYPTO_zero_keys(raw_secret, requested);
   3371   GNUNET_free(raw_secret);
   3372 
   3373   if (buf != secret)
   3374   {
   3375     GNUNET_memcpy(secret, buf, secret_len);
   3376     GNUNET_CRYPTO_zero_keys(buf, size);
   3377     GNUNET_free(buf);
   3378   }
   3379 
   3380   return result;
   3381 }