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