-bugfixes, change plugin call
[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"
36
37 #define ID_REST_STATE_INIT 0
38
39 #define ID_REST_STATE_POST_INIT 1
40
41 /**
42  * @brief struct returned by the initialization function of the plugin
43  */
44 struct Plugin
45 {
46   const struct GNUNET_CONFIGURATION_Handle *cfg;
47 };
48
49 const struct GNUNET_CONFIGURATION_Handle *cfg;
50
51 struct EgoEntry
52 {
53   /**
54    * DLL
55    */
56   struct EgoEntry *next;
57   
58   /**
59    * DLL
60    */
61   struct EgoEntry *prev;
62   
63   /**
64    * Ego Identifier
65    */
66   char *identifier;
67   
68   /**
69    * Ego Pkey
70    */
71   struct GNUNET_CRYPTO_EcdsaPublicKey pk;
72
73   /**
74    * The Ego
75    */
76   struct GNUNET_IDENTITY_Ego *ego;
77 };
78
79 struct RequestHandle
80 {
81   /**
82    * Ego list
83    */
84   struct EgoEntry *ego_head;
85
86   /**
87    * Ego list
88    */
89   struct EgoEntry *ego_tail;
90   
91   /**
92    * The processing state
93    */
94   int state;
95
96   /**
97    * Handle to GNS service.
98    */
99   struct GNUNET_IDENTITY_Handle *identity_handle;
100
101   /**
102    * IDENTITY Operation
103    */
104   struct GNUNET_IDENTITY_Operation *op;
105
106   /**
107    * Desired timeout for the lookup (default is no timeout).
108    */
109   struct GNUNET_TIME_Relative timeout;
110
111   /**
112    * ID of a task associated with the resolution process.
113    */
114   struct GNUNET_SCHEDULER_Task * timeout_task;    
115
116   /**
117    * The root of the received JSON or NULL
118    */
119   json_t *json_root;
120
121   /**
122    * The plugin result processor
123    */
124   GNUNET_REST_ResultProcessor proc;
125
126   /**
127    * The closure of the result processor
128    */
129   void *proc_cls;
130
131   /**
132    * The name to look up
133    */
134   char *name;
135
136   /**
137    * The subsystem set from REST
138    */
139   char *subsys;
140
141   /**
142    * The url
143    */
144   const char *url;
145
146   /**
147    * The data from the REST request
148    */
149   const char* data;
150
151   /**
152    * the length of the REST data
153    */
154   size_t data_size;
155
156   /**
157    * HTTP method
158    */
159   const char* method;
160
161 };
162
163 /**
164  * Cleanup lookup handle
165  * @praram handle Handle to clean up
166  */
167 void
168 cleanup_handle (struct RequestHandle *handle)
169 {
170   struct EgoEntry *ego_entry;
171   struct EgoEntry *ego_tmp;
172   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
173               "Cleaning up\n");
174   if (NULL != handle->json_root)
175     json_decref (handle->json_root);
176   if (NULL != handle->name)
177     GNUNET_free (handle->name);
178   if (NULL != handle->timeout_task)
179     GNUNET_SCHEDULER_cancel (handle->timeout_task);
180   if (NULL != handle->identity_handle)
181     GNUNET_IDENTITY_disconnect (handle->identity_handle);
182   if (NULL != handle->subsys)
183     GNUNET_free (handle->subsys);
184   for (ego_entry = handle->ego_head;
185        NULL != ego_entry;)
186   {
187     ego_tmp = ego_entry;
188     ego_entry = ego_entry->next;
189     GNUNET_free (ego_tmp->identifier);
190     GNUNET_free (ego_tmp);
191   }
192   GNUNET_free (handle);
193 }
194
195
196 /**
197  * Task run on shutdown.  Cleans up everything.
198  *
199  * @param cls unused
200  * @param tc scheduler context
201  */
202 static void
203 do_error (void *cls,
204           const struct GNUNET_SCHEDULER_TaskContext *tc)
205 {
206   struct RequestHandle *handle = cls;
207   handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
208   cleanup_handle (handle);
209 }
210
211
212
213 void
214 ego_info_response (struct RequestHandle *handle)
215 {
216   const char* egoname;
217   char* keystring;
218   char* result_str;
219   struct EgoEntry *ego_entry;
220   json_t *ego_arr;
221   json_t *ego_json;
222
223   if (strlen (EGO_NAMESPACE) > strlen (handle->url))
224   {
225     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
226     cleanup_handle (handle);
227     GNUNET_break (0);
228     return;
229   }
230   ego_arr = json_array ();
231
232   egoname = &handle->url[strlen (EGO_NAMESPACE)];
233
234   if (strlen (EGO_NAMESPACE) + 1 >= strlen (handle->url))
235   {
236     egoname = NULL;
237   }
238
239   //Return all egos
240     for (ego_entry = handle->ego_head;
241        NULL != ego_entry;
242        ego_entry = ego_entry->next)
243   {
244     if ( (NULL != egoname) && (0 != strcmp (egoname, ego_entry->identifier)) )
245       continue;
246     ego_json = json_object ();
247     keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&ego_entry->pk);
248     json_object_set_new (ego_json, "identity", json_string (ego_entry->identifier));
249     json_object_set_new (ego_json, "key", json_string (keystring));
250     json_array_append (ego_arr, ego_json);
251     json_decref (ego_json);
252     GNUNET_free (keystring);
253   }
254   result_str = json_dumps (ego_arr, JSON_COMPACT);
255   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
256   json_decref (ego_arr);
257   handle->proc (handle->proc_cls, result_str, strlen (result_str), GNUNET_OK);
258   GNUNET_free (result_str);
259   cleanup_handle (handle);
260
261 }
262
263 static void
264 do_finished (void *cls, const char *emsg)
265 {
266   struct RequestHandle *handle = cls;
267
268   handle->op = NULL;
269   if (NULL != emsg)
270   {
271     GNUNET_SCHEDULER_add_now (&do_error, handle);
272   }
273   handle->proc (handle->proc_cls, NULL, 0, GNUNET_OK);
274   cleanup_handle (handle);
275 }
276
277 static void
278 ego_create_cont (struct RequestHandle *handle)
279 {
280   const char* egoname;
281   char term_data[handle->data_size+1];
282   json_t *egoname_json;
283   json_t *root_json;
284   json_error_t error;
285   struct EgoEntry *ego_entry;
286
287   if (strlen (API_NAMESPACE) != strlen (handle->url))
288   {
289     GNUNET_break(0);
290     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
291     cleanup_handle (handle);
292     return;
293   }
294   if (0 >= handle->data_size)
295   {
296     GNUNET_break(0);
297     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
298     cleanup_handle (handle);
299     return;
300   }
301
302   term_data[handle->data_size] = '\0';
303   memcpy (term_data, handle->data, handle->data_size);
304   root_json = json_loads (term_data, 0, &error);
305
306   if ((NULL == root_json) || !json_is_object (root_json))
307   {
308     GNUNET_break(0);
309     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
310     cleanup_handle (handle);
311     return;
312   }
313   egoname_json = json_object_get (root_json, "ego");
314   if (!json_is_string (egoname_json))
315   {
316     GNUNET_break(0);
317     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
318     cleanup_handle (handle);
319     return;
320   }
321   egoname = json_string_value (egoname_json);
322   for (ego_entry = handle->ego_head;
323        NULL != ego_entry;
324        ego_entry = ego_entry->next)
325   {
326     if (0 == strcasecmp (egoname, ego_entry->identifier))
327     {
328       json_decref (egoname_json);
329       json_decref (root_json);
330       handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
331       cleanup_handle (handle);
332       return;
333     }
334   }
335   GNUNET_asprintf (&handle->name, "%s", egoname);
336   json_decref (egoname_json);
337   json_decref (root_json);
338   handle->op = GNUNET_IDENTITY_create (handle->identity_handle,
339                                               handle->name,
340                                               &do_finished,
341                                               handle);
342 }
343
344 void 
345 subsys_set_cont (struct RequestHandle *handle)
346 {
347   const char *egoname;
348   const char *subsys;
349   char term_data[handle->data_size+1];
350   struct EgoEntry *ego_entry;
351   int ego_exists = GNUNET_NO;
352   json_t *root_json;
353   json_t *subsys_json;
354   json_error_t error;
355
356   if (strlen (API_NAMESPACE)+1 >= strlen (handle->url))
357   {
358     GNUNET_break(0);
359     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
360     cleanup_handle (handle);
361     return;
362   }
363
364   egoname = &handle->url[strlen(API_NAMESPACE)+1];
365   for (ego_entry = handle->ego_head;
366        NULL != ego_entry;
367        ego_entry = ego_entry->next)
368   {
369     if (0 == strcasecmp (egoname, ego_entry->identifier))
370     {
371       ego_exists = GNUNET_YES;
372       break;
373     }
374   }
375   if (GNUNET_NO == ego_exists)
376   {
377     GNUNET_break(0);
378     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
379     cleanup_handle (handle);
380     return;
381   }
382
383   if (0 >= handle->data_size)
384   {
385     GNUNET_break(0);
386     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
387     cleanup_handle (handle);
388     return;
389   }
390
391   term_data[handle->data_size] = '\0';
392   memcpy (term_data, handle->data, handle->data_size);
393   root_json = json_loads (term_data, 0, &error);
394
395   if ((NULL == root_json) || !json_is_object (root_json))
396   {
397     GNUNET_break(0);
398     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
399     cleanup_handle (handle);
400     return;
401   }
402   subsys_json = json_object_get (root_json, "subsystem");
403   if (!json_is_string (subsys_json))
404   {
405     GNUNET_break(0);
406     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
407     cleanup_handle (handle);
408     return;
409   }
410   subsys = json_string_value (subsys_json);
411   GNUNET_asprintf (&handle->subsys, "%s", subsys);
412   json_decref (subsys_json);
413   json_decref (root_json);
414   handle->op = GNUNET_IDENTITY_set (handle->identity_handle,
415                                     handle->subsys,
416                                     ego_entry->ego,
417                                     &do_finished,
418                                     handle);
419 }
420
421 void 
422 ego_delete_cont (struct RequestHandle *handle)
423 {
424   const char *egoname;
425   struct EgoEntry *ego_entry;
426   int ego_exists = GNUNET_NO;
427
428   if (strlen (API_NAMESPACE)+1 >= strlen (handle->url))
429   {
430     GNUNET_break(0);
431     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
432     cleanup_handle (handle);
433     return;
434   }
435
436   egoname = &handle->url[strlen(API_NAMESPACE)+1];
437   for (ego_entry = handle->ego_head;
438        NULL != ego_entry;
439        ego_entry = ego_entry->next)
440   {
441     if (0 == strcasecmp (egoname, ego_entry->identifier))
442     {
443       ego_exists = GNUNET_YES;
444       break;
445     }
446   }
447   if (GNUNET_NO == ego_exists)
448   {
449     GNUNET_break(0);
450     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
451     cleanup_handle (handle);
452     return;
453   }
454   handle->op = GNUNET_IDENTITY_delete (handle->identity_handle,
455                                        egoname,
456                                        &do_finished,
457                                        handle);
458
459 }
460
461 void
462 init_cont (struct RequestHandle *handle)
463 {
464   if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_GET))
465     ego_info_response (handle);
466   else if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_POST))
467     ego_create_cont (handle);
468   else if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_PUT))
469     subsys_set_cont (handle);
470   else if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_DELETE))
471     ego_delete_cont (handle);
472   else
473     GNUNET_SCHEDULER_add_now (&do_error, handle);
474 }
475
476 /**
477  * If listing is enabled, prints information about the egos.
478  *
479  * This function is initially called for all egos and then again
480  * whenever a ego's identifier changes or if it is deleted.  At the
481  * end of the initial pass over all egos, the function is once called
482  * with 'NULL' for 'ego'. That does NOT mean that the callback won't
483  * be invoked in the future or that there was an error.
484  *
485  * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
486  * this function is only called ONCE, and 'NULL' being passed in
487  * 'ego' does indicate an error (i.e. name is taken or no default
488  * value is known).  If 'ego' is non-NULL and if '*ctx'
489  * is set in those callbacks, the value WILL be passed to a subsequent
490  * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
491  * that one was not NULL).
492  *
493  * When an identity is renamed, this function is called with the
494  * (known) ego but the NEW identifier.
495  *
496  * When an identity is deleted, this function is called with the
497  * (known) ego and "NULL" for the 'identifier'.  In this case,
498  * the 'ego' is henceforth invalid (and the 'ctx' should also be
499  * cleaned up).
500  *
501  * @param cls closure
502  * @param ego ego handle
503  * @param ctx context for application to store data for this ego
504  *                 (during the lifetime of this process, initially NULL)
505  * @param identifier identifier assigned by the user for this ego,
506  *                   NULL if the user just deleted the ego and it
507  *                   must thus no longer be used
508 */
509 static void
510 list_ego (void *cls,
511            struct GNUNET_IDENTITY_Ego *ego,
512            void **ctx,
513            const char *identifier)
514 {
515   struct RequestHandle *handle = cls;
516   struct EgoEntry *ego_entry;
517   
518   if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
519   {
520     handle->state = ID_REST_STATE_POST_INIT;
521     init_cont (handle);
522     return;
523   }
524   if (ID_REST_STATE_INIT == handle->state) {
525     ego_entry = GNUNET_new (struct EgoEntry);
526     GNUNET_IDENTITY_ego_get_public_key (ego, &(ego_entry->pk));
527     ego_entry->ego = ego;
528     GNUNET_asprintf (&ego_entry->identifier, "%s", identifier);
529     GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
530   }
531
532 }
533
534 /**
535  * Function processing the REST call
536  *
537  * @param method HTTP method
538  * @param url URL of the HTTP request
539  * @param data body of the HTTP request (optional)
540  * @param data_size length of the body
541  * @param proc callback function for the result
542  * @param proc_cls closure for callback function
543  * @return GNUNET_OK if request accepted
544  */
545 void
546 rest_identity_process_request(struct RestConnectionDataHandle *conndata_handle,
547                               GNUNET_REST_ResultProcessor proc,
548                               void *proc_cls)
549 {
550   struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
551
552
553
554   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
555
556   handle->proc_cls = proc_cls;
557   handle->proc = proc;
558   handle->state = ID_REST_STATE_INIT;
559   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
560               "Connecting...\n");
561   handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
562                                                      &list_ego,
563                                                      handle); 
564   handle->timeout_task =
565     GNUNET_SCHEDULER_add_delayed (handle->timeout,
566                                   &do_error,
567                                   handle);
568   handle->data = conndata_handle->data;
569   handle->data_size = conndata_handle->data_size;
570   handle->url = conndata_handle->url;
571   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
572               "Connected\n");
573   handle->method = conndata_handle->method;
574 }
575
576 /**
577  * Entry point for the plugin.
578  *
579  * @param cls Config info
580  * @return NULL on error, otherwise the plugin context
581  */
582 void *
583 libgnunet_plugin_rest_identity_init (void *cls)
584 {
585   static struct Plugin plugin;
586   cfg = cls;
587   struct GNUNET_REST_Plugin *api;
588
589   if (NULL != plugin.cfg)
590     return NULL;                /* can only initialize once! */
591   memset (&plugin, 0, sizeof (struct Plugin));
592   plugin.cfg = cfg;
593   api = GNUNET_new (struct GNUNET_REST_Plugin);
594   api->cls = &plugin;
595   api->name = API_NAMESPACE;
596   api->process_request = &rest_identity_process_request;
597   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
598               _("Identity REST API initialized\n"));
599   return api;
600 }
601
602
603 /**
604  * Exit point from the plugin.
605  *
606  * @param cls the plugin context (as returned by "init")
607  * @return always NULL
608  */
609 void *
610 libgnunet_plugin_rest_identity_done (void *cls)
611 {
612   struct GNUNET_REST_Plugin *api = cls;
613   struct Plugin *plugin = api->cls;
614
615   plugin->cfg = NULL;
616   GNUNET_free (api);
617   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
618               "Identity REST plugin is finished\n");
619   return NULL;
620 }
621
622 /* end of plugin_rest_gns.c */