memcmp() -> GNUNET_memcmp(), first take
[oweals/gnunet.git] / src / reclaim / plugin_rest_reclaim.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  * @author Martin Schanzenbach
22  * @author Philippe Buschmann
23  * @file reclaim/plugin_rest_reclaim.c
24  * @brief GNUnet reclaim REST plugin
25  *
26  */
27 #include "platform.h"
28
29 #include "microhttpd.h"
30 #include <inttypes.h>
31 #include <jansson.h>
32
33 #include "gnunet_gns_service.h"
34 #include "gnunet_gnsrecord_lib.h"
35 #include "gnunet_identity_service.h"
36 #include "gnunet_namestore_service.h"
37 #include "gnunet_reclaim_attribute_lib.h"
38 #include "gnunet_reclaim_service.h"
39 #include "gnunet_rest_lib.h"
40 #include "gnunet_rest_plugin.h"
41 #include "gnunet_signatures.h"
42 #include "json_reclaim.h"
43 /**
44  * REST root namespace
45  */
46 #define GNUNET_REST_API_NS_RECLAIM "/reclaim"
47
48 /**
49  * Attribute namespace
50  */
51 #define GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES "/reclaim/attributes"
52
53 /**
54  * Ticket namespace
55  */
56 #define GNUNET_REST_API_NS_IDENTITY_TICKETS "/reclaim/tickets"
57
58 /**
59  * Revoke namespace
60  */
61 #define GNUNET_REST_API_NS_IDENTITY_REVOKE "/reclaim/revoke"
62
63 /**
64  * Revoke namespace
65  */
66 #define GNUNET_REST_API_NS_IDENTITY_CONSUME "/reclaim/consume"
67
68 /**
69  * State while collecting all egos
70  */
71 #define ID_REST_STATE_INIT 0
72
73 /**
74  * Done collecting egos
75  */
76 #define ID_REST_STATE_POST_INIT 1
77
78 /**
79  * The configuration handle
80  */
81 const struct GNUNET_CONFIGURATION_Handle *cfg;
82
83 /**
84  * HTTP methods allows for this plugin
85  */
86 static char *allow_methods;
87
88 /**
89  * @brief struct returned by the initialization function of the plugin
90  */
91 struct Plugin
92 {
93   const struct GNUNET_CONFIGURATION_Handle *cfg;
94 };
95
96 /**
97  * The ego list
98  */
99 struct EgoEntry
100 {
101   /**
102    * DLL
103    */
104   struct EgoEntry *next;
105
106   /**
107    * DLL
108    */
109   struct EgoEntry *prev;
110
111   /**
112    * Ego Identifier
113    */
114   char *identifier;
115
116   /**
117    * Public key string
118    */
119   char *keystring;
120
121   /**
122    * The Ego
123    */
124   struct GNUNET_IDENTITY_Ego *ego;
125 };
126
127
128 struct RequestHandle
129 {
130   /**
131    * Ego list
132    */
133   struct EgoEntry *ego_head;
134
135   /**
136    * Ego list
137    */
138   struct EgoEntry *ego_tail;
139
140   /**
141    * Selected ego
142    */
143   struct EgoEntry *ego_entry;
144
145   /**
146    * Pointer to ego private key
147    */
148   struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key;
149
150   /**
151    * The processing state
152    */
153   int state;
154
155   /**
156    * Handle to Identity service.
157    */
158   struct GNUNET_IDENTITY_Handle *identity_handle;
159
160   /**
161    * Rest connection
162    */
163   struct GNUNET_REST_RequestHandle *rest_handle;
164
165   /**
166    * Handle to NAMESTORE
167    */
168   struct GNUNET_NAMESTORE_Handle *namestore_handle;
169
170   /**
171    * Iterator for NAMESTORE
172    */
173   struct GNUNET_NAMESTORE_ZoneIterator *namestore_handle_it;
174
175   /**
176    * Attribute claim list
177    */
178   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attr_list;
179
180   /**
181    * IDENTITY Operation
182    */
183   struct GNUNET_IDENTITY_Operation *op;
184
185   /**
186    * Identity Provider
187    */
188   struct GNUNET_RECLAIM_Handle *idp;
189
190   /**
191    * Idp Operation
192    */
193   struct GNUNET_RECLAIM_Operation *idp_op;
194
195   /**
196    * Attribute iterator
197    */
198   struct GNUNET_RECLAIM_AttributeIterator *attr_it;
199
200   /**
201    * Ticket iterator
202    */
203   struct GNUNET_RECLAIM_TicketIterator *ticket_it;
204
205   /**
206    * A ticket
207    */
208   struct GNUNET_RECLAIM_Ticket ticket;
209
210   /**
211    * Desired timeout for the lookup (default is no timeout).
212    */
213   struct GNUNET_TIME_Relative timeout;
214
215   /**
216    * ID of a task associated with the resolution process.
217    */
218   struct GNUNET_SCHEDULER_Task *timeout_task;
219
220   /**
221    * The plugin result processor
222    */
223   GNUNET_REST_ResultProcessor proc;
224
225   /**
226    * The closure of the result processor
227    */
228   void *proc_cls;
229
230   /**
231    * The url
232    */
233   char *url;
234
235   /**
236    * Error response message
237    */
238   char *emsg;
239
240   /**
241    * Reponse code
242    */
243   int response_code;
244
245   /**
246    * Response object
247    */
248   json_t *resp_object;
249 };
250
251 /**
252  * Cleanup lookup handle
253  * @param handle Handle to clean up
254  */
255 static void
256 cleanup_handle (struct RequestHandle *handle)
257 {
258   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_entry;
259   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_tmp;
260   struct EgoEntry *ego_entry;
261   struct EgoEntry *ego_tmp;
262   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
263   if (NULL != handle->resp_object)
264     json_decref (handle->resp_object);
265   if (NULL != handle->timeout_task)
266     GNUNET_SCHEDULER_cancel (handle->timeout_task);
267   if (NULL != handle->identity_handle)
268     GNUNET_IDENTITY_disconnect (handle->identity_handle);
269   if (NULL != handle->attr_it)
270     GNUNET_RECLAIM_get_attributes_stop (handle->attr_it);
271   if (NULL != handle->ticket_it)
272     GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it);
273   if (NULL != handle->idp)
274     GNUNET_RECLAIM_disconnect (handle->idp);
275   if (NULL != handle->url)
276     GNUNET_free (handle->url);
277   if (NULL != handle->emsg)
278     GNUNET_free (handle->emsg);
279   if (NULL != handle->namestore_handle)
280     GNUNET_NAMESTORE_disconnect (handle->namestore_handle);
281   if (NULL != handle->attr_list) {
282     for (claim_entry = handle->attr_list->list_head; NULL != claim_entry;) {
283       claim_tmp = claim_entry;
284       claim_entry = claim_entry->next;
285       GNUNET_free (claim_tmp->claim);
286       GNUNET_free (claim_tmp);
287     }
288     GNUNET_free (handle->attr_list);
289   }
290   for (ego_entry = handle->ego_head; NULL != ego_entry;) {
291     ego_tmp = ego_entry;
292     ego_entry = ego_entry->next;
293     GNUNET_free (ego_tmp->identifier);
294     GNUNET_free (ego_tmp->keystring);
295     GNUNET_free (ego_tmp);
296   }
297   if (NULL != handle->attr_it) {
298     GNUNET_free (handle->attr_it);
299   }
300   GNUNET_free (handle);
301 }
302
303 static void
304 cleanup_handle_delayed (void *cls)
305 {
306   cleanup_handle (cls);
307 }
308
309
310 /**
311  * Task run on error, sends error message.  Cleans up everything.
312  *
313  * @param cls the `struct RequestHandle`
314  */
315 static void
316 do_error (void *cls)
317 {
318   struct RequestHandle *handle = cls;
319   struct MHD_Response *resp;
320   char *json_error;
321
322   GNUNET_asprintf (&json_error, "{ \"error\" : \"%s\" }", handle->emsg);
323   if (0 == handle->response_code) {
324     handle->response_code = MHD_HTTP_BAD_REQUEST;
325   }
326   resp = GNUNET_REST_create_response (json_error);
327   MHD_add_response_header (resp, "Content-Type", "application/json");
328   handle->proc (handle->proc_cls, resp, handle->response_code);
329   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
330   GNUNET_free (json_error);
331 }
332
333
334 /**
335  * Task run on timeout, sends error message.  Cleans up everything.
336  *
337  * @param cls the `struct RequestHandle`
338  */
339 static void
340 do_timeout (void *cls)
341 {
342   struct RequestHandle *handle = cls;
343
344   handle->timeout_task = NULL;
345   do_error (handle);
346 }
347
348
349 static void
350 collect_error_cb (void *cls)
351 {
352   struct RequestHandle *handle = cls;
353
354   do_error (handle);
355 }
356
357 static void
358 finished_cont (void *cls, int32_t success, const char *emsg)
359 {
360   struct RequestHandle *handle = cls;
361   struct MHD_Response *resp;
362
363   resp = GNUNET_REST_create_response (emsg);
364   if (GNUNET_OK != success) {
365     GNUNET_SCHEDULER_add_now (&do_error, handle);
366     return;
367   }
368   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
369   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
370 }
371
372
373 /**
374  * Return attributes for identity
375  *
376  * @param cls the request handle
377  */
378 static void
379 return_response (void *cls)
380 {
381   char *result_str;
382   struct RequestHandle *handle = cls;
383   struct MHD_Response *resp;
384
385   result_str = json_dumps (handle->resp_object, 0);
386   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
387   resp = GNUNET_REST_create_response (result_str);
388   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
389   GNUNET_free (result_str);
390   cleanup_handle (handle);
391 }
392
393 static void
394 collect_finished_cb (void *cls)
395 {
396   struct RequestHandle *handle = cls;
397   // Done
398   handle->attr_it = NULL;
399   handle->ticket_it = NULL;
400   GNUNET_SCHEDULER_add_now (&return_response, handle);
401 }
402
403
404 /**
405  * Collect all attributes for an ego
406  *
407  */
408 static void
409 ticket_collect (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket)
410 {
411   json_t *json_resource;
412   struct RequestHandle *handle = cls;
413   json_t *value;
414   char *tmp;
415
416   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding ticket\n");
417   tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd, sizeof (uint64_t));
418   json_resource = json_object ();
419   GNUNET_free (tmp);
420   json_array_append (handle->resp_object, json_resource);
421
422   tmp = GNUNET_STRINGS_data_to_string_alloc (
423       &ticket->identity, sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
424   value = json_string (tmp);
425   json_object_set_new (json_resource, "issuer", value);
426   GNUNET_free (tmp);
427   tmp = GNUNET_STRINGS_data_to_string_alloc (
428       &ticket->audience, sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
429   value = json_string (tmp);
430   json_object_set_new (json_resource, "audience", value);
431   GNUNET_free (tmp);
432   tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd, sizeof (uint64_t));
433   value = json_string (tmp);
434   json_object_set_new (json_resource, "rnd", value);
435   GNUNET_free (tmp);
436   GNUNET_RECLAIM_ticket_iteration_next (handle->ticket_it);
437 }
438
439
440 /**
441  * List tickets for identity request
442  *
443  * @param con_handle the connection handle
444  * @param url the url
445  * @param cls the RequestHandle
446  */
447 static void
448 list_tickets_cont (struct GNUNET_REST_RequestHandle *con_handle,
449                    const char *url, void *cls)
450 {
451   const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
452   struct RequestHandle *handle = cls;
453   struct EgoEntry *ego_entry;
454   char *identity;
455
456   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting tickets for %s.\n",
457               handle->url);
458   if (strlen (GNUNET_REST_API_NS_IDENTITY_TICKETS) >= strlen (handle->url)) {
459     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
460     GNUNET_SCHEDULER_add_now (&do_error, handle);
461     return;
462   }
463   identity = handle->url + strlen (GNUNET_REST_API_NS_IDENTITY_TICKETS) + 1;
464
465   for (ego_entry = handle->ego_head; NULL != ego_entry;
466        ego_entry = ego_entry->next)
467     if (0 == strcmp (identity, ego_entry->identifier))
468       break;
469   handle->resp_object = json_array ();
470
471   if (NULL == ego_entry) {
472     // Done
473     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n", identity);
474     GNUNET_SCHEDULER_add_now (&return_response, handle);
475     return;
476   }
477   priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
478   handle->idp = GNUNET_RECLAIM_connect (cfg);
479   handle->ticket_it = GNUNET_RECLAIM_ticket_iteration_start (
480       handle->idp, priv_key, &collect_error_cb, handle, &ticket_collect, handle,
481       &collect_finished_cb, handle);
482 }
483
484
485 static void
486 add_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle,
487                     const char *url, void *cls)
488 {
489   const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
490   const char *identity;
491   struct RequestHandle *handle = cls;
492   struct EgoEntry *ego_entry;
493   struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attribute;
494   struct GNUNET_TIME_Relative exp;
495   char term_data[handle->rest_handle->data_size + 1];
496   json_t *data_json;
497   json_error_t err;
498   struct GNUNET_JSON_Specification attrspec[] = {
499       GNUNET_RECLAIM_JSON_spec_claim (&attribute), GNUNET_JSON_spec_end ()};
500
501   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding an attribute for %s.\n",
502               handle->url);
503   if (strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) >= strlen (handle->url)) {
504     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
505     GNUNET_SCHEDULER_add_now (&do_error, handle);
506     return;
507   }
508   identity = handle->url + strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) + 1;
509
510   for (ego_entry = handle->ego_head; NULL != ego_entry;
511        ego_entry = ego_entry->next)
512     if (0 == strcmp (identity, ego_entry->identifier))
513       break;
514
515   if (NULL == ego_entry) {
516     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Identity unknown (%s)\n", identity);
517     return;
518   }
519   identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
520
521   if (0 >= handle->rest_handle->data_size) {
522     GNUNET_SCHEDULER_add_now (&do_error, handle);
523     return;
524   }
525
526   term_data[handle->rest_handle->data_size] = '\0';
527   GNUNET_memcpy (term_data, handle->rest_handle->data,
528                  handle->rest_handle->data_size);
529   data_json = json_loads (term_data, JSON_DECODE_ANY, &err);
530   GNUNET_assert (GNUNET_OK ==
531                  GNUNET_JSON_parse (data_json, attrspec, NULL, NULL));
532   json_decref (data_json);
533   if (NULL == attribute) {
534     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unable to parse attribute from %s\n",
535                 term_data);
536     GNUNET_SCHEDULER_add_now (&do_error, handle);
537     return;
538   }
539   /**
540    * New ID for attribute
541    */
542   if (0 == attribute->id)
543     attribute->id =
544         GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_STRONG, UINT64_MAX);
545   handle->idp = GNUNET_RECLAIM_connect (cfg);
546   exp = GNUNET_TIME_UNIT_HOURS;
547   handle->idp_op = GNUNET_RECLAIM_attribute_store (
548       handle->idp, identity_priv, attribute, &exp, &finished_cont, handle);
549   GNUNET_JSON_parse_free (attrspec);
550 }
551
552
553 /**
554  * Collect all attributes for an ego
555  *
556  */
557 static void
558 attr_collect (void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
559               const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr)
560 {
561   struct RequestHandle *handle = cls;
562   json_t *attr_obj;
563   const char *type;
564   char *tmp_value;
565   char *id_str;
566
567   if ((NULL == attr->name) || (NULL == attr->data)) {
568     GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
569     return;
570   }
571
572   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n", attr->name);
573
574   tmp_value = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type, attr->data,
575                                                         attr->data_size);
576
577   attr_obj = json_object ();
578   json_object_set_new (attr_obj, "value", json_string (tmp_value));
579   json_object_set_new (attr_obj, "name", json_string (attr->name));
580   type = GNUNET_RECLAIM_ATTRIBUTE_number_to_typename (attr->type);
581   json_object_set_new (attr_obj, "type", json_string (type));
582   id_str = GNUNET_STRINGS_data_to_string_alloc (&attr->id, sizeof (uint64_t));
583   json_object_set_new (attr_obj, "id", json_string (id_str));
584   json_array_append (handle->resp_object, attr_obj);
585   json_decref (attr_obj);
586   GNUNET_free (tmp_value);
587   GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
588 }
589
590
591 /**
592  * List attributes for identity request
593  *
594  * @param con_handle the connection handle
595  * @param url the url
596  * @param cls the RequestHandle
597  */
598 static void
599 list_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle,
600                      const char *url, void *cls)
601 {
602   const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
603   struct RequestHandle *handle = cls;
604   struct EgoEntry *ego_entry;
605   char *identity;
606
607   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting attributes for %s.\n",
608               handle->url);
609   if (strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) >= strlen (handle->url)) {
610     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
611     GNUNET_SCHEDULER_add_now (&do_error, handle);
612     return;
613   }
614   identity = handle->url + strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) + 1;
615
616   for (ego_entry = handle->ego_head; NULL != ego_entry;
617        ego_entry = ego_entry->next)
618     if (0 == strcmp (identity, ego_entry->identifier))
619       break;
620   handle->resp_object = json_array ();
621
622
623   if (NULL == ego_entry) {
624     // Done
625     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n", identity);
626     GNUNET_SCHEDULER_add_now (&return_response, handle);
627     return;
628   }
629   priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
630   handle->idp = GNUNET_RECLAIM_connect (cfg);
631   handle->attr_it = GNUNET_RECLAIM_get_attributes_start (
632       handle->idp, priv_key, &collect_error_cb, handle, &attr_collect, handle,
633       &collect_finished_cb, handle);
634 }
635
636
637 static void
638 delete_finished_cb (void *cls, int32_t success, const char *emsg)
639 {
640   struct RequestHandle *handle = cls;
641   struct MHD_Response *resp;
642
643   resp = GNUNET_REST_create_response (emsg);
644   if (GNUNET_OK != success) {
645     GNUNET_SCHEDULER_add_now (&do_error, handle);
646     return;
647   }
648   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
649   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
650 }
651
652
653 /**
654  * List attributes for identity request
655  *
656  * @param con_handle the connection handle
657  * @param url the url
658  * @param cls the RequestHandle
659  */
660 static void
661 delete_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle,
662                        const char *url, void *cls)
663 {
664   const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
665   struct RequestHandle *handle = cls;
666   struct GNUNET_RECLAIM_ATTRIBUTE_Claim attr;
667   struct EgoEntry *ego_entry;
668   char *identity_id_str;
669   char *identity;
670   char *id;
671
672   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Deleting attributes.\n");
673   if (strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) >= strlen (handle->url)) {
674     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
675     GNUNET_SCHEDULER_add_now (&do_error, handle);
676     return;
677   }
678   identity_id_str =
679       strdup (handle->url + strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) + 1);
680   identity = strtok (identity_id_str, "/");
681   id = strtok (NULL, "/");
682   if ((NULL == identity) || (NULL == id)) {
683     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Malformed request.\n");
684     GNUNET_free (identity_id_str);
685     GNUNET_SCHEDULER_add_now (&do_error, handle);
686     return;
687   }
688
689   for (ego_entry = handle->ego_head; NULL != ego_entry;
690        ego_entry = ego_entry->next)
691     if (0 == strcmp (identity, ego_entry->identifier))
692       break;
693   handle->resp_object = json_array ();
694   if (NULL == ego_entry) {
695     // Done
696     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n", identity);
697     GNUNET_free (identity_id_str);
698     GNUNET_SCHEDULER_add_now (&return_response, handle);
699     return;
700   }
701   priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
702   handle->idp = GNUNET_RECLAIM_connect (cfg);
703   memset (&attr, 0, sizeof (struct GNUNET_RECLAIM_ATTRIBUTE_Claim));
704   GNUNET_STRINGS_string_to_data (id, strlen (id), &attr.id, sizeof (uint64_t));
705   attr.name = "";
706   handle->idp_op = GNUNET_RECLAIM_attribute_delete (
707       handle->idp, priv_key, &attr, &delete_finished_cb, handle);
708   GNUNET_free (identity_id_str);
709 }
710
711
712 static void
713 revoke_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle,
714                     const char *url, void *cls)
715 {
716   const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
717   struct RequestHandle *handle = cls;
718   struct EgoEntry *ego_entry;
719   struct GNUNET_RECLAIM_Ticket *ticket;
720   struct GNUNET_CRYPTO_EcdsaPublicKey tmp_pk;
721   char term_data[handle->rest_handle->data_size + 1];
722   json_t *data_json;
723   json_error_t err;
724   struct GNUNET_JSON_Specification tktspec[] = {
725       GNUNET_RECLAIM_JSON_spec_ticket (&ticket), GNUNET_JSON_spec_end ()};
726
727   if (0 >= handle->rest_handle->data_size) {
728     GNUNET_SCHEDULER_add_now (&do_error, handle);
729     return;
730   }
731
732   term_data[handle->rest_handle->data_size] = '\0';
733   GNUNET_memcpy (term_data, handle->rest_handle->data,
734                  handle->rest_handle->data_size);
735   data_json = json_loads (term_data, JSON_DECODE_ANY, &err);
736   GNUNET_assert (GNUNET_OK ==
737                  GNUNET_JSON_parse (data_json, tktspec, NULL, NULL));
738   json_decref (data_json);
739   if (NULL == ticket) {
740     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unable to parse ticket from %s\n",
741                 term_data);
742     GNUNET_SCHEDULER_add_now (&do_error, handle);
743     return;
744   }
745   if (GNUNET_OK != GNUNET_JSON_parse (data_json, tktspec, NULL, NULL)) {
746     handle->emsg = GNUNET_strdup ("Not a ticket!\n");
747     GNUNET_SCHEDULER_add_now (&do_error, handle);
748     GNUNET_JSON_parse_free (tktspec);
749     json_decref (data_json);
750     return;
751   }
752
753   for (ego_entry = handle->ego_head; NULL != ego_entry;
754        ego_entry = ego_entry->next) {
755     GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &tmp_pk);
756     if (0 == memcmp (&ticket->identity, &tmp_pk,
757                      sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
758       break;
759   }
760   if (NULL == ego_entry) {
761     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Identity unknown\n");
762     GNUNET_JSON_parse_free (tktspec);
763     return;
764   }
765   identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
766
767   handle->idp = GNUNET_RECLAIM_connect (cfg);
768   handle->idp_op = GNUNET_RECLAIM_ticket_revoke (
769       handle->idp, identity_priv, ticket, &finished_cont, handle);
770   GNUNET_JSON_parse_free (tktspec);
771 }
772
773 static void
774 consume_cont (void *cls, const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
775               const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr)
776 {
777   struct RequestHandle *handle = cls;
778   char *val_str;
779   json_t *value;
780
781   if (NULL == identity) {
782     GNUNET_SCHEDULER_add_now (&return_response, handle);
783     return;
784   }
785
786   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n", attr->name);
787   val_str = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type, attr->data,
788                                                       attr->data_size);
789   if (NULL == val_str) {
790     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to parse value for: %s\n",
791                 attr->name);
792     return;
793   }
794   value = json_string (val_str);
795   json_object_set_new (handle->resp_object, attr->name, value);
796   json_decref (value);
797   GNUNET_free (val_str);
798 }
799
800 static void
801 consume_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle,
802                      const char *url, void *cls)
803 {
804   const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
805   struct RequestHandle *handle = cls;
806   struct EgoEntry *ego_entry;
807   struct GNUNET_RECLAIM_Ticket *ticket;
808   struct GNUNET_CRYPTO_EcdsaPublicKey tmp_pk;
809   char term_data[handle->rest_handle->data_size + 1];
810   json_t *data_json;
811   json_error_t err;
812   struct GNUNET_JSON_Specification tktspec[] = {
813       GNUNET_RECLAIM_JSON_spec_ticket (&ticket), GNUNET_JSON_spec_end ()};
814
815   if (0 >= handle->rest_handle->data_size) {
816     GNUNET_SCHEDULER_add_now (&do_error, handle);
817     return;
818   }
819
820   term_data[handle->rest_handle->data_size] = '\0';
821   GNUNET_memcpy (term_data, handle->rest_handle->data,
822                  handle->rest_handle->data_size);
823   data_json = json_loads (term_data, JSON_DECODE_ANY, &err);
824   if (NULL == data_json) {
825     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
826                 "Unable to parse JSON Object from %s\n", term_data);
827     GNUNET_SCHEDULER_add_now (&do_error, handle);
828     return;
829   }
830   if (GNUNET_OK != GNUNET_JSON_parse (data_json, tktspec, NULL, NULL)) {
831     handle->emsg = GNUNET_strdup ("Not a ticket!\n");
832     GNUNET_SCHEDULER_add_now (&do_error, handle);
833     GNUNET_JSON_parse_free (tktspec);
834     json_decref (data_json);
835     return;
836   }
837   for (ego_entry = handle->ego_head; NULL != ego_entry;
838        ego_entry = ego_entry->next) {
839     GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &tmp_pk);
840     if (0 == memcmp (&ticket->audience, &tmp_pk,
841                      sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
842       break;
843   }
844   if (NULL == ego_entry) {
845     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Identity unknown\n");
846     GNUNET_JSON_parse_free (tktspec);
847     return;
848   }
849   identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
850   handle->resp_object = json_object ();
851   handle->idp = GNUNET_RECLAIM_connect (cfg);
852   handle->idp_op = GNUNET_RECLAIM_ticket_consume (
853       handle->idp, identity_priv, ticket, &consume_cont, handle);
854   GNUNET_JSON_parse_free (tktspec);
855 }
856
857
858 /**
859  * Respond to OPTIONS request
860  *
861  * @param con_handle the connection handle
862  * @param url the url
863  * @param cls the RequestHandle
864  */
865 static void
866 options_cont (struct GNUNET_REST_RequestHandle *con_handle, const char *url,
867               void *cls)
868 {
869   struct MHD_Response *resp;
870   struct RequestHandle *handle = cls;
871
872   // For now, independent of path return all options
873   resp = GNUNET_REST_create_response (NULL);
874   MHD_add_response_header (resp, "Access-Control-Allow-Methods", allow_methods);
875   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
876   cleanup_handle (handle);
877   return;
878 }
879
880 /**
881  * Handle rest request
882  *
883  * @param handle the request handle
884  */
885 static void
886 init_cont (struct RequestHandle *handle)
887 {
888   struct GNUNET_REST_RequestHandlerError err;
889   static const struct GNUNET_REST_RequestHandler handlers[] = {
890       {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES,
891        &list_attribute_cont},
892       {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES,
893        &add_attribute_cont},
894       {MHD_HTTP_METHOD_DELETE, GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES,
895        &delete_attribute_cont},
896       {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_TICKETS,
897        &list_tickets_cont},
898       {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_REVOKE,
899        &revoke_ticket_cont},
900       {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_CONSUME,
901        &consume_ticket_cont},
902       {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_RECLAIM, &options_cont},
903       GNUNET_REST_HANDLER_END};
904
905   if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle, handlers,
906                                                &err, handle)) {
907     handle->response_code = err.error_code;
908     GNUNET_SCHEDULER_add_now (&do_error, handle);
909   }
910 }
911
912 /**
913  * If listing is enabled, prints information about the egos.
914  *
915  * This function is initially called for all egos and then again
916  * whenever a ego's identifier changes or if it is deleted.  At the
917  * end of the initial pass over all egos, the function is once called
918  * with 'NULL' for 'ego'. That does NOT mean that the callback won't
919  * be invoked in the future or that there was an error.
920  *
921  * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
922  * this function is only called ONCE, and 'NULL' being passed in
923  * 'ego' does indicate an error (i.e. name is taken or no default
924  * value is known).  If 'ego' is non-NULL and if '*ctx'
925  * is set in those callbacks, the value WILL be passed to a subsequent
926  * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
927  * that one was not NULL).
928  *
929  * When an identity is renamed, this function is called with the
930  * (known) ego but the NEW identifier.
931  *
932  * When an identity is deleted, this function is called with the
933  * (known) ego and "NULL" for the 'identifier'.  In this case,
934  * the 'ego' is henceforth invalid (and the 'ctx' should also be
935  * cleaned up).
936  *
937  * @param cls closure
938  * @param ego ego handle
939  * @param ctx context for application to store data for this ego
940  *                 (during the lifetime of this process, initially NULL)
941  * @param identifier identifier assigned by the user for this ego,
942  *                   NULL if the user just deleted the ego and it
943  *                   must thus no longer be used
944  */
945 static void
946 list_ego (void *cls, struct GNUNET_IDENTITY_Ego *ego, void **ctx,
947           const char *identifier)
948 {
949   struct RequestHandle *handle = cls;
950   struct EgoEntry *ego_entry;
951   struct GNUNET_CRYPTO_EcdsaPublicKey pk;
952
953   if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state)) {
954     handle->state = ID_REST_STATE_POST_INIT;
955     init_cont (handle);
956     return;
957   }
958   if (ID_REST_STATE_INIT == handle->state) {
959     ego_entry = GNUNET_new (struct EgoEntry);
960     GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
961     ego_entry->keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
962     ego_entry->ego = ego;
963     ego_entry->identifier = GNUNET_strdup (identifier);
964     GNUNET_CONTAINER_DLL_insert_tail (handle->ego_head, handle->ego_tail,
965                                       ego_entry);
966   }
967 }
968
969 static void
970 rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
971                                GNUNET_REST_ResultProcessor proc, void *proc_cls)
972 {
973   struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
974   handle->response_code = 0;
975   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
976   handle->proc_cls = proc_cls;
977   handle->proc = proc;
978   handle->state = ID_REST_STATE_INIT;
979   handle->rest_handle = rest_handle;
980
981   handle->url = GNUNET_strdup (rest_handle->url);
982   if (handle->url[strlen (handle->url) - 1] == '/')
983     handle->url[strlen (handle->url) - 1] = '\0';
984   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
985   handle->identity_handle = GNUNET_IDENTITY_connect (cfg, &list_ego, handle);
986   handle->namestore_handle = GNUNET_NAMESTORE_connect (cfg);
987   handle->timeout_task =
988       GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_timeout, handle);
989   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
990 }
991
992 /**
993  * Entry point for the plugin.
994  *
995  * @param cls Config info
996  * @return NULL on error, otherwise the plugin context
997  */
998 void *
999 libgnunet_plugin_rest_reclaim_init (void *cls)
1000 {
1001   static struct Plugin plugin;
1002   struct GNUNET_REST_Plugin *api;
1003
1004   cfg = cls;
1005   if (NULL != plugin.cfg)
1006     return NULL; /* can only initialize once! */
1007   memset (&plugin, 0, sizeof (struct Plugin));
1008   plugin.cfg = cfg;
1009   api = GNUNET_new (struct GNUNET_REST_Plugin);
1010   api->cls = &plugin;
1011   api->name = GNUNET_REST_API_NS_RECLAIM;
1012   api->process_request = &rest_identity_process_request;
1013   GNUNET_asprintf (&allow_methods, "%s, %s, %s, %s, %s", MHD_HTTP_METHOD_GET,
1014                    MHD_HTTP_METHOD_POST, MHD_HTTP_METHOD_PUT,
1015                    MHD_HTTP_METHOD_DELETE, MHD_HTTP_METHOD_OPTIONS);
1016
1017   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1018               _ ("Identity Provider REST API initialized\n"));
1019   return api;
1020 }
1021
1022
1023 /**
1024  * Exit point from the plugin.
1025  *
1026  * @param cls the plugin context (as returned by "init")
1027  * @return always NULL
1028  */
1029 void *
1030 libgnunet_plugin_rest_reclaim_done (void *cls)
1031 {
1032   struct GNUNET_REST_Plugin *api = cls;
1033   struct Plugin *plugin = api->cls;
1034   plugin->cfg = NULL;
1035
1036   GNUNET_free_non_null (allow_methods);
1037   GNUNET_free (api);
1038   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1039               "Identity Provider REST plugin is finished\n");
1040   return NULL;
1041 }
1042
1043 /* end of plugin_rest_reclaim.c */