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