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