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