libgnunetchat

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

gnunet_chat_lib_intern.c (28588B)


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