do not check for existing egos...
[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
29 #include "gnunet-service-reclaim_tickets.h"
30
31 #define DEFAULT_TICKET_REFRESH_INTERVAL GNUNET_TIME_UNIT_HOURS
32
33 struct ParallelLookup;
34
35
36 /**
37  * A reference to a ticket stored in GNS
38  */
39 struct TicketReference
40 {
41   /**
42    * DLL
43    */
44   struct TicketReference *next;
45
46   /**
47    * DLL
48    */
49   struct TicketReference *prev;
50
51   /**
52    * Attributes
53    */
54   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs;
55
56   /**
57    * Tickets
58    */
59   struct GNUNET_RECLAIM_Ticket ticket;
60 };
61
62
63 struct RECLAIM_TICKETS_ConsumeHandle
64 {
65   /**
66    * Ticket
67    */
68   struct GNUNET_RECLAIM_Ticket ticket;
69
70   /**
71    * LookupRequest
72    */
73   struct GNUNET_GNS_LookupRequest *lookup_request;
74
75   /**
76    * Audience Key
77    */
78   struct GNUNET_CRYPTO_EcdsaPrivateKey identity;
79
80   /**
81    * Audience Key
82    */
83   struct GNUNET_CRYPTO_EcdsaPublicKey identity_pub;
84
85   /**
86    * Lookup DLL
87    */
88   struct ParallelLookup *parallel_lookups_head;
89
90   /**
91    * Lookup DLL
92    */
93   struct ParallelLookup *parallel_lookups_tail;
94
95   /**
96    * Kill task
97    */
98   struct GNUNET_SCHEDULER_Task *kill_task;
99
100   /**
101    * Attributes
102    */
103   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs;
104
105   /**
106    * Lookup time
107    */
108   struct GNUNET_TIME_Absolute lookup_start_time;
109
110   /**
111    * Callback
112    */
113   RECLAIM_TICKETS_ConsumeCallback cb;
114
115   /**
116    * Callbacl closure
117    */
118   void *cb_cls;
119 };
120
121 /**
122  * Handle for a parallel GNS lookup job
123  */
124 struct ParallelLookup
125 {
126   /* DLL */
127   struct ParallelLookup *next;
128
129   /* DLL */
130   struct ParallelLookup *prev;
131
132   /* The GNS request */
133   struct GNUNET_GNS_LookupRequest *lookup_request;
134
135   /* The handle the return to */
136   struct RECLAIM_TICKETS_ConsumeHandle *handle;
137
138   /**
139    * Lookup time
140    */
141   struct GNUNET_TIME_Absolute lookup_start_time;
142
143   /* The label to look up */
144   char *label;
145 };
146
147
148 /**
149  * Ticket issue request handle
150  */
151 struct TicketIssueHandle
152 {
153   /**
154    * Attributes to issue
155    */
156   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs;
157
158   /**
159    * Issuer Key
160    */
161   struct GNUNET_CRYPTO_EcdsaPrivateKey identity;
162
163   /**
164    * Ticket to issue
165    */
166   struct GNUNET_RECLAIM_Ticket ticket;
167
168   /**
169    * QueueEntry
170    */
171   struct GNUNET_NAMESTORE_QueueEntry *ns_qe;
172
173   /**
174    * Callback
175    */
176   RECLAIM_TICKETS_TicketResult cb;
177
178   /**
179    * Callback cls
180    */
181   void *cb_cls;
182 };
183
184 /**
185  * Ticket iterator
186  */
187 struct RECLAIM_TICKETS_Iterator
188 {
189   /**
190    * Namestore queue entry
191    */
192   struct GNUNET_NAMESTORE_ZoneIterator *ns_it;
193
194   /**
195    * Iter callback
196    */
197   RECLAIM_TICKETS_TicketIter cb;
198
199   /**
200    * Iter cls
201    */
202   void *cb_cls;
203 };
204
205
206 struct RevokedAttributeEntry
207 {
208   /**
209    * DLL
210    */
211   struct RevokedAttributeEntry *next;
212
213   /**
214    * DLL
215    */
216   struct RevokedAttributeEntry *prev;
217
218   /**
219    * Old ID of the attribute
220    */
221   uint64_t old_id;
222
223   /**
224    * New ID of the attribute
225    */
226   uint64_t new_id;
227 };
228
229
230 /**
231  * Ticket revocation request handle
232  */
233 struct RECLAIM_TICKETS_RevokeHandle
234 {
235   /**
236    * Issuer Key
237    */
238   struct GNUNET_CRYPTO_EcdsaPrivateKey identity;
239
240   /**
241    * Callback
242    */
243   RECLAIM_TICKETS_RevokeCallback cb;
244
245   /**
246    * Callback cls
247    */
248   void *cb_cls;
249
250   /**
251    * Ticket to issue
252    */
253   struct GNUNET_RECLAIM_Ticket ticket;
254
255   /**
256    * QueueEntry
257    */
258   struct GNUNET_NAMESTORE_QueueEntry *ns_qe;
259
260   /**
261    * Namestore iterator
262    */
263   struct GNUNET_NAMESTORE_ZoneIterator *ns_it;
264
265   /**
266    * Revoked attributes
267    */
268   struct RevokedAttributeEntry *attrs_head;
269
270   /**
271    * Revoked attributes
272    */
273   struct RevokedAttributeEntry *attrs_tail;
274
275   /**
276    * Current attribute to move
277    */
278   struct RevokedAttributeEntry *move_attr;
279
280   /**
281    * Number of attributes in ticket
282    */
283   unsigned int ticket_attrs;
284
285   /**
286    * Tickets to update
287    */
288   struct TicketRecordsEntry *tickets_to_update_head;
289
290   /**
291    * Tickets to update
292    */
293   struct TicketRecordsEntry *tickets_to_update_tail;
294 };
295
296 /**
297  * Ticket expiration interval
298  */
299 static struct GNUNET_TIME_Relative ticket_refresh_interval;
300
301 /* Namestore handle */
302 static struct GNUNET_NAMESTORE_Handle *nsh;
303
304 /* GNS handle */
305 static struct GNUNET_GNS_Handle *gns;
306
307 /* Handle to the statistics service */
308 static struct GNUNET_STATISTICS_Handle *stats;
309
310 static void
311 move_attrs (struct RECLAIM_TICKETS_RevokeHandle *rh);
312
313 static void
314 move_attrs_cont (void *cls)
315 {
316   move_attrs ((struct RECLAIM_TICKETS_RevokeHandle *)cls);
317 }
318
319 /**
320  * Cleanup revoke handle
321  *
322  * @param rh the ticket revocation handle
323  */
324 static void
325 cleanup_rvk (struct RECLAIM_TICKETS_RevokeHandle *rh)
326 {
327   struct RevokedAttributeEntry *ae;
328   struct TicketRecordsEntry *le;
329   if (NULL != rh->ns_qe)
330     GNUNET_NAMESTORE_cancel (rh->ns_qe);
331   if (NULL != rh->ns_it)
332     GNUNET_NAMESTORE_zone_iteration_stop (rh->ns_it);
333   while (NULL != (ae = rh->attrs_head)) {
334     GNUNET_CONTAINER_DLL_remove (rh->attrs_head, rh->attrs_tail, ae);
335     GNUNET_free (ae);
336   }
337   while (NULL != (le = rh->tickets_to_update_head)) {
338     GNUNET_CONTAINER_DLL_remove (rh->tickets_to_update_head,
339                                  rh->tickets_to_update_head, le);
340     if (NULL != le->data)
341       GNUNET_free (le->data);
342     if (NULL != le->label)
343       GNUNET_free (le->label);
344     GNUNET_free (le);
345   }
346   GNUNET_free (rh);
347 }
348
349 static void
350 del_attr_finished (void *cls, int32_t success, const char *emsg)
351 {
352   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
353   rvk->ns_qe = NULL;
354   if (GNUNET_SYSERR == success) {
355     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error removing attribute: %s\n",
356                 emsg);
357     rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
358     cleanup_rvk (rvk);
359     return;
360   }
361   rvk->move_attr = rvk->move_attr->next;
362   GNUNET_SCHEDULER_add_now (&move_attrs_cont, rvk);
363 }
364
365 static void
366 move_attr_finished (void *cls, int32_t success, const char *emsg)
367 {
368   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
369   char *label;
370   rvk->ns_qe = NULL;
371   if (GNUNET_SYSERR == success) {
372     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error moving attribute: %s\n", emsg);
373     rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
374     cleanup_rvk (rvk);
375     return;
376   }
377   label = GNUNET_STRINGS_data_to_string_alloc (&rvk->move_attr->old_id,
378                                                sizeof (uint64_t));
379   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Removing attribute %s\n", label);
380   rvk->ns_qe = GNUNET_NAMESTORE_records_store (nsh, &rvk->identity, label, 0,
381                                                NULL, &del_attr_finished, rvk);
382 }
383
384
385 static void
386 rvk_move_attr_cb (void *cls, const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
387                   const char *label, unsigned int rd_count,
388                   const struct GNUNET_GNSRECORD_Data *rd)
389 {
390   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
391   struct GNUNET_RECLAIM_ATTRIBUTE_Claim *claim;
392   struct GNUNET_GNSRECORD_Data new_rd;
393   struct RevokedAttributeEntry *le;
394   char *new_label;
395   char *attr_data;
396   rvk->ns_qe = NULL;
397   if (0 == rd_count) {
398     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
399                 "The attribute %s no longer exists!\n", label);
400     le = rvk->move_attr;
401     rvk->move_attr = le->next;
402     GNUNET_CONTAINER_DLL_remove (rvk->attrs_head, rvk->attrs_tail, le);
403     GNUNET_free (le);
404     GNUNET_SCHEDULER_add_now (&move_attrs_cont, rvk);
405     return;
406   }
407   /** find a new place for this attribute **/
408   rvk->move_attr->new_id =
409       GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_STRONG, UINT64_MAX);
410   new_rd = *rd;
411   claim = GNUNET_RECLAIM_ATTRIBUTE_deserialize (rd->data, rd->data_size);
412   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
413               "Attribute to update: Name=%s, ID=%" PRIu64 "\n", claim->name,
414               claim->id);
415   claim->id = rvk->move_attr->new_id;
416   new_rd.data_size = GNUNET_RECLAIM_ATTRIBUTE_serialize_get_size (claim);
417   attr_data = GNUNET_malloc (rd->data_size);
418   new_rd.data_size = GNUNET_RECLAIM_ATTRIBUTE_serialize (claim, attr_data);
419   new_rd.data = attr_data;
420   new_label = GNUNET_STRINGS_data_to_string_alloc (&rvk->move_attr->new_id,
421                                                    sizeof (uint64_t));
422   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute %s\n", new_label);
423   rvk->ns_qe = GNUNET_NAMESTORE_records_store (
424       nsh, &rvk->identity, new_label, 1, &new_rd, &move_attr_finished, rvk);
425   GNUNET_free (new_label);
426   GNUNET_free (claim);
427   GNUNET_free (attr_data);
428 }
429
430
431 static void
432 rvk_ticket_update (void *cls, const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
433                    const char *label, unsigned int rd_count,
434                    const struct GNUNET_GNSRECORD_Data *rd)
435 {
436   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
437   struct TicketRecordsEntry *le;
438   struct RevokedAttributeEntry *ae;
439   int has_changed = GNUNET_NO;
440
441   /** Let everything point to the old record **/
442   for (int i = 0; i < rd_count; i++) {
443     if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF != rd[i].record_type)
444       continue;
445     for (ae = rvk->attrs_head; NULL != ae; ae = ae->next) {
446       if (0 != memcmp (rd[i].data, &ae->old_id, sizeof (uint64_t)))
447         continue;
448       has_changed = GNUNET_YES;
449       break;
450     }
451     if (GNUNET_YES == has_changed)
452       break;
453   }
454   if (GNUNET_YES == has_changed) {
455     le = GNUNET_new (struct TicketRecordsEntry);
456     le->data_size = GNUNET_GNSRECORD_records_get_size (rd_count, rd);
457     le->data = GNUNET_malloc (le->data_size);
458     le->rd_count = rd_count;
459     le->label = GNUNET_strdup (label);
460     GNUNET_GNSRECORD_records_serialize (rd_count, rd, le->data_size, le->data);
461     GNUNET_CONTAINER_DLL_insert (rvk->tickets_to_update_head,
462                                  rvk->tickets_to_update_tail, le);
463   }
464   GNUNET_NAMESTORE_zone_iterator_next (rvk->ns_it, 1);
465 }
466
467
468 static void
469 process_tickets (void *cls);
470
471
472 static void
473 ticket_processed (void *cls, int32_t success, const char *emsg)
474 {
475   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
476   rvk->ns_qe = NULL;
477   GNUNET_SCHEDULER_add_now (&process_tickets, rvk);
478 }
479
480 static void
481 process_tickets (void *cls)
482 {
483   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
484   struct TicketRecordsEntry *le;
485   struct RevokedAttributeEntry *ae;
486   if (NULL == rvk->tickets_to_update_head) {
487     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
488                 "Finished updatding tickets, success\n");
489     rvk->cb (rvk->cb_cls, GNUNET_OK);
490     cleanup_rvk (rvk);
491     return;
492   }
493   le = rvk->tickets_to_update_head;
494   GNUNET_CONTAINER_DLL_remove (rvk->tickets_to_update_head,
495                                rvk->tickets_to_update_tail, le);
496   struct GNUNET_GNSRECORD_Data rd[le->rd_count];
497   GNUNET_GNSRECORD_records_deserialize (le->data_size, le->data, le->rd_count,
498                                         rd);
499   for (int i = 0; i < le->rd_count; i++) {
500     if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF != rd[i].record_type)
501       continue;
502     for (ae = rvk->attrs_head; NULL != ae; ae = ae->next) {
503       if (0 != memcmp (rd[i].data, &ae->old_id, sizeof (uint64_t)))
504         continue;
505       rd[i].data = &ae->new_id;
506     }
507   }
508   rvk->ns_qe = GNUNET_NAMESTORE_records_store (
509       nsh, &rvk->identity, le->label, le->rd_count, rd, &ticket_processed, rvk);
510   GNUNET_free (le->label);
511   GNUNET_free (le->data);
512   GNUNET_free (le);
513 }
514
515 static void
516 rvk_ticket_update_finished (void *cls)
517 {
518   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
519   rvk->ns_it = NULL;
520   GNUNET_SCHEDULER_add_now (&process_tickets, rvk);
521 }
522
523
524 static void
525 rvk_ns_iter_err (void *cls)
526 {
527   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
528   rvk->ns_it = NULL;
529   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
530               "Namestore error on revocation (id=%" PRIu64 "\n",
531               rvk->move_attr->old_id);
532   rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
533   cleanup_rvk (rvk);
534 }
535
536
537 static void
538 rvk_ns_err (void *cls)
539 {
540   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
541   rvk->ns_qe = NULL;
542   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
543               "Namestore error on revocation (id=%" PRIu64 "\n",
544               rvk->move_attr->old_id);
545   rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
546   cleanup_rvk (rvk);
547 }
548
549
550 static void
551 move_attrs (struct RECLAIM_TICKETS_RevokeHandle *rvk)
552 {
553   char *label;
554
555   if (NULL == rvk->move_attr) {
556     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Finished moving attributes\n");
557     rvk->ns_it = GNUNET_NAMESTORE_zone_iteration_start (
558         nsh, &rvk->identity, &rvk_ns_iter_err, rvk, &rvk_ticket_update, rvk,
559         &rvk_ticket_update_finished, rvk);
560     return;
561   }
562   label = GNUNET_STRINGS_data_to_string_alloc (&rvk->move_attr->old_id,
563                                                sizeof (uint64_t));
564   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Moving attribute %s\n", label);
565
566   rvk->ns_qe = GNUNET_NAMESTORE_records_lookup (
567       nsh, &rvk->identity, label, &rvk_ns_err, rvk, &rvk_move_attr_cb, rvk);
568   GNUNET_free (label);
569 }
570
571
572 static void
573 remove_ticket_cont (void *cls, int32_t success, const char *emsg)
574 {
575   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
576   rvk->ns_qe = NULL;
577   if (GNUNET_SYSERR == success) {
578     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%s\n", emsg);
579     rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
580     cleanup_rvk (rvk);
581     return;
582   }
583   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deleted ticket\n");
584   if (0 == rvk->ticket_attrs) {
585     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
586                 "No attributes to move... strange\n");
587     rvk->cb (rvk->cb_cls, GNUNET_OK);
588     cleanup_rvk (rvk);
589     return;
590   }
591   rvk->move_attr = rvk->attrs_head;
592   move_attrs (rvk);
593 }
594
595
596 static void
597 revoke_attrs_cb (void *cls, const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
598                  const char *label, unsigned int rd_count,
599                  const struct GNUNET_GNSRECORD_Data *rd)
600
601 {
602   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
603   struct RevokedAttributeEntry *le;
604   rvk->ns_qe = NULL;
605   for (int i = 0; i < rd_count; i++) {
606     if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF != rd[i].record_type)
607       continue;
608     le = GNUNET_new (struct RevokedAttributeEntry);
609     le->old_id = *((uint64_t *)rd[i].data);
610     GNUNET_CONTAINER_DLL_insert (rvk->attrs_head, rvk->attrs_tail, le);
611     rvk->ticket_attrs++;
612   }
613
614   /** Now, remove ticket **/
615   rvk->ns_qe = GNUNET_NAMESTORE_records_store (nsh, &rvk->identity, label, 0,
616                                                NULL, &remove_ticket_cont, rvk);
617 }
618
619
620 static void
621 rvk_attrs_err_cb (void *cls)
622 {
623   struct RECLAIM_TICKETS_RevokeHandle *rvk = cls;
624   rvk->cb (rvk->cb_cls, GNUNET_SYSERR);
625   cleanup_rvk (rvk);
626 }
627
628
629 struct RECLAIM_TICKETS_RevokeHandle *
630 RECLAIM_TICKETS_revoke (const struct GNUNET_RECLAIM_Ticket *ticket,
631                         const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity,
632                         RECLAIM_TICKETS_RevokeCallback cb, void *cb_cls)
633 {
634   struct RECLAIM_TICKETS_RevokeHandle *rvk;
635   char *label;
636
637   rvk = GNUNET_new (struct RECLAIM_TICKETS_RevokeHandle);
638   rvk->cb = cb;
639   rvk->cb_cls = cb_cls;
640   rvk->identity = *identity;
641   rvk->ticket = *ticket;
642   GNUNET_CRYPTO_ecdsa_key_get_public (&rvk->identity, &rvk->ticket.identity);
643   /** Get shared attributes **/
644   label = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd, sizeof (uint64_t));
645
646   rvk->ns_qe = GNUNET_NAMESTORE_records_lookup (
647       nsh, identity, label, &rvk_attrs_err_cb, rvk, &revoke_attrs_cb, rvk);
648   return rvk;
649 }
650
651
652 void
653 RECLAIM_TICKETS_revoke_cancel (struct RECLAIM_TICKETS_RevokeHandle *rh)
654 {
655   cleanup_rvk (rh);
656 }
657 /*******************************
658  * Ticket consume
659  *******************************/
660
661 /**
662  * Cleanup ticket consume handle
663  * @param cth the handle to clean up
664  */
665 static void
666 cleanup_cth (struct RECLAIM_TICKETS_ConsumeHandle *cth)
667 {
668   struct ParallelLookup *lu;
669   if (NULL != cth->lookup_request)
670     GNUNET_GNS_lookup_cancel (cth->lookup_request);
671   if (NULL != cth->kill_task)
672     GNUNET_SCHEDULER_cancel (cth->kill_task);
673   while (NULL != (lu = cth->parallel_lookups_head)) {
674     if (NULL != lu->lookup_request)
675       GNUNET_GNS_lookup_cancel (lu->lookup_request);
676     GNUNET_free_non_null (lu->label);
677     GNUNET_CONTAINER_DLL_remove (cth->parallel_lookups_head,
678                                  cth->parallel_lookups_tail, lu);
679     GNUNET_free (lu);
680   }
681
682   if (NULL != cth->attrs)
683     GNUNET_RECLAIM_ATTRIBUTE_list_destroy (cth->attrs);
684   GNUNET_free (cth);
685 }
686
687
688 static void
689 process_parallel_lookup_result (void *cls, uint32_t rd_count,
690                                 const struct GNUNET_GNSRECORD_Data *rd)
691 {
692   struct ParallelLookup *parallel_lookup = cls;
693   struct RECLAIM_TICKETS_ConsumeHandle *cth = parallel_lookup->handle;
694   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *attr_le;
695   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Parallel lookup finished (count=%u)\n",
696               rd_count);
697
698   GNUNET_CONTAINER_DLL_remove (cth->parallel_lookups_head,
699                                cth->parallel_lookups_tail, parallel_lookup);
700   GNUNET_free (parallel_lookup->label);
701
702   GNUNET_STATISTICS_update (
703       stats, "attribute_lookup_time_total",
704       GNUNET_TIME_absolute_get_duration (parallel_lookup->lookup_start_time)
705           .rel_value_us,
706       GNUNET_YES);
707   GNUNET_STATISTICS_update (stats, "attribute_lookups_count", 1, GNUNET_YES);
708
709
710   GNUNET_free (parallel_lookup);
711   if (1 != rd_count)
712     GNUNET_break (0); // TODO
713   if (rd->record_type == GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR) {
714     attr_le = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry);
715     attr_le->claim =
716         GNUNET_RECLAIM_ATTRIBUTE_deserialize (rd->data, rd->data_size);
717     GNUNET_CONTAINER_DLL_insert (cth->attrs->list_head, cth->attrs->list_tail,
718                                  attr_le);
719   }
720   if (NULL != cth->parallel_lookups_head)
721     return; // Wait for more
722   /* Else we are done */
723
724   cth->cb (cth->cb_cls, &cth->ticket.identity, cth->attrs, GNUNET_OK, NULL);
725   cleanup_cth (cth);
726 }
727
728
729 static void
730 abort_parallel_lookups (void *cls)
731 {
732   struct RECLAIM_TICKETS_ConsumeHandle *cth = cls;
733   struct ParallelLookup *lu;
734   struct ParallelLookup *tmp;
735
736   cth->kill_task = NULL;
737   for (lu = cth->parallel_lookups_head; NULL != lu;) {
738     GNUNET_GNS_lookup_cancel (lu->lookup_request);
739     GNUNET_free (lu->label);
740     tmp = lu->next;
741     GNUNET_CONTAINER_DLL_remove (cth->parallel_lookups_head,
742                                  cth->parallel_lookups_tail, lu);
743     GNUNET_free (lu);
744     lu = tmp;
745   }
746   cth->cb (cth->cb_cls, NULL, NULL, GNUNET_SYSERR, "Aborted");
747 }
748
749
750 static void
751 lookup_authz_cb (void *cls, uint32_t rd_count,
752                  const struct GNUNET_GNSRECORD_Data *rd)
753 {
754   struct RECLAIM_TICKETS_ConsumeHandle *cth = cls;
755   struct ParallelLookup *parallel_lookup;
756   char *lbl;
757
758   cth->lookup_request = NULL;
759
760   GNUNET_STATISTICS_update (
761       stats, "reclaim_authz_lookup_time_total",
762       GNUNET_TIME_absolute_get_duration (cth->lookup_start_time).rel_value_us,
763       GNUNET_YES);
764   GNUNET_STATISTICS_update (stats, "reclaim_authz_lookups_count", 1,
765                             GNUNET_YES);
766
767   for (int i = 0; i < rd_count; i++) {
768     if (GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF != rd[i].record_type)
769       continue;
770     lbl = GNUNET_STRINGS_data_to_string_alloc (rd[i].data, rd[i].data_size);
771     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Attribute ref found %s\n", lbl);
772     parallel_lookup = GNUNET_new (struct ParallelLookup);
773     parallel_lookup->handle = cth;
774     parallel_lookup->label = lbl;
775     parallel_lookup->lookup_start_time = GNUNET_TIME_absolute_get ();
776     parallel_lookup->lookup_request = GNUNET_GNS_lookup (
777         gns, lbl, &cth->ticket.identity, GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR,
778         GNUNET_GNS_LO_DEFAULT, &process_parallel_lookup_result,
779         parallel_lookup);
780     GNUNET_CONTAINER_DLL_insert (cth->parallel_lookups_head,
781                                  cth->parallel_lookups_tail, parallel_lookup);
782   }
783   if (NULL != cth->parallel_lookups_head) {
784     cth->kill_task = GNUNET_SCHEDULER_add_delayed (
785         GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 3),
786         &abort_parallel_lookups, cth);
787     return;
788   }
789   cth->cb (cth->cb_cls, &cth->ticket.identity, cth->attrs, GNUNET_OK, NULL);
790   cleanup_cth (cth);
791 }
792
793
794 struct RECLAIM_TICKETS_ConsumeHandle *
795 RECLAIM_TICKETS_consume (const struct GNUNET_CRYPTO_EcdsaPrivateKey *id,
796                          const struct GNUNET_RECLAIM_Ticket *ticket,
797                          RECLAIM_TICKETS_ConsumeCallback cb, void *cb_cls)
798 {
799   struct RECLAIM_TICKETS_ConsumeHandle *cth;
800   char *label;
801   cth = GNUNET_new (struct RECLAIM_TICKETS_ConsumeHandle);
802
803   cth->identity = *id;
804   GNUNET_CRYPTO_ecdsa_key_get_public (&cth->identity, &cth->identity_pub);
805   cth->attrs = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList);
806   cth->ticket = *ticket;
807   cth->cb = cb;
808   cth->cb_cls = cb_cls;
809   label =
810       GNUNET_STRINGS_data_to_string_alloc (&cth->ticket.rnd, sizeof (uint64_t));
811   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Looking for AuthZ info under %s\n",
812               label);
813   cth->lookup_start_time = GNUNET_TIME_absolute_get ();
814   cth->lookup_request = GNUNET_GNS_lookup (
815       gns, label, &cth->ticket.identity, GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF,
816       GNUNET_GNS_LO_DEFAULT, &lookup_authz_cb, cth);
817   GNUNET_free (label);
818   return cth;
819 }
820
821 void
822 RECLAIM_TICKETS_consume_cancel (struct RECLAIM_TICKETS_ConsumeHandle *cth)
823 {
824   cleanup_cth (cth);
825   return;
826 }
827
828
829 /*******************************
830  * Ticket issue
831  *******************************/
832
833 /**
834  * Cleanup ticket consume handle
835  * @param handle the handle to clean up
836  */
837 static void
838 cleanup_issue_handle (struct TicketIssueHandle *handle)
839 {
840   if (NULL != handle->ns_qe)
841     GNUNET_NAMESTORE_cancel (handle->ns_qe);
842   GNUNET_free (handle);
843 }
844
845
846 static void
847 store_ticket_issue_cont (void *cls, int32_t success, const char *emsg)
848 {
849   struct TicketIssueHandle *handle = cls;
850
851   handle->ns_qe = NULL;
852   if (GNUNET_SYSERR == success) {
853     handle->cb (handle->cb_cls, &handle->ticket, GNUNET_SYSERR,
854                 "Error storing AuthZ ticket in GNS");
855     return;
856   }
857   handle->cb (handle->cb_cls, &handle->ticket, GNUNET_OK, NULL);
858   cleanup_issue_handle (handle);
859 }
860
861
862 static void
863 issue_ticket (struct TicketIssueHandle *ih)
864 {
865   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le;
866   struct GNUNET_GNSRECORD_Data *attrs_record;
867   char *label;
868   size_t list_len = 1;
869   int i;
870
871   for (le = ih->attrs->list_head; NULL != le; le = le->next)
872     list_len++;
873
874   attrs_record =
875       GNUNET_malloc (list_len * sizeof (struct GNUNET_GNSRECORD_Data));
876   i = 0;
877   for (le = ih->attrs->list_head; NULL != le; le = le->next) {
878     attrs_record[i].data = &le->claim->id;
879     attrs_record[i].data_size = sizeof (le->claim->id);
880     //FIXME: Should this be the attribute expiration time or ticket refresh intv
881     attrs_record[i].expiration_time = ticket_refresh_interval.rel_value_us;
882     attrs_record[i].record_type = GNUNET_GNSRECORD_TYPE_RECLAIM_ATTR_REF;
883     attrs_record[i].flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
884     i++;
885   }
886   attrs_record[i].data = &ih->ticket;
887   attrs_record[i].data_size = sizeof (struct GNUNET_RECLAIM_Ticket);
888   attrs_record[i].expiration_time = ticket_refresh_interval.rel_value_us;
889   attrs_record[i].record_type = GNUNET_GNSRECORD_TYPE_RECLAIM_TICKET;
890   attrs_record[i].flags =
891       GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION | GNUNET_GNSRECORD_RF_PRIVATE;
892
893   label =
894       GNUNET_STRINGS_data_to_string_alloc (&ih->ticket.rnd, sizeof (uint64_t));
895   // Publish record
896   ih->ns_qe = GNUNET_NAMESTORE_records_store (nsh, &ih->identity, label,
897                                               list_len, attrs_record,
898                                               &store_ticket_issue_cont, ih);
899   GNUNET_free (attrs_record);
900   GNUNET_free (label);
901 }
902
903
904 void
905 RECLAIM_TICKETS_issue (const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity,
906                        const struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attrs,
907                        const struct GNUNET_CRYPTO_EcdsaPublicKey *audience,
908                        RECLAIM_TICKETS_TicketResult cb, void *cb_cls)
909 {
910   struct TicketIssueHandle *tih;
911   tih = GNUNET_new (struct TicketIssueHandle);
912   tih->cb = cb;
913   tih->cb_cls = cb_cls;
914   tih->attrs = GNUNET_RECLAIM_ATTRIBUTE_list_dup (attrs);
915   tih->identity = *identity;
916   GNUNET_CRYPTO_ecdsa_key_get_public (identity, &tih->ticket.identity);
917   tih->ticket.rnd =
918       GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_STRONG, UINT64_MAX);
919   tih->ticket.audience = *audience;
920   issue_ticket (tih);
921 }
922
923 /************************************
924  * Ticket iteration
925  ************************************/
926
927 static void
928 cleanup_iter (struct RECLAIM_TICKETS_Iterator *iter)
929 {
930   if (NULL != iter->ns_it)
931     GNUNET_NAMESTORE_zone_iteration_stop (iter->ns_it);
932   GNUNET_free (iter);
933 }
934
935
936 static void
937 collect_tickets_cb (void *cls, const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
938                     const char *label, unsigned int rd_count,
939                     const struct GNUNET_GNSRECORD_Data *rd)
940 {
941   struct RECLAIM_TICKETS_Iterator *iter = cls;
942
943   for (int i = 0; i < rd_count; i++) {
944     if (GNUNET_GNSRECORD_TYPE_RECLAIM_TICKET != rd[i].record_type)
945       continue;
946     iter->cb (iter->cb_cls, (struct GNUNET_RECLAIM_Ticket *)rd[i].data);
947     return;
948   }
949   GNUNET_NAMESTORE_zone_iterator_next (iter->ns_it, 1);
950 }
951
952
953 static void
954 collect_tickets_finished_cb (void *cls)
955 {
956   struct RECLAIM_TICKETS_Iterator *iter = cls;
957   iter->ns_it = NULL;
958   iter->cb (iter->cb_cls, NULL);
959   cleanup_iter (iter);
960 }
961
962
963 static void
964 collect_tickets_error_cb (void *cls)
965 {
966   struct RECLAIM_TICKETS_Iterator *iter = cls;
967   iter->ns_it = NULL;
968   iter->cb (iter->cb_cls, NULL);
969   cleanup_iter (iter);
970 }
971
972
973 void
974 RECLAIM_TICKETS_iteration_next (struct RECLAIM_TICKETS_Iterator *iter)
975 {
976   GNUNET_NAMESTORE_zone_iterator_next (iter->ns_it, 1);
977 }
978
979
980 void
981 RECLAIM_TICKETS_iteration_stop (struct RECLAIM_TICKETS_Iterator *iter)
982 {
983   GNUNET_NAMESTORE_zone_iteration_stop (iter->ns_it);
984   cleanup_iter (iter);
985 }
986
987
988 struct RECLAIM_TICKETS_Iterator *
989 RECLAIM_TICKETS_iteration_start (
990     const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity,
991     RECLAIM_TICKETS_TicketIter cb, void *cb_cls)
992 {
993   struct RECLAIM_TICKETS_Iterator *iter;
994
995   iter = GNUNET_new (struct RECLAIM_TICKETS_Iterator);
996   iter->cb = cb;
997   iter->cb_cls = cb_cls;
998   iter->ns_it = GNUNET_NAMESTORE_zone_iteration_start (
999       nsh, identity, &collect_tickets_error_cb, iter, &collect_tickets_cb, iter,
1000       &collect_tickets_finished_cb, iter);
1001   return iter;
1002 }
1003
1004
1005 int
1006 RECLAIM_TICKETS_init (const struct GNUNET_CONFIGURATION_Handle *c)
1007 {
1008   // Get ticket expiration time (relative) from config
1009   if (GNUNET_OK
1010       == GNUNET_CONFIGURATION_get_value_time (c,
1011                                               "reclaim",
1012                                               "TICKET_REFRESH_INTERVAL",
1013                                               &ticket_refresh_interval)) {
1014     GNUNET_log (
1015       GNUNET_ERROR_TYPE_DEBUG,
1016       "Configured refresh interval for tickets: %s\n",
1017       GNUNET_STRINGS_relative_time_to_string (ticket_refresh_interval,
1018                                               GNUNET_YES));
1019   } else {
1020     ticket_refresh_interval = DEFAULT_TICKET_REFRESH_INTERVAL;
1021   }
1022   // Connect to identity and namestore services
1023   nsh = GNUNET_NAMESTORE_connect (c);
1024   if (NULL == nsh) {
1025     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
1026                          "error connecting to namestore");
1027     return GNUNET_SYSERR;
1028   }
1029   gns = GNUNET_GNS_connect (c);
1030   if (NULL == gns) {
1031     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "error connecting to gns");
1032     return GNUNET_SYSERR;
1033   }
1034   stats = GNUNET_STATISTICS_create ("reclaim", c);
1035   return GNUNET_OK;
1036 }
1037
1038 void
1039 RECLAIM_TICKETS_deinit (void)
1040 {
1041   if (NULL != nsh)
1042     GNUNET_NAMESTORE_disconnect (nsh);
1043   nsh = NULL;
1044   if (NULL != gns)
1045     GNUNET_GNS_disconnect (gns);
1046   gns = NULL;
1047   if (NULL != stats) {
1048     GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1049     stats = NULL;
1050   }
1051 }