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