libgnunetchat

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

gnunet_chat_lib_intern.c (28565B)


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