libgnunetchat

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

gnunet_chat_lib_intern.c (28809B)


      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_intern.c
     23  */
     24 
     25 #include "gnunet_chat_account.h"
     26 #include "gnunet_chat_contact.h"
     27 #include "gnunet_chat_context.h"
     28 #include "gnunet_chat_group.h"
     29 #include "gnunet_chat_handle.h"
     30 #include "gnunet_chat_lib.h"
     31 
     32 #include <gnunet/gnunet_common.h>
     33 #include <gnunet/gnunet_messenger_service.h>
     34 #include <gnunet/gnunet_reclaim_lib.h>
     35 #include <gnunet/gnunet_reclaim_service.h>
     36 #include <gnunet/gnunet_time_lib.h>
     37 #include <stdint.h>
     38 #include <string.h>
     39 
     40 #define GNUNET_UNUSED __attribute__ ((unused))
     41 
     42 void
     43 task_handle_destruction (void *cls)
     44 {
     45   GNUNET_assert(cls);
     46 
     47   struct GNUNET_CHAT_Handle *handle = (struct GNUNET_CHAT_Handle*) cls;
     48 
     49   struct GNUNET_CHAT_InternalAccounts *accounts = handle->accounts_head;
     50   while (accounts)
     51   {
     52     if ((accounts->op) && (GNUNET_CHAT_ACCOUNT_NONE != accounts->method))
     53       break;
     54 
     55     accounts = accounts->next;
     56   }
     57 
     58   if (accounts)
     59   {
     60     handle->destruction = GNUNET_SCHEDULER_add_delayed_with_priority(
     61       GNUNET_TIME_relative_get_millisecond_(),
     62       GNUNET_SCHEDULER_PRIORITY_BACKGROUND,
     63       task_handle_destruction,
     64       handle
     65     );
     66 
     67     return;
     68   }
     69 
     70   handle->destruction = NULL;
     71   handle_destroy(handle);
     72 }
     73 
     74 void
     75 task_handle_connection (void *cls)
     76 {
     77   GNUNET_assert(cls);
     78 
     79   struct GNUNET_CHAT_Handle *handle = (struct GNUNET_CHAT_Handle*) cls;
     80 
     81   handle->connection = NULL;
     82 
     83   if (! handle->next)
     84     return;
     85 
     86   struct GNUNET_CHAT_Account *account = handle->next;
     87   struct GNUNET_HashCode local_secret;
     88   const struct GNUNET_HashCode *secret;
     89 
     90   if (handle->next_secret)
     91   {
     92     GNUNET_memcpy(
     93       &local_secret,
     94       handle->next_secret,
     95       sizeof(local_secret)
     96     );
     97 
     98     secret = &local_secret;
     99 
    100     GNUNET_CRYPTO_zero_keys(
    101       handle->next_secret,
    102       sizeof(*(handle->next_secret))
    103     );
    104 
    105     GNUNET_free(handle->next_secret);
    106     handle->next_secret = NULL;
    107   }
    108   else
    109     secret = NULL;
    110 
    111   handle->next = NULL;
    112   handle_connect(handle, account, secret);
    113 
    114   GNUNET_CRYPTO_zero_keys(
    115     &local_secret,
    116     sizeof(local_secret)
    117   );
    118 }
    119 
    120 void
    121 task_handle_disconnection (void *cls)
    122 {
    123   GNUNET_assert(cls);
    124 
    125   struct GNUNET_CHAT_Handle *handle = (struct GNUNET_CHAT_Handle*) cls;
    126 
    127   handle->connection = NULL;
    128   handle_disconnect(handle);
    129 
    130   if (! handle->next)
    131     return;
    132 
    133   task_handle_connection(cls);
    134 }
    135 
    136 void
    137 cb_lobby_lookup (void *cls,
    138                  uint32_t count,
    139                  const struct GNUNET_GNSRECORD_Data *data)
    140 {
    141   GNUNET_assert(cls);
    142 
    143   struct GNUNET_CHAT_UriLookups *lookups = (struct GNUNET_CHAT_UriLookups*) cls;
    144 
    145   if ((!(lookups->handle)) || (!(lookups->uri)) ||
    146       (GNUNET_CHAT_URI_TYPE_CHAT != lookups->uri->type))
    147     goto drop_lookup;
    148 
    149   struct GNUNET_CHAT_Context *context = handle_process_records(
    150     lookups->handle,
    151     lookups->uri->chat.label,
    152     count,
    153     data
    154   );
    155 
    156   if (context)
    157     context_write_records(context);
    158 
    159 drop_lookup:
    160   if (lookups->uri)
    161     uri_destroy(lookups->uri);
    162 
    163   if (lookups->handle)
    164     GNUNET_CONTAINER_DLL_remove(
    165       lookups->handle->lookups_head,
    166       lookups->handle->lookups_tail,
    167       lookups
    168     );
    169 
    170   GNUNET_free(lookups);
    171 }
    172 
    173 struct GNUNET_CHAT_IterateFiles
    174 {
    175   struct GNUNET_CHAT_Handle *handle;
    176   GNUNET_CHAT_FileCallback cb;
    177   void *cls;
    178 };
    179 
    180 enum GNUNET_GenericReturnValue
    181 it_iterate_files (void *cls,
    182                   GNUNET_UNUSED const struct GNUNET_HashCode *key,
    183                   void *value)
    184 {
    185   GNUNET_assert((cls) && (key));
    186 
    187   struct GNUNET_CHAT_IterateFiles *it = cls;
    188 
    189   if (!(it->cb))
    190     return GNUNET_YES;
    191 
    192   struct GNUNET_CHAT_File *file = (struct GNUNET_CHAT_File*) value;
    193 
    194   if (!file)
    195     return GNUNET_YES;
    196 
    197   return it->cb(it->cls, it->handle, file);
    198 }
    199 
    200 struct GNUNET_CHAT_HandleIterateContacts
    201 {
    202   struct GNUNET_CHAT_Handle *handle;
    203   GNUNET_CHAT_ContactCallback cb;
    204   void *cls;
    205 };
    206 
    207 enum GNUNET_GenericReturnValue
    208 it_handle_iterate_contacts (void *cls,
    209                             GNUNET_UNUSED const struct GNUNET_ShortHashCode *key,
    210                             void *value)
    211 {
    212   GNUNET_assert((cls) && (value));
    213 
    214   struct GNUNET_CHAT_HandleIterateContacts *it = cls;
    215 
    216   if (!(it->cb))
    217     return GNUNET_YES;
    218 
    219   struct GNUNET_CHAT_Contact *contact = value;
    220 
    221   return it->cb(it->cls, it->handle, contact);
    222 }
    223 
    224 enum GNUNET_GenericReturnValue
    225 it_handle_find_own_contact (GNUNET_UNUSED void *cls,
    226                             struct GNUNET_CHAT_Handle *handle,
    227                             struct GNUNET_CHAT_Contact *contact)
    228 {
    229   GNUNET_assert((handle) && (contact));
    230 
    231   if (GNUNET_YES != GNUNET_CHAT_contact_is_owned(contact))
    232     return GNUNET_YES;
    233 
    234   const char *contact_key = GNUNET_CHAT_contact_get_key(contact);
    235   const char *handle_key = GNUNET_CHAT_get_key(handle);
    236 
    237   if ((!contact_key) || (!handle_key) ||
    238       (0 != strcmp(contact_key, handle_key)))
    239     return GNUNET_YES;
    240 
    241   handle->own_contact = contact;
    242   return GNUNET_NO;
    243 }
    244 
    245 struct GNUNET_CHAT_HandleIterateGroups
    246 {
    247   struct GNUNET_CHAT_Handle *handle;
    248   GNUNET_CHAT_GroupCallback cb;
    249   void *cls;
    250 };
    251 
    252 enum GNUNET_GenericReturnValue
    253 it_handle_iterate_groups (void *cls,
    254                           GNUNET_UNUSED const struct GNUNET_HashCode *key,
    255                           void *value)
    256 {
    257   GNUNET_assert((cls) && (value));
    258 
    259   struct GNUNET_CHAT_HandleIterateGroups *it = cls;
    260 
    261   if (!(it->cb))
    262     return GNUNET_YES;
    263 
    264   struct GNUNET_CHAT_Group *group = value;
    265 
    266   return it->cb(it->cls, it->handle, group);
    267 }
    268 
    269 typedef void
    270 (*GNUNET_CHAT_ContactIterateContextCallback) (struct GNUNET_CHAT_Contact *contact,
    271                                               struct GNUNET_CHAT_Context *context,
    272                                               const char *tag);
    273 
    274 struct GNUNET_CHAT_ContactIterateContexts
    275 {
    276   struct GNUNET_CHAT_Contact *contact;
    277   const char *tag;
    278 
    279   GNUNET_CHAT_ContactIterateContextCallback cb;
    280 };
    281 
    282 enum GNUNET_GenericReturnValue
    283 it_contact_iterate_contexts (void *cls,
    284                              const struct GNUNET_HashCode *key,
    285                              GNUNET_UNUSED void *value)
    286 {
    287   GNUNET_assert((cls) && (key));
    288 
    289   struct GNUNET_CHAT_ContactIterateContexts *it = cls;
    290 
    291   if (!(it->cb))
    292     return GNUNET_YES;
    293 
    294   struct GNUNET_CHAT_Handle *handle = it->contact->handle;
    295   struct GNUNET_CHAT_Context *context = GNUNET_CONTAINER_multihashmap_get(
    296     handle->contexts, key);
    297 
    298   if (! context)
    299     return GNUNET_YES;
    300 
    301   it->cb(it->contact, context, it->tag);
    302   return GNUNET_YES;
    303 }
    304 
    305 struct GNUNET_CHAT_RoomFindContact
    306 {
    307   const struct GNUNET_CRYPTO_BlindablePublicKey *ignore_key;
    308   const struct GNUNET_MESSENGER_Contact *contact;
    309 };
    310 
    311 enum GNUNET_GenericReturnValue
    312 it_room_find_contact (void *cls,
    313                       GNUNET_UNUSED struct GNUNET_MESSENGER_Room *room,
    314                       const struct GNUNET_MESSENGER_Contact *member)
    315 {
    316   GNUNET_assert((cls) && (member));
    317 
    318   const struct GNUNET_CRYPTO_BlindablePublicKey *key = GNUNET_MESSENGER_contact_get_key(
    319       member
    320   );
    321 
    322   struct GNUNET_CHAT_RoomFindContact *find = cls;
    323 
    324   if ((find->ignore_key) && (key) &&
    325       (0 == GNUNET_memcmp(find->ignore_key, key)))
    326     return GNUNET_YES;
    327 
    328   find->contact = member;
    329   return GNUNET_NO;
    330 }
    331 
    332 void
    333 task_lobby_destruction (void *cls)
    334 {
    335   GNUNET_assert(cls);
    336 
    337   struct GNUNET_CHAT_Lobby *lobby = (struct GNUNET_CHAT_Lobby*) cls;
    338   struct GNUNET_CHAT_InternalLobbies *lobbies = lobby->handle->lobbies_head;
    339 
    340   while (lobbies)
    341   {
    342     if (lobbies->lobby == lobby)
    343     {
    344       GNUNET_CONTAINER_DLL_remove(
    345         lobby->handle->lobbies_head,
    346         lobby->handle->lobbies_tail,
    347         lobbies
    348       );
    349 
    350       GNUNET_free(lobbies);
    351       break;
    352     }
    353 
    354     lobbies = lobbies->next;
    355   }
    356 
    357   struct GNUNET_CHAT_Context *context = lobby->context;
    358 
    359   lobby->destruction = NULL;
    360   lobby_destroy(lobby);
    361 
    362   if ((! context) ||
    363       (GNUNET_OK == GNUNET_CHAT_context_get_status(context)))
    364     return;
    365 
    366   context_delete(context, GNUNET_YES);
    367 }
    368 
    369 void
    370 task_contact_destruction (void *cls)
    371 {
    372   GNUNET_assert(cls);
    373 
    374   struct GNUNET_CHAT_Contact *contact = (struct GNUNET_CHAT_Contact*) cls;
    375   struct GNUNET_ShortHashCode shorthash;
    376 
    377   util_shorthash_from_member(contact->member, &shorthash);
    378 
    379   contact_leave (contact, contact->context);
    380 
    381   const uint32_t other_contexts = GNUNET_CONTAINER_multihashmap_size(
    382     contact->joined
    383   );
    384 
    385   if (0 >= other_contexts)
    386     GNUNET_CONTAINER_multishortmap_remove(
    387       contact->handle->contacts, &shorthash, contact
    388     );
    389 
    390   context_delete(contact->context, GNUNET_YES);
    391 
    392   contact->destruction = NULL;
    393 
    394   if (0 >= other_contexts)
    395     contact_destroy(contact);
    396 }
    397 
    398 void
    399 task_group_destruction (void *cls)
    400 {
    401   GNUNET_assert(cls);
    402 
    403   struct GNUNET_CHAT_Group *group = (struct GNUNET_CHAT_Group*) cls;
    404   struct GNUNET_HashCode key;
    405 
    406   GNUNET_memcpy(&key, GNUNET_MESSENGER_room_get_key(
    407     group->context->room
    408   ), sizeof(key));
    409 
    410   GNUNET_CONTAINER_multihashmap_remove(
    411     group->handle->groups, &key, group
    412   );
    413 
    414   context_delete(group->context, GNUNET_YES);
    415 
    416   group->destruction = NULL;
    417 
    418   group_destroy(group);
    419 }
    420 
    421 struct GNUNET_CHAT_GroupIterateContacts
    422 {
    423   struct GNUNET_CHAT_Group *group;
    424   GNUNET_CHAT_GroupContactCallback cb;
    425   void *cls;
    426 };
    427 
    428 enum GNUNET_GenericReturnValue
    429 it_group_iterate_contacts (void* cls,
    430 			                     GNUNET_UNUSED struct GNUNET_MESSENGER_Room *room,
    431                            const struct GNUNET_MESSENGER_Contact *member)
    432 {
    433   GNUNET_assert((cls) && (member));
    434 
    435   struct GNUNET_CHAT_GroupIterateContacts *it = cls;
    436 
    437   if (!(it->cb))
    438     return GNUNET_YES;
    439 
    440   return it->cb(it->cls, it->group, handle_get_contact_from_messenger(
    441     it->group->handle, member
    442   ));
    443 }
    444 
    445 struct GNUNET_CHAT_ContextIterateMessages
    446 {
    447   struct GNUNET_CHAT_Context *context;
    448   GNUNET_CHAT_ContextMessageCallback cb;
    449   void *cls;
    450 };
    451 
    452 enum GNUNET_GenericReturnValue
    453 it_context_iterate_messages (void *cls,
    454                              GNUNET_UNUSED const struct GNUNET_HashCode *key,
    455                              void *value)
    456 {
    457   GNUNET_assert((cls) && (value));
    458 
    459   struct GNUNET_CHAT_ContextIterateMessages *it = cls;
    460 
    461   if (!(it->cb))
    462     return GNUNET_YES;
    463 
    464   struct GNUNET_CHAT_Message *message = value;
    465 
    466   return it->cb(it->cls, it->context, message);
    467 }
    468 
    469 struct GNUNET_CHAT_ContextIterateFiles
    470 {
    471   struct GNUNET_CHAT_Context *context;
    472   GNUNET_CHAT_ContextFileCallback cb;
    473   void *cls;
    474 };
    475 
    476 enum GNUNET_GenericReturnValue
    477 it_context_iterate_files (void *cls,
    478                           const struct GNUNET_HashCode *key,
    479                           GNUNET_UNUSED void *value)
    480 {
    481   GNUNET_assert((cls) && (key));
    482 
    483   struct GNUNET_CHAT_ContextIterateFiles *it = cls;
    484 
    485   if (!(it->cb))
    486     return GNUNET_YES;
    487 
    488   struct GNUNET_CHAT_Message *message = GNUNET_CONTAINER_multihashmap_get(
    489     it->context->messages, key
    490   );
    491 
    492   if ((!message) || (! message->msg))
    493     return GNUNET_YES;
    494 
    495   struct GNUNET_CHAT_File *file = GNUNET_CONTAINER_multihashmap_get(
    496     it->context->handle->files, &(message->msg->body.file.hash)
    497   );
    498 
    499   if (!file)
    500     return GNUNET_YES;
    501 
    502   return it->cb(it->cls, it->context, file);
    503 }
    504 
    505 struct GNUNET_CHAT_ContextIterateDiscourses
    506 {
    507   struct GNUNET_CHAT_Context *context;
    508   GNUNET_CHAT_DiscourseCallback cb;
    509   void *cls;
    510 };
    511 
    512 enum GNUNET_GenericReturnValue
    513 it_context_iterate_discourses (void *cls,
    514                                GNUNET_UNUSED const struct GNUNET_ShortHashCode *key,
    515                                void *value)
    516 {
    517   GNUNET_assert((cls) && (value));
    518 
    519   struct GNUNET_CHAT_ContextIterateDiscourses *it = cls;
    520   struct GNUNET_CHAT_Discourse *discourse = value;
    521 
    522   if (!(it->cb))
    523     return GNUNET_YES;
    524 
    525   return it->cb(it->cls, it->context, discourse);
    526 }
    527 
    528 struct GNUNET_CHAT_MessageIterateReadReceipts
    529 {
    530   struct GNUNET_CHAT_Message *message;
    531   GNUNET_CHAT_MessageReadReceiptCallback cb;
    532   void *cls;
    533 };
    534 
    535 enum GNUNET_GenericReturnValue
    536 it_message_iterate_read_receipts (void *cls,
    537                                   GNUNET_UNUSED struct GNUNET_MESSENGER_Room *room,
    538                                   const struct GNUNET_MESSENGER_Contact *member)
    539 {
    540   GNUNET_assert((cls) && (member));
    541 
    542   struct GNUNET_CHAT_MessageIterateReadReceipts *it = cls;
    543   struct GNUNET_CHAT_Handle *handle = it->message->context->handle;
    544 
    545   if (!handle)
    546     return GNUNET_NO;
    547 
    548   struct GNUNET_ShortHashCode shorthash;
    549   util_shorthash_from_member(member, &shorthash);
    550 
    551   struct GNUNET_CHAT_Contact *contact = GNUNET_CONTAINER_multishortmap_get(
    552       handle->contacts, &shorthash
    553   );
    554 
    555   if (!contact)
    556     return GNUNET_YES;
    557 
    558   struct GNUNET_TIME_Absolute *timestamp = GNUNET_CONTAINER_multishortmap_get(
    559     it->message->context->timestamps, &shorthash
    560   );
    561 
    562   if (!timestamp)
    563     return GNUNET_YES;
    564 
    565   struct GNUNET_TIME_Absolute abs = GNUNET_TIME_absolute_ntoh(
    566     it->message->msg->header.timestamp
    567   );
    568 
    569   struct GNUNET_TIME_Relative delta = GNUNET_TIME_absolute_get_difference(
    570     *timestamp, abs
    571   );
    572 
    573   int read_receipt;
    574   if (GNUNET_TIME_relative_get_zero_().rel_value_us == delta.rel_value_us)
    575     read_receipt = GNUNET_YES;
    576   else
    577     read_receipt = GNUNET_NO;
    578 
    579   if (it->cb)
    580     it->cb(it->cls, it->message, contact, read_receipt);
    581 
    582   return GNUNET_YES;
    583 }
    584 
    585 void
    586 cont_update_attribute_with_status (void *cls,
    587                                    int32_t success,
    588                                    const char *emsg)
    589 {
    590   GNUNET_assert(cls);
    591 
    592   struct GNUNET_CHAT_AttributeProcess *attributes = (
    593     (struct GNUNET_CHAT_AttributeProcess*) cls
    594   );
    595 
    596   attributes->op = NULL;
    597 
    598   struct GNUNET_CHAT_Account *account = attributes->account;
    599   struct GNUNET_CHAT_Handle *handle = attributes->handle;
    600 
    601   const char *attribute_name = NULL;
    602 
    603   if (attributes->attribute)
    604     attribute_name = attributes->attribute->name;
    605 
    606   if (GNUNET_SYSERR == success)
    607     handle_send_internal_message(
    608       handle,
    609       account,
    610       NULL,
    611       GNUNET_CHAT_FLAG_WARNING,
    612       emsg,
    613       GNUNET_YES
    614     );
    615   else
    616     handle_send_internal_message(
    617       handle,
    618       account,
    619       NULL,
    620       GNUNET_CHAT_FLAG_ATTRIBUTES,
    621       attribute_name,
    622       GNUNET_YES
    623     );
    624 
    625   internal_attributes_destroy(attributes);
    626 }
    627 
    628 void
    629 cb_task_finish_iterate_attribute (void *cls)
    630 {
    631   GNUNET_assert(cls);
    632 
    633   struct GNUNET_CHAT_AttributeProcess *attributes = (
    634     (struct GNUNET_CHAT_AttributeProcess*) cls
    635   );
    636 
    637   attributes->iter = NULL;
    638 
    639   struct GNUNET_CHAT_Handle *handle = attributes->handle;
    640 
    641   const struct GNUNET_CRYPTO_BlindablePrivateKey *key;
    642 
    643   if (attributes->account)
    644     key = account_get_key(attributes->account);
    645   else
    646     key = handle_get_key(handle);
    647 
    648   if (attributes->name)
    649     GNUNET_free(attributes->name);
    650 
    651   attributes->name = NULL;
    652 
    653   if ((! attributes->op) && (key) &&
    654       (attributes->attribute))
    655     attributes->op = GNUNET_RECLAIM_attribute_store(
    656       handle->reclaim,
    657       key,
    658       attributes->attribute,
    659       &(attributes->expires),
    660       cont_update_attribute_with_status,
    661       attributes
    662     );
    663 
    664   if (attributes->data)
    665     GNUNET_free(attributes->data);
    666 
    667   attributes->data = NULL;
    668 
    669   if (attributes->op)
    670     return;
    671 
    672   internal_attributes_destroy(attributes);
    673 }
    674 
    675 void
    676 cb_task_error_iterate_attribute (void *cls)
    677 {
    678   GNUNET_assert(cls);
    679 
    680   struct GNUNET_CHAT_AttributeProcess *attributes = (
    681     (struct GNUNET_CHAT_AttributeProcess*) cls
    682   );
    683 
    684   handle_send_internal_message(
    685     attributes->handle,
    686     attributes->account,
    687     NULL,
    688     GNUNET_CHAT_FLAG_WARNING,
    689     "Attribute iteration failed!",
    690     GNUNET_YES
    691   );
    692 
    693   cb_task_finish_iterate_attribute(cls);
    694 }
    695 
    696 void
    697 cb_store_attribute (void *cls,
    698                     const struct GNUNET_CRYPTO_BlindablePublicKey *identity,
    699                     const struct GNUNET_RECLAIM_Attribute *attribute)
    700 {
    701   GNUNET_assert(cls);
    702 
    703   struct GNUNET_CHAT_AttributeProcess *attributes = (
    704     (struct GNUNET_CHAT_AttributeProcess*) cls
    705   );
    706 
    707   struct GNUNET_CHAT_Handle *handle = attributes->handle;
    708 
    709   const struct GNUNET_CRYPTO_BlindablePrivateKey *key = handle_get_key(
    710     handle
    711   );
    712 
    713   if (! attributes->name)
    714   {
    715     internal_attributes_stop_iter(attributes);
    716     return;
    717   }
    718 
    719   if (0 == strcmp(attribute->name, attributes->name))
    720   {
    721     internal_attributes_stop_iter(attributes);
    722 
    723     if (attributes->attribute)
    724     {
    725       attributes->attribute->credential = attribute->credential;
    726       attributes->attribute->flag = attribute->flag;
    727       attributes->attribute->id = attribute->id;
    728     }
    729 
    730     attributes->op = GNUNET_RECLAIM_attribute_store(
    731       handle->reclaim,
    732       key,
    733       attributes->attribute,
    734       &(attributes->expires),
    735       cont_update_attribute_with_status,
    736       attributes
    737     );
    738 
    739     if (attributes->data)
    740       GNUNET_free(attributes->data);
    741 
    742     attributes->data = NULL;
    743 
    744     GNUNET_free(attributes->name);
    745     attributes->name = NULL;
    746     return;
    747   }
    748 
    749   internal_attributes_next_iter(attributes);
    750 }
    751 
    752 void
    753 cb_delete_attribute (void *cls,
    754                      const struct GNUNET_CRYPTO_BlindablePublicKey *identity,
    755                      const struct GNUNET_RECLAIM_Attribute *attribute)
    756 {
    757   GNUNET_assert(cls);
    758 
    759   struct GNUNET_CHAT_AttributeProcess *attributes = (
    760     (struct GNUNET_CHAT_AttributeProcess*) cls
    761   );
    762 
    763   if (! attributes->name)
    764   {
    765     internal_attributes_stop_iter(attributes);
    766     return;
    767   }
    768 
    769   struct GNUNET_CHAT_Handle *handle = attributes->handle;
    770 
    771   const struct GNUNET_CRYPTO_BlindablePrivateKey *key = handle_get_key(
    772     handle
    773   );
    774 
    775   if (0 == strcmp(attribute->name, attributes->name))
    776   {
    777     internal_attributes_stop_iter(attributes);
    778 
    779     attributes->op = GNUNET_RECLAIM_attribute_delete(
    780       handle->reclaim,
    781       key,
    782       attribute,
    783       cont_update_attribute_with_status,
    784       attributes
    785     );
    786 
    787     GNUNET_free(attributes->name);
    788     attributes->name = NULL;
    789     return;
    790   }
    791 
    792   internal_attributes_next_iter(attributes);
    793 }
    794 
    795 void
    796 cb_iterate_attribute (void *cls,
    797                       const struct GNUNET_CRYPTO_BlindablePublicKey *identity,
    798                       const struct GNUNET_RECLAIM_Attribute *attribute)
    799 {
    800   GNUNET_assert(cls);
    801 
    802   struct GNUNET_CHAT_AttributeProcess *attributes = (
    803     (struct GNUNET_CHAT_AttributeProcess*) cls
    804   );
    805 
    806   struct GNUNET_CHAT_Handle *handle = attributes->handle;
    807   enum GNUNET_GenericReturnValue result = GNUNET_YES;
    808 
    809   char *value = GNUNET_RECLAIM_attribute_value_to_string(
    810     attribute->type,
    811     attribute->data,
    812     attribute->data_size
    813   );
    814 
    815   if (attributes->callback)
    816     result = attributes->callback(attributes->closure, handle, attribute->name, value);
    817   else if (attributes->account_callback)
    818     result = attributes->account_callback(
    819       attributes->closure,
    820       attributes->account,
    821       attribute->name,
    822       value
    823     );
    824 
    825   if (value)
    826     GNUNET_free (value);
    827 
    828   if (GNUNET_YES != result)
    829     internal_attributes_stop_iter(attributes);
    830   else
    831     internal_attributes_next_iter(attributes);
    832 }
    833 
    834 void
    835 cb_issue_ticket (void *cls,
    836                  const struct GNUNET_RECLAIM_Ticket *ticket,
    837                  const struct GNUNET_RECLAIM_PresentationList *presentations)
    838 {
    839   GNUNET_assert(cls);
    840 
    841   struct GNUNET_CHAT_AttributeProcess *attributes = (
    842     (struct GNUNET_CHAT_AttributeProcess*) cls
    843   );
    844 
    845   attributes->op = NULL;
    846 
    847   if ((!(attributes->contact)) || (!(attributes->contact->member)))
    848     goto skip_sending;
    849 
    850   struct GNUNET_CHAT_Context *context = contact_find_context(
    851     attributes->contact,
    852     GNUNET_YES
    853   );
    854 
    855   if ((!context) || (!ticket))
    856     goto skip_sending;
    857 
    858   char *identifier = GNUNET_strdup(ticket->gns_name);
    859 
    860   if (!identifier)
    861     goto skip_sending;
    862 
    863   struct GNUNET_MESSENGER_Message message;
    864   memset(&message, 0, sizeof(message));
    865 
    866   message.header.kind = GNUNET_MESSENGER_KIND_TICKET;
    867   message.body.ticket.identifier = identifier;
    868 
    869   GNUNET_MESSENGER_send_message(
    870     context->room,
    871     &message,
    872     attributes->contact->member
    873   );
    874 
    875   GNUNET_free(identifier);
    876 
    877 skip_sending:
    878   internal_attributes_destroy(attributes);
    879 }
    880 
    881 static struct GNUNET_RECLAIM_AttributeList*
    882 attribute_list_from_attribute (const struct GNUNET_RECLAIM_Attribute *attribute)
    883 {
    884   struct GNUNET_RECLAIM_AttributeList *attrs;
    885   struct GNUNET_RECLAIM_AttributeListEntry *le;
    886 
    887   attrs = GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
    888 
    889   if (!attrs)
    890     return NULL;
    891 
    892   le = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
    893 
    894   if (!le)
    895   {
    896     GNUNET_free (attrs);
    897     return NULL;
    898   }
    899 
    900   le->attribute = GNUNET_RECLAIM_attribute_new (
    901     attribute->name,
    902     &(attribute->credential),
    903     attribute->type,
    904     attribute->data,
    905     attribute->data_size
    906   );
    907 
    908   le->attribute->flag = attribute->flag;
    909   le->attribute->id = attribute->id;
    910 
    911   GNUNET_CONTAINER_DLL_insert (
    912     attrs->list_head,
    913     attrs->list_tail,
    914     le
    915   );
    916 
    917   return attrs;
    918 }
    919 
    920 void
    921 cb_share_attribute (void *cls,
    922                     const struct GNUNET_CRYPTO_BlindablePublicKey *identity,
    923                     const struct GNUNET_RECLAIM_Attribute *attribute)
    924 {
    925   GNUNET_assert(cls);
    926 
    927   struct GNUNET_CHAT_AttributeProcess *attributes = (
    928     (struct GNUNET_CHAT_AttributeProcess*) cls
    929   );
    930 
    931   if (! attributes->name)
    932   {
    933     internal_attributes_stop_iter(attributes);
    934     return;
    935   }
    936 
    937   struct GNUNET_CHAT_Handle *handle = attributes->handle;
    938 
    939   if (0 != strcmp(attribute->name, attributes->name))
    940   {
    941     internal_attributes_next_iter(attributes);
    942     return;
    943   }
    944 
    945   internal_attributes_stop_iter(attributes);
    946 
    947   GNUNET_free(attributes->name);
    948   attributes->name = NULL;
    949 
    950   const struct GNUNET_CRYPTO_BlindablePrivateKey *key = handle_get_key(
    951     handle
    952   );
    953 
    954   if (!key)
    955     return;
    956 
    957   const struct GNUNET_CRYPTO_BlindablePublicKey *pubkey = contact_get_key(
    958     attributes->contact
    959   );
    960 
    961   if (!pubkey)
    962     return;
    963 
    964   char *rp_uri = GNUNET_CRYPTO_blindable_public_key_to_string(pubkey);
    965 
    966   struct GNUNET_RECLAIM_AttributeList *attrs;
    967   attrs = attribute_list_from_attribute(attribute);
    968 
    969   if (!attrs)
    970     goto cleanup;
    971 
    972   attributes->op = GNUNET_RECLAIM_ticket_issue(
    973     handle->reclaim,
    974     key,
    975     rp_uri,
    976     attrs,
    977     cb_issue_ticket,
    978     attributes
    979   );
    980 
    981   GNUNET_RECLAIM_attribute_list_destroy(attrs);
    982 
    983 cleanup:
    984   GNUNET_free(rp_uri);
    985 }
    986 
    987 void
    988 cb_task_finish_iterate_ticket (void *cls)
    989 {
    990   GNUNET_assert(cls);
    991 
    992   struct GNUNET_CHAT_TicketProcess *tickets = (
    993     (struct GNUNET_CHAT_TicketProcess*) cls
    994   );
    995 
    996   tickets->iter = NULL;
    997 
    998   internal_tickets_destroy(tickets);
    999 }
   1000 
   1001 void
   1002 cb_task_error_iterate_ticket (void *cls)
   1003 {
   1004   GNUNET_assert(cls);
   1005 
   1006   struct GNUNET_CHAT_TicketProcess *tickets = (
   1007     (struct GNUNET_CHAT_TicketProcess*) cls
   1008   );
   1009 
   1010   handle_send_internal_message(
   1011     tickets->handle,
   1012     NULL,
   1013     NULL,
   1014     GNUNET_CHAT_FLAG_WARNING,
   1015     "Ticket iteration failed!",
   1016     GNUNET_YES
   1017   );
   1018 
   1019   cb_task_finish_iterate_ticket(cls);
   1020 }
   1021 
   1022 void
   1023 cont_revoke_ticket (void *cls,
   1024                     int32_t success,
   1025                     const char *emsg)
   1026 {
   1027   GNUNET_assert(cls);
   1028 
   1029   struct GNUNET_CHAT_TicketProcess *tickets = (
   1030     (struct GNUNET_CHAT_TicketProcess*) cls
   1031   );
   1032 
   1033   tickets->op = NULL;
   1034 
   1035   struct GNUNET_CHAT_Handle *handle = tickets->handle;
   1036 
   1037   if (success == GNUNET_SYSERR)
   1038     handle_send_internal_message(
   1039       handle,
   1040       NULL,
   1041       NULL,
   1042       GNUNET_CHAT_FLAG_WARNING,
   1043       emsg,
   1044       GNUNET_YES
   1045     );
   1046   else
   1047     handle_send_internal_message(
   1048       handle,
   1049       NULL,
   1050       NULL,
   1051       GNUNET_CHAT_FLAG_SHARE_ATTRIBUTES,
   1052       NULL,
   1053       GNUNET_NO
   1054     );
   1055 
   1056   internal_tickets_destroy(tickets);
   1057 }
   1058 
   1059 void
   1060 cb_consume_ticket_check (void *cls,
   1061                          const struct GNUNET_CRYPTO_BlindablePublicKey *identity,
   1062                          const struct GNUNET_RECLAIM_Attribute *attribute,
   1063                          const struct GNUNET_RECLAIM_Presentation *presentation)
   1064 {
   1065   GNUNET_assert(cls);
   1066 
   1067   struct GNUNET_CHAT_TicketProcess *tickets = (
   1068     (struct GNUNET_CHAT_TicketProcess*) cls
   1069   );
   1070 
   1071   if ((!identity) && (!attribute) && (!presentation))
   1072   {
   1073     tickets->op = NULL;
   1074 
   1075     struct GNUNET_CHAT_Handle *handle = tickets->handle;
   1076 
   1077     const struct GNUNET_CRYPTO_BlindablePrivateKey *key = handle_get_key(
   1078       handle
   1079     );
   1080 
   1081     if (tickets->name)
   1082     {
   1083       GNUNET_free(tickets->name);
   1084       tickets->name = NULL;
   1085     }
   1086     else if (key)
   1087       tickets->op = GNUNET_RECLAIM_ticket_revoke(
   1088         handle->reclaim,
   1089         key,
   1090         tickets->ticket,
   1091         cont_revoke_ticket,
   1092         tickets
   1093       );
   1094 
   1095     if (tickets->ticket)
   1096     {
   1097       GNUNET_free(tickets->ticket);
   1098       tickets->ticket = NULL;
   1099     }
   1100 
   1101     if (tickets->op)
   1102       return;
   1103 
   1104     internal_tickets_destroy(tickets);
   1105     return;
   1106   }
   1107 
   1108   if ((!attribute) || (! tickets->name) ||
   1109       (0 != strcmp(tickets->name, attribute->name)))
   1110     return;
   1111 
   1112   if (tickets->name)
   1113   {
   1114     GNUNET_free(tickets->name);
   1115     tickets->name = NULL;
   1116   }
   1117 }
   1118 
   1119 static enum GNUNET_GenericReturnValue
   1120 is_contact_ticket_audience (const struct GNUNET_CHAT_Contact *contact,
   1121                             const char *rp_uri)
   1122 {
   1123   GNUNET_assert((contact) && (rp_uri));
   1124 
   1125   const struct GNUNET_CRYPTO_BlindablePublicKey *pubkey;
   1126   pubkey = contact_get_key(contact);
   1127 
   1128   if (!pubkey)
   1129     return GNUNET_NO;
   1130 
   1131   struct GNUNET_CRYPTO_BlindablePublicKey audience;
   1132   enum GNUNET_GenericReturnValue parsing;
   1133 
   1134   parsing = GNUNET_CRYPTO_blindable_public_key_from_string(rp_uri, &audience);
   1135 
   1136   if ((GNUNET_OK != parsing) || (0 != GNUNET_memcmp(pubkey, &audience)))
   1137     return GNUNET_NO;
   1138 
   1139   return GNUNET_YES;
   1140 }
   1141 
   1142 void
   1143 cb_iterate_ticket_check (void *cls,
   1144                          const struct GNUNET_RECLAIM_Ticket *ticket,
   1145                          const char *rp_uri)
   1146 {
   1147   GNUNET_assert(cls);
   1148 
   1149   struct GNUNET_CHAT_TicketProcess *tickets = (
   1150     (struct GNUNET_CHAT_TicketProcess*) cls
   1151   );
   1152 
   1153   struct GNUNET_CHAT_Handle *handle = tickets->handle;
   1154 
   1155   if ((!rp_uri) || (!(tickets->contact)) ||
   1156       (GNUNET_YES != is_contact_ticket_audience(tickets->contact, rp_uri)))
   1157   {
   1158     internal_tickets_next_iter(tickets);
   1159     return;
   1160   }
   1161 
   1162   const struct GNUNET_CRYPTO_BlindablePrivateKey *key = handle_get_key(
   1163     handle
   1164   );
   1165 
   1166   if (!key)
   1167   {
   1168     internal_tickets_stop_iter(tickets);
   1169     return;
   1170   }
   1171 
   1172   struct GNUNET_CHAT_TicketProcess *new_tickets;
   1173   new_tickets = internal_tickets_copy(tickets, ticket);
   1174 
   1175   if (!new_tickets)
   1176   {
   1177     internal_tickets_stop_iter(tickets);
   1178     return;
   1179   }
   1180 
   1181   new_tickets->op = GNUNET_RECLAIM_ticket_consume(
   1182     handle->reclaim,
   1183     ticket,
   1184     rp_uri,
   1185     cb_consume_ticket_check,
   1186     new_tickets
   1187   );
   1188 
   1189   internal_tickets_next_iter(tickets);
   1190 }
   1191 
   1192 void
   1193 cb_consume_ticket (void *cls,
   1194                    const struct GNUNET_CRYPTO_BlindablePublicKey *identity,
   1195                    const struct GNUNET_RECLAIM_Attribute *attribute,
   1196                    const struct GNUNET_RECLAIM_Presentation *presentation)
   1197 {
   1198   GNUNET_assert(cls);
   1199 
   1200   struct GNUNET_CHAT_TicketProcess *tickets = (
   1201     (struct GNUNET_CHAT_TicketProcess*) cls
   1202   );
   1203 
   1204   if ((!identity) && (!attribute) && (!presentation))
   1205   {
   1206     tickets->op = NULL;
   1207 
   1208     internal_tickets_destroy(tickets);
   1209     return;
   1210   }
   1211 
   1212   if (!attribute)
   1213     return;
   1214 
   1215   char *value = GNUNET_RECLAIM_attribute_value_to_string(
   1216     attribute->type,
   1217     attribute->data,
   1218     attribute->data_size
   1219   );
   1220 
   1221   if (tickets->callback)
   1222     tickets->callback(tickets->closure, tickets->contact, attribute->name, value);
   1223 
   1224   if (value)
   1225     GNUNET_free (value);
   1226 }
   1227 
   1228 void
   1229 cb_iterate_ticket (void *cls,
   1230                    const struct GNUNET_RECLAIM_Ticket *ticket,
   1231                    const char *rp_uri)
   1232 {
   1233   GNUNET_assert(cls);
   1234 
   1235   struct GNUNET_CHAT_TicketProcess *tickets = (
   1236     (struct GNUNET_CHAT_TicketProcess*) cls
   1237   );
   1238 
   1239   struct GNUNET_CHAT_Handle *handle = tickets->handle;
   1240 
   1241   if ((!rp_uri) || (!(tickets->contact)) ||
   1242       (GNUNET_YES != is_contact_ticket_audience(tickets->contact, rp_uri)))
   1243   {
   1244     internal_tickets_next_iter(tickets);
   1245     return;
   1246   }
   1247 
   1248   const struct GNUNET_CRYPTO_BlindablePrivateKey *key = handle_get_key(
   1249     handle
   1250   );
   1251 
   1252   if (!key)
   1253   {
   1254     internal_tickets_stop_iter(tickets);
   1255     return;
   1256   }
   1257 
   1258   struct GNUNET_CHAT_TicketProcess *new_tickets;
   1259   new_tickets = internal_tickets_copy(tickets, NULL);
   1260 
   1261   if (!new_tickets)
   1262   {
   1263     internal_tickets_stop_iter(tickets);
   1264     return;
   1265   }
   1266 
   1267   new_tickets->op = GNUNET_RECLAIM_ticket_consume(
   1268     handle->reclaim,
   1269     ticket,
   1270     rp_uri,
   1271     cb_consume_ticket,
   1272     new_tickets
   1273   );
   1274 
   1275   internal_tickets_next_iter(tickets);
   1276 }