REST Service
[oweals/gnunet.git] / src / gns / plugin_rest_gns.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 gns/plugin_rest_gns.c
23  * @brief GNUnet GNS REST plugin
24  *
25  */
26
27 #include "platform.h"
28 #include "gnunet_rest_plugin.h"
29 #include <gnunet_dnsparser_lib.h>
30 #include <gnunet_identity_service.h>
31 #include <gnunet_gnsrecord_lib.h>
32 #include <gnunet_namestore_service.h>
33 #include <gnunet_gns_service.h>
34 #include <jansson.h>
35
36 #define API_NAMESPACE "gns"
37
38 /**
39  * @brief struct returned by the initialization function of the plugin
40  */
41 struct Plugin
42 {
43   const struct GNUNET_CONFIGURATION_Handle *cfg;
44 };
45
46 const struct GNUNET_CONFIGURATION_Handle *cfg;
47
48 struct LookupHandle
49 {
50     /**
51      * Handle to GNS service.
52      */
53     struct GNUNET_GNS_Handle *gns;
54     
55     /**
56      * Desired timeout for the lookup (default is no timeout).
57      */
58     struct GNUNET_TIME_Relative timeout;
59     
60     /**
61      * Handle to lookup request
62      */
63     struct GNUNET_GNS_LookupRequest *lookup_request;
64
65     /**
66      * Lookup an ego with the identity service.
67      */
68     struct GNUNET_IDENTITY_EgoLookup *el;
69
70     /**
71      * Handle for identity service.
72      */
73     struct GNUNET_IDENTITY_Handle *identity;
74
75     /**
76      * Active operation on identity service.
77      */
78     struct GNUNET_IDENTITY_Operation *id_op;
79     /**
80      * ID of a task associated with the resolution process.
81      */
82     struct GNUNET_SCHEDULER_Task * timeout_task;    
83     
84     json_t *json_root;
85
86     GNUNET_REST_ResultProcessor proc;
87
88     char *name;
89
90     const char *ego_str;
91
92     const char *pkey_str;
93
94     int type;
95
96     struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
97
98     struct GNUNET_CRYPTO_EcdsaPublicKey pkeym;
99     
100     enum GNUNET_GNS_LocalOptions options;
101     
102     struct GNUNET_CRYPTO_EcdsaPrivateKey shorten_key;
103     void *proc_cls;
104 };
105
106 void
107 cleanup_handle (struct LookupHandle *handle)
108 {
109     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
110                                     "Cleaning up\n");
111     if (NULL != handle->json_root)
112         json_decref (handle->json_root);
113
114     if (NULL != handle->name)
115        GNUNET_free (handle->name);
116     if (NULL != handle->el)
117     {
118         GNUNET_IDENTITY_ego_lookup_cancel (handle->el);
119         handle->el = NULL;
120     }
121     if (NULL != handle->id_op)
122     {
123         GNUNET_IDENTITY_cancel (handle->id_op);
124         handle->id_op = NULL;
125     }
126     if (NULL != handle->lookup_request)
127     {
128         GNUNET_GNS_lookup_cancel (handle->lookup_request);
129         handle->lookup_request = NULL;
130     }
131     if (NULL != handle->identity)
132     {
133         GNUNET_IDENTITY_disconnect (handle->identity);
134         handle->identity = NULL;
135     }
136     if (NULL != handle->gns)
137     {
138         GNUNET_GNS_disconnect (handle->gns);
139         handle->gns = NULL;
140     }
141
142     if (NULL != handle->timeout_task)
143     {
144         GNUNET_SCHEDULER_cancel (handle->timeout_task);
145     }
146     GNUNET_free (handle);
147 }
148
149
150 /**
151  * Task run on shutdown.  Cleans up everything.
152  *
153  * @param cls unused
154  * @param tc scheduler context
155  */
156 static void
157 do_error (void *cls,
158          const struct GNUNET_SCHEDULER_TaskContext *tc)
159 {
160   struct LookupHandle *handle = cls;
161   
162   cleanup_handle (handle);
163   handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
164 }
165
166 /**
167  * Function called with the result of a GNS lookup.
168  *
169  * @param cls the 'const char *' name that was resolved
170  * @param rd_count number of records returned
171  * @param rd array of @a rd_count records with the results
172  */
173 static void
174 process_lookup_result (void *cls, uint32_t rd_count,
175                const struct GNUNET_GNSRECORD_Data *rd)
176 {
177     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
178                                               "result \n");
179   struct LookupHandle *handle = cls;
180   uint32_t i;
181   const char *typename;
182   char *string_val;
183   char *result;
184   json_t *result_root;
185   json_t *result_name;
186   json_t *result_array;
187   json_t *record_obj;
188
189
190   result_root = json_object();
191   result_name = json_string (handle->name);
192   result_array = json_array();
193   json_object_set (result_root, "name", result_name);
194   json_decref (result_name);
195
196   handle->lookup_request = NULL;
197   
198   for (i=0; i<rd_count; i++)
199   {
200     if ( (rd[i].record_type != handle->type) &&
201      (GNUNET_GNSRECORD_TYPE_ANY != handle->type) )
202       continue;
203     typename = GNUNET_GNSRECORD_number_to_typename (rd[i].record_type);
204     string_val = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
205                            rd[i].data,
206                            rd[i].data_size);
207     if (NULL == string_val)
208     {
209       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
210            "Record %u of type %d malformed, skipping\n",
211            (unsigned int) i,
212            (int) rd[i].record_type);
213       continue;
214     }
215     else
216     {
217       record_obj = json_object();
218       json_object_set_new (record_obj, "type", json_string (typename));
219       json_object_set_new (record_obj, "value", json_string (string_val));
220       json_array_append (result_array, record_obj);
221       json_decref (record_obj);
222     }
223     GNUNET_free (string_val);
224   }
225   json_object_set (result_root, "query_result", result_array);
226   json_decref (result_array);
227   result = json_dumps (result_root, JSON_COMPACT);
228   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Result %s\n", result);
229   json_decref (result_root);
230   handle->proc (handle->proc_cls, result, strlen (result), GNUNET_OK);
231   GNUNET_free (result);
232   cleanup_handle (handle);
233 }
234
235 /**
236  * Perform the actual resolution, starting with the zone
237  * identified by the given public key and the shorten zone.
238  *
239  * @param pkey public key to use for the zone, can be NULL
240  * @param shorten_key private key used for shortening, can be NULL
241  */
242 static void
243 lookup_with_keys (struct LookupHandle *handle, const struct GNUNET_CRYPTO_EcdsaPrivateKey *shorten_key)
244 {
245     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
246                                               "Lookup w/ keys \n");
247   if (UINT32_MAX == handle->type)
248   {
249     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
250              _("Invalid typename specified, assuming `ANY'\n"));
251     handle->type = GNUNET_GNSRECORD_TYPE_ANY;
252   }
253
254   if (NULL != handle->name)
255   {
256     handle->lookup_request = GNUNET_GNS_lookup (handle->gns,
257                     handle->name,
258                     &handle->pkey,
259                     handle->type,
260                     handle->options,
261                     shorten_key,
262                     &process_lookup_result,
263                     handle);
264   }
265   else
266   {
267     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
268          _("Please specify name to lookup!\n"));
269     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
270     cleanup_handle (handle);
271     return;
272   }
273 }
274
275 /**
276  * Method called to with the ego we are to use for shortening
277  * during the lookup.
278  *
279  * @param cls closure contains the public key to use
280  * @param ego ego handle, NULL if not found
281  * @param ctx context for application to store data for this ego
282  *                 (during the lifetime of this process, initially NULL)
283  * @param name name assigned by the user for this ego,
284  *                   NULL if the user just deleted the ego and it
285  *                   must thus no longer be used
286  */
287 static void
288 identity_shorten_cb (void *cls,
289              struct GNUNET_IDENTITY_Ego *ego,
290              void **ctx,
291              const char *name)
292 {
293     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
294                                               "shorten \n");
295   struct LookupHandle *handle = cls;
296
297   handle->id_op = NULL;
298   if (NULL == ego)
299     lookup_with_keys (handle, NULL);
300   else
301     lookup_with_keys (handle,
302               GNUNET_IDENTITY_ego_get_private_key (ego));
303 }
304
305 /**
306  * Perform the actual resolution, starting with the zone
307  * identified by the given public key.
308  *
309  * @param pkey public key to use for the zone
310  */
311 static void
312 lookup_with_public_key (struct LookupHandle *handle)
313 {
314     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
315                                   "Lookup w/ pkey \n");
316   handle->pkeym = handle->pkey;
317   GNUNET_break (NULL == handle->id_op);
318   handle->id_op = GNUNET_IDENTITY_get (handle->identity,
319                    "gns-short",
320                    &identity_shorten_cb,
321                    handle);
322   if (NULL == handle->id_op)
323   {
324     GNUNET_break (0);
325     lookup_with_keys (handle, NULL);
326   }
327 }
328
329 /**
330  * Method called to with the ego we are to use for the lookup,
331  * when the ego is determined by a name.
332  *
333  * @param cls closure (NULL, unused)
334  * @param ego ego handle, NULL if not found
335  */
336 static void
337 identity_zone_cb (void *cls,
338           const struct GNUNET_IDENTITY_Ego *ego)
339 {
340   struct LookupHandle *handle = cls;
341
342   handle->el = NULL;
343   if (NULL == ego)
344   {
345     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
346          _("Ego for not found, cannot perform lookup.\n"));
347     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
348     cleanup_handle (handle);
349     return;
350   }
351   else
352   {
353     GNUNET_IDENTITY_ego_get_public_key (ego, &handle->pkey);
354     lookup_with_public_key (handle);
355   }
356   json_decref(handle->json_root);
357 }
358
359 /**
360  * Method called to with the ego we are to use for the lookup,
361  * when the ego is the one for the default master zone.
362  *
363  * @param cls closure (NULL, unused)
364  * @param ego ego handle, NULL if not found
365  * @param ctx context for application to store data for this ego
366  *                 (during the lifetime of this process, initially NULL)
367  * @param name name assigned by the user for this ego,
368  *                   NULL if the user just deleted the ego and it
369  *                   must thus no longer be used
370  */
371 static void
372 identity_master_cb (void *cls,
373             struct GNUNET_IDENTITY_Ego *ego,
374             void **ctx,
375             const char *name)
376 {
377   const char *dot;
378   struct LookupHandle *handle = cls;
379     
380   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
381                       "Master cb \n");
382   handle->id_op = NULL;
383   if (NULL == ego)
384   {
385     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
386          _("Ego for `gns-master' not found, cannot perform lookup.  Did you run gnunet-gns-import.sh?\n"));
387     handle->proc (handle->proc_cls, NULL, 0, GNUNET_SYSERR);
388     cleanup_handle (handle);
389     return;
390   }
391   GNUNET_IDENTITY_ego_get_public_key (ego, &handle->pkey);
392   /* main name is our own master zone, do no look for that in the DHT */
393   handle->options = GNUNET_GNS_LO_LOCAL_MASTER;
394   /* if the name is of the form 'label.gnu', never go to the DHT */
395   dot = NULL;
396   if (NULL != handle->name)
397     dot = strchr (handle->name, '.');
398   if ( (NULL != dot) &&
399        (0 == strcasecmp (dot, ".gnu")) )
400     handle->options = GNUNET_GNS_LO_NO_DHT;
401   lookup_with_public_key (handle);
402 }
403
404 int
405 parse_url (const char *url, struct LookupHandle *handle)
406 {
407     char *name;
408     char *type;
409     char tmp_url[strlen(url)+1];
410     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Parsing %s\n", url);
411     strcpy (tmp_url, url);
412
413     char *tok = strtok ((char*)tmp_url, "/");
414     
415     
416     if (NULL == tok)
417         return GNUNET_SYSERR;
418
419     name = strtok (NULL, "/");
420
421     if (NULL == name)
422         return GNUNET_SYSERR;
423
424     GNUNET_asprintf (&handle->name,
425                      "%s",
426                      name);
427
428     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
429             "Got name: %s\n", handle->name);
430
431     type = strtok (NULL, "/");
432
433     if (NULL == type)
434     {
435         handle->type = GNUNET_GNSRECORD_TYPE_ANY;
436         return GNUNET_OK;
437     }
438
439     handle->type = GNUNET_GNSRECORD_typename_to_number (type);
440  
441     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
442             "Got type: %s\n", type);   
443     return GNUNET_OK;
444 }
445
446 int
447 parse_json (const char *data, size_t data_size, struct LookupHandle *handle)
448 {
449     json_error_t error;
450     json_t *pkey_json;
451     json_t *ego_json;
452     json_t *options_json;
453
454     char term_data[data_size+1];
455     term_data[data_size] = '\0';
456
457     memcpy (term_data, data, data_size);
458
459     handle->json_root = json_loads (term_data, 0, &error);
460
461     if (NULL == handle->json_root)
462     {
463         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, error.text);
464         return GNUNET_SYSERR;
465     }
466
467     if(!json_is_object(handle->json_root))
468     {
469         return GNUNET_SYSERR;
470     }
471    
472     ego_json = json_object_get (handle->json_root, "ego");
473     
474     if(json_is_string(ego_json))
475     {
476         handle->ego_str = json_string_value (ego_json);
477     }
478
479     pkey_json = json_object_get (handle->json_root, "pkey");
480     if(json_is_string(pkey_json))
481     {
482         handle->pkey_str = json_string_value (pkey_json);
483     }
484
485     options_json = json_object_get (handle->json_root, "options");
486     if(json_is_integer (options_json))
487     {
488         handle->options = json_integer_value (options_json);
489     }
490     return GNUNET_OK;
491 }
492
493
494 /**
495  * Function processing the REST call
496  *
497  * @param method HTTP method
498  * @param url URL of the HTTP request
499  * @param data body of the HTTP request (optional)
500  * @param data_size length of the body
501  * @param proc callback function for the result
502  * @param proc_cls closure for callback function
503  * @return GNUNET_OK if request accepted
504  */
505 void
506 rest_gns_process_request(const char *method,
507                          const char *url,
508                          const char *data,
509                          size_t data_size,
510                          GNUNET_REST_ResultProcessor proc,
511                          void *proc_cls)
512 {
513     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
514                         "In plugin\n");
515     struct LookupHandle *handle = GNUNET_new (struct LookupHandle);
516     
517     handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL; //TODO read from json
518
519     //parse name and type from url
520     if (GNUNET_OK != parse_url (url, handle))
521     {
522         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error parsing url...\n");
523         proc (proc_cls, NULL, 0, GNUNET_SYSERR);
524         cleanup_handle (handle);
525         return;
526     }
527  
528     handle->proc_cls = proc_cls;
529     handle->proc = proc;
530  
531     if (0 < data_size)
532     {
533         if (GNUNET_OK != parse_json (data, data_size, handle))
534         {
535             GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Error parsing json...\n");
536             proc (proc_cls, NULL, 0, GNUNET_SYSERR);
537             cleanup_handle (handle);
538             return;
539         }
540     }
541     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
542                         "Connecting...\n");
543     handle->gns = GNUNET_GNS_connect (cfg);
544     handle->identity = GNUNET_IDENTITY_connect (cfg, NULL, NULL);
545
546     handle->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->timeout,
547                                   &do_error, handle);
548     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
549                         "Connected\n");
550
551     if (NULL == handle->gns)
552     {
553         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
554                                         "Connecting to GNS failed\n");
555         proc (proc_cls, NULL, 0, GNUNET_SYSERR);
556         cleanup_handle (handle);
557         return;
558     }
559
560     if (NULL != handle->pkey_str)
561     {
562         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
563                                       "pkey_str\n");
564         if (GNUNET_OK !=
565         GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->pkey_str,
566                                                     strlen(handle->pkey_str),
567                                                     &(handle->pkey)))
568         {
569             proc (proc_cls, NULL, 0, GNUNET_SYSERR);
570             cleanup_handle (handle);
571             return;
572         }
573         lookup_with_public_key (handle);
574
575     }
576     if (NULL != handle->ego_str)
577     {
578         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
579                                       "ego_str\n");
580         handle->el = GNUNET_IDENTITY_ego_lookup (cfg,
581                 handle->ego_str,
582                 &identity_zone_cb,
583                 handle);
584         return;
585     }
586     if ( (NULL != handle->name) &&
587             (strlen (handle->name) > 4) &&
588             (0 == strcmp (".zkey",
589                           &handle->name[strlen (handle->name) - 4])) )
590     {
591         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
592                                       "zkey\n");
593         /* no zone required, use 'anonymous' zone */
594         GNUNET_CRYPTO_ecdsa_key_get_public
595             (GNUNET_CRYPTO_ecdsa_key_get_anonymous (),
596              &(handle->pkey));
597         lookup_with_public_key (handle);
598     }
599     else
600     {
601         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
602                                       "gns_master\n");
603         GNUNET_break (NULL == handle->id_op);
604         handle->id_op = GNUNET_IDENTITY_get (handle->identity,
605                 "gns-master",
606                 &identity_master_cb,
607                 handle);
608         GNUNET_assert (NULL != handle->id_op);
609     }
610 }
611
612 /**
613  * Entry point for the plugin.
614  *
615  * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
616  * @return NULL on error, otherwise the plugin context
617  */
618 void *
619 libgnunet_plugin_rest_gns_init (void *cls)
620 {
621   static struct Plugin plugin;
622   cfg = cls;
623   struct GNUNET_REST_Plugin *api;
624
625   if (NULL != plugin.cfg)
626     return NULL;                /* can only initialize once! */
627   memset (&plugin, 0, sizeof (struct Plugin));
628   plugin.cfg = cfg;
629   api = GNUNET_new (struct GNUNET_REST_Plugin);
630   api->cls = &plugin;
631   api->name = API_NAMESPACE;
632   api->process_request = &rest_gns_process_request;
633   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
634        _("GNS REST API initialized\n"));
635   return api;
636 }
637
638
639 /**
640  * Exit point from the plugin.
641  *
642  * @param cls the plugin context (as returned by "init")
643  * @return always NULL
644  */
645 void *
646 libgnunet_plugin_namestore_sqlite_done (void *cls)
647 {
648   struct GNUNET_REST_Plugin *api = cls;
649   struct Plugin *plugin = api->cls;
650
651   plugin->cfg = NULL;
652   GNUNET_free (api);
653   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
654        "GNS REST plugin is finished\n");
655   return NULL;
656 }
657
658 /* end of plugin_rest_gns.c */