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