- build rest before jsonapi
[oweals/gnunet.git] / src / namestore / plugin_rest_namestore.c
1 /*
2    This file is part of GNUnet.
3    Copyright (C) 2012-2015 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18    Boston, MA 02110-1301, USA.
19    */
20 /**
21  * @author Martin Schanzenbach
22  * @file namestore/plugin_rest_namestore.c
23  * @brief GNUnet Namestore REST plugin
24  *
25  */
26
27 #include "platform.h"
28 #include "gnunet_rest_plugin.h"
29 #include "gnunet_namestore_service.h"
30 #include "gnunet_identity_service.h"
31 #include "gnunet_rest_lib.h"
32 #include "gnunet_jsonapi_lib.h"
33 #include "microhttpd.h"
34 #include <jansson.h>
35
36 #define GNUNET_REST_API_NS_NAMESTORE "/names"
37
38 #define GNUNET_REST_API_NS_NAMESTORE_ZKEY "/names/zkey"
39
40 #define GNUNET_REST_JSONAPI_NAMESTORE_TYPEINFO "record"
41
42 #define GNUNET_REST_JSONAPI_NAMESTORE_NAME "name"
43
44 #define GNUNET_REST_JSONAPI_NAMESTORE_REVINFO "revinfo"
45
46 #define GNUNET_REST_JSONAPI_NAMESTORE_RECORD GNUNET_REST_JSONAPI_NAMESTORE_TYPEINFO
47
48 #define GNUNET_REST_JSONAPI_NAMESTORE_RECORD_TYPE "record_type"
49
50 #define GNUNET_REST_JSONAPI_NAMESTORE_VALUE "value"
51
52 #define GNUNET_REST_JSONAPI_NAMESTORE_PUBLIC "public"
53
54 #define GNUNET_REST_JSONAPI_NAMESTORE_SHADOW "shadow"
55
56 #define GNUNET_REST_JSONAPI_NAMESTORE_PKEY "pkey"
57
58 #define GNUNET_REST_JSONAPI_NAMESTORE_ZKEY "zkey"
59
60 #define GNUNET_REST_JSONAPI_NAMESTORE_EXPIRATION "expiration"
61
62 #define GNUNET_REST_JSONAPI_NAMESTORE_EGO "ego"
63
64 /**
65  * @brief struct returned by the initialization function of the plugin
66  */
67 struct Plugin
68 {
69   const struct GNUNET_CONFIGURATION_Handle *cfg;
70 };
71
72
73 /**
74  * HTTP methods allows for this plugin
75  */
76 static char* allow_methods;
77
78 const struct GNUNET_CONFIGURATION_Handle *cfg;
79
80 struct RecordEntry
81 {
82   /**
83    * DLL
84    */
85   struct RecordEntry *next;
86
87   /**
88    * DLL
89    */
90   struct RecordEntry *prev;
91
92 };
93
94 struct RequestHandle
95 {
96   /**
97    * Ego list
98    */
99   struct RecordEntry *record_head;
100
101   /**
102    * Ego list
103    */
104   struct record_entry *record_tail;
105
106   /**
107    * JSON response object
108    */
109   struct GNUNET_JSONAPI_Document *resp_object;
110
111   /**
112    * Rest connection
113    */
114   struct GNUNET_REST_RequestHandle *rest_handle;
115
116   /**
117    * Handle to GNS service.
118    */
119   struct GNUNET_IDENTITY_Handle *identity_handle;
120
121   /**
122    * Handle to NAMESTORE
123    */
124   struct GNUNET_NAMESTORE_Handle *ns_handle;
125
126   /**
127    * Handle to NAMESTORE it
128    */
129   struct GNUNET_NAMESTORE_ZoneIterator *list_it;
130
131   /**
132    * Private key for the zone
133    */
134   struct GNUNET_CRYPTO_EcdsaPrivateKey zone_pkey;
135
136   /**
137    * Handle to identity lookup
138    */
139   struct GNUNET_IDENTITY_EgoLookup *ego_lookup;
140
141   /**
142    * Default Ego operation
143    */
144   struct GNUNET_IDENTITY_Operation *get_default;
145
146   /**
147    * Name of the ego
148    */
149   char *ego_name;
150
151   /**
152    * Record is public
153    */
154   int is_public;
155
156   /**
157    * Shadow record
158    */
159   int is_shadow;
160
161   /**
162    * Name of the record to modify
163    */
164   char *name;
165
166   /**
167    * Value of the record
168    */
169   char *value;
170
171   /**
172    * Zkey string
173    */
174   const char* zkey_str;
175
176   /**
177    * record type
178    */
179   uint32_t type;
180
181   /**
182    * Records to store
183    */
184   struct GNUNET_GNSRECORD_Data *rd;
185
186   /**
187    * record count
188    */
189   unsigned int rd_count;
190
191   /**
192    * NAMESTORE Operation
193    */
194   struct GNUNET_NAMESTORE_QueueEntry *add_qe;
195
196   /**
197    * NAMESTORE Operation
198    */
199   struct GNUNET_NAMESTORE_QueueEntry *reverse_qe;
200
201   /**
202    * Desired timeout for the lookup (default is no timeout).
203    */
204   struct GNUNET_TIME_Relative timeout;
205
206   /**
207    * ID of a task associated with the resolution process.
208    */
209   struct GNUNET_SCHEDULER_Task * timeout_task;
210
211   /**
212    * The plugin result processor
213    */
214   GNUNET_REST_ResultProcessor proc;
215
216   /**
217    * The closure of the result processor
218    */
219   void *proc_cls;
220
221   /**
222    * The url
223    */
224   char *url;
225
226   /**
227    * Cfg
228    */
229   const struct GNUNET_CONFIGURATION_Handle *cfg;
230
231   /**
232    * HTTP response code
233    */
234   int response_code;
235
236 };
237
238
239 /**
240  * Cleanup lookup handle
241  *
242  * @param handle Handle to clean up
243  */
244 static void
245 cleanup_handle (struct RequestHandle *handle)
246 {
247   struct RecordEntry *record_entry;
248   struct RecordEntry *record_tmp;
249   int i;
250
251   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
252               "Cleaning up\n");
253   if (NULL != handle->resp_object)
254     GNUNET_JSONAPI_document_delete (handle->resp_object);
255   if (NULL != handle->name)
256     GNUNET_free (handle->name);
257   if (NULL != handle->timeout_task)
258     GNUNET_SCHEDULER_cancel (handle->timeout_task);
259   if (NULL != handle->ego_lookup)
260     GNUNET_IDENTITY_ego_lookup_cancel (handle->ego_lookup);
261   if (NULL != handle->get_default)
262     GNUNET_IDENTITY_cancel (handle->get_default);
263   if (NULL != handle->list_it)
264     GNUNET_NAMESTORE_zone_iteration_stop (handle->list_it);
265   if (NULL != handle->add_qe)
266     GNUNET_NAMESTORE_cancel (handle->add_qe);
267   if (NULL != handle->identity_handle)
268     GNUNET_IDENTITY_disconnect (handle->identity_handle);
269   if (NULL != handle->ns_handle)
270     GNUNET_NAMESTORE_disconnect (handle->ns_handle);
271   if (NULL != handle->url)
272     GNUNET_free (handle->url);
273   if (NULL != handle->value)
274     GNUNET_free (handle->value);
275   if (NULL != handle->rd)
276   {
277     for (i = 0; i < handle->rd_count; i++)
278     {
279       if (NULL != handle->rd[i].data)
280         GNUNET_free ((void*)handle->rd[i].data);
281     }
282     GNUNET_free (handle->rd);
283   }
284   if (NULL != handle->ego_name)
285     GNUNET_free (handle->ego_name);
286   for (record_entry = handle->record_head;
287        NULL != record_entry;)
288   {
289     record_tmp = record_entry;
290     record_entry = record_entry->next;
291     GNUNET_free (record_tmp);
292   }
293   GNUNET_free (handle);
294 }
295
296
297 /**
298  * Create json representation of a GNSRECORD
299  *
300  * @param rd the GNSRECORD_Data
301  */
302 static json_t *
303 gnsrecord_to_json (const struct GNUNET_GNSRECORD_Data *rd)
304 {
305   const char *typename;
306   char *string_val;
307   const char *exp_str;
308   json_t *record_obj;
309
310   typename = GNUNET_GNSRECORD_number_to_typename (rd->record_type);
311   string_val = GNUNET_GNSRECORD_value_to_string (rd->record_type,
312                                                  rd->data,
313                                                  rd->data_size);
314
315   if (NULL == string_val)
316   {
317     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
318                 "Record of type %d malformed, skipping\n",
319                 (int) rd->record_type);
320     return NULL;
321   }
322   record_obj = json_object();
323   json_object_set_new (record_obj,
324                        GNUNET_REST_JSONAPI_NAMESTORE_RECORD_TYPE,
325                        json_string (typename));
326   json_object_set_new (record_obj,
327                        GNUNET_REST_JSONAPI_NAMESTORE_VALUE,
328                        json_string (string_val));
329   GNUNET_free (string_val);
330
331   if (GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION & rd->flags)
332   {
333     struct GNUNET_TIME_Relative time_rel;
334     time_rel.rel_value_us = rd->expiration_time;
335     exp_str = GNUNET_STRINGS_relative_time_to_string (time_rel, 1);
336   }
337   else
338   {
339     struct GNUNET_TIME_Absolute time_abs;
340     time_abs.abs_value_us = rd->expiration_time;
341     exp_str = GNUNET_STRINGS_absolute_time_to_string (time_abs);
342   }
343   json_object_set_new (record_obj, GNUNET_REST_JSONAPI_NAMESTORE_EXPIRATION, json_string (exp_str));
344
345   json_object_set_new (record_obj, "expired",
346                        json_boolean (GNUNET_YES == GNUNET_GNSRECORD_is_expired (rd)));
347   return record_obj;
348 }
349
350
351 /**
352  * Task run on error.  Generates error response and cleans up.
353  *
354  * @param cls the request to generate an error response for
355  */
356 static void
357 do_error (void *cls)
358 {
359   struct RequestHandle *handle = cls;
360   struct MHD_Response *resp = GNUNET_REST_create_json_response (NULL);
361
362   handle->proc (handle->proc_cls, resp, handle->response_code);
363   cleanup_handle (handle);
364 }
365
366
367 /**
368  * Task run on timeout.
369  *
370  * @param cls the request to time out
371  */
372 static void
373 do_timeout (void *cls)
374 {
375   struct RequestHandle *handle = cls;
376
377   handle->timeout_task = NULL;
378   do_error (handle);
379 }
380
381
382 static void
383 cleanup_handle_delayed (void *cls)
384 {
385   cleanup_handle (cls);
386 }
387
388
389 /**
390  * Create a response with requested records
391  *
392  * @param handle the RequestHandle
393  */
394 static void
395 namestore_list_response (void *cls,
396                          const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
397                          const char *rname,
398                          unsigned int rd_len,
399                          const struct GNUNET_GNSRECORD_Data *rd)
400 {
401   struct RequestHandle *handle = cls;
402   struct GNUNET_JSONAPI_Resource *json_resource;
403   struct MHD_Response *resp;
404   json_t *result_array;
405   json_t *record_obj;
406   int i;
407   char *result;
408
409   if (NULL == handle->resp_object)
410     handle->resp_object = GNUNET_JSONAPI_document_new ();
411
412   if (NULL == rname)
413   {
414     handle->list_it = NULL;
415     //Handle response
416     if (GNUNET_SYSERR == GNUNET_JSONAPI_document_serialize (handle->resp_object, &result))
417     {
418       GNUNET_SCHEDULER_add_now (&do_error, handle);
419       return;
420     }
421     resp = GNUNET_REST_create_json_response (result);
422     handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
423     GNUNET_free (result);
424     GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
425     return;
426   }
427
428   if ( (NULL != handle->name) &&
429        (0 != strcmp (handle->name, rname)) )
430   {
431     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
432                 "%s does not match %s\n", rname, handle->name);
433     GNUNET_NAMESTORE_zone_iterator_next (handle->list_it);
434     return;
435   }
436
437   result_array = json_array ();
438   for (i=0; i<rd_len; i++)
439   {
440     if ( (GNUNET_GNSRECORD_TYPE_NICK == rd[i].record_type) &&
441          (0 != strcmp (rname, "+")) )
442       continue;
443
444     if ( (rd[i].record_type != handle->type) &&
445          (GNUNET_GNSRECORD_TYPE_ANY != handle->type) )
446       continue;
447     record_obj = gnsrecord_to_json (&(rd[i]));
448     json_array_append (result_array, record_obj);
449     json_decref (record_obj);
450   }
451
452   if (0 < json_array_size(result_array))
453   {
454     json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_NAMESTORE_TYPEINFO,
455                                                       rname);
456     GNUNET_JSONAPI_resource_add_attr (json_resource,
457                                            GNUNET_REST_JSONAPI_NAMESTORE_RECORD,
458                                            result_array);
459     GNUNET_JSONAPI_document_resource_add (handle->resp_object, json_resource);
460   }
461
462   json_decref (result_array);
463   GNUNET_NAMESTORE_zone_iterator_next (handle->list_it);
464 }
465
466 static void
467 create_finished (void *cls, int32_t success, const char *emsg)
468 {
469   struct RequestHandle *handle = cls;
470   struct MHD_Response *resp;
471
472   handle->add_qe = NULL;
473   if (GNUNET_YES != success)
474   {
475     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
476                 "Error storing records%s%s\n",
477                 (NULL == emsg) ? "" : ": ",
478                 (NULL == emsg) ? "" : emsg);
479     GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
480     return;
481   }
482   resp = GNUNET_REST_create_json_response (NULL);
483   handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
484   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
485 }
486
487
488 /**
489  * We're storing a new record; this requires
490  * that no record already exists
491  *
492  * @param cls closure, unused
493  * @param zone_key private key of the zone
494  * @param rec_name name that is being mapped (at most 255 characters long)
495  * @param rd_count number of entries in @a rd array
496  * @param rd array of records with data to store
497  */
498 static void
499 create_new_record_cont (void *cls,
500                         const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
501                         const char *rec_name,
502                         unsigned int rd_count,
503                         const struct GNUNET_GNSRECORD_Data *rd)
504 {
505   struct RequestHandle *handle = cls;
506
507   handle->add_qe = NULL;
508   if ( (NULL != zone_key) &&
509        (0 != strcmp (rec_name, handle->name)) )
510   {
511     GNUNET_break (0);
512     GNUNET_SCHEDULER_add_now (&do_error, handle);
513     return;
514   }
515
516   if (0 != rd_count)
517   {
518     handle->proc (handle->proc_cls,
519                   GNUNET_REST_create_json_response (NULL),
520                   MHD_HTTP_CONFLICT);
521     GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
522     return;
523   }
524
525   GNUNET_assert (NULL != handle->name);
526   handle->add_qe = GNUNET_NAMESTORE_records_store (handle->ns_handle,
527                                                    &handle->zone_pkey,
528                                                    handle->name,
529                                                    handle->rd_count,
530                                                    handle->rd,
531                                                    &create_finished,
532                                                    handle);
533 }
534
535 static void
536 del_finished (void *cls,
537               int32_t success,
538               const char *emsg)
539 {
540   struct RequestHandle *handle = cls;
541
542   handle->add_qe = NULL;
543   if (GNUNET_NO == success)
544   {
545     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
546                 _("Deleting record failed, record does not exist%s%s\n"),
547                 (NULL != emsg) ? ": " : "",
548                 (NULL != emsg) ? emsg : "");
549     GNUNET_SCHEDULER_add_now (&do_error, handle); //do_not_found TODO
550     return;
551   }
552   if (GNUNET_SYSERR == success)
553   {
554     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
555                 _("Deleting record failed%s%s\n"),
556                 (NULL != emsg) ? ": " : "",
557                 (NULL != emsg) ? emsg : "");
558     GNUNET_SCHEDULER_add_now (&do_error, handle);
559     return;
560   }
561   handle->proc (handle->proc_cls,
562                 GNUNET_REST_create_json_response (NULL),
563                 MHD_HTTP_NO_CONTENT);
564   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
565 }
566
567 static void
568 del_cont (void *cls,
569           const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
570           const char *label,
571           unsigned int rd_count,
572           const struct GNUNET_GNSRECORD_Data *rd)
573 {
574   struct RequestHandle *handle = cls;
575   handle->add_qe = NULL;
576   if (0 == rd_count)
577   {
578     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
579                 _("There are no records under label `%s' that could be deleted.\n"),
580                 label);
581     GNUNET_SCHEDULER_add_now (&do_error, handle);
582     return;
583   }
584
585   handle->add_qe = GNUNET_NAMESTORE_records_store (handle->ns_handle,
586                                                    &handle->zone_pkey,
587                                                    handle->name,
588                                                    0, NULL,
589                                                    &del_finished,
590                                                    handle);
591 }
592
593 static void
594 namestore_delete_cont (struct GNUNET_REST_RequestHandle *con,
595                        const char *url,
596                        void *cls)
597 {
598   struct RequestHandle *handle = cls;
599
600   if (NULL == handle->name)
601   {
602     GNUNET_SCHEDULER_add_now (&do_error, handle);
603     return;
604   }
605
606   handle->add_qe = GNUNET_NAMESTORE_records_lookup (handle->ns_handle,
607                                                     &handle->zone_pkey,
608                                                     handle->name,
609                                                     &del_cont,
610                                                     handle);
611 }
612
613 static int
614 json_to_gnsrecord (const json_t *records_json,
615                    struct GNUNET_GNSRECORD_Data **rd,
616                    unsigned int *rd_count)
617 {
618   struct GNUNET_TIME_Relative etime_rel;
619   struct GNUNET_TIME_Absolute etime_abs;
620   char *value;
621   void *rdata;
622   size_t rdata_size;
623   const char *typestring;
624   const char *expirationstring;
625   int i;
626   json_t *type_json;
627   json_t *value_json;
628   json_t *record_json;
629   json_t *exp_json;
630
631   *rd_count = json_array_size (records_json);
632   *rd = GNUNET_malloc (sizeof (struct GNUNET_GNSRECORD_Data) * *rd_count);
633   for (i = 0; i < *rd_count; i++)
634   {
635     memset (&((*rd)[i]), 0, sizeof (struct GNUNET_GNSRECORD_Data));
636     record_json = json_array_get (records_json, i);
637     type_json = json_object_get (record_json,
638                                  GNUNET_REST_JSONAPI_NAMESTORE_RECORD_TYPE);
639     if (!json_is_string (type_json))
640     {
641       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
642                   "Type property is no string\n");
643       return GNUNET_SYSERR;
644     }
645     typestring = json_string_value (type_json);
646     (*rd)[i].record_type = GNUNET_GNSRECORD_typename_to_number (typestring);
647     if (UINT32_MAX == (*rd)[i].record_type)
648     {
649       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Unsupported type `%s'\n"),
650                   json_string_value (type_json));
651       return GNUNET_SYSERR;
652     }
653     value_json = json_object_get (record_json,
654                                   GNUNET_REST_JSONAPI_NAMESTORE_VALUE);
655     if (!json_is_string (value_json))
656     {
657       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
658                   "Value property is no string\n");
659       return GNUNET_SYSERR;
660     }
661     value = GNUNET_strdup (json_string_value (value_json));
662     if (GNUNET_OK != GNUNET_GNSRECORD_string_to_value ((*rd)[i].record_type,
663                                                        value,
664                                                        &rdata,
665                                                        &rdata_size))
666     {
667       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Value `%s' invalid for record type `%s'\n"),
668                   value, typestring);
669       return GNUNET_SYSERR;
670     }
671     (*rd)[i].data = rdata;
672     (*rd)[i].data_size = rdata_size;
673     /**TODO
674      * if (1 == handle->is_shadow)
675      rde->flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
676      if (1 != handle->is_public)
677      rde->flags |= GNUNET_GNSRECORD_RF_PRIVATE;
678      */
679     exp_json = json_object_get (record_json,
680                                 GNUNET_REST_JSONAPI_NAMESTORE_EXPIRATION);
681     if (!json_is_string (exp_json))
682     {
683       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
684                   "Expiration property is no string\n");
685       return GNUNET_SYSERR;
686     }
687     expirationstring = json_string_value (exp_json);
688     if (0 == strcmp (expirationstring, "never"))
689     {
690       (*rd)[i].expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
691     }
692     else if (GNUNET_OK ==
693              GNUNET_STRINGS_fancy_time_to_relative (expirationstring,
694                                                     &etime_rel))
695     {
696       (*rd)[i].expiration_time = etime_rel.rel_value_us;
697       (*rd)[i].flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
698     }
699     else if (GNUNET_OK ==
700              GNUNET_STRINGS_fancy_time_to_absolute (expirationstring,
701                                                     &etime_abs))
702     {
703       (*rd)[i].expiration_time = etime_abs.abs_value_us;
704     }
705     else
706     {
707       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Value `%s' invalid for record type `%s'\n"),
708                   value, typestring);
709       return GNUNET_SYSERR;
710     }
711   }
712   return GNUNET_OK;
713 }
714
715 static void
716 namestore_create_cont (struct GNUNET_REST_RequestHandle *con,
717                        const char *url,
718                        void *cls)
719 {
720   struct RequestHandle *handle = cls;
721   struct MHD_Response *resp;
722   struct GNUNET_JSONAPI_Document *json_obj;
723   struct GNUNET_JSONAPI_Resource *json_res;
724   json_t *records_json;
725   char term_data[handle->rest_handle->data_size+1];
726
727   if (strlen (GNUNET_REST_API_NS_NAMESTORE) != strlen (handle->url))
728   {
729     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
730                 "Cannot create under %s\n", handle->url);
731     GNUNET_SCHEDULER_add_now (&do_error, handle);
732     return;
733   }
734   if (0 >= handle->rest_handle->data_size)
735   {
736     GNUNET_SCHEDULER_add_now (&do_error, handle);
737     return;
738   }
739   term_data[handle->rest_handle->data_size] = '\0';
740   memcpy (term_data,
741           handle->rest_handle->data,
742           handle->rest_handle->data_size);
743   GNUNET_assert (GNUNET_OK == GNUNET_JSONAPI_document_parse (term_data,
744                                                            &json_obj));
745   if (NULL == json_obj)
746   {
747     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
748                 "Unable to parse JSONAPI Object from %s\n",
749                 term_data);
750     GNUNET_SCHEDULER_add_now (&do_error, handle);
751     return;
752   }
753   if (1 != GNUNET_JSONAPI_document_resource_count (json_obj))
754   {
755     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
756                 "Cannot create more than 1 resource! (Got %d)\n",
757                 GNUNET_JSONAPI_document_resource_count (json_obj));
758     GNUNET_JSONAPI_document_delete (json_obj);
759     GNUNET_SCHEDULER_add_now (&do_error, handle);
760     return;
761   }
762   json_res = GNUNET_JSONAPI_document_get_resource (json_obj, 0);
763   if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type (json_res,
764                                                             GNUNET_REST_JSONAPI_NAMESTORE_RECORD))
765   {
766     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
767                 "Unsupported JSON data type\n");
768     GNUNET_JSONAPI_document_delete (json_obj);
769     resp = GNUNET_REST_create_json_response (NULL);
770     handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
771     cleanup_handle (handle);
772     return;
773   }
774   handle->name = GNUNET_strdup (GNUNET_JSONAPI_resource_get_id (json_res));
775   records_json = GNUNET_JSONAPI_resource_read_attr (json_res,
776                                                          GNUNET_REST_JSONAPI_NAMESTORE_RECORD);
777   if (NULL == records_json)
778   {
779     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
780                 "No records given\n");
781     GNUNET_JSONAPI_document_delete (json_obj);
782     GNUNET_SCHEDULER_add_now (&do_error, handle);
783     return;
784   }
785   if (GNUNET_SYSERR == json_to_gnsrecord (records_json, &handle->rd, &handle->rd_count))
786   {
787     GNUNET_JSONAPI_document_delete (json_obj);
788     GNUNET_SCHEDULER_add_now (&do_error, handle);
789     return;
790   }
791   GNUNET_JSONAPI_document_delete (json_obj);
792
793   handle->add_qe = GNUNET_NAMESTORE_records_lookup (handle->ns_handle,
794                                                     &handle->zone_pkey,
795                                                     handle->name,
796                                                     &create_new_record_cont, handle );
797 }
798
799 static void
800 namestore_zkey_response (void *cls,
801                          const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
802                          const char *label,
803                          unsigned int rd_count,
804                          const struct GNUNET_GNSRECORD_Data *rd)
805 {
806   struct RequestHandle *handle = cls;
807   struct MHD_Response *resp;
808   struct GNUNET_JSONAPI_Document *json_obj;
809   struct GNUNET_JSONAPI_Resource *json_res;
810   json_t *name_json;
811   char* result;
812
813   handle->reverse_qe = NULL;
814   json_obj = GNUNET_JSONAPI_document_new ();
815   if (NULL != label)
816   {
817     name_json = json_string (label);
818     json_res = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_NAMESTORE_REVINFO,
819                                                  handle->zkey_str);
820     GNUNET_JSONAPI_resource_add_attr (json_res,
821                                            GNUNET_REST_JSONAPI_NAMESTORE_NAME,
822                                            name_json);
823     GNUNET_JSONAPI_document_resource_add (json_obj, json_res);
824     json_decref (name_json);
825   }
826   //Handle response
827   if (GNUNET_SYSERR == GNUNET_JSONAPI_document_serialize (json_obj, &result))
828   {
829     GNUNET_JSONAPI_document_delete (json_obj);
830     GNUNET_SCHEDULER_add_now (&do_error, handle);
831     return;
832   }
833   resp = GNUNET_REST_create_json_response (result);
834   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
835   GNUNET_JSONAPI_document_delete (json_obj);
836   GNUNET_free (result);
837   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
838   return;
839
840 }
841
842 static void
843 namestore_zkey_cont (struct GNUNET_REST_RequestHandle *con,
844                      const char *url,
845                      void *cls)
846 {
847   struct RequestHandle *handle = cls;
848   struct GNUNET_HashCode key;
849   struct GNUNET_CRYPTO_EcdsaPublicKey pubkey;
850
851   GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_NAMESTORE_ZKEY,
852                       strlen (GNUNET_REST_JSONAPI_NAMESTORE_ZKEY),
853                       &key);
854   if ( GNUNET_NO ==
855        GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
856                                                &key) )
857   {
858     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
859                 "No zkey given %s\n", handle->url);
860     GNUNET_SCHEDULER_add_now (&do_error, handle);
861     return;
862   }
863   handle->zkey_str = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
864                                             &key);
865   if (GNUNET_OK !=
866       GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->zkey_str,
867                                                   strlen (handle->zkey_str),
868                                                   &pubkey))
869   {
870     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
871                 "Zkey invalid %s\n", handle->zkey_str);
872     GNUNET_SCHEDULER_add_now (&do_error, handle);
873     return;
874   }
875   handle->reverse_qe = GNUNET_NAMESTORE_zone_to_name (handle->ns_handle,
876                                                       &handle->zone_pkey,
877                                                       &pubkey,
878                                                       &namestore_zkey_response,
879                                                       handle);
880 }
881
882 static void
883 namestore_info_cont (struct GNUNET_REST_RequestHandle *con,
884                      const char *url,
885                      void *cls)
886 {
887   struct RequestHandle *handle = cls;
888   handle->list_it = GNUNET_NAMESTORE_zone_iteration_start (handle->ns_handle,
889                                                            &handle->zone_pkey,
890                                                            &namestore_list_response,
891                                                            handle);
892 }
893
894 static char*
895 get_name_from_url (const char* url)
896 {
897   if (strlen (url) <= strlen (GNUNET_REST_API_NS_NAMESTORE))
898     return NULL;
899   return (char*)url + strlen (GNUNET_REST_API_NS_NAMESTORE) + 1;
900 }
901
902 /**
903  * Respond to OPTIONS request
904  *
905  * @param con_handle the connection handle
906  * @param url the url
907  * @param cls the RequestHandle
908  */
909 static void
910 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
911               const char* url,
912               void *cls)
913 {
914   struct MHD_Response *resp;
915   struct RequestHandle *handle = cls;
916
917   //For now, independent of path return all options
918   resp = GNUNET_REST_create_json_response (NULL);
919   MHD_add_response_header (resp,
920                            "Access-Control-Allow-Methods",
921                            allow_methods);
922   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
923   cleanup_handle (handle);
924   return;
925 }
926
927 /**
928  * Function called with the result from the check if the namestore
929  * service is actually running.  If it is, we start the actual
930  * operation.
931  *
932  * @param cls closure with our configuration
933  * @param result #GNUNET_YES if the namestore service is running
934  */
935 static void
936 testservice_task (void *cls,
937                   int result)
938 {
939   struct RequestHandle *handle = cls;
940   struct GNUNET_REST_RequestHandlerError err;
941   static const struct GNUNET_REST_RequestHandler handlers[] = {
942     {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_NAMESTORE_ZKEY, &namestore_zkey_cont}, //reverse
943     {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_NAMESTORE, &namestore_info_cont}, //list
944     {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_NAMESTORE, &namestore_create_cont}, //create
945     //    {MHD_HTTP_METHOD_PUT, GNUNET_REST_API_NS_NAMESTORE, &namestore_edit_cont}, //update. TODO this shoul be PATCH
946     {MHD_HTTP_METHOD_DELETE, GNUNET_REST_API_NS_NAMESTORE, &namestore_delete_cont}, //delete
947     {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_NAMESTORE, &options_cont},
948     GNUNET_REST_HANDLER_END
949   };
950
951   if (GNUNET_YES != result)
952   {
953     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Service `%s' is not running\n"),
954                 "namestore");
955     GNUNET_SCHEDULER_add_now (&do_error, handle);
956     return;
957   }
958   handle->ns_handle = GNUNET_NAMESTORE_connect (cfg);
959   if (NULL == handle->ns_handle)
960   {
961     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
962                 _("Failed to connect to namestore\n"));
963     GNUNET_SCHEDULER_add_now (&do_error, handle);
964     return;
965   }
966
967   if (GNUNET_OK != GNUNET_JSONAPI_handle_request (handle->rest_handle,
968                                                   handlers,
969                                                   &err,
970                                                   handle))
971   {
972     handle->response_code = err.error_code;
973     GNUNET_SCHEDULER_add_now (&do_error, (void*) handle);
974   }
975
976 }
977
978 /**
979  * Callback invoked from identity service with ego information.
980  * An @a ego of NULL means the ego was not found.
981  *
982  * @param cls closure with the configuration
983  * @param ego an ego known to identity service, or NULL
984  */
985 static void
986 identity_cb (void *cls,
987              const struct GNUNET_IDENTITY_Ego *ego)
988 {
989   struct RequestHandle *handle = cls;
990   struct MHD_Response *resp;
991
992   handle->ego_lookup = NULL;
993   if (NULL == ego)
994   {
995     if (NULL != handle->ego_name)
996     {
997       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
998                   _("Ego `%s' not known to identity service\n"),
999                   handle->ego_name);
1000     }
1001     resp = GNUNET_REST_create_json_response (NULL);
1002     handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND);
1003     cleanup_handle (handle);
1004     return;
1005   }
1006   handle->zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
1007   GNUNET_CLIENT_service_test ("namestore", handle->cfg,
1008                               GNUNET_TIME_UNIT_SECONDS,
1009                               &testservice_task,
1010                               (void *) handle);
1011 }
1012
1013 static void
1014 default_ego_cb (void *cls,
1015                 struct GNUNET_IDENTITY_Ego *ego,
1016                 void **ctx,
1017                 const char *name)
1018 {
1019   struct RequestHandle *handle = cls;
1020   struct MHD_Response *resp;
1021   handle->get_default = NULL;
1022   if (NULL == ego)
1023   {
1024     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1025                 _("No default ego configured in identity service\n"));
1026     resp = GNUNET_REST_create_json_response (NULL);
1027     handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND);
1028     cleanup_handle (handle);
1029     return;
1030   }
1031   else
1032   {
1033     identity_cb (cls, ego);
1034   }
1035 }
1036
1037 static void
1038 id_connect_cb (void *cls,
1039                struct GNUNET_IDENTITY_Ego *ego,
1040                void **ctx,
1041                const char *name)
1042 {
1043   struct RequestHandle *handle = cls;
1044   if (NULL == ego)
1045   {
1046     handle->get_default = GNUNET_IDENTITY_get (handle->identity_handle,
1047                                                "namestore",
1048                                                &default_ego_cb, handle);
1049   }
1050 }
1051
1052 static void
1053 testservice_id_task (void *cls, int result)
1054 {
1055   struct RequestHandle *handle = cls;
1056   struct MHD_Response *resp;
1057   struct GNUNET_HashCode key;
1058   char *ego;
1059   char *name;
1060   char *type;
1061
1062   if (result != GNUNET_YES)
1063   {
1064     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1065                 _("Identity service is not running\n"));
1066     resp = GNUNET_REST_create_json_response (NULL);
1067     handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND);
1068     cleanup_handle (handle);
1069     return;
1070   }
1071   ego = NULL;
1072   GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_NAMESTORE_EGO,
1073                       strlen (GNUNET_REST_JSONAPI_NAMESTORE_EGO),
1074                       &key);
1075   if ( GNUNET_YES ==
1076        GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1077                                                &key) )
1078   {
1079     ego = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1080                                              &key);
1081   }
1082
1083   handle->type = GNUNET_GNSRECORD_TYPE_ANY;
1084   GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_NAMESTORE_RECORD_TYPE,
1085                       strlen (GNUNET_REST_JSONAPI_NAMESTORE_RECORD_TYPE),
1086                       &key);
1087   if ( GNUNET_YES ==
1088        GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1089                                                &key) )
1090   {
1091     type = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1092                                               &key);
1093
1094     handle->type = GNUNET_GNSRECORD_typename_to_number (type);
1095   }
1096   name = get_name_from_url (handle->url);
1097   if (NULL != ego)
1098     handle->ego_name = GNUNET_strdup (ego);
1099   if (NULL != name)
1100     handle->name = GNUNET_strdup (name);
1101   if (NULL == handle->ego_name)
1102   {
1103     handle->identity_handle = GNUNET_IDENTITY_connect (handle->cfg, &id_connect_cb, handle);
1104     if (NULL == handle->identity_handle)
1105     {
1106       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Cannot connect to identity service\n"));
1107       resp = GNUNET_REST_create_json_response (NULL);
1108       handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND);
1109       cleanup_handle (handle);
1110     }
1111     return;
1112   }
1113   handle->ego_lookup = GNUNET_IDENTITY_ego_lookup (cfg,
1114                                                    handle->ego_name,
1115                                                    &identity_cb,
1116                                                    handle);
1117 }
1118
1119 /**
1120  * Function processing the REST call
1121  *
1122  * @param method HTTP method
1123  * @param url URL of the HTTP request
1124  * @param data body of the HTTP request (optional)
1125  * @param data_size length of the body
1126  * @param proc callback function for the result
1127  * @param proc_cls closure for callback function
1128  * @return GNUNET_OK if request accepted
1129  */
1130 static void
1131 rest_identity_process_request(struct GNUNET_REST_RequestHandle *rest_handle,
1132                               GNUNET_REST_ResultProcessor proc,
1133                               void *proc_cls)
1134 {
1135   struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
1136
1137   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
1138   handle->proc_cls = proc_cls;
1139   handle->proc = proc;
1140   handle->rest_handle = rest_handle;
1141   handle->url = GNUNET_strdup (rest_handle->url);
1142   if (handle->url[strlen (handle->url)-1] == '/')
1143     handle->url[strlen (handle->url)-1] = '\0';
1144   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1145               "Connecting...\n");
1146   handle->cfg = cfg;
1147   GNUNET_CLIENT_service_test ("identity",
1148                               cfg,
1149                               GNUNET_TIME_UNIT_SECONDS,
1150                               &testservice_id_task,
1151                               handle);
1152   handle->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->timeout,
1153                                                        &do_timeout,
1154                                                        handle);
1155 }
1156
1157 /**
1158  * Entry point for the plugin.
1159  *
1160  * @param cls Config info
1161  * @return NULL on error, otherwise the plugin context
1162  */
1163 void *
1164 libgnunet_plugin_rest_namestore_init (void *cls)
1165 {
1166   static struct Plugin plugin;
1167   cfg = cls;
1168   struct GNUNET_REST_Plugin *api;
1169
1170   if (NULL != plugin.cfg)
1171     return NULL;                /* can only initialize once! */
1172   memset (&plugin, 0, sizeof (struct Plugin));
1173   plugin.cfg = cfg;
1174   api = GNUNET_new (struct GNUNET_REST_Plugin);
1175   api->cls = &plugin;
1176   api->name = GNUNET_REST_API_NS_NAMESTORE;
1177   api->process_request = &rest_identity_process_request;
1178   GNUNET_asprintf (&allow_methods,
1179                    "%s, %s, %s, %s, %s",
1180                    MHD_HTTP_METHOD_GET,
1181                    MHD_HTTP_METHOD_POST,
1182                    MHD_HTTP_METHOD_PUT,
1183                    MHD_HTTP_METHOD_DELETE,
1184                    MHD_HTTP_METHOD_OPTIONS);
1185   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1186               _("Namestore REST API initialized\n"));
1187   return api;
1188 }
1189
1190
1191 /**
1192  * Exit point from the plugin.
1193  *
1194  * @param cls the plugin context (as returned by "init")
1195  * @return always NULL
1196  */
1197 void *
1198 libgnunet_plugin_rest_namestore_done (void *cls)
1199 {
1200   struct GNUNET_REST_Plugin *api = cls;
1201   struct Plugin *plugin = api->cls;
1202
1203   plugin->cfg = NULL;
1204   GNUNET_free (api);
1205   GNUNET_free_non_null (allow_methods);
1206   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1207               "Namestore REST plugin is finished\n");
1208   return NULL;
1209 }
1210
1211 /* end of plugin_rest_namestore.c */