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