Merge branch 'master' into schanzen/reclaim_256bit
[oweals/gnunet.git] / src / reclaim / gnunet-service-reclaim_tickets.c
1 /*
2    This file is part of GNUnet.
3    Copyright (C) 2012-2015 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 /**
22  * @author Martin Schanzenbach
23  * @file src/reclaim/gnunet-service-reclaim_tickets.c
24  * @brief reclaim tickets
25  *
26  */
27 #include <inttypes.h>
28 #include "gnunet-service-reclaim_tickets.h"
29
30
31 /**
32  * FIXME: the defaul ticket iteration interval should probably
33  * be the minimim attribute expiration.
34  */
35 #define DEFAULT_TICKET_REFRESH_INTERVAL GNUNET_TIME_UNIT_HOURS
36
37 /**
38  * Handle for a parallel GNS lookup job
39  * (Declaration further below)
40  */
41 struct ParallelLookup;
42
43
44 /**
45  * A reference to a ticket stored in GNS
46  */
47 struct TicketReference
48 {
49   /**
50    * DLL
51    */
52   struct TicketReference *next;
53
54   /**
55    * DLL
56    */
57   struct TicketReference *prev;
58
59   /**
60    * Attributes
61    */
62   struct GNUNET_RECLAIM_AttributeList *attrs;
63
64   /**
65    * Tickets
66    */
67   struct GNUNET_RECLAIM_Ticket ticket;
68 };
69
70
71 /**
72  * Handle to a consume operation
73  */
74 struct RECLAIM_TICKETS_ConsumeHandle
75 {
76   /**
77    * Ticket
78    */
79   struct GNUNET_RECLAIM_Ticket ticket;
80
81   /**
82    * LookupRequest
83    */
84   struct GNUNET_GNS_LookupRequest *lookup_request;
85
86   /**
87    * Audience Key
88    */
89   struct GNUNET_CRYPTO_EcdsaPrivateKey identity;
90
91   /**
92    * Audience Key
93    */
94   struct GNUNET_CRYPTO_EcdsaPublicKey identity_pub;
95
96   /**
97    * Lookup DLL
98    */
99   struct ParallelLookup *parallel_lookups_head;
100
101   /**
102    * Lookup DLL
103    */
104   struct ParallelLookup *parallel_lookups_tail;
105
106   /**
107    * Kill task
108    */
109   struct GNUNET_SCHEDULER_Task *kill_task;
110
111   /**
112    * Attributes
113    */
114   struct GNUNET_RECLAIM_AttributeList *attrs;
115
116   /**
117    * Attestations
118    */
119   struct GNUNET_RECLAIM_AttestationList *attests;
120
121   /**
122    * Lookup time
123    */
124   struct GNUNET_TIME_Absolute lookup_start_time;
125
126   /**
127    * Callback
128    */
129   RECLAIM_TICKETS_ConsumeCallback cb;
130
131   /**
132    * Callbacl closure
133    */
134   void *cb_cls;
135 };
136
137
138 /**
139  * Handle for a parallel GNS lookup job
140  */
141 struct ParallelLookup
142 {
143   /* DLL */
144   struct ParallelLookup *next;
145
146   /* DLL */
147   struct ParallelLookup *prev;
148
149   /* The GNS request */
150   struct GNUNET_GNS_LookupRequest *lookup_request;
151
152   /* The handle the return to */
153   struct RECLAIM_TICKETS_ConsumeHandle *handle;
154
155   /**
156    * Lookup time
157    */
158   struct GNUNET_TIME_Absolute lookup_start_time;
159
160   /* The label to look up */
161   char *label;
162 };
163
164
165 /**
166  * Ticket issue request handle
167  */
168 struct TicketIssueHandle
169 {
170   /**
171    * Attributes to issue
172    */
173   struct GNUNET_RECLAIM_AttributeList *attrs;
174
175   /**
176    * Issuer Key
177    */
178   struct GNUNET_CRYPTO_EcdsaPrivateKey identity;
179
180   /**
181    * Ticket to issue
182    */
183   struct GNUNET_RECLAIM_Ticket ticket;
184
185   /**
186    * QueueEntry
187    */
188   struct GNUNET_NAMESTORE_QueueEntry *ns_qe;
189
190   /**
191    * Namestore Iterator
192    */
193   struct GNUNET_NAMESTORE_ZoneIterator *ns_it;
194
195   /**
196    * Callback
197    */
198   RECLAIM_TICKETS_TicketResult cb;
199
200   /**
201    * Callback cls
202    */
203   void *cb_cls;
204 };
205
206
207 /**
208  * Ticket iterator
209  */
210 struct RECLAIM_TICKETS_Iterator
211 {
212   /**
213    * Namestore queue entry
214    */
215   struct GNUNET_NAMESTORE_ZoneIterator *ns_it;
216
217   /**
218    * Iter callback
219    */
220   RECLAIM_TICKETS_TicketIter cb;
221
222   /**
223    * Iter cls
224    */
225   void *cb_cls;
226 };
227
228
229 struct RevokedAttributeEntry
230 {
231   /**
232    * DLL
233    */
234   struct RevokedAttributeEntry *next;
235
236   /**
237    * DLL
238    */
239   struct RevokedAttributeEntry *prev;
240
241   /**
242    * Old ID of the attribute
243    */
244   struct GNUNET_RECLAIM_Identifier old_id;
245
246   /**
247    * New ID of the attribute
248    */
249   struct GNUNET_RECLAIM_Identifier new_id;
250 };
251
252
253 /**
254  * Ticket revocation request handle
255  */
256 struct RECLAIM_TICKETS_RevokeHandle
257 {
258   /**
259    * Issuer Key
260    */
261   struct GNUNET_CRYPTO_EcdsaPrivateKey identity;
262
263   /**
264    * Callback
265    */
266   RECLAIM_TICKETS_RevokeCallback cb;
267
268   /**
269    * Callback cls
270    */
271   void *cb_cls;
272
273   /**
274    * Ticket to issue
275    */
276   struct GNUNET_RECLAIM_Ticket ticket;
277
278   /**
279    * QueueEntry
280    */
281   struct GNUNET_NAMESTORE_QueueEntry *ns_qe;
282
283   /**
284    * Namestore iterator
285    */
286   struct GNUNET_NAMESTORE_ZoneIterator *ns_it;
287
288   /**
289    * Revoked attributes
290    */
291   struct RevokedAttributeEntry *attrs_head;
292
293   /**
294    * Revoked attributes
295    */
296   struct RevokedAttributeEntry *attrs_tail;
297
298   /**
299    * Current attribute to move
300    */
301   struct RevokedAttributeEntry *move_attr;
302
303   /**
304    * Number of attributes in ticket
305    */
306   unsigned int ticket_attrs;
307
308   /**
309    * Tickets to update
310    */
311   struct TicketRecordsEntry *tickets_to_update_head;
312
313   /**
314    * Tickets to update
315    */
316   struct TicketRecordsEntry *tickets_to_update_tail;
317 };
318
319
320 /**
321  * Ticket expiration interval
322  */
323 static struct GNUNET_TIME_Relative ticket_refresh_interval;
324
325
326 /* Namestore handle */
327 static struct GNUNET_NAMESTORE_Handle *nsh;
328
329
330 /* GNS handle */
331 static struct GNUNET_GNS_Handle *gns;
332
333
334 /* Handle to the statistics service */
335 static struct GNUNET_STATISTICS_Handle *stats;
336
337
338 /**
339  * Cleanup revoke handle
340  *
341  * @param rh the ticket revocation handle
342  */
343 static void
344 cleanup_rvk (struct RECLAIM_TICKETS_RevokeHandle *rh)
345 {
346   struct RevokedAttributeEntry *ae;
347   struct TicketRecordsEntry *le;
348
349   if (NULL != rh->ns_qe)
350     GNUNET_NAMESTORE_cancel (rh->ns_qe);
351   if (NULL != rh->ns_it)
352     GNUNET_NAMESTORE_zone_iteration_stop (rh->ns_it);
353   while (NULL != (ae = rh->attrs_head))
354   {
355     GNUNET_CONTAINER_DLL_remove (rh->attrs_head, rh->attrs_tail, ae);
356     GNUNET_free (ae);
357   }
358   while (NULL != (le = rh->tickets_to_update_head))
359   {
360     GNUNET_CONTAINER_DLL_remove (rh->tickets_to_update_head,
361                                  rh->tickets_to_update_head,
362                                  le);
363     if (NULL != le->data)
364       GNUNET_free (le->data);
365     if (NULL != le->label)
366       GNUNET_free (le->label);
367     GNUNET_free (le);
368   }
369   GNUNET_free (rh);
370 }
371
372
373 /**
374  * For each ticket, store new, updated attribute references
375  * (Implementation further below)
376  *
377  * @param cls handle to the operation
378  */
379 static void
380 process_tickets (void *cls);
381
382
383 /**
384  * Finished storing updated attribute references.
385  * Abort on error, else continue processing tickets
386  *
387  * @param cls handle to the operation
388  * @param success result of namestore operation
389  * @param emsg (NULL on success)
390  */
391 static void
392 ticket_processed (void *cls, int32_t success, const char *emsg)
393 {
394   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
395
396   rvk->ns_qe = NULL;
397   GNUNET_SCHEDULER_add_now (&process_tickets, rvk);
398 }
399
400
401 /**
402  * For each ticket, store new, updated attribute references
403  *
404  * @param cls handle to the operation
405  */
406 static void
407 process_tickets (void *cls)
408 {
409   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
410   struct TicketRecordsEntry *le;
411   struct RevokedAttributeEntry *ae;
412
413   if (NULL == rvk->tickets_to_update_head)
414   {
415     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
416                 "Finished updatding tickets, success\n");
417     rvk->cb (rvk->cb_cls, GNUNET_OK);
418     cleanup_rvk (rvk);
419     return;
420   }
421   le = rvk->tickets_to_update_head;
422   GNUNET_CONTAINER_DLL_remove (rvk->tickets_to_update_head,
423                                rvk->tickets_to_update_tail,
424                                le);
425   struct GNUNET_GNSRECORD_Data rd[le->rd_count];
426   if (GNUNET_OK != GNUNET_GNSRECORD_records_deserialize (le->data_size,
427                                                          le->data,
428                                                          le->rd_count,
429                                                          rd))
430   {
431     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
432                 "Unable to deserialize ticket record(s)\n");
433     rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
434     cleanup_rvk (rvk);
435     return;
436   }
437   for (int i = 0; i < le->rd_count; i++)
438   {
439     if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTRIBUTE_REF != rd[i].record_type)
440       continue;
441     for (ae = rvk->attrs_head; NULL != ae; ae = ae->next)
442     {
443       if (0 != memcmp (rd[i].data, &ae->old_id, sizeof(ae->old_id)))
444         continue;
445       rd[i].data = &ae->new_id;
446     }
447   }
448   rvk->ns_qe = GNUNET_NAMESTORE_records_store (nsh,
449                                                &rvk->identity,
450                                                le->label,
451                                                le->rd_count,
452                                                rd,
453                                                &ticket_processed,
454                                                rvk);
455   GNUNET_free (le->label);
456   GNUNET_free (le->data);
457   GNUNET_free (le);
458 }
459
460
461 /**
462  * Done collecting tickets. Start processing.
463  *
464  * @param cls handle to the operation
465  */
466 static void
467 rvk_ticket_update_finished (void *cls)
468 {
469   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
470
471   rvk->ns_it = NULL;
472   GNUNET_SCHEDULER_add_now (&process_tickets, rvk);
473 }
474
475
476 /**
477  * We need to update all other tickets with the new attribute IDs.
478  * We first collect them all. Processing after.
479  *
480  * @param cls handle to the operation
481  * @param zone ticket issuer private key
482  * @param label ticket rnd
483  * @param rd_cound size of record set
484  * @param rd record set
485  */
486 static void
487 rvk_ticket_update (void *cls,
488                    const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
489                    const char *label,
490                    unsigned int rd_count,
491                    const struct GNUNET_GNSRECORD_Data *rd)
492 {
493   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
494   struct TicketRecordsEntry *le;
495   struct RevokedAttributeEntry *ae;
496   int has_changed = GNUNET_NO;
497
498   /** Let everything point to the old record **/
499   for (int i = 0; i < rd_count; i++)
500   {
501     if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTRIBUTE_REF != rd[i].record_type)
502       continue;
503     for (ae = rvk->attrs_head; NULL != ae; ae = ae->next)
504     {
505       if (0 != memcmp (rd[i].data, &ae->old_id, sizeof(ae->old_id)))
506         continue;
507       has_changed = GNUNET_YES;
508       break;
509     }
510     if (GNUNET_YES == has_changed)
511       break;
512   }
513   if (GNUNET_YES == has_changed)
514   {
515     le = GNUNET_new (struct TicketRecordsEntry);
516     le->data_size = GNUNET_GNSRECORD_records_get_size (rd_count, rd);
517     le->data = GNUNET_malloc (le->data_size);
518     le->rd_count = rd_count;
519     le->label = GNUNET_strdup (label);
520     GNUNET_GNSRECORD_records_serialize (rd_count, rd, le->data_size, le->data);
521     GNUNET_CONTAINER_DLL_insert (rvk->tickets_to_update_head,
522                                  rvk->tickets_to_update_tail,
523                                  le);
524   }
525   GNUNET_NAMESTORE_zone_iterator_next (rvk->ns_it, 1);
526 }
527
528
529 /**
530  * Error iterating namestore. Abort.
531  *
532  * @param cls handle to the operation
533  */
534 static void
535 rvk_ns_iter_err (void *cls)
536 {
537   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
538
539   rvk->ns_it = NULL;
540   rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
541   cleanup_rvk (rvk);
542 }
543
544
545 /**
546  * Error storing new attribute in namestore. Abort
547  *
548  * @param cls handle to the operation
549  */
550 static void
551 rvk_ns_err (void *cls)
552 {
553   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
554
555   rvk->ns_qe = NULL;
556   rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
557   cleanup_rvk (rvk);
558 }
559
560
561 /**
562  * We change every attribute ID of the ticket attributes we
563  * want to revoke.
564  * When we are done, we need to update any other ticket which
565  * included references to any of the changed attributes.
566  * (Implementation further below)
567  *
568  * @param rvk handle to the operation
569  */
570 static void
571 move_attrs (struct RECLAIM_TICKETS_RevokeHandle *rh);
572
573
574 /**
575  * Delayed continuation for move_attrs
576  *
577  * @param cls handle to the operation.
578  */
579 static void
580 move_attrs_cont (void *cls)
581 {
582   move_attrs ((struct RECLAIM_TICKETS_RevokeHandle *) cls);
583 }
584
585
586 /**
587  * Done deleting the old record. Abort on error.
588  * Else, continue updating attribute IDs.
589  *
590  * @param cls handle to the operation
591  * @param success result of the namestore operation
592  * @param emsg error message (NULL on success)
593  */
594 static void
595 del_attr_finished (void *cls, int32_t success, const char *emsg)
596 {
597   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
598
599   rvk->ns_qe = NULL;
600   if (GNUNET_SYSERR == success)
601   {
602     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
603                 "Error removing attribute: %s\n",
604                 emsg);
605     rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
606     cleanup_rvk (rvk);
607     return;
608   }
609   rvk->move_attr = rvk->move_attr->next;
610   GNUNET_SCHEDULER_add_now (&move_attrs_cont, rvk);
611 }
612
613
614 /**
615  * Updated an attribute ID.
616  * Abort on error if namestore operation failed.
617  * Else, we have to delete the old record.
618  *
619  * @param cls handle to the operation
620  * @param success result of the store operation
621  * @param emsg error message (NULL on success)
622  */
623 static void
624 move_attr_finished (void *cls, int32_t success, const char *emsg)
625 {
626   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
627   char *label;
628
629   rvk->ns_qe = NULL;
630   if (GNUNET_SYSERR == success)
631   {
632     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error moving attribute: %s\n", emsg);
633     rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
634     cleanup_rvk (rvk);
635     return;
636   }
637   label = GNUNET_STRINGS_data_to_string_alloc (&rvk->move_attr->old_id,
638                                                sizeof(rvk->move_attr->old_id));
639   GNUNET_assert (NULL != label);
640   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Removing attribute %s\n", label);
641   rvk->ns_qe = GNUNET_NAMESTORE_records_store (nsh,
642                                                &rvk->identity,
643                                                label,
644                                                0,
645                                                NULL,
646                                                &del_attr_finished,
647                                                rvk);
648   GNUNET_free (label);
649 }
650
651
652 /**
653  * Got the referenced attribute. Updating the ID
654  *
655  * @param cls handle to the operation
656  * @param zone issuer identity
657  * @param label attribute ID
658  * @param rd_count size of record set (should be 1)
659  * @param rd record set (the attribute)
660  */
661 static void
662 rvk_move_attr_cb (void *cls,
663                   const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
664                   const char *label,
665                   unsigned int rd_count,
666                   const struct GNUNET_GNSRECORD_Data *rd)
667 {
668   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
669   struct GNUNET_GNSRECORD_Data new_rd[rd_count];
670   struct RevokedAttributeEntry *le;
671   char *new_label;
672   char *attr_data;
673
674   rvk->ns_qe = NULL;
675   if (0 == rd_count)
676   {
677     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
678                 "The claim %s no longer exists!\n",
679                 label);
680     le = rvk->move_attr;
681     rvk->move_attr = le->next;
682     GNUNET_CONTAINER_DLL_remove (rvk->attrs_head, rvk->attrs_tail, le);
683     GNUNET_free (le);
684     GNUNET_SCHEDULER_add_now (&move_attrs_cont, rvk);
685     return;
686   }
687   GNUNET_RECLAIM_id_generate (&rvk->move_attr->new_id);
688   new_label = NULL;
689   attr_data = NULL;
690   // new_rd = *rd;
691   for (int i = 0; i < rd_count; i++)
692   {
693     if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTRIBUTE == rd[i].record_type)
694     {
695       /** find a new place for this attribute **/
696       struct GNUNET_RECLAIM_Attribute *claim;
697       claim = GNUNET_RECLAIM_attribute_deserialize (rd[i].data,
698                                                     rd[i].data_size);
699       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
700                   "Attribute to update: Name=%s\n",
701                   claim->name);
702       claim->id = rvk->move_attr->new_id;
703       new_rd[i].data_size = GNUNET_RECLAIM_attribute_serialize_get_size (claim);
704       attr_data = GNUNET_malloc (rd[i].data_size);
705       new_rd[i].data_size = GNUNET_RECLAIM_attribute_serialize (claim,
706                                                                 attr_data);
707       new_rd[i].data = attr_data;
708       new_rd[i].record_type = rd[i].record_type;
709       new_rd[i].flags = rd[i].flags;
710       new_rd[i].expiration_time = rd[i].expiration_time;
711       new_label =
712         GNUNET_STRINGS_data_to_string_alloc (&rvk->move_attr->new_id,
713                                              sizeof (rvk->move_attr->new_id));
714       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute %s\n", new_label);
715       GNUNET_free (claim);
716     }
717     else if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTESTATION == rd[i].record_type)
718     {
719       struct GNUNET_RECLAIM_Attestation *attest;
720       attest = GNUNET_RECLAIM_attestation_deserialize (rd[i].data,
721                                                        rd[i].data_size);
722       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
723                   "Attestation to update: Name=%s\n",
724                   attest->name);
725       attest->id = rvk->move_attr->new_id;
726       new_rd[i].data_size =
727         GNUNET_RECLAIM_attestation_serialize_get_size (attest);
728       attr_data = GNUNET_malloc (rd[i].data_size);
729       new_rd[i].data_size = GNUNET_RECLAIM_attestation_serialize (attest,
730                                                                   attr_data);
731       new_rd[i].data = attr_data;
732       new_rd[i].record_type = rd[i].record_type;
733       new_rd[i].flags = rd[i].flags;
734       new_rd[i].expiration_time = rd[i].expiration_time;
735       new_label =
736         GNUNET_STRINGS_data_to_string_alloc (&rvk->move_attr->new_id,
737                                              sizeof (rvk->move_attr->new_id));
738       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attestation %s\n",
739                   new_label);
740       GNUNET_free (attest);
741     }
742   }
743   rvk->ns_qe = GNUNET_NAMESTORE_records_store (nsh,
744                                                &rvk->identity,
745                                                new_label,
746                                                rd_count,
747                                                new_rd,
748                                                &move_attr_finished,
749                                                rvk);
750   GNUNET_free (new_label);
751   GNUNET_free (attr_data);
752 }
753
754
755 /**
756  * We change every attribute ID of the ticket attributes we
757  * want to revoke.
758  * When we are done, we need to update any other ticket which
759  * included references to any of the changed attributes.
760  *
761  * @param rvk handle to the operation
762  */
763 static void
764 move_attrs (struct RECLAIM_TICKETS_RevokeHandle *rvk)
765 {
766   char *label;
767
768   if (NULL == rvk->move_attr)
769   {
770     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Finished moving attributes\n");
771     rvk->ns_it =
772       GNUNET_NAMESTORE_zone_iteration_start (nsh,
773                                              &rvk->identity,
774                                              &rvk_ns_iter_err,
775                                              rvk,
776                                              &rvk_ticket_update,
777                                              rvk,
778                                              &rvk_ticket_update_finished,
779                                              rvk);
780     return;
781   }
782   label = GNUNET_STRINGS_data_to_string_alloc (&rvk->move_attr->old_id,
783                                                sizeof (rvk->move_attr->old_id));
784   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Moving claim %s\n", label);
785
786   rvk->ns_qe = GNUNET_NAMESTORE_records_lookup (nsh,
787                                                 &rvk->identity,
788                                                 label,
789                                                 &rvk_ns_err,
790                                                 rvk,
791                                                 &rvk_move_attr_cb,
792                                                 rvk);
793   GNUNET_free (label);
794 }
795
796
797 /**
798  * Finished deleting ticket and attribute references.
799  * Abort on failure.
800  * Else, we start changing every attribute ID in the
801  * found attribute references so that access is no longer
802  * possible.
803  *
804  * @param cls handle to the operation
805  * @param success Namestore operation return value
806  * @param emsg error message (NULL on success)
807  */
808 static void
809 remove_ticket_cont (void *cls, int32_t success, const char *emsg)
810 {
811   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
812
813   rvk->ns_qe = NULL;
814   if (GNUNET_SYSERR == success)
815   {
816     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n", emsg);
817     rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
818     cleanup_rvk (rvk);
819     return;
820   }
821   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deleted ticket\n");
822   if (0 == rvk->ticket_attrs)
823   {
824     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
825                 "No attributes to move... strange\n");
826     rvk->cb (rvk->cb_cls, GNUNET_OK);
827     cleanup_rvk (rvk);
828     return;
829   }
830   rvk->move_attr = rvk->attrs_head;
831   move_attrs (rvk);
832 }
833
834
835 /**
836  * We found the attribute references.
837  * Store them for later and remove the record set.
838  *
839  * @param cls handle to the operation
840  * @param zone the issuer key
841  * @param label ticket rnd
842  * @param rd_cound size of record set
843  * @param rd record set
844  */
845 static void
846 revoke_attrs_cb (void *cls,
847                  const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
848                  const char *label,
849                  unsigned int rd_count,
850                  const struct GNUNET_GNSRECORD_Data *rd)
851
852 {
853   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
854   struct RevokedAttributeEntry *le;
855
856   rvk->ns_qe = NULL;
857   /**
858    * Temporarily store attribute references.
859    * We need it later.
860    */
861   for (int i = 0; i < rd_count; i++)
862   {
863     if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTRIBUTE_REF != rd[i].record_type)
864       continue;
865     le = GNUNET_new (struct RevokedAttributeEntry);
866     le->old_id = *((struct GNUNET_RECLAIM_Identifier *) rd[i].data);
867     GNUNET_CONTAINER_DLL_insert (rvk->attrs_head, rvk->attrs_tail, le);
868     rvk->ticket_attrs++;
869   }
870
871   /** Remove attribute references **/
872   rvk->ns_qe = GNUNET_NAMESTORE_records_store (nsh,
873                                                &rvk->identity,
874                                                label,
875                                                0,
876                                                NULL,
877                                                &remove_ticket_cont,
878                                                rvk);
879 }
880
881
882 /**
883  * Failed to query namestore. Abort operation
884  *
885  * @param cls handle to the operation
886  */
887 static void
888 rvk_attrs_err_cb (void *cls)
889 {
890   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
891
892   rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
893   cleanup_rvk (rvk);
894 }
895
896
897 /**
898  * Revoke a ticket.
899  * We start by looking up attribute references in order
900  * to change attribute IDs.
901  *
902  * @param ticket ticket to revoke
903  * @param identity private key of issuer
904  * @param cb revocation status callback
905  * @param cb_cls callback closure
906  * @return handle to the operation
907  */
908 struct RECLAIM_TICKETS_RevokeHandle *
909 RECLAIM_TICKETS_revoke (const struct GNUNET_RECLAIM_Ticket *ticket,
910                         const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity,
911                         RECLAIM_TICKETS_RevokeCallback cb,
912                         void *cb_cls)
913 {
914   struct RECLAIM_TICKETS_RevokeHandle *rvk;
915   char *label;
916
917   rvk = GNUNET_new (struct RECLAIM_TICKETS_RevokeHandle);
918   rvk->cb = cb;
919   rvk->cb_cls = cb_cls;
920   rvk->identity = *identity;
921   rvk->ticket = *ticket;
922   GNUNET_CRYPTO_ecdsa_key_get_public (&rvk->identity, &rvk->ticket.identity);
923   /** Get shared attributes **/
924   label = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd,
925                                                sizeof(ticket->rnd));
926   GNUNET_assert (NULL != label);
927   rvk->ns_qe = GNUNET_NAMESTORE_records_lookup (nsh,
928                                                 identity,
929                                                 label,
930                                                 &rvk_attrs_err_cb,
931                                                 rvk,
932                                                 &revoke_attrs_cb,
933                                                 rvk);
934   GNUNET_free (label);
935   return rvk;
936 }
937
938
939 /**
940  * Cancel a revocation.
941  *
942  * @param rh handle to the operation
943  */
944 void
945 RECLAIM_TICKETS_revoke_cancel (struct RECLAIM_TICKETS_RevokeHandle *rh)
946 {
947   GNUNET_assert (NULL != rh);
948   cleanup_rvk (rh);
949 }
950
951
952 /*******************************
953 * Ticket consume
954 *******************************/
955
956 /**
957  * Cleanup ticket consume handle
958  *
959  * @param cth the handle to clean up
960  */
961 static void
962 cleanup_cth (struct RECLAIM_TICKETS_ConsumeHandle *cth)
963 {
964   struct ParallelLookup *lu;
965
966   if (NULL != cth->lookup_request)
967     GNUNET_GNS_lookup_cancel (cth->lookup_request);
968   if (NULL != cth->kill_task)
969     GNUNET_SCHEDULER_cancel (cth->kill_task);
970   while (NULL != (lu = cth->parallel_lookups_head))
971   {
972     if (NULL != lu->lookup_request)
973       GNUNET_GNS_lookup_cancel (lu->lookup_request);
974     GNUNET_free_non_null (lu->label);
975     GNUNET_CONTAINER_DLL_remove (cth->parallel_lookups_head,
976                                  cth->parallel_lookups_tail,
977                                  lu);
978     GNUNET_free (lu);
979   }
980
981   if (NULL != cth->attrs)
982     GNUNET_RECLAIM_attribute_list_destroy (cth->attrs);
983   if (NULL != cth->attests)
984     GNUNET_RECLAIM_attestation_list_destroy (cth->attests);
985   GNUNET_free (cth);
986 }
987
988
989 /**
990  * We found an attribute record.
991  *
992  * @param cls handle to the operation
993  * @param rd_cound size of record set
994  * @param rd record set
995  */
996 static void
997 process_parallel_lookup_result (void *cls,
998                                 uint32_t rd_count,
999                                 const struct GNUNET_GNSRECORD_Data *rd)
1000 {
1001   struct ParallelLookup *parallel_lookup = cls;
1002   struct RECLAIM_TICKETS_ConsumeHandle *cth = parallel_lookup->handle;
1003   struct GNUNET_RECLAIM_AttributeListEntry *attr_le;
1004
1005   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1006               "Parallel lookup finished (count=%u)\n",
1007               rd_count);
1008
1009   GNUNET_CONTAINER_DLL_remove (cth->parallel_lookups_head,
1010                                cth->parallel_lookups_tail,
1011                                parallel_lookup);
1012   GNUNET_free (parallel_lookup->label);
1013
1014   GNUNET_STATISTICS_update (stats,
1015                             "attribute_lookup_time_total",
1016                             GNUNET_TIME_absolute_get_duration (
1017                               parallel_lookup->lookup_start_time)
1018                             .rel_value_us,
1019                             GNUNET_YES);
1020   GNUNET_STATISTICS_update (stats, "attribute_lookups_count", 1, GNUNET_YES);
1021
1022
1023   GNUNET_free (parallel_lookup);
1024   if (0 == rd_count)
1025     GNUNET_break (0);
1026   // REMARK: It is possible now to find rd_count > 1
1027   for (int i = 0; i < rd_count; i++)
1028   {
1029     if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTRIBUTE == rd[i].record_type)
1030     {
1031       attr_le = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
1032       attr_le->attribute =
1033         GNUNET_RECLAIM_attribute_deserialize (rd[i].data, rd[i].data_size);
1034       GNUNET_CONTAINER_DLL_insert (cth->attrs->list_head,
1035                                    cth->attrs->list_tail,
1036                                    attr_le);
1037     }
1038     else if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTESTATION == rd[i].record_type)
1039     {
1040       struct GNUNET_RECLAIM_AttestationListEntry *ale;
1041       ale = GNUNET_new (struct GNUNET_RECLAIM_AttestationListEntry);
1042       ale->attestation =
1043         GNUNET_RECLAIM_attestation_deserialize (rd[i].data,
1044                                                 rd[i].data_size);
1045       GNUNET_CONTAINER_DLL_insert (cth->attests->list_head,
1046                                    cth->attests->list_tail,
1047                                    ale);
1048     }
1049     else
1050     {
1051       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1052                   "Parallel Lookup of Reference without Attestation");
1053       continue;
1054     }
1055
1056
1057   }
1058   if (NULL != cth->parallel_lookups_head)
1059     return; // Wait for more
1060   /* Else we are done */
1061   cth->cb (cth->cb_cls, &cth->ticket.identity,
1062            cth->attrs, cth->attests, GNUNET_OK, NULL);
1063   cleanup_cth (cth);
1064 }
1065
1066
1067 /**
1068  * Cancel the lookups for attribute records
1069  *
1070  * @param cls handle to the operation
1071  */
1072 static void
1073 abort_parallel_lookups (void *cls)
1074 {
1075   struct RECLAIM_TICKETS_ConsumeHandle *cth = cls;
1076   struct ParallelLookup *lu;
1077   struct ParallelLookup *tmp;
1078
1079   cth->kill_task = NULL;
1080   for (lu = cth->parallel_lookups_head; NULL != lu;)
1081   {
1082     GNUNET_GNS_lookup_cancel (lu->lookup_request);
1083     GNUNET_free (lu->label);
1084     tmp = lu->next;
1085     GNUNET_CONTAINER_DLL_remove (cth->parallel_lookups_head,
1086                                  cth->parallel_lookups_tail,
1087                                  lu);
1088     GNUNET_free (lu);
1089     lu = tmp;
1090   }
1091   cth->cb (cth->cb_cls, NULL, NULL, NULL, GNUNET_SYSERR, "Aborted");
1092 }
1093
1094
1095 /**
1096  * GNS result with attribute references.
1097  * For each result, we start a (parallel) lookup of the actual
1098  * attribute record under the referenced label.
1099  *
1100  * @param cls handle to the operation
1101  * @param rd_cound size of the record set
1102  * @param rd record set
1103  */
1104 static void
1105 lookup_authz_cb (void *cls,
1106                  uint32_t rd_count,
1107                  const struct GNUNET_GNSRECORD_Data *rd)
1108 {
1109   struct RECLAIM_TICKETS_ConsumeHandle *cth = cls;
1110   struct ParallelLookup *parallel_lookup;
1111   char *lbl;
1112
1113   cth->lookup_request = NULL;
1114
1115   GNUNET_STATISTICS_update (stats,
1116                             "reclaim_authz_lookup_time_total",
1117                             GNUNET_TIME_absolute_get_duration (
1118                               cth->lookup_start_time)
1119                             .rel_value_us,
1120                             GNUNET_YES);
1121   GNUNET_STATISTICS_update (stats,
1122                             "reclaim_authz_lookups_count",
1123                             1,
1124                             GNUNET_YES);
1125
1126   for (int i = 0; i < rd_count; i++)
1127   {
1128     if ((GNUNET_GNSRECORD_TYPE_RECLAIM_ATTRIBUTE_REF != rd[i].record_type) &&
1129         (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTESTATION_REF != rd[i].record_type))
1130       continue;
1131     lbl = GNUNET_STRINGS_data_to_string_alloc (rd[i].data, rd[i].data_size);
1132     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ticket reference found %s\n", lbl);
1133     parallel_lookup = GNUNET_new (struct ParallelLookup);
1134     parallel_lookup->handle = cth;
1135     parallel_lookup->label = lbl;
1136     parallel_lookup->lookup_start_time = GNUNET_TIME_absolute_get ();
1137     parallel_lookup->lookup_request =
1138       GNUNET_GNS_lookup (gns,
1139                          lbl,
1140                          &cth->ticket.identity,
1141                          GNUNET_GNSRECORD_TYPE_ANY,
1142                          GNUNET_GNS_LO_DEFAULT,
1143                          &process_parallel_lookup_result,
1144                          parallel_lookup);
1145     GNUNET_CONTAINER_DLL_insert (cth->parallel_lookups_head,
1146                                  cth->parallel_lookups_tail,
1147                                  parallel_lookup);
1148   }
1149   /**
1150    * We started lookups. Add a timeout task.
1151    * FIXME: Really needed here?
1152    */
1153   if (NULL != cth->parallel_lookups_head)
1154   {
1155     cth->kill_task = GNUNET_SCHEDULER_add_delayed (
1156       GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 3),
1157       &abort_parallel_lookups,
1158       cth);
1159     return;
1160   }
1161   /**
1162    * No references found, return empty attribute list
1163    */
1164   cth->cb (cth->cb_cls, &cth->ticket.identity,
1165            cth->attrs, cth->attests, GNUNET_OK, NULL);
1166   cleanup_cth (cth);
1167 }
1168
1169
1170 /**
1171  * Consume a ticket.
1172  * We first looking attribute references under the label
1173  * ticket.rnd in GNS.
1174  *
1175  * @param id the audience of the ticket
1176  * @param ticket the ticket to consume
1177  * @param cb callback to call with attributes of ticket
1178  * @param cb_cls callback closure
1179  * @return handle to the operation
1180  */
1181 struct RECLAIM_TICKETS_ConsumeHandle *
1182 RECLAIM_TICKETS_consume (const struct GNUNET_CRYPTO_EcdsaPrivateKey *id,
1183                          const struct GNUNET_RECLAIM_Ticket *ticket,
1184                          RECLAIM_TICKETS_ConsumeCallback cb,
1185                          void *cb_cls)
1186 {
1187   struct RECLAIM_TICKETS_ConsumeHandle *cth;
1188   char *label;
1189
1190   cth = GNUNET_new (struct RECLAIM_TICKETS_ConsumeHandle);
1191
1192   cth->identity = *id;
1193   GNUNET_CRYPTO_ecdsa_key_get_public (&cth->identity, &cth->identity_pub);
1194   cth->attrs = GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
1195   cth->attests = GNUNET_new (struct GNUNET_RECLAIM_AttestationList);
1196   cth->ticket = *ticket;
1197   cth->cb = cb;
1198   cth->cb_cls = cb_cls;
1199   label =
1200     GNUNET_STRINGS_data_to_string_alloc (&cth->ticket.rnd,
1201                                          sizeof(cth->ticket.rnd));
1202   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1203               "Looking for AuthZ info under %s\n",
1204               label);
1205   cth->lookup_start_time = GNUNET_TIME_absolute_get ();
1206   cth->lookup_request =
1207     GNUNET_GNS_lookup (gns,
1208                        label,
1209                        &cth->ticket.identity,
1210                        GNUNET_GNSRECORD_TYPE_RECLAIM_ATTRIBUTE_REF,
1211                        GNUNET_GNS_LO_DEFAULT,
1212                        &lookup_authz_cb,
1213                        cth);
1214   GNUNET_free (label);
1215   return cth;
1216 }
1217
1218
1219 /**
1220  * Cancel a consume operation
1221  *
1222  * @param cth the operation to cancel
1223  */
1224 void
1225 RECLAIM_TICKETS_consume_cancel (struct RECLAIM_TICKETS_ConsumeHandle *cth)
1226 {
1227   cleanup_cth (cth);
1228   return;
1229 }
1230
1231
1232 /*******************************
1233 * Ticket issue
1234 *******************************/
1235
1236 /**
1237  * Cleanup ticket consume handle
1238  * @param handle the handle to clean up
1239  */
1240 static void
1241 cleanup_issue_handle (struct TicketIssueHandle *handle)
1242 {
1243   if (NULL != handle->ns_qe)
1244     GNUNET_NAMESTORE_cancel (handle->ns_qe);
1245   GNUNET_free (handle);
1246 }
1247
1248
1249 /**
1250  * Store finished, abort on error.
1251  * Else, return new ticket to caller.
1252  *
1253  * @param cls handle to the operation
1254  * @param success store operation result
1255  * @param emsg error message (or NULL on success)
1256  */
1257 static void
1258 store_ticket_issue_cont (void *cls, int32_t success, const char *emsg)
1259 {
1260   struct TicketIssueHandle *handle = cls;
1261
1262   handle->ns_qe = NULL;
1263   if (GNUNET_SYSERR == success)
1264   {
1265     handle->cb (handle->cb_cls,
1266                 &handle->ticket,
1267                 GNUNET_SYSERR,
1268                 "Error storing AuthZ ticket in GNS");
1269     return;
1270   }
1271   handle->cb (handle->cb_cls, &handle->ticket, GNUNET_OK, NULL);
1272   cleanup_issue_handle (handle);
1273 }
1274
1275
1276 /**
1277  * Issue a new ticket.
1278  * We store references to attribute record labels and the ticket itself
1279  * under the label base64(ticket.rnd).
1280  *
1281  * @param ih handle to the operation containing relevant metadata
1282  */
1283 static void
1284 issue_ticket (struct TicketIssueHandle *ih)
1285 {
1286   struct GNUNET_RECLAIM_AttributeListEntry *le;
1287   struct GNUNET_GNSRECORD_Data *attrs_record;
1288   char *label;
1289   int i;
1290   int attrs_count = 0;
1291
1292   for (le = ih->attrs->list_head; NULL != le; le = le->next)
1293     attrs_count++;
1294
1295   //Worst case we have one attestation per attribute
1296   attrs_record =
1297     GNUNET_malloc (2 * attrs_count * sizeof(struct GNUNET_GNSRECORD_Data));
1298   i = 0;
1299   for (le = ih->attrs->list_head; NULL != le; le = le->next)
1300   {
1301     attrs_record[i].data = &le->attribute->id;
1302     attrs_record[i].data_size = sizeof(le->attribute->id);
1303     attrs_record[i].expiration_time = ticket_refresh_interval.rel_value_us;
1304     attrs_record[i].record_type = GNUNET_GNSRECORD_TYPE_RECLAIM_ATTRIBUTE_REF;
1305     attrs_record[i].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
1306     i++;
1307     if (GNUNET_NO == GNUNET_RECLAIM_id_is_zero (&le->attribute->attestation))
1308     {
1309       int j;
1310       for (j = 0; j < i; j++)
1311       {
1312         if (attrs_record[j].record_type
1313             != GNUNET_GNSRECORD_TYPE_RECLAIM_ATTESTATION_REF)
1314           continue;
1315         if (0 == memcmp (attrs_record[j].data,
1316                          &le->attribute->attestation,
1317                          sizeof (le->attribute->attestation)))
1318           break;
1319       }
1320       if (j < i)
1321         continue; // Skip as we have already added this attestation.
1322       attrs_record[i].data = &le->attribute->attestation;
1323       attrs_record[i].data_size = sizeof(le->attribute->attestation);
1324       attrs_record[i].expiration_time = ticket_refresh_interval.rel_value_us;
1325       attrs_record[i].record_type =
1326         GNUNET_GNSRECORD_TYPE_RECLAIM_ATTESTATION_REF;
1327       attrs_record[i].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
1328       i++;
1329     }
1330   }
1331   attrs_record[i].data = &ih->ticket;
1332   attrs_record[i].data_size = sizeof(struct GNUNET_RECLAIM_Ticket);
1333   attrs_record[i].expiration_time = ticket_refresh_interval.rel_value_us;
1334   attrs_record[i].record_type = GNUNET_GNSRECORD_TYPE_RECLAIM_TICKET;
1335   attrs_record[i].flags =
1336     GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION | GNUNET_GNSRECORD_RF_PRIVATE;
1337   i++;
1338
1339   label =
1340     GNUNET_STRINGS_data_to_string_alloc (&ih->ticket.rnd,
1341                                          sizeof(ih->ticket.rnd));
1342   // Publish record
1343   ih->ns_qe = GNUNET_NAMESTORE_records_store (nsh,
1344                                               &ih->identity,
1345                                               label,
1346                                               i,
1347                                               attrs_record,
1348                                               &store_ticket_issue_cont,
1349                                               ih);
1350   GNUNET_free (attrs_record);
1351   GNUNET_free (label);
1352 }
1353
1354
1355 /*************************************************
1356 * Ticket iteration (finding a specific ticket)
1357 *************************************************/
1358
1359
1360 /**
1361  * Namestore error on issue. Abort.
1362  *
1363  * @param cls handle to the operation
1364  */
1365 static void
1366 filter_tickets_error_cb (void *cls)
1367 {
1368   struct TicketIssueHandle *tih = cls;
1369
1370   tih->ns_it = NULL;
1371   tih->cb (tih->cb_cls,
1372            &tih->ticket,
1373            GNUNET_SYSERR,
1374            "Error storing AuthZ ticket in GNS");
1375   cleanup_issue_handle (tih);
1376 }
1377
1378
1379 /**
1380  * Iterator over records.
1381  * Check if any previously issued ticket already
1382  * matches what we need to prevent duplicates and
1383  * improve resolution synergy.
1384  *
1385  * @param cls handle to the operation
1386  * @param zone issuer identity
1387  * @param label ticket rnd
1388  * @param rd_count size of record set
1389  * @param rd record set
1390  */
1391 static void
1392 filter_tickets_cb (void *cls,
1393                    const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
1394                    const char *label,
1395                    unsigned int rd_count,
1396                    const struct GNUNET_GNSRECORD_Data *rd)
1397 {
1398   struct TicketIssueHandle *tih = cls;
1399   struct GNUNET_RECLAIM_Ticket *ticket = NULL;
1400
1401   // figure out the number of requested attributes
1402   struct GNUNET_RECLAIM_AttributeListEntry *le;
1403   unsigned int attr_cnt = 0;
1404   unsigned int attest_cnt = 0;
1405
1406   for (le = tih->attrs->list_head; NULL != le; le = le->next)
1407   {
1408     attr_cnt++;
1409     if (GNUNET_NO == GNUNET_RECLAIM_id_is_zero (&le->attribute->attestation))
1410       attest_cnt++;
1411   }
1412
1413   // ticket search
1414   unsigned int found_attrs_cnt = 0;
1415   unsigned int found_attests_cnt = 0;
1416
1417   for (int i = 0; i < rd_count; i++)
1418   {
1419     // found ticket
1420     if (GNUNET_GNSRECORD_TYPE_RECLAIM_TICKET == rd[i].record_type)
1421     {
1422       ticket = (struct GNUNET_RECLAIM_Ticket *) rd[i].data;
1423       // cmp audience
1424       if (0 == memcmp (&tih->ticket.audience,
1425                        &ticket->audience,
1426                        sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)))
1427       {
1428         tih->ticket = *ticket;
1429         continue;
1430       }
1431       ticket = NULL;
1432     }
1433
1434     // cmp requested attributes with ticket attributes
1435     if ((GNUNET_GNSRECORD_TYPE_RECLAIM_ATTRIBUTE_REF != rd[i].record_type) &&
1436         (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTESTATION_REF != rd[i].record_type))
1437       continue;
1438     for (le = tih->attrs->list_head; NULL != le; le = le->next)
1439     {
1440       if (GNUNET_YES == GNUNET_RECLAIM_id_is_equal (rd[i].data,
1441                                                     &le->attribute->id))
1442         found_attrs_cnt++;
1443     }
1444     for (le = tih->attrs->list_head; NULL != le; le = le->next)
1445     {
1446       if (GNUNET_YES == GNUNET_RECLAIM_id_is_equal (rd[i].data,
1447                                                     &le->attribute->attestation))
1448         found_attests_cnt++;
1449     }
1450   }
1451
1452   /**
1453    * If we found a matching ticket, return that to the caller and
1454    * we are done.
1455    */
1456   if ((attr_cnt == found_attrs_cnt) &&
1457       (attest_cnt == found_attests_cnt) &&
1458       (NULL != ticket))
1459   {
1460     GNUNET_NAMESTORE_zone_iteration_stop (tih->ns_it);
1461     tih->cb (tih->cb_cls, &tih->ticket, GNUNET_OK, NULL);
1462     cleanup_issue_handle (tih);
1463     return;
1464   }
1465
1466   // ticket not found in current record, checking next record set
1467   GNUNET_NAMESTORE_zone_iterator_next (tih->ns_it, 1);
1468 }
1469
1470
1471 /**
1472  * Done iterating over tickets and we apparently did
1473  * not find an existing, matching ticket.
1474  * Continue by issuing a new ticket.
1475  *
1476  * @param cls handle to the operation
1477  */
1478 static void
1479 filter_tickets_finished_cb (void *cls)
1480 {
1481   struct TicketIssueHandle *tih = cls;
1482
1483   GNUNET_CRYPTO_ecdsa_key_get_public (&tih->identity, &tih->ticket.identity);
1484   GNUNET_RECLAIM_id_generate (&tih->ticket.rnd);
1485   issue_ticket (tih);
1486 }
1487
1488
1489 /**
1490  * Issue a new reclaim ticket, thereby authorizing
1491  * the audience to access the set of provided attributes.
1492  *
1493  * @param identity the issuer
1494  * @param attrs the attributes to share
1495  * @param audience the audience to share the attributes with
1496  * @param cb the callback to call with the ticket result
1497  * @param cb_cls the callback closure
1498  * FIXME: Return handle??
1499  */
1500 void
1501 RECLAIM_TICKETS_issue (const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity,
1502                        const struct GNUNET_RECLAIM_AttributeList *attrs,
1503                        const struct GNUNET_CRYPTO_EcdsaPublicKey *audience,
1504                        RECLAIM_TICKETS_TicketResult cb,
1505                        void *cb_cls)
1506 {
1507   struct TicketIssueHandle *tih;
1508
1509   tih = GNUNET_new (struct TicketIssueHandle);
1510   tih->cb = cb;
1511   tih->cb_cls = cb_cls;
1512   tih->attrs = GNUNET_RECLAIM_attribute_list_dup (attrs);
1513   tih->identity = *identity;
1514   tih->ticket.audience = *audience;
1515
1516   // First check whether the ticket has already been issued
1517   tih->ns_it =
1518     GNUNET_NAMESTORE_zone_iteration_start (nsh,
1519                                            &tih->identity,
1520                                            &filter_tickets_error_cb,
1521                                            tih,
1522                                            &filter_tickets_cb,
1523                                            tih,
1524                                            &filter_tickets_finished_cb,
1525                                            tih);
1526 }
1527
1528
1529 /************************************
1530 * Ticket iteration
1531 ************************************/
1532
1533 /**
1534  * Cleanup ticket iterator
1535  *
1536  * @param iter handle to the iteration
1537  */
1538 static void
1539 cleanup_iter (struct RECLAIM_TICKETS_Iterator *iter)
1540 {
1541   if (NULL != iter->ns_it)
1542     GNUNET_NAMESTORE_zone_iteration_stop (iter->ns_it);
1543   GNUNET_free (iter);
1544 }
1545
1546
1547 /**
1548  * Return each record of type @GNUNET_GNSRECORD_TYPE_RECLAIM_TICKET
1549  * to the caller and proceed with the iteration.
1550  * FIXME: Should we _not_ proceed automatically here?
1551  *
1552  * @param cls handle to the iteration
1553  * @param zone the ticket issuer
1554  * @param label the ticket rnd
1555  * @param rd_count number of records in record set
1556  * @param rd record set containing a ticket
1557  */
1558 static void
1559 collect_tickets_cb (void *cls,
1560                     const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
1561                     const char *label,
1562                     unsigned int rd_count,
1563                     const struct GNUNET_GNSRECORD_Data *rd)
1564 {
1565   struct RECLAIM_TICKETS_Iterator *iter = cls;
1566
1567   for (int i = 0; i < rd_count; i++)
1568   {
1569     if (GNUNET_GNSRECORD_TYPE_RECLAIM_TICKET != rd[i].record_type)
1570       continue;
1571     iter->cb (iter->cb_cls, (struct GNUNET_RECLAIM_Ticket *) rd[i].data);
1572     return;
1573   }
1574   GNUNET_NAMESTORE_zone_iterator_next (iter->ns_it, 1);
1575 }
1576
1577
1578 /**
1579  * Signal ticket iteration has finished
1580  *
1581  * @param cls handle to the iteration
1582  */
1583 static void
1584 collect_tickets_finished_cb (void *cls)
1585 {
1586   struct RECLAIM_TICKETS_Iterator *iter = cls;
1587
1588   iter->ns_it = NULL;
1589   iter->cb (iter->cb_cls, NULL);
1590   cleanup_iter (iter);
1591 }
1592
1593
1594 /**
1595  * Cancel ticket iteration on namestore error
1596  *
1597  * @param cls the iteration handle
1598  */
1599 static void
1600 collect_tickets_error_cb (void *cls)
1601 {
1602   struct RECLAIM_TICKETS_Iterator *iter = cls;
1603
1604   iter->ns_it = NULL;
1605   iter->cb (iter->cb_cls, NULL);
1606   cleanup_iter (iter);
1607 }
1608
1609
1610 /**
1611  * Continue ticket iteration
1612  *
1613  * @param iter the iteration to continue
1614  */
1615 void
1616 RECLAIM_TICKETS_iteration_next (struct RECLAIM_TICKETS_Iterator *iter)
1617 {
1618   GNUNET_NAMESTORE_zone_iterator_next (iter->ns_it, 1);
1619 }
1620
1621
1622 /**
1623  * Stop a running ticket iteration
1624  *
1625  * @param iter iteration to cancel
1626  */
1627 void
1628 RECLAIM_TICKETS_iteration_stop (struct RECLAIM_TICKETS_Iterator *iter)
1629 {
1630   GNUNET_NAMESTORE_zone_iteration_stop (iter->ns_it);
1631   cleanup_iter (iter);
1632 }
1633
1634
1635 /**
1636  * Iterate over all tickets issued by an identity
1637  *
1638  * @param identity the issuing identity
1639  * @param cb ticket callback function
1640  * @param cb_cls callback closure
1641  * @return a handle to the iteration
1642  */
1643 struct RECLAIM_TICKETS_Iterator *
1644 RECLAIM_TICKETS_iteration_start (
1645   const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity,
1646   RECLAIM_TICKETS_TicketIter cb,
1647   void *cb_cls)
1648 {
1649   struct RECLAIM_TICKETS_Iterator *iter;
1650
1651   iter = GNUNET_new (struct RECLAIM_TICKETS_Iterator);
1652   iter->cb = cb;
1653   iter->cb_cls = cb_cls;
1654   iter->ns_it =
1655     GNUNET_NAMESTORE_zone_iteration_start (nsh,
1656                                            identity,
1657                                            &collect_tickets_error_cb,
1658                                            iter,
1659                                            &collect_tickets_cb,
1660                                            iter,
1661                                            &collect_tickets_finished_cb,
1662                                            iter);
1663   return iter;
1664 }
1665
1666
1667 /**
1668  * Initialize tickets component
1669  *
1670  * @param c the configuration
1671  * @return GNUNET_SYSERR on error
1672  */
1673 int
1674 RECLAIM_TICKETS_init (const struct GNUNET_CONFIGURATION_Handle *c)
1675 {
1676   // Get ticket expiration time (relative) from config
1677   if (GNUNET_OK ==
1678       GNUNET_CONFIGURATION_get_value_time (c,
1679                                            "reclaim",
1680                                            "TICKET_REFRESH_INTERVAL",
1681                                            &ticket_refresh_interval))
1682   {
1683     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1684                 "Configured refresh interval for tickets: %s\n",
1685                 GNUNET_STRINGS_relative_time_to_string (ticket_refresh_interval,
1686                                                         GNUNET_YES));
1687   }
1688   else
1689   {
1690     ticket_refresh_interval = DEFAULT_TICKET_REFRESH_INTERVAL;
1691   }
1692   // Connect to identity and namestore services
1693   nsh = GNUNET_NAMESTORE_connect (c);
1694   if (NULL == nsh)
1695   {
1696     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
1697                          "error connecting to namestore");
1698     return GNUNET_SYSERR;
1699   }
1700   gns = GNUNET_GNS_connect (c);
1701   if (NULL == gns)
1702   {
1703     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "error connecting to gns");
1704     return GNUNET_SYSERR;
1705   }
1706   stats = GNUNET_STATISTICS_create ("reclaim", c);
1707   return GNUNET_OK;
1708 }
1709
1710
1711 /**
1712  * Close handles and clean up.
1713  * FIXME: cancel all pending operations (gns, ns etc)
1714  */
1715 void
1716 RECLAIM_TICKETS_deinit (void)
1717 {
1718   if (NULL != nsh)
1719     GNUNET_NAMESTORE_disconnect (nsh);
1720   nsh = NULL;
1721   if (NULL != gns)
1722     GNUNET_GNS_disconnect (gns);
1723   gns = NULL;
1724   if (NULL != stats)
1725   {
1726     GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1727     stats = NULL;
1728   }
1729 }