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