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