90928165e4461cc2f51045aa90b243e430c7c06b
[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  * @author Philippe Buschmann
21  * @file namestore/plugin_rest_namestore.c
22  * @brief GNUnet Namestore REST plugin
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_json_lib.h"
32 #include "microhttpd.h"
33 #include <jansson.h>
34
35
36 #define GNUNET_REST_API_NS_NAMESTORE "/namestore"
37
38 #define GNUNET_REST_SUBSYSTEM_NAMESTORE "namestore"
39
40 #define GNUNET_REST_NAMESTORE_ERROR_UNKNOWN "Unknown Error"
41
42 #define GNUNET_REST_NAMESTORE_RD_COUNT 1
43
44 /**
45  * The configuration handle
46  */
47 const struct GNUNET_CONFIGURATION_Handle *cfg;
48
49 /**
50  * HTTP methods allows for this plugin
51  */
52 static char* allow_methods;
53
54 /**
55  * @brief struct returned by the initialization function of the plugin
56  */
57 struct Plugin
58 {
59   const struct GNUNET_CONFIGURATION_Handle *cfg;
60 };
61
62 /**
63  * The default namestore ego
64  */
65 struct EgoEntry
66 {
67   /**
68    * Ego Identifier
69    */
70   const char *identifier;
71
72   /**
73    * Public key string
74    */
75   char *keystring;
76
77   /**
78    * The Ego
79    */
80   struct GNUNET_IDENTITY_Ego *ego;
81 };
82
83
84 struct RequestHandle
85 {
86   /**
87    * Records to store
88    */
89   char *label_name;
90
91   /**
92    * Records to store
93    */
94   struct GNUNET_GNSRECORD_Data *rd;
95
96   /**
97    * NAMESTORE Operation
98    */
99   struct GNUNET_NAMESTORE_QueueEntry *add_qe;
100
101   /**
102    * Response object
103    */
104   json_t *resp_object;
105
106   /**
107    * Handle to NAMESTORE
108    */
109   struct GNUNET_NAMESTORE_Handle *ns_handle;
110
111   /**
112    * Handle to NAMESTORE it
113    */
114   struct GNUNET_NAMESTORE_ZoneIterator *list_it;
115
116   /**
117    * Private key for the zone
118    */
119   struct GNUNET_CRYPTO_EcdsaPrivateKey zone_pkey;
120
121   /**
122    * IDENTITY Operation
123    */
124   struct EgoEntry *ego_entry;
125
126   /**
127    * IDENTITY Operation
128    */
129   struct GNUNET_IDENTITY_Operation *op;
130
131   /**
132    * Handle to Identity service.
133    */
134   struct GNUNET_IDENTITY_Handle *identity_handle;
135
136   /**
137    * Rest connection
138    */
139   struct GNUNET_REST_RequestHandle *rest_handle;
140   
141   /**
142    * Desired timeout for the lookup (default is no timeout).
143    */
144   struct GNUNET_TIME_Relative timeout;
145
146   /**
147    * ID of a task associated with the resolution process.
148    */
149   struct GNUNET_SCHEDULER_Task *timeout_task;
150
151   /**
152    * The plugin result processor
153    */
154   GNUNET_REST_ResultProcessor proc;
155
156   /**
157    * The closure of the result processor
158    */
159   void *proc_cls;
160
161   /**
162    * The url
163    */
164   char *url;
165
166   /**
167    * Error response message
168    */
169   char *emsg;
170
171   /**
172    * Reponse code
173    */
174   int response_code;
175
176 };
177
178 /**
179  * Cleanup lookup handle
180  * @param handle Handle to clean up
181  */
182 static void
183 cleanup_handle (void *cls)
184 {
185   struct RequestHandle *handle = cls;
186
187   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
188               "Cleaning up\n");
189   if (NULL != handle->timeout_task)
190   {
191     GNUNET_SCHEDULER_cancel (handle->timeout_task);
192     handle->timeout_task = NULL;
193   }
194   if (NULL != handle->label_name)
195     GNUNET_free(handle->label_name);
196   if (NULL != handle->url)
197     GNUNET_free(handle->url);
198   if (NULL != handle->emsg)
199     GNUNET_free(handle->emsg);
200   if (NULL != handle->rd)
201   {
202     if (NULL != handle->rd->data)
203       GNUNET_free((void*)handle->rd->data);
204     GNUNET_free(handle->rd);
205   }
206   if (NULL != handle->timeout_task)
207     GNUNET_SCHEDULER_cancel(handle->timeout_task);
208   if (NULL != handle->list_it)
209     GNUNET_NAMESTORE_zone_iteration_stop(handle->list_it);
210   if (NULL != handle->add_qe)
211     GNUNET_NAMESTORE_cancel(handle->add_qe);
212   if (NULL != handle->identity_handle)
213     GNUNET_IDENTITY_disconnect(handle->identity_handle);
214   if (NULL != handle->ns_handle)
215   {
216     GNUNET_NAMESTORE_disconnect(handle->ns_handle);
217   }
218
219   if (NULL != handle->ego_entry)
220   {
221     if (NULL != handle->ego_entry->keystring)
222       GNUNET_free(handle->ego_entry->keystring);
223
224     GNUNET_free(handle->ego_entry);
225   }
226
227   if(NULL != handle->resp_object)
228   {
229     json_decref(handle->resp_object);
230   }
231
232   GNUNET_free (handle);
233 }
234
235
236 /**
237  * Task run on errors.  Reports an error and cleans up everything.
238  *
239  * @param cls the `struct RequestHandle`
240  */
241 static void
242 do_error (void *cls)
243 {
244   struct RequestHandle *handle = cls;
245   struct MHD_Response *resp;
246   json_t *json_error = json_object();
247   char *response;
248
249   if (NULL == handle->emsg)
250     handle->emsg = GNUNET_strdup(GNUNET_REST_NAMESTORE_ERROR_UNKNOWN);
251
252   json_object_set_new(json_error,"error", json_string(handle->emsg));
253
254   if (0 == handle->response_code)
255     handle->response_code = MHD_HTTP_OK;
256   response = json_dumps (json_error, 0);
257   resp = GNUNET_REST_create_response (response);
258   handle->proc (handle->proc_cls, resp, handle->response_code);
259   json_decref(json_error);
260   GNUNET_free(response);
261   GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
262 }
263
264 /**
265  * Does internal server error when iteration failed.
266  */
267 static void
268 namestore_iteration_error (void *cls)
269 {
270   struct RequestHandle *handle = cls;
271   struct MHD_Response *resp = GNUNET_REST_create_response (NULL);
272   handle->proc (handle->proc_cls, resp, MHD_HTTP_INTERNAL_SERVER_ERROR);
273   GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
274 }
275
276 static void
277 create_finished (void *cls, int32_t success, const char *emsg)
278 {
279   struct RequestHandle *handle = cls;
280   struct MHD_Response *resp;
281
282   handle->add_qe = NULL;
283   if (GNUNET_YES != success)
284   {
285     handle->emsg = GNUNET_strdup("Error storing records");
286     GNUNET_SCHEDULER_add_now (&do_error, handle);
287     return;
288   }
289   resp = GNUNET_REST_create_response (NULL);
290   handle->proc (handle->proc_cls, resp, MHD_HTTP_NO_CONTENT);
291   GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
292 }
293
294 static void
295 del_finished (void *cls, int32_t success, const char *emsg)
296 {
297   struct RequestHandle *handle = cls;
298
299   handle->add_qe = NULL;
300   if (GNUNET_NO == success)
301   {
302     handle->emsg = GNUNET_strdup("Deleting record failed. Record does not exist");
303     GNUNET_SCHEDULER_add_now (&do_error, handle);
304     return;
305   }
306   if (GNUNET_SYSERR == success)
307   {
308     handle->emsg = GNUNET_strdup("Deleting record failed");
309     GNUNET_SCHEDULER_add_now (&do_error, handle);
310     return;
311   }
312   handle->proc (handle->proc_cls,
313                 GNUNET_REST_create_response (NULL),
314                 MHD_HTTP_NO_CONTENT);
315   GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
316 }
317 /**
318  * Iteration over all results finished, build final
319  * response.
320  *
321  * @param cls the `struct RequestHandle`
322  */
323 static void
324 namestore_list_finished (void *cls)
325 {
326   struct RequestHandle *handle = cls;
327   char *result_str;
328   struct MHD_Response *resp;
329
330   handle->list_it = NULL;
331
332   if (NULL == handle->resp_object)
333   {
334     GNUNET_SCHEDULER_add_now (&do_error, handle);
335     return;
336   }
337
338   result_str = json_dumps (handle->resp_object, 0);
339   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
340   resp = GNUNET_REST_create_response (result_str);
341   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
342   GNUNET_free_non_null (result_str);
343   GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
344 }
345
346
347
348 /**
349  * Create a response with requested records
350  *
351  * @param handle the RequestHandle
352  */
353 static void
354 namestore_list_iteration (void *cls,
355                          const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
356                          const char *rname,
357                          unsigned int rd_len,
358                          const struct GNUNET_GNSRECORD_Data *rd)
359 {
360   struct RequestHandle *handle = cls;
361   json_t *record_obj;
362
363   if (NULL == handle->resp_object)
364     handle->resp_object = json_array();
365
366   /*if ( (NULL != handle->ego_entry->identifier) &&
367        (0 != strcmp (handle->ego_entry->identifier,
368                      rname)) )
369   {
370     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
371                 "%s does not match %s\n", rname,
372                handle->ego_entry->identifier);
373     GNUNET_NAMESTORE_zone_iterator_next (handle->list_it, 1);
374     return;
375   }*/
376
377   for (unsigned int i = 0; i < rd_len; i++)
378   {
379     if ( (GNUNET_GNSRECORD_TYPE_NICK == rd[i].record_type) &&
380          (0 != strcmp (rname, GNUNET_GNS_EMPTY_LABEL_AT)) )
381       continue;
382
383     record_obj = GNUNET_JSON_from_gns_record(rname,rd);
384
385     if(NULL == record_obj)
386       continue;
387
388     json_array_append (handle->resp_object, record_obj);
389     json_decref (record_obj);
390   }
391
392   GNUNET_NAMESTORE_zone_iterator_next (handle->list_it, 1);
393 }
394
395
396 /**
397  * Handle namestore GET request
398  *
399  * @param con_handle the connection handle
400  * @param url the url
401  * @param cls the RequestHandle
402  */
403 void
404 namestore_get (struct GNUNET_REST_RequestHandle *con_handle,
405                  const char* url,
406                  void *cls)
407 {
408   struct RequestHandle *handle = cls;
409   if (strlen (GNUNET_REST_API_NS_NAMESTORE) != strlen (handle->url))
410   {
411     handle->emsg = GNUNET_strdup("Wrong URL");
412     GNUNET_SCHEDULER_add_now (&do_error, handle);
413     return;
414   }
415   handle->list_it = GNUNET_NAMESTORE_zone_iteration_start (handle->ns_handle,
416                                                            &handle->zone_pkey,
417                                                            &namestore_iteration_error,
418                                                            handle,
419                                                            &namestore_list_iteration,
420                                                            handle,
421                                                            &namestore_list_finished,
422                                                            handle);
423 }
424
425
426 /**
427  * We're storing a new record; this requires
428  * that no record already exists
429  *
430  * @param cls closure, unused
431  * @param zone_key private key of the zone
432  * @param rec_name name that is being mapped (at most 255 characters long)
433  * @param rd_count number of entries in @a rd array
434  * @param rd array of records with data to store
435  */
436 static void
437 create_new_record_cont (void *cls,
438                         const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
439                         const char *rec_name,
440                         unsigned int rd_count,
441                         const struct GNUNET_GNSRECORD_Data *rd)
442 {
443   struct RequestHandle *handle = cls;
444
445   handle->add_qe = NULL;
446   if (0 != strcmp (rec_name, handle->label_name))
447   {
448     GNUNET_break (0);
449     GNUNET_SCHEDULER_add_now (&do_error, handle);
450     return;
451   }
452
453   if (0 != rd_count)
454   {
455     handle->proc (handle->proc_cls,
456                   GNUNET_REST_create_response (NULL),
457                   MHD_HTTP_CONFLICT);
458     GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
459     return;
460   }
461   handle->add_qe = GNUNET_NAMESTORE_records_store (handle->ns_handle,
462                                                    &handle->zone_pkey,
463                                                    handle->label_name,
464                                                    GNUNET_REST_NAMESTORE_RD_COUNT,
465                                                    handle->rd,
466                                                    &create_finished,
467                                                    handle);
468 }
469
470 /**
471  * Handle namestore POST request
472  *
473  * @param con_handle the connection handle
474  * @param url the url
475  * @param cls the RequestHandle
476  */
477 void
478 namestore_add (struct GNUNET_REST_RequestHandle *con_handle,
479                const char* url,
480                void *cls)
481 {
482   struct RequestHandle *handle = cls;
483   struct GNUNET_GNSRECORD_Data *gns_record;
484   json_t *data_js;
485   json_t *name_json;
486   json_error_t err;
487   char term_data[handle->rest_handle->data_size + 1];
488   struct GNUNET_JSON_Specification gnsspec[] = {
489     GNUNET_JSON_spec_gnsrecord_data(&gns_record),
490     GNUNET_JSON_spec_end ()
491   };
492
493   if (strlen (GNUNET_REST_API_NS_NAMESTORE) != strlen (handle->url))
494   {
495     handle->emsg = GNUNET_strdup("Wrong URL");
496     GNUNET_SCHEDULER_add_now (&do_error, handle);
497     return;
498   }
499   if (0 >= handle->rest_handle->data_size)
500   {
501     handle->emsg = GNUNET_strdup("No data");
502     GNUNET_SCHEDULER_add_now (&do_error, handle);
503     return;
504   }
505   term_data[handle->rest_handle->data_size] = '\0';
506   GNUNET_memcpy(term_data, handle->rest_handle->data,
507                 handle->rest_handle->data_size);
508   data_js = json_loads (term_data, JSON_DECODE_ANY, &err);
509   GNUNET_assert (GNUNET_OK == GNUNET_JSON_parse (data_js, gnsspec, NULL, NULL));
510   name_json = json_object_get(data_js, "label");
511   if (!json_is_string(name_json))
512   {
513     handle->emsg = GNUNET_strdup("Missing name");
514     GNUNET_SCHEDULER_add_now (&do_error, handle);
515     return;
516   }
517   handle->label_name = GNUNET_strdup(json_string_value(name_json));
518   if(NULL == handle->label_name)
519   {
520     handle->emsg = GNUNET_strdup("Missing name");
521     GNUNET_SCHEDULER_add_now (&do_error, handle);
522     return;
523   }
524   json_decref (data_js);
525   handle->rd = gns_record;
526   handle->add_qe = GNUNET_NAMESTORE_records_lookup (handle->ns_handle,
527                                                     &handle->zone_pkey,
528                                                     handle->label_name,
529                                                     &do_error,
530                                                     handle,
531                                                     &create_new_record_cont,
532                                                     handle);
533 }
534
535
536 static void
537 del_cont (void *cls,
538           const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
539           const char *label,
540           unsigned int rd_count,
541           const struct GNUNET_GNSRECORD_Data *rd)
542 {
543   struct RequestHandle *handle = cls;
544
545   handle->add_qe = NULL;
546   if (0 == rd_count)
547   {
548     handle->emsg = GNUNET_strdup("Record not found");
549     GNUNET_SCHEDULER_add_now (&do_error, handle);
550     return;
551   }
552
553   handle->add_qe = GNUNET_NAMESTORE_records_store (handle->ns_handle,
554                                                    &handle->zone_pkey,
555                                                    handle->label_name,
556                                                    0, NULL,
557                                                    &del_finished,
558                                                    handle);
559 }
560
561 /**
562  * Handle namestore DELETE request
563  *
564  * @param con_handle the connection handle
565  * @param url the url
566  * @param cls the RequestHandle
567  */
568 void
569 namestore_delete (struct GNUNET_REST_RequestHandle *con_handle,
570                  const char* url,
571                  void *cls)
572 {
573   struct RequestHandle *handle = cls;
574   struct GNUNET_HashCode key;
575
576   GNUNET_CRYPTO_hash ("label", strlen ("label"), &key);
577   if ( GNUNET_NO
578       == GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map,
579                                                  &key))
580   {
581     handle->emsg = GNUNET_strdup("Missing name");
582     GNUNET_SCHEDULER_add_now (&do_error, handle);
583     return;
584   }
585   handle->label_name = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map,
586                                                           &key);
587   
588   handle->add_qe = GNUNET_NAMESTORE_records_lookup (handle->ns_handle,
589                                                     &handle->zone_pkey,
590                                                     handle->label_name,
591                                                     &do_error,
592                                                     handle,
593                                                     &del_cont,
594                                                     handle);
595
596 }
597
598
599
600 /**
601  * Respond to OPTIONS request
602  *
603  * @param con_handle the connection handle
604  * @param url the url
605  * @param cls the RequestHandle
606  */
607 static void
608 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
609               const char* url,
610               void *cls)
611 {
612   struct MHD_Response *resp;
613   struct RequestHandle *handle = cls;
614
615   //independent of path return all options
616   resp = GNUNET_REST_create_response (NULL);
617   MHD_add_response_header (resp,
618                            "Access-Control-Allow-Methods",
619                            allow_methods);
620   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
621   GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
622   return;
623 }
624
625
626 /**
627  * Handle rest request
628  *
629  * @param handle the request handle
630  */
631 static void
632 init_cont (struct RequestHandle *handle)
633 {
634   struct GNUNET_REST_RequestHandlerError err;
635   static const struct GNUNET_REST_RequestHandler handlers[] = {
636     {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_NAMESTORE, &namestore_get},
637     {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_NAMESTORE, &namestore_add},
638     {MHD_HTTP_METHOD_DELETE, GNUNET_REST_API_NS_NAMESTORE, &namestore_delete},
639     {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_NAMESTORE, &options_cont},
640     GNUNET_REST_HANDLER_END
641   };
642
643   if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle,
644                                                handlers,
645                                                &err,
646                                                handle))
647   {
648     handle->response_code = err.error_code;
649     GNUNET_SCHEDULER_add_now (&do_error, handle);
650   }
651 }
652
653 /**
654  *
655  */
656 static void
657 default_ego_cb (void *cls,
658                 struct GNUNET_IDENTITY_Ego *ego,
659                 void **ctx,
660                 const char *name)
661 {
662   struct RequestHandle *handle = cls;
663   struct EgoEntry *ego_entry;
664   struct GNUNET_CRYPTO_EcdsaPublicKey pk;
665
666   handle->op = NULL;
667
668   if (NULL == ego)
669   {
670     handle->emsg = GNUNET_strdup ("No default ego configured in identity service");
671     GNUNET_SCHEDULER_add_now (&do_error, handle);
672     return;
673   }
674   ego_entry = GNUNET_new(struct EgoEntry);
675   GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
676   ego_entry->keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
677   ego_entry->identifier = name;
678   ego_entry->ego = ego;
679   handle->ego_entry = ego_entry;
680   handle->zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
681   init_cont (handle);
682 }
683
684
685 /**
686  * Connect to identity callback
687  */
688 static void
689 id_connect_cb (void *cls,
690                struct GNUNET_IDENTITY_Ego *ego,
691                void **ctx,
692                const char *name)
693 {
694   struct RequestHandle *handle = cls;
695   if (NULL == ego)
696   {
697     handle->op = GNUNET_IDENTITY_get (handle->identity_handle,
698                                       GNUNET_REST_SUBSYSTEM_NAMESTORE,
699                                       &default_ego_cb,
700                                       handle);
701   }
702 }
703
704
705 /**
706  * Function processing the REST call
707  *
708  * @param method HTTP method
709  * @param url URL of the HTTP request
710  * @param data body of the HTTP request (optional)
711  * @param data_size length of the body
712  * @param proc callback function for the result
713  * @param proc_cls closure for callback function
714  * @return GNUNET_OK if request accepted
715  */
716 static void
717 rest_process_request(struct GNUNET_REST_RequestHandle *rest_handle,
718                               GNUNET_REST_ResultProcessor proc,
719                               void *proc_cls)
720 {
721   struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
722   
723   handle->response_code = 0;
724   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
725   handle->proc_cls = proc_cls;
726   handle->proc = proc;
727   handle->rest_handle = rest_handle;
728   
729   handle->url = GNUNET_strdup (rest_handle->url);
730   if (handle->url[strlen (handle->url)-1] == '/')
731     handle->url[strlen (handle->url)-1] = '\0';
732   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
733
734   handle->identity_handle = GNUNET_IDENTITY_connect (cfg, &id_connect_cb, handle);
735   handle->ns_handle = GNUNET_NAMESTORE_connect (cfg);
736   handle->timeout_task =
737     GNUNET_SCHEDULER_add_delayed (handle->timeout,
738                                   &do_error,
739                                   handle);
740   
741   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
742 }
743
744
745 /**
746  * Entry point for the plugin.
747  *
748  * @param cls Config info
749  * @return NULL on error, otherwise the plugin context
750  */
751 void *
752 libgnunet_plugin_rest_namestore_init (void *cls)
753 {
754   static struct Plugin plugin;
755   struct GNUNET_REST_Plugin *api;
756
757   cfg = cls;
758   if (NULL != plugin.cfg)
759     return NULL;                /* can only initialize once! */
760   memset (&plugin, 0, sizeof (struct Plugin));
761   plugin.cfg = cfg;
762   api = GNUNET_new (struct GNUNET_REST_Plugin);
763   api->cls = &plugin;
764   api->name = GNUNET_REST_API_NS_NAMESTORE;
765   api->process_request = &rest_process_request;
766   GNUNET_asprintf (&allow_methods,
767                    "%s, %s, %s, %s, %s",
768                    MHD_HTTP_METHOD_GET,
769                    MHD_HTTP_METHOD_POST,
770                    MHD_HTTP_METHOD_PUT,
771                    MHD_HTTP_METHOD_DELETE,
772                    MHD_HTTP_METHOD_OPTIONS);
773
774   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
775               _("Namestore REST API initialized\n"));
776   return api;
777 }
778
779
780 /**
781  * Exit point from the plugin.
782  *
783  * @param cls the plugin context (as returned by "init")
784  * @return always NULL
785  */
786 void *
787 libgnunet_plugin_rest_namestore_done (void *cls)
788 {
789   struct GNUNET_REST_Plugin *api = cls;
790   struct Plugin *plugin = api->cls;
791   plugin->cfg = NULL;
792
793   GNUNET_free_non_null (allow_methods);
794   GNUNET_free (api);
795   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
796               "Namestore REST plugin is finished\n");
797   return NULL;
798 }
799
800 /* end of plugin_rest_namestore.c */
801