add changelog
[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  * Cleanup revoke handle
335  *
336  * @param rh the ticket revocation handle
337  */
338 static void
339 cleanup_rvk (struct RECLAIM_TICKETS_RevokeHandle *rh)
340 {
341   struct RevokedAttributeEntry *ae;
342   struct TicketRecordsEntry *le;
343
344   if (NULL != rh->ns_qe)
345     GNUNET_NAMESTORE_cancel (rh->ns_qe);
346   if (NULL != rh->ns_it)
347     GNUNET_NAMESTORE_zone_iteration_stop (rh->ns_it);
348   while (NULL != (ae = rh->attrs_head))
349   {
350     GNUNET_CONTAINER_DLL_remove (rh->attrs_head, rh->attrs_tail, ae);
351     GNUNET_free (ae);
352   }
353   while (NULL != (le = rh->tickets_to_update_head))
354   {
355     GNUNET_CONTAINER_DLL_remove (rh->tickets_to_update_head,
356                                  rh->tickets_to_update_head,
357                                  le);
358     if (NULL != le->data)
359       GNUNET_free (le->data);
360     if (NULL != le->label)
361       GNUNET_free (le->label);
362     GNUNET_free (le);
363   }
364   GNUNET_free (rh);
365 }
366
367
368 /**
369  * For each ticket, store new, updated attribute references
370  * (Implementation further below)
371  *
372  * @param cls handle to the operation
373  */
374 static void
375 process_tickets (void *cls);
376
377
378 /**
379  * Finished storing updated attribute references.
380  * Abort on error, else continue processing tickets
381  *
382  * @param cls handle to the operation
383  * @param success result of namestore operation
384  * @param emsg (NULL on success)
385  */
386 static void
387 ticket_processed (void *cls, int32_t success, const char *emsg)
388 {
389   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
390
391   rvk->ns_qe = NULL;
392   GNUNET_SCHEDULER_add_now (&process_tickets, rvk);
393 }
394
395
396 /**
397  * For each ticket, store new, updated attribute references
398  *
399  * @param cls handle to the operation
400  */
401 static void
402 process_tickets (void *cls)
403 {
404   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
405   struct TicketRecordsEntry *le;
406   struct RevokedAttributeEntry *ae;
407
408   if (NULL == rvk->tickets_to_update_head)
409   {
410     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
411                 "Finished updatding tickets, success\n");
412     rvk->cb (rvk->cb_cls, GNUNET_OK);
413     cleanup_rvk (rvk);
414     return;
415   }
416   le = rvk->tickets_to_update_head;
417   GNUNET_CONTAINER_DLL_remove (rvk->tickets_to_update_head,
418                                rvk->tickets_to_update_tail,
419                                le);
420   struct GNUNET_GNSRECORD_Data rd[le->rd_count];
421   if (GNUNET_OK != GNUNET_GNSRECORD_records_deserialize (le->data_size,
422                                                          le->data,
423                                                          le->rd_count,
424                                                          rd))
425   {
426     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
427                 "Unable to deserialize ticket record(s)\n");
428     rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
429     cleanup_rvk (rvk);
430     return;
431   }
432   for (int i = 0; i < le->rd_count; i++)
433   {
434     if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF != rd[i].record_type)
435       continue;
436     for (ae = rvk->attrs_head; NULL != ae; ae = ae->next)
437     {
438       if (0 != memcmp (rd[i].data, &ae->old_id, sizeof(uint64_t)))
439         continue;
440       rd[i].data = &ae->new_id;
441     }
442   }
443   rvk->ns_qe = GNUNET_NAMESTORE_records_store (nsh,
444                                                &rvk->identity,
445                                                le->label,
446                                                le->rd_count,
447                                                rd,
448                                                &ticket_processed,
449                                                rvk);
450   GNUNET_free (le->label);
451   GNUNET_free (le->data);
452   GNUNET_free (le);
453 }
454
455
456 /**
457  * Done collecting tickets. Start processing.
458  *
459  * @param cls handle to the operation
460  */
461 static void
462 rvk_ticket_update_finished (void *cls)
463 {
464   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
465
466   rvk->ns_it = NULL;
467   GNUNET_SCHEDULER_add_now (&process_tickets, rvk);
468 }
469
470
471 /**
472  * We need to update all other tickets with the new attribute IDs.
473  * We first collect them all. Processing after.
474  *
475  * @param cls handle to the operation
476  * @param zone ticket issuer private key
477  * @param label ticket rnd
478  * @param rd_cound size of record set
479  * @param rd record set
480  */
481 static void
482 rvk_ticket_update (void *cls,
483                    const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
484                    const char *label,
485                    unsigned int rd_count,
486                    const struct GNUNET_GNSRECORD_Data *rd)
487 {
488   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
489   struct TicketRecordsEntry *le;
490   struct RevokedAttributeEntry *ae;
491   int has_changed = GNUNET_NO;
492
493   /** Let everything point to the old record **/
494   for (int i = 0; i < rd_count; i++)
495   {
496     if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF != rd[i].record_type)
497       continue;
498     for (ae = rvk->attrs_head; NULL != ae; ae = ae->next)
499     {
500       if (0 != memcmp (rd[i].data, &ae->old_id, sizeof(uint64_t)))
501         continue;
502       has_changed = GNUNET_YES;
503       break;
504     }
505     if (GNUNET_YES == has_changed)
506       break;
507   }
508   if (GNUNET_YES == has_changed)
509   {
510     le = GNUNET_new (struct TicketRecordsEntry);
511     le->data_size = GNUNET_GNSRECORD_records_get_size (rd_count, rd);
512     le->data = GNUNET_malloc (le->data_size);
513     le->rd_count = rd_count;
514     le->label = GNUNET_strdup (label);
515     GNUNET_GNSRECORD_records_serialize (rd_count, rd, le->data_size, le->data);
516     GNUNET_CONTAINER_DLL_insert (rvk->tickets_to_update_head,
517                                  rvk->tickets_to_update_tail,
518                                  le);
519   }
520   GNUNET_NAMESTORE_zone_iterator_next (rvk->ns_it, 1);
521 }
522
523
524 /**
525  * Error iterating namestore. Abort.
526  *
527  * @param cls handle to the operation
528  */
529 static void
530 rvk_ns_iter_err (void *cls)
531 {
532   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
533
534   rvk->ns_it = NULL;
535   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
536               "Namestore error on revocation (id=%" PRIu64 "\n",
537               rvk->move_attr->old_id);
538   rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
539   cleanup_rvk (rvk);
540 }
541
542
543 /**
544  * Error storing new attribute in namestore. Abort
545  *
546  * @param cls handle to the operation
547  */
548 static void
549 rvk_ns_err (void *cls)
550 {
551   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
552
553   rvk->ns_qe = NULL;
554   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
555               "Namestore error on revocation (id=%" PRIu64 "\n",
556               rvk->move_attr->old_id);
557   rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
558   cleanup_rvk (rvk);
559 }
560
561
562 /**
563  * We change every attribute ID of the ticket attributes we
564  * want to revoke.
565  * When we are done, we need to update any other ticket which
566  * included references to any of the changed attributes.
567  * (Implementation further below)
568  *
569  * @param rvk handle to the operation
570  */
571 static void
572 move_attrs (struct RECLAIM_TICKETS_RevokeHandle *rh);
573
574
575 /**
576  * Delayed continuation for move_attrs
577  *
578  * @param cls handle to the operation.
579  */
580 static void
581 move_attrs_cont (void *cls)
582 {
583   move_attrs ((struct RECLAIM_TICKETS_RevokeHandle *) cls);
584 }
585
586
587 /**
588  * Done deleting the old record. Abort on error.
589  * Else, continue updating attribute IDs.
590  *
591  * @param cls handle to the operation
592  * @param success result of the namestore operation
593  * @param emsg error message (NULL on success)
594  */
595 static void
596 del_attr_finished (void *cls, int32_t success, const char *emsg)
597 {
598   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
599
600   rvk->ns_qe = NULL;
601   if (GNUNET_SYSERR == success)
602   {
603     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
604                 "Error removing attribute: %s\n",
605                 emsg);
606     rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
607     cleanup_rvk (rvk);
608     return;
609   }
610   rvk->move_attr = rvk->move_attr->next;
611   GNUNET_SCHEDULER_add_now (&move_attrs_cont, rvk);
612 }
613
614
615 /**
616  * Updated an attribute ID.
617  * Abort on error if namestore operation failed.
618  * Else, we have to delete the old record.
619  *
620  * @param cls handle to the operation
621  * @param success result of the store operation
622  * @param emsg error message (NULL on success)
623  */
624 static void
625 move_attr_finished (void *cls, int32_t success, const char *emsg)
626 {
627   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
628   char *label;
629
630   rvk->ns_qe = NULL;
631   if (GNUNET_SYSERR == success)
632   {
633     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error moving attribute: %s\n", emsg);
634     rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
635     cleanup_rvk (rvk);
636     return;
637   }
638   label = GNUNET_STRINGS_data_to_string_alloc (&rvk->move_attr->old_id,
639                                                sizeof(uint64_t));
640   GNUNET_assert (NULL != label);
641   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Removing attribute %s\n", label);
642   rvk->ns_qe = GNUNET_NAMESTORE_records_store (nsh,
643                                                &rvk->identity,
644                                                label,
645                                                0,
646                                                NULL,
647                                                &del_attr_finished,
648                                                rvk);
649   GNUNET_free (label);
650 }
651
652
653 /**
654  * Got the referenced attribute. Updating the ID
655  *
656  * @param cls handle to the operation
657  * @param zone issuer identity
658  * @param label attribute ID
659  * @param rd_count size of record set (should be 1)
660  * @param rd record set (the attribute)
661  */
662 static void
663 rvk_move_attr_cb (void *cls,
664                   const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
665                   const char *label,
666                   unsigned int rd_count,
667                   const struct GNUNET_GNSRECORD_Data *rd)
668 {
669   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
670   struct GNUNET_RECLAIM_ATTRIBUTE_Claim *claim;
671   struct GNUNET_GNSRECORD_Data new_rd;
672   struct RevokedAttributeEntry *le;
673   char *new_label;
674   char *attr_data;
675
676   rvk->ns_qe = NULL;
677   if (0 == rd_count)
678   {
679     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
680                 "The attribute %s no longer exists!\n",
681                 label);
682     le = rvk->move_attr;
683     rvk->move_attr = le->next;
684     GNUNET_CONTAINER_DLL_remove (rvk->attrs_head, rvk->attrs_tail, le);
685     GNUNET_free (le);
686     GNUNET_SCHEDULER_add_now (&move_attrs_cont, rvk);
687     return;
688   }
689   /** find a new place for this attribute **/
690   rvk->move_attr->new_id =
691     GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_STRONG, UINT64_MAX);
692   new_rd = *rd;
693   claim = GNUNET_RECLAIM_ATTRIBUTE_deserialize (rd->data, rd->data_size);
694   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
695               "Attribute to update: Name=%s, ID=%" PRIu64 "\n",
696               claim->name,
697               claim->id);
698   claim->id = rvk->move_attr->new_id;
699   new_rd.data_size = GNUNET_RECLAIM_ATTRIBUTE_serialize_get_size (claim);
700   attr_data = GNUNET_malloc (rd->data_size);
701   new_rd.data_size = GNUNET_RECLAIM_ATTRIBUTE_serialize (claim, attr_data);
702   new_rd.data = attr_data;
703   new_label = GNUNET_STRINGS_data_to_string_alloc (&rvk->move_attr->new_id,
704                                                    sizeof(uint64_t));
705   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute %s\n", new_label);
706   rvk->ns_qe = GNUNET_NAMESTORE_records_store (nsh,
707                                                &rvk->identity,
708                                                new_label,
709                                                1,
710                                                &new_rd,
711                                                &move_attr_finished,
712                                                rvk);
713   GNUNET_free (new_label);
714   GNUNET_free (claim);
715   GNUNET_free (attr_data);
716 }
717
718
719 /**
720  * We change every attribute ID of the ticket attributes we
721  * want to revoke.
722  * When we are done, we need to update any other ticket which
723  * included references to any of the changed attributes.
724  *
725  * @param rvk handle to the operation
726  */
727 static void
728 move_attrs (struct RECLAIM_TICKETS_RevokeHandle *rvk)
729 {
730   char *label;
731
732   if (NULL == rvk->move_attr)
733   {
734     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Finished moving attributes\n");
735     rvk->ns_it =
736       GNUNET_NAMESTORE_zone_iteration_start (nsh,
737                                              &rvk->identity,
738                                              &rvk_ns_iter_err,
739                                              rvk,
740                                              &rvk_ticket_update,
741                                              rvk,
742                                              &rvk_ticket_update_finished,
743                                              rvk);
744     return;
745   }
746   label = GNUNET_STRINGS_data_to_string_alloc (&rvk->move_attr->old_id,
747                                                sizeof(uint64_t));
748   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Moving attribute %s\n", label);
749
750   rvk->ns_qe = GNUNET_NAMESTORE_records_lookup (nsh,
751                                                 &rvk->identity,
752                                                 label,
753                                                 &rvk_ns_err,
754                                                 rvk,
755                                                 &rvk_move_attr_cb,
756                                                 rvk);
757   GNUNET_free (label);
758 }
759
760
761 /**
762  * Finished deleting ticket and attribute references.
763  * Abort on failure.
764  * Else, we start changing every attribute ID in the
765  * found attribute references so that access is no longer
766  * possible.
767  *
768  * @param cls handle to the operation
769  * @param success Namestore operation return value
770  * @param emsg error message (NULL on success)
771  */
772 static void
773 remove_ticket_cont (void *cls, int32_t success, const char *emsg)
774 {
775   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
776
777   rvk->ns_qe = NULL;
778   if (GNUNET_SYSERR == success)
779   {
780     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n", emsg);
781     rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
782     cleanup_rvk (rvk);
783     return;
784   }
785   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deleted ticket\n");
786   if (0 == rvk->ticket_attrs)
787   {
788     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
789                 "No attributes to move... strange\n");
790     rvk->cb (rvk->cb_cls, GNUNET_OK);
791     cleanup_rvk (rvk);
792     return;
793   }
794   rvk->move_attr = rvk->attrs_head;
795   move_attrs (rvk);
796 }
797
798
799 /**
800  * We found the attribute references.
801  * Store them for later and remove the record set.
802  *
803  * @param cls handle to the operation
804  * @param zone the issuer key
805  * @param label ticket rnd
806  * @param rd_cound size of record set
807  * @param rd record set
808  */
809 static void
810 revoke_attrs_cb (void *cls,
811                  const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
812                  const char *label,
813                  unsigned int rd_count,
814                  const struct GNUNET_GNSRECORD_Data *rd)
815
816 {
817   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
818   struct RevokedAttributeEntry *le;
819
820   rvk->ns_qe = NULL;
821   /**
822    * Temporarily store attribute references.
823    * We need it later.
824    */
825   for (int i = 0; i < rd_count; i++)
826   {
827     if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF != rd[i].record_type)
828       continue;
829     le = GNUNET_new (struct RevokedAttributeEntry);
830     le->old_id = *((uint64_t *) rd[i].data);
831     GNUNET_CONTAINER_DLL_insert (rvk->attrs_head, rvk->attrs_tail, le);
832     rvk->ticket_attrs++;
833   }
834
835   /** Remove attribute references **/
836   rvk->ns_qe = GNUNET_NAMESTORE_records_store (nsh,
837                                                &rvk->identity,
838                                                label,
839                                                0,
840                                                NULL,
841                                                &remove_ticket_cont,
842                                                rvk);
843 }
844
845
846 /**
847  * Failed to query namestore. Abort operation
848  *
849  * @param cls handle to the operation
850  */
851 static void
852 rvk_attrs_err_cb (void *cls)
853 {
854   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
855
856   rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
857   cleanup_rvk (rvk);
858 }
859
860
861 /**
862  * Revoke a ticket.
863  * We start by looking up attribute references in order
864  * to change attribute IDs.
865  *
866  * @param ticket ticket to revoke
867  * @param identity private key of issuer
868  * @param cb revocation status callback
869  * @param cb_cls callback closure
870  * @return handle to the operation
871  */
872 struct RECLAIM_TICKETS_RevokeHandle *
873 RECLAIM_TICKETS_revoke (const struct GNUNET_RECLAIM_Ticket *ticket,
874                         const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity,
875                         RECLAIM_TICKETS_RevokeCallback cb,
876                         void *cb_cls)
877 {
878   struct RECLAIM_TICKETS_RevokeHandle *rvk;
879   char *label;
880
881   rvk = GNUNET_new (struct RECLAIM_TICKETS_RevokeHandle);
882   rvk->cb = cb;
883   rvk->cb_cls = cb_cls;
884   rvk->identity = *identity;
885   rvk->ticket = *ticket;
886   GNUNET_CRYPTO_ecdsa_key_get_public (&rvk->identity, &rvk->ticket.identity);
887   /** Get shared attributes **/
888   label = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd, sizeof(uint64_t));
889   GNUNET_assert (NULL != label);
890   rvk->ns_qe = GNUNET_NAMESTORE_records_lookup (nsh,
891                                                 identity,
892                                                 label,
893                                                 &rvk_attrs_err_cb,
894                                                 rvk,
895                                                 &revoke_attrs_cb,
896                                                 rvk);
897   GNUNET_free (label);
898   return rvk;
899 }
900
901
902 /**
903  * Cancel a revocation.
904  *
905  * @param rh handle to the operation
906  */
907 void
908 RECLAIM_TICKETS_revoke_cancel (struct RECLAIM_TICKETS_RevokeHandle *rh)
909 {
910   GNUNET_assert (NULL != rh);
911   cleanup_rvk (rh);
912 }
913
914
915 /*******************************
916 * Ticket consume
917 *******************************/
918
919 /**
920  * Cleanup ticket consume handle
921  *
922  * @param cth the handle to clean up
923  */
924 static void
925 cleanup_cth (struct RECLAIM_TICKETS_ConsumeHandle *cth)
926 {
927   struct ParallelLookup *lu;
928
929   if (NULL != cth->lookup_request)
930     GNUNET_GNS_lookup_cancel (cth->lookup_request);
931   if (NULL != cth->kill_task)
932     GNUNET_SCHEDULER_cancel (cth->kill_task);
933   while (NULL != (lu = cth->parallel_lookups_head))
934   {
935     if (NULL != lu->lookup_request)
936       GNUNET_GNS_lookup_cancel (lu->lookup_request);
937     GNUNET_free_non_null (lu->label);
938     GNUNET_CONTAINER_DLL_remove (cth->parallel_lookups_head,
939                                  cth->parallel_lookups_tail,
940                                  lu);
941     GNUNET_free (lu);
942   }
943
944   if (NULL != cth->attrs)
945     GNUNET_RECLAIM_ATTRIBUTE_list_destroy (cth->attrs);
946   GNUNET_free (cth);
947 }
948
949
950 /**
951  * We found an attribute record.
952  *
953  * @param cls handle to the operation
954  * @param rd_cound size of record set
955  * @param rd record set
956  */
957 static void
958 process_parallel_lookup_result (void *cls,
959                                 uint32_t rd_count,
960                                 const struct GNUNET_GNSRECORD_Data *rd)
961 {
962   struct ParallelLookup *parallel_lookup = cls;
963   struct RECLAIM_TICKETS_ConsumeHandle *cth = parallel_lookup->handle;
964   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *attr_le;
965
966   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
967               "Parallel lookup finished (count=%u)\n",
968               rd_count);
969
970   GNUNET_CONTAINER_DLL_remove (cth->parallel_lookups_head,
971                                cth->parallel_lookups_tail,
972                                parallel_lookup);
973   GNUNET_free (parallel_lookup->label);
974
975   GNUNET_STATISTICS_update (stats,
976                             "attribute_lookup_time_total",
977                             GNUNET_TIME_absolute_get_duration (
978                               parallel_lookup->lookup_start_time)
979                             .rel_value_us,
980                             GNUNET_YES);
981   GNUNET_STATISTICS_update (stats, "attribute_lookups_count", 1, GNUNET_YES);
982
983
984   GNUNET_free (parallel_lookup);
985   if (1 != rd_count)
986     GNUNET_break (0); // FIXME: We should never find this.
987   if (rd->record_type == GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR)
988   {
989     attr_le = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry);
990     attr_le->claim =
991       GNUNET_RECLAIM_ATTRIBUTE_deserialize (rd->data, rd->data_size);
992     GNUNET_CONTAINER_DLL_insert (cth->attrs->list_head,
993                                  cth->attrs->list_tail,
994                                  attr_le);
995   }
996
997   if (NULL != cth->parallel_lookups_head)
998     return; // Wait for more
999
1000   /* Else we are done */
1001   cth->cb (cth->cb_cls, &cth->ticket.identity, cth->attrs, GNUNET_OK, NULL);
1002   cleanup_cth (cth);
1003 }
1004
1005
1006 /**
1007  * Cancel the lookups for attribute records
1008  *
1009  * @param cls handle to the operation
1010  */
1011 static void
1012 abort_parallel_lookups (void *cls)
1013 {
1014   struct RECLAIM_TICKETS_ConsumeHandle *cth = cls;
1015   struct ParallelLookup *lu;
1016   struct ParallelLookup *tmp;
1017
1018   cth->kill_task = NULL;
1019   for (lu = cth->parallel_lookups_head; NULL != lu;)
1020   {
1021     GNUNET_GNS_lookup_cancel (lu->lookup_request);
1022     GNUNET_free (lu->label);
1023     tmp = lu->next;
1024     GNUNET_CONTAINER_DLL_remove (cth->parallel_lookups_head,
1025                                  cth->parallel_lookups_tail,
1026                                  lu);
1027     GNUNET_free (lu);
1028     lu = tmp;
1029   }
1030   cth->cb (cth->cb_cls, NULL, NULL, GNUNET_SYSERR, "Aborted");
1031 }
1032
1033
1034 /**
1035  * GNS result with attribute references.
1036  * For each result, we start a (parallel) lookup of the actual
1037  * attribute record under the referenced label.
1038  *
1039  * @param cls handle to the operation
1040  * @param rd_cound size of the record set
1041  * @param rd record set
1042  */
1043 static void
1044 lookup_authz_cb (void *cls,
1045                  uint32_t rd_count,
1046                  const struct GNUNET_GNSRECORD_Data *rd)
1047 {
1048   struct RECLAIM_TICKETS_ConsumeHandle *cth = cls;
1049   struct ParallelLookup *parallel_lookup;
1050   char *lbl;
1051
1052   cth->lookup_request = NULL;
1053
1054   GNUNET_STATISTICS_update (stats,
1055                             "reclaim_authz_lookup_time_total",
1056                             GNUNET_TIME_absolute_get_duration (
1057                               cth->lookup_start_time)
1058                             .rel_value_us,
1059                             GNUNET_YES);
1060   GNUNET_STATISTICS_update (stats,
1061                             "reclaim_authz_lookups_count",
1062                             1,
1063                             GNUNET_YES);
1064
1065   for (int i = 0; i < rd_count; i++)
1066   {
1067     if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF != rd[i].record_type)
1068       continue;
1069     lbl = GNUNET_STRINGS_data_to_string_alloc (rd[i].data, rd[i].data_size);
1070     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Attribute ref found %s\n", lbl);
1071     parallel_lookup = GNUNET_new (struct ParallelLookup);
1072     parallel_lookup->handle = cth;
1073     parallel_lookup->label = lbl;
1074     parallel_lookup->lookup_start_time = GNUNET_TIME_absolute_get ();
1075     parallel_lookup->lookup_request =
1076       GNUNET_GNS_lookup (gns,
1077                          lbl,
1078                          &cth->ticket.identity,
1079                          GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR,
1080                          GNUNET_GNS_LO_DEFAULT,
1081                          &process_parallel_lookup_result,
1082                          parallel_lookup);
1083     GNUNET_CONTAINER_DLL_insert (cth->parallel_lookups_head,
1084                                  cth->parallel_lookups_tail,
1085                                  parallel_lookup);
1086   }
1087   /**
1088    * We started lookups. Add a timeout task.
1089    * FIXME: Really needed here?
1090    */
1091   if (NULL != cth->parallel_lookups_head)
1092   {
1093     cth->kill_task = GNUNET_SCHEDULER_add_delayed (
1094       GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 3),
1095       &abort_parallel_lookups,
1096       cth);
1097     return;
1098   }
1099   /**
1100    * No references found, return empty attribute list
1101    */
1102   cth->cb (cth->cb_cls, &cth->ticket.identity, cth->attrs, GNUNET_OK, NULL);
1103   cleanup_cth (cth);
1104 }
1105
1106
1107 /**
1108  * Consume a ticket.
1109  * We first looking attribute references under the label
1110  * ticket.rnd in GNS.
1111  *
1112  * @param id the audience of the ticket
1113  * @param ticket the ticket to consume
1114  * @param cb callback to call with attributes of ticket
1115  * @param cb_cls callback closure
1116  * @return handle to the operation
1117  */
1118 struct RECLAIM_TICKETS_ConsumeHandle *
1119 RECLAIM_TICKETS_consume (const struct GNUNET_CRYPTO_EcdsaPrivateKey *id,
1120                          const struct GNUNET_RECLAIM_Ticket *ticket,
1121                          RECLAIM_TICKETS_ConsumeCallback cb,
1122                          void *cb_cls)
1123 {
1124   struct RECLAIM_TICKETS_ConsumeHandle *cth;
1125   char *label;
1126
1127   cth = GNUNET_new (struct RECLAIM_TICKETS_ConsumeHandle);
1128
1129   cth->identity = *id;
1130   GNUNET_CRYPTO_ecdsa_key_get_public (&cth->identity, &cth->identity_pub);
1131   cth->attrs = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList);
1132   cth->ticket = *ticket;
1133   cth->cb = cb;
1134   cth->cb_cls = cb_cls;
1135   label =
1136     GNUNET_STRINGS_data_to_string_alloc (&cth->ticket.rnd, sizeof(uint64_t));
1137   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1138               "Looking for AuthZ info under %s\n",
1139               label);
1140   cth->lookup_start_time = GNUNET_TIME_absolute_get ();
1141   cth->lookup_request =
1142     GNUNET_GNS_lookup (gns,
1143                        label,
1144                        &cth->ticket.identity,
1145                        GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF,
1146                        GNUNET_GNS_LO_DEFAULT,
1147                        &lookup_authz_cb,
1148                        cth);
1149   GNUNET_free (label);
1150   return cth;
1151 }
1152
1153
1154 /**
1155  * Cancel a consume operation
1156  *
1157  * @param cth the operation to cancel
1158  */
1159 void
1160 RECLAIM_TICKETS_consume_cancel (struct RECLAIM_TICKETS_ConsumeHandle *cth)
1161 {
1162   cleanup_cth (cth);
1163   return;
1164 }
1165
1166
1167 /*******************************
1168 * Ticket issue
1169 *******************************/
1170
1171 /**
1172  * Cleanup ticket consume handle
1173  * @param handle the handle to clean up
1174  */
1175 static void
1176 cleanup_issue_handle (struct TicketIssueHandle *handle)
1177 {
1178   if (NULL != handle->ns_qe)
1179     GNUNET_NAMESTORE_cancel (handle->ns_qe);
1180   GNUNET_free (handle);
1181 }
1182
1183
1184 /**
1185  * Store finished, abort on error.
1186  * Else, return new ticket to caller.
1187  *
1188  * @param cls handle to the operation
1189  * @param success store operation result
1190  * @param emsg error message (or NULL on success)
1191  */
1192 static void
1193 store_ticket_issue_cont (void *cls, int32_t success, const char *emsg)
1194 {
1195   struct TicketIssueHandle *handle = cls;
1196
1197   handle->ns_qe = NULL;
1198   if (GNUNET_SYSERR == success)
1199   {
1200     handle->cb (handle->cb_cls,
1201                 &handle->ticket,
1202                 GNUNET_SYSERR,
1203                 "Error storing AuthZ ticket in GNS");
1204     return;
1205   }
1206   handle->cb (handle->cb_cls, &handle->ticket, GNUNET_OK, NULL);
1207   cleanup_issue_handle (handle);
1208 }
1209
1210
1211 /**
1212  * Issue a new ticket.
1213  * We store references to attribute record labels and the ticket itself
1214  * under the label base64(ticket.rnd).
1215  *
1216  * @param ih handle to the operation containing relevant metadata
1217  */
1218 static void
1219 issue_ticket (struct TicketIssueHandle *ih)
1220 {
1221   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le;
1222   struct GNUNET_GNSRECORD_Data *attrs_record;
1223   char *label;
1224   size_t list_len = 1;
1225   int i;
1226
1227   for (le = ih->attrs->list_head; NULL != le; le = le->next)
1228     list_len++;
1229
1230   attrs_record =
1231     GNUNET_malloc (list_len * sizeof(struct GNUNET_GNSRECORD_Data));
1232   i = 0;
1233   for (le = ih->attrs->list_head; NULL != le; le = le->next)
1234   {
1235     attrs_record[i].data = &le->claim->id;
1236     attrs_record[i].data_size = sizeof(le->claim->id);
1237     /**
1238      * FIXME: Should this be the attribute expiration time or ticket
1239      * refresh interval? Probably min(attrs.expiration)
1240      */
1241     attrs_record[i].expiration_time = ticket_refresh_interval.rel_value_us;
1242     attrs_record[i].record_type = GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF;
1243     attrs_record[i].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
1244     i++;
1245   }
1246   attrs_record[i].data = &ih->ticket;
1247   attrs_record[i].data_size = sizeof(struct GNUNET_RECLAIM_Ticket);
1248   attrs_record[i].expiration_time = ticket_refresh_interval.rel_value_us;
1249   attrs_record[i].record_type = GNUNET_GNSRECORD_TYPE_RECLAIM_TICKET;
1250   attrs_record[i].flags =
1251     GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION | GNUNET_GNSRECORD_RF_PRIVATE;
1252
1253   label =
1254     GNUNET_STRINGS_data_to_string_alloc (&ih->ticket.rnd, sizeof(uint64_t));
1255   // Publish record
1256   ih->ns_qe = GNUNET_NAMESTORE_records_store (nsh,
1257                                               &ih->identity,
1258                                               label,
1259                                               list_len,
1260                                               attrs_record,
1261                                               &store_ticket_issue_cont,
1262                                               ih);
1263   GNUNET_free (attrs_record);
1264   GNUNET_free (label);
1265 }
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 }