0d55a4cc798bba5c364d4e5faa6bc261bf472c1f
[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), GNUNET_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   }
386   handle->proc (handle->proc_cls, NULL, 0, GNUNET_OK);
387   cleanup_handle (handle);
388 }
389
390 static void
391 ego_create_cont (struct RequestHandle *handle)
392 {
393   const char* egoname;
394   char term_data[handle->data_size+1];
395   json_t *egoname_json;
396   json_t *root_json;
397   json_error_t error;
398   struct EgoEntry *ego_entry;
399
400   if (strlen (API_NAMESPACE) != strlen (handle->url))
401   {
402     GNUNET_break(0);
403     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
404     cleanup_handle (handle);
405     return;
406   }
407   if (0 >= handle->data_size)
408   {
409     GNUNET_break(0);
410     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
411     cleanup_handle (handle);
412     return;
413   }
414
415   term_data[handle->data_size] = '\0';
416   memcpy (term_data, handle->data, handle->data_size);
417   root_json = json_loads (term_data, 0, &error);
418
419   if ((NULL == root_json) || !json_is_object (root_json))
420   {
421     GNUNET_break(0);
422     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
423     cleanup_handle (handle);
424     return;
425   }
426   egoname_json = json_object_get (root_json, "ego");
427   if (!json_is_string (egoname_json))
428   {
429     GNUNET_break(0);
430     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
431     cleanup_handle (handle);
432     return;
433   }
434   egoname = json_string_value (egoname_json);
435   for (ego_entry = handle->ego_head;
436        NULL != ego_entry;
437        ego_entry = ego_entry->next)
438   {
439     if (0 == strcasecmp (egoname, ego_entry->identifier))
440     {
441       json_decref (egoname_json);
442       json_decref (root_json);
443       handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
444       cleanup_handle (handle);
445       return;
446     }
447   }
448   GNUNET_asprintf (&handle->name, "%s", egoname);
449   json_decref (egoname_json);
450   json_decref (root_json);
451   handle->op = GNUNET_IDENTITY_create (handle->identity_handle,
452                                        handle->name,
453                                        &do_finished,
454                                        handle);
455 }
456
457 void 
458 subsys_set_cont (struct RequestHandle *handle)
459 {
460   const char *egoname;
461   const char *subsys;
462   char term_data[handle->data_size+1];
463   struct EgoEntry *ego_entry;
464   int ego_exists = GNUNET_NO;
465   json_t *root_json;
466   json_t *subsys_json;
467   json_error_t error;
468
469   if (strlen (API_NAMESPACE) >= strlen (handle->url))
470   {
471     GNUNET_break(0);
472     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
473     cleanup_handle (handle);
474     return;
475   }
476
477   egoname = &handle->url[strlen(API_NAMESPACE)+1];
478   for (ego_entry = handle->ego_head;
479        NULL != ego_entry;
480        ego_entry = ego_entry->next)
481   {
482     if (0 == strcasecmp (egoname, ego_entry->identifier))
483     {
484       ego_exists = GNUNET_YES;
485       break;
486     }
487   }
488   if (GNUNET_NO == ego_exists)
489   {
490     GNUNET_break(0);
491     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
492     cleanup_handle (handle);
493     return;
494   }
495
496   if (0 >= handle->data_size)
497   {
498     GNUNET_break(0);
499     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
500     cleanup_handle (handle);
501     return;
502   }
503
504   term_data[handle->data_size] = '\0';
505   memcpy (term_data, handle->data, handle->data_size);
506   root_json = json_loads (term_data, 0, &error);
507
508   if ((NULL == root_json) || !json_is_object (root_json))
509   {
510     GNUNET_break(0);
511     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
512     cleanup_handle (handle);
513     return;
514   }
515   subsys_json = json_object_get (root_json, "subsystem");
516   if (!json_is_string (subsys_json))
517   {
518     GNUNET_break(0);
519     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
520     cleanup_handle (handle);
521     return;
522   }
523   subsys = json_string_value (subsys_json);
524   GNUNET_asprintf (&handle->subsys, "%s", subsys);
525   json_decref (subsys_json);
526   json_decref (root_json);
527   handle->op = GNUNET_IDENTITY_set (handle->identity_handle,
528                                     handle->subsys,
529                                     ego_entry->ego,
530                                     &do_finished,
531                                     handle);
532 }
533
534 void 
535 ego_delete_cont (struct RequestHandle *handle)
536 {
537   const char *egoname;
538   struct EgoEntry *ego_entry;
539   int ego_exists = GNUNET_NO;
540
541   if (strlen (API_NAMESPACE) >= strlen (handle->url))
542   {
543     GNUNET_break(0);
544     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
545     cleanup_handle (handle);
546     return;
547   }
548
549   egoname = &handle->url[strlen(API_NAMESPACE)+1];
550   for (ego_entry = handle->ego_head;
551        NULL != ego_entry;
552        ego_entry = ego_entry->next)
553   {
554     if (0 == strcasecmp (egoname, ego_entry->identifier))
555     {
556       ego_exists = GNUNET_YES;
557       break;
558     }
559   }
560   if (GNUNET_NO == ego_exists)
561   {
562     GNUNET_break(0);
563     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
564     cleanup_handle (handle);
565     return;
566   }
567   handle->op = GNUNET_IDENTITY_delete (handle->identity_handle,
568                                        egoname,
569                                        &do_finished,
570                                        handle);
571
572 }
573
574 void
575 init_cont (struct RequestHandle *handle)
576 {
577   if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_GET))
578     ego_info_response (handle);
579   else if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_POST))
580     ego_create_cont (handle);
581   else if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_PUT))
582     subsys_set_cont (handle);
583   else if (0 == strcasecmp (handle->method, MHD_HTTP_METHOD_DELETE))
584     ego_delete_cont (handle);
585   else
586     GNUNET_SCHEDULER_add_now (&do_error, handle);
587 }
588
589 /**
590  * If listing is enabled, prints information about the egos.
591  *
592  * This function is initially called for all egos and then again
593  * whenever a ego's identifier changes or if it is deleted.  At the
594  * end of the initial pass over all egos, the function is once called
595  * with 'NULL' for 'ego'. That does NOT mean that the callback won't
596  * be invoked in the future or that there was an error.
597  *
598  * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
599  * this function is only called ONCE, and 'NULL' being passed in
600  * 'ego' does indicate an error (i.e. name is taken or no default
601  * value is known).  If 'ego' is non-NULL and if '*ctx'
602  * is set in those callbacks, the value WILL be passed to a subsequent
603  * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
604  * that one was not NULL).
605  *
606  * When an identity is renamed, this function is called with the
607  * (known) ego but the NEW identifier.
608  *
609  * When an identity is deleted, this function is called with the
610  * (known) ego and "NULL" for the 'identifier'.  In this case,
611  * the 'ego' is henceforth invalid (and the 'ctx' should also be
612  * cleaned up).
613  *
614  * @param cls closure
615  * @param ego ego handle
616  * @param ctx context for application to store data for this ego
617  *                 (during the lifetime of this process, initially NULL)
618  * @param identifier identifier assigned by the user for this ego,
619  *                   NULL if the user just deleted the ego and it
620  *                   must thus no longer be used
621  */
622 static void
623 list_ego (void *cls,
624           struct GNUNET_IDENTITY_Ego *ego,
625           void **ctx,
626           const char *identifier)
627 {
628   struct RequestHandle *handle = cls;
629   struct EgoEntry *ego_entry;
630
631   if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
632   {
633     handle->state = ID_REST_STATE_POST_INIT;
634     init_cont (handle);
635     return;
636   }
637   if (ID_REST_STATE_INIT == handle->state) {
638     ego_entry = GNUNET_new (struct EgoEntry);
639     GNUNET_IDENTITY_ego_get_public_key (ego, &(ego_entry->pk));
640     ego_entry->ego = ego;
641     GNUNET_asprintf (&ego_entry->identifier, "%s", identifier);
642     GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
643   }
644
645 }
646
647 /**
648  * Function processing the REST call
649  *
650  * @param method HTTP method
651  * @param url URL of the HTTP request
652  * @param data body of the HTTP request (optional)
653  * @param data_size length of the body
654  * @param proc callback function for the result
655  * @param proc_cls closure for callback function
656  * @return GNUNET_OK if request accepted
657  */
658 void
659 rest_identity_process_request(struct RestConnectionDataHandle *conndata_handle,
660                               GNUNET_REST_ResultProcessor proc,
661                               void *proc_cls)
662 {
663   struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
664
665
666
667   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
668
669   handle->proc_cls = proc_cls;
670   handle->proc = proc;
671   handle->state = ID_REST_STATE_INIT;
672   handle->conndata_handle = conndata_handle;
673   handle->data = conndata_handle->data;
674   handle->data_size = conndata_handle->data_size;
675   handle->method = conndata_handle->method;
676   GNUNET_asprintf (&handle->url, "%s", conndata_handle->url);
677   if (handle->url[strlen (handle->url)-1] == '/')
678     handle->url[strlen (handle->url)-1] = '\0';
679   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
680               "Connecting...\n");
681   handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
682                                                      &list_ego,
683                                                      handle); 
684   handle->timeout_task =
685     GNUNET_SCHEDULER_add_delayed (handle->timeout,
686                                   &do_error,
687                                   handle);
688
689
690   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
691               "Connected\n");
692 }
693
694 /**
695  * Entry point for the plugin.
696  *
697  * @param cls Config info
698  * @return NULL on error, otherwise the plugin context
699  */
700 void *
701 libgnunet_plugin_rest_identity_init (void *cls)
702 {
703   static struct Plugin plugin;
704   cfg = cls;
705   struct GNUNET_REST_Plugin *api;
706
707   if (NULL != plugin.cfg)
708     return NULL;                /* can only initialize once! */
709   memset (&plugin, 0, sizeof (struct Plugin));
710   plugin.cfg = cfg;
711   api = GNUNET_new (struct GNUNET_REST_Plugin);
712   api->cls = &plugin;
713   api->name = API_NAMESPACE;
714   api->process_request = &rest_identity_process_request;
715   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
716               _("Identity REST API initialized\n"));
717   return api;
718 }
719
720
721 /**
722  * Exit point from the plugin.
723  *
724  * @param cls the plugin context (as returned by "init")
725  * @return always NULL
726  */
727 void *
728 libgnunet_plugin_rest_identity_done (void *cls)
729 {
730   struct GNUNET_REST_Plugin *api = cls;
731   struct Plugin *plugin = api->cls;
732
733   plugin->cfg = NULL;
734   GNUNET_free (api);
735   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
736               "Identity REST plugin is finished\n");
737   return NULL;
738 }
739
740 /* end of plugin_rest_gns.c */