-start a lib for REST and json:api
[oweals/gnunet.git] / src / identity / plugin_rest_identity.c
1 /*
2    This file is part of GNUnet.
3    Copyright (C) 2012-2015 Christian Grothoff (and other contributing authors)
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., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, 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 "microhttpd.h"
32 #include <jansson.h>
33
34 #define API_NAMESPACE "/identity"
35
36 #define EGO_NAMESPACE "/identity/egos"
37
38 #define ID_REST_STATE_INIT 0
39
40 #define ID_REST_STATE_POST_INIT 1
41
42 #define URL_PARAM_SUBSYS "service"
43
44 #define GNUNET_REST_JSONAPI_IDENTITY_EGO "ego"
45
46 #define GNUNET_REST_JSONAPI_IDENTITY_KEY "key"
47
48 /**
49  * @brief struct returned by the initialization function of the plugin
50  */
51 struct Plugin
52 {
53   const struct GNUNET_CONFIGURATION_Handle *cfg;
54 };
55
56 const struct GNUNET_CONFIGURATION_Handle *cfg;
57
58 struct EgoEntry
59 {
60   /**
61    * DLL
62    */
63   struct EgoEntry *next;
64   
65   /**
66    * DLL
67    */
68   struct EgoEntry *prev;
69   
70   /**
71    * Ego Identifier
72    */
73   char *identifier;
74   
75   /**
76    * Ego Pkey
77    */
78   struct GNUNET_CRYPTO_EcdsaPublicKey pk;
79
80   /**
81    * The Ego
82    */
83   struct GNUNET_IDENTITY_Ego *ego;
84 };
85
86 struct RequestHandle
87 {
88   /**
89    * Ego list
90    */
91   struct EgoEntry *ego_head;
92
93   /**
94    * Ego list
95    */
96   struct EgoEntry *ego_tail;
97
98   struct RestConnectionDataHandle *conndata_handle;
99   
100   /**
101    * The processing state
102    */
103   int state;
104
105   /**
106    * Handle to GNS service.
107    */
108   struct GNUNET_IDENTITY_Handle *identity_handle;
109
110   /**
111    * IDENTITY Operation
112    */
113   struct GNUNET_IDENTITY_Operation *op;
114
115   /**
116    * Desired timeout for the lookup (default is no timeout).
117    */
118   struct GNUNET_TIME_Relative timeout;
119
120   /**
121    * ID of a task associated with the resolution process.
122    */
123   struct GNUNET_SCHEDULER_Task * timeout_task;    
124
125   /**
126    * The root of the received JSON or NULL
127    */
128   json_t *json_root;
129
130   /**
131    * The plugin result processor
132    */
133   GNUNET_REST_ResultProcessor proc;
134
135   /**
136    * The closure of the result processor
137    */
138   void *proc_cls;
139
140   /**
141    * The name to look up
142    */
143   char *name;
144
145   /**
146    * The subsystem set from REST
147    */
148   char *subsys;
149
150   /**
151    * The url
152    */
153   char *url;
154
155   /**
156    * The data from the REST request
157    */
158   const char* data;
159
160   /**
161    * the length of the REST data
162    */
163   size_t data_size;
164
165   /**
166    * HTTP method
167    */
168   const char* method;
169
170 };
171
172
173 /**
174  * Cleanup lookup handle
175  * @praram handle Handle to clean up
176  */
177 void
178 cleanup_handle (struct RequestHandle *handle)
179 {
180   struct EgoEntry *ego_entry;
181   struct EgoEntry *ego_tmp;
182   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
183               "Cleaning up\n");
184   if (NULL != handle->json_root)
185     json_decref (handle->json_root);
186   if (NULL != handle->name)
187     GNUNET_free (handle->name);
188   if (NULL != handle->timeout_task)
189     GNUNET_SCHEDULER_cancel (handle->timeout_task);
190   if (NULL != handle->identity_handle)
191     GNUNET_IDENTITY_disconnect (handle->identity_handle);
192   if (NULL != handle->subsys)
193     GNUNET_free (handle->subsys);
194   if (NULL != handle->url)
195     GNUNET_free (handle->url);
196   for (ego_entry = handle->ego_head;
197        NULL != ego_entry;)
198   {
199     ego_tmp = ego_entry;
200     ego_entry = ego_entry->next;
201     GNUNET_free (ego_tmp->identifier);
202     GNUNET_free (ego_tmp);
203   }
204   GNUNET_free (handle);
205 }
206
207
208 /**
209  * Task run on shutdown.  Cleans up everything.
210  *
211  * @param cls unused
212  * @param tc scheduler context
213  */
214 static void
215 do_error (void *cls,
216           const struct GNUNET_SCHEDULER_TaskContext *tc)
217 {
218   struct RequestHandle *handle = cls;
219   struct MHD_Response *resp = GNUNET_REST_create_json_response (NULL);
220   handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
221   cleanup_handle (handle);
222 }
223
224 /**
225  * Callback for IDENTITY_get()
226  *
227  * @param cls the RequestHandle
228  * @param ego the Ego found
229  * @param ctx the context
230  * @param name the id of the ego
231  */
232 void
233 get_ego_for_subsys (void *cls,
234                     struct GNUNET_IDENTITY_Ego *ego,
235                     void **ctx,
236                     const char *name)
237 {
238   struct RequestHandle *handle = cls;
239   struct EgoEntry *ego_entry;
240   struct MHD_Response *resp;
241   char *result_str;
242   char *keystring;
243   json_t *ego_json;
244   json_t *root_json;
245
246   root_json = json_object ();
247
248   //Return all egos
249   for (ego_entry = handle->ego_head;
250        NULL != ego_entry;
251        ego_entry = ego_entry->next)
252   {
253     if ( (NULL != name) && (0 != strcmp (name, ego_entry->identifier)) )
254       continue;
255     if (NULL == name)
256       continue;
257     ego_json = json_object ();
258     keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&ego_entry->pk);
259     json_object_set_new (ego_json, "id", json_string (ego_entry->identifier));
260     json_object_set_new (ego_json, "type", json_string (GNUNET_REST_JSONAPI_IDENTITY_EGO));
261     json_object_set_new (ego_json, "key", json_string (keystring));
262     GNUNET_free (keystring);
263     break;
264   }
265   if (NULL == ego_json)
266   {
267     json_decref (root_json);
268     GNUNET_SCHEDULER_add_now (&do_error, handle);
269     return;
270   }
271   json_object_set (root_json, GNUNET_REST_JSONAPI_KEY_DATA, ego_json);
272   result_str = json_dumps (root_json, JSON_COMPACT);
273   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
274   json_decref (ego_json);
275   json_decref (root_json);
276   resp = GNUNET_REST_create_json_response (result_str);
277   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
278   GNUNET_free (result_str);
279   cleanup_handle (handle);
280 }
281
282 /**
283  * Create a response with requested ego(s)
284  *
285  * @param handle the RequestHandle
286  */
287 void
288 ego_info_response (struct RequestHandle *handle)
289 {
290   const char *egoname;
291   char *keystring;
292   char *result_str;
293   char *subsys_val;
294   struct EgoEntry *ego_entry;
295   struct GNUNET_HashCode key;
296   struct MHD_Response *resp;
297   struct JsonApiResponse *json_response;
298   struct JsonApiResource *json_resource;
299   json_t *key_str;
300
301   if (GNUNET_NO == GNUNET_REST_namespace_match (handle->url, EGO_NAMESPACE))
302   {
303     resp = GNUNET_REST_create_json_response (NULL);
304     handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
305     cleanup_handle (handle);
306     GNUNET_break (0);
307     return;
308   }
309   json_response = GNUNET_REST_jsonapi_response_new ();
310   if ( (strlen (EGO_NAMESPACE) == strlen (handle->url) )) {
311     GNUNET_CRYPTO_hash (URL_PARAM_SUBSYS, strlen (URL_PARAM_SUBSYS), &key);
312     if ( GNUNET_YES ==
313          GNUNET_CONTAINER_multihashmap_contains (handle->conndata_handle->url_param_map,
314                                                  &key) )
315     {
316       subsys_val = GNUNET_CONTAINER_multihashmap_get (handle->conndata_handle->url_param_map,
317                                                       &key);
318       if (NULL != subsys_val)
319       {
320         GNUNET_asprintf (&handle->subsys, "%s", subsys_val);
321         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Looking for %s's ego\n", subsys_val);
322         handle->op = GNUNET_IDENTITY_get (handle->identity_handle,
323                                           handle->subsys,
324                                           &get_ego_for_subsys,
325                                           handle);
326         return;
327       }
328     }
329   }
330   json_response = GNUNET_REST_jsonapi_response_new ();
331   egoname = &handle->url[strlen (EGO_NAMESPACE)+1];
332
333   if (strlen (EGO_NAMESPACE) == strlen (handle->url))
334   {
335     egoname = NULL;
336   }
337
338   //Return all egos
339   for (ego_entry = handle->ego_head;
340        NULL != ego_entry;
341        ego_entry = ego_entry->next)
342   {
343     if ( (NULL != egoname) && (0 != strcmp (egoname, ego_entry->identifier)) )
344       continue;
345     keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&ego_entry->pk);
346     json_resource = GNUNET_REST_jsonapi_resource_new (GNUNET_REST_JSONAPI_IDENTITY_EGO, ego_entry->identifier);
347     key_str = json_string (keystring);
348     GNUNET_REST_jsonapi_resource_add_attr (json_resource,
349                                            GNUNET_REST_JSONAPI_IDENTITY_KEY,
350                                            key_str);
351     json_decref (key_str);
352     GNUNET_free (keystring);
353     GNUNET_REST_jsonapi_response_resource_add (json_response, json_resource);
354   }
355
356   GNUNET_REST_jsonapi_data_serialize (json_response, &result_str);
357
358   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
359   resp = GNUNET_REST_create_json_response (result_str);
360   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
361   GNUNET_free (result_str);
362   cleanup_handle (handle);
363 }
364
365 static void
366 do_finished (void *cls, const char *emsg)
367 {
368   struct RequestHandle *handle = cls;
369   struct MHD_Response *resp;
370
371   handle->op = NULL;
372   if (NULL != emsg)
373   {
374     GNUNET_SCHEDULER_add_now (&do_error, handle);
375     return;
376   }
377   resp = GNUNET_REST_create_json_response (NULL);
378   handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
379   cleanup_handle (handle);
380 }
381
382 static void
383 set_finished (void *cls, const char *emsg)
384 {
385   struct RequestHandle *handle = cls;
386   struct MHD_Response *resp;
387
388   handle->op = NULL;
389   if (NULL != emsg)
390   {
391     GNUNET_SCHEDULER_add_now (&do_error, handle);
392     return;
393   }
394   resp = GNUNET_REST_create_json_response (NULL);
395   handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
396   cleanup_handle (handle);
397 }
398
399 static void
400 create_finished (void *cls, const char *emsg)
401 {
402   struct RequestHandle *handle = cls;
403   struct MHD_Response *resp;
404
405   handle->op = NULL;
406   if (NULL != emsg)
407   {
408     GNUNET_SCHEDULER_add_now (&do_error, handle);
409     return;
410   }
411   resp = GNUNET_REST_create_json_response (NULL);
412   handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
413   cleanup_handle (handle);
414 }
415
416 static void
417 ego_create_cont (struct RequestHandle *handle)
418 {
419   const char* egoname;
420   char term_data[handle->data_size+1];
421   struct EgoEntry *ego_entry;
422   struct MHD_Response *resp;
423   json_t *root_json;
424   json_t *data_json;
425   json_t *type_json;
426   json_t *egoname_json;
427   json_error_t error;
428
429   if (strlen (API_NAMESPACE) != strlen (handle->url))
430   {
431     GNUNET_SCHEDULER_add_now (&do_error, handle);
432     return;
433   }
434   if (0 >= handle->data_size)
435   {
436     GNUNET_SCHEDULER_add_now (&do_error, handle);
437     return;
438   }
439
440   term_data[handle->data_size] = '\0';
441   memcpy (term_data, handle->data, handle->data_size);
442   root_json = json_loads (term_data, 0, &error);
443
444   if ((NULL == root_json) || !json_is_object (root_json))
445   {
446     GNUNET_SCHEDULER_add_now (&do_error, handle);
447     return;
448   }
449   data_json = json_object_get (root_json, GNUNET_REST_JSONAPI_KEY_DATA);
450   if ((NULL == data_json) || !json_is_object (data_json))
451   {
452     GNUNET_SCHEDULER_add_now (&do_error, handle);
453     return;
454   }
455   type_json = json_object_get (data_json, "type");
456   if (!json_is_string (type_json) ||
457       (0 != strcmp (GNUNET_REST_JSONAPI_IDENTITY_EGO, json_string_value (type_json))))
458   {
459     json_decref (data_json);
460     json_decref (root_json);
461     resp = GNUNET_REST_create_json_response (NULL);
462     handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
463     cleanup_handle (handle);
464     return;
465   }
466   json_decref (type_json);
467   egoname_json = json_object_get (data_json, "id");
468   if (!json_is_string (egoname_json))
469   {
470     json_decref (data_json);
471     json_decref (root_json);
472     GNUNET_SCHEDULER_add_now (&do_error, handle);
473     return;
474   }
475
476   egoname = json_string_value (egoname_json);
477   for (ego_entry = handle->ego_head;
478        NULL != ego_entry;
479        ego_entry = ego_entry->next)
480   {
481     if (0 == strcasecmp (egoname, ego_entry->identifier))
482     {
483       json_decref (egoname_json);
484       json_decref (data_json);
485       json_decref (root_json);
486       resp = GNUNET_REST_create_json_response (NULL);
487       handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
488       cleanup_handle (handle);
489       return;
490     }
491   }
492   GNUNET_asprintf (&handle->name, "%s", egoname);
493   json_decref (egoname_json);
494   json_decref (data_json);
495   json_decref (root_json);
496   handle->op = GNUNET_IDENTITY_create (handle->identity_handle,
497                                        handle->name,
498                                        &create_finished,
499                                        handle);
500 }
501
502 void 
503 subsys_set_cont (struct RequestHandle *handle)
504 {
505   const char *egoname;
506   const char *subsys;
507   char term_data[handle->data_size+1];
508   struct EgoEntry *ego_entry;
509   struct MHD_Response *resp;
510   int ego_exists = GNUNET_NO;
511   json_t *root_json;
512   json_t *data_json;
513   json_t *type_json;
514   json_t *id_json;
515   json_t *subsys_json;
516   json_error_t error;
517
518   if (strlen (API_NAMESPACE) > strlen (handle->url))
519   {
520     GNUNET_SCHEDULER_add_now (&do_error, handle);
521     return;
522   }
523
524   egoname = &handle->url[strlen(EGO_NAMESPACE)+1];
525   for (ego_entry = handle->ego_head;
526        NULL != ego_entry;
527        ego_entry = ego_entry->next)
528   {
529     if (0 == strcasecmp (egoname, ego_entry->identifier))
530     {
531       ego_exists = GNUNET_YES;
532       break;
533     }
534   }
535   if (GNUNET_NO == ego_exists)
536   {
537     resp = GNUNET_REST_create_json_response (NULL);
538     handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND);
539     cleanup_handle (handle);
540     return;
541   }
542
543   if (0 >= handle->data_size)
544   {
545     GNUNET_SCHEDULER_add_now (&do_error, handle);
546     return;
547   }
548
549   term_data[handle->data_size] = '\0';
550   memcpy (term_data, handle->data, handle->data_size);
551   root_json = json_loads (term_data, 0, &error);
552
553   if ((NULL == root_json) || !json_is_object (root_json))
554   {
555     GNUNET_SCHEDULER_add_now (&do_error, handle);
556     return;
557   }
558   data_json = json_object_get (root_json, "data");
559   if (!json_is_object (data_json))
560   {
561     json_decref (root_json);
562     GNUNET_SCHEDULER_add_now (&do_error, handle);
563     return;
564   }
565   id_json = json_object_get (data_json, "id");
566   if (!json_is_string (id_json) ||
567       (0 != strcmp (egoname, json_string_value (id_json))))
568   {
569     json_decref (root_json);
570     json_decref (data_json);
571     GNUNET_SCHEDULER_add_now (&do_error, handle);
572     return;
573   }
574   json_decref (id_json);
575
576   type_json = json_object_get (data_json, "type");
577   if (!json_is_string (type_json) ||
578       (0 != strcmp (GNUNET_REST_JSONAPI_IDENTITY_EGO, json_string_value (type_json))))
579   {
580     json_decref (root_json);
581     json_decref (data_json);
582     GNUNET_SCHEDULER_add_now (&do_error, handle);
583     return;
584   }
585   json_decref (type_json);
586
587   subsys_json = json_object_get (data_json, "subsystem");
588   if (!json_is_string (subsys_json))
589   {
590     json_decref (root_json);
591     json_decref (data_json);
592     GNUNET_SCHEDULER_add_now (&do_error, handle);
593     return;
594   }
595   subsys = json_string_value (subsys_json);
596   GNUNET_asprintf (&handle->subsys, "%s", subsys);
597   json_decref (subsys_json);
598   json_decref (root_json);
599   handle->op = GNUNET_IDENTITY_set (handle->identity_handle,
600                                     handle->subsys,
601                                     ego_entry->ego,
602                                     &set_finished,
603                                     handle);
604 }
605
606 void 
607 ego_delete_cont (struct RequestHandle *handle)
608 {
609   const char *egoname;
610   struct EgoEntry *ego_entry;
611   struct MHD_Response *resp;
612   int ego_exists = GNUNET_NO;
613
614   if (strlen (API_NAMESPACE) >= strlen (handle->url))
615   {
616     GNUNET_SCHEDULER_add_now (&do_error, handle);
617     return;
618   }
619
620   egoname = &handle->url[strlen(API_NAMESPACE)+1];
621   for (ego_entry = handle->ego_head;
622        NULL != ego_entry;
623        ego_entry = ego_entry->next)
624   {
625     if (0 == strcasecmp (egoname, ego_entry->identifier))
626     {
627       ego_exists = GNUNET_YES;
628       break;
629     }
630   }
631   if (GNUNET_NO == ego_exists)
632   {
633     resp = GNUNET_REST_create_json_response (NULL);
634     handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND);
635     cleanup_handle (handle);
636     return;
637   }
638   handle->op = GNUNET_IDENTITY_delete (handle->identity_handle,
639                                        egoname,
640                                        &do_finished,
641                                        handle);
642
643 }
644
645 void
646 init_cont (struct RequestHandle *handle)
647 {
648   if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_GET))
649     ego_info_response (handle);
650   else if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_POST))
651     ego_create_cont (handle);
652   else if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_PUT))
653     subsys_set_cont (handle);
654   else if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_DELETE))
655     ego_delete_cont (handle);
656   else
657     GNUNET_SCHEDULER_add_now (&do_error, handle);
658 }
659
660 /**
661  * If listing is enabled, prints information about the egos.
662  *
663  * This function is initially called for all egos and then again
664  * whenever a ego's identifier changes or if it is deleted.  At the
665  * end of the initial pass over all egos, the function is once called
666  * with 'NULL' for 'ego'. That does NOT mean that the callback won't
667  * be invoked in the future or that there was an error.
668  *
669  * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
670  * this function is only called ONCE, and 'NULL' being passed in
671  * 'ego' does indicate an error (i.e. name is taken or no default
672  * value is known).  If 'ego' is non-NULL and if '*ctx'
673  * is set in those callbacks, the value WILL be passed to a subsequent
674  * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
675  * that one was not NULL).
676  *
677  * When an identity is renamed, this function is called with the
678  * (known) ego but the NEW identifier.
679  *
680  * When an identity is deleted, this function is called with the
681  * (known) ego and "NULL" for the 'identifier'.  In this case,
682  * the 'ego' is henceforth invalid (and the 'ctx' should also be
683  * cleaned up).
684  *
685  * @param cls closure
686  * @param ego ego handle
687  * @param ctx context for application to store data for this ego
688  *                 (during the lifetime of this process, initially NULL)
689  * @param identifier identifier assigned by the user for this ego,
690  *                   NULL if the user just deleted the ego and it
691  *                   must thus no longer be used
692  */
693 static void
694 list_ego (void *cls,
695           struct GNUNET_IDENTITY_Ego *ego,
696           void **ctx,
697           const char *identifier)
698 {
699   struct RequestHandle *handle = cls;
700   struct EgoEntry *ego_entry;
701
702   if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
703   {
704     handle->state = ID_REST_STATE_POST_INIT;
705     init_cont (handle);
706     return;
707   }
708   if (ID_REST_STATE_INIT == handle->state) {
709     ego_entry = GNUNET_new (struct EgoEntry);
710     GNUNET_IDENTITY_ego_get_public_key (ego, &(ego_entry->pk));
711     ego_entry->ego = ego;
712     GNUNET_asprintf (&ego_entry->identifier, "%s", identifier);
713     GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
714   }
715
716 }
717
718 /**
719  * Function processing the REST call
720  *
721  * @param method HTTP method
722  * @param url URL of the HTTP request
723  * @param data body of the HTTP request (optional)
724  * @param data_size length of the body
725  * @param proc callback function for the result
726  * @param proc_cls closure for callback function
727  * @return GNUNET_OK if request accepted
728  */
729 void
730 rest_identity_process_request(struct RestConnectionDataHandle *conndata_handle,
731                               GNUNET_REST_ResultProcessor proc,
732                               void *proc_cls)
733 {
734   struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
735
736
737
738   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
739
740   handle->proc_cls = proc_cls;
741   handle->proc = proc;
742   handle->state = ID_REST_STATE_INIT;
743   handle->conndata_handle = conndata_handle;
744   handle->data = conndata_handle->data;
745   handle->data_size = conndata_handle->data_size;
746   handle->method = conndata_handle->method;
747   GNUNET_asprintf (&handle->url, "%s", conndata_handle->url);
748   if (handle->url[strlen (handle->url)-1] == '/')
749     handle->url[strlen (handle->url)-1] = '\0';
750   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
751               "Connecting...\n");
752   handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
753                                                      &list_ego,
754                                                      handle); 
755   handle->timeout_task =
756     GNUNET_SCHEDULER_add_delayed (handle->timeout,
757                                   &do_error,
758                                   handle);
759
760
761   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
762               "Connected\n");
763 }
764
765 /**
766  * Entry point for the plugin.
767  *
768  * @param cls Config info
769  * @return NULL on error, otherwise the plugin context
770  */
771 void *
772 libgnunet_plugin_rest_identity_init (void *cls)
773 {
774   static struct Plugin plugin;
775   cfg = cls;
776   struct GNUNET_REST_Plugin *api;
777
778   if (NULL != plugin.cfg)
779     return NULL;                /* can only initialize once! */
780   memset (&plugin, 0, sizeof (struct Plugin));
781   plugin.cfg = cfg;
782   api = GNUNET_new (struct GNUNET_REST_Plugin);
783   api->cls = &plugin;
784   api->name = API_NAMESPACE;
785   api->process_request = &rest_identity_process_request;
786   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
787               _("Identity REST API initialized\n"));
788   return api;
789 }
790
791
792 /**
793  * Exit point from the plugin.
794  *
795  * @param cls the plugin context (as returned by "init")
796  * @return always NULL
797  */
798 void *
799 libgnunet_plugin_rest_identity_done (void *cls)
800 {
801   struct GNUNET_REST_Plugin *api = cls;
802   struct Plugin *plugin = api->cls;
803
804   plugin->cfg = NULL;
805   GNUNET_free (api);
806   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
807               "Identity REST plugin is finished\n");
808   return NULL;
809 }
810
811 /* end of plugin_rest_gns.c */