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