libgnunetchat

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

gnunet_chat_lib_intern.c (28119B)


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