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