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