fix
[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 it
6    under the terms of the GNU Affero General Public License as published
7    by the Free Software Foundation, either version 3 of the License,
8    or (at your 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    Affero General Public License for more details.
14   
15    You should have received a copy of the GNU Affero General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17    */
18 /**
19  * @author Martin Schanzenbach
20  * @file namestore/plugin_rest_namestore.c
21  * @brief GNUnet Namestore REST plugin
22  *
23  */
24
25 #include "platform.h"
26 #include "gnunet_rest_plugin.h"
27 #include "gnunet_gns_service.h"
28 #include "gnunet_namestore_service.h"
29 #include "gnunet_identity_service.h"
30 #include "gnunet_rest_lib.h"
31 #include "gnunet_jsonapi_lib.h"
32 #include "gnunet_jsonapi_util.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
250   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
251               "Cleaning up\n");
252   if (NULL != handle->resp_object)
253     GNUNET_JSONAPI_document_delete (handle->resp_object);
254   if (NULL != handle->name)
255     GNUNET_free (handle->name);
256   if (NULL != handle->timeout_task)
257     GNUNET_SCHEDULER_cancel (handle->timeout_task);
258   if (NULL != handle->ego_lookup)
259     GNUNET_IDENTITY_ego_lookup_cancel (handle->ego_lookup);
260   if (NULL != handle->get_default)
261     GNUNET_IDENTITY_cancel (handle->get_default);
262   if (NULL != handle->list_it)
263     GNUNET_NAMESTORE_zone_iteration_stop (handle->list_it);
264   if (NULL != handle->add_qe)
265     GNUNET_NAMESTORE_cancel (handle->add_qe);
266   if (NULL != handle->identity_handle)
267     GNUNET_IDENTITY_disconnect (handle->identity_handle);
268   if (NULL != handle->ns_handle)
269     GNUNET_NAMESTORE_disconnect (handle->ns_handle);
270   if (NULL != handle->url)
271     GNUNET_free (handle->url);
272   if (NULL != handle->value)
273     GNUNET_free (handle->value);
274   if (NULL != handle->rd)
275   {
276     for (unsigned int i = 0; i < handle->rd_count; i++)
277     {
278       if (NULL != handle->rd[i].data)
279         GNUNET_free ((void*)handle->rd[i].data);
280     }
281     GNUNET_free (handle->rd);
282   }
283   if (NULL != handle->ego_name)
284     GNUNET_free (handle->ego_name);
285   for (record_entry = handle->record_head;
286        NULL != record_entry;)
287   {
288     record_tmp = record_entry;
289     record_entry = record_entry->next;
290     GNUNET_free (record_tmp);
291   }
292   GNUNET_free (handle);
293 }
294
295
296 /**
297  * Create json representation of a GNSRECORD
298  *
299  * @param rd the GNSRECORD_Data
300  */
301 static json_t *
302 gnsrecord_to_json (const struct GNUNET_GNSRECORD_Data *rd)
303 {
304   const char *typename;
305   char *string_val;
306   const char *exp_str;
307   json_t *record_obj;
308
309   typename = GNUNET_GNSRECORD_number_to_typename (rd->record_type);
310   string_val = GNUNET_GNSRECORD_value_to_string (rd->record_type,
311                                                  rd->data,
312                                                  rd->data_size);
313
314   if (NULL == string_val)
315   {
316     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
317                 "Record of type %d malformed, skipping\n",
318                 (int) rd->record_type);
319     return NULL;
320   }
321   record_obj = json_object();
322   json_object_set_new (record_obj,
323                        GNUNET_REST_JSONAPI_NAMESTORE_RECORD_TYPE,
324                        json_string (typename));
325   json_object_set_new (record_obj,
326                        GNUNET_REST_JSONAPI_NAMESTORE_VALUE,
327                        json_string (string_val));
328   GNUNET_free (string_val);
329
330   if (GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION & rd->flags)
331   {
332     struct GNUNET_TIME_Relative time_rel;
333     time_rel.rel_value_us = rd->expiration_time;
334     exp_str = GNUNET_STRINGS_relative_time_to_string (time_rel, 1);
335   }
336   else
337   {
338     struct GNUNET_TIME_Absolute time_abs;
339     time_abs.abs_value_us = rd->expiration_time;
340     exp_str = GNUNET_STRINGS_absolute_time_to_string (time_abs);
341   }
342   json_object_set_new (record_obj, GNUNET_REST_JSONAPI_NAMESTORE_EXPIRATION, json_string (exp_str));
343
344   json_object_set_new (record_obj, "expired",
345                        json_boolean (GNUNET_YES == GNUNET_GNSRECORD_is_expired (rd)));
346   return record_obj;
347 }
348
349
350 /**
351  * Task run on error.  Generates error response and cleans up.
352  *
353  * @param cls the request to generate an error response for
354  */
355 static void
356 do_error (void *cls)
357 {
358   struct RequestHandle *handle = cls;
359   struct MHD_Response *resp = GNUNET_REST_create_response (NULL);
360
361   handle->proc (handle->proc_cls, resp, handle->response_code);
362   cleanup_handle (handle);
363 }
364
365
366 /**
367  * Task run on timeout.
368  *
369  * @param cls the request to time out
370  */
371 static void
372 do_timeout (void *cls)
373 {
374   struct RequestHandle *handle = cls;
375
376   handle->timeout_task = NULL;
377   do_error (handle);
378 }
379
380
381 static void
382 cleanup_handle_delayed (void *cls)
383 {
384   cleanup_handle (cls);
385 }
386
387
388 /**
389  * Iteration over all results finished, build final
390  * response.
391  *
392  * @param cls the `struct RequestHandle`
393  */
394 static void
395 namestore_list_finished (void *cls)
396 {
397   struct RequestHandle *handle = cls;
398   char *result;
399   struct MHD_Response *resp;
400
401   handle->list_it = NULL;
402   if (NULL == handle->resp_object)
403     handle->resp_object = GNUNET_JSONAPI_document_new ();
404
405   if (GNUNET_SYSERR ==
406       GNUNET_JSONAPI_document_serialize (handle->resp_object,
407                                          &result))
408   {
409     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
410     GNUNET_SCHEDULER_add_now (&do_error,
411                               handle);
412     return;
413   }
414   resp = GNUNET_REST_create_response (result);
415   handle->proc (handle->proc_cls,
416                 resp,
417                 MHD_HTTP_OK);
418   GNUNET_free_non_null (result);
419   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed,
420                             handle);
421 }
422
423
424
425 /**
426  * Create a response with requested records
427  *
428  * @param handle the RequestHandle
429  */
430 static void
431 namestore_list_response (void *cls,
432                          const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
433                          const char *rname,
434                          unsigned int rd_len,
435                          const struct GNUNET_GNSRECORD_Data *rd)
436 {
437   struct RequestHandle *handle = cls;
438   struct GNUNET_JSONAPI_Resource *json_resource;
439   json_t *result_array;
440   json_t *record_obj;
441
442   if (NULL == handle->resp_object)
443     handle->resp_object = GNUNET_JSONAPI_document_new ();
444
445   if ( (NULL != handle->name) &&
446        (0 != strcmp (handle->name,
447                      rname)) )
448   {
449     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
450                 "%s does not match %s\n",
451                 rname,
452                 handle->name);
453     GNUNET_NAMESTORE_zone_iterator_next (handle->list_it,
454                                          1);
455     return;
456   }
457
458   result_array = json_array ();
459   for (unsigned int i=0; i<rd_len; i++)
460   {
461     if ( (GNUNET_GNSRECORD_TYPE_NICK == rd[i].record_type) &&
462          (0 != strcmp (rname, GNUNET_GNS_EMPTY_LABEL_AT)) )
463       continue;
464
465     if ( (rd[i].record_type != handle->type) &&
466          (GNUNET_GNSRECORD_TYPE_ANY != handle->type) )
467       continue;
468     record_obj = gnsrecord_to_json (&rd[i]);
469     json_array_append (result_array,
470                        record_obj);
471     json_decref (record_obj);
472   }
473
474   if (0 < json_array_size(result_array))
475   {
476     json_resource = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_NAMESTORE_TYPEINFO,
477                                                  rname);
478     GNUNET_JSONAPI_resource_add_attr (json_resource,
479                                       GNUNET_REST_JSONAPI_NAMESTORE_RECORD,
480                                       result_array);
481     GNUNET_JSONAPI_document_resource_add (handle->resp_object, json_resource);
482   }
483
484   json_decref (result_array);
485   GNUNET_NAMESTORE_zone_iterator_next (handle->list_it,
486                                        1);
487 }
488
489
490 static void
491 create_finished (void *cls, int32_t success, const char *emsg)
492 {
493   struct RequestHandle *handle = cls;
494   struct MHD_Response *resp;
495
496   handle->add_qe = NULL;
497   if (GNUNET_YES != success)
498   {
499     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
500                 "Error storing records%s%s\n",
501                 (NULL == emsg) ? "" : ": ",
502                 (NULL == emsg) ? "" : emsg);
503     GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
504     return;
505   }
506   resp = GNUNET_REST_create_response (NULL);
507   handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
508   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
509 }
510
511
512 /**
513  * We're storing a new record; this requires
514  * that no record already exists
515  *
516  * @param cls closure, unused
517  * @param zone_key private key of the zone
518  * @param rec_name name that is being mapped (at most 255 characters long)
519  * @param rd_count number of entries in @a rd array
520  * @param rd array of records with data to store
521  */
522 static void
523 create_new_record_cont (void *cls,
524                         const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
525                         const char *rec_name,
526                         unsigned int rd_count,
527                         const struct GNUNET_GNSRECORD_Data *rd)
528 {
529   struct RequestHandle *handle = cls;
530
531   handle->add_qe = NULL;
532   if (0 != strcmp (rec_name, handle->name))
533   {
534     GNUNET_break (0);
535     do_error (handle);
536     return;
537   }
538
539   if (0 != rd_count)
540   {
541     handle->proc (handle->proc_cls,
542                   GNUNET_REST_create_response (NULL),
543                   MHD_HTTP_CONFLICT);
544     GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
545     return;
546   }
547
548   GNUNET_assert (NULL != handle->name);
549   handle->add_qe = GNUNET_NAMESTORE_records_store (handle->ns_handle,
550                                                    &handle->zone_pkey,
551                                                    handle->name,
552                                                    handle->rd_count,
553                                                    handle->rd,
554                                                    &create_finished,
555                                                    handle);
556 }
557
558
559 static void
560 del_finished (void *cls,
561               int32_t success,
562               const char *emsg)
563 {
564   struct RequestHandle *handle = cls;
565
566   handle->add_qe = NULL;
567   if (GNUNET_NO == success)
568   {
569     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
570                 _("Deleting record failed, record does not exist%s%s\n"),
571                 (NULL != emsg) ? ": " : "",
572                 (NULL != emsg) ? emsg : "");
573     GNUNET_SCHEDULER_add_now (&do_error, handle); //do_not_found TODO
574     return;
575   }
576   if (GNUNET_SYSERR == success)
577   {
578     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
579                 _("Deleting record failed%s%s\n"),
580                 (NULL != emsg) ? ": " : "",
581                 (NULL != emsg) ? emsg : "");
582     GNUNET_SCHEDULER_add_now (&do_error, handle);
583     return;
584   }
585   handle->proc (handle->proc_cls,
586                 GNUNET_REST_create_response (NULL),
587                 MHD_HTTP_NO_CONTENT);
588   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
589 }
590
591
592 static void
593 del_cont (void *cls,
594           const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
595           const char *label,
596           unsigned int rd_count,
597           const struct GNUNET_GNSRECORD_Data *rd)
598 {
599   struct RequestHandle *handle = cls;
600
601   handle->add_qe = NULL;
602   if (0 == rd_count)
603   {
604     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
605                 _("There are no records under label `%s' that could be deleted.\n"),
606                 label);
607     do_error (handle);
608     return;
609   }
610
611   handle->add_qe = GNUNET_NAMESTORE_records_store (handle->ns_handle,
612                                                    &handle->zone_pkey,
613                                                    handle->name,
614                                                    0, NULL,
615                                                    &del_finished,
616                                                    handle);
617 }
618
619
620 static void
621 namestore_delete_cont (struct GNUNET_REST_RequestHandle *con,
622                        const char *url,
623                        void *cls)
624 {
625   struct RequestHandle *handle = cls;
626
627   if (NULL == handle->name)
628   {
629     GNUNET_SCHEDULER_add_now (&do_error, handle);
630     return;
631   }
632
633   handle->add_qe = GNUNET_NAMESTORE_records_lookup (handle->ns_handle,
634                                                     &handle->zone_pkey,
635                                                     handle->name,
636                                                     &do_error,
637                                                     handle,
638                                                     &del_cont,
639                                                     handle);
640 }
641
642
643 static int
644 json_to_gnsrecord (const json_t *records_json,
645                    struct GNUNET_GNSRECORD_Data **rd,
646                    unsigned int *rd_count)
647 {
648   struct GNUNET_TIME_Relative etime_rel;
649   struct GNUNET_TIME_Absolute etime_abs;
650   char *value;
651   void *rdata;
652   size_t rdata_size;
653   const char *typestring;
654   const char *expirationstring;
655   json_t *type_json;
656   json_t *value_json;
657   json_t *record_json;
658   json_t *exp_json;
659
660   *rd_count = json_array_size (records_json);
661   *rd = GNUNET_new_array (*rd_count,
662                            struct GNUNET_GNSRECORD_Data);
663   for (unsigned int i = 0; i < *rd_count; i++)
664   {
665     memset (&(*rd)[i],
666             0,
667             sizeof (struct GNUNET_GNSRECORD_Data));
668     record_json = json_array_get (records_json,
669                                   i);
670     type_json = json_object_get (record_json,
671                                  GNUNET_REST_JSONAPI_NAMESTORE_RECORD_TYPE);
672     if (! json_is_string (type_json))
673     {
674       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
675                   "Type property is no string\n");
676       return GNUNET_SYSERR;
677     }
678     typestring = json_string_value (type_json);
679     (*rd)[i].record_type = GNUNET_GNSRECORD_typename_to_number (typestring);
680     if (UINT32_MAX == (*rd)[i].record_type)
681     {
682       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Unsupported type `%s'\n"),
683                   json_string_value (type_json));
684       return GNUNET_SYSERR;
685     }
686     value_json = json_object_get (record_json,
687                                   GNUNET_REST_JSONAPI_NAMESTORE_VALUE);
688     if (! json_is_string (value_json))
689     {
690       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
691                   "Value property is no string\n");
692       return GNUNET_SYSERR;
693     }
694     value = GNUNET_strdup (json_string_value (value_json));
695     if (GNUNET_OK !=
696         GNUNET_GNSRECORD_string_to_value ((*rd)[i].record_type,
697                                           value,
698                                           &rdata,
699                                           &rdata_size))
700     {
701       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
702                   _("Value `%s' invalid for record type `%s'\n"),
703                   value,
704                   typestring);
705       return GNUNET_SYSERR;
706     }
707     (*rd)[i].data = rdata;
708     (*rd)[i].data_size = rdata_size;
709     /**TODO
710      * if (1 == handle->is_shadow)
711      rde->flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
712      if (1 != handle->is_public)
713      rde->flags |= GNUNET_GNSRECORD_RF_PRIVATE;
714      */
715     exp_json = json_object_get (record_json,
716                                 GNUNET_REST_JSONAPI_NAMESTORE_EXPIRATION);
717     if (! json_is_string (exp_json))
718     {
719       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
720                   "Expiration property is no string\n");
721       return GNUNET_SYSERR;
722     }
723     expirationstring = json_string_value (exp_json);
724     if (0 == strcmp (expirationstring, "never"))
725     {
726       (*rd)[i].expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
727     }
728     else if (GNUNET_OK ==
729              GNUNET_STRINGS_fancy_time_to_relative (expirationstring,
730                                                     &etime_rel))
731     {
732       (*rd)[i].expiration_time = etime_rel.rel_value_us;
733       (*rd)[i].flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
734     }
735     else if (GNUNET_OK ==
736              GNUNET_STRINGS_fancy_time_to_absolute (expirationstring,
737                                                     &etime_abs))
738     {
739       (*rd)[i].expiration_time = etime_abs.abs_value_us;
740     }
741     else
742     {
743       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
744                   _("Value `%s' invalid for record type `%s'\n"),
745                   value,
746                   typestring);
747       return GNUNET_SYSERR;
748     }
749   }
750   return GNUNET_OK;
751 }
752
753
754 static void
755 namestore_create_cont (struct GNUNET_REST_RequestHandle *con,
756                        const char *url,
757                        void *cls)
758 {
759   struct RequestHandle *handle = cls;
760   struct MHD_Response *resp;
761   struct GNUNET_JSONAPI_Document *json_obj;
762   struct GNUNET_JSONAPI_Resource *json_res;
763   json_t *records_json;
764   json_t *data_js;
765   json_error_t err;
766   char term_data[handle->rest_handle->data_size+1];
767   struct GNUNET_JSON_Specification docspec[] = {
768     GNUNET_JSON_spec_jsonapi_document (&json_obj),
769     GNUNET_JSON_spec_end()
770   };
771
772   if (strlen (GNUNET_REST_API_NS_NAMESTORE) != strlen (handle->url))
773   {
774     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
775                 "Cannot create under %s\n", handle->url);
776     GNUNET_SCHEDULER_add_now (&do_error, handle);
777     return;
778   }
779   if (0 >= handle->rest_handle->data_size)
780   {
781     GNUNET_SCHEDULER_add_now (&do_error, handle);
782     return;
783   }
784   term_data[handle->rest_handle->data_size] = '\0';
785   GNUNET_memcpy (term_data,
786                  handle->rest_handle->data,
787                  handle->rest_handle->data_size);
788   data_js = json_loads (term_data,
789                         JSON_DECODE_ANY,
790                         &err);
791   GNUNET_assert (GNUNET_OK ==
792                  GNUNET_JSON_parse (data_js, docspec,
793                                     NULL, NULL));
794   json_decref (data_js);
795   if (NULL == json_obj)
796   {
797     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
798                 "Unable to parse JSONAPI Object from %s\n",
799                 term_data);
800     GNUNET_SCHEDULER_add_now (&do_error, handle);
801     return;
802   }
803   if (1 != GNUNET_JSONAPI_document_resource_count (json_obj))
804   {
805     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
806                 "Cannot create more than 1 resource! (Got %d)\n",
807                 GNUNET_JSONAPI_document_resource_count (json_obj));
808     GNUNET_JSONAPI_document_delete (json_obj);
809     GNUNET_SCHEDULER_add_now (&do_error, handle);
810     return;
811   }
812   json_res = GNUNET_JSONAPI_document_get_resource (json_obj, 0);
813   if (GNUNET_NO == GNUNET_JSONAPI_resource_check_type (json_res,
814                                                        GNUNET_REST_JSONAPI_NAMESTORE_RECORD))
815   {
816     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
817                 "Unsupported JSON data type\n");
818     GNUNET_JSONAPI_document_delete (json_obj);
819     resp = GNUNET_REST_create_response (NULL);
820     handle->proc (handle->proc_cls, resp, MHD_HTTP_CONFLICT);
821     cleanup_handle (handle);
822     return;
823   }
824   handle->name = GNUNET_strdup (GNUNET_JSONAPI_resource_get_id (json_res));
825   records_json = GNUNET_JSONAPI_resource_read_attr (json_res,
826                                                     GNUNET_REST_JSONAPI_NAMESTORE_RECORD);
827   if (NULL == records_json)
828   {
829     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
830                 "No records given\n");
831     GNUNET_JSONAPI_document_delete (json_obj);
832     GNUNET_SCHEDULER_add_now (&do_error, handle);
833     return;
834   }
835   if (GNUNET_SYSERR == json_to_gnsrecord (records_json, &handle->rd, &handle->rd_count))
836   {
837     GNUNET_JSONAPI_document_delete (json_obj);
838     GNUNET_SCHEDULER_add_now (&do_error, handle);
839     return;
840   }
841   GNUNET_JSONAPI_document_delete (json_obj);
842
843   handle->add_qe = GNUNET_NAMESTORE_records_lookup (handle->ns_handle,
844                                                     &handle->zone_pkey,
845                                                     handle->name,
846                                                     &do_error,
847                                                     handle,
848                                                     &create_new_record_cont,
849                                                     handle);
850 }
851
852
853 static void
854 namestore_zkey_response (void *cls,
855                          const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
856                          const char *label,
857                          unsigned int rd_count,
858                          const struct GNUNET_GNSRECORD_Data *rd)
859 {
860   struct RequestHandle *handle = cls;
861   struct MHD_Response *resp;
862   struct GNUNET_JSONAPI_Document *json_obj;
863   struct GNUNET_JSONAPI_Resource *json_res;
864   json_t *name_json;
865   char* result;
866
867   handle->reverse_qe = NULL;
868   json_obj = GNUNET_JSONAPI_document_new ();
869   if (NULL != label)
870   {
871     name_json = json_string (label);
872     json_res = GNUNET_JSONAPI_resource_new (GNUNET_REST_JSONAPI_NAMESTORE_REVINFO,
873                                             handle->zkey_str);
874     GNUNET_JSONAPI_resource_add_attr (json_res,
875                                       GNUNET_REST_JSONAPI_NAMESTORE_NAME,
876                                       name_json);
877     GNUNET_JSONAPI_document_resource_add (json_obj, json_res);
878     json_decref (name_json);
879   }
880   //Handle response
881   if (GNUNET_SYSERR == GNUNET_JSONAPI_document_serialize (json_obj, &result))
882   {
883     GNUNET_JSONAPI_document_delete (json_obj);
884     GNUNET_SCHEDULER_add_now (&do_error, handle);
885     return;
886   }
887   resp = GNUNET_REST_create_response (result);
888   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
889   GNUNET_JSONAPI_document_delete (json_obj);
890   GNUNET_free (result);
891   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
892 }
893
894
895 static void
896 namestore_zkey_cont (struct GNUNET_REST_RequestHandle *con,
897                      const char *url,
898                      void *cls)
899 {
900   struct RequestHandle *handle = cls;
901   struct GNUNET_HashCode key;
902   struct GNUNET_CRYPTO_EcdsaPublicKey pubkey;
903
904   GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_NAMESTORE_ZKEY,
905                       strlen (GNUNET_REST_JSONAPI_NAMESTORE_ZKEY),
906                       &key);
907   if ( GNUNET_NO ==
908        GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
909                                                &key) )
910   {
911     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
912                 "No zkey given %s\n", handle->url);
913     GNUNET_SCHEDULER_add_now (&do_error, handle);
914     return;
915   }
916   handle->zkey_str = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
917                                                         &key);
918   if ((NULL == handle->zkey_str) ||
919       (GNUNET_OK !=
920        GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->zkey_str,
921                                                    strlen (handle->zkey_str),
922                                                    &pubkey)))
923   {
924     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
925                 "Zkey invalid %s\n", handle->zkey_str);
926     GNUNET_SCHEDULER_add_now (&do_error, handle);
927     return;
928   }
929   handle->reverse_qe = GNUNET_NAMESTORE_zone_to_name (handle->ns_handle,
930                                                       &handle->zone_pkey,
931                                                       &pubkey,
932                                                       &do_error,
933                                                       handle,
934                                                       &namestore_zkey_response,
935                                                       handle);
936 }
937
938
939 static void
940 namestore_info_cont (struct GNUNET_REST_RequestHandle *con,
941                      const char *url,
942                      void *cls)
943 {
944   struct RequestHandle *handle = cls;
945
946   handle->list_it = GNUNET_NAMESTORE_zone_iteration_start (handle->ns_handle,
947                                                            &handle->zone_pkey,
948                                                            &do_error,
949                                                            handle,
950                                                            &namestore_list_response,
951                                                            handle,
952                                                            &namestore_list_finished,
953                                                            handle);
954 }
955
956
957 static char*
958 get_name_from_url (const char* url)
959 {
960   if (strlen (url) <= strlen (GNUNET_REST_API_NS_NAMESTORE))
961     return NULL;
962   return (char*)url + strlen (GNUNET_REST_API_NS_NAMESTORE) + 1;
963 }
964
965 /**
966  * Respond to OPTIONS request
967  *
968  * @param con_handle the connection handle
969  * @param url the url
970  * @param cls the RequestHandle
971  */
972 static void
973 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
974               const char* url,
975               void *cls)
976 {
977   struct MHD_Response *resp;
978   struct RequestHandle *handle = cls;
979
980   //For now, independent of path return all options
981   resp = GNUNET_REST_create_response (NULL);
982   MHD_add_response_header (resp,
983                            "Access-Control-Allow-Methods",
984                            allow_methods);
985   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
986   cleanup_handle (handle);
987 }
988
989
990 /**
991  * Callback invoked from identity service with ego information.
992  * An @a ego of NULL means the ego was not found.
993  *
994  * @param cls closure with the configuration
995  * @param ego an ego known to identity service, or NULL
996  */
997 static void
998 identity_cb (void *cls,
999              const struct GNUNET_IDENTITY_Ego *ego)
1000 {
1001   struct RequestHandle *handle = cls;
1002   struct MHD_Response *resp;
1003   struct GNUNET_REST_RequestHandlerError err;
1004   static const struct GNUNET_REST_RequestHandler handlers[] = {
1005     {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_NAMESTORE_ZKEY, &namestore_zkey_cont}, //reverse
1006     {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_NAMESTORE, &namestore_info_cont}, //list
1007     {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_NAMESTORE, &namestore_create_cont}, //create
1008     //    {MHD_HTTP_METHOD_PUT, GNUNET_REST_API_NS_NAMESTORE, &namestore_edit_cont}, //update. TODO this shoul be PATCH
1009     {MHD_HTTP_METHOD_DELETE, GNUNET_REST_API_NS_NAMESTORE, &namestore_delete_cont}, //delete
1010     {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_NAMESTORE, &options_cont},
1011     GNUNET_REST_HANDLER_END
1012   };
1013
1014   handle->ego_lookup = NULL;
1015   if (NULL == ego)
1016   {
1017     if (NULL != handle->ego_name)
1018     {
1019       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1020                   _("Ego `%s' not known to identity service\n"),
1021                   handle->ego_name);
1022     }
1023     resp = GNUNET_REST_create_response (NULL);
1024     handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND);
1025     cleanup_handle (handle);
1026     return;
1027   }
1028   handle->zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
1029   handle->ns_handle = GNUNET_NAMESTORE_connect (cfg);
1030   if (NULL == handle->ns_handle)
1031   {
1032     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1033                 _("Failed to connect to namestore\n"));
1034     GNUNET_SCHEDULER_add_now (&do_error, handle);
1035     return;
1036   }
1037
1038   if (GNUNET_OK !=
1039       GNUNET_JSONAPI_handle_request (handle->rest_handle,
1040                                      handlers,
1041                                      &err,
1042                                      handle))
1043   {
1044     handle->response_code = err.error_code;
1045     GNUNET_SCHEDULER_add_now (&do_error,
1046                               (void *) handle);
1047   }
1048 }
1049
1050
1051 static void
1052 default_ego_cb (void *cls,
1053                 struct GNUNET_IDENTITY_Ego *ego,
1054                 void **ctx,
1055                 const char *name)
1056 {
1057   struct RequestHandle *handle = cls;
1058   struct MHD_Response *resp;
1059   handle->get_default = NULL;
1060   if (NULL == ego)
1061   {
1062     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1063                 _("No default ego configured in identity service\n"));
1064     resp = GNUNET_REST_create_response (NULL);
1065     handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND);
1066     cleanup_handle (handle);
1067     return;
1068   }
1069   else
1070   {
1071     identity_cb (cls, ego);
1072   }
1073 }
1074
1075 static void
1076 id_connect_cb (void *cls,
1077                struct GNUNET_IDENTITY_Ego *ego,
1078                void **ctx,
1079                const char *name)
1080 {
1081   struct RequestHandle *handle = cls;
1082   if (NULL == ego)
1083   {
1084     handle->get_default = GNUNET_IDENTITY_get (handle->identity_handle,
1085                                                "namestore",
1086                                                &default_ego_cb, handle);
1087   }
1088 }
1089
1090
1091 /**
1092  * Function processing the REST call
1093  *
1094  * @param method HTTP method
1095  * @param url URL of the HTTP request
1096  * @param data body of the HTTP request (optional)
1097  * @param data_size length of the body
1098  * @param proc callback function for the result
1099  * @param proc_cls closure for callback function
1100  * @return #GNUNET_OK if request accepted
1101  */
1102 static void
1103 rest_identity_process_request(struct GNUNET_REST_RequestHandle *rest_handle,
1104                               GNUNET_REST_ResultProcessor proc,
1105                               void *proc_cls)
1106 {
1107   struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
1108   struct MHD_Response *resp;
1109   struct GNUNET_HashCode key;
1110   char *ego;
1111   char *name;
1112   char *type;
1113
1114   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
1115   handle->proc_cls = proc_cls;
1116   handle->proc = proc;
1117   handle->rest_handle = rest_handle;
1118   handle->url = GNUNET_strdup (rest_handle->url);
1119   if (handle->url[strlen (handle->url)-1] == '/')
1120     handle->url[strlen (handle->url)-1] = '\0';
1121   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1122               "Connecting...\n");
1123   handle->cfg = cfg;
1124   ego = NULL;
1125   GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_NAMESTORE_EGO,
1126                       strlen (GNUNET_REST_JSONAPI_NAMESTORE_EGO),
1127                       &key);
1128   if ( GNUNET_YES ==
1129        GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1130                                                &key) )
1131   {
1132     ego = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1133                                              &key);
1134   }
1135
1136   handle->type = GNUNET_GNSRECORD_TYPE_ANY;
1137   GNUNET_CRYPTO_hash (GNUNET_REST_JSONAPI_NAMESTORE_RECORD_TYPE,
1138                       strlen (GNUNET_REST_JSONAPI_NAMESTORE_RECORD_TYPE),
1139                       &key);
1140   if ( GNUNET_YES ==
1141        GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1142                                                &key) )
1143   {
1144     type = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1145                                               &key);
1146     if (NULL != type)
1147       handle->type = GNUNET_GNSRECORD_typename_to_number (type);
1148   }
1149   name = get_name_from_url (handle->url);
1150   if (NULL != ego)
1151     handle->ego_name = GNUNET_strdup (ego);
1152   if (NULL != name)
1153     handle->name = GNUNET_strdup (name);
1154   if (NULL == handle->ego_name)
1155   {
1156     handle->identity_handle = GNUNET_IDENTITY_connect (handle->cfg, &id_connect_cb, handle);
1157     if (NULL == handle->identity_handle)
1158     {
1159       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Cannot connect to identity service\n"));
1160       resp = GNUNET_REST_create_response (NULL);
1161       handle->proc (handle->proc_cls, resp, MHD_HTTP_NOT_FOUND);
1162       cleanup_handle (handle);
1163     }
1164     return;
1165   }
1166   handle->ego_lookup = GNUNET_IDENTITY_ego_lookup (cfg,
1167                                                    handle->ego_name,
1168                                                    &identity_cb,
1169                                                    handle);
1170   handle->timeout_task = GNUNET_SCHEDULER_add_delayed (handle->timeout,
1171                                                        &do_timeout,
1172                                                        handle);
1173 }
1174
1175 /**
1176  * Entry point for the plugin.
1177  *
1178  * @param cls Config info
1179  * @return NULL on error, otherwise the plugin context
1180  */
1181 void *
1182 libgnunet_plugin_rest_namestore_init (void *cls)
1183 {
1184   static struct Plugin plugin;
1185   cfg = cls;
1186   struct GNUNET_REST_Plugin *api;
1187
1188   if (NULL != plugin.cfg)
1189     return NULL;                /* can only initialize once! */
1190   memset (&plugin, 0, sizeof (struct Plugin));
1191   plugin.cfg = cfg;
1192   api = GNUNET_new (struct GNUNET_REST_Plugin);
1193   api->cls = &plugin;
1194   api->name = GNUNET_REST_API_NS_NAMESTORE;
1195   api->process_request = &rest_identity_process_request;
1196   GNUNET_asprintf (&allow_methods,
1197                    "%s, %s, %s, %s, %s",
1198                    MHD_HTTP_METHOD_GET,
1199                    MHD_HTTP_METHOD_POST,
1200                    MHD_HTTP_METHOD_PUT,
1201                    MHD_HTTP_METHOD_DELETE,
1202                    MHD_HTTP_METHOD_OPTIONS);
1203   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1204               _("Namestore REST API initialized\n"));
1205   return api;
1206 }
1207
1208
1209 /**
1210  * Exit point from the plugin.
1211  *
1212  * @param cls the plugin context (as returned by "init")
1213  * @return always NULL
1214  */
1215 void *
1216 libgnunet_plugin_rest_namestore_done (void *cls)
1217 {
1218   struct GNUNET_REST_Plugin *api = cls;
1219   struct Plugin *plugin = api->cls;
1220
1221   plugin->cfg = NULL;
1222   GNUNET_free (api);
1223   GNUNET_free_non_null (allow_methods);
1224   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1225               "Namestore REST plugin is finished\n");
1226   return NULL;
1227 }
1228
1229 /* end of plugin_rest_namestore.c */