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