merge
[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 /**
19  * @author Martin Schanzenbach
20  * @author Philippe Buschmann
21  * @file reclaim/plugin_rest_reclaim.c
22  * @brief GNUnet reclaim REST plugin
23  *
24  */
25
26 #include "platform.h"
27 #include "gnunet_rest_plugin.h"
28 #include "gnunet_identity_service.h"
29 #include "gnunet_gns_service.h"
30 #include "gnunet_gnsrecord_lib.h"
31 #include "gnunet_namestore_service.h"
32 #include "gnunet_rest_lib.h"
33 #include "gnunet_jsonapi_lib.h"
34 #include "gnunet_jsonapi_util.h"
35 #include "microhttpd.h"
36 #include <jansson.h>
37 #include <inttypes.h>
38 #include "gnunet_signatures.h"
39 #include "gnunet_reclaim_attribute_lib.h"
40 #include "gnunet_reclaim_service.h"
41
42 /**
43  * REST root namespace
44  */
45 #define GNUNET_REST_API_NS_RECLAIM "/reclaim"
46
47 /**
48  * Attribute namespace
49  */
50 #define GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES "/reclaim/attributes"
51
52 /**
53  * Ticket namespace
54  */
55 #define GNUNET_REST_API_NS_IDENTITY_TICKETS "/reclaim/tickets"
56
57 /**
58  * Revoke namespace
59  */
60 #define GNUNET_REST_API_NS_IDENTITY_REVOKE "/reclaim/revoke"
61
62 /**
63  * Revoke namespace
64  */
65 #define GNUNET_REST_API_NS_IDENTITY_CONSUME "/reclaim/consume"
66
67 /**
68  * Attribute key
69  */
70 #define GNUNET_REST_JSONAPI_RECLAIM_ATTRIBUTE "attribute"
71
72 /**
73  * Ticket key
74  */
75 #define GNUNET_REST_JSONAPI_IDENTITY_TICKET "ticket"
76
77
78 /**
79  * Value key
80  */
81 #define GNUNET_REST_JSONAPI_RECLAIM_ATTRIBUTE_VALUE "value"
82
83 /**
84  * State while collecting all egos
85  */
86 #define ID_REST_STATE_INIT 0
87
88 /**
89  * Done collecting egos
90  */
91 #define ID_REST_STATE_POST_INIT 1
92
93 /**
94  * The configuration handle
95  */
96 const struct GNUNET_CONFIGURATION_Handle *cfg;
97
98 /**
99  * HTTP methods allows for this plugin
100  */
101 static char* allow_methods;
102
103 /**
104  * @brief struct returned by the initialization function of the plugin
105  */
106 struct Plugin
107 {
108   const struct GNUNET_CONFIGURATION_Handle *cfg;
109 };
110
111 /**
112  * The ego list
113  */
114 struct EgoEntry
115 {
116   /**
117    * DLL
118    */
119   struct EgoEntry *next;
120
121   /**
122    * DLL
123    */
124   struct EgoEntry *prev;
125
126   /**
127    * Ego Identifier
128    */
129   char *identifier;
130
131   /**
132    * Public key string
133    */
134   char *keystring;
135
136   /**
137    * The Ego
138    */
139   struct GNUNET_IDENTITY_Ego *ego;
140 };
141
142
143 struct RequestHandle
144 {
145   /**
146    * Ego list
147    */
148   struct EgoEntry *ego_head;
149
150   /**
151    * Ego list
152    */
153   struct EgoEntry *ego_tail;
154
155   /**
156    * Selected ego
157    */
158   struct EgoEntry *ego_entry;
159
160   /**
161    * Pointer to ego private key
162    */
163   struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key;
164
165   /**
166    * The processing state
167    */
168   int state;
169
170   /**
171    * Handle to Identity service.
172    */
173   struct GNUNET_IDENTITY_Handle *identity_handle;
174
175   /**
176    * Rest connection
177    */
178   struct GNUNET_REST_RequestHandle *rest_handle;
179
180   /**
181    * Handle to NAMESTORE
182    */
183   struct GNUNET_NAMESTORE_Handle *namestore_handle;
184
185   /**
186    * Iterator for NAMESTORE
187    */
188   struct GNUNET_NAMESTORE_ZoneIterator *namestore_handle_it;
189
190   /**
191    * Attribute claim list
192    */
193   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attr_list;
194
195   /**
196    * IDENTITY Operation
197    */
198   struct GNUNET_IDENTITY_Operation *op;
199
200   /**
201    * Identity Provider
202    */
203   struct GNUNET_RECLAIM_Handle *idp;
204
205   /**
206    * Idp Operation
207    */
208   struct GNUNET_RECLAIM_Operation *idp_op;
209
210   /**
211    * Attribute iterator
212    */
213   struct GNUNET_RECLAIM_AttributeIterator *attr_it;
214
215   /**
216    * Ticket iterator
217    */
218   struct GNUNET_RECLAIM_TicketIterator *ticket_it;
219
220   /**
221    * A ticket
222    */
223   struct GNUNET_RECLAIM_Ticket ticket;
224
225   /**
226    * Desired timeout for the lookup (default is no timeout).
227    */
228   struct GNUNET_TIME_Relative timeout;
229
230   /**
231    * ID of a task associated with the resolution process.
232    */
233   struct GNUNET_SCHEDULER_Task *timeout_task;
234
235   /**
236    * The plugin result processor
237    */
238   GNUNET_REST_ResultProcessor proc;
239
240   /**
241    * The closure of the result processor
242    */
243   void *proc_cls;
244
245   /**
246    * The url
247    */
248   char *url;
249
250   /**
251    * Error response message
252    */
253   char *emsg;
254
255   /**
256    * Reponse code
257    */
258   int response_code;
259
260   /**
261    * Response object
262    */
263   struct GNUNET_JSONAPI_Document *resp_object;
264
265 };
266
267 /**
268  * Cleanup lookup handle
269  * @param handle Handle to clean up
270  */
271 static void
272 cleanup_handle (struct RequestHandle *handle)
273 {
274   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_entry;
275   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_tmp;
276   struct EgoEntry *ego_entry;
277   struct EgoEntry *ego_tmp;
278   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
279               "Cleaning up\n");
280   if (NULL != handle->resp_object)
281     GNUNET_JSONAPI_document_delete (handle->resp_object);
282   if (NULL != handle->timeout_task)
283     GNUNET_SCHEDULER_cancel (handle->timeout_task);
284   if (NULL != handle->identity_handle)
285     GNUNET_IDENTITY_disconnect (handle->identity_handle);
286   if (NULL != handle->attr_it)
287     GNUNET_RECLAIM_get_attributes_stop (handle->attr_it);
288   if (NULL != handle->ticket_it)
289     GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it);
290   if (NULL != handle->idp)
291     GNUNET_RECLAIM_disconnect (handle->idp);
292   if (NULL != handle->url)
293     GNUNET_free (handle->url);
294   if (NULL != handle->emsg)
295     GNUNET_free (handle->emsg);
296   if (NULL != handle->namestore_handle)
297     GNUNET_NAMESTORE_disconnect (handle->namestore_handle);
298   if ( NULL != handle->attr_list )
299   {
300     for (claim_entry = handle->attr_list->list_head;
301     NULL != claim_entry;)
302     {
303       claim_tmp = claim_entry;
304       claim_entry = claim_entry->next;
305       GNUNET_free(claim_tmp->claim);
306       GNUNET_free(claim_tmp);
307     }
308     GNUNET_free (handle->attr_list);
309   }
310   for (ego_entry = handle->ego_head;
311        NULL != ego_entry;)
312   {
313     ego_tmp = ego_entry;
314     ego_entry = ego_entry->next;
315     GNUNET_free (ego_tmp->identifier);
316     GNUNET_free (ego_tmp->keystring);
317     GNUNET_free (ego_tmp);
318   }
319   if (NULL != handle->attr_it)
320   {
321     GNUNET_free(handle->attr_it);
322   }
323   GNUNET_free (handle);
324 }
325
326 static void
327 cleanup_handle_delayed (void *cls)
328 {
329   cleanup_handle (cls);
330 }
331
332
333 /**
334  * Task run on error, sends error message.  Cleans up everything.
335  *
336  * @param cls the `struct RequestHandle`
337  */
338 static void
339 do_error (void *cls)
340 {
341   struct RequestHandle *handle = cls;
342   struct MHD_Response *resp;
343   char *json_error;
344
345   GNUNET_asprintf (&json_error, "{ \"error\" : \"%s\" }",
346                    handle->emsg);
347   if ( 0 == handle->response_code )
348   {
349     handle->response_code = MHD_HTTP_BAD_REQUEST;
350   }
351   resp = GNUNET_REST_create_response (json_error);
352   MHD_add_response_header (resp, "Content-Type", "application/json");
353   handle->proc (handle->proc_cls, resp, handle->response_code);
354   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
355   GNUNET_free (json_error);
356 }
357
358
359 /**
360  * Task run on timeout, sends error message.  Cleans up everything.
361  *
362  * @param cls the `struct RequestHandle`
363  */
364 static void
365 do_timeout (void *cls)
366 {
367   struct RequestHandle *handle = cls;
368
369   handle->timeout_task = NULL;
370   do_error (handle);
371 }
372
373
374 static void
375 collect_error_cb (void *cls)
376 {
377   struct RequestHandle *handle = cls;
378
379   do_error (handle);
380 }
381
382 static void
383 finished_cont (void *cls,
384                int32_t success,
385                const char *emsg)
386 {
387   struct RequestHandle *handle = cls;
388   struct MHD_Response *resp;
389
390   resp = GNUNET_REST_create_response (emsg);
391   if (GNUNET_OK != success)
392   {
393     GNUNET_SCHEDULER_add_now (&do_error, handle);
394     return;
395   }
396   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
397   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
398 }
399
400
401 /**
402  * Return attributes for identity
403  *
404  * @param cls the request handle
405  */
406 static void
407 return_response (void *cls)
408 {
409   char* result_str;
410   struct RequestHandle *handle = cls;
411   struct MHD_Response *resp;
412
413   GNUNET_JSONAPI_document_serialize (handle->resp_object, &result_str);
414   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
415   resp = GNUNET_REST_create_response (result_str);
416   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
417   GNUNET_free (result_str);
418   cleanup_handle (handle);
419 }
420
421 static void
422 collect_finished_cb (void *cls)
423 {
424   struct RequestHandle *handle = cls;
425   //Done
426   handle->attr_it = NULL;
427   handle->ticket_it = NULL;
428   GNUNET_SCHEDULER_add_now (&return_response, handle);
429 }
430
431
432 /**
433  * Collect all attributes for an ego
434  *
435  */
436 static void
437 ticket_collect (void *cls,
438                 const struct GNUNET_RECLAIM_Ticket *ticket)
439 {
440   struct GNUNET_JSONAPI_Resource *json_resource;
441   struct RequestHandle *handle = cls;
442   json_t *value;
443   char* tmp;
444
445   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding ticket\n");
446   tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd,
447                                              sizeof (uint64_t));
448   json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_IDENTITY_TICKET,
449                                                        tmp);
450   GNUNET_free (tmp);
451   GNUNET_JSONAPI_document_resource_add (handle->resp_object, json_resource);
452
453   tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->identity,
454                                              sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
455   value = json_string (tmp);
456   GNUNET_JSONAPI_resource_add_attr (json_resource,
457                                     "issuer",
458                                     value);
459   GNUNET_free (tmp);
460   json_decref (value);
461   tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->audience,
462                                              sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
463   value = json_string (tmp);
464   GNUNET_JSONAPI_resource_add_attr (json_resource,
465                                     "audience",
466                                     value);
467   GNUNET_free (tmp);
468   json_decref (value);
469   tmp = GNUNET_STRINGS_data_to_string_alloc (&ticket->rnd,
470                                              sizeof (uint64_t));
471   value = json_string (tmp);
472   GNUNET_JSONAPI_resource_add_attr (json_resource,
473                                     "rnd",
474                                     value);
475   GNUNET_free (tmp);
476   json_decref (value);
477   GNUNET_RECLAIM_ticket_iteration_next (handle->ticket_it);
478 }
479
480
481
482 /**
483  * List tickets for identity request
484  *
485  * @param con_handle the connection handle
486  * @param url the url
487  * @param cls the RequestHandle
488  */
489 static void
490 list_tickets_cont (struct GNUNET_REST_RequestHandle *con_handle,
491                    const char* url,
492                    void *cls)
493 {
494   const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
495   struct RequestHandle *handle = cls;
496   struct EgoEntry *ego_entry;
497   char *identity;
498
499   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting tickets for %s.\n",
500               handle->url);
501   if ( strlen (GNUNET_REST_API_NS_IDENTITY_TICKETS) >=
502        strlen (handle->url))
503   {
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_IDENTITY_TICKETS) + 1;
509
510   for (ego_entry = handle->ego_head;
511        NULL != ego_entry;
512        ego_entry = ego_entry->next)
513     if (0 == strcmp (identity, ego_entry->identifier))
514       break;
515   handle->resp_object = GNUNET_JSONAPI_document_new ();
516
517   if (NULL == ego_entry)
518   {
519     //Done
520     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n",
521                 identity);
522     GNUNET_SCHEDULER_add_now (&return_response, handle);
523     return;
524   }
525   priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
526   handle->idp = GNUNET_RECLAIM_connect (cfg);
527   handle->ticket_it = GNUNET_RECLAIM_ticket_iteration_start (handle->idp,
528                                                                        priv_key,
529                                                                        &collect_error_cb,
530                                                                        handle,
531                                                                        &ticket_collect,
532                                                                        handle,
533                                                                        &collect_finished_cb,
534                                                                        handle);
535 }
536
537
538 static void
539 add_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle,
540                     const char* url,
541                     void *cls)
542 {
543   const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
544   const char* identity;
545   const char* name_str;
546   const char* value_str;
547   const char* exp_str;
548
549   struct RequestHandle *handle = cls;
550   struct EgoEntry *ego_entry;
551   struct MHD_Response *resp;
552   struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attribute;
553   struct GNUNET_JSONAPI_Document *json_obj;
554   struct GNUNET_JSONAPI_Resource *json_res;
555   struct GNUNET_TIME_Relative exp;
556   char term_data[handle->rest_handle->data_size+1];
557   json_t *value_json;
558   json_t *data_json;
559   json_t *exp_json;
560   json_error_t err;
561   struct GNUNET_JSON_Specification docspec[] = {
562     GNUNET_JSON_spec_jsonapi_document (&json_obj),
563     GNUNET_JSON_spec_end()
564   };
565
566   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding an attribute for %s.\n",
567               handle->url);
568   if ( strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) >=
569        strlen (handle->url))
570   {
571     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
572     GNUNET_SCHEDULER_add_now (&do_error, handle);
573     return;
574   }
575   identity = handle->url + strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) + 1;
576
577   for (ego_entry = handle->ego_head;
578        NULL != ego_entry;
579        ego_entry = ego_entry->next)
580     if (0 == strcmp (identity, ego_entry->identifier))
581       break;
582
583   if (NULL == ego_entry)
584   {
585     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
586                 "Identity unknown (%s)\n", identity);
587     GNUNET_JSONAPI_document_delete (json_obj);
588     return;
589   }
590   identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
591
592   if (0 >= handle->rest_handle->data_size)
593   {
594     GNUNET_SCHEDULER_add_now (&do_error, handle);
595     return;
596   }
597
598   term_data[handle->rest_handle->data_size] = '\0';
599   GNUNET_memcpy (term_data,
600                  handle->rest_handle->data,
601                  handle->rest_handle->data_size);
602   data_json = json_loads (term_data,
603                           JSON_DECODE_ANY,
604                           &err);
605   GNUNET_assert (GNUNET_OK ==
606                  GNUNET_JSON_parse (data_json, docspec,
607                                     NULL, NULL));
608   json_decref (data_json);
609   if (NULL == json_obj)
610   {
611     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
612                 "Unable to parse JSONAPI Object from %s\n",
613                 term_data);
614     GNUNET_SCHEDULER_add_now (&do_error, handle);
615     return;
616   }
617   if (1 != GNUNET_JSONAPI_document_resource_count (json_obj))
618   {
619     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
620                 "Cannot create more than 1 resource! (Got %d)\n",
621                 GNUNET_JSONAPI_document_resource_count (json_obj));
622     GNUNET_JSONAPI_document_delete (json_obj);
623     GNUNET_SCHEDULER_add_now (&do_error, handle);
624     return;
625   }
626   json_res = GNUNET_JSONAPI_document_get_resource (json_obj, 0);
627   if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type (json_res,
628                                                        GNUNET_REST_JSONAPI_RECLAIM_ATTRIBUTE))
629   {
630     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
631                 "Unsupported JSON data type\n");
632     GNUNET_JSONAPI_document_delete (json_obj);
633     resp = GNUNET_REST_create_response (NULL);
634     handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
635     cleanup_handle (handle);
636     return;
637   }
638   name_str = GNUNET_JSONAPI_resource_get_id (json_res);
639   exp_json = GNUNET_JSONAPI_resource_read_attr (json_res,
640                                                 "exp");
641   exp_str = json_string_value (exp_json);
642   if (NULL == exp_str) {
643     exp = GNUNET_TIME_UNIT_HOURS;
644   } else {
645     if (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_relative (exp_str,
646                                            &exp)) {
647       exp = GNUNET_TIME_UNIT_HOURS;
648     }
649   }
650
651   value_json = GNUNET_JSONAPI_resource_read_attr (json_res,
652                                                   "value");
653   value_str = json_string_value (value_json);
654   attribute = GNUNET_RECLAIM_ATTRIBUTE_claim_new (name_str,
655                                                       GNUNET_RECLAIM_ATTRIBUTE_TYPE_STRING,
656                                                       value_str,
657                                                       strlen (value_str) + 1);
658   handle->idp = GNUNET_RECLAIM_connect (cfg);
659   handle->idp_op = GNUNET_RECLAIM_attribute_store (handle->idp,
660                                                              identity_priv,
661                                                              attribute,
662                                                              &exp,
663                                                              &finished_cont,
664                                                              handle);
665   GNUNET_free (attribute);
666   GNUNET_JSONAPI_document_delete (json_obj);
667 }
668
669
670
671 /**
672  * Collect all attributes for an ego
673  *
674  */
675 static void
676 attr_collect (void *cls,
677               const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
678               const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr)
679 {
680   struct GNUNET_JSONAPI_Resource *json_resource;
681   struct RequestHandle *handle = cls;
682   json_t *value;
683   char* tmp_value;
684   
685   if ((NULL == attr->name) || (NULL == attr->data))
686   {
687     GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
688     return;
689   }
690
691   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n",
692               attr->name);
693   json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_RECLAIM_ATTRIBUTE,
694                                                attr->name);
695   GNUNET_JSONAPI_document_resource_add (handle->resp_object, json_resource);
696
697   tmp_value = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type,
698                                            attr->data,
699                                            attr->data_size);
700
701   value = json_string (tmp_value);
702
703   GNUNET_JSONAPI_resource_add_attr (json_resource,
704                                     "value",
705                                     value);
706   json_decref (value);
707   GNUNET_free(tmp_value);
708   GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
709 }
710
711
712
713 /**
714  * List attributes for identity request
715  *
716  * @param con_handle the connection handle
717  * @param url the url
718  * @param cls the RequestHandle
719  */
720 static void
721 list_attribute_cont (struct GNUNET_REST_RequestHandle *con_handle,
722                      const char* url,
723                      void *cls)
724 {
725   const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
726   struct RequestHandle *handle = cls;
727   struct EgoEntry *ego_entry;
728   char *identity;
729
730   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Getting attributes for %s.\n",
731               handle->url);
732   if ( strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) >=
733        strlen (handle->url))
734   {
735     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "No identity given.\n");
736     GNUNET_SCHEDULER_add_now (&do_error, handle);
737     return;
738   }
739   identity = handle->url + strlen (GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES) + 1;
740
741   for (ego_entry = handle->ego_head;
742        NULL != ego_entry;
743        ego_entry = ego_entry->next)
744     if (0 == strcmp (identity, ego_entry->identifier))
745       break;
746   handle->resp_object = GNUNET_JSONAPI_document_new ();
747
748
749   if (NULL == ego_entry)
750   {
751     //Done
752     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ego %s not found.\n",
753                 identity);
754     GNUNET_SCHEDULER_add_now (&return_response, handle);
755     return;
756   }
757   priv_key = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
758   handle->idp = GNUNET_RECLAIM_connect (cfg);
759   handle->attr_it = GNUNET_RECLAIM_get_attributes_start (handle->idp,
760                                                                    priv_key,
761                                                                    &collect_error_cb,
762                                                                    handle,
763                                                                    &attr_collect,
764                                                                    handle,
765                                                                    &collect_finished_cb,
766                                                                    handle);
767 }
768
769
770 static void
771 revoke_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle,
772                     const char* url,
773                     void *cls)
774 {
775   const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
776   const char* identity_str;
777   const char* audience_str;
778   const char* rnd_str;
779
780   struct RequestHandle *handle = cls;
781   struct EgoEntry *ego_entry;
782   struct MHD_Response *resp;
783   struct GNUNET_RECLAIM_Ticket ticket;
784   struct GNUNET_JSONAPI_Document *json_obj;
785   struct GNUNET_JSONAPI_Resource *json_res;
786   struct GNUNET_CRYPTO_EcdsaPublicKey tmp_pk;
787   char term_data[handle->rest_handle->data_size+1];
788   json_t *rnd_json;
789   json_t *identity_json;
790   json_t *audience_json;
791   json_t *data_json;
792   json_error_t err;
793   struct GNUNET_JSON_Specification docspec[] = {
794     GNUNET_JSON_spec_jsonapi_document (&json_obj),
795     GNUNET_JSON_spec_end()
796   };
797
798   if (0 >= handle->rest_handle->data_size)
799   {
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,
806                  handle->rest_handle->data,
807                  handle->rest_handle->data_size);
808   data_json = json_loads (term_data,
809                           JSON_DECODE_ANY,
810                           &err);
811   GNUNET_assert (GNUNET_OK ==
812                  GNUNET_JSON_parse (data_json, docspec,
813                                     NULL, NULL));
814   json_decref (data_json);
815   if (NULL == json_obj)
816   {
817     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
818                 "Unable to parse JSONAPI Object from %s\n",
819                 term_data);
820     GNUNET_SCHEDULER_add_now (&do_error, handle);
821     return;
822   }
823   if (1 != GNUNET_JSONAPI_document_resource_count (json_obj))
824   {
825     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
826                 "Cannot create more than 1 resource! (Got %d)\n",
827                 GNUNET_JSONAPI_document_resource_count (json_obj));
828     GNUNET_JSONAPI_document_delete (json_obj);
829     GNUNET_SCHEDULER_add_now (&do_error, handle);
830     return;
831   }
832   json_res = GNUNET_JSONAPI_document_get_resource (json_obj, 0);
833   if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type (json_res,
834                                                        GNUNET_REST_JSONAPI_IDENTITY_TICKET))
835   {
836     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
837                 "Unsupported JSON data type\n");
838     GNUNET_JSONAPI_document_delete (json_obj);
839     resp = GNUNET_REST_create_response (NULL);
840     handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
841     cleanup_handle (handle);
842     return;
843   }
844   rnd_json = GNUNET_JSONAPI_resource_read_attr (json_res,
845                                                 "rnd");
846   identity_json = GNUNET_JSONAPI_resource_read_attr (json_res,
847                                                      "issuer");
848   audience_json = GNUNET_JSONAPI_resource_read_attr (json_res,
849                                                      "audience");
850   rnd_str = json_string_value (rnd_json);
851   identity_str = json_string_value (identity_json);
852   audience_str = json_string_value (audience_json);
853
854   GNUNET_STRINGS_string_to_data (rnd_str,
855                                  strlen (rnd_str),
856                                  &ticket.rnd,
857                                  sizeof (uint64_t));
858   GNUNET_STRINGS_string_to_data (identity_str,
859                                  strlen (identity_str),
860                                  &ticket.identity,
861                                  sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
862   GNUNET_STRINGS_string_to_data (audience_str,
863                                  strlen (audience_str),
864                                  &ticket.audience,
865                                  sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
866
867   for (ego_entry = handle->ego_head;
868        NULL != ego_entry;
869        ego_entry = ego_entry->next)
870   {
871     GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego,
872                                         &tmp_pk);
873     if (0 == memcmp (&ticket.identity,
874                      &tmp_pk,
875                      sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
876       break;
877   }
878   if (NULL == ego_entry)
879   {
880     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
881                 "Identity unknown (%s)\n", identity_str);
882     GNUNET_JSONAPI_document_delete (json_obj);
883     return;
884   }
885   identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
886
887   handle->idp = GNUNET_RECLAIM_connect (cfg);
888   handle->idp_op = GNUNET_RECLAIM_ticket_revoke (handle->idp,
889                                                            identity_priv,
890                                                            &ticket,
891                                                            &finished_cont,
892                                                            handle);
893   GNUNET_JSONAPI_document_delete (json_obj);
894 }
895
896 static void
897 consume_cont (void *cls,
898               const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
899               const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr)
900 {
901   struct RequestHandle *handle = cls;
902   struct GNUNET_JSONAPI_Resource *json_resource;
903   json_t *value;
904
905   if (NULL == identity)
906   {
907     GNUNET_SCHEDULER_add_now (&return_response, handle);
908     return;
909   }
910
911   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Adding attribute: %s\n",
912               attr->name);
913   json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_RECLAIM_ATTRIBUTE,
914                                                attr->name);
915   GNUNET_JSONAPI_document_resource_add (handle->resp_object, json_resource);
916
917   value = json_string (attr->data);
918   GNUNET_JSONAPI_resource_add_attr (json_resource,
919                                     "value",
920                                     value);
921   json_decref (value);
922 }
923
924 static void
925 consume_ticket_cont (struct GNUNET_REST_RequestHandle *con_handle,
926                      const char* url,
927                      void *cls)
928 {
929   const struct GNUNET_CRYPTO_EcdsaPrivateKey *identity_priv;
930   const char* identity_str;
931   const char* audience_str;
932   const char* rnd_str;
933
934   struct RequestHandle *handle = cls;
935   struct EgoEntry *ego_entry;
936   struct MHD_Response *resp;
937   struct GNUNET_RECLAIM_Ticket ticket;
938   struct GNUNET_JSONAPI_Document *json_obj;
939   struct GNUNET_JSONAPI_Resource *json_res;
940   struct GNUNET_CRYPTO_EcdsaPublicKey tmp_pk;
941   char term_data[handle->rest_handle->data_size+1];
942   json_t *rnd_json;
943   json_t *identity_json;
944   json_t *audience_json;
945   json_t *data_json;
946   json_error_t err;
947   struct GNUNET_JSON_Specification docspec[] = {
948     GNUNET_JSON_spec_jsonapi_document (&json_obj),
949     GNUNET_JSON_spec_end()
950   };
951
952   if (0 >= handle->rest_handle->data_size)
953   {
954     GNUNET_SCHEDULER_add_now (&do_error, handle);
955     return;
956   }
957
958   term_data[handle->rest_handle->data_size] = '\0';
959   GNUNET_memcpy (term_data,
960                  handle->rest_handle->data,
961                  handle->rest_handle->data_size);
962   data_json = json_loads (term_data,
963                           JSON_DECODE_ANY,
964                           &err);
965   GNUNET_assert (GNUNET_OK ==
966                  GNUNET_JSON_parse (data_json, docspec,
967                                     NULL, NULL));
968   json_decref (data_json);
969   if (NULL == json_obj)
970   {
971     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
972                 "Unable to parse JSONAPI Object from %s\n",
973                 term_data);
974     GNUNET_SCHEDULER_add_now (&do_error, handle);
975     return;
976   }
977   if (1 != GNUNET_JSONAPI_document_resource_count (json_obj))
978   {
979     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
980                 "Cannot create more than 1 resource! (Got %d)\n",
981                 GNUNET_JSONAPI_document_resource_count (json_obj));
982     GNUNET_JSONAPI_document_delete (json_obj);
983     GNUNET_SCHEDULER_add_now (&do_error, handle);
984     return;
985   }
986   json_res = GNUNET_JSONAPI_document_get_resource (json_obj, 0);
987   if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type (json_res,
988                                                        GNUNET_REST_JSONAPI_IDENTITY_TICKET))
989   {
990     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
991                 "Unsupported JSON data type\n");
992     GNUNET_JSONAPI_document_delete (json_obj);
993     resp = GNUNET_REST_create_response (NULL);
994     handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
995     cleanup_handle (handle);
996     return;
997   }
998   rnd_json = GNUNET_JSONAPI_resource_read_attr (json_res,
999                                                 "rnd");
1000   identity_json = GNUNET_JSONAPI_resource_read_attr (json_res,
1001                                                      "identity");
1002   audience_json = GNUNET_JSONAPI_resource_read_attr (json_res,
1003                                                      "audience");
1004   rnd_str = json_string_value (rnd_json);
1005   identity_str = json_string_value (identity_json);
1006   audience_str = json_string_value (audience_json);
1007
1008   GNUNET_STRINGS_string_to_data (rnd_str,
1009                                  strlen (rnd_str),
1010                                  &ticket.rnd,
1011                                  sizeof (uint64_t));
1012   GNUNET_STRINGS_string_to_data (identity_str,
1013                                  strlen (identity_str),
1014                                  &ticket.identity,
1015                                  sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1016   GNUNET_STRINGS_string_to_data (audience_str,
1017                                  strlen (audience_str),
1018                                  &ticket.audience,
1019                                  sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1020
1021   for (ego_entry = handle->ego_head;
1022        NULL != ego_entry;
1023        ego_entry = ego_entry->next)
1024   {
1025     GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego,
1026                                         &tmp_pk);
1027     if (0 == memcmp (&ticket.audience,
1028                      &tmp_pk,
1029                      sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
1030       break;
1031   }
1032   if (NULL == ego_entry)
1033   {
1034     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1035                 "Identity unknown (%s)\n", identity_str);
1036     GNUNET_JSONAPI_document_delete (json_obj);
1037     return;
1038   }
1039   identity_priv = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1040   handle->resp_object = GNUNET_JSONAPI_document_new ();
1041   handle->idp = GNUNET_RECLAIM_connect (cfg);
1042   handle->idp_op = GNUNET_RECLAIM_ticket_consume (handle->idp,
1043                                                             identity_priv,
1044                                                             &ticket,
1045                                                             &consume_cont,
1046                                                             handle);
1047   GNUNET_JSONAPI_document_delete (json_obj);
1048 }
1049
1050
1051
1052 /**
1053  * Respond to OPTIONS request
1054  *
1055  * @param con_handle the connection handle
1056  * @param url the url
1057  * @param cls the RequestHandle
1058  */
1059 static void
1060 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
1061               const char* url,
1062               void *cls)
1063 {
1064   struct MHD_Response *resp;
1065   struct RequestHandle *handle = cls;
1066
1067   //For now, independent of path return all options
1068   resp = GNUNET_REST_create_response (NULL);
1069   MHD_add_response_header (resp,
1070                            "Access-Control-Allow-Methods",
1071                            allow_methods);
1072   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1073   cleanup_handle (handle);
1074   return;
1075 }
1076
1077 /**
1078  * Handle rest request
1079  *
1080  * @param handle the request handle
1081  */
1082 static void
1083 init_cont (struct RequestHandle *handle)
1084 {
1085   struct GNUNET_REST_RequestHandlerError err;
1086   static const struct GNUNET_REST_RequestHandler handlers[] = {
1087     {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES, &list_attribute_cont},
1088     {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_RECLAIM_ATTRIBUTES, &add_attribute_cont},
1089     {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY_TICKETS, &list_tickets_cont},
1090     {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_REVOKE, &revoke_ticket_cont},
1091     {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY_CONSUME, &consume_ticket_cont},
1092     {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_RECLAIM,
1093       &options_cont},
1094     GNUNET_REST_HANDLER_END
1095   };
1096
1097   if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle,
1098                                                handlers,
1099                                                &err,
1100                                                handle))
1101   {
1102     handle->response_code = err.error_code;
1103     GNUNET_SCHEDULER_add_now (&do_error, handle);
1104   }
1105 }
1106
1107 /**
1108  * If listing is enabled, prints information about the egos.
1109  *
1110  * This function is initially called for all egos and then again
1111  * whenever a ego's identifier changes or if it is deleted.  At the
1112  * end of the initial pass over all egos, the function is once called
1113  * with 'NULL' for 'ego'. That does NOT mean that the callback won't
1114  * be invoked in the future or that there was an error.
1115  *
1116  * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
1117  * this function is only called ONCE, and 'NULL' being passed in
1118  * 'ego' does indicate an error (i.e. name is taken or no default
1119  * value is known).  If 'ego' is non-NULL and if '*ctx'
1120  * is set in those callbacks, the value WILL be passed to a subsequent
1121  * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
1122  * that one was not NULL).
1123  *
1124  * When an identity is renamed, this function is called with the
1125  * (known) ego but the NEW identifier.
1126  *
1127  * When an identity is deleted, this function is called with the
1128  * (known) ego and "NULL" for the 'identifier'.  In this case,
1129  * the 'ego' is henceforth invalid (and the 'ctx' should also be
1130  * cleaned up).
1131  *
1132  * @param cls closure
1133  * @param ego ego handle
1134  * @param ctx context for application to store data for this ego
1135  *                 (during the lifetime of this process, initially NULL)
1136  * @param identifier identifier assigned by the user for this ego,
1137  *                   NULL if the user just deleted the ego and it
1138  *                   must thus no longer be used
1139  */
1140 static void
1141 list_ego (void *cls,
1142           struct GNUNET_IDENTITY_Ego *ego,
1143           void **ctx,
1144           const char *identifier)
1145 {
1146   struct RequestHandle *handle = cls;
1147   struct EgoEntry *ego_entry;
1148   struct GNUNET_CRYPTO_EcdsaPublicKey pk;
1149
1150   if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
1151   {
1152     handle->state = ID_REST_STATE_POST_INIT;
1153     init_cont (handle);
1154     return;
1155   }
1156   if (ID_REST_STATE_INIT == handle->state) {
1157     ego_entry = GNUNET_new (struct EgoEntry);
1158     GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1159     ego_entry->keystring =
1160       GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
1161     ego_entry->ego = ego;
1162     ego_entry->identifier = GNUNET_strdup (identifier);
1163     GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
1164   }
1165
1166 }
1167
1168 static void
1169 rest_identity_process_request(struct GNUNET_REST_RequestHandle *rest_handle,
1170                               GNUNET_REST_ResultProcessor proc,
1171                               void *proc_cls)
1172 {
1173   struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
1174   handle->response_code = 0;
1175   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
1176   handle->proc_cls = proc_cls;
1177   handle->proc = proc;
1178   handle->state = ID_REST_STATE_INIT;
1179   handle->rest_handle = rest_handle;
1180
1181   handle->url = GNUNET_strdup (rest_handle->url);
1182   if (handle->url[strlen (handle->url)-1] == '/')
1183     handle->url[strlen (handle->url)-1] = '\0';
1184   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1185               "Connecting...\n");
1186   handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
1187                                                      &list_ego,
1188                                                      handle);
1189   handle->namestore_handle = GNUNET_NAMESTORE_connect (cfg);
1190   handle->timeout_task =
1191     GNUNET_SCHEDULER_add_delayed (handle->timeout,
1192                                   &do_timeout,
1193                                   handle);
1194   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1195               "Connected\n");
1196 }
1197
1198 /**
1199  * Entry point for the plugin.
1200  *
1201  * @param cls Config info
1202  * @return NULL on error, otherwise the plugin context
1203  */
1204 void *
1205 libgnunet_plugin_rest_reclaim_init (void *cls)
1206 {
1207   static struct Plugin plugin;
1208   struct GNUNET_REST_Plugin *api;
1209
1210   cfg = cls;
1211   if (NULL != plugin.cfg)
1212     return NULL;                /* can only initialize once! */
1213   memset (&plugin, 0, sizeof (struct Plugin));
1214   plugin.cfg = cfg;
1215   api = GNUNET_new (struct GNUNET_REST_Plugin);
1216   api->cls = &plugin;
1217   api->name = GNUNET_REST_API_NS_RECLAIM;
1218   api->process_request = &rest_identity_process_request;
1219   GNUNET_asprintf (&allow_methods,
1220                    "%s, %s, %s, %s, %s",
1221                    MHD_HTTP_METHOD_GET,
1222                    MHD_HTTP_METHOD_POST,
1223                    MHD_HTTP_METHOD_PUT,
1224                    MHD_HTTP_METHOD_DELETE,
1225                    MHD_HTTP_METHOD_OPTIONS);
1226
1227   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1228               _("Identity Provider REST API initialized\n"));
1229   return api;
1230 }
1231
1232
1233 /**
1234  * Exit point from the plugin.
1235  *
1236  * @param cls the plugin context (as returned by "init")
1237  * @return always NULL
1238  */
1239 void *
1240 libgnunet_plugin_rest_reclaim_done (void *cls)
1241 {
1242   struct GNUNET_REST_Plugin *api = cls;
1243   struct Plugin *plugin = api->cls;
1244   plugin->cfg = NULL;
1245
1246   GNUNET_free_non_null (allow_methods);
1247   GNUNET_free (api);
1248   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1249               "Identity Provider REST plugin is finished\n");
1250   return NULL;
1251 }
1252
1253 /* end of plugin_rest_reclaim.c */