reduce loop counters to more practical levels
[oweals/gnunet.git] / src / identity / plugin_rest_identity.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
6    it under the terms of the GNU General Public License as published
7    by the Free Software Foundation; either version 3, or (at your
8    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    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with GNUnet; see the file COPYING.  If not, write to the
17    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18    Boston, MA 02110-1301, USA.
19    */
20 /**
21  * @author Martin Schanzenbach
22  * @file identity/plugin_rest_identity.c
23  * @brief GNUnet Namestore REST plugin
24  *
25  */
26
27 #include "platform.h"
28 #include "gnunet_rest_plugin.h"
29 #include "gnunet_identity_service.h"
30 #include "gnunet_rest_lib.h"
31 #include "gnunet_jsonapi_lib.h"
32 #include "gnunet_jsonapi_util.h"
33 #include "microhttpd.h"
34 #include <jansson.h>
35 #include "gnunet_signatures.h"
36
37 /**
38  * REST root namespace
39  */
40 #define GNUNET_REST_API_NS_IDENTITY "/identity"
41
42 /**
43  * State while collecting all egos
44  */
45 #define ID_REST_STATE_INIT 0
46
47 /**
48  * Done collecting egos
49  */
50 #define ID_REST_STATE_POST_INIT 1
51
52 /**
53  * Resource type
54  */
55 #define GNUNET_REST_JSONAPI_IDENTITY_EGO "ego"
56
57 /**
58  * Name attribute
59  */
60 #define GNUNET_REST_JSONAPI_IDENTITY_NAME "name"
61
62 /**
63  * Attribute to rename "name" TODO we changed id to the pubkey
64  * so this can be unified with "name"
65  */
66 #define GNUNET_REST_JSONAPI_IDENTITY_NEWNAME "newname"
67
68 /**
69  * URL parameter to change the subsytem for ego
70  */
71 #define GNUNET_REST_JSONAPI_IDENTITY_SUBSYSTEM "subsystem"
72
73
74 /**
75  * Error messages
76  */
77 #define GNUNET_REST_ERROR_RESOURCE_INVALID "Resource location invalid"
78 #define GNUNET_REST_ERROR_NO_DATA "No data"
79
80 /**
81  * GNUid token lifetime
82  */
83 #define GNUNET_GNUID_TOKEN_EXPIRATION_MICROSECONDS 300000000
84
85 /**
86  * The configuration handle
87  */
88 const struct GNUNET_CONFIGURATION_Handle *cfg;
89
90 /**
91  * HTTP methods allows for this plugin
92  */
93 static char* allow_methods;
94
95 /**
96  * @brief struct returned by the initialization function of the plugin
97  */
98 struct Plugin
99 {
100   const struct GNUNET_CONFIGURATION_Handle *cfg;
101 };
102
103 /**
104  * The ego list
105  */
106 struct EgoEntry
107 {
108   /**
109    * DLL
110    */
111   struct EgoEntry *next;
112
113   /**
114    * DLL
115    */
116   struct EgoEntry *prev;
117
118   /**
119    * Ego Identifier
120    */
121   char *identifier;
122
123   /**
124    * Public key string
125    */
126   char *keystring;
127
128   /**
129    * The Ego
130    */
131   struct GNUNET_IDENTITY_Ego *ego;
132 };
133
134
135 struct RequestHandle
136 {
137   /**
138    * Ego list
139    */
140   struct EgoEntry *ego_head;
141
142   /**
143    * Ego list
144    */
145   struct EgoEntry *ego_tail;
146
147   /**
148    * Handle to the rest connection
149    */
150   struct GNUNET_REST_RequestHandle *conndata_handle;
151
152   /**
153    * response code
154    */
155   int response_code;
156
157   /**
158    * The processing state
159    */
160   int state;
161
162   /**
163    * Handle to GNS service.
164    */
165   struct GNUNET_IDENTITY_Handle *identity_handle;
166
167   /**
168    * IDENTITY Operation
169    */
170   struct GNUNET_IDENTITY_Operation *op;
171
172   /**
173    * Desired timeout for the lookup (default is no timeout).
174    */
175   struct GNUNET_TIME_Relative timeout;
176
177   /**
178    * ID of a task associated with the resolution process.
179    */
180   struct GNUNET_SCHEDULER_Task * timeout_task;
181
182   /**
183    * The plugin result processor
184    */
185   GNUNET_REST_ResultProcessor proc;
186
187   /**
188    * The closure of the result processor
189    */
190   void *proc_cls;
191
192   /**
193    * The name to look up
194    */
195   char *name;
196
197   /**
198    * The subsystem set from REST
199    */
200   char *subsys;
201
202   /**
203    * The url
204    */
205   char *url;
206
207   /**
208    * The data from the REST request
209    */
210   const char* data;
211
212   /**
213    * the length of the REST data
214    */
215   size_t data_size;
216
217   /**
218    * HTTP method
219    */
220   const char* method;
221
222   /**
223    * Error response message
224    */
225   char *emsg;
226
227 };
228
229
230 /**
231  * Cleanup lookup handle
232  * @param handle Handle to clean up
233  */
234 static void
235 cleanup_handle (struct RequestHandle *handle)
236 {
237   struct EgoEntry *ego_entry;
238   struct EgoEntry *ego_tmp;
239   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
240               "Cleaning up\n");
241   if (NULL != handle->name)
242     GNUNET_free (handle->name);
243   if (NULL != handle->timeout_task)
244   {
245     GNUNET_SCHEDULER_cancel (handle->timeout_task);
246     handle->timeout_task = NULL;
247   }
248   if (NULL != handle->identity_handle)
249     GNUNET_IDENTITY_disconnect (handle->identity_handle);
250   if (NULL != handle->subsys)
251     GNUNET_free (handle->subsys);
252   if (NULL != handle->url)
253     GNUNET_free (handle->url);
254   if (NULL != handle->emsg)
255     GNUNET_free (handle->emsg);
256   for (ego_entry = handle->ego_head;
257        NULL != ego_entry;)
258   {
259     ego_tmp = ego_entry;
260     ego_entry = ego_entry->next;
261     GNUNET_free (ego_tmp->identifier);
262     GNUNET_free (ego_tmp->keystring);
263     GNUNET_free (ego_tmp);
264   }
265   GNUNET_free (handle);
266 }
267
268
269 /**
270  * Task run on errors.  Reports an error and cleans up everything.
271  *
272  * @param cls the `struct RequestHandle`
273  */
274 static void
275 do_error (void *cls)
276 {
277   struct RequestHandle *handle = cls;
278   struct MHD_Response *resp;
279   char *json_error;
280
281   GNUNET_asprintf (&json_error,
282                    "{Error while processing request: %s}",
283                    &handle->emsg);
284
285   resp = GNUNET_REST_create_response (json_error);
286   handle->proc (handle->proc_cls,
287                 resp,
288                 handle->response_code);
289   cleanup_handle (handle);
290   GNUNET_free (json_error);
291 }
292
293
294 /**
295  * Callback for IDENTITY_get()
296  *
297  * @param cls the RequestHandle
298  * @param ego the Ego found
299  * @param ctx the context
300  * @param name the id of the ego
301  */
302 static void
303 get_ego_for_subsys (void *cls,
304                     struct GNUNET_IDENTITY_Ego *ego,
305                     void **ctx,
306                     const char *name)
307 {
308   struct RequestHandle *handle = cls;
309   struct GNUNET_JSONAPI_Document *json_document;
310   struct GNUNET_JSONAPI_Resource *json_resource;
311   struct EgoEntry *ego_entry;
312   struct MHD_Response *resp;
313   json_t *name_json;
314   char *result_str;
315
316   json_document = GNUNET_JSONAPI_document_new ();
317
318   for (ego_entry = handle->ego_head;
319        NULL != ego_entry;
320        ego_entry = ego_entry->next)
321   {
322     if ( (NULL != name) && (0 != strcmp (name, ego_entry->identifier)) )
323       continue;
324     if (NULL == name)
325       continue;
326     json_resource = GNUNET_JSONAPI_resource_new
327       (GNUNET_REST_JSONAPI_IDENTITY_EGO, ego_entry->keystring);
328     name_json = json_string (ego_entry->identifier);
329     GNUNET_JSONAPI_resource_add_attr (json_resource,
330                                            GNUNET_REST_JSONAPI_IDENTITY_NAME,
331                                            name_json);
332     json_decref (name_json);
333     GNUNET_JSONAPI_document_resource_add (json_document, json_resource);
334     break;
335   }
336   if (0 == GNUNET_JSONAPI_document_resource_count (json_document))
337   {
338     GNUNET_JSONAPI_document_delete (json_document);
339     handle->emsg = GNUNET_strdup("No identity matches results!");
340     GNUNET_SCHEDULER_add_now (&do_error, handle);
341     return;
342   }
343   GNUNET_JSONAPI_document_serialize (json_document, &result_str);
344   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
345   resp = GNUNET_REST_create_response (result_str);
346   GNUNET_JSONAPI_document_delete (json_document);
347   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
348   GNUNET_free (result_str);
349   cleanup_handle (handle);
350 }
351
352 /**
353  * Create a response with requested ego(s)
354  *
355  * @param con the Rest handle
356  * @param url the requested url
357  * @param cls the request handle
358  */
359 static void
360 ego_info_response (struct GNUNET_REST_RequestHandle *con,
361                    const char *url,
362                    void *cls)
363 {
364   const char *egoname;
365   char *result_str;
366   char *subsys_val;
367   char *keystring;
368   struct RequestHandle *handle = cls;
369   struct EgoEntry *ego_entry;
370   struct GNUNET_HashCode key;
371   struct MHD_Response *resp;
372   struct GNUNET_JSONAPI_Document *json_document;
373   struct GNUNET_JSONAPI_Resource *json_resource;
374   json_t *name_str;
375
376   if (GNUNET_NO == GNUNET_REST_namespace_match (handle->url, GNUNET_REST_API_NS_IDENTITY))
377   {
378     resp = GNUNET_REST_create_response (NULL);
379     handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
380     cleanup_handle (handle);
381     return;
382   }
383   egoname = NULL;
384   keystring = NULL;
385   if (strlen (GNUNET_REST_API_NS_IDENTITY) < strlen (handle->url))
386   {
387     keystring = &handle->url[strlen (GNUNET_REST_API_NS_IDENTITY)+1];
388     //Return all egos
389     for (ego_entry = handle->ego_head;
390          NULL != ego_entry;
391          ego_entry = ego_entry->next)
392     {
393       if ( (NULL != keystring) && (0 != strcmp (keystring, ego_entry->keystring)) )
394         continue;
395       egoname = ego_entry->identifier;
396     }
397   }
398
399   if ( NULL == egoname ) {
400     GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_IDENTITY_SUBSYSTEM,
401                         strlen (GNUNET_REST_JSONAPI_IDENTITY_SUBSYSTEM),
402                         &key);
403     if ( GNUNET_YES ==
404          GNUNET_CONTAINER_multihashmap_contains (handle->conndata_handle->url_param_map,
405                                                  &key) )
406     {
407       subsys_val = GNUNET_CONTAINER_multihashmap_get (handle->conndata_handle->url_param_map,
408                                                       &key);
409       if (NULL != subsys_val)
410       {
411         GNUNET_asprintf (&handle->subsys, "%s", subsys_val);
412         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Looking for %s's ego\n", subsys_val);
413         handle->op = GNUNET_IDENTITY_get (handle->identity_handle,
414                                           handle->subsys,
415                                           &get_ego_for_subsys,
416                                           handle);
417         return;
418       }
419     }
420   }
421
422   json_document = GNUNET_JSONAPI_document_new ();
423
424   //Return all egos
425   for (ego_entry = handle->ego_head;
426        NULL != ego_entry;
427        ego_entry = ego_entry->next)
428   {
429     if ( (NULL != egoname) && (0 != strcmp (egoname, ego_entry->identifier)) )
430       continue;
431     json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_IDENTITY_EGO,
432                                                       ego_entry->keystring);
433     name_str = json_string (ego_entry->identifier);
434     GNUNET_JSONAPI_resource_add_attr (
435                                            json_resource,
436                                            GNUNET_REST_JSONAPI_IDENTITY_NAME,
437                                            name_str);
438     json_decref (name_str);
439     GNUNET_JSONAPI_document_resource_add (json_document, json_resource);
440   }
441   if (0 == GNUNET_JSONAPI_document_resource_count (json_document))
442   {
443     GNUNET_JSONAPI_document_delete (json_document);
444     handle->emsg = GNUNET_strdup ("No identities found!");
445     GNUNET_SCHEDULER_add_now (&do_error, handle);
446     return;
447   }
448   GNUNET_JSONAPI_document_serialize (json_document, &result_str);
449   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
450   resp = GNUNET_REST_create_response (result_str);
451   GNUNET_JSONAPI_document_delete (json_document);
452   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
453   GNUNET_free (result_str);
454   cleanup_handle (handle);
455 }
456
457 /**
458  * Processing finished
459  *
460  * @param cls request handle
461  * @param emsg error message
462  */
463 static void
464 do_finished (void *cls, const char *emsg)
465 {
466   struct RequestHandle *handle = cls;
467   struct MHD_Response *resp;
468
469   handle->op = NULL;
470   if (NULL != emsg)
471   {
472     handle->emsg = GNUNET_strdup (emsg);
473     GNUNET_SCHEDULER_add_now (&do_error, handle);
474     return;
475   }
476   resp = GNUNET_REST_create_response (NULL);
477   handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
478   cleanup_handle (handle);
479 }
480
481 /**
482  * Create a new ego
483  *
484  * @param con rest handle
485  * @param url url
486  * @param cls request handle
487  */
488 static void
489 ego_create_cont (struct GNUNET_REST_RequestHandle *con,
490                  const char *url,
491                  void *cls)
492 {
493   struct RequestHandle *handle = cls;
494   struct EgoEntry *ego_entry;
495   struct MHD_Response *resp;
496   struct GNUNET_JSONAPI_Document *json_obj;
497   struct GNUNET_JSONAPI_Resource *json_res;
498   json_t *egoname_json;
499   json_t *data_js;
500   json_error_t err;
501   const char* egoname;
502   char term_data[handle->data_size+1];
503   struct GNUNET_JSON_Specification docspec[] = {
504     GNUNET_JSON_spec_jsonapi_document (&json_obj),
505     GNUNET_JSON_spec_end()
506   };
507   if (strlen (GNUNET_REST_API_NS_IDENTITY) != strlen (handle->url))
508   {
509     handle->emsg = GNUNET_strdup (GNUNET_REST_ERROR_RESOURCE_INVALID);
510     GNUNET_SCHEDULER_add_now (&do_error, handle);
511     return;
512   }
513   if (0 >= handle->data_size)
514   {
515     handle->emsg = GNUNET_strdup (GNUNET_REST_ERROR_NO_DATA);
516     GNUNET_SCHEDULER_add_now (&do_error, handle);
517     return;
518   }
519   term_data[handle->data_size] = '\0';
520   GNUNET_memcpy (term_data, handle->data, handle->data_size);
521   data_js = json_loads (term_data,
522                         JSON_DECODE_ANY,
523                         &err);
524   GNUNET_assert (NULL != data_js);
525   GNUNET_assert (GNUNET_OK ==
526                  GNUNET_JSON_parse (data_js, docspec,
527                                     NULL, NULL));
528
529   json_decref (data_js);
530
531   if (NULL == json_obj)
532   {
533     GNUNET_SCHEDULER_add_now (&do_error, handle);
534     return;
535   }
536   if (1 != GNUNET_JSONAPI_document_resource_count (json_obj))
537   {
538     GNUNET_JSONAPI_document_delete (json_obj);
539     handle->emsg = GNUNET_strdup ("Provided resource count invalid");
540     GNUNET_SCHEDULER_add_now (&do_error, handle);
541     return;
542   }
543   json_res = GNUNET_JSONAPI_document_get_resource (json_obj, 0);
544   if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type (json_res, GNUNET_REST_JSONAPI_IDENTITY_EGO))
545   {
546     GNUNET_JSONAPI_document_delete (json_obj);
547     resp = GNUNET_REST_create_response (NULL);
548     handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
549     cleanup_handle (handle);
550     return;
551   }
552   egoname_json = GNUNET_JSONAPI_resource_read_attr (json_res, GNUNET_REST_JSONAPI_IDENTITY_NAME);
553   if (!json_is_string (egoname_json))
554   {
555     GNUNET_JSONAPI_document_delete (json_obj);
556     handle->emsg = GNUNET_strdup ("No name provided");
557     GNUNET_SCHEDULER_add_now (&do_error, handle);
558     return;
559   }
560   egoname = json_string_value (egoname_json);
561   for (ego_entry = handle->ego_head;
562        NULL != ego_entry;
563        ego_entry = ego_entry->next)
564   {
565     if (0 == strcasecmp (egoname, ego_entry->identifier))
566     {
567       GNUNET_JSONAPI_document_delete (json_obj);
568       resp = GNUNET_REST_create_response (NULL);
569       handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
570       cleanup_handle (handle);
571       return;
572     }
573   }
574   GNUNET_asprintf (&handle->name, "%s", egoname);
575   GNUNET_JSONAPI_document_delete (json_obj);
576   handle->op = GNUNET_IDENTITY_create (handle->identity_handle,
577                                        handle->name,
578                                        &do_finished,
579                                        handle);
580 }
581
582
583 /**
584  * Handle ego edit request
585  *
586  * @param con rest connection handle
587  * @param url the url that is requested
588  * @param cls the RequestHandle
589  */
590 static void
591 ego_edit_cont (struct GNUNET_REST_RequestHandle *con,
592                const char *url,
593                void *cls)
594 {
595   struct GNUNET_JSONAPI_Document *json_obj;
596   struct GNUNET_JSONAPI_Resource *json_res;
597   struct RequestHandle *handle = cls;
598   struct EgoEntry *ego_entry;
599   struct EgoEntry *ego_entry_tmp;
600   struct MHD_Response *resp;
601   json_t *subsys_json;
602   json_t *name_json;
603   json_t *data_js;
604   json_error_t err;
605   const char *keystring;
606   const char *subsys;
607   const char *newname;
608   char term_data[handle->data_size+1];
609   int ego_exists = GNUNET_NO;
610   struct GNUNET_JSON_Specification docspec[] = {
611     GNUNET_JSON_spec_jsonapi_document (&json_obj),
612     GNUNET_JSON_spec_end()
613   };
614
615   if (strlen (GNUNET_REST_API_NS_IDENTITY) > strlen (handle->url))
616   {
617     handle->emsg = GNUNET_strdup (GNUNET_REST_ERROR_RESOURCE_INVALID);
618     GNUNET_SCHEDULER_add_now (&do_error, handle);
619     return;
620   }
621
622   keystring = &handle->url[strlen(GNUNET_REST_API_NS_IDENTITY)+1];
623
624   for (ego_entry = handle->ego_head;
625        NULL != ego_entry;
626        ego_entry = ego_entry->next)
627   {
628     if (0 != strcasecmp (keystring, ego_entry->keystring))
629       continue;
630     ego_exists = GNUNET_YES;
631     break;
632   }
633
634   if (GNUNET_NO == ego_exists)
635   {
636     resp = GNUNET_REST_create_response (NULL);
637     handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND);
638     cleanup_handle (handle);
639     return;
640   }
641
642   if (0 >= handle->data_size)
643   {
644     handle->emsg = GNUNET_strdup (GNUNET_REST_ERROR_NO_DATA);
645     GNUNET_SCHEDULER_add_now (&do_error, handle);
646     return;
647   }
648
649   term_data[handle->data_size] = '\0';
650   GNUNET_memcpy (term_data, handle->data, handle->data_size);
651   data_js = json_loads (term_data,
652                         JSON_DECODE_ANY,
653                         &err);
654   GNUNET_assert (NULL != data_js);
655   GNUNET_assert (GNUNET_OK ==
656                  GNUNET_JSON_parse (data_js, docspec,
657                                     NULL, NULL));
658
659   json_decref (data_js);
660
661   if (NULL == json_obj)
662   {
663     handle->emsg = GNUNET_strdup ("Data invalid");
664     GNUNET_SCHEDULER_add_now (&do_error, handle);
665     return;
666   }
667
668   if (1 != GNUNET_JSONAPI_document_resource_count (json_obj))
669   {
670     GNUNET_JSONAPI_document_delete (json_obj);
671     handle->emsg = GNUNET_strdup ("Resource amount invalid");
672     GNUNET_SCHEDULER_add_now (&do_error, handle);
673     return;
674   }
675   json_res = GNUNET_JSONAPI_document_get_resource (json_obj, 0);
676
677   if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type (json_res, GNUNET_REST_JSONAPI_IDENTITY_EGO))
678   {
679     GNUNET_JSONAPI_document_delete (json_obj);
680     handle->emsg = GNUNET_strdup ("Resource type invalid");
681     GNUNET_SCHEDULER_add_now (&do_error, handle);
682     return;
683   }
684
685   //This is a rename
686   name_json = GNUNET_JSONAPI_resource_read_attr (json_res,
687                                                  GNUNET_REST_JSONAPI_IDENTITY_NEWNAME);
688   if ((NULL != name_json) && json_is_string (name_json))
689   {
690     newname = json_string_value (name_json);
691     for (ego_entry_tmp = handle->ego_head;
692          NULL != ego_entry_tmp;
693          ego_entry_tmp = ego_entry_tmp->next)
694     {
695       if (0 == strcasecmp (newname, ego_entry_tmp->identifier) &&
696           0 != strcasecmp (keystring, ego_entry_tmp->keystring))
697       {
698         //Ego with same name not allowed
699         GNUNET_JSONAPI_document_delete (json_obj);
700         resp = GNUNET_REST_create_response (NULL);
701         handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
702         cleanup_handle (handle);
703         return;
704       }
705     }
706     handle->op = GNUNET_IDENTITY_rename (handle->identity_handle,
707                                          ego_entry->identifier,
708                                          newname,
709                                          &do_finished,
710                                          handle);
711     GNUNET_JSONAPI_document_delete (json_obj);
712     return;
713   }
714
715   //Set subsystem
716   subsys_json = GNUNET_JSONAPI_resource_read_attr (json_res, GNUNET_REST_JSONAPI_IDENTITY_SUBSYSTEM);
717   if ( (NULL != subsys_json) && json_is_string (subsys_json))
718   {
719     subsys = json_string_value (subsys_json);
720     GNUNET_asprintf (&handle->subsys, "%s", subsys);
721     GNUNET_JSONAPI_document_delete (json_obj);
722     handle->op = GNUNET_IDENTITY_set (handle->identity_handle,
723                                       handle->subsys,
724                                       ego_entry->ego,
725                                       &do_finished,
726                                       handle);
727     return;
728   }
729   GNUNET_JSONAPI_document_delete (json_obj);
730   handle->emsg = GNUNET_strdup ("Subsystem not provided");
731   GNUNET_SCHEDULER_add_now (&do_error, handle);
732 }
733
734 void
735 ego_delete_cont (struct GNUNET_REST_RequestHandle *con_handle,
736                  const char* url,
737                  void *cls)
738 {
739   const char *keystring;
740   struct EgoEntry *ego_entry;
741   struct MHD_Response *resp;
742   struct RequestHandle *handle = cls;
743   int ego_exists = GNUNET_NO;
744
745   if (strlen (GNUNET_REST_API_NS_IDENTITY) >= strlen (handle->url))
746   {
747     handle->emsg = GNUNET_strdup (GNUNET_REST_ERROR_RESOURCE_INVALID);
748     GNUNET_SCHEDULER_add_now (&do_error, handle);
749     return;
750   }
751
752   keystring = &handle->url[strlen(GNUNET_REST_API_NS_IDENTITY)+1];
753   for (ego_entry = handle->ego_head;
754        NULL != ego_entry;
755        ego_entry = ego_entry->next)
756   {
757     if (0 != strcasecmp (keystring, ego_entry->keystring))
758       continue;
759     ego_exists = GNUNET_YES;
760     break;
761   }
762   if (GNUNET_NO == ego_exists)
763   {
764     resp = GNUNET_REST_create_response (NULL);
765     handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND);
766     cleanup_handle (handle);
767     return;
768   }
769   handle->op = GNUNET_IDENTITY_delete (handle->identity_handle,
770                                        ego_entry->identifier,
771                                        &do_finished,
772                                        handle);
773
774 }
775
776
777 /**
778  * Respond to OPTIONS request
779  *
780  * @param con_handle the connection handle
781  * @param url the url
782  * @param cls the RequestHandle
783  */
784 static void
785 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
786               const char* url,
787               void *cls)
788 {
789   struct MHD_Response *resp;
790   struct RequestHandle *handle = cls;
791
792   //For now, independent of path return all options
793   resp = GNUNET_REST_create_response (NULL);
794   MHD_add_response_header (resp,
795                            "Access-Control-Allow-Methods",
796                            allow_methods);
797   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
798   cleanup_handle (handle);
799   return;
800 }
801
802 /**
803  * Handle rest request
804  *
805  * @param handle the request handle
806  */
807 static void
808 init_cont (struct RequestHandle *handle)
809 {
810   struct GNUNET_REST_RequestHandlerError err;
811   static const struct GNUNET_REST_RequestHandler handlers[] = {
812     {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_IDENTITY, &ego_info_response},
813     {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_IDENTITY, &ego_create_cont},
814     {MHD_HTTP_METHOD_PUT, GNUNET_REST_API_NS_IDENTITY, &ego_edit_cont},
815     {MHD_HTTP_METHOD_DELETE, GNUNET_REST_API_NS_IDENTITY, &ego_delete_cont},
816     {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_IDENTITY, &options_cont},
817     GNUNET_REST_HANDLER_END
818   };
819
820   if (GNUNET_NO == GNUNET_JSONAPI_handle_request (handle->conndata_handle,
821                                                   handlers,
822                                                   &err,
823                                                   handle))
824   {
825     handle->response_code = err.error_code;
826     GNUNET_SCHEDULER_add_now (&do_error, handle);
827   }
828 }
829
830 /**
831  * If listing is enabled, prints information about the egos.
832  *
833  * This function is initially called for all egos and then again
834  * whenever a ego's identifier changes or if it is deleted.  At the
835  * end of the initial pass over all egos, the function is once called
836  * with 'NULL' for 'ego'. That does NOT mean that the callback won't
837  * be invoked in the future or that there was an error.
838  *
839  * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
840  * this function is only called ONCE, and 'NULL' being passed in
841  * 'ego' does indicate an error (i.e. name is taken or no default
842  * value is known).  If 'ego' is non-NULL and if '*ctx'
843  * is set in those callbacks, the value WILL be passed to a subsequent
844  * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
845  * that one was not NULL).
846  *
847  * When an identity is renamed, this function is called with the
848  * (known) ego but the NEW identifier.
849  *
850  * When an identity is deleted, this function is called with the
851  * (known) ego and "NULL" for the 'identifier'.  In this case,
852  * the 'ego' is henceforth invalid (and the 'ctx' should also be
853  * cleaned up).
854  *
855  * @param cls closure
856  * @param ego ego handle
857  * @param ctx context for application to store data for this ego
858  *                 (during the lifetime of this process, initially NULL)
859  * @param identifier identifier assigned by the user for this ego,
860  *                   NULL if the user just deleted the ego and it
861  *                   must thus no longer be used
862  */
863 static void
864 list_ego (void *cls,
865           struct GNUNET_IDENTITY_Ego *ego,
866           void **ctx,
867           const char *identifier)
868 {
869   struct RequestHandle *handle = cls;
870   struct EgoEntry *ego_entry;
871   struct GNUNET_CRYPTO_EcdsaPublicKey pk;
872
873   if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
874   {
875     handle->state = ID_REST_STATE_POST_INIT;
876     init_cont (handle);
877     return;
878   }
879   if (ID_REST_STATE_INIT == handle->state) {
880     ego_entry = GNUNET_new (struct EgoEntry);
881     GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
882     ego_entry->keystring =
883       GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
884     ego_entry->ego = ego;
885     GNUNET_asprintf (&ego_entry->identifier, "%s", identifier);
886     GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
887   }
888
889 }
890
891 /**
892  * Function processing the REST call
893  *
894  * @param method HTTP method
895  * @param url URL of the HTTP request
896  * @param data body of the HTTP request (optional)
897  * @param data_size length of the body
898  * @param proc callback function for the result
899  * @param proc_cls closure for callback function
900  * @return GNUNET_OK if request accepted
901  */
902 static void
903 rest_identity_process_request(struct GNUNET_REST_RequestHandle *conndata_handle,
904                               GNUNET_REST_ResultProcessor proc,
905                               void *proc_cls)
906 {
907   struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
908
909
910
911   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
912
913   handle->proc_cls = proc_cls;
914   handle->proc = proc;
915   handle->state = ID_REST_STATE_INIT;
916   handle->conndata_handle = conndata_handle;
917   handle->data = conndata_handle->data;
918   handle->data_size = conndata_handle->data_size;
919   handle->method = conndata_handle->method;
920   GNUNET_asprintf (&handle->url, "%s", conndata_handle->url);
921   if (handle->url[strlen (handle->url)-1] == '/')
922     handle->url[strlen (handle->url)-1] = '\0';
923   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
924               "Connecting...\n");
925   handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
926                                                      &list_ego,
927                                                      handle);
928   handle->timeout_task =
929     GNUNET_SCHEDULER_add_delayed (handle->timeout,
930                                   &do_error,
931                                   handle);
932
933
934   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
935               "Connected\n");
936 }
937
938 /**
939  * Entry point for the plugin.
940  *
941  * @param cls Config info
942  * @return NULL on error, otherwise the plugin context
943  */
944 void *
945 libgnunet_plugin_rest_identity_init (void *cls)
946 {
947   static struct Plugin plugin;
948   struct GNUNET_REST_Plugin *api;
949
950   cfg = cls;
951   if (NULL != plugin.cfg)
952     return NULL;                /* can only initialize once! */
953   memset (&plugin, 0, sizeof (struct Plugin));
954   plugin.cfg = cfg;
955   api = GNUNET_new (struct GNUNET_REST_Plugin);
956   api->cls = &plugin;
957   api->name = GNUNET_REST_API_NS_IDENTITY;
958   api->process_request = &rest_identity_process_request;
959   GNUNET_asprintf (&allow_methods,
960                    "%s, %s, %s, %s, %s",
961                    MHD_HTTP_METHOD_GET,
962                    MHD_HTTP_METHOD_POST,
963                    MHD_HTTP_METHOD_PUT,
964                    MHD_HTTP_METHOD_DELETE,
965                    MHD_HTTP_METHOD_OPTIONS);
966
967   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
968               _("Identity REST API initialized\n"));
969   return api;
970 }
971
972
973 /**
974  * Exit point from the plugin.
975  *
976  * @param cls the plugin context (as returned by "init")
977  * @return always NULL
978  */
979 void *
980 libgnunet_plugin_rest_identity_done (void *cls)
981 {
982   struct GNUNET_REST_Plugin *api = cls;
983   struct Plugin *plugin = api->cls;
984
985   plugin->cfg = NULL;
986   GNUNET_free_non_null (allow_methods);
987   GNUNET_free (api);
988   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
989               "Identity REST plugin is finished\n");
990   return NULL;
991 }
992
993 /* end of plugin_rest_gns.c */