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