From 8ec74596f3d40d1f33c0b2969b390b3c9be56dd9 Mon Sep 17 00:00:00 2001 From: David Barksdale Date: Mon, 8 Oct 2018 16:34:39 -0500 Subject: Starting WebRTC transport --- gnunet-build/packages/gnunet/gnunet/Buildrules | 16 +- .../packages/gnunet/gnunet/files/configuration.js | 2 +- .../gnunet/files/gnunet-git-4ac7b3-all.patch | 40 +- .../gnunet/gnunet/files/plugin_transport_webrtc.c | 542 +++++++++++++++++++++ .../gnunet/files/plugin_transport_webrtc_int.js | 20 + gnunet-build/packages/gnunet/gnunet/files/pre.js | 1 + 6 files changed, 615 insertions(+), 6 deletions(-) create mode 100644 gnunet-build/packages/gnunet/gnunet/files/plugin_transport_webrtc.c create mode 100644 gnunet-build/packages/gnunet/gnunet/files/plugin_transport_webrtc_int.js diff --git a/gnunet-build/packages/gnunet/gnunet/Buildrules b/gnunet-build/packages/gnunet/gnunet/Buildrules index ddf8785..754a362 100644 --- a/gnunet-build/packages/gnunet/gnunet/Buildrules +++ b/gnunet-build/packages/gnunet/gnunet/Buildrules @@ -16,6 +16,8 @@ pkg_compile() { cp "${F}/scheduler.c" "${S}/src/util/" cp "${F}/plugin_transport_http_client_emscripten.c" \ "${S}/src/transport/" + cp "${F}/plugin_transport_webrtc.c" \ + "${S}/src/transport/" cp "${F}/plugin_peerstore_emscripten.c" \ "${S}/src/peerstore/" cp "${F}/plugin_datastore_emscripten.c" \ @@ -193,6 +195,16 @@ pkg_compile() { -o "${S}/src/transport/libgnunet_plugin_transport_http_client.js" \ "${S}/src/transport/libgnunet_plugin_transport_http_client_la-plugin_transport_http_client_emscripten.lo" \ "${S}/src/transport/libgnunet_plugin_transport_http_client_la-plugin_transport_http_common.lo" + ./libtool --tag=CC --mode=link \ + emcc -fno-strict-aliasing -Wall \ + ${OPT_LEVEL} \ + -s SIDE_MODULE=1 \ + -s EXPORTED_FUNCTIONS='[ + "_libgnunet_plugin_transport_webrtc_init", + ]' \ + "-I${SYSROOT}/usr/include" "-L${SYSROOT}/usr/lib" \ + -o "${S}/src/transport/libgnunet_plugin_transport_webrtc.js" \ + "${S}/src/transport/libgnunet_plugin_transport_webrtc_la-plugin_transport_webrtc.lo" ./libtool --tag=CC --mode=link \ emcc -fno-strict-aliasing -Wall \ ${OPT_LEVEL} \ @@ -215,11 +227,13 @@ pkg_compile() { --js-library "${F}/network.js" \ --js-library "${F}/plugin.js" \ --js-library "${F}/scheduler.js" \ - --js-library "${F}/plugin_transport_http_client_emscripten_int.js" \ + --js-library "${F}/plugin_transport_http_client_emscripten_int.js" \ + --js-library "${F}/plugin_transport_webrtc_int.js" \ --pre-js "${F}/pre.js" cp "${S}/src/transport/.libs/gnunet-service-transport.js" \ "${S}/src/transport/.libs/gnunet-service-transport.js.mem" \ "${S}/src/transport/libgnunet_plugin_transport_http_client.js" \ + "${S}/src/transport/libgnunet_plugin_transport_webrtc.js" \ "${D}/var/lib/gnunet/js/" # # Core diff --git a/gnunet-build/packages/gnunet/gnunet/files/configuration.js b/gnunet-build/packages/gnunet/gnunet/files/configuration.js index d67d53f..f227ec6 100644 --- a/gnunet-build/packages/gnunet/gnunet/files/configuration.js +++ b/gnunet-build/packages/gnunet/gnunet/files/configuration.js @@ -25,7 +25,7 @@ mergeInto(LibraryManager.library, { transport: { UNIXPATH: 'transport', NEIGHBOUR_LIMIT: 50, - PLUGINS: 'http_client', + PLUGINS: 'http_client webrtc', }, ats: { UNIXPATH: 'ats', diff --git a/gnunet-build/packages/gnunet/gnunet/files/gnunet-git-4ac7b3-all.patch b/gnunet-build/packages/gnunet/gnunet/files/gnunet-git-4ac7b3-all.patch index 69ec34b..2adb46c 100644 --- a/gnunet-build/packages/gnunet/gnunet/files/gnunet-git-4ac7b3-all.patch +++ b/gnunet-build/packages/gnunet/gnunet/files/gnunet-git-4ac7b3-all.patch @@ -903,7 +903,7 @@ index 3aef05769..beaf6a204 100644 $(FLAT_PLUGIN) diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am -index d0db6b141..6ac35c1b7 100644 +index d0db6b141..46ac240d8 100644 --- a/src/transport/Makefile.am +++ b/src/transport/Makefile.am @@ -80,20 +80,6 @@ endif @@ -927,7 +927,7 @@ index d0db6b141..6ac35c1b7 100644 install-exec-hook: $(top_srcdir)/src/transport/install-wlan-helper.sh $(DESTDIR)$(libexecdir) $(SUDO_BINARY) || true if HAVE_LIBBLUETOOTH -@@ -278,27 +264,7 @@ gnunet_service_transport_CFLAGS = \ +@@ -278,27 +264,8 @@ gnunet_service_transport_CFLAGS = \ $(CFLAGS) # -DANALYZE @@ -952,11 +952,12 @@ index d0db6b141..6ac35c1b7 100644 -# to the plugin_LTLIBRARIES above -noinst_LTLIBRARIES = \ - libgnunet_plugin_transport_template.la -+plugin_LTLIBRARIES = libgnunet_plugin_transport_http_client.la ++plugin_LTLIBRARIES = libgnunet_plugin_transport_http_client.la \ ++ libgnunet_plugin_transport_webrtc.la libgnunet_plugin_transport_tcp_la_SOURCES = \ plugin_transport_tcp.c -@@ -398,7 +364,7 @@ libgnunet_plugin_transport_unix_la_LDFLAGS = \ +@@ -398,19 +365,35 @@ libgnunet_plugin_transport_unix_la_LDFLAGS = \ libgnunet_plugin_transport_http_client_la_SOURCES = \ @@ -965,6 +966,37 @@ index d0db6b141..6ac35c1b7 100644 libgnunet_plugin_transport_http_client_la_LIBADD = \ $(top_builddir)/src/hello/libgnunethello.la \ $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ +- $(LIB_GNURL) \ +- $(top_builddir)/src/util/libgnunetutil.la ++ $(top_builddir)/src/util/libgnunetutil.la \ ++ $(LTLIBINTL) + libgnunet_plugin_transport_http_client_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) + libgnunet_plugin_transport_http_client_la_CFLAGS = \ + $(CFLAGS) + libgnunet_plugin_transport_http_client_la_CPPFLAGS = \ +- $(CPP_GNURL) $(AM_CPPFLAGS) ++ $(AM_CPPFLAGS) ++ ++ ++libgnunet_plugin_transport_webrtc_la_SOURCES = \ ++ plugin_transport_webrtc.c ++libgnunet_plugin_transport_webrtc_la_LIBADD = \ ++ $(top_builddir)/src/hello/libgnunethello.la \ ++ $(top_builddir)/src/statistics/libgnunetstatistics.la \ ++ $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ ++ $(top_builddir)/src/util/libgnunetutil.la \ ++ $(LTLIBINTL) ++libgnunet_plugin_transport_webrtc_la_LDFLAGS = \ ++ $(GN_PLUGIN_LDFLAGS) ++libgnunet_plugin_transport_webrtc_la_CFLAGS = \ ++ $(CFLAGS) ++libgnunet_plugin_transport_webrtc_la_CPPFLAGS = \ ++ $(AM_CPPFLAGS) + + + libgnunet_plugin_transport_http_server_la_SOURCES = \ diff --git a/src/transport/tcp_connection_legacy.c b/src/transport/tcp_connection_legacy.c index 66902c6a0..02c1cf37e 100644 --- a/src/transport/tcp_connection_legacy.c diff --git a/gnunet-build/packages/gnunet/gnunet/files/plugin_transport_webrtc.c b/gnunet-build/packages/gnunet/gnunet/files/plugin_transport_webrtc.c new file mode 100644 index 0000000..8204757 --- /dev/null +++ b/gnunet-build/packages/gnunet/gnunet/files/plugin_transport_webrtc.c @@ -0,0 +1,542 @@ +/* + This file is part of GNUnet + Copyright (C) 2002-2018 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +/** + * @file transport/plugin_transport_webrtc.c + * @brief WebRTC transport plugin + * @author Christian Grothoff + * @author David Barksdale + */ + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_statistics_service.h" +#include "gnunet_transport_service.h" +#include "gnunet_transport_plugin.h" + +#define LOG(kind,...) GNUNET_log_from (kind, PLUGIN_NAME,__VA_ARGS__) + +/** + * After how long do we expire an address that we + * learned from another peer if it is not reconfirmed + * by anyone? + */ +#define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 1) + +#define PLUGIN_NAME "webrtc" + +/** + * Encapsulation of all of the state of the plugin. + */ +struct Plugin; + + +/** + * Session handle for connections. + */ +struct GNUNET_ATS_Session +{ + /** + * To whom are we talking to (set to our identity + * if we are still waiting for the welcome message) + */ + struct GNUNET_PeerIdentity sender; + + /** + * Stored in a linked list (or a peer map, or ...) + */ + struct GNUNET_ATS_Session *next; + + /** + * Pointer to the global plugin struct. + */ + struct Plugin *plugin; + + /** + * The client (used to identify this connection) + */ + /* void *client; */ + + /** + * Continuation function to call once the transmission buffer + * has again space available. NULL if there is no + * continuation to call. + */ + GNUNET_TRANSPORT_TransmitContinuation transmit_cont; + + /** + * Closure for @e transmit_cont. + */ + void *transmit_cont_cls; + + /** + * At what time did we reset @e last_received last? + */ + struct GNUNET_TIME_Absolute last_quota_update; + + /** + * How many bytes have we received since the @e last_quota_update + * timestamp? + */ + uint64_t last_received; + + /** + * Number of bytes per ms that this peer is allowed + * to send to us. + */ + uint32_t quota; + +}; + +/** + * Encapsulation of all of the state of the plugin. + */ +struct Plugin +{ + /** + * Our environment. + */ + struct GNUNET_TRANSPORT_PluginEnvironment *env; + + /** + * List of open sessions (or peer map, or...) + */ + struct GNUNET_ATS_Session *sessions; + + /** + * Function to call about session status changes. + */ + GNUNET_TRANSPORT_SessionInfoCallback sic; + + /** + * Closure for @e sic. + */ + void *sic_cls; +}; + + +#if 0 +/** + * If a session monitor is attached, notify it about the new + * session state. + * + * @param plugin our plugin + * @param session session that changed state + * @param state new state of the session + */ +static void +notify_session_monitor (struct Plugin *plugin, + struct GNUNET_ATS_Session *session, + enum GNUNET_TRANSPORT_SessionState state) +{ + struct GNUNET_TRANSPORT_SessionInfo info; + + if (NULL == plugin->sic) + return; + memset (&info, 0, sizeof (info)); + info.state = state; + info.is_inbound = GNUNET_SYSERR; /* FIXME */ + // info.num_msg_pending = + // info.num_bytes_pending = + // info.receive_delay = + // info.session_timeout = session->timeout; + // info.address = session->address; + plugin->sic (plugin->sic_cls, + session, + &info); +} +#endif + + +/** + * Function that can be used by the transport service to transmit + * a message using the plugin. Note that in the case of a + * peer disconnecting, the continuation MUST be called + * prior to the disconnect notification itself. This function + * will be called with this peer's HELLO message to initiate + * a fresh connection to another peer. + * + * @param cls closure + * @param session which session must be used + * @param msgbuf the message to transmit + * @param msgbuf_size number of bytes in @a msgbuf + * @param priority how important is the message (most plugins will + * ignore message priority and just FIFO) + * @param to how long to wait at most for the transmission (does not + * require plugins to discard the message after the timeout, + * just advisory for the desired delay; most plugins will ignore + * this as well) + * @param cont continuation to call once the message has + * been transmitted (or if the transport is ready + * for the next transmission call; or if the + * peer disconnected...); can be NULL + * @param cont_cls closure for @a cont + * @return number of bytes used (on the physical network, with overheads); + * -1 on hard errors (i.e. address invalid); 0 is a legal value + * and does NOT mean that the message was not transmitted (DV) + */ +static ssize_t +webrtc_plugin_send (void *cls, + struct GNUNET_ATS_Session *session, + const char *msgbuf, + size_t msgbuf_size, + unsigned int priority, + struct GNUNET_TIME_Relative to, + GNUNET_TRANSPORT_TransmitContinuation cont, + void *cont_cls) +{ + /* struct Plugin *plugin = cls; */ + ssize_t bytes_sent = 0; + + return bytes_sent; +} + + +/** + * Function that can be used to force the plugin to disconnect + * from the given peer and cancel all previous transmissions + * (and their continuationc). + * + * @param cls closure + * @param target peer from which to disconnect + */ +static void +webrtc_plugin_disconnect_peer (void *cls, + const struct GNUNET_PeerIdentity *target) +{ + // struct Plugin *plugin = cls; + // FIXME +} + + +/** + * Function that can be used to force the plugin to disconnect + * from the given peer and cancel all previous transmissions + * (and their continuationc). + * + * @param cls closure + * @param session session from which to disconnect + * @return #GNUNET_OK on success + */ +static int +webrtc_plugin_disconnect_session (void *cls, + struct GNUNET_ATS_Session *session) +{ + // struct Plugin *plugin = cls; + // FIXME + return GNUNET_SYSERR; +} + + +/** + * Function that is called to get the keepalive factor. + * GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT is divided by this number to + * calculate the interval between keepalive packets. + * + * @param cls closure with the `struct Plugin` + * @return keepalive factor + */ +static unsigned int +webrtc_plugin_query_keepalive_factor (void *cls) +{ + return 3; +} + + +/** + * Function obtain the network type for a session + * + * @param cls closure ('struct Plugin*') + * @param session the session + * @return the network type in HBO or #GNUNET_SYSERR + */ +static enum GNUNET_ATS_Network_Type +webrtc_plugin_get_network (void *cls, + struct GNUNET_ATS_Session *session) +{ + GNUNET_assert (NULL != session); + return GNUNET_ATS_NET_UNSPECIFIED; /* Change to correct network type */ +} + + +/** + * Function obtain the network type for an address. + * + * @param cls closure (`struct Plugin *`) + * @param address the address + * @return the network type + */ +static enum GNUNET_ATS_Network_Type +webrtc_plugin_get_network_for_address (void *cls, + const struct GNUNET_HELLO_Address *address) +{ + return GNUNET_ATS_NET_WAN; /* FOR NOW */ +} + + +/** + * Convert the transports address to a nice, human-readable + * format. + * + * @param cls closure + * @param type name of the transport that generated the address + * @param addr one of the addresses of the host, NULL for the last address + * the specific address format depends on the transport + * @param addrlen length of the address + * @param numeric should (IP) addresses be displayed in numeric form? + * @param timeout after how long should we give up? + * @param asc function to call on each string + * @param asc_cls closure for @a asc + */ +static void +webrtc_plugin_address_pretty_printer (void *cls, const char *type, + const void *addr, size_t addrlen, + int numeric, + struct GNUNET_TIME_Relative timeout, + GNUNET_TRANSPORT_AddressStringCallback + asc, void *asc_cls) +{ + asc (asc_cls, "converted address", GNUNET_OK); /* return address */ + asc (asc_cls, NULL, GNUNET_OK); /* done */ +} + + + +/** + * Another peer has suggested an address for this + * peer and transport plugin. Check that this could be a valid + * address. If so, consider adding it to the list + * of addresses. + * + * @param cls closure + * @param addr pointer to the address + * @param addrlen length of addr + * @return #GNUNET_OK if this is a plausible address for this peer + * and transport + */ +static int +webrtc_plugin_address_suggested (void *cls, const void *addr, size_t addrlen) +{ + /* struct Plugin *plugin = cls; */ + + /* check if the address is belonging to the plugin*/ + return GNUNET_OK; +} + + +/** + * Function called for a quick conversion of the binary address to + * a numeric address. Note that the caller must not free the + * address and that the next call to this function is allowed + * to override the address again. + * + * @param cls closure + * @param addr binary address + * @param addrlen length of the address + * @return string representing the same address + */ +static const char * +webrtc_plugin_address_to_string (void *cls, const void *addr, size_t addrlen) +{ + /* + * Print address in format webrtc.options.address + */ + + if (0 == addrlen) + { + return TRANSPORT_SESSION_INBOUND_STRING; + } + + GNUNET_break (0); + return NULL; +} + + +/** + * Function called to convert a string address to + * a binary address. + * + * @param cls closure ('struct Plugin*') + * @param addr string address + * @param addrlen length of the @a addr + * @param buf location to store the buffer + * @param added location to store the number of bytes in the buffer. + * If the function returns #GNUNET_SYSERR, its contents are undefined. + * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure + */ +static int +webrtc_plugin_string_to_address (void *cls, + const char *addr, + uint16_t addrlen, + void **buf, size_t *added) +{ + /* + * Parse string in format webrtc.options.address + */ + GNUNET_break (0); + return GNUNET_SYSERR; +} + + +/** + * Create a new session to transmit data to the target + * This session will used to send data to this peer and the plugin will + * notify us by calling the env->session_end function + * + * @param cls closure + * @param address pointer to the GNUNET_HELLO_Address + * @return the session if the address is valid, NULL otherwise + */ +static struct GNUNET_ATS_Session * +webrtc_plugin_get_session (void *cls, + const struct GNUNET_HELLO_Address *address) +{ + GNUNET_break (0); + return NULL; +} + + +static void +webrtc_plugin_update_session_timeout (void *cls, + const struct GNUNET_PeerIdentity *peer, + struct GNUNET_ATS_Session *session) +{ + +} + + +#if 0 +/** + * Return information about the given session to the + * monitor callback. + * + * @param cls the `struct Plugin` with the monitor callback (`sic`) + * @param peer peer we send information about + * @param value our `struct GNUNET_ATS_Session` to send information about + * @return #GNUNET_OK (continue to iterate) + */ +static int +send_session_info_iter (void *cls, + const struct GNUNET_PeerIdentity *peer, + void *value) +{ + struct Plugin *plugin = cls; + struct GNUNET_ATS_Session *session = value; + + notify_session_monitor (plugin, + session, + GNUNET_TRANSPORT_SS_UP); + return GNUNET_OK; +} +#endif + + +/** + * Begin monitoring sessions of a plugin. There can only + * be one active monitor per plugin (i.e. if there are + * multiple monitors, the transport service needs to + * multiplex the generated events over all of them). + * + * @param cls closure of the plugin + * @param sic callback to invoke, NULL to disable monitor; + * plugin will being by iterating over all active + * sessions immediately and then enter monitor mode + * @param sic_cls closure for @a sic + */ +static void +webrtc_plugin_setup_monitor (void *cls, + GNUNET_TRANSPORT_SessionInfoCallback sic, + void *sic_cls) +{ + struct Plugin *plugin = cls; + + plugin->sic = sic; + plugin->sic_cls = sic_cls; + if (NULL != sic) + { +#if 0 + GNUNET_CONTAINER_multipeermap_iterate (NULL /* FIXME */, + &send_session_info_iter, + plugin); +#endif + /* signal end of first iteration */ + sic (sic_cls, NULL, NULL); + } +} + + +/** + * Entry point for the plugin. + */ +void * +libgnunet_plugin_transport_webrtc_init (void *cls) +{ + struct GNUNET_TRANSPORT_PluginEnvironment *env = cls; + struct GNUNET_TRANSPORT_PluginFunctions *api; + struct Plugin *plugin; + + if (NULL == env->receive) + { + /* run in 'stub' mode (i.e. as part of gnunet-peerinfo), don't fully + initialze the plugin or the API */ + api = GNUNET_new (struct GNUNET_TRANSPORT_PluginFunctions); + api->cls = NULL; + api->address_to_string = &webrtc_plugin_address_to_string; + api->string_to_address = &webrtc_plugin_string_to_address; + api->address_pretty_printer = &webrtc_plugin_address_pretty_printer; + return api; + } + + plugin = GNUNET_new (struct Plugin); + plugin->env = env; + api = GNUNET_new (struct GNUNET_TRANSPORT_PluginFunctions); + api->cls = plugin; + api->send = &webrtc_plugin_send; + api->disconnect_peer = &webrtc_plugin_disconnect_peer; + api->disconnect_session = &webrtc_plugin_disconnect_session; + api->query_keepalive_factor = &webrtc_plugin_query_keepalive_factor; + api->address_pretty_printer = &webrtc_plugin_address_pretty_printer; + api->check_address = &webrtc_plugin_address_suggested; + api->address_to_string = &webrtc_plugin_address_to_string; + api->string_to_address = &webrtc_plugin_string_to_address; + api->get_session = &webrtc_plugin_get_session; + api->get_network = &webrtc_plugin_get_network; + api->get_network_for_address = &webrtc_plugin_get_network_for_address; + api->update_session_timeout = &webrtc_plugin_update_session_timeout; + api->setup_monitor = &webrtc_plugin_setup_monitor; + LOG (GNUNET_ERROR_TYPE_INFO, "Template plugin successfully loaded\n"); + return api; +} + + +/** + * Exit point from the plugin. + */ +void * +libgnunet_plugin_transport_webrtc_done (void *cls) +{ + struct GNUNET_TRANSPORT_PluginFunctions *api = cls; + struct Plugin *plugin = api->cls; + + GNUNET_free (plugin); + GNUNET_free (api); + return NULL; +} + +// vim: set expandtab ts=2 sw=2: diff --git a/gnunet-build/packages/gnunet/gnunet/files/plugin_transport_webrtc_int.js b/gnunet-build/packages/gnunet/gnunet/files/plugin_transport_webrtc_int.js new file mode 100644 index 0000000..8b114fa --- /dev/null +++ b/gnunet-build/packages/gnunet/gnunet/files/plugin_transport_webrtc_int.js @@ -0,0 +1,20 @@ +// plugin_transport_webrtc_int.js - js for webrtc plugin +// Copyright (C) 2018 David Barksdale +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +mergeInto(LibraryManager.library, { +}); + +// vim: set expandtab ts=2 sw=2: diff --git a/gnunet-build/packages/gnunet/gnunet/files/pre.js b/gnunet-build/packages/gnunet/gnunet/files/pre.js index 2da36af..74e2fdc 100644 --- a/gnunet-build/packages/gnunet/gnunet/files/pre.js +++ b/gnunet-build/packages/gnunet/gnunet/files/pre.js @@ -29,6 +29,7 @@ gnunet_prerun = function() { 'datastore_emscripten', 'peerstore_emscripten', 'transport_http_client', + 'transport_webrtc', ].forEach(function(plugin) { FS.createPreloadedFile('/', 'libgnunet_plugin_' + plugin + '.js', 'libgnunet_plugin_' + plugin + '.js', true, false); -- cgit v1.2.3-18-g5258