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