libgnunetchat

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

gnunet_chat_lib.c (73892B)


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