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