Merge branch 'master' of gnunet.org:gnunet
[oweals/gnunet.git] / src / rest / gnunet-rest-server.c
1 /*
2    This file is part of GNUnet.
3    Copyright (C) 2012-2015 GNUnet e.V.
4
5    GNUnet is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published
7    by the Free Software Foundation; either version 3, or (at your
8    option) any later version.
9
10    GNUnet is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with GNUnet; see the file COPYING.  If not, write to the
17    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18    Boston, MA 02110-1301, USA.
19    */
20 /**
21  * @author Martin Schanzenbach
22  * @file src/rest/gnunet-rest-server.c
23  * @brief REST service for GNUnet services
24  *
25  */
26 #include "platform.h"
27 #include <microhttpd.h>
28 #include "gnunet_util_lib.h"
29 #include "gnunet_rest_plugin.h"
30
31
32 /**
33  * Default Socks5 listen port.
34  */
35 #define GNUNET_REST_SERVICE_PORT 7776
36
37 /**
38  * Maximum supported length for a URI.
39  * Should die. @deprecated
40  */
41 #define MAX_HTTP_URI_LENGTH 2048
42
43 /**
44  * Port for plaintext HTTP.
45  */
46 #define HTTP_PORT 80
47
48 /**
49  * Port for HTTPS.
50  */
51 #define HTTPS_PORT 443
52
53 /**
54  * After how long do we clean up unused MHD SSL/TLS instances?
55  */
56 #define MHD_CACHE_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
57
58 #define GN_REST_STATE_INIT 0
59 #define GN_REST_STATE_PROCESSING 1
60
61 /**
62  * The task ID
63  */
64 static struct GNUNET_SCHEDULER_Task *httpd_task;
65
66 /**
67  * The port the service is running on (default 7776)
68  */
69 static unsigned long long port = GNUNET_REST_SERVICE_PORT;
70
71 /**
72  * The listen socket of the service for IPv4
73  */
74 static struct GNUNET_NETWORK_Handle *lsock4;
75
76 /**
77  * The listen socket of the service for IPv6
78  */
79 static struct GNUNET_NETWORK_Handle *lsock6;
80
81 /**
82  * The listen task ID for IPv4
83  */
84 static struct GNUNET_SCHEDULER_Task * ltask4;
85
86 /**
87  * The listen task ID for IPv6
88  */
89 static struct GNUNET_SCHEDULER_Task * ltask6;
90
91 /**
92  * Daemon for HTTP
93  */
94 static struct MHD_Daemon *httpd;
95
96 /**
97  * Response we return on failures.
98  */
99 static struct MHD_Response *failure_response;
100
101 /**
102  * Our configuration.
103  */
104 static const struct GNUNET_CONFIGURATION_Handle *cfg;
105
106 /**
107  * Map of loaded plugins.
108  */
109 static struct GNUNET_CONTAINER_MultiHashMap *plugin_map;
110
111 /**
112  * Allowed Origins (CORS)
113  */
114 static char* allow_origin;
115
116 /**
117  * Allowed Headers (CORS)
118  */
119 static char* allow_headers;
120
121 /**
122  * Allowed Credentials (CORS)
123  */
124 static char* allow_credentials;
125
126 /**
127  * MHD Connection handle
128  */
129 struct MhdConnectionHandle
130 {
131   struct MHD_Connection *con;
132
133   struct MHD_Response *response;
134
135   struct GNUNET_REST_Plugin *plugin;
136
137   struct GNUNET_REST_RequestHandle *data_handle;
138
139   struct MHD_PostProcessor *pp;
140
141   int status;
142
143   int state;
144 };
145
146 /* ************************* Global helpers ********************* */
147
148
149 /**
150  * Task run whenever HTTP server operations are pending.
151  *
152  * @param cls NULL
153  */
154 static void
155 do_httpd (void *cls);
156
157
158 /**
159  * Run MHD now, we have extra data ready for the callback.
160  */
161 static void
162 run_mhd_now ()
163 {
164   if (NULL != httpd_task)
165   {
166     GNUNET_SCHEDULER_cancel (httpd_task);
167     httpd_task = NULL;
168   }
169   httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd,
170                                          NULL);
171
172 }
173
174 /**
175  * Plugin result callback
176  *
177  * @param cls closure (MHD connection handle)
178  * @param data the data to return to the caller
179  * @param len length of the data
180  * @param status #GNUNET_OK if successful
181  */
182 static void
183 plugin_callback (void *cls,
184                  struct MHD_Response *resp,
185                  int status)
186 {
187   struct MhdConnectionHandle *handle = cls;
188   handle->status = status;
189   handle->response = resp;
190   run_mhd_now();
191 }
192
193
194 static int
195 cleanup_url_map (void *cls,
196                  const struct GNUNET_HashCode *key,
197                  void *value)
198 {
199   GNUNET_free_non_null (value);
200   return GNUNET_YES;
201 }
202
203
204 static void
205 cleanup_handle (struct MhdConnectionHandle *handle)
206 {
207   if (NULL != handle->response)
208     MHD_destroy_response (handle->response);
209   if (NULL != handle->data_handle)
210   {
211
212     if (NULL != handle->data_handle->header_param_map)
213     {
214       GNUNET_CONTAINER_multihashmap_iterate (handle->data_handle->header_param_map,
215                                              &cleanup_url_map,
216                                              NULL);
217       GNUNET_CONTAINER_multihashmap_destroy (handle->data_handle->header_param_map);
218     }
219     if (NULL != handle->data_handle->url_param_map)
220     {
221       GNUNET_CONTAINER_multihashmap_iterate (handle->data_handle->url_param_map,
222                                              &cleanup_url_map,
223                                              NULL);
224       GNUNET_CONTAINER_multihashmap_destroy (handle->data_handle->url_param_map);
225     }
226     GNUNET_free (handle->data_handle);
227   }
228   GNUNET_free (handle);
229 }
230
231 static int
232 header_iterator (void *cls,
233               enum MHD_ValueKind kind,
234               const char *key,
235               const char *value)
236 {
237   struct GNUNET_REST_RequestHandle *handle = cls;
238   struct GNUNET_HashCode hkey;
239   char *val;
240   char *lowerkey;
241
242   lowerkey = GNUNET_strdup (key);
243   GNUNET_STRINGS_utf8_tolower (key, lowerkey);
244   GNUNET_CRYPTO_hash (lowerkey, strlen (lowerkey), &hkey);
245   GNUNET_asprintf (&val, "%s", value);
246   if (GNUNET_OK !=
247       GNUNET_CONTAINER_multihashmap_put (handle->header_param_map,
248                                          &hkey,
249                                          val,
250                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
251   {
252     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
253                 "Could not load add header `%s'=%s\n",
254                 lowerkey, value);
255   }
256   GNUNET_free (lowerkey);
257   return MHD_YES;
258 }
259
260
261 static int
262 url_iterator (void *cls,
263               enum MHD_ValueKind kind,
264               const char *key,
265               const char *value)
266 {
267   struct GNUNET_REST_RequestHandle *handle = cls;
268   struct GNUNET_HashCode hkey;
269   char *val;
270
271   GNUNET_CRYPTO_hash (key, strlen (key), &hkey);
272   GNUNET_asprintf (&val, "%s", value);
273   if (GNUNET_OK !=
274       GNUNET_CONTAINER_multihashmap_put (handle->url_param_map,
275                                          &hkey,
276                                          val,
277                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
278   {
279     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
280                 "Could not load add url param `%s'=%s\n",
281                 key, value);
282   }
283   return MHD_YES;
284 }
285
286 static int
287 post_data_iter (void *cls,
288                          enum MHD_ValueKind kind,
289                          const char *key,
290                          const char *filename,
291                          const char *content_type,
292                          const char *transfer_encoding,
293                          const char *data,
294                          uint64_t off,
295                          size_t size)
296 {
297   struct GNUNET_REST_RequestHandle *handle = cls;
298   struct GNUNET_HashCode hkey;
299   char *val;
300
301   if (MHD_POSTDATA_KIND != kind)
302     return MHD_YES;
303
304   GNUNET_CRYPTO_hash (key, strlen (key), &hkey);
305   GNUNET_asprintf (&val, "%s", data);
306   if (GNUNET_OK !=
307       GNUNET_CONTAINER_multihashmap_put (handle->url_param_map,
308                                          &hkey,
309                                          val,
310                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
311   {
312     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
313                 "Could not load add url param '%s'=%s\n",
314                 key, data);
315     GNUNET_free(val);
316   }
317   return MHD_YES;
318
319 }
320
321 /* ********************************* MHD response generation ******************* */
322
323 /**
324  * Main MHD callback for handling requests.
325  *
326  * @param cls unused
327  * @param con MHD connection handle
328  * @param url the url in the request
329  * @param meth the HTTP method used ("GET", "PUT", etc.)
330  * @param ver the HTTP version string (i.e. "HTTP/1.1")
331  * @param upload_data the data being uploaded (excluding HEADERS,
332  *        for a POST that fits into memory and that is encoded
333  *        with a supported encoding, the POST data will NOT be
334  *        given in upload_data and is instead available as
335  *        part of MHD_get_connection_values; very large POST
336  *        data *will* be made available incrementally in
337  *        upload_data)
338  * @param upload_data_size set initially to the size of the
339  *        @a upload_data provided; the method must update this
340  *        value to the number of bytes NOT processed;
341  * @param con_cls pointer to location where we store the 'struct Request'
342  * @return MHD_YES if the connection was handled successfully,
343  *         MHD_NO if the socket must be closed due to a serious
344  *         error while handling the request
345  */
346 static int
347 create_response (void *cls,
348                  struct MHD_Connection *con,
349                  const char *url,
350                  const char *meth,
351                  const char *ver,
352                  const char *upload_data,
353                  size_t *upload_data_size,
354                  void **con_cls)
355 {
356   char *plugin_name;
357   struct GNUNET_HashCode key;
358   struct MhdConnectionHandle *con_handle;
359   struct GNUNET_REST_RequestHandle *rest_conndata_handle;
360
361   con_handle = *con_cls;
362
363   if (NULL == *con_cls)
364   {
365     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
366                 "New connection %s\n", url);
367     char tmp_url[strlen(url)+1];
368     strcpy (tmp_url, url);
369     con_handle = GNUNET_new (struct MhdConnectionHandle);
370     con_handle->con = con;
371     con_handle->state = GN_REST_STATE_INIT;
372     *con_cls = con_handle;
373
374     plugin_name = strtok(tmp_url, "/");
375
376     if (NULL != plugin_name)
377     {
378       GNUNET_CRYPTO_hash (plugin_name, strlen (plugin_name), &key);
379
380       con_handle->plugin = GNUNET_CONTAINER_multihashmap_get (plugin_map,
381                                                               &key);
382     }
383     if (NULL == con_handle->plugin)
384     {
385       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
386                   "Queueing response with MHD\n");
387       GNUNET_free (con_handle);
388       return MHD_queue_response (con,
389                                  MHD_HTTP_NOT_FOUND,
390                                  failure_response);
391     }
392
393     return MHD_YES;
394   }
395   if (GN_REST_STATE_INIT == con_handle->state)
396   {
397     rest_conndata_handle = GNUNET_new (struct GNUNET_REST_RequestHandle);
398     rest_conndata_handle->method = meth;
399     rest_conndata_handle->url = url;
400     rest_conndata_handle->data = upload_data;
401     rest_conndata_handle->data_size = *upload_data_size;
402     rest_conndata_handle->url_param_map = GNUNET_CONTAINER_multihashmap_create (16,
403                                                                                 GNUNET_NO);
404     rest_conndata_handle->header_param_map = GNUNET_CONTAINER_multihashmap_create (16,
405                                                                                    GNUNET_NO);
406     con_handle->data_handle = rest_conndata_handle;
407     MHD_get_connection_values (con,
408                                MHD_GET_ARGUMENT_KIND,
409                                &url_iterator,
410                                rest_conndata_handle);
411     MHD_get_connection_values (con,
412                                MHD_HEADER_KIND,
413                                &header_iterator,
414                                rest_conndata_handle);
415     con_handle->pp = MHD_create_post_processor(con,
416                                                65536,
417                                                post_data_iter,
418                                                rest_conndata_handle);
419     if (*upload_data_size)
420     {
421       MHD_post_process(con_handle->pp, upload_data, *upload_data_size);
422     }
423     MHD_destroy_post_processor(con_handle->pp);
424
425     con_handle->state = GN_REST_STATE_PROCESSING;
426     con_handle->plugin->process_request (rest_conndata_handle,
427                                          &plugin_callback,
428                                          con_handle);
429     *upload_data_size = 0;
430   }
431   if (NULL != con_handle->response)
432   {
433     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
434                 "Queueing response from plugin with MHD\n");
435     //Handle Preflights
436     if (NULL != allow_origin)
437     {
438       MHD_add_response_header (con_handle->response,
439                                MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
440                                allow_origin);
441     }
442     if (NULL != allow_credentials)
443     {
444       MHD_add_response_header (con_handle->response,
445                                "Access-Control-Allow-Credentials",
446                                allow_credentials);
447     }
448     if (NULL != allow_headers)
449     {
450       MHD_add_response_header (con_handle->response,
451                                "Access-Control-Allow-Headers",
452                                allow_headers);
453     }
454     //Always add JSONAPI content type. TODO
455     MHD_add_response_header (con_handle->response,
456                              MHD_HTTP_HEADER_CONTENT_TYPE,
457                              "application/vnd.api+json");
458     int ret = MHD_queue_response (con,
459                                   con_handle->status,
460                                   con_handle->response);
461     cleanup_handle (con_handle);
462     return ret;
463   }
464   return MHD_YES;
465 }
466
467
468 /* ******************** MHD HTTP setup and event loop ******************** */
469
470 /**
471  * Function called when MHD decides that we are done with a connection.
472  *
473  * @param cls NULL
474  * @param connection connection handle
475  * @param con_cls value as set by the last call to
476  *        the MHD_AccessHandlerCallback, should be our handle
477  * @param toe reason for request termination (ignored)
478  */
479 static void
480 mhd_completed_cb (void *cls,
481                   struct MHD_Connection *connection,
482                   void **con_cls,
483                   enum MHD_RequestTerminationCode toe)
484 {
485   if (MHD_REQUEST_TERMINATED_COMPLETED_OK != toe)
486     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
487                 "MHD encountered error handling request: %d\n",
488                 toe);
489 }
490
491
492 /**
493  * Kill the MHD daemon.
494  */
495 static void
496 kill_httpd ()
497 {
498   if (NULL != httpd)
499   {
500     MHD_stop_daemon (httpd);
501     httpd = NULL;
502   }
503   if (NULL != httpd_task)
504   {
505     GNUNET_SCHEDULER_cancel (httpd_task);
506     httpd_task = NULL;
507   }
508   if (NULL != ltask4)
509   {
510     GNUNET_SCHEDULER_cancel (ltask4);
511     ltask4 = NULL;
512   }
513   if (NULL != ltask6)
514   {
515     GNUNET_SCHEDULER_cancel (ltask6);
516     ltask6 = NULL;
517   }
518
519   if (NULL != lsock4)
520   {
521     GNUNET_NETWORK_socket_close (lsock4);
522     lsock4 = NULL;
523   }
524   if (NULL != lsock6)
525   {
526     GNUNET_NETWORK_socket_close (lsock6);
527     lsock6 = NULL;
528   }
529   }
530
531
532 /**
533  * Schedule MHD.  This function should be called initially when an
534  * MHD is first getting its client socket, and will then automatically
535  * always be called later whenever there is work to be done.
536  *
537  * @param hd the daemon to schedule
538  */
539 static void
540 schedule_httpd ()
541 {
542   fd_set rs;
543   fd_set ws;
544   fd_set es;
545   struct GNUNET_NETWORK_FDSet *wrs;
546   struct GNUNET_NETWORK_FDSet *wws;
547   int max;
548   int haveto;
549   MHD_UNSIGNED_LONG_LONG timeout;
550   struct GNUNET_TIME_Relative tv;
551
552   FD_ZERO (&rs);
553   FD_ZERO (&ws);
554   FD_ZERO (&es);
555   max = -1;
556   if (MHD_YES != MHD_get_fdset (httpd, &rs, &ws, &es, &max))
557   {
558     kill_httpd ();
559     return;
560   }
561   haveto = MHD_get_timeout (httpd, &timeout);
562   if (MHD_YES == haveto)
563     tv.rel_value_us = (uint64_t) timeout * 1000LL;
564   else
565     tv = GNUNET_TIME_UNIT_FOREVER_REL;
566   if (-1 != max)
567   {
568     wrs = GNUNET_NETWORK_fdset_create ();
569     wws = GNUNET_NETWORK_fdset_create ();
570     GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
571     GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
572   }
573   else
574   {
575     wrs = NULL;
576     wws = NULL;
577   }
578   if (NULL != httpd_task)
579   {
580     GNUNET_SCHEDULER_cancel (httpd_task);
581     httpd_task = NULL;
582   }
583   if ( (MHD_YES == haveto) ||
584        (-1 != max))
585   {
586     httpd_task =
587       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
588                                    tv, wrs, wws,
589                                    &do_httpd, NULL);
590     
591   }
592   if (NULL != wrs)
593     GNUNET_NETWORK_fdset_destroy (wrs);
594   if (NULL != wws)
595     GNUNET_NETWORK_fdset_destroy (wws);
596 }
597
598 /**
599  * Task run whenever HTTP server operations are pending.
600  *
601  * @param cls NULL
602  */
603 static void
604 do_httpd (void *cls)
605 {
606   httpd_task = NULL;
607   MHD_run (httpd);
608   schedule_httpd ();
609 }
610
611
612 /**
613  * Accept new incoming connections
614  *
615  * @param cls the closure with the lsock4 or lsock6
616  * @param tc the scheduler context
617  */
618 static void
619 do_accept (void *cls)
620 {
621   struct GNUNET_NETWORK_Handle *lsock = cls;
622   struct GNUNET_NETWORK_Handle *s;
623   int fd;
624   const struct sockaddr *addr;
625   socklen_t len;
626
627   GNUNET_assert (NULL != lsock);
628   if (lsock == lsock4)
629   {
630     ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
631                                             lsock,
632                                             &do_accept, lsock);
633
634   }
635   else if (lsock == lsock6)
636   {
637     ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
638                                             lsock,
639                                             &do_accept, lsock);
640
641   }
642   else
643     GNUNET_assert (0);
644   s = GNUNET_NETWORK_socket_accept (lsock, NULL, NULL);
645   if (NULL == s)
646   {
647     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "accept");
648     return;
649   }
650   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
651               "Got an inbound connection, waiting for data\n");
652   fd = GNUNET_NETWORK_get_fd (s);
653   addr = GNUNET_NETWORK_get_addr (s);
654   len = GNUNET_NETWORK_get_addrlen (s);
655   if (MHD_YES != MHD_add_connection (httpd, fd, addr, len))
656   {
657     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
658                 _("Failed to pass client to MHD\n"));
659     return;
660   }
661   GNUNET_free(s);
662   schedule_httpd ();
663 }
664
665
666 /**
667  * Task run on shutdown
668  *
669  * @param cls closure
670  */
671 static void
672 do_shutdown (void *cls)
673 {
674   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
675               "Shutting down...\n");
676   kill_httpd ();
677   GNUNET_free_non_null (allow_origin);
678   GNUNET_free_non_null (allow_credentials);
679   GNUNET_free_non_null (allow_headers);
680 }
681
682
683 /**
684  * Create an IPv4 listen socket bound to our port.
685  *
686  * @return NULL on error
687  */
688 static struct GNUNET_NETWORK_Handle *
689 bind_v4 ()
690 {
691   struct GNUNET_NETWORK_Handle *ls;
692   struct sockaddr_in sa4;
693   int eno;
694
695   memset (&sa4, 0, sizeof (sa4));
696   sa4.sin_family = AF_INET;
697   sa4.sin_port = htons (port);
698 #if HAVE_SOCKADDR_IN_SIN_LEN
699   sa4.sin_len = sizeof (sa4);
700 #endif
701   ls = GNUNET_NETWORK_socket_create (AF_INET,
702                                      SOCK_STREAM,
703                                      0);
704   if (NULL == ls)
705     return NULL;
706   if (GNUNET_OK !=
707       GNUNET_NETWORK_socket_bind (ls, (const struct sockaddr *) &sa4,
708                                   sizeof (sa4)))
709   {
710     eno = errno;
711     GNUNET_NETWORK_socket_close (ls);
712     errno = eno;
713     return NULL;
714   }
715   return ls;
716 }
717
718
719 /**
720  * Create an IPv6 listen socket bound to our port.
721  *
722  * @return NULL on error
723  */
724 static struct GNUNET_NETWORK_Handle *
725 bind_v6 ()
726 {
727   struct GNUNET_NETWORK_Handle *ls;
728   struct sockaddr_in6 sa6;
729   int eno;
730
731   memset (&sa6, 0, sizeof (sa6));
732   sa6.sin6_family = AF_INET6;
733   sa6.sin6_port = htons (port);
734 #if HAVE_SOCKADDR_IN_SIN_LEN
735   sa6.sin6_len = sizeof (sa6);
736 #endif
737   ls = GNUNET_NETWORK_socket_create (AF_INET6,
738                                      SOCK_STREAM,
739                                      0);
740   if (NULL == ls)
741     return NULL;
742   if (GNUNET_OK !=
743       GNUNET_NETWORK_socket_bind (ls, (const struct sockaddr *) &sa6,
744                                   sizeof (sa6)))
745   {
746     eno = errno;
747     GNUNET_NETWORK_socket_close (ls);
748     errno = eno;
749     return NULL;
750   }
751   return ls;
752 }
753
754
755 /**
756  * Callback for plugin load
757  *
758  * @param cls NULL
759  * @param libname the name of the library loaded
760  * @param lib_ret the object returned by the plugin initializer
761  */
762 static void
763 load_plugin (void *cls,
764              const char *libname,
765              void *lib_ret)
766 {
767   struct GNUNET_REST_Plugin *plugin = lib_ret;
768   struct GNUNET_HashCode key;
769   if (NULL == lib_ret)
770   {
771     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
772                 "Could not load plugin `%s'\n",
773                 libname);
774     return;
775   }
776   GNUNET_assert (1 < strlen (plugin->name));
777   GNUNET_assert ('/' == *plugin->name);
778   GNUNET_CRYPTO_hash (plugin->name+1, strlen (plugin->name+1), &key);
779   if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (plugin_map,
780                                                       &key,
781                                                       plugin,
782                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
783   {
784     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
785                 "Could not load add plugin `%s'\n",
786                 libname);
787     return;
788   }
789   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
790               "Loaded plugin `%s'\n",
791               libname);
792 }
793
794
795 /**
796  * Main function that will be run
797  *
798  * @param cls closure
799  * @param args remaining command-line arguments
800  * @param cfgfile name of the configuration file used (for saving, can be NULL)
801  * @param c configuration
802  */
803 static void
804 run (void *cls,
805      char *const *args,
806      const char *cfgfile,
807      const struct GNUNET_CONFIGURATION_Handle *c)
808 {
809   cfg = c;
810   plugin_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
811
812   /* Get CORS data from cfg */
813   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "rest",
814                                                           "REST_ALLOW_ORIGIN",
815                                                           &allow_origin))
816   {
817     //No origin specified
818     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
819                 "No CORS Access-Control-Allow-Origin Header will be sent...\n");
820   }
821
822   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "rest",
823                                                           "REST_ALLOW_CREDENTIALS",
824                                                           &allow_credentials))
825   {
826     //No origin specified
827     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
828                 "No CORS Access-Control-Allow-Origin Header will be sent...\n");
829   }
830
831   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, "rest",
832                                                           "REST_ALLOW_HEADERS",
833                                                           &allow_headers))
834   {
835     //No origin specified
836     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
837                 "No CORS Access-Control-Allow-Headers Header will be sent...\n");
838   }
839
840   /* Open listen socket proxy */
841   lsock6 = bind_v6 ();
842   if (NULL == lsock6)
843   {
844     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
845   }
846   else
847   {
848     if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock6, 5))
849     {
850       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
851       GNUNET_NETWORK_socket_close (lsock6);
852       lsock6 = NULL;
853     }
854     else
855     {
856       ltask6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
857                                               lsock6, &do_accept, lsock6);
858
859     }
860   }
861   lsock4 = bind_v4 ();
862   if (NULL == lsock4)
863   {
864     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
865   }
866   else
867   {
868     if (GNUNET_OK != GNUNET_NETWORK_socket_listen (lsock4, 5))
869     {
870       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen");
871       GNUNET_NETWORK_socket_close (lsock4);
872       lsock4 = NULL;
873     }
874     else
875     {
876       ltask4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
877                                               lsock4, &do_accept, lsock4);
878
879     }
880   }
881   if ( (NULL == lsock4) &&
882        (NULL == lsock6) )
883   {
884     GNUNET_SCHEDULER_shutdown ();
885     return;
886   }
887   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
888               "Service listens on port %llu\n",
889               port);
890   httpd = MHD_start_daemon (MHD_USE_DEBUG | MHD_USE_NO_LISTEN_SOCKET,
891                             0,
892                             NULL, NULL,
893                             &create_response, NULL,
894                             MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
895                             MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb, NULL,
896                             MHD_OPTION_END);
897   if (NULL == httpd)
898   {
899     GNUNET_SCHEDULER_shutdown ();
900     return;
901   }
902   /* Load plugins */
903   GNUNET_PLUGIN_load_all ("libgnunet_plugin_rest",
904                           (void *) cfg,
905                           &load_plugin,
906                           NULL);
907   GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
908 }
909
910
911 /**
912  *
913  * The main function for gnunet-rest-service
914  *
915  * @param argc number of arguments from the cli
916  * @param argv command line arguments
917  * @return 0 ok, 1 on error
918  *
919  */
920 int
921 main (int argc, char *const *argv)
922 {
923   struct GNUNET_GETOPT_CommandLineOption options[] = {
924     GNUNET_GETOPT_option_ulong ('p',
925                                 "port",
926                                 "PORT",
927                                 gettext_noop ("listen on specified port (default: 7776)"),
928                                 &port),
929     GNUNET_GETOPT_OPTION_END
930   };
931   static const char* err_page =
932     "{}";
933   int ret;
934
935   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
936     return 2;
937   GNUNET_log_setup ("gnunet-rest-server", "WARNING", NULL);
938   failure_response = MHD_create_response_from_buffer (strlen(err_page),
939                                                       (void*)err_page,
940                                                       MHD_RESPMEM_PERSISTENT);
941   ret =
942     (GNUNET_OK ==
943      GNUNET_PROGRAM_run (argc, argv, "gnunet-rest-server",
944                          _("GNUnet REST server"),
945                          options,
946                          &run, NULL)) ? 0: 1;
947   MHD_destroy_response (failure_response);
948   GNUNET_free_non_null ((char *) argv);
949   return ret;
950 }
951
952 /* end of gnunet-rest-server.c */