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