[PATCH 2/3] fff-web: add web interface package

Tim Niemeyer tim.niemeyer at mastersword.de
So Dez 13 22:16:10 CET 2015


Signed-off-by: Tim Niemeyer <tim.niemeyer at mastersword.de>
---

 src/packages/fff/fff-web/Makefile                  |  42 +
 src/packages/fff/fff-web/files/etc/config/uhttpd   |  35 +
 src/packages/fff/fff-web/files/etc/httpd.conf      |   1 +
 .../fff/fff-web/files/www/public/cgi-bin/status    |  63 ++
 .../files/www/public/css/grids-responsive-min.css  | 491 ++++++++++++
 .../fff/fff-web/files/www/public/css/pure-min.css  | 892 +++++++++++++++++++++
 .../fff/fff-web/files/www/public/css/side-menu.css | 276 +++++++
 .../fff/fff-web/files/www/public/css/style.css     |  78 ++
 .../fff/fff-web/files/www/public/index.html        |  13 +
 .../fff/fff-web/files/www/ssl/cgi-bin/home         |  71 ++
 .../fff/fff-web/files/www/ssl/cgi-bin/misc         |  33 +
 .../fff/fff-web/files/www/ssl/cgi-bin/password     |  16 +
 .../fff/fff-web/files/www/ssl/cgi-bin/settings     |  16 +
 .../fff/fff-web/files/www/ssl/cgi-bin/upgrade      |  36 +
 src/packages/fff/fff-web/files/www/ssl/home.html   |  54 ++
 src/packages/fff/fff-web/files/www/ssl/home.js     |  46 ++
 src/packages/fff/fff-web/files/www/ssl/index.html  | 124 +++
 .../fff/fff-web/files/www/ssl/password.html        |  28 +
 src/packages/fff/fff-web/files/www/ssl/password.js |  24 +
 .../fff/fff-web/files/www/ssl/settings.html        |  26 +
 src/packages/fff/fff-web/files/www/ssl/settings.js | 142 ++++
 src/packages/fff/fff-web/files/www/ssl/shared.js   | 432 ++++++++++
 src/packages/fff/fff-web/files/www/ssl/style.css   | 226 ++++++
 .../fff/fff-web/files/www/ssl/upgrade.html         |  42 +
 src/packages/fff/fff-web/files/www/ssl/upgrade.js  |  12 +
 .../fff/fff-web/files/www/ssl/wifiscan.html        |  26 +
 src/packages/fff/fff-web/files/www/ssl/wifiscan.js |  77 ++
 src/packages/fff/fff/Makefile                      |   4 +-
 28 files changed, 3324 insertions(+), 2 deletions(-)
 create mode 100644 src/packages/fff/fff-web/Makefile
 create mode 100644 src/packages/fff/fff-web/files/etc/config/uhttpd
 create mode 100755 src/packages/fff/fff-web/files/etc/httpd.conf
 create mode 100755 src/packages/fff/fff-web/files/www/public/cgi-bin/status
 create mode 100644 src/packages/fff/fff-web/files/www/public/css/grids-responsive-min.css
 create mode 100644 src/packages/fff/fff-web/files/www/public/css/pure-min.css
 create mode 100644 src/packages/fff/fff-web/files/www/public/css/side-menu.css
 create mode 100644 src/packages/fff/fff-web/files/www/public/css/style.css
 create mode 100644 src/packages/fff/fff-web/files/www/public/index.html
 create mode 100755 src/packages/fff/fff-web/files/www/ssl/cgi-bin/home
 create mode 100755 src/packages/fff/fff-web/files/www/ssl/cgi-bin/misc
 create mode 100755 src/packages/fff/fff-web/files/www/ssl/cgi-bin/password
 create mode 100755 src/packages/fff/fff-web/files/www/ssl/cgi-bin/settings
 create mode 100755 src/packages/fff/fff-web/files/www/ssl/cgi-bin/upgrade
 create mode 100644 src/packages/fff/fff-web/files/www/ssl/home.html
 create mode 100644 src/packages/fff/fff-web/files/www/ssl/home.js
 create mode 100644 src/packages/fff/fff-web/files/www/ssl/index.html
 create mode 100644 src/packages/fff/fff-web/files/www/ssl/password.html
 create mode 100644 src/packages/fff/fff-web/files/www/ssl/password.js
 create mode 100644 src/packages/fff/fff-web/files/www/ssl/settings.html
 create mode 100644 src/packages/fff/fff-web/files/www/ssl/settings.js
 create mode 100644 src/packages/fff/fff-web/files/www/ssl/shared.js
 create mode 100644 src/packages/fff/fff-web/files/www/ssl/style.css
 create mode 100644 src/packages/fff/fff-web/files/www/ssl/upgrade.html
 create mode 100644 src/packages/fff/fff-web/files/www/ssl/upgrade.js
 create mode 100644 src/packages/fff/fff-web/files/www/ssl/wifiscan.html
 create mode 100644 src/packages/fff/fff-web/files/www/ssl/wifiscan.js

diff --git a/src/packages/fff/fff-web/Makefile b/src/packages/fff/fff-web/Makefile
new file mode 100644
index 0000000..429c38f
--- /dev/null
+++ b/src/packages/fff/fff-web/Makefile
@@ -0,0 +1,42 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=fff-web
+PKG_VERSION:=0.0.1
+PKG_RELEASE:=1
+
+PKG_BUILD_DIR:=$(BUILD_DIR)/fff-web
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/fff-web
+    SECTION:=base
+    CATEGORY:=Freifunk
+    TITLE:= Freifunk-Franken Webinterface
+    URL:=http://www.freifunk-franken.de
+    DEPENDS:=+uhttpd +haserl +uhttpd-mod-tls +px5g +simple-tc
+endef
+
+define Package/fff-web/description
+    This is the Webinterface for the Freifunk Franken Firmware
+endef
+
+define Build/Prepare
+	echo "all: " > $(PKG_BUILD_DIR)/Makefile
+endef
+
+define Build/Configure
+	# nothing
+endef
+
+define Build/Compile
+	# nothing
+endef
+
+define Package/fff-web/install
+	$(CP) ./files/* $(1)/
+ifeq ($(CONFIG_PACKAGE_fff-nodewatcher),y)
+	ln -s ../../tmp/crawldata/node.data $(1)/www/public/node.data
+endif
+endef
+
+$(eval $(call BuildPackage,fff-web))
diff --git a/src/packages/fff/fff-web/files/etc/config/uhttpd b/src/packages/fff/fff-web/files/etc/config/uhttpd
new file mode 100644
index 0000000..bf95281
--- /dev/null
+++ b/src/packages/fff/fff-web/files/etc/config/uhttpd
@@ -0,0 +1,35 @@
+
+config uhttpd public
+	list listen_http 80
+	option home /www/public
+	option rfc1918_filter 1
+	option cgi_prefix /cgi-bin
+	option script_timeout 60
+	option network_timeout 30
+	option tcp_keepalive 1
+	option config "_"
+
+config uhttpd ssl
+	list listen_https 443
+	option home /www/ssl
+	option rfc1918_filter 1
+	option cert /etc/uhttpd.crt
+	option key /etc/uhttpd.key
+	option cgi_prefix /cgi-bin
+	option script_timeout 60
+	option network_timeout 30
+	option tcp_keepalive 1
+	option config "/etc/httpd.conf"
+
+
+# Certificate defaults for px5g key generator
+config cert px5g
+
+# Validity time
+option days 1400
+
+# RSA key size
+option bits 2048
+
+# Common name
+option commonname OpenWrt
diff --git a/src/packages/fff/fff-web/files/etc/httpd.conf b/src/packages/fff/fff-web/files/etc/httpd.conf
new file mode 100755
index 0000000..77f237a
--- /dev/null
+++ b/src/packages/fff/fff-web/files/etc/httpd.conf
@@ -0,0 +1 @@
+/:root:$p$root
diff --git a/src/packages/fff/fff-web/files/www/public/cgi-bin/status b/src/packages/fff/fff-web/files/www/public/cgi-bin/status
new file mode 100755
index 0000000..bfc9a61
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/public/cgi-bin/status
@@ -0,0 +1,63 @@
+#!/usr/bin/haserl
+<% echo -en "content-type: text/html\r\n\r\n" %>
+<% . /etc/firmware_release %> 
+<!DOCTYPE html>
+<html lang="de"><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+    <meta charset="utf-8">
+	<meta name="viewport" content="width=device-width, initial-scale=1">
+	<title>info</title>
+	<link rel="stylesheet" href="/css/pure-min.css">
+	<link rel="stylesheet" href="/css/side-menu.css">
+	<link rel="stylesheet" href="/css/grids-responsive-min.css">
+	<link rel="stylesheet" href="/css/style.css">
+</head>
+<body>
+	<div class="header">
+		<div class="home-menu pure-menu pure-menu-open pure-menu-horizontal pure-menu-fixed">
+			<a class="pure-menu-heading" href="http://www.freifunk-franken.de">freifunk-franken.de</a>
+			<ul>
+<%
+contact="$(uci get -q freifunk. at settings[0].contact)"
+if [ -n "$contact" ]; then
+	echo "				<li><a href='#'>Kontakt: $contact</a></li>"
+fi
+
+geo="$(uci get -q freifunk. at settings[0].geo)"
+if [ -n "$geo" ]; then
+	lat="${geo%% *}" lon="${geo##* }"
+	echo "				<li><a href=\"https://www.openstreetmap.org?mlat=$lat&mlon=$lon&zoom=17\">Position: ${lat:0:8}N, ${lon:0:8}E</a></li>"
+fi
+%>
+				<li><form action="https://<% echo -n "$HTTP_HOST" %>"><button type="submit" class="pure-button">Login</button></form></li>
+			</ul>
+		</div>
+	</div>
+	<div class="header">
+<%
+echo "		<h1>$(uci get -q freifunk. at settings[0].name)</h1>"
+%>
+		<h2>Firmware Version <% echo -n $FIRMWARE_VERSION %></h2>
+	</div>
+	<div class="content">
+		<div class="pure-g" style="text-align:center;">
+			<div class="pure-u-1 pure-u-md-1-3">
+				<div class="blockhead"></div>
+				<h2>Nachbarknoten</h2>
+				<h3><% echo -n $(cat /sys/kernel/debug/batman_adv/bat0/originators | grep '^[0-9a-f]' | cut -b 37-53 | sort | uniq | wc -l 2> /dev/null) %></h3>
+			</div>
+			<div class="pure-u-1 pure-u-md-1-3">
+				<div class="blockhead"></div>
+				<h2>Alle Knoten</h2>
+				<h3><% echo -n $((`cat /sys/kernel/debug/batman_adv/bat0/transtable_global | grep '^ [^ ]' | cut -b 39-55 | sort | uniq | wc -l 2> /dev/null`+1)) %></h3>
+			</div>
+			<div class="pure-u-1 pure-u-md-1-3">
+				<div class="blockhead"></div>
+				<h2>Lokale Clients</h2>
+				<h3><% echo -n $(cat /sys/kernel/debug/batman_adv/bat0/transtable_local 2> /dev/null | grep -c 'W') %></h3>
+			</div>
+		</div>
+	</div>
+
+</body>
+</html>
diff --git a/src/packages/fff/fff-web/files/www/public/css/grids-responsive-min.css b/src/packages/fff/fff-web/files/www/public/css/grids-responsive-min.css
new file mode 100644
index 0000000..63482de
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/public/css/grids-responsive-min.css
@@ -0,0 +1,491 @@
+/*!
+Pure v0.5.0
+Copyright 2014 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+https://github.com/yui/pure/blob/master/LICENSE.md
+*/
+ at media screen and (min-width:35.5em){
+.pure-u-sm-1,.pure-u-sm-1-1,.pure-u-sm-1-2,.pure-u-sm-1-3,.pure-u-sm-2-3,.pure-u-sm-1-4,.pure-u-sm-3-4,.pure-u-sm-1-5,.pure-u-sm-2-5,.pure-u-sm-3-5,.pure-u-sm-4-5,.pure-u-sm-5-5,.pure-u-sm-1-6,.pure-u-sm-5-6,.pure-u-sm-1-8,.pure-u-sm-3-8,.pure-u-sm-5-8,.pure-u-sm-7-8,.pure-u-sm-1-12,.pure-u-sm-5-12,.pure-u-sm-7-12,.pure-u-sm-11-12,.pure-u-sm-1-24,.pure-u-sm-2-24,.pure-u-sm-3-24,.pure-u-sm-4-24,.pure-u-sm-5-24,.pure-u-sm-6-24,.pure-u-sm-7-24,.pure-u-sm-8-24,.pure-u-sm-9-24,.pure-u-sm-10-24,.pure-u-sm-11-24,.pure-u-sm-12-24,.pure-u-sm-13-24,.pure-u-sm-14-24,.pure-u-sm-15-24,.pure-u-sm-16-24,.pure-u-sm-17-24,.pure-u-sm-18-24,.pure-u-sm-19-24,.pure-u-sm-20-24,.pure-u-sm-21-24,.pure-u-sm-22-24,.pure-u-sm-23-24,.pure-u-sm-24-24{
+display:inline-block;
+*display:inline;
+zoom:1;
+letter-spacing:normal;
+word-spacing:normal;
+vertical-align:top;
+text-rendering:auto
+}
+.pure-u-sm-1-24{
+width:4.1667%;
+*width:4.1357%
+}
+.pure-u-sm-1-12,.pure-u-sm-2-24{
+width:8.3333%;
+*width:8.3023%
+}
+.pure-u-sm-1-8,.pure-u-sm-3-24{
+width:12.5%;
+*width:12.469%
+}
+.pure-u-sm-1-6,.pure-u-sm-4-24{
+width:16.6667%;
+*width:16.6357%
+}
+.pure-u-sm-1-5{
+width:20%;
+*width:19.969%
+}
+.pure-u-sm-5-24{
+width:20.8333%;
+*width:20.8023%
+}
+.pure-u-sm-1-4,.pure-u-sm-6-24{
+width:25%;
+*width:24.969%
+}
+.pure-u-sm-7-24{
+width:29.1667%;
+*width:29.1357%
+}
+.pure-u-sm-1-3,.pure-u-sm-8-24{
+width:33.3333%;
+*width:33.3023%
+}
+.pure-u-sm-3-8,.pure-u-sm-9-24{
+width:37.5%;
+*width:37.469%
+}
+.pure-u-sm-2-5{
+width:40%;
+*width:39.969%
+}
+.pure-u-sm-5-12,.pure-u-sm-10-24{
+width:41.6667%;
+*width:41.6357%
+}
+.pure-u-sm-11-24{
+width:45.8333%;
+*width:45.8023%
+}
+.pure-u-sm-1-2,.pure-u-sm-12-24{
+width:50%;
+*width:49.969%
+}
+.pure-u-sm-13-24{
+width:54.1667%;
+*width:54.1357%
+}
+.pure-u-sm-7-12,.pure-u-sm-14-24{
+width:58.3333%;
+*width:58.3023%
+}
+.pure-u-sm-3-5{
+width:60%;
+*width:59.969%
+}
+.pure-u-sm-5-8,.pure-u-sm-15-24{
+width:62.5%;
+*width:62.469%
+}
+.pure-u-sm-2-3,.pure-u-sm-16-24{
+width:66.6667%;
+*width:66.6357%
+}
+.pure-u-sm-17-24{
+width:70.8333%;
+*width:70.8023%
+}
+.pure-u-sm-3-4,.pure-u-sm-18-24{
+width:75%;
+*width:74.969%
+}
+.pure-u-sm-19-24{
+width:79.1667%;
+*width:79.1357%
+}
+.pure-u-sm-4-5{
+width:80%;
+*width:79.969%
+}
+.pure-u-sm-5-6,.pure-u-sm-20-24{
+width:83.3333%;
+*width:83.3023%
+}
+.pure-u-sm-7-8,.pure-u-sm-21-24{
+width:87.5%;
+*width:87.469%
+}
+.pure-u-sm-11-12,.pure-u-sm-22-24{
+width:91.6667%;
+*width:91.6357%
+}
+.pure-u-sm-23-24{
+width:95.8333%;
+*width:95.8023%
+}
+.pure-u-sm-1,.pure-u-sm-1-1,.pure-u-sm-5-5,.pure-u-sm-24-24{
+width:100%
+}
+}@media screen and (min-width:48em){
+.pure-u-md-1,.pure-u-md-1-1,.pure-u-md-1-2,.pure-u-md-1-3,.pure-u-md-2-3,.pure-u-md-1-4,.pure-u-md-3-4,.pure-u-md-1-5,.pure-u-md-2-5,.pure-u-md-3-5,.pure-u-md-4-5,.pure-u-md-5-5,.pure-u-md-1-6,.pure-u-md-5-6,.pure-u-md-1-8,.pure-u-md-3-8,.pure-u-md-5-8,.pure-u-md-7-8,.pure-u-md-1-12,.pure-u-md-5-12,.pure-u-md-7-12,.pure-u-md-11-12,.pure-u-md-1-24,.pure-u-md-2-24,.pure-u-md-3-24,.pure-u-md-4-24,.pure-u-md-5-24,.pure-u-md-6-24,.pure-u-md-7-24,.pure-u-md-8-24,.pure-u-md-9-24,.pure-u-md-10-24,.pure-u-md-11-24,.pure-u-md-12-24,.pure-u-md-13-24,.pure-u-md-14-24,.pure-u-md-15-24,.pure-u-md-16-24,.pure-u-md-17-24,.pure-u-md-18-24,.pure-u-md-19-24,.pure-u-md-20-24,.pure-u-md-21-24,.pure-u-md-22-24,.pure-u-md-23-24,.pure-u-md-24-24{
+display:inline-block;
+*display:inline;
+zoom:1;
+letter-spacing:normal;
+word-spacing:normal;
+vertical-align:top;
+text-rendering:auto
+}
+.pure-u-md-1-24{
+width:4.1667%;
+*width:4.1357%
+}
+.pure-u-md-1-12,.pure-u-md-2-24{
+width:8.3333%;
+*width:8.3023%
+}
+.pure-u-md-1-8,.pure-u-md-3-24{
+width:12.5%;
+*width:12.469%
+}
+.pure-u-md-1-6,.pure-u-md-4-24{
+width:16.6667%;
+*width:16.6357%
+}
+.pure-u-md-1-5{
+width:20%;
+*width:19.969%
+}
+.pure-u-md-5-24{
+width:20.8333%;
+*width:20.8023%
+}
+.pure-u-md-1-4,.pure-u-md-6-24{
+width:25%;
+*width:24.969%
+}
+.pure-u-md-7-24{
+width:29.1667%;
+*width:29.1357%
+}
+.pure-u-md-1-3,.pure-u-md-8-24{
+width:33.3333%;
+*width:33.3023%
+}
+.pure-u-md-3-8,.pure-u-md-9-24{
+width:37.5%;
+*width:37.469%
+}
+.pure-u-md-2-5{
+width:40%;
+*width:39.969%
+}
+.pure-u-md-5-12,.pure-u-md-10-24{
+width:41.6667%;
+*width:41.6357%
+}
+.pure-u-md-11-24{
+width:45.8333%;
+*width:45.8023%
+}
+.pure-u-md-1-2,.pure-u-md-12-24{
+width:50%;
+*width:49.969%
+}
+.pure-u-md-13-24{
+width:54.1667%;
+*width:54.1357%
+}
+.pure-u-md-7-12,.pure-u-md-14-24{
+width:58.3333%;
+*width:58.3023%
+}
+.pure-u-md-3-5{
+width:60%;
+*width:59.969%
+}
+.pure-u-md-5-8,.pure-u-md-15-24{
+width:62.5%;
+*width:62.469%
+}
+.pure-u-md-2-3,.pure-u-md-16-24{
+width:66.6667%;
+*width:66.6357%
+}
+.pure-u-md-17-24{
+width:70.8333%;
+*width:70.8023%
+}
+.pure-u-md-3-4,.pure-u-md-18-24{
+width:75%;
+*width:74.969%
+}
+.pure-u-md-19-24{
+width:79.1667%;
+*width:79.1357%
+}
+.pure-u-md-4-5{
+width:80%;
+*width:79.969%
+}
+.pure-u-md-5-6,.pure-u-md-20-24{
+width:83.3333%;
+*width:83.3023%
+}
+.pure-u-md-7-8,.pure-u-md-21-24{
+width:87.5%;
+*width:87.469%
+}
+.pure-u-md-11-12,.pure-u-md-22-24{
+width:91.6667%;
+*width:91.6357%
+}
+.pure-u-md-23-24{
+width:95.8333%;
+*width:95.8023%
+}
+.pure-u-md-1,.pure-u-md-1-1,.pure-u-md-5-5,.pure-u-md-24-24{
+width:100%
+}
+}@media screen and (min-width:64em){
+.pure-u-lg-1,.pure-u-lg-1-1,.pure-u-lg-1-2,.pure-u-lg-1-3,.pure-u-lg-2-3,.pure-u-lg-1-4,.pure-u-lg-3-4,.pure-u-lg-1-5,.pure-u-lg-2-5,.pure-u-lg-3-5,.pure-u-lg-4-5,.pure-u-lg-5-5,.pure-u-lg-1-6,.pure-u-lg-5-6,.pure-u-lg-1-8,.pure-u-lg-3-8,.pure-u-lg-5-8,.pure-u-lg-7-8,.pure-u-lg-1-12,.pure-u-lg-5-12,.pure-u-lg-7-12,.pure-u-lg-11-12,.pure-u-lg-1-24,.pure-u-lg-2-24,.pure-u-lg-3-24,.pure-u-lg-4-24,.pure-u-lg-5-24,.pure-u-lg-6-24,.pure-u-lg-7-24,.pure-u-lg-8-24,.pure-u-lg-9-24,.pure-u-lg-10-24,.pure-u-lg-11-24,.pure-u-lg-12-24,.pure-u-lg-13-24,.pure-u-lg-14-24,.pure-u-lg-15-24,.pure-u-lg-16-24,.pure-u-lg-17-24,.pure-u-lg-18-24,.pure-u-lg-19-24,.pure-u-lg-20-24,.pure-u-lg-21-24,.pure-u-lg-22-24,.pure-u-lg-23-24,.pure-u-lg-24-24{
+display:inline-block;
+*display:inline;
+zoom:1;
+letter-spacing:normal;
+word-spacing:normal;
+vertical-align:top;
+text-rendering:auto
+}
+.pure-u-lg-1-24{
+width:4.1667%;
+*width:4.1357%
+}
+.pure-u-lg-1-12,.pure-u-lg-2-24{
+width:8.3333%;
+*width:8.3023%
+}
+.pure-u-lg-1-8,.pure-u-lg-3-24{
+width:12.5%;
+*width:12.469%
+}
+.pure-u-lg-1-6,.pure-u-lg-4-24{
+width:16.6667%;
+*width:16.6357%
+}
+.pure-u-lg-1-5{
+width:20%;
+*width:19.969%
+}
+.pure-u-lg-5-24{
+width:20.8333%;
+*width:20.8023%
+}
+.pure-u-lg-1-4,.pure-u-lg-6-24{
+width:25%;
+*width:24.969%
+}
+.pure-u-lg-7-24{
+width:29.1667%;
+*width:29.1357%
+}
+.pure-u-lg-1-3,.pure-u-lg-8-24{
+width:33.3333%;
+*width:33.3023%
+}
+.pure-u-lg-3-8,.pure-u-lg-9-24{
+width:37.5%;
+*width:37.469%
+}
+.pure-u-lg-2-5{
+width:40%;
+*width:39.969%
+}
+.pure-u-lg-5-12,.pure-u-lg-10-24{
+width:41.6667%;
+*width:41.6357%
+}
+.pure-u-lg-11-24{
+width:45.8333%;
+*width:45.8023%
+}
+.pure-u-lg-1-2,.pure-u-lg-12-24{
+width:50%;
+*width:49.969%
+}
+.pure-u-lg-13-24{
+width:54.1667%;
+*width:54.1357%
+}
+.pure-u-lg-7-12,.pure-u-lg-14-24{
+width:58.3333%;
+*width:58.3023%
+}
+.pure-u-lg-3-5{
+width:60%;
+*width:59.969%
+}
+.pure-u-lg-5-8,.pure-u-lg-15-24{
+width:62.5%;
+*width:62.469%
+}
+.pure-u-lg-2-3,.pure-u-lg-16-24{
+width:66.6667%;
+*width:66.6357%
+}
+.pure-u-lg-17-24{
+width:70.8333%;
+*width:70.8023%
+}
+.pure-u-lg-3-4,.pure-u-lg-18-24{
+width:75%;
+*width:74.969%
+}
+.pure-u-lg-19-24{
+width:79.1667%;
+*width:79.1357%
+}
+.pure-u-lg-4-5{
+width:80%;
+*width:79.969%
+}
+.pure-u-lg-5-6,.pure-u-lg-20-24{
+width:83.3333%;
+*width:83.3023%
+}
+.pure-u-lg-7-8,.pure-u-lg-21-24{
+width:87.5%;
+*width:87.469%
+}
+.pure-u-lg-11-12,.pure-u-lg-22-24{
+width:91.6667%;
+*width:91.6357%
+}
+.pure-u-lg-23-24{
+width:95.8333%;
+*width:95.8023%
+}
+.pure-u-lg-1,.pure-u-lg-1-1,.pure-u-lg-5-5,.pure-u-lg-24-24{
+width:100%
+}
+}@media screen and (min-width:80em){
+.pure-u-xl-1,.pure-u-xl-1-1,.pure-u-xl-1-2,.pure-u-xl-1-3,.pure-u-xl-2-3,.pure-u-xl-1-4,.pure-u-xl-3-4,.pure-u-xl-1-5,.pure-u-xl-2-5,.pure-u-xl-3-5,.pure-u-xl-4-5,.pure-u-xl-5-5,.pure-u-xl-1-6,.pure-u-xl-5-6,.pure-u-xl-1-8,.pure-u-xl-3-8,.pure-u-xl-5-8,.pure-u-xl-7-8,.pure-u-xl-1-12,.pure-u-xl-5-12,.pure-u-xl-7-12,.pure-u-xl-11-12,.pure-u-xl-1-24,.pure-u-xl-2-24,.pure-u-xl-3-24,.pure-u-xl-4-24,.pure-u-xl-5-24,.pure-u-xl-6-24,.pure-u-xl-7-24,.pure-u-xl-8-24,.pure-u-xl-9-24,.pure-u-xl-10-24,.pure-u-xl-11-24,.pure-u-xl-12-24,.pure-u-xl-13-24,.pure-u-xl-14-24,.pure-u-xl-15-24,.pure-u-xl-16-24,.pure-u-xl-17-24,.pure-u-xl-18-24,.pure-u-xl-19-24,.pure-u-xl-20-24,.pure-u-xl-21-24,.pure-u-xl-22-24,.pure-u-xl-23-24,.pure-u-xl-24-24{
+display:inline-block;
+*display:inline;
+zoom:1;
+letter-spacing:normal;
+word-spacing:normal;
+vertical-align:top;
+text-rendering:auto
+}
+.pure-u-xl-1-24{
+width:4.1667%;
+*width:4.1357%
+}
+.pure-u-xl-1-12,.pure-u-xl-2-24{
+width:8.3333%;
+*width:8.3023%
+}
+.pure-u-xl-1-8,.pure-u-xl-3-24{
+width:12.5%;
+*width:12.469%
+}
+.pure-u-xl-1-6,.pure-u-xl-4-24{
+width:16.6667%;
+*width:16.6357%
+}
+.pure-u-xl-1-5{
+width:20%;
+*width:19.969%
+}
+.pure-u-xl-5-24{
+width:20.8333%;
+*width:20.8023%
+}
+.pure-u-xl-1-4,.pure-u-xl-6-24{
+width:25%;
+*width:24.969%
+}
+.pure-u-xl-7-24{
+width:29.1667%;
+*width:29.1357%
+}
+.pure-u-xl-1-3,.pure-u-xl-8-24{
+width:33.3333%;
+*width:33.3023%
+}
+.pure-u-xl-3-8,.pure-u-xl-9-24{
+width:37.5%;
+*width:37.469%
+}
+.pure-u-xl-2-5{
+width:40%;
+*width:39.969%
+}
+.pure-u-xl-5-12,.pure-u-xl-10-24{
+width:41.6667%;
+*width:41.6357%
+}
+.pure-u-xl-11-24{
+width:45.8333%;
+*width:45.8023%
+}
+.pure-u-xl-1-2,.pure-u-xl-12-24{
+width:50%;
+*width:49.969%
+}
+.pure-u-xl-13-24{
+width:54.1667%;
+*width:54.1357%
+}
+.pure-u-xl-7-12,.pure-u-xl-14-24{
+width:58.3333%;
+*width:58.3023%
+}
+.pure-u-xl-3-5{
+width:60%;
+*width:59.969%
+}
+.pure-u-xl-5-8,.pure-u-xl-15-24{
+width:62.5%;
+*width:62.469%
+}
+.pure-u-xl-2-3,.pure-u-xl-16-24{
+width:66.6667%;
+*width:66.6357%
+}
+.pure-u-xl-17-24{
+width:70.8333%;
+*width:70.8023%
+}
+.pure-u-xl-3-4,.pure-u-xl-18-24{
+width:75%;
+*width:74.969%
+}
+.pure-u-xl-19-24{
+width:79.1667%;
+*width:79.1357%
+}
+.pure-u-xl-4-5{
+width:80%;
+*width:79.969%
+}
+.pure-u-xl-5-6,.pure-u-xl-20-24{
+width:83.3333%;
+*width:83.3023%
+}
+.pure-u-xl-7-8,.pure-u-xl-21-24{
+width:87.5%;
+*width:87.469%
+}
+.pure-u-xl-11-12,.pure-u-xl-22-24{
+width:91.6667%;
+*width:91.6357%
+}
+.pure-u-xl-23-24{
+width:95.8333%;
+*width:95.8023%
+}
+.pure-u-xl-1,.pure-u-xl-1-1,.pure-u-xl-5-5,.pure-u-xl-24-24{
+width:100%
+}
+}
diff --git a/src/packages/fff/fff-web/files/www/public/css/pure-min.css b/src/packages/fff/fff-web/files/www/public/css/pure-min.css
new file mode 100644
index 0000000..1370a60
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/public/css/pure-min.css
@@ -0,0 +1,892 @@
+/*!
+Pure v0.5.0
+Copyright 2014 Yahoo! Inc. All rights reserved.
+Licensed under the BSD License.
+https://github.com/yui/pure/blob/master/LICENSE.md
+*/
+/*!
+normalize.css v1.1.3 | MIT License | git.io/normalize
+Copyright (c) Nicolas Gallagher and Jonathan Neal
+*/
+/*! normalize.css v1.1.3 | MIT License | git.io/normalize */article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{
+display:block
+}
+audio,canvas,video{
+display:inline-block;
+*display:inline;
+*zoom:1
+}
+audio:not([controls]){
+display:none;
+height:0
+}
+[hidden]{
+display:none
+}
+html{
+font-size:100%;
+-ms-text-size-adjust:100%;
+-webkit-text-size-adjust:100%
+}
+html,button,input,select,textarea{
+font-family:sans-serif
+}
+body{
+margin:0
+}
+a:focus{
+outline:thin dotted
+}
+a:active,a:hover{
+outline:0
+}
+h1{
+font-size:2em;
+margin:.67em 0
+}
+h2{
+font-size:1.5em;
+margin:.83em 0
+}
+h3{
+font-size:1.17em;
+margin:1em 0
+}
+h4{
+font-size:1em;
+margin:1.33em 0
+}
+h5{
+font-size:.83em;
+margin:1.67em 0
+}
+h6{
+font-size:.67em;
+margin:2.33em 0
+}
+abbr[title]{
+border-bottom:1px dotted
+}
+b,strong{
+font-weight:700
+}
+blockquote{
+margin:1em 40px
+}
+dfn{
+font-style:italic
+}
+hr{
+-moz-box-sizing:content-box;
+box-sizing:content-box;
+height:0
+}
+mark{
+background:#ff0;
+color:#000
+}
+p,pre{
+margin:1em 0
+}
+code,kbd,pre,samp{
+font-family:monospace,serif;
+_font-family:'courier new',monospace;
+font-size:1em
+}
+pre{
+white-space:pre;
+white-space:pre-wrap;
+word-wrap:break-word
+}
+q{
+quotes:none
+}
+q:before,q:after{
+content:'';
+content:none
+}
+small{
+font-size:80%
+}
+sub,sup{
+font-size:75%;
+line-height:0;
+position:relative;
+vertical-align:baseline
+}
+sup{
+top:-.5em
+}
+sub{
+bottom:-.25em
+}
+dl,menu,ol,ul{
+margin:1em 0
+}
+dd{
+margin:0 0 0 40px
+}
+menu,ol,ul{
+padding:0 0 0 40px
+}
+nav ul,nav ol{
+list-style:none;
+list-style-image:none
+}
+img{
+border:0;
+-ms-interpolation-mode:bicubic
+}
+svg:not(:root){
+overflow:hidden
+}
+figure{
+margin:0
+}
+form{
+margin:0
+}
+fieldset{
+border:1px solid silver;
+margin:0 2px;
+padding:.35em .625em .75em
+}
+legend{
+border:0;
+padding:0;
+white-space:normal;
+*margin-left:-7px
+}
+button,input,select,textarea{
+font-size:100%;
+margin:0;
+vertical-align:baseline;
+*vertical-align:middle
+}
+button,input{
+line-height:normal
+}
+button,select{
+text-transform:none
+}
+button,html input[type=button],input[type=reset],input[type=submit]{
+-webkit-appearance:button;
+cursor:pointer;
+*overflow:visible
+}
+button[disabled],html input[disabled]{
+cursor:default
+}
+input[type=checkbox],input[type=radio]{
+box-sizing:border-box;
+padding:0;
+*height:13px;
+*width:13px
+}
+input[type=search]{
+-webkit-appearance:textfield;
+-moz-box-sizing:content-box;
+-webkit-box-sizing:content-box;
+box-sizing:content-box
+}
+input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{
+-webkit-appearance:none
+}
+button::-moz-focus-inner,input::-moz-focus-inner{
+border:0;
+padding:0
+}
+textarea{
+overflow:auto;
+vertical-align:top
+}
+table{
+border-collapse:collapse;
+border-spacing:0
+}
+[hidden]{
+display:none!important
+}
+.pure-img{
+max-width:100%;
+height:auto;
+display:block
+}
+.pure-g{
+letter-spacing:-.31em;
+*letter-spacing:normal;
+*word-spacing:-.43em;
+text-rendering:optimizespeed;
+font-family:FreeSans,Arimo,"Droid Sans",Helvetica,Arial,sans-serif;
+display:-webkit-flex;
+-webkit-flex-flow:row wrap;
+display:-ms-flexbox;
+-ms-flex-flow:row wrap
+}
+.opera-only :-o-prefocus,.pure-g{
+word-spacing:-.43em
+}
+.pure-u{
+display:inline-block;
+*display:inline;
+zoom:1;
+letter-spacing:normal;
+word-spacing:normal;
+vertical-align:top;
+text-rendering:auto
+}
+.pure-g [class *="pure-u"]{
+font-family:sans-serif
+}
+.pure-u-1,.pure-u-1-1,.pure-u-1-2,.pure-u-1-3,.pure-u-2-3,.pure-u-1-4,.pure-u-3-4,.pure-u-1-5,.pure-u-2-5,.pure-u-3-5,.pure-u-4-5,.pure-u-5-5,.pure-u-1-6,.pure-u-5-6,.pure-u-1-8,.pure-u-3-8,.pure-u-5-8,.pure-u-7-8,.pure-u-1-12,.pure-u-5-12,.pure-u-7-12,.pure-u-11-12,.pure-u-1-24,.pure-u-2-24,.pure-u-3-24,.pure-u-4-24,.pure-u-5-24,.pure-u-6-24,.pure-u-7-24,.pure-u-8-24,.pure-u-9-24,.pure-u-10-24,.pure-u-11-24,.pure-u-12-24,.pure-u-13-24,.pure-u-14-24,.pure-u-15-24,.pure-u-16-24,.pure-u-17-24,.pure-u-18-24,.pure-u-19-24,.pure-u-20-24,.pure-u-21-24,.pure-u-22-24,.pure-u-23-24,.pure-u-24-24{
+display:inline-block;
+*display:inline;
+zoom:1;
+letter-spacing:normal;
+word-spacing:normal;
+vertical-align:top;
+text-rendering:auto
+}
+.pure-u-1-24{
+width:4.1667%;
+*width:4.1357%
+}
+.pure-u-1-12,.pure-u-2-24{
+width:8.3333%;
+*width:8.3023%
+}
+.pure-u-1-8,.pure-u-3-24{
+width:12.5%;
+*width:12.469%
+}
+.pure-u-1-6,.pure-u-4-24{
+width:16.6667%;
+*width:16.6357%
+}
+.pure-u-1-5{
+width:20%;
+*width:19.969%
+}
+.pure-u-5-24{
+width:20.8333%;
+*width:20.8023%
+}
+.pure-u-1-4,.pure-u-6-24{
+width:25%;
+*width:24.969%
+}
+.pure-u-7-24{
+width:29.1667%;
+*width:29.1357%
+}
+.pure-u-1-3,.pure-u-8-24{
+width:33.3333%;
+*width:33.3023%
+}
+.pure-u-3-8,.pure-u-9-24{
+width:37.5%;
+*width:37.469%
+}
+.pure-u-2-5{
+width:40%;
+*width:39.969%
+}
+.pure-u-5-12,.pure-u-10-24{
+width:41.6667%;
+*width:41.6357%
+}
+.pure-u-11-24{
+width:45.8333%;
+*width:45.8023%
+}
+.pure-u-1-2,.pure-u-12-24{
+width:50%;
+*width:49.969%
+}
+.pure-u-13-24{
+width:54.1667%;
+*width:54.1357%
+}
+.pure-u-7-12,.pure-u-14-24{
+width:58.3333%;
+*width:58.3023%
+}
+.pure-u-3-5{
+width:60%;
+*width:59.969%
+}
+.pure-u-5-8,.pure-u-15-24{
+width:62.5%;
+*width:62.469%
+}
+.pure-u-2-3,.pure-u-16-24{
+width:66.6667%;
+*width:66.6357%
+}
+.pure-u-17-24{
+width:70.8333%;
+*width:70.8023%
+}
+.pure-u-3-4,.pure-u-18-24{
+width:75%;
+*width:74.969%
+}
+.pure-u-19-24{
+width:79.1667%;
+*width:79.1357%
+}
+.pure-u-4-5{
+width:80%;
+*width:79.969%
+}
+.pure-u-5-6,.pure-u-20-24{
+width:83.3333%;
+*width:83.3023%
+}
+.pure-u-7-8,.pure-u-21-24{
+width:87.5%;
+*width:87.469%
+}
+.pure-u-11-12,.pure-u-22-24{
+width:91.6667%;
+*width:91.6357%
+}
+.pure-u-23-24{
+width:95.8333%;
+*width:95.8023%
+}
+.pure-u-1,.pure-u-1-1,.pure-u-5-5,.pure-u-24-24{
+width:100%
+}
+.pure-button{
+display:inline-block;
+*display:inline;
+zoom:1;
+line-height:normal;
+white-space:nowrap;
+vertical-align:baseline;
+text-align:center;
+cursor:pointer;
+-webkit-user-drag:none;
+-webkit-user-select:none;
+-moz-user-select:none;
+-ms-user-select:none;
+user-select:none
+}
+.pure-button::-moz-focus-inner{
+padding:0;
+border:0
+}
+.pure-button{
+font-family:inherit;
+font-size:100%;
+*font-size:90%;
+*overflow:visible;
+padding:.5em 1em;
+color:#444;
+color:rgba(0,0,0,.8);
+*color:#444;
+border:1px solid #999;
+border:0 rgba(0,0,0,0);
+background-color:#E6E6E6;
+text-decoration:none;
+border-radius:2px
+}
+.pure-button-hover,.pure-button:hover,.pure-button:focus{
+filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#1a000000', GradientType=0);
+background-image:-webkit-gradient(linear,0 0,0 100%,from(transparent),color-stop(40%,rgba(0,0,0,.05)),to(rgba(0,0,0,.1)));
+background-image:-webkit-linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1));
+background-image:-moz-linear-gradient(top,rgba(0,0,0,.05) 0,rgba(0,0,0,.1));
+background-image:-o-linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1));
+background-image:linear-gradient(transparent,rgba(0,0,0,.05) 40%,rgba(0,0,0,.1))
+}
+.pure-button:focus{
+outline:0
+}
+.pure-button-active,.pure-button:active{
+box-shadow:0 0 0 1px rgba(0,0,0,.15) inset,0 0 6px rgba(0,0,0,.2) inset
+}
+.pure-button[disabled],.pure-button-disabled,.pure-button-disabled:hover,.pure-button-disabled:focus,.pure-button-disabled:active{
+border:0;
+background-image:none;
+filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);
+filter:alpha(opacity=40);
+-khtml-opacity:.4;
+-moz-opacity:.4;
+opacity:.4;
+cursor:not-allowed;
+box-shadow:none
+}
+.pure-button-hidden{
+display:none
+}
+.pure-button::-moz-focus-inner{
+padding:0;
+border:0
+}
+.pure-button-primary,.pure-button-selected,a.pure-button-primary,a.pure-button-selected{
+background-color:#0078e7;
+color:#fff
+}
+.pure-form input[type=text],.pure-form input[type=password],.pure-form input[type=email],.pure-form input[type=url],.pure-form input[type=date],.pure-form input[type=month],.pure-form input[type=time],.pure-form input[type=datetime],.pure-form input[type=datetime-local],.pure-form input[type=week],.pure-form input[type=number],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=color],.pure-form select,.pure-form textarea{
+padding:.5em .6em;
+display:inline-block;
+border:1px solid #ccc;
+box-shadow:inset 0 1px 3px #ddd;
+border-radius:4px;
+-webkit-box-sizing:border-box;
+-moz-box-sizing:border-box;
+box-sizing:border-box
+}
+.pure-form input:not([type]){
+padding:.5em .6em;
+display:inline-block;
+border:1px solid #ccc;
+box-shadow:inset 0 1px 3px #ddd;
+border-radius:4px;
+-webkit-box-sizing:border-box;
+-moz-box-sizing:border-box;
+box-sizing:border-box
+}
+.pure-form input[type=color]{
+padding:.2em .5em
+}
+.pure-form input[type=text]:focus,.pure-form input[type=password]:focus,.pure-form input[type=email]:focus,.pure-form input[type=url]:focus,.pure-form input[type=date]:focus,.pure-form input[type=month]:focus,.pure-form input[type=time]:focus,.pure-form input[type=datetime]:focus,.pure-form input[type=datetime-local]:focus,.pure-form input[type=week]:focus,.pure-form input[type=number]:focus,.pure-form input[type=search]:focus,.pure-form input[type=tel]:focus,.pure-form input[type=color]:focus,.pure-form select:focus,.pure-form textarea:focus{
+outline:0;
+outline:thin dotted \9;
+border-color:#129FEA
+}
+.pure-form input:not([type]):focus{
+outline:0;
+outline:thin dotted \9;
+border-color:#129FEA
+}
+.pure-form input[type=file]:focus,.pure-form input[type=radio]:focus,.pure-form input[type=checkbox]:focus{
+outline:thin dotted #333;
+outline:1px auto #129FEA
+}
+.pure-form .pure-checkbox,.pure-form .pure-radio{
+margin:.5em 0;
+display:block
+}
+.pure-form input[type=text][disabled],.pure-form input[type=password][disabled],.pure-form input[type=email][disabled],.pure-form input[type=url][disabled],.pure-form input[type=date][disabled],.pure-form input[type=month][disabled],.pure-form input[type=time][disabled],.pure-form input[type=datetime][disabled],.pure-form input[type=datetime-local][disabled],.pure-form input[type=week][disabled],.pure-form input[type=number][disabled],.pure-form input[type=search][disabled],.pure-form input[type=tel][disabled],.pure-form input[type=color][disabled],.pure-form select[disabled],.pure-form textarea[disabled]{
+cursor:not-allowed;
+background-color:#eaeded;
+color:#cad2d3
+}
+.pure-form input:not([type])[disabled]{
+cursor:not-allowed;
+background-color:#eaeded;
+color:#cad2d3
+}
+.pure-form input[readonly],.pure-form select[readonly],.pure-form textarea[readonly]{
+background:#eee;
+color:#777;
+border-color:#ccc
+}
+.pure-form input:focus:invalid,.pure-form textarea:focus:invalid,.pure-form select:focus:invalid{
+color:#b94a48;
+border-color:#ee5f5b
+}
+.pure-form input:focus:invalid:focus,.pure-form textarea:focus:invalid:focus,.pure-form select:focus:invalid:focus{
+border-color:#e9322d
+}
+.pure-form input[type=file]:focus:invalid:focus,.pure-form input[type=radio]:focus:invalid:focus,.pure-form input[type=checkbox]:focus:invalid:focus{
+outline-color:#e9322d
+}
+.pure-form select{
+border:1px solid #ccc;
+background-color:#fff
+}
+.pure-form select[multiple]{
+height:auto
+}
+.pure-form label{
+margin:.5em 0 .2em
+}
+.pure-form fieldset{
+margin:0;
+padding:.35em 0 .75em;
+border:0
+}
+.pure-form legend{
+display:block;
+width:100%;
+padding:.3em 0;
+margin-bottom:.3em;
+color:#333;
+border-bottom:1px solid #e5e5e5
+}
+.pure-form-stacked input[type=text],.pure-form-stacked input[type=password],.pure-form-stacked input[type=email],.pure-form-stacked input[type=url],.pure-form-stacked input[type=date],.pure-form-stacked input[type=month],.pure-form-stacked input[type=time],.pure-form-stacked input[type=datetime],.pure-form-stacked input[type=datetime-local],.pure-form-stacked input[type=week],.pure-form-stacked input[type=number],.pure-form-stacked input[type=search],.pure-form-stacked input[type=tel],.pure-form-stacked input[type=color],.pure-form-stacked select,.pure-form-stacked label,.pure-form-stacked textarea{
+display:block;
+margin:.25em 0
+}
+.pure-form-stacked input:not([type]){
+display:block;
+margin:.25em 0
+}
+.pure-form-aligned input,.pure-form-aligned textarea,.pure-form-aligned select,.pure-form-aligned .pure-help-inline,.pure-form-message-inline{
+display:inline-block;
+*display:inline;
+*zoom:1;
+vertical-align:middle
+}
+.pure-form-aligned textarea{
+vertical-align:top
+}
+.pure-form-aligned .pure-control-group{
+margin-bottom:.5em
+}
+.pure-form-aligned .pure-control-group label{
+text-align:right;
+display:inline-block;
+vertical-align:middle;
+width:10em;
+margin:0 1em 0 0
+}
+.pure-form-aligned .pure-controls{
+margin:1.5em 0 0 10em
+}
+.pure-form input.pure-input-rounded,.pure-form .pure-input-rounded{
+border-radius:2em;
+padding:.5em 1em
+}
+.pure-form .pure-group fieldset{
+margin-bottom:10px
+}
+.pure-form .pure-group input{
+display:block;
+padding:10px;
+margin:0;
+border-radius:0;
+position:relative;
+top:-1px
+}
+.pure-form .pure-group input:focus{
+z-index:2
+}
+.pure-form .pure-group input:first-child{
+top:1px;
+border-radius:4px 4px 0 0
+}
+.pure-form .pure-group input:last-child{
+top:-2px;
+border-radius:0 0 4px 4px
+}
+.pure-form .pure-group button{
+margin:.35em 0
+}
+.pure-form .pure-input-1{
+width:100%
+}
+.pure-form .pure-input-2-3{
+width:66%
+}
+.pure-form .pure-input-1-2{
+width:50%
+}
+.pure-form .pure-input-1-3{
+width:33%
+}
+.pure-form .pure-input-1-4{
+width:25%
+}
+.pure-form .pure-help-inline,.pure-form-message-inline{
+display:inline-block;
+padding-left:.3em;
+color:#666;
+vertical-align:middle;
+font-size:.875em
+}
+.pure-form-message{
+display:block;
+color:#666;
+font-size:.875em
+}
+ at media only screen and (max-width :480px){
+.pure-form button[type=submit]{
+margin:.7em 0 0
+}
+.pure-form input:not([type]),.pure-form input[type=text],.pure-form input[type=password],.pure-form input[type=email],.pure-form input[type=url],.pure-form input[type=date],.pure-form input[type=month],.pure-form input[type=time],.pure-form input[type=datetime],.pure-form input[type=datetime-local],.pure-form input[type=week],.pure-form input[type=number],.pure-form input[type=search],.pure-form input[type=tel],.pure-form input[type=color],.pure-form label{
+margin-bottom:.3em;
+display:block
+}
+.pure-group input:not([type]),.pure-group input[type=text],.pure-group input[type=password],.pure-group input[type=email],.pure-group input[type=url],.pure-group input[type=date],.pure-group input[type=month],.pure-group input[type=time],.pure-group input[type=datetime],.pure-group input[type=datetime-local],.pure-group input[type=week],.pure-group input[type=number],.pure-group input[type=search],.pure-group input[type=tel],.pure-group input[type=color]{
+margin-bottom:0
+}
+.pure-form-aligned .pure-control-group label{
+margin-bottom:.3em;
+text-align:left;
+display:block;
+width:100%
+}
+.pure-form-aligned .pure-controls{
+margin:1.5em 0 0
+}
+.pure-form .pure-help-inline,.pure-form-message-inline,.pure-form-message{
+display:block;
+font-size:.75em;
+padding:.2em 0 .8em
+}
+
+}
+.pure-menu ul{
+position:absolute;
+visibility:hidden
+}
+.pure-menu.pure-menu-open{
+visibility:visible;
+z-index:2;
+width:100%
+}
+.pure-menu ul{
+left:-10000px;
+list-style:none;
+margin:0;
+padding:0;
+top:-10000px;
+z-index:1
+}
+.pure-menu>ul{
+position:relative
+}
+.pure-menu-open>ul{
+left:0;
+top:0;
+visibility:visible
+}
+.pure-menu-open>ul:focus{
+outline:0
+}
+.pure-menu li{
+position:relative
+}
+.pure-menu a,.pure-menu .pure-menu-heading{
+display:block;
+color:inherit;
+line-height:1.5em;
+padding:5px 20px;
+text-decoration:none;
+white-space:nowrap
+}
+.pure-menu.pure-menu-horizontal>.pure-menu-heading{
+display:inline-block;
+*display:inline;
+zoom:1;
+margin:0;
+vertical-align:middle
+}
+.pure-menu.pure-menu-horizontal>ul{
+display:inline-block;
+*display:inline;
+zoom:1;
+vertical-align:middle
+}
+.pure-menu li a{
+padding:5px 20px
+}
+.pure-menu-can-have-children>.pure-menu-label:after{
+content:'\25B8';
+float:right;
+font-family:'Lucida Grande','Lucida Sans Unicode','DejaVu Sans',sans-serif;
+margin-right:-20px;
+margin-top:-1px
+}
+.pure-menu-can-have-children>.pure-menu-label{
+padding-right:30px
+}
+.pure-menu-separator{
+background-color:#dfdfdf;
+display:block;
+height:1px;
+font-size:0;
+margin:7px 2px;
+overflow:hidden
+}
+.pure-menu-hidden{
+display:none
+}
+.pure-menu-fixed{
+position:fixed;
+top:0;
+left:0;
+width:100%
+}
+.pure-menu-horizontal li{
+display:inline-block;
+*display:inline;
+zoom:1;
+vertical-align:middle
+}
+.pure-menu-horizontal li li{
+display:block
+}
+.pure-menu-horizontal>.pure-menu-children>.pure-menu-can-have-children>.pure-menu-label:after{
+content:"\25BE"
+}
+.pure-menu-horizontal>.pure-menu-children>.pure-menu-can-have-children>.pure-menu-label{
+padding-right:30px
+}
+.pure-menu-horizontal li.pure-menu-separator{
+height:50%;
+width:1px;
+margin:0 7px
+}
+.pure-menu-horizontal li li.pure-menu-separator{
+height:1px;
+width:auto;
+margin:7px 2px
+}
+.pure-menu.pure-menu-open,.pure-menu.pure-menu-horizontal li .pure-menu-children{
+background:#fff;
+border:1px solid #b7b7b7
+}
+.pure-menu.pure-menu-horizontal,.pure-menu.pure-menu-horizontal .pure-menu-heading{
+border:0
+}
+.pure-menu a{
+border:1px solid transparent;
+border-left:0;
+border-right:0
+}
+.pure-menu a,.pure-menu .pure-menu-can-have-children>li:after{
+color:#777
+}
+.pure-menu .pure-menu-can-have-children>li:hover:after{
+color:#fff
+}
+.pure-menu .pure-menu-open{
+background:#dedede
+}
+.pure-menu li a:hover,.pure-menu li a:focus{
+background:#eee
+}
+.pure-menu li.pure-menu-disabled a:hover,.pure-menu li.pure-menu-disabled a:focus{
+background:#fff;
+color:#bfbfbf
+}
+.pure-menu .pure-menu-disabled>a{
+background-image:none;
+border-color:transparent;
+cursor:default
+}
+.pure-menu .pure-menu-disabled>a,.pure-menu .pure-menu-can-have-children.pure-menu-disabled>a:after{
+color:#bfbfbf
+}
+.pure-menu .pure-menu-heading{
+color:#565d64;
+text-transform:uppercase;
+font-size:90%;
+margin-top:.5em;
+border-bottom-width:1px;
+border-bottom-style:solid;
+border-bottom-color:#dfdfdf
+}
+.pure-menu .pure-menu-selected a{
+color:#000
+}
+.pure-menu.pure-menu-open.pure-menu-fixed{
+border:0;
+border-bottom:1px solid #b7b7b7
+}
+.pure-paginator{
+letter-spacing:-.31em;
+*letter-spacing:normal;
+*word-spacing:-.43em;
+text-rendering:optimizespeed;
+list-style:none;
+margin:0;
+padding:0
+}
+.opera-only :-o-prefocus,.pure-paginator{
+word-spacing:-.43em
+}
+.pure-paginator li{
+display:inline-block;
+*display:inline;
+zoom:1;
+letter-spacing:normal;
+word-spacing:normal;
+vertical-align:top;
+text-rendering:auto
+}
+.pure-paginator .pure-button{
+border-radius:0;
+padding:.8em 1.4em;
+vertical-align:top;
+height:1.1em
+}
+.pure-paginator .pure-button:focus,.pure-paginator .pure-button:active{
+outline-style:none
+}
+.pure-paginator .prev,.pure-paginator .next{
+color:#C0C1C3;
+text-shadow:0 -1px 0 rgba(0,0,0,.45)
+}
+.pure-paginator .prev{
+border-radius:2px 0 0 2px
+}
+.pure-paginator .next{
+border-radius:0 2px 2px 0
+}
+ at media (max-width:480px){
+.pure-menu-horizontal{
+width:100%
+}
+.pure-menu-children li{
+display:block;
+border-bottom:1px solid #000
+}
+
+}
+.pure-table{
+border-collapse:collapse;
+border-spacing:0;
+empty-cells:show;
+border:1px solid #cbcbcb
+}
+.pure-table caption{
+color:#000;
+font:italic 85%/1 arial,sans-serif;
+padding:1em 0;
+text-align:center
+}
+.pure-table td,.pure-table th{
+border-left:1px solid #cbcbcb;
+border-width:0 0 0 1px;
+font-size:inherit;
+margin:0;
+overflow:visible;
+padding:.5em 1em
+}
+.pure-table td:first-child,.pure-table th:first-child{
+border-left-width:0
+}
+.pure-table thead{
+background:#e0e0e0;
+color:#000;
+text-align:left;
+vertical-align:bottom
+}
+.pure-table td{
+background-color:transparent
+}
+.pure-table-odd td{
+background-color:#f2f2f2
+}
+.pure-table-striped tr:nth-child(2n-1) td{
+background-color:#f2f2f2
+}
+.pure-table-bordered td{
+border-bottom:1px solid #cbcbcb
+}
+.pure-table-bordered tbody>tr:last-child td,.pure-table-horizontal tbody>tr:last-child td{
+border-bottom-width:0
+}
+.pure-table-horizontal td,.pure-table-horizontal th{
+border-width:0 0 1px;
+border-bottom:1px solid #cbcbcb
+}
+.pure-table-horizontal tbody>tr:last-child td{
+border-bottom-width:0
+}
diff --git a/src/packages/fff/fff-web/files/www/public/css/side-menu.css b/src/packages/fff/fff-web/files/www/public/css/side-menu.css
new file mode 100644
index 0000000..bbe083e
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/public/css/side-menu.css
@@ -0,0 +1,276 @@
+body {
+    /* color: #777; */
+}
+
+.pure-img-responsive {
+    max-width: 100%;
+    height: auto;
+}
+
+/*
+Add transition to containers so they can push in and out.
+*/
+#layout,
+#menu,
+.menu-link {
+    -webkit-transition: all 0.2s ease-out;
+    -moz-transition: all 0.2s ease-out;
+    -ms-transition: all 0.2s ease-out;
+    -o-transition: all 0.2s ease-out;
+    transition: all 0.2s ease-out;
+}
+
+/*
+This is the parent `<div>` that contains the menu and the content area.
+*/
+#layout {
+    position: relative;
+    padding-left: 0;
+}
+    #layout.active {
+        position: relative;
+        left: 200px;
+    }
+        #layout.active #menu {
+            left: 200px;
+            width: 200px;
+        }
+
+        #layout.active .menu-link {
+            left: 200px;
+        }
+/*
+The content `<div>` is where all your content goes.
+*/
+.content {
+    margin: 0 auto;
+    padding: 0 2em;
+    max-width: 800px;
+    margin-bottom: 50px;
+    line-height: 1.6em;
+	color: #444;
+}
+
+.header {
+     margin: 0;
+     color: #333;
+     text-align: center;
+     padding: 2.5em 2em 0;
+     border-bottom: 1px solid #eee;
+ }
+    .header h1 {
+        margin: 0.2em 0;
+        font-size: 3em;
+        font-weight: 300;
+    }
+     .header h2 {
+        font-weight: 300;
+        color: #ccc;
+        padding: 0;
+        margin-top: 0;
+    }
+
+.content-subhead {
+    margin: 50px 0 20px 0;
+    font-weight: 300;
+    color: #888;
+}
+
+#inout {
+padding: 1em;
+}
+.blockhead {
+	display: block;
+	height: 1em;
+}
+
+.wanmarker {
+	background: #77c;
+	padding: 0.25em;
+    color: #fff;
+}
+
+.lanmarker {
+	background: #cc7;
+	padding: 0.25em;
+    color: #fff;
+}
+
+.wlanmarker {
+	background: #7c7;
+	padding: 0.25em;
+    color: #fff;
+}
+
+.ffmarker {
+    background: #ffc12e;
+    padding: 0.25em;
+    color: #fff;
+}
+
+.infotext {
+	font-size: 0.8em;
+	padding-right: 1em;
+}
+
+/*
+The `#menu` `<div>` is the parent `<div>` that contains the `.pure-menu` that
+appears on the left side of the page.
+*/
+
+#menu {
+    margin-left: -200px; /* "#menu" width */
+    width: 200px;
+    position: fixed;
+    top: 0;
+    left: 0;
+    bottom: 0;
+    z-index: 1000; /* so the menu or its navicon stays above all content */
+    background: #191818;
+    overflow-y: auto;
+    -webkit-overflow-scrolling: touch;
+}
+    /*
+    All anchors inside the menu should be styled like this.
+    */
+    #menu a {
+        color: #aaa;
+        border: none;
+        padding: 0.6em 0 0.6em 0.6em;
+    }
+
+    /*
+    Remove all background/borders, since we are applying them to #menu.
+    */
+     #menu .pure-menu,
+     #menu .pure-menu ul {
+        border: none;
+        background: transparent;
+    }
+
+    /*
+    Add that light border to separate items into groups.
+    */
+    #menu .pure-menu ul,
+    #menu .pure-menu .menu-item-divided {
+        border-top: 1px solid #333;
+    }
+        /*
+        Change color of the anchor links on hover/focus.
+        */
+        #menu .pure-menu li a:hover,
+        #menu .pure-menu li a:focus {
+            background: #dc0067;
+			color: #ffffff;
+        }
+
+    /*
+    This styles the selected menu item `<li>`.
+    */
+	
+	#menu .pure-menu-selected {
+        background: #ffc12e;
+		color: #fff;
+    }
+        /*
+        This styles a link within a selected menu item `<li>`.
+        */
+        #menu .pure-menu-selected a {
+            color: #fff;
+        }
+
+    /*
+    This styles the menu heading.
+    */
+    #menu .pure-menu-heading {
+        font-size: 110%;
+        color: #fff;
+        margin: 0;
+    }
+
+/* -- Dynamic Button For Responsive Menu -------------------------------------*/
+
+/*
+The button to open/close the Menu is custom-made and not part of Pure. Here's
+how it works:
+*/
+
+/*
+`.menu-link` represents the responsive menu toggle that shows/hides on
+small screens.
+*/
+.menu-link {
+    position: fixed;
+    display: block; /* show this only on small screens */
+    top: 0;
+    left: 0; /* "#menu width" */
+    background: #000;
+    background: rgba(0,0,0,0.7);
+    font-size: 10px; /* change this value to increase/decrease button size */
+    z-index: 10;
+    width: 2em;
+    height: auto;
+    padding: 2.1em 1.6em;
+}
+
+    .menu-link:hover,
+    .menu-link:focus {
+        background: #000;
+    }
+
+    .menu-link span {
+        position: relative;
+        display: block;
+    }
+
+    .menu-link span,
+    .menu-link span:before,
+    .menu-link span:after {
+        background-color: #fff;
+        width: 100%;
+        height: 0.2em;
+    }
+
+        .menu-link span:before,
+        .menu-link span:after {
+            position: absolute;
+            margin-top: -0.6em;
+            content: " ";
+        }
+
+        .menu-link span:after {
+            margin-top: 0.6em;
+        }
+
+
+/* -- Responsive Styles (Media Queries) ------------------------------------- */
+
+/*
+Hides the menu at `48em`, but modify this based on your app's needs.
+*/
+ at media (min-width: 48em) {
+
+    .header,
+    .content {
+        padding-left: 2em;
+        padding-right: 2em;
+    }
+
+    #layout {
+        padding-left: 200px; /* left col width "#menu" */
+        left: 0;
+    }
+    #menu {
+        left: 200px;
+    }
+
+    .menu-link {
+        position: fixed;
+        left: 200px;
+        display: none;
+    }
+
+    #layout.active .menu-link {
+        left: 200px;
+    }
+}
\ No newline at end of file
diff --git a/src/packages/fff/fff-web/files/www/public/css/style.css b/src/packages/fff/fff-web/files/www/public/css/style.css
new file mode 100644
index 0000000..a194087
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/public/css/style.css
@@ -0,0 +1,78 @@
+* {
+	-webkit-box-sizing: border-box;
+	-moz-box-sizing: border-box;
+	box-sizing: border-box;
+}
+
+body {
+	line-height: 1.7em;
+	color: #7f8c8d;
+	font-size: 13px;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+label {
+	color: #34495e;
+}
+
+.pure-img-responsive {
+	max-width: 100%;
+	height: auto;
+}
+
+.pure-button {
+	background-color: #1f8dd6;
+	color: white;
+	padding: 0.5em 2em;
+	border-radius: 5px;
+}
+
+a.pure-button-primary {
+	background: white;
+	color: #1f8dd6;
+	border-radius: 5px;
+	font-size: 120%;
+}
+
+.home-menu {
+	padding: 0.5em;
+	text-align: center;
+	box-shadow: 0 1px 1px rgba(0,0,0, 0.10);
+}
+
+.home-menu.pure-menu-open {
+	background: #2d3e50;
+}
+
+.home-menu .pure-menu-heading {
+	color: white;
+	font-weight: 400;
+	font-size: 120%;
+}
+
+.home-menu .pure-menu-selected a {
+	color: white;
+}
+
+.home-menu a {
+	color: #6FBEF3;
+}
+
+.home-menu li a:hover,
+.home-menu li a:focus {
+	background: none;
+	color: #AECFE5;
+}
+
+.home-menu {
+	text-align: left;
+}
+
+.home-menu ul {
+	float: right;
+}
diff --git a/src/packages/fff/fff-web/files/www/public/index.html b/src/packages/fff/fff-web/files/www/public/index.html
new file mode 100644
index 0000000..2278278
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/public/index.html
@@ -0,0 +1,13 @@
+<html>
+<head>
+	<meta charset="utf-8"/>
+	<meta http-equiv="refresh" content="0; URL=/cgi-bin/status">
+	<meta http-equiv="cache-control" content="no-cache">
+	<meta http-equiv="expires" content="0">
+	<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT">
+	<meta http-equiv="pragma" content="no-cache">
+</head>
+<body>
+	<a href="/cgi-bin/status">Redirecting...</a>
+</body>
+</html>
diff --git a/src/packages/fff/fff-web/files/www/ssl/cgi-bin/home b/src/packages/fff/fff-web/files/www/ssl/cgi-bin/home
new file mode 100755
index 0000000..7b04b05
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/ssl/cgi-bin/home
@@ -0,0 +1,71 @@
+#!/usr/bin/haserl
+<%
+echo -en "content-type: text/plain\r\n\r\n"
+
+addr() {
+	local addr="$(ip -$1 address show dev $2 2> /dev/null | tr '/' ' '| awk '/inet/{ printf("%s ", $2); }')"
+	echo "${addr:--}"
+}
+
+default_gw() {
+	ip -$1 route list 0/0 dev $2 2> /dev/null | awk '{print($3); exit(0)}'
+}
+
+print() {
+	echo "	option $1 '$2'"
+}
+
+printList() {
+	for item in $2; do
+		echo "	list $1 '$item'"
+	done
+}
+
+#include OpenWrt version
+. /etc/openwrt_release
+. /etc/firmware_release
+
+echo "package misc"
+echo "config data 'data'"
+print 'mac' "$(uci -q get network.mesh.macaddr)"
+wanif=$(uci -q get network.wan.ifname)
+printList 'freifunk_addr4' "$(addr 4 br-mesh)"
+printList 'freifunk_addr6' "$(addr 6 br-mesh)"
+printList 'wan_addr4' "$(addr 4 $wanif)"
+printList 'wan_addr6' "$(addr 6 $wanif)"
+
+up="$(uptime)"
+print 'load' "${up##*:}"
+uptime="${up%%,*}"
+print 'uptime' "${uptime##*up}"
+print 'uname' "$(uname -s -m -r)"
+print 'date' "$(date)"
+
+if [ $(sockread /var/run/fastd.status < /dev/null 2> /dev/null | grep  -c '"connection": {') -gt 0 ]; then
+	print 'has_vpn' 'Ja'
+else
+	print 'has_vpn' 'Nein'
+fi
+
+if [ -n "$(default_gw 4 $wanif)" -o  -n "$(default_gw 6 $wanif)" ]; then
+	print 'has_internet' 'Ja'
+else
+	print 'has_internet' 'Nein'
+fi
+
+print 'node_count' "$((`cat /sys/kernel/debug/batman_adv/bat0/transtable_global | grep '^ [^ ]' | cut -b 39-55 | sort | uniq | wc -l 2> /dev/null`+1))"
+print 'neigh_count' "$(cat /sys/kernel/debug/batman_adv/bat0/originators | grep '^[0-9a-f]' | cut -b 37-53 | sort | uniq | wc -l 2> /dev/null)"
+print 'firmware_version' "$FIRMWARE_VERSION"
+print 'fastd_version' "$(fastd --version 2> /dev/null | cut -d' ' -f 2)"
+print 'batman_version' "$(cat /sys/module/batman_adv/version 2> /dev/null)"
+print 'openwrt_version' "$DISTRIB_DESCRIPTION"
+name="$(uci get -q 'system. at system[0].hostname')"
+print 'name' "${name:--}"
+print 'model' "$(cat /tmp/sysinfo/model 2> /dev/null)"
+print 'freifunk_user_count' "$(cat /sys/kernel/debug/batman_adv/bat0/transtable_local 2> /dev/null | grep -c 'W')"
+print 'freifunk_rx_bytes' "$(cat /sys/class/net/br-mesh/statistics/rx_bytes 2> /dev/null)"
+print 'freifunk_tx_bytes' "$(cat /sys/class/net/br-mesh/statistics/tx_bytes 2> /dev/null)"
+print 'wan_rx_bytes' "$(cat /sys/class/net/$wanif/statistics/rx_bytes 2> /dev/null)"
+print 'wan_tx_bytes' "$(cat /sys/class/net/$wanif/statistics/tx_bytes 2> /dev/null)"
+
+%>
diff --git a/src/packages/fff/fff-web/files/www/ssl/cgi-bin/misc b/src/packages/fff/fff-web/files/www/ssl/cgi-bin/misc
new file mode 100755
index 0000000..fc98a65
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/ssl/cgi-bin/misc
@@ -0,0 +1,33 @@
+#!/usr/bin/haserl
+<%
+echo -en "content-type: text/plain\r\n\r\n"
+
+case $GET_func in
+	reboot)
+		reboot
+		echo "(I) Bitte warten. Neustart wird durchgeführt..."
+	;;
+	wifi_status)
+		wifi status
+	;;
+	wifiscan)
+		iw dev "$GET_device" scan 2> /dev/null | grep '^BSS \|SSID\|set: channel\|signal\|capability\|MESH ID'
+	;;
+	set_config_file)
+		file_name="$GET_name"
+		file_data="$GET_data"
+
+		if echo "$file_data" > "/etc/config/$file_name" 2> /dev/null; then
+			echo "(I) Einstellungen wurden gespeichert. Bitte Neustarten."
+			if [ "$file_name" = "system" ]; then
+				uci get -q 'system. at system[0].hostname' > /proc/sys/kernel/hostname
+			fi
+		else
+			echo "(E) Beim Speichern ist ein Fehler aufgetreten. Bitte Neustarten."
+		fi
+	;;
+	*)
+		echo "(E) misc: Invalid command: '$GET_func'"
+	;;
+esac
+%>
diff --git a/src/packages/fff/fff-web/files/www/ssl/cgi-bin/password b/src/packages/fff/fff-web/files/www/ssl/cgi-bin/password
new file mode 100755
index 0000000..0b27024
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/ssl/cgi-bin/password
@@ -0,0 +1,16 @@
+#!/usr/bin/haserl
+<%
+echo -en "content-type: text/plain\r\n\r\n"
+
+pass1="$GET_pass1"
+pass2="$GET_pass2"
+
+(echo "$pass1"; sleep 1; echo "$pass2") | passwd &> /dev/null
+if [ $? -eq 0 ]; then
+	#force instant password change
+	/etc/init.d/uhttpd restart 2> /dev/null
+else
+	echo "(E) Es ist ein Fehler aufgetreten."
+fi
+
+%>
diff --git a/src/packages/fff/fff-web/files/www/ssl/cgi-bin/settings b/src/packages/fff/fff-web/files/www/ssl/cgi-bin/settings
new file mode 100755
index 0000000..8d527a9
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/ssl/cgi-bin/settings
@@ -0,0 +1,16 @@
+#!/usr/bin/haserl
+<%
+echo -en "content-type: text/plain\r\n\r\n"
+
+. /lib/functions.sh
+
+case "$GET_func" in
+	get_settings)
+		uci export -qn system
+		uci export -qn simple-tc
+	;;
+	*)
+		echo "(E) settings: Invalid command: '$GET_func'"
+	;;
+esac
+%>
diff --git a/src/packages/fff/fff-web/files/www/ssl/cgi-bin/upgrade b/src/packages/fff/fff-web/files/www/ssl/cgi-bin/upgrade
new file mode 100755
index 0000000..04f5b58
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/ssl/cgi-bin/upgrade
@@ -0,0 +1,36 @@
+#!/usr/bin/haserl --upload-dir=/tmp --upload-limit=12000
+<%
+echo -en "content-type: text/plain\r\n\r\n"
+
+case "${GET_func:-$POST_func}" in
+	apply_firmware)
+		path="$POST_firmware"
+		keep="$POST_keep_config"
+
+		if [ ! -f "$path" ]; then
+			echo "(E) Datei nicht gefunden."
+			exit 1
+		fi
+
+		if [ "$keep" = "yes" ]; then
+			args=""
+		else
+			args="-n"
+		fi
+
+		echo "(I) Starte sysupgrade..."
+
+		#apply openwrt or vendor image
+		sysupgrade $args $path
+	;;
+	restore_firmware)
+		echo "(I) Undo all changes ..."
+		echo y | firstboot
+		echo "(I) Rebooting now ..."
+		reboot
+	;;
+	*)
+		echo "(E) upgrade: Invalid command: '$GET_func'"
+	;;
+esac
+%>
diff --git a/src/packages/fff/fff-web/files/www/ssl/home.html b/src/packages/fff/fff-web/files/www/ssl/home.html
new file mode 100644
index 0000000..3cb0583
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/ssl/home.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<title>Home</title>
+<meta charset="utf-8"/>
+<link rel="stylesheet" type="text/css" href="style.css" />
+<script src="shared.js"></script>
+<script src="home.js"></script>
+</head>
+<body onload="init();">
+
+<fieldset>
+<legend id="system">System</legend>
+<div><label>Name: </label><span id="name">?</span></div>
+<div><label>Modell: </label><span id="model">?</span></div>
+<div><label>MAC-Adresse: </label><span id="mac">?</span></div>
+<div><label>Bekannte Knoten: </label><span id="node_count">?</span></div>
+<div><label>Nachbarknoten: </label><span id="neigh_count">?</span></div>
+<div><label>VPN aktiv: </label><span id="has_vpn">?</span></div>
+<div><label>Laufzeit: </label><span id="uptime">?</span></div>
+<div><label>Auslastung: </label><span id="load">?</span></div>
+<div><label>System: </label><span id="uname">?</span></div>
+<div><label>Uhrzeit: </label><span id="date">?</span></div>
+</fieldset>
+
+<fieldset>
+<legend id="freifunk">Netz: Freifunk</legend>
+<div><label>Nutzer: </label><span id="freifunk_user_count">?</span></div>
+<div><label>Empfangen: </label><span id="freifunk_rx_bytes">?</span> </div>
+<div><label>Gesendet: </label><span id="freifunk_tx_bytes">?</span></div>
+<div><label>IPv4 Adressen: </label><span id="freifunk_addr4">?</span></div>
+<div><label>IPv6 Adressen: </label><span id="freifunk_addr6">?</span></div>
+</fieldset>
+
+<fieldset>
+<legend id="wan">Netz: WAN</legend>
+<div><label>Internet Vorhanden: </label><span id="has_internet">?</span></div>
+<div><label>Empfangen: </label><span id="wan_rx_bytes">?</span> </div>
+<div><label>Gesendet: </label><span id="wan_tx_bytes">?</span></div>
+<div><label>IPv4 Adressen: </label><span id="wan_addr4">?</span></div>
+<div><label>IPv6 Adressen: </label><span id="wan_addr6">?</span></div>
+</fieldset>
+
+<fieldset>
+<legend id="software">Software</legend>
+<div><label>Firmware Version: </label><span id="firmware_version">?</span></div>
+<div><label>OpenWrt Version: </label><span id="openwrt_version">?</span></div>
+<div><label>Batman-Adv Version: </label><span id="batman_version">?</span></div>
+<div><label>Fastd Version: </label><span id="fastd_version">?</span></div>
+</fieldset>
+
+</body>
+</html>
diff --git a/src/packages/fff/fff-web/files/www/ssl/home.js b/src/packages/fff/fff-web/files/www/ssl/home.js
new file mode 100644
index 0000000..da12f32
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/ssl/home.js
@@ -0,0 +1,46 @@
+
+function formatSize(bytes) {
+	if(typeof bytes === "undefined" || bytes == "") {
+		return "-";
+	} else if (bytes < 1000) {
+		return bytes + "  B";
+	} else if (bytes < 1000*1000) {
+		return (bytes/ 1000.0).toFixed(0)  + " KB";
+	} else if (bytes < 1000*1000*1000) {
+		return (bytes/1000.0/1000.0).toFixed(1)  + " MB";
+	} else {
+		return (bytes/1000.0/1000.0/1000.0).toFixed(2) + " GB";
+	}
+}
+
+function init() {
+	send("/cgi-bin/home", { }, function(data) {
+		var obj = fromUCI(data).misc.data;
+		for(var key in obj) {
+			var value = obj[key];
+
+			if(key == 'stype') {
+				continue;
+			}
+
+			//for traffic
+			if(/_bytes$/.test(key)) {
+				value = formatSize(value);
+			}
+
+			//for addresses
+			if(typeof(value) == 'object') {
+				value = "<ul><li>"+value.join("</li><li>")+"</li></ul>"
+			}
+
+			setText(key, value);
+		}
+	});
+
+	addHelpText($("system"), "Eine \xdcbersicht \xfcber den Router.");
+	addHelpText($("freifunk"), "Das \xf6ffentliche Freifunknetz..");
+	addHelpText($("wan"), "Das Netz \xfcber dass das Internet erreicht wird.");
+	addHelpText($("software"), "Einige installierte Softwareversionen.");
+	addHelpText($("freifunk_user_count"), "Die Anzahl der Nutzer an diesem Router in den letzten zwei Stunden.");
+	addHelpText($("has_vpn"), "Status der VPN-Verbindung zum Server im Internet.");
+}
diff --git a/src/packages/fff/fff-web/files/www/ssl/index.html b/src/packages/fff/fff-web/files/www/ssl/index.html
new file mode 100644
index 0000000..cc6cff7
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/ssl/index.html
@@ -0,0 +1,124 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<title>Freifunk</title>
+<meta charset="utf-8"/>
+<link rel="stylesheet" type="text/css" href="style.css" />
+<script src="shared.js"></script>
+
+<script type="text/javascript">
+var html_cache = {};
+var js_cache = {};
+var adv_mode = false;
+
+function adv_apply()
+{
+	var inputs = document.getElementsByClassName('adv_disable');
+	var elems = document.getElementsByClassName('adv_hide');
+
+	for(var i=0;  i < inputs.length; i++)
+		inputs[i].disabled = adv_mode ? "": "disabled";
+	for(var i=0;  i < elems.length; i++)
+		elems[i].style.display = adv_mode ? "block" : "none";
+}
+
+function adv_toggle(e)
+{
+	adv_mode = !adv_mode;
+	e.innerHTML = adv_mode ? "Erweitert: An" : "Erweitert: Aus";
+	adv_apply();
+}
+
+function nav_onclick() 
+{
+	setText('msg', "");
+	var url = this.getAttribute("href");
+	if(url == '#') return false;
+
+	var id = url.substring(0, url.lastIndexOf('.'));
+
+	var process_html = function(data) {
+		var b = $("body");
+		removeChilds(b);
+		var pattern = /<body[^>]*>((.|[\n\r])*)<\/body>/im;
+		b.innerHTML = pattern.exec(data)[1];
+		html_cache[id] = data;
+	};
+
+	var process_js = function(data) {
+		(window.execScript || function(data) {
+			window["eval"].call(window, data);
+			window["eval"].call(window, "init();");
+		})(data);
+		js_cache[id] = data;
+	};
+
+	//load html file
+	if(id in html_cache)
+		process_html(html_cache[id]);
+	else
+		jx.load(url, process_html, 'text');
+
+	//load javascript file
+	if(id in js_cache)
+		process_js(js_cache[id]);
+	else
+		jx.load(url.replace(".html", ".js"), process_js, 'text');
+
+	onDesc($("globalnav"), 'UL', function(n) { hide(n); });
+	onParents(this, 'UL', function(n) { show(n); });
+	onChilds(this.parentNode, 'UL', function(n) { show(n); });
+
+	onDesc($("globalnav"), 'A', function(n) { removeClass(n, "here"); });
+	onParents(this, 'LI', function(n) { addClass(n.firstChild, "here"); });
+
+	return false;
+}
+
+function preselect() {
+	onDesc($("globalnav"), 'UL', function(n) { hide(n); });
+	onDesc($("globalnav"), 'A', function(n) {
+		if(n.getAttribute("href") != '#')
+			n.onclick = nav_onclick;
+	});
+	$("first").onclick();
+}
+
+function reboot() {
+	if(!confirm("Neustart durchf\xFChren?")) return;
+	send("/cgi-bin/misc", { func : "reboot" }, function(data) {
+		setText('msg', data);
+	});
+}
+
+function logout() {
+	window.location="https://none@" + window.location.host;
+}
+</script>
+
+</head>
+<body onload="preselect();">
+
+<ul id="globalnav">
+	<li><a href="home.html" id="first">Home</a></li>
+	<li><a href="settings.html">Einstellungen</a></li>
+	<li><a href="wifiscan.html">WifiScan</a></li>
+	<li><a href="upgrade.html">Upgrade</a></li>
+	<li><a href="password.html">Password</a></li>
+	<li><a href="#" onclick="reboot()">Neustart</a></li>
+	<li><a href="#" onclick="logout()">Logout</a></li>
+	<li><a href="#" onclick="adv_toggle(this)">Erweitert: Aus</a></li>
+</ul>
+<br>
+<pre id="msg" tabindex="-1"></pre>
+
+<div id="help"></div>
+
+<div id="body"></div>
+
+<div id="footer"></div>
+
+</body>
+</html>
+
diff --git a/src/packages/fff/fff-web/files/www/ssl/password.html b/src/packages/fff/fff-web/files/www/ssl/password.html
new file mode 100644
index 0000000..08f10b4
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/ssl/password.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<title>Password</title>
+<meta charset="utf-8"/>
+<link rel="stylesheet" type="text/css" href="style.css" />
+<script src="shared.js"></script>
+<script src="password.js"></script>
+</head>
+<body onload="init();">
+
+<fieldset>
+	<legend>Passwort</legend>
+	<div>
+		<label>Neues Password:</label> <input id="p1" type="password">
+	</div>
+	<div>
+		<label>Bestätigung:</label> <input id="p2" type="password">
+	</div>
+	<div><br />Das Passwort ist für den Zugriff auf die Weboberfläche des Routers und auch den Zugriff per SSH. Der Benutzername ist 'root'.</div>
+</fieldset>
+<div>
+	<button type="button" onclick="apply()">Ändern</button>
+</div>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/src/packages/fff/fff-web/files/www/ssl/password.js b/src/packages/fff/fff-web/files/www/ssl/password.js
new file mode 100644
index 0000000..96ffd23
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/ssl/password.js
@@ -0,0 +1,24 @@
+
+function init() {
+	$("p1").focus();
+}
+
+function apply()
+{
+	p1 = $('p1').value;
+	p2 = $('p2').value;
+
+	$('p1').value = "";
+	$('p2').value = "";
+
+	if(p1 != p2) {
+		setText('msg', "(E) Die Passwörter sind nicht identisch.");
+		return;
+	} else {
+		setText('msg', "(I) Das Passwort wird geändert. Bitte die Seite neu laden.");
+	}
+
+	send("/cgi-bin/password", { func : "set_password", pass1 : p1, pass2 : p2 }, function(data) {
+		setText('msg', data);
+	});
+}
diff --git a/src/packages/fff/fff-web/files/www/ssl/settings.html b/src/packages/fff/fff-web/files/www/ssl/settings.html
new file mode 100644
index 0000000..eb081a2
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/ssl/settings.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<title>Einstellungen</title>
+<meta charset="utf-8"/>
+<link rel="stylesheet" type="text/css" href="style.css" />
+<script src="shared.js"></script>
+<script src="settings.js"></script>
+</head>
+<body onload="init();">
+
+<fieldset>
+	<legend>Allgemeine Einstellungen</legend>
+	<span id="general"></span>
+</fieldset>
+<fieldset>
+	<legend>Bandbreitenkontrolle</legend>
+	<span id="traffic"></span>
+	<div><br />Die für das Freifunknetz beanspruchte Internet-Bandbreite am WAN kann hier begrenzt werden.</div>
+</fieldset>
+
+<button type="button" onclick="save_data()">Speichern</button>
+
+</body>
+</html>
diff --git a/src/packages/fff/fff-web/files/www/ssl/settings.js b/src/packages/fff/fff-web/files/www/ssl/settings.js
new file mode 100644
index 0000000..66d9d0d
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/ssl/settings.js
@@ -0,0 +1,142 @@
+
+/*
+All required uci packages are stored variable uci.
+The GUI code displayes and manipulated this variable.
+*/
+var uci = {};
+var gid = 0;
+
+
+function init()
+{
+	send("/cgi-bin/settings", { func : "get_settings" }, function(data) {
+		uci = fromUCI(data);
+		rebuild_general();
+		adv_apply();
+	});
+}
+
+function updateFrom(src)
+{
+	var obj = {};
+	collect_inputs(src, obj);
+	for(var name in obj)
+	{
+		var value = obj[name];
+		var path = name.split('#');
+
+		var pkg = path[0];
+		var sec = path[1];
+		var opt = path[2];
+
+		uci[pkg].pchanged = true;
+		uci[pkg][sec][opt] = value;
+	}
+}
+
+function getChangeModeAction(ifname)
+{
+	return function(e) {
+		var src = (e.target || e.srcElement);
+		var mode = (src.data || src.value);
+		delNetSection(ifname);
+		addNetSection(ifname, mode);
+	};
+}
+
+function appendSetting(p, path, value, mode)
+{
+	var id = path.join('#');
+	var b;
+	var cfg = path[0];
+	var name = path[path.length-1];
+	switch(name)
+	{
+	case "geo":
+		b = append_input(p, "GPS-Koordinaten", id, value);
+		b.lastChild.placeholder = "52.02713078 8.52829987";
+		addInputCheck(b.lastChild, /^$|^\d{1,3}\.\d{1,8} {1,3}\d{1,3}\.\d{1,8}$/, "Ung\xfcltige Eingabe. Bitte nur maximal 8 Nachkommastellen und keine Kommas verwenden.");
+		addHelpText(b, "Die Koordinaten dieses Knotens auf der Freifunk-Karte (z.B. \"52.02713078 8.52829987\").");
+		break;
+	case "hostname":
+		b = append_input(p, "Knotenname", id, value);
+		b.lastChild.placeholder = "MeinRouter";
+		addInputCheck(b.lastChild, /^$|^[\-\^'\w\.\:\[\]\(\)\/ &@\+\u0080-\u00FF]{0,32}$/, "Ung\xfcltige Eingabe.");
+		addHelpText(b, "Der Name dieses Knotens auf der Freifunk-Karte.");
+		break;
+	case "contact":
+		b = append_input(p, "Kontaktdaten", id, value);
+		b.lastChild.placeholder = "info at example.com";
+		addInputCheck(b.lastChild, /^$|^[\-\^'\w\.\:\[\]\(\)\/ &@\+\u0080-\u00FF]{0,32}$/, "Ung\xfcltige Eingabe.");
+		addHelpText(b, "Kontaktdaten f\xfcr die \xf6ffentliche Freifunk-Karte und Statusseite. Falls ihr euch von anderen Leuten kontaktieren lassen wollt (z.B. \"info at example.com\").");
+		break;
+	case "enabled":
+		if(cfg == "simple-tc") {
+			b = append_radio(p, "Bandbreitenkontrolle", id, value, [["An", "1"], ["Aus", "0"]]);
+			addHelpText(b, "Bandbreitenkontrolle f\xfcr den Upload-/Download \xfcber das Freifunknetz \xfcber den eigenen Internetanschluss.");
+		}
+		break;
+	case "limit_egress":
+		b = append_input(p, "Freifunk Upload", id, value);
+		addInputCheck(b.lastChild, /^\d+$/, "Upload ist ung\xfcltig.");
+		addHelpText(b, "Maximaler Upload in KBit/s f\xfcr die Bandbreitenkontrolle.");
+		break;
+	case "limit_ingress":
+		b = append_input(p, "Freifunk Download", id, value);
+		addInputCheck(b.lastChild, /^\d+$/, "Download ist ung\xfcltig.");
+		addHelpText(b, "Maximaler Download in KBit/s f\xfcr die Bandbreitenkontrolle.");
+		break;
+	default:
+		return;
+	}
+
+	b.id = id; //needed for updateFrom
+	b.onchange = function() {
+		updateFrom(b);
+	};
+
+	return b;
+}
+
+function rebuild_general()
+{
+	var gfs = $("general");
+	var tfs = $("traffic");
+
+	removeChilds(gfs);
+	removeChilds(tfs);
+
+	if('system' in uci) {
+		var f = uci['system'];
+		var i = firstSectionID(f, "system");
+		appendSetting(gfs, ['system', i, "hostname"], f[i]["hostname"]);
+		appendSetting(gfs, ['system', i, "geo"], f[i]["geo"]);
+		appendSetting(gfs, ['system', i, "contact"], f[i]["contact"]);
+	}
+
+	if('simple-tc' in uci) {
+		var t = uci['simple-tc'];
+		var i = firstSectionID(t, "interface");
+		appendSetting(tfs, ['simple-tc', i, "enabled"], t[i]["enabled"]);
+		appendSetting(tfs, ['simple-tc', i, "limit_ingress"], t[i]["limit_ingress"]);
+		appendSetting(tfs, ['simple-tc', i, "limit_egress"], t[i]["limit_egress"]);
+	}
+}
+
+function save_data()
+{
+	for(var name in uci)
+	{
+		var obj = uci[name];
+		if(!obj.pchanged)
+			continue;
+		var data = toUCI(obj);
+		send("/cgi-bin/misc", { func : "set_config_file", name : name, data : data },
+			function(data) {
+				$('msg').innerHTML = data;
+				$('msg').focus();
+				init();
+			}
+		);
+	}
+}
diff --git a/src/packages/fff/fff-web/files/www/ssl/shared.js b/src/packages/fff/fff-web/files/www/ssl/shared.js
new file mode 100644
index 0000000..1c4c0e0
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/ssl/shared.js
@@ -0,0 +1,432 @@
+
+function $(id) { return document.getElementById(id); }
+function create(name) { return document.createElement(name); }
+function show(e) { e.style.display='block'; }
+function hide(e) { e.style.display='none'; }
+function addClass(e, c) { e.classList.add(c); } //HTML5!
+function removeClass(e, c) { e.classList.remove(c); }
+function setText(id, txt) { $(id).innerHTML = txt; }
+function inArray(item, array) { return array.indexOf(item) != -1; }
+
+function split(str)
+{
+	if(typeof str != 'string')
+		return [];
+	var a = str.match(/[^\s]+/g);
+	return (a ? a : []);
+}
+
+function uniq(arr)
+{
+  var obj = {};
+  for(var i in arr) obj[arr[i]] = 0;
+  return Object.keys(obj);
+}
+
+//remove an item from a string list
+function removeItem(str, item)
+{
+	var array = split(str);
+	for(var i in array)
+		if(array[i] == item)
+			array.splice(i, 1);
+	return array.join(' ');
+}
+
+function addItem(str, item)
+{
+	var array = split(str);
+	for(var i in array)
+		if(array[i] == item)
+			return str;
+	array.push(item);
+	return array.join(' ');
+}
+
+function replaceItem(str, old_item, new_item)
+{
+	var array = split(str);
+	for(var i in array)
+		if(array[i] == old_item)
+			array[i] = new_item;
+	return array.join(' ');
+}
+
+function addHelpText(elem, text) {
+	var help = $("help");
+
+	if(help) {
+		elem.onmouseover = function(e) {
+			help.style.top = (e.clientY-20)+"px";
+			help.style.left = (e.clientX+80)+"px";
+			help.innerHTML = text;
+			show(help);
+		};
+
+		elem.onmouseout = function() {
+			help.innerHTML = "";
+			hide(help);
+		};
+	}
+}
+
+//to config file syntax
+function toUCI(pkg_obj)
+{
+	var str = "\n";
+	for(var sid in pkg_obj)
+	{
+		if(sid == "pchanged")
+			continue;
+
+		var options = pkg_obj[sid];
+		var sname = (sid.substring(0, 3) != "cfg") ? (" '"+sid+"'") : "";
+		str += "config "+options.stype+sname+"\n";
+		for(var oname in options)
+		{
+			if(oname == "stype")
+				continue;
+			var value = options[oname];
+			if(typeof value == 'object')
+			{
+				for(var i in value)
+					str += "	list "+oname+" '"+value[i]+"'\n";
+			}
+			else
+				str += "	option "+oname+" '"+value+"'\n";
+		}
+		str += "\n";
+	}
+	return str;
+}
+
+// parses output from one or multiple
+// calls like "uci export -qn foo"
+function fromUCI(pkgs_str)
+{
+	var pkg_objs = {};
+	var pkg;
+	var cfg;
+
+	var lines = pkgs_str.split("\n");
+	for(var i = 0; i < lines.length; ++i)
+	{
+		var line = lines[i];
+		var items = split(line);
+		if(items.length < 2) continue;
+		switch(items[0])
+		{
+			case 'package':
+				pkg = { pchanged : false };
+				pkg_objs[items[1]] = pkg;
+				break;
+			case 'config':
+				var val = (items.length == 3) ? line.match(/'(.*)'/)[1] : ("cfg"+(++gid));
+				cfg = { stype : items[1] };
+				pkg[val] = cfg;
+				break;
+			case 'option':
+				var val = line.match(/'(.*)'/)[1];
+				cfg[items[1]] = val;
+				break;
+			case 'list':
+				var val = line.match(/'(.*)'/)[1];
+				if(!(items[1] in cfg)) cfg[items[1]] = [];
+				cfg[items[1]].push(val);
+				break;
+		}
+	}
+	return pkg_objs;
+}
+
+function firstSectionID(obj, stype)
+{
+	for(var id in obj)
+		if(obj[id].stype == stype)
+			return id;
+}
+
+function config_foreach(objs, stype, func)
+{
+	for(var key in objs)
+	{
+		var obj = objs[key];
+		if((obj["stype"] == stype || stype == "*") && func(key, obj))
+			return;
+	}
+}
+
+function config_find(objs, mobj)
+{
+	for(var key in objs)
+	{
+		var obj = objs[key];
+		var found = true;
+		for(mkey in mobj)
+		{
+			if(obj[mkey] != mobj[mkey])
+			{
+				found = false;
+				break;
+			}
+		}
+		if(found)
+			return obj;
+	}
+	return null;
+}
+
+function params(obj)
+{
+	var str = "";
+	for(var key in obj) {
+		if(str.length) str += "&";
+		else str += "?";
+		str += encodeURIComponent(key) + "=" + encodeURIComponent(obj[key]);
+	}
+	return str.replace(/%20/g, "+");
+}
+
+function send(url, obj, func)
+{
+	url += params(obj);
+	jx.load(url, func, 'text');
+}
+
+function onDesc(e, tag, func)
+{
+	for(var i = 0; i < e.childNodes.length; ++i) {
+		var c = e.childNodes[i];
+		if(c.tagName == tag && func(c) == false) return;
+		onDesc(c, tag, func);
+	}
+}
+
+function onChilds(e, tag, func)
+{
+	for(var i = 0; i < e.childNodes.length; ++i) {
+		var c = e.childNodes[i];
+		if(c.tagName == tag && func(c) == false) return;
+	}
+}
+
+function onParents(e, tag, func)
+{
+	while(e != document) {
+		e = e.parentNode;
+		if(e.tagName == tag && func(e) == false) return;
+	}
+}
+
+function removeChilds(p)
+{
+	while(p.hasChildNodes())
+		p.removeChild(p.firstChild);
+}
+
+function show_error(data)
+{
+	var is_error = (data.substr(0, 3) == "(E)");
+	if(is_error)
+		setText('msg', data);
+	return is_error;
+}
+
+function checkName(name)
+{
+	if(/[\w_]{2,12}/.test(name))
+		return true;
+	alert("Name '"+name+"' ist ung\xfcltig.");
+	return false;
+}
+
+//prepend input check
+function addInputCheck(input, regex, msg)
+{
+	var prev_value = input.value;
+	var prev_onchange = input.onchange;
+	input.onchange = function(e) {
+		if(regex.test(input.value)) {
+			if(prev_onchange)
+				prev_onchange(e);
+			return;
+		}
+		alert(msg);
+		input.value = prev_value;
+		e.stopPropagation();
+	};
+}
+
+function collect_inputs(p, obj)
+{
+	if(p.tagName == "SELECT")
+		obj[p.name] = p.value;
+	if(p.tagName == "INPUT")
+		if(p.type == "text" || p.type == "password" || (p.type == "radio" && p.checked))
+			obj[p.name] = p.value
+		else if(p.type == "checkbox" && p.checked)
+		{
+			var v = obj[p.name];
+			v = (typeof v == "undefined") ? (p.data || p.value) : (v + " " + (p.data || p.value));
+			obj[p.name] = v;
+		}
+
+	for(var i = 0; i < p.childNodes.length; ++i)
+		collect_inputs(p.childNodes[i], obj);
+}
+
+function append(parent, tag, id)
+{
+	var e = create(tag);
+	if(id) e.id = id;
+	parent.appendChild(e);
+	return e;
+}
+
+function append_section(parent, title, id)
+{
+	var fs = append(parent, "fieldset");
+	var lg = create("legend");
+	lg.innerHTML = title;
+	if(id) fs.id = id;
+	fs.appendChild(lg);
+	return fs;
+}
+
+function append_button(parent, text, onclick)
+{
+	var button = append(parent, 'button');
+	button.type = 'button';
+	button.innerHTML = text;
+	button.onclick = onclick;
+	return button;
+}
+
+function append_label(parent, title, value)
+{
+	var div = append(parent, 'div');
+	var label = append(div, 'label');
+	label.innerHTML = title + ":";
+
+	if(typeof value == 'string')
+	{
+		//div.className = "label_option";
+		var span = append(div, 'span');
+		span.innerHTML = value;
+	}
+	else
+	{
+		div.className = "list_option";
+		var span = append(div, 'span');
+		for(var i in value)
+		{
+			var d = append(span, 'div');
+			d.innerHTML = value[i];
+		}
+	}
+	return div;
+}
+
+function append_options(parent, name, selected, choices)
+{
+	var select = append(parent, 'select');
+	select.style.minWidth = "5em";
+	select.name = name;
+	for(var i in choices)
+	{
+		var s = (typeof choices[i] != 'object');
+		var choice_text = " " + (s ? choices[i] : choices[i][0]);
+		var choice_value = "" + (s ? choices[i] : choices[i][1]);
+
+		var option = append(select, 'option');
+		option.value = choice_value;
+		option.selected = (choice_value == selected) ? "selected" : "";
+		option.innerHTML= choice_text;
+	}
+	return select;
+}
+
+function append_selection(parent, title, name, selected, choices)
+{
+	var p = append(parent, 'div');
+	var label = append(p, 'label');
+
+	p.className = "select_option";
+	label.innerHTML = title + ":";
+
+	append_options(p, name, selected, choices);
+	return p;
+}
+
+//append an input field
+//e.g. append_input(parent, "Name", "name_string", "MyName")
+function append_input(parent, title, name, value)
+{
+	var div = append(parent, 'div');
+	var label = create('label');
+	var input = create('input');
+
+	label.innerHTML = title + ":";
+	input.value = (typeof value == "undefined") ? "" : value;
+	input.name = name;
+	input.type = "text";
+
+	div.appendChild(label);
+	div.appendChild(input);
+
+	return div;
+}
+
+//append a radio field
+//e.g. append_radio(parent, "Enabled", "enabled", 0, [["Yes", 1], ["No", 0])
+function append_radio(parent, title, name, selected, choices) {
+	return _selection("radio", parent, title, name, [selected], choices);
+}
+
+//append a checkbox field
+//e.g. append_check(parent, "Enabled", "enabled", ["grass"], [["Grass", "grass"], ["Butter", "butter"]])
+function append_check(parent, title, name, selected, choices) {
+	return _selection("checkbox", parent, title, name, selected, choices);
+}
+
+function _selection(type, parent, title, name, selected, choices)
+{
+	var p = append(parent, 'div');
+	var label = append(p, 'label');
+	var span = append(p, 'span');
+
+	p.className = "radio_option";
+	label.innerHTML = title + ":";
+
+	for (var i in choices)
+	{
+		var s = (typeof choices[i] == 'string');
+		var choice_text = "" + (s ? choices[i] : choices[i][0]);
+		var choice_value = "" + (s ? choices[i] : choices[i][1]);
+		var choice_help = s ? undefined : choices[i][2];
+
+		var div = append(span, 'div');
+		var input = append(div, 'input');
+		var label = append(div, 'label');
+
+		input.name = name;
+		input.value = choice_value;
+		input.data = choice_value; //for IE :-(
+		input.type = type;
+		if(inArray(choice_value, selected))
+			input.checked = "checked"
+
+		label.innerHTML = " " + choice_text;
+
+		if(choice_text == "_")
+			hide(div);
+
+		if(choice_help) {
+			addHelpText(label, choice_help);
+		}
+	}
+	return p;
+}
+
+//from jx_compressed.js
+jx={getHTTPObject:function(){var A=false;if(typeof ActiveXObject!="undefined"){try{A=new ActiveXObject("Msxml2.XMLHTTP")}catch(C){try{A=new ActiveXObject("Microsoft.XMLHTTP")}catch(B){A=false}}}else{if(window.XMLHttpRequest){try{A=new XMLHttpRequest()}catch(C){A=false}}}return A},load:function(url,callback,format){var http=this.init();if(!http||!url){return }if(http.overrideMimeType){http.overrideMimeType("text/xml")}if(!format){var format="text"}format=format.toLowerCase();var now="uid="+new Date().getTime();url+=(url.indexOf("?")+1)?"&":"?";url+=now;http.open("GET",url,true);http.onreadystatechange=function(){if(http.readyState==4){if(http.status==200){var result="";if(http.responseText){result=http.responseText}if(format.charAt(0)=="j"){result=result.replace(/[\n\r]/g,"");result=eval("("+result+")")}if(callback){callback(result)}}else{if(error){error(http.status)}}}};http.send(null)},init:function(){return this.getHTTPObject()}}
diff --git a/src/packages/fff/fff-web/files/www/ssl/style.css b/src/packages/fff/fff-web/files/www/ssl/style.css
new file mode 100644
index 0000000..61fc3f0
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/ssl/style.css
@@ -0,0 +1,226 @@
+
+/* common */
+
+* { margin:0; padding:0; }
+
+a { text-decoration: none; }
+
+li { list-style-type: none; }
+
+html {
+	font: 90%/1.3 arial,sans-serif;
+	padding:1em;
+	background:#fafafa;
+}
+
+body {
+	font: normal 16px verdana,arial,'Bitstream Vera Sans',helvetica,sans-serif;
+}
+
+
+/* specific */
+
+.mac {
+	color: #0033CC;
+	cursor: help;
+}
+
+#nds_files label {
+	width: 18em;
+}
+
+#nds_macs {
+	margin-bottom: 2em;
+}
+
+#wifiscan table {
+	text-align: center;
+}
+
+#wifiscan td:nth-of-type(1) {
+	text-align: left;
+}
+
+#help {
+	padding: 5px;
+	height: 0px;
+	position: absolute;
+	min-height: 50px;
+	background-color: #f2f2f2;
+	display:none;
+	border: 2px dotted grey;
+}
+
+#switches label {
+	width: 6em;
+}
+
+/* forms */
+
+legend {
+	color: #0b77b7;
+	font-size: 1.2em;
+}
+
+label {
+	float: left;
+	width: 12em;
+	text-align: right;
+	margin-right: 1em;
+	white-space: nowrap;
+}
+
+fieldset {
+	border: 1px solid #ddd;
+	padding: 0.5em;
+	margin: 0.5em;
+	width: 36em;
+}
+
+fieldset fieldset {
+	width: auto;
+}
+
+fieldset > * {
+	margin: 0.3em 0;
+	clear: both;
+}
+
+fieldset div > * {
+	display: inline-block;
+	vertical-align: middle;
+}
+
+input {
+	padding: 0.15em;
+	width: 15em;
+	border: 1px solid #ddd;
+	background: #fafafa;
+	font: bold 0.95em arial, sans-serif;
+	-moz-border-radius: 0.4em;
+	-khtml-border-radius: 0.4em;
+}
+
+input:hover, input:focus {
+	border-color: #c5c5c5;
+	background: #f6f6f6;
+}
+
+select {
+	min-width: 4em;
+}
+
+option {
+	padding-right: 1em;
+}
+
+.radio_option div {
+	float: left;
+	white-space: nowrap;
+	clear: none;
+}
+
+.radio_option div label, .radio_option div input {
+	vertical-align: middle;
+	display: inline;
+	float: none;
+	width: auto;
+	background: none;
+	border: none;
+}
+
+.select_option label {
+	font-size: 1em;
+	color: #000;
+}
+
+.list_option div {
+	clear: left;
+	margin: 0;
+	padding: 0;
+	float: left;
+	list-style: none;
+}
+
+
+/* navigation */
+
+#globalnav {
+	position: relative;
+	float: auto;
+	width: 98%;
+	padding: 0 0 1.75em 1em;
+	margin: 0;
+	margin-bottom: 2%;
+	list-style: none;
+	line-height: 1em;
+}
+
+#globalnav li {
+	float: left;
+	margin: 0;
+	padding: 0;
+}
+
+#globalnav a {
+	display: block;
+	color: #444;
+	text-decoration: none;
+	font-weight: bold;
+	background: #ddd;
+	margin: 0;
+	padding: 0.25em 1em;
+	border-left: 1px solid #fff;
+	border-top: 1px solid #fff;
+	border-right: 1px solid #aaa;
+}
+
+#globalnav a:hover,
+#globalnav a:active,
+#globalnav a.here:link,
+#globalnav a.here:visited {
+	background: #bbb;
+}
+
+#globalnav a.here:link,
+#globalnav a.here:visited {
+	position: relative;
+	z-index: 102;
+}
+
+/* sub-navigation */
+
+#globalnav ul {
+	position: absolute;
+	left: 0;
+	top: 1.5em;
+	float: left;
+	background: #bbb;
+	width: 100%;
+	margin: 0;
+	padding: 0.25em 0.25em 0.25em 1em;
+	list-style: none;
+	border-top: 1px solid #fff;
+}
+
+#globalnav ul li {
+	float: left;
+	display: block;
+	margin-top: 1px;
+}
+
+#globalnav ul a {
+	background: #bbb;
+	color: #fff;
+	display: inline;
+	margin: 0;
+	padding: 0 1em;
+	border: 0;
+}
+
+#globalnav ul a:hover,
+#globalnav ul a:active,
+#globalnav ul a.here:link,
+#globalnav ul a.here:visited {
+	color: #444;
+}
diff --git a/src/packages/fff/fff-web/files/www/ssl/upgrade.html b/src/packages/fff/fff-web/files/www/ssl/upgrade.html
new file mode 100644
index 0000000..41d4543
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/ssl/upgrade.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<title>Upgrade</title>
+<meta charset="utf-8"/>
+<link rel="stylesheet" type="text/css" href="style.css" />
+<script src="shared.js"></script>
+<script src="upgrade.js"></script>
+</head>
+<body onload="init();">
+
+<fieldset>
+	<legend>Router Zurücksetzen</legend>
+	<div>
+		<button type="button" onclick="restore_firmware()">Zurücksetzen</button>
+	</div>
+	<div><br />Alle Einstellungen werden zurückgesetzt und der Router startet neu.</div>
+</fieldset>
+<fieldset>
+	<legend>Manuelles Update</legend>
+	<div id="selected_image"></div>
+	<form action="/cgi-bin/upgrade" enctype="multipart/form-data" method="post">
+	<div class="radio_option">
+		<label>
+			<input type="file" id="import_file" name="firmware" onchange="$('selected_image').innerHTML=this.value.replace(/^.*[\\\/](.*)$/, '$1');" style="visibility:hidden;position:absolute;top:-50;left:-50"/>
+			<input type="hidden" name="func" value="apply_firmware" />
+			<button type="button" onclick="$('import_file').click()">Image Auswählen</button>
+			<button type="submit">Senden</button>
+		</label>
+		<div>
+			<input type="checkbox" name="keep_config" value="yes" />
+			<label>Konfiguration erhalten</label>
+		</div>
+	</div>
+	</form>
+	<div><br />Hier kann ein Freifunk-Image verwendet werden (*-sysupgrade.bin) oder die Firmware des Routerherstellers.</div>
+</fieldset>
+
+
+</body>
+</html>
diff --git a/src/packages/fff/fff-web/files/www/ssl/upgrade.js b/src/packages/fff/fff-web/files/www/ssl/upgrade.js
new file mode 100644
index 0000000..b3d6e48
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/ssl/upgrade.js
@@ -0,0 +1,12 @@
+
+function init() {
+	/* Nothing to do */
+}
+
+function restore_firmware() {
+	if(!confirm("Sollen alle Einstellungen zur\xFCckgesetzt werden?")) return;
+	send("/cgi-bin/upgrade", { func : 'restore_firmware' }, function(text) {
+		setText('msg', text);
+	});
+}
+
diff --git a/src/packages/fff/fff-web/files/www/ssl/wifiscan.html b/src/packages/fff/fff-web/files/www/ssl/wifiscan.html
new file mode 100644
index 0000000..9e58c85
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/ssl/wifiscan.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+
+<html>
+<head>
+<title>Wifiscan</title>
+<meta charset="utf-8"/>
+<link rel="stylesheet" type="text/css" href="style.css" />
+<script src="shared.js"></script>
+<script src="wifiscan.js"></script>
+</head>
+<body onload="init();">
+
+<fieldset id="wifiscan">
+	<legend>Wifi Scan</legend>
+	<div>
+		<select id="wifiscan_selection"></select>
+		<button type="button" onclick="wifi_scan()">Scan starten</button>
+	</div>
+	<table id="wifiscan_table" style="display: none">
+		<tr><th>Name</th><th>Kanal</th><th>Signal</th><th>Typ</th></tr>
+		<tbody id="wifiscan_tbody"></tbody>
+	</table>
+</fieldset>
+
+</body>
+</html>
diff --git a/src/packages/fff/fff-web/files/www/ssl/wifiscan.js b/src/packages/fff/fff-web/files/www/ssl/wifiscan.js
new file mode 100644
index 0000000..3be2591
--- /dev/null
+++ b/src/packages/fff/fff-web/files/www/ssl/wifiscan.js
@@ -0,0 +1,77 @@
+
+function fetch(regex, data)
+{
+	var result = data.match(regex);
+	return result ? result[1] : "";
+}
+
+function append_td(tr, value) {
+	append(tr, 'td').innerHTML = value ? value : "?";
+}
+
+function wifi_scan()
+{
+	var s = $('wifiscan_selection');
+	var device = s.options[s.selectedIndex].value;
+
+	send("/cgi-bin/misc", {func:'wifiscan', device:device}, function(data) {
+		var tbody = $("wifiscan_tbody");
+		removeChilds(tbody);
+
+		var items = data.split(/BSS /).filter(Boolean);
+		for(var i = 0; i < items.length; ++i)
+		{
+			var item = items[i];
+			var ssid = fetch(/SSID: (.*)\n/, item);
+			var channel = fetch(/channel (.*)\n/, item);
+			var signal = fetch(/signal: (.*)\n/, item);
+			var capability = fetch(/capability: (.*)\n/, item);
+			var mesh_id = fetch(/MESH ID: (.*)\n/, item);
+
+			var tr = append(tbody, 'tr');
+			append_td(tr, mesh_id ? mesh_id : ssid);
+			append_td(tr, channel);
+			append_td(tr, signal);
+
+			//determine the wifi mode
+			if(mesh_id) {
+				append_td(tr, "  802.11s");
+			} else  if(/IBSS/.test(capability)) {
+				append_td(tr, "  AdHoc");
+			} else  if(/ESS/.test(capability)) {
+				append_td(tr, "  AccessPoint");
+			} else {
+				append_td(tr, "  ???");
+			}
+		}
+
+		var table = $('wifiscan_table');
+		show(table);
+	});
+}
+
+function add_list_entry(device, ifname) {
+	var list = $('wifiscan_selection');
+	var o = append(list, 'option');
+	o.style.paddingRight = "1em";
+	o.innerHTML = device;
+	o.value = ifname;
+}
+
+/*
+* Create a selection of wireless devices
+* represented as the first interface found.
+*/
+function init() {
+        send("/cgi-bin/misc", {func:'wifi_status'}, function(data) {
+                var data = JSON.parse(data);
+                for(var device in data) {
+                        var interfaces = data[device].interfaces;
+                        if(interfaces.length == 0)
+                                continue;
+                        var ifname = interfaces[0].ifname ;
+                        if(typeof(ifname) == 'string')
+                                add_list_entry(device, ifname);
+                }
+	});
+}
diff --git a/src/packages/fff/fff/Makefile b/src/packages/fff/fff/Makefile
index 899f641..d8ad9da 100644
--- a/src/packages/fff/fff/Makefile
+++ b/src/packages/fff/fff/Makefile
@@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=fff
 PKG_VERSION:=0.0.1
-PKG_RELEASE:=2
+PKG_RELEASE:=3
 
 PKG_BUILD_DIR:=$(BUILD_DIR)/fff
 
@@ -14,7 +14,7 @@ define Package/fff-base
     DEFAULT:=y
     TITLE:= Freifunk-Franken Base
     URL:=http://www.freifunk-franken.de
-    DEPENDS:=+fff-nodewatcher
+    DEPENDS:=+fff-nodewatcher +fff-web
 endef
 
 define Package/fff-base/description
-- 
2.1.4




Mehr Informationen über die Mailingliste franken-dev