]> git.ipfire.org Git - ipfire-2.x.git/commitdiff
Merge branch 'captive-portal' into next
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 4 Oct 2017 15:10:07 +0000 (16:10 +0100)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 4 Oct 2017 15:10:07 +0000 (16:10 +0100)
48 files changed:
config/backup/include
config/cfgroot/general-functions.pl
config/cfgroot/lang.pl
config/cfgroot/network-functions.pl
config/cron/crontab
config/httpd/vhosts.d/captive.conf [new file with mode: 0644]
config/menu/30-network.menu
config/rootfiles/common/apache2
config/rootfiles/common/bootstrap [new file with mode: 0644]
config/rootfiles/common/configroot
config/rootfiles/common/misc-progs
config/rootfiles/common/stage2
config/rootfiles/common/ubuntu-font-family [new file with mode: 0644]
config/rootfiles/common/web-user-interface
config/rootfiles/common/x86_64/stage2
doc/language_issues.de
doc/language_issues.en
doc/language_issues.es
doc/language_issues.fr
doc/language_issues.it
doc/language_issues.nl
doc/language_issues.pl
doc/language_issues.ru
doc/language_issues.tr
doc/language_missings
html/cgi-bin/captive.cgi [new file with mode: 0755]
html/cgi-bin/captive/index.cgi [new file with mode: 0755]
html/cgi-bin/captive/logo.cgi [new file with mode: 0644]
html/cgi-bin/captive/redirect.cgi [new file with mode: 0755]
html/cgi-bin/logs.cgi/log.dat
html/html/captive/assets/captive.css [new file with mode: 0644]
html/html/captive/assets/favicon.ico [new file with mode: 0644]
html/html/captive/assets/ipfire.png [new file with mode: 0644]
html/html/captive/template.html [new file with mode: 0644]
langs/de/cgi-bin/de.pl
langs/en/cgi-bin/en.pl
lfs/apache2
lfs/bootstrap [new file with mode: 0644]
lfs/configroot
lfs/stage2
lfs/ubuntu-font-family [new file with mode: 0644]
lfs/web-user-interface
make.sh
src/initscripts/system/firewall
src/misc-progs/Makefile
src/misc-progs/captivectrl.c [new file with mode: 0644]
src/misc-progs/wirelessctrl.c
src/scripts/captive-cleanup [new file with mode: 0755]

index e1edeff6af10dc12f7927c194d4ea09014d1c321..ba78c69f5a4310a3580dde858dcafcd6e696254c 100644 (file)
@@ -41,3 +41,4 @@
 /root/.bash_history
 /var/ipfire/ethernet/aliases
 /var/ipfire/ethernet/wireless
+/var/ipfire/captive/*
index f448c34befc7cf8af483f674f02665c0203d6a54..0577afe2809e62b3de113f96b3819ab04a43d917 100644 (file)
@@ -235,7 +235,13 @@ sub writehashpart
 sub age {
        my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
                $atime, $mtime, $ctime, $blksize, $blocks) = stat $_[0];
-       my $totalsecs = time() - $mtime;
+       my $t = time() - $mtime;
+
+       return &format_time($t);
+}
+
+sub format_time($) {
+       my $totalsecs = shift;
        my @s = ();
 
        my $secs = $totalsecs % 60;
index c77e0a0665692e0948f265ad2d196ac43d6bdf86..2b09c4a9ff44ab4baf05b3e7439b218c35a28d2b 100644 (file)
@@ -179,4 +179,18 @@ sub FindWebLanguage() {
        return undef;
 }
 
+sub DetectBrowserLanguages() {
+       my $langs = $ENV{"HTTP_ACCEPT_LANGUAGE"};
+       my @results = ();
+
+       foreach my $lang (split /[,;]/, $langs) {
+               # Drop all q= arguments
+               next if ($lang =~ m/^q=/);
+
+               push(@results, $lang);
+       }
+
+       return @results;
+}
+
 1;
index 66f1ed554ab12041c31cc0b3163f9e1c8a75710f..93b8190305abb93a7d29e0c8a117e22d7e46b6e9 100644 (file)
@@ -374,6 +374,26 @@ sub wifi_get_signal_level($) {
 
        return $signal_level;
 }
+
+sub get_hardware_address($) {
+       my $ip_address = shift;
+       my $ret;
+
+       open(FILE, "/proc/net/arp") or die("Could not read ARP table");
+
+       while (<FILE>) {
+               my ($ip_addr, $hwtype, $flags, $hwaddr, $mask, $device) = split(/\s+/, $_);
+               if ($ip_addr eq $ip_address) {
+                       $ret = $hwaddr;
+                       last;
+               }
+       }
+
+       close(FILE);
+
+       return $ret;
+}
+
 1;
 
 # Remove the next line to enable the testsuite
index c6d8a725c73ec49c76a3cb807f31aed121654029..4561f4a243239b8b5bd3525c067dc6a70395489c 100644 (file)
@@ -65,6 +65,12 @@ HOME=/
 # Retry sending spooled mails regularly
 %hourly * /usr/sbin/dma -q
 
+# Cleanup captive clients
+%hourly * /usr/bin/captive-cleanup
+
+# Reload captive firewall rules
+%nightly * 23-1   /usr/local/bin/captivectrl >/dev/null
+
 # Cleanup the mail spool directory
 %weekly * * /usr/sbin/dma-cleanup-spool
 
diff --git a/config/httpd/vhosts.d/captive.conf b/config/httpd/vhosts.d/captive.conf
new file mode 100644 (file)
index 0000000..e4e1d78
--- /dev/null
@@ -0,0 +1,30 @@
+Listen 1013
+
+<VirtualHost *:1013>
+       DocumentRoot /srv/web/ipfire/html/captive
+
+       # Close all connections as soon as a reply has been sent.
+       # Most browsers open loads of connections which then causes
+       # the access page being loaded again after a correct coupon
+       # code was entered.
+       KeepAlive Off
+
+       ScriptAlias /cgi-bin/ /srv/web/ipfire/cgi-bin/captive/
+       Alias /assets/ /srv/web/ipfire/html/captive/assets/
+
+       Alias /favicon.ico /srv/web/ipfire/html/captive/assets/favicon.ico
+
+       # All unknown URIs will be redirected to the first
+       # redirector script.
+       ScriptAliasMatch .* /srv/web/ipfire/cgi-bin/captive/redirect.cgi
+
+       <Directory /srv/web/ipfire/cgi-bin/captive>
+               Options ExecCGI
+               Require all granted
+       </Directory>
+
+       <Directory /srv/web/ipfire/html/captive>
+               Options +FollowSymlinks
+               Require all granted
+       </Directory>
+</VirtualHost>
index 8e1336a3b6957e0c3756bd504188b63c2cf1ec0f..137fd686c3f9ad73f17cc401148c20c343390aad 100644 (file)
                                 'title' => "$Lang::tr{'dhcp server'}",
                                 'enabled' => 1,
                                 };
+       $subnetwork->{'32.captive'} = {'caption' => $Lang::tr{'Captive menu'},
+                               'uri' => '/cgi-bin/captive.cgi',
+                               'title' => $Lang::tr{'Captive menu'},
+                               'enabled' => 1,
+                               };
     $subnetwork->{'40.scheduler'} = {
                                 'caption' => $Lang::tr{'connscheduler'},
                                 'uri' => '/cgi-bin/connscheduler.cgi',
index 233301a71937ba661da045b469db5a42242dd124..040b482141614b12f02cf82df0d4538d8663daa8 100644 (file)
@@ -43,6 +43,7 @@ etc/httpd/conf/server-tuning.conf
 etc/httpd/conf/ssl-global.conf
 etc/httpd/conf/uid.conf
 #etc/httpd/conf/vhosts.d
+etc/httpd/conf/vhosts.d/captive.conf
 etc/httpd/conf/vhosts.d/ipfire-interface-ssl.conf
 etc/httpd/conf/vhosts.d/ipfire-interface.conf
 #etc/httpd/conf/vhosts.d/nagios.conf
@@ -91,6 +92,8 @@ etc/httpd/conf/vhosts.d/ipfire-interface.conf
 #srv/web/ipfire/error/include/top.html
 #srv/web/ipfire/htdocs
 #srv/web/ipfire/htdocs/index.html
+#srv/web/ipfire/html
+srv/web/ipfire/html/captive
 #srv/web/ipfire/icons
 #srv/web/ipfire/icons/README
 #srv/web/ipfire/icons/README.html
@@ -1662,3 +1665,4 @@ usr/sbin/httpd
 #usr/share/man/man8/rotatelogs.8
 #usr/share/man/man8/suexec.8
 var/log/httpd
+var/log/httpd/captive
diff --git a/config/rootfiles/common/bootstrap b/config/rootfiles/common/bootstrap
new file mode 100644 (file)
index 0000000..cc5566a
--- /dev/null
@@ -0,0 +1,17 @@
+#usr/share/bootstrap
+#usr/share/bootstrap/css
+usr/share/bootstrap/css/bootstrap.css
+usr/share/bootstrap/css/bootstrap.css.map
+usr/share/bootstrap/css/bootstrap-grid.css
+usr/share/bootstrap/css/bootstrap-grid.css.map
+usr/share/bootstrap/css/bootstrap-grid.min.css
+usr/share/bootstrap/css/bootstrap-grid.min.css.map
+usr/share/bootstrap/css/bootstrap.min.css
+usr/share/bootstrap/css/bootstrap.min.css.map
+usr/share/bootstrap/css/bootstrap-reboot.css
+usr/share/bootstrap/css/bootstrap-reboot.css.map
+usr/share/bootstrap/css/bootstrap-reboot.min.css
+usr/share/bootstrap/css/bootstrap-reboot.min.css.map
+#usr/share/bootstrap/js
+usr/share/bootstrap/js/bootstrap.js
+usr/share/bootstrap/js/bootstrap.min.js
index 911a780ecc36a3c0b825399972d9a30034d777a5..58424ed2b67a9ccf342be07f7edf78d658550af4 100644 (file)
@@ -11,6 +11,11 @@ var/ipfire/auth
 var/ipfire/backup/exclude.user
 var/ipfire/backup/include.user
 var/ipfire/ca
+var/ipfire/captive
+var/ipfire/captive/agb.txt
+var/ipfire/captive/clients
+var/ipfire/captive/settings
+var/ipfire/captive/voucher_out
 var/ipfire/certs
 #var/ipfire/certs/index.txt
 #var/ipfire/certs/serial
@@ -51,16 +56,14 @@ var/ipfire/extrahd
 #var/ipfire/extrahd/settings
 var/ipfire/firewall
 #var/ipfire/firewall/config
-#var/ipfire/firewall/dmz
 #var/ipfire/firewall/geoipblock
 #var/ipfire/firewall/input
-#var/ipfire/firewall/nat
 #var/ipfire/firewall/outgoing
 #var/ipfire/firewall/p2protocols
 #var/ipfire/firewall/settings
 var/ipfire/fwhosts
-#var/ipfire/fwhosts/customgroups
 #var/ipfire/fwhosts/customgeoipgrp
+#var/ipfire/fwhosts/customgroups
 #var/ipfire/fwhosts/customhosts
 #var/ipfire/fwhosts/customnetworks
 #var/ipfire/fwhosts/customservicegrp
@@ -116,6 +119,7 @@ var/ipfire/menu.d/70-log.menu
 #var/ipfire/menu.d/EX-tor.menu
 #var/ipfire/menu.d/EX-wlanap.menu
 var/ipfire/modem
+var/ipfire/modem-lib.pl
 #var/ipfire/modem/defaults
 #var/ipfire/modem/settings
 var/ipfire/modem-lib.pl
index f94f3195dfc1436a89b51e9f6fa25976254af9b1..789179513b1053547288759bf6e7889cbd133a77 100644 (file)
@@ -1,6 +1,7 @@
 usr/local/bin/addonctrl
 #usr/local/bin/applejuicectrl
 usr/local/bin/backupctrl
+usr/local/bin/captivectrl
 #usr/local/bin/clamavctrl
 usr/local/bin/collectdctrl
 usr/local/bin/ddnsctrl
index ec36774b315d2644e3f7e13c548bcd995a6b7ff5..8c6dd40afdec5dc027f778216b20de99299b8608 100644 (file)
@@ -69,6 +69,7 @@ run
 #sbin
 #srv
 #usr/bin
+usr/bin/captive-cleanup
 #usr/bin/perl
 #usr/include
 #usr/lib
diff --git a/config/rootfiles/common/ubuntu-font-family b/config/rootfiles/common/ubuntu-font-family
new file mode 100644 (file)
index 0000000..b9f7c0b
--- /dev/null
@@ -0,0 +1,13 @@
+usr/share/fonts/Ubuntu-B.ttf
+usr/share/fonts/Ubuntu-BI.ttf
+usr/share/fonts/Ubuntu-C.ttf
+usr/share/fonts/Ubuntu-L.ttf
+usr/share/fonts/Ubuntu-LI.ttf
+usr/share/fonts/Ubuntu-M.ttf
+usr/share/fonts/Ubuntu-MI.ttf
+usr/share/fonts/Ubuntu-R.ttf
+usr/share/fonts/Ubuntu-RI.ttf
+usr/share/fonts/UbuntuMono-B.ttf
+usr/share/fonts/UbuntuMono-BI.ttf
+usr/share/fonts/UbuntuMono-R.ttf
+usr/share/fonts/UbuntuMono-RI.ttf
index f204f165777963f78456613a63c7efea008fddf2..531f8bdad50fe2f8fb38c57ba677409b2e1b74af 100644 (file)
@@ -8,6 +8,11 @@ srv/web/ipfire/cgi-bin/aliases.cgi
 srv/web/ipfire/cgi-bin/atm-status.cgi
 srv/web/ipfire/cgi-bin/backup.cgi
 srv/web/ipfire/cgi-bin/bluetooth.cgi
+#srv/web/ipfire/cgi-bin/captive
+srv/web/ipfire/cgi-bin/captive.cgi
+srv/web/ipfire/cgi-bin/captive/index.cgi
+srv/web/ipfire/cgi-bin/captive/logo.cgi
+srv/web/ipfire/cgi-bin/captive/redirect.cgi
 srv/web/ipfire/cgi-bin/chpasswd.cgi
 srv/web/ipfire/cgi-bin/connections.cgi
 srv/web/ipfire/cgi-bin/connscheduler.cgi
@@ -23,8 +28,8 @@ srv/web/ipfire/cgi-bin/fireinfo.cgi
 srv/web/ipfire/cgi-bin/firewall.cgi
 srv/web/ipfire/cgi-bin/fwhosts.cgi
 srv/web/ipfire/cgi-bin/geoip-block.cgi
-#srv/web/ipfire/cgi-bin/guardian.cgi
 srv/web/ipfire/cgi-bin/gpl.cgi
+#srv/web/ipfire/cgi-bin/guardian.cgi
 srv/web/ipfire/cgi-bin/gui.cgi
 srv/web/ipfire/cgi-bin/hardwaregraphs.cgi
 srv/web/ipfire/cgi-bin/hosts.cgi
@@ -89,6 +94,19 @@ srv/web/ipfire/cgi-bin/wirelessclient.cgi
 srv/web/ipfire/cgi-bin/wlanap.cgi
 #srv/web/ipfire/html
 srv/web/ipfire/html/blob.gif
+#srv/web/ipfire/html/captive
+#srv/web/ipfire/html/captive/assets
+srv/web/ipfire/html/captive/assets/Ubuntu-L.ttf
+srv/web/ipfire/html/captive/assets/Ubuntu-M.ttf
+srv/web/ipfire/html/captive/assets/Ubuntu-R.ttf
+srv/web/ipfire/html/captive/assets/bootstrap-grid.min.css
+srv/web/ipfire/html/captive/assets/bootstrap-grid.min.css.map
+srv/web/ipfire/html/captive/assets/bootstrap-reboot.min.css
+srv/web/ipfire/html/captive/assets/bootstrap-reboot.min.css.map
+srv/web/ipfire/html/captive/assets/captive.css
+srv/web/ipfire/html/captive/assets/favicon.ico
+srv/web/ipfire/html/captive/assets/ipfire.png
+srv/web/ipfire/html/captive/template.html
 srv/web/ipfire/html/clwarn.cgi
 srv/web/ipfire/html/dial.cgi
 srv/web/ipfire/html/favicon.ico
index 95eff36969995dd229b8203a43dcfbf551db12fb..2897adc5e2f55aae4ce2c88edb1fea0a29abc00a 100644 (file)
@@ -70,6 +70,7 @@ run
 #sbin
 #srv
 #usr/bin
+usr/bin/captive-cleanup
 #usr/bin/perl
 #usr/include
 #usr/lib
index b9f7747581f0f00a7a9d15de50191349c9f70c91..f5d6d36751ef0c482ffb4728189f189ba8f21d79 100644 (file)
@@ -1,4 +1,24 @@
 WARNING: translation string unused: Async logging enabled
+WARNING: translation string unused: Captive 1day
+WARNING: translation string unused: Captive 1month
+WARNING: translation string unused: Captive 1week
+WARNING: translation string unused: Captive activate
+WARNING: translation string unused: Captive auth_lic
+WARNING: translation string unused: Captive auth_vou
+WARNING: translation string unused: Captive err doublevoucher
+WARNING: translation string unused: Captive expire
+WARNING: translation string unused: Captive genvoucher
+WARNING: translation string unused: Captive invalid logosize
+WARNING: translation string unused: Captive invalid_voucher
+WARNING: translation string unused: Captive ip
+WARNING: translation string unused: Captive logo_set
+WARNING: translation string unused: Captive logo_upload
+WARNING: translation string unused: Captive logo_upload1
+WARNING: translation string unused: Captive nr
+WARNING: translation string unused: Captive time
+WARNING: translation string unused: Captive voactive
+WARNING: translation string unused: Captive voucher
+WARNING: translation string unused: Captive vout
 WARNING: translation string unused: ConnSched scheduler
 WARNING: translation string unused: ConnSched select profile
 WARNING: translation string unused: HDD temperature
@@ -665,9 +685,11 @@ WARNING: translation string unused: xtaccess all error
 WARNING: translation string unused: xtaccess bad transfert
 WARNING: translation string unused: year-graph
 WARNING: translation string unused: yearly firewallhits
+WARNING: untranslated string: 24 hours
 WARNING: untranslated string: Scan for Songs
 WARNING: untranslated string: addons
 WARNING: untranslated string: bytes
+WARNING: untranslated string: captive
 WARNING: untranslated string: community rules
 WARNING: untranslated string: dead peer detection
 WARNING: untranslated string: emerging rules
@@ -714,11 +736,15 @@ WARNING: untranslated string: ike lifetime should be between 1 and 8 hours
 WARNING: untranslated string: info messages
 WARNING: untranslated string: no data
 WARNING: untranslated string: none
+WARNING: untranslated string: one hour
+WARNING: untranslated string: one month
+WARNING: untranslated string: one week
 WARNING: untranslated string: qos add subclass
 WARNING: untranslated string: route config changed
 WARNING: untranslated string: routing config added
 WARNING: untranslated string: routing config changed
 WARNING: untranslated string: routing table
 WARNING: untranslated string: show tls-auth key
+WARNING: untranslated string: unlimited
 WARNING: untranslated string: vpn force mobike
 WARNING: untranslated string: vpn statistics n2n
index f2f6ab7d3d90154afd2e4e6fe56f02ed0b832eff..1a22e2836fe97d32fdf67e143762451c1f646ee1 100644 (file)
@@ -1,4 +1,25 @@
 WARNING: translation string unused: Async logging enabled
+WARNING: translation string unused: Captive 1day
+WARNING: translation string unused: Captive 1month
+WARNING: translation string unused: Captive 1week
+WARNING: translation string unused: Captive activate
+WARNING: translation string unused: Captive auth_lic
+WARNING: translation string unused: Captive auth_vou
+WARNING: translation string unused: Captive err doublevoucher
+WARNING: translation string unused: Captive expire
+WARNING: translation string unused: Captive heading terms
+WARNING: translation string unused: Captive heading voucher
+WARNING: translation string unused: Captive invalid coupon
+WARNING: translation string unused: Captive invalid logosize
+WARNING: translation string unused: Captive invalid_voucher
+WARNING: translation string unused: Captive ip
+WARNING: translation string unused: Captive logo_set
+WARNING: translation string unused: Captive nr
+WARNING: translation string unused: Captive please enter a coupon code
+WARNING: translation string unused: Captive time
+WARNING: translation string unused: Captive voactive
+WARNING: translation string unused: Captive voucher
+WARNING: translation string unused: Captive vout
 WARNING: translation string unused: ConnSched scheduler
 WARNING: translation string unused: ConnSched select profile
 WARNING: translation string unused: HDD temperature
@@ -701,8 +722,10 @@ WARNING: translation string unused: xtaccess all error
 WARNING: translation string unused: xtaccess bad transfert
 WARNING: translation string unused: year-graph
 WARNING: translation string unused: yearly firewallhits
+WARNING: untranslated string: Captive clients
 WARNING: untranslated string: Scan for Songs
 WARNING: untranslated string: bytes
+WARNING: untranslated string: captive
 WARNING: untranslated string: fwhost cust geoipgrp
 WARNING: untranslated string: fwhost err hostip
 WARNING: untranslated string: guardian block a host
index d34d63f59a2d71925a31239a6f550a75a4153cb9..a7010105b6cc2f817277c45de904e6c2f2823b72 100644 (file)
@@ -618,6 +618,35 @@ WARNING: translation string unused: xtaccess all error
 WARNING: translation string unused: xtaccess bad transfert
 WARNING: translation string unused: year-graph
 WARNING: translation string unused: yearly firewallhits
+WARNING: untranslated string: 24 hours
+WARNING: untranslated string: Captive ACTIVATE
+WARNING: untranslated string: Captive GAIN ACCESS
+WARNING: untranslated string: Captive activated
+WARNING: untranslated string: Captive active on
+WARNING: untranslated string: Captive agree tac
+WARNING: untranslated string: Captive authentication
+WARNING: untranslated string: Captive brand color
+WARNING: untranslated string: Captive branding
+WARNING: untranslated string: Captive client session expiry time
+WARNING: untranslated string: Captive clients
+WARNING: untranslated string: Captive config
+WARNING: untranslated string: Captive coupon
+WARNING: untranslated string: Captive expiry time
+WARNING: untranslated string: Captive generate coupon
+WARNING: untranslated string: Captive issued coupons
+WARNING: untranslated string: Captive logo uploaded
+WARNING: untranslated string: Captive mac
+WARNING: untranslated string: Captive menu
+WARNING: untranslated string: Captive noexpiretime
+WARNING: untranslated string: Captive nolimit
+WARNING: untranslated string: Captive please accept the terms and conditions
+WARNING: untranslated string: Captive terms
+WARNING: untranslated string: Captive terms short
+WARNING: untranslated string: Captive title
+WARNING: untranslated string: Captive upload logo
+WARNING: untranslated string: Captive upload logo recommendations
+WARNING: untranslated string: Captive vouchervalid
+WARNING: untranslated string: Captive wrong ext
 WARNING: untranslated string: ConnSched dial
 WARNING: untranslated string: ConnSched hangup
 WARNING: untranslated string: ConnSched reboot
@@ -649,6 +678,7 @@ WARNING: untranslated string: bit
 WARNING: untranslated string: block
 WARNING: untranslated string: bytes
 WARNING: untranslated string: capabilities
+WARNING: untranslated string: captive
 WARNING: untranslated string: ccd add
 WARNING: untranslated string: ccd choose net
 WARNING: untranslated string: ccd client options
@@ -729,6 +759,7 @@ WARNING: untranslated string: drop action1
 WARNING: untranslated string: drop action2
 WARNING: untranslated string: drop forward
 WARNING: untranslated string: drop outgoing
+WARNING: untranslated string: eight hours
 WARNING: untranslated string: email config
 WARNING: untranslated string: email empty field
 WARNING: untranslated string: email invalid
@@ -1019,6 +1050,9 @@ WARNING: untranslated string: no hardware random number generator
 WARNING: untranslated string: none
 WARNING: untranslated string: not a valid dh key
 WARNING: untranslated string: notice
+WARNING: untranslated string: one hour
+WARNING: untranslated string: one month
+WARNING: untranslated string: one week
 WARNING: untranslated string: openvpn default
 WARNING: untranslated string: openvpn destination port used
 WARNING: untranslated string: openvpn fragment allowed with udp
@@ -1143,6 +1177,7 @@ WARNING: untranslated string: tor use exit nodes
 WARNING: untranslated string: unblock
 WARNING: untranslated string: unblock all
 WARNING: untranslated string: uncheck all
+WARNING: untranslated string: unlimited
 WARNING: untranslated string: uplink
 WARNING: untranslated string: uplink bit rate
 WARNING: untranslated string: upload dh key
index 4a916761b510e809326b013923a3f4f57750683c..1389cbbe754bbf52a59e2a327c5495a69a2a6471 100644 (file)
@@ -628,6 +628,35 @@ WARNING: translation string unused: xtaccess all error
 WARNING: translation string unused: xtaccess bad transfert
 WARNING: translation string unused: year-graph
 WARNING: translation string unused: yearly firewallhits
+WARNING: untranslated string: 24 hours
+WARNING: untranslated string: Captive ACTIVATE
+WARNING: untranslated string: Captive GAIN ACCESS
+WARNING: untranslated string: Captive activated
+WARNING: untranslated string: Captive active on
+WARNING: untranslated string: Captive agree tac
+WARNING: untranslated string: Captive authentication
+WARNING: untranslated string: Captive brand color
+WARNING: untranslated string: Captive branding
+WARNING: untranslated string: Captive client session expiry time
+WARNING: untranslated string: Captive clients
+WARNING: untranslated string: Captive config
+WARNING: untranslated string: Captive coupon
+WARNING: untranslated string: Captive expiry time
+WARNING: untranslated string: Captive generate coupon
+WARNING: untranslated string: Captive issued coupons
+WARNING: untranslated string: Captive logo uploaded
+WARNING: untranslated string: Captive mac
+WARNING: untranslated string: Captive menu
+WARNING: untranslated string: Captive noexpiretime
+WARNING: untranslated string: Captive nolimit
+WARNING: untranslated string: Captive please accept the terms and conditions
+WARNING: untranslated string: Captive terms
+WARNING: untranslated string: Captive terms short
+WARNING: untranslated string: Captive title
+WARNING: untranslated string: Captive upload logo
+WARNING: untranslated string: Captive upload logo recommendations
+WARNING: untranslated string: Captive vouchervalid
+WARNING: untranslated string: Captive wrong ext
 WARNING: untranslated string: ConnSched dial
 WARNING: untranslated string: ConnSched hangup
 WARNING: untranslated string: ConnSched reboot
@@ -658,6 +687,7 @@ WARNING: untranslated string: bit
 WARNING: untranslated string: block
 WARNING: untranslated string: bytes
 WARNING: untranslated string: capabilities
+WARNING: untranslated string: captive
 WARNING: untranslated string: ccd add
 WARNING: untranslated string: ccd choose net
 WARNING: untranslated string: ccd client options
@@ -739,6 +769,7 @@ WARNING: untranslated string: drop action1
 WARNING: untranslated string: drop action2
 WARNING: untranslated string: drop forward
 WARNING: untranslated string: drop outgoing
+WARNING: untranslated string: eight hours
 WARNING: untranslated string: email config
 WARNING: untranslated string: email empty field
 WARNING: untranslated string: email invalid
@@ -1038,6 +1069,9 @@ WARNING: untranslated string: not a valid dh key
 WARNING: untranslated string: notice
 WARNING: untranslated string: ntp common settings
 WARNING: untranslated string: ntp sync
+WARNING: untranslated string: one hour
+WARNING: untranslated string: one month
+WARNING: untranslated string: one week
 WARNING: untranslated string: openvpn default
 WARNING: untranslated string: openvpn destination port used
 WARNING: untranslated string: openvpn fragment allowed with udp
@@ -1157,6 +1191,7 @@ WARNING: untranslated string: tor use exit nodes
 WARNING: untranslated string: unblock
 WARNING: untranslated string: unblock all
 WARNING: untranslated string: uncheck all
+WARNING: untranslated string: unlimited
 WARNING: untranslated string: uplink
 WARNING: untranslated string: uplink bit rate
 WARNING: untranslated string: upload dh key
index 0baf400861de33058849102cc8ab8172139ea360..5b921a84a9ed80218ffdbe8582e42a2425a941c7 100644 (file)
@@ -695,6 +695,35 @@ WARNING: translation string unused: xtaccess all error
 WARNING: translation string unused: xtaccess bad transfert
 WARNING: translation string unused: year-graph
 WARNING: translation string unused: yearly firewallhits
+WARNING: untranslated string: 24 hours
+WARNING: untranslated string: Captive ACTIVATE
+WARNING: untranslated string: Captive GAIN ACCESS
+WARNING: untranslated string: Captive activated
+WARNING: untranslated string: Captive active on
+WARNING: untranslated string: Captive agree tac
+WARNING: untranslated string: Captive authentication
+WARNING: untranslated string: Captive brand color
+WARNING: untranslated string: Captive branding
+WARNING: untranslated string: Captive client session expiry time
+WARNING: untranslated string: Captive clients
+WARNING: untranslated string: Captive config
+WARNING: untranslated string: Captive coupon
+WARNING: untranslated string: Captive expiry time
+WARNING: untranslated string: Captive generate coupon
+WARNING: untranslated string: Captive issued coupons
+WARNING: untranslated string: Captive logo uploaded
+WARNING: untranslated string: Captive mac
+WARNING: untranslated string: Captive menu
+WARNING: untranslated string: Captive noexpiretime
+WARNING: untranslated string: Captive nolimit
+WARNING: untranslated string: Captive please accept the terms and conditions
+WARNING: untranslated string: Captive terms
+WARNING: untranslated string: Captive terms short
+WARNING: untranslated string: Captive title
+WARNING: untranslated string: Captive upload logo
+WARNING: untranslated string: Captive upload logo recommendations
+WARNING: untranslated string: Captive vouchervalid
+WARNING: untranslated string: Captive wrong ext
 WARNING: untranslated string: MTU settings
 WARNING: untranslated string: Number of Countries for the pie chart
 WARNING: untranslated string: Scan for Songs
@@ -707,6 +736,7 @@ WARNING: untranslated string: advproxy group required
 WARNING: untranslated string: application layer gateways
 WARNING: untranslated string: block
 WARNING: untranslated string: bytes
+WARNING: untranslated string: captive
 WARNING: untranslated string: check all
 WARNING: untranslated string: dhcp dns enable update
 WARNING: untranslated string: dhcp dns key name
@@ -715,6 +745,7 @@ WARNING: untranslated string: dhcp dns update algo
 WARNING: untranslated string: dhcp dns update secret
 WARNING: untranslated string: dl client arch insecure
 WARNING: untranslated string: dnssec disabled warning
+WARNING: untranslated string: eight hours
 WARNING: untranslated string: email config
 WARNING: untranslated string: email empty field
 WARNING: untranslated string: email invalid
@@ -808,6 +839,9 @@ WARNING: untranslated string: masquerading enabled
 WARNING: untranslated string: messages
 WARNING: untranslated string: no data
 WARNING: untranslated string: none
+WARNING: untranslated string: one hour
+WARNING: untranslated string: one month
+WARNING: untranslated string: one week
 WARNING: untranslated string: outgoing compression in bytes per second
 WARNING: untranslated string: outgoing overhead in bytes per second
 WARNING: untranslated string: ovpn add conf
@@ -825,6 +859,7 @@ WARNING: untranslated string: search
 WARNING: untranslated string: unblock
 WARNING: untranslated string: unblock all
 WARNING: untranslated string: uncheck all
+WARNING: untranslated string: unlimited
 WARNING: untranslated string: uplink bit rate
 WARNING: untranslated string: vpn broken
 WARNING: untranslated string: vpn connecting
index 4f64b5d68c9811e5dfc47142527932495b81e401..16f6c0d89350082f8cc4ed83a6953eec6f780208 100644 (file)
@@ -691,6 +691,35 @@ WARNING: translation string unused: xtaccess all error
 WARNING: translation string unused: xtaccess bad transfert
 WARNING: translation string unused: year-graph
 WARNING: translation string unused: yearly firewallhits
+WARNING: untranslated string: 24 hours
+WARNING: untranslated string: Captive ACTIVATE
+WARNING: untranslated string: Captive GAIN ACCESS
+WARNING: untranslated string: Captive activated
+WARNING: untranslated string: Captive active on
+WARNING: untranslated string: Captive agree tac
+WARNING: untranslated string: Captive authentication
+WARNING: untranslated string: Captive brand color
+WARNING: untranslated string: Captive branding
+WARNING: untranslated string: Captive client session expiry time
+WARNING: untranslated string: Captive clients
+WARNING: untranslated string: Captive config
+WARNING: untranslated string: Captive coupon
+WARNING: untranslated string: Captive expiry time
+WARNING: untranslated string: Captive generate coupon
+WARNING: untranslated string: Captive issued coupons
+WARNING: untranslated string: Captive logo uploaded
+WARNING: untranslated string: Captive mac
+WARNING: untranslated string: Captive menu
+WARNING: untranslated string: Captive noexpiretime
+WARNING: untranslated string: Captive nolimit
+WARNING: untranslated string: Captive please accept the terms and conditions
+WARNING: untranslated string: Captive terms
+WARNING: untranslated string: Captive terms short
+WARNING: untranslated string: Captive title
+WARNING: untranslated string: Captive upload logo
+WARNING: untranslated string: Captive upload logo recommendations
+WARNING: untranslated string: Captive vouchervalid
+WARNING: untranslated string: Captive wrong ext
 WARNING: untranslated string: MTU settings
 WARNING: untranslated string: Number of Countries for the pie chart
 WARNING: untranslated string: Scan for Songs
@@ -706,6 +735,7 @@ WARNING: untranslated string: atm device
 WARNING: untranslated string: block
 WARNING: untranslated string: bytes
 WARNING: untranslated string: capabilities
+WARNING: untranslated string: captive
 WARNING: untranslated string: check all
 WARNING: untranslated string: default
 WARNING: untranslated string: dh
@@ -727,6 +757,7 @@ WARNING: untranslated string: dnssec not supported
 WARNING: untranslated string: dnssec validating
 WARNING: untranslated string: download tls-auth key
 WARNING: untranslated string: drop outgoing
+WARNING: untranslated string: eight hours
 WARNING: untranslated string: email config
 WARNING: untranslated string: email empty field
 WARNING: untranslated string: email invalid
@@ -840,6 +871,9 @@ WARNING: untranslated string: nameserver
 WARNING: untranslated string: no data
 WARNING: untranslated string: none
 WARNING: untranslated string: not a valid dh key
+WARNING: untranslated string: one hour
+WARNING: untranslated string: one month
+WARNING: untranslated string: one week
 WARNING: untranslated string: outgoing compression in bytes per second
 WARNING: untranslated string: outgoing overhead in bytes per second
 WARNING: untranslated string: ovpn add conf
@@ -871,6 +905,7 @@ WARNING: untranslated string: ta key
 WARNING: untranslated string: unblock
 WARNING: untranslated string: unblock all
 WARNING: untranslated string: uncheck all
+WARNING: untranslated string: unlimited
 WARNING: untranslated string: uplink bit rate
 WARNING: untranslated string: upload dh key
 WARNING: untranslated string: vendor
index d34d63f59a2d71925a31239a6f550a75a4153cb9..a7010105b6cc2f817277c45de904e6c2f2823b72 100644 (file)
@@ -618,6 +618,35 @@ WARNING: translation string unused: xtaccess all error
 WARNING: translation string unused: xtaccess bad transfert
 WARNING: translation string unused: year-graph
 WARNING: translation string unused: yearly firewallhits
+WARNING: untranslated string: 24 hours
+WARNING: untranslated string: Captive ACTIVATE
+WARNING: untranslated string: Captive GAIN ACCESS
+WARNING: untranslated string: Captive activated
+WARNING: untranslated string: Captive active on
+WARNING: untranslated string: Captive agree tac
+WARNING: untranslated string: Captive authentication
+WARNING: untranslated string: Captive brand color
+WARNING: untranslated string: Captive branding
+WARNING: untranslated string: Captive client session expiry time
+WARNING: untranslated string: Captive clients
+WARNING: untranslated string: Captive config
+WARNING: untranslated string: Captive coupon
+WARNING: untranslated string: Captive expiry time
+WARNING: untranslated string: Captive generate coupon
+WARNING: untranslated string: Captive issued coupons
+WARNING: untranslated string: Captive logo uploaded
+WARNING: untranslated string: Captive mac
+WARNING: untranslated string: Captive menu
+WARNING: untranslated string: Captive noexpiretime
+WARNING: untranslated string: Captive nolimit
+WARNING: untranslated string: Captive please accept the terms and conditions
+WARNING: untranslated string: Captive terms
+WARNING: untranslated string: Captive terms short
+WARNING: untranslated string: Captive title
+WARNING: untranslated string: Captive upload logo
+WARNING: untranslated string: Captive upload logo recommendations
+WARNING: untranslated string: Captive vouchervalid
+WARNING: untranslated string: Captive wrong ext
 WARNING: untranslated string: ConnSched dial
 WARNING: untranslated string: ConnSched hangup
 WARNING: untranslated string: ConnSched reboot
@@ -649,6 +678,7 @@ WARNING: untranslated string: bit
 WARNING: untranslated string: block
 WARNING: untranslated string: bytes
 WARNING: untranslated string: capabilities
+WARNING: untranslated string: captive
 WARNING: untranslated string: ccd add
 WARNING: untranslated string: ccd choose net
 WARNING: untranslated string: ccd client options
@@ -729,6 +759,7 @@ WARNING: untranslated string: drop action1
 WARNING: untranslated string: drop action2
 WARNING: untranslated string: drop forward
 WARNING: untranslated string: drop outgoing
+WARNING: untranslated string: eight hours
 WARNING: untranslated string: email config
 WARNING: untranslated string: email empty field
 WARNING: untranslated string: email invalid
@@ -1019,6 +1050,9 @@ WARNING: untranslated string: no hardware random number generator
 WARNING: untranslated string: none
 WARNING: untranslated string: not a valid dh key
 WARNING: untranslated string: notice
+WARNING: untranslated string: one hour
+WARNING: untranslated string: one month
+WARNING: untranslated string: one week
 WARNING: untranslated string: openvpn default
 WARNING: untranslated string: openvpn destination port used
 WARNING: untranslated string: openvpn fragment allowed with udp
@@ -1143,6 +1177,7 @@ WARNING: untranslated string: tor use exit nodes
 WARNING: untranslated string: unblock
 WARNING: untranslated string: unblock all
 WARNING: untranslated string: uncheck all
+WARNING: untranslated string: unlimited
 WARNING: untranslated string: uplink
 WARNING: untranslated string: uplink bit rate
 WARNING: untranslated string: upload dh key
index d07c421eb2547fdbb6315f98dfe5755f68722f40..a2a771ecee9fbfdd0be885ed9bdcb22be540335d 100644 (file)
@@ -619,7 +619,36 @@ WARNING: translation string unused: wlanap wlan services
 WARNING: translation string unused: xtaccess all error
 WARNING: translation string unused: xtaccess bad transfert
 WARNING: translation string unused: yearly firewallhits
+WARNING: untranslated string: 24 hours
 WARNING: untranslated string: Add a route
+WARNING: untranslated string: Captive ACTIVATE
+WARNING: untranslated string: Captive GAIN ACCESS
+WARNING: untranslated string: Captive activated
+WARNING: untranslated string: Captive active on
+WARNING: untranslated string: Captive agree tac
+WARNING: untranslated string: Captive authentication
+WARNING: untranslated string: Captive brand color
+WARNING: untranslated string: Captive branding
+WARNING: untranslated string: Captive client session expiry time
+WARNING: untranslated string: Captive clients
+WARNING: untranslated string: Captive config
+WARNING: untranslated string: Captive coupon
+WARNING: untranslated string: Captive expiry time
+WARNING: untranslated string: Captive generate coupon
+WARNING: untranslated string: Captive issued coupons
+WARNING: untranslated string: Captive logo uploaded
+WARNING: untranslated string: Captive mac
+WARNING: untranslated string: Captive menu
+WARNING: untranslated string: Captive noexpiretime
+WARNING: untranslated string: Captive nolimit
+WARNING: untranslated string: Captive please accept the terms and conditions
+WARNING: untranslated string: Captive terms
+WARNING: untranslated string: Captive terms short
+WARNING: untranslated string: Captive title
+WARNING: untranslated string: Captive upload logo
+WARNING: untranslated string: Captive upload logo recommendations
+WARNING: untranslated string: Captive vouchervalid
+WARNING: untranslated string: Captive wrong ext
 WARNING: untranslated string: ConnSched dial
 WARNING: untranslated string: ConnSched hangup
 WARNING: untranslated string: ConnSched reboot
@@ -651,6 +680,7 @@ WARNING: untranslated string: bit
 WARNING: untranslated string: block
 WARNING: untranslated string: bytes
 WARNING: untranslated string: capabilities
+WARNING: untranslated string: captive
 WARNING: untranslated string: ccd add
 WARNING: untranslated string: ccd choose net
 WARNING: untranslated string: ccd client options
@@ -733,6 +763,7 @@ WARNING: untranslated string: drop action1
 WARNING: untranslated string: drop action2
 WARNING: untranslated string: drop forward
 WARNING: untranslated string: drop outgoing
+WARNING: untranslated string: eight hours
 WARNING: untranslated string: email config
 WARNING: untranslated string: email empty field
 WARNING: untranslated string: email invalid
@@ -1021,6 +1052,9 @@ WARNING: untranslated string: no hardware random number generator
 WARNING: untranslated string: none
 WARNING: untranslated string: not a valid dh key
 WARNING: untranslated string: notice
+WARNING: untranslated string: one hour
+WARNING: untranslated string: one month
+WARNING: untranslated string: one week
 WARNING: untranslated string: openvpn default
 WARNING: untranslated string: openvpn destination port used
 WARNING: untranslated string: openvpn fragment allowed with udp
@@ -1138,6 +1172,7 @@ WARNING: untranslated string: tor use exit nodes
 WARNING: untranslated string: unblock
 WARNING: untranslated string: unblock all
 WARNING: untranslated string: uncheck all
+WARNING: untranslated string: unlimited
 WARNING: untranslated string: uplink
 WARNING: untranslated string: uplink bit rate
 WARNING: untranslated string: upload dh key
index 034dd2a7bd4e5096396467a5b5e8968be59a3745..96849cd691b95df7e7c4884c2f07255f0781552d 100644 (file)
@@ -703,10 +703,41 @@ WARNING: translation string unused: xtaccess all error
 WARNING: translation string unused: xtaccess bad transfert
 WARNING: translation string unused: year-graph
 WARNING: translation string unused: yearly firewallhits
+WARNING: untranslated string: 24 hours
+WARNING: untranslated string: Captive ACTIVATE
+WARNING: untranslated string: Captive GAIN ACCESS
+WARNING: untranslated string: Captive activated
+WARNING: untranslated string: Captive active on
+WARNING: untranslated string: Captive agree tac
+WARNING: untranslated string: Captive authentication
+WARNING: untranslated string: Captive brand color
+WARNING: untranslated string: Captive branding
+WARNING: untranslated string: Captive client session expiry time
+WARNING: untranslated string: Captive clients
+WARNING: untranslated string: Captive config
+WARNING: untranslated string: Captive coupon
+WARNING: untranslated string: Captive expiry time
+WARNING: untranslated string: Captive generate coupon
+WARNING: untranslated string: Captive issued coupons
+WARNING: untranslated string: Captive logo uploaded
+WARNING: untranslated string: Captive mac
+WARNING: untranslated string: Captive menu
+WARNING: untranslated string: Captive noexpiretime
+WARNING: untranslated string: Captive nolimit
+WARNING: untranslated string: Captive please accept the terms and conditions
+WARNING: untranslated string: Captive terms
+WARNING: untranslated string: Captive terms short
+WARNING: untranslated string: Captive title
+WARNING: untranslated string: Captive upload logo
+WARNING: untranslated string: Captive upload logo recommendations
+WARNING: untranslated string: Captive vouchervalid
+WARNING: untranslated string: Captive wrong ext
 WARNING: untranslated string: Scan for Songs
 WARNING: untranslated string: application layer gateways
 WARNING: untranslated string: bytes
+WARNING: untranslated string: captive
 WARNING: untranslated string: dnssec disabled warning
+WARNING: untranslated string: eight hours
 WARNING: untranslated string: firewall graph country
 WARNING: untranslated string: firewall graph ip
 WARNING: untranslated string: firewall graph port
@@ -755,10 +786,14 @@ WARNING: untranslated string: guardian watch snort alertfile
 WARNING: untranslated string: ike lifetime should be between 1 and 8 hours
 WARNING: untranslated string: info messages
 WARNING: untranslated string: no data
+WARNING: untranslated string: one hour
+WARNING: untranslated string: one month
+WARNING: untranslated string: one week
 WARNING: untranslated string: route config changed
 WARNING: untranslated string: routing config added
 WARNING: untranslated string: routing config changed
 WARNING: untranslated string: routing table
+WARNING: untranslated string: unlimited
 WARNING: untranslated string: uplink bit rate
 WARNING: untranslated string: vpn broken
 WARNING: untranslated string: vpn connecting
index fd372c15fb957c8b42dbc0225667d1b7145a0935..46f6d694d134a14cfd82264a45ddf957caf7ff6c 100644 (file)
@@ -2,6 +2,10 @@
 # Checking cgi-bin translations for language: en                           #
 ############################################################################
 < addon
+< Captive clients
+< Captive genvoucher
+< Captive logo_upload
+< Captive logo_upload1
 < ccd maxclients
 < ovpn_fragment
 ############################################################################
 < bit
 < block
 < capabilities
+< Captive 1day
+< Captive 1month
+< Captive 1week
+< Captive activate
+< Captive ACTIVATE
+< Captive activated
+< Captive active on
+< Captive agree tac
+< Captive authentication
+< Captive auth_lic
+< Captive auth_vou
+< Captive brand color
+< Captive branding
+< Captive clients
+< Captive client session expiry time
+< Captive config
+< Captive coupon
+< Captive err doublevoucher
+< Captive expire
+< Captive expiry time
+< Captive GAIN ACCESS
+< Captive generate coupon
+< Captive genvoucher
+< Captive invalid logosize
+< Captive invalid_voucher
+< Captive ip
+< Captive issued coupons
+< Captive logo_set
+< Captive logo_upload
+< Captive logo_upload1
+< Captive logo uploaded
+< Captive mac
+< Captive menu
+< Captive noexpiretime
+< Captive nolimit
+< Captive nr
+< Captive please accept the terms and conditions
+< Captive terms
+< Captive terms short
+< Captive time
+< Captive title
+< Captive upload logo
+< Captive upload logo recommendations
+< Captive voactive
+< Captive voucher
+< Captive vouchervalid
+< Captive vout
+< Captive wrong ext
 < ccd add
 < ccd choose net
 < ccd clientip
 < drop action2
 < drop forward
 < drop outgoing
+< eight hours
 < email config
 < email empty field
 < email error
 < bit
 < block
 < capabilities
+< Captive 1day
+< Captive 1month
+< Captive 1week
+< Captive activate
+< Captive ACTIVATE
+< Captive activated
+< Captive active on
+< Captive agree tac
+< Captive authentication
+< Captive auth_lic
+< Captive auth_vou
+< Captive brand color
+< Captive branding
+< Captive clients
+< Captive client session expiry time
+< Captive config
+< Captive coupon
+< Captive err doublevoucher
+< Captive expire
+< Captive expiry time
+< Captive GAIN ACCESS
+< Captive generate coupon
+< Captive genvoucher
+< Captive invalid logosize
+< Captive invalid_voucher
+< Captive ip
+< Captive issued coupons
+< Captive logo_set
+< Captive logo_upload
+< Captive logo_upload1
+< Captive logo uploaded
+< Captive mac
+< Captive menu
+< Captive noexpiretime
+< Captive nolimit
+< Captive nr
+< Captive please accept the terms and conditions
+< Captive terms
+< Captive terms short
+< Captive time
+< Captive title
+< Captive upload logo
+< Captive upload logo recommendations
+< Captive voactive
+< Captive voucher
+< Captive vouchervalid
+< Captive vout
+< Captive wrong ext
 < ccd add
 < ccd choose net
 < ccd clientip
 < drop action2
 < drop forward
 < drop outgoing
+< eight hours
 < email config
 < email empty field
 < email error
 < bit
 < block
 < capabilities
+< Captive 1day
+< Captive 1month
+< Captive 1week
+< Captive activate
+< Captive ACTIVATE
+< Captive activated
+< Captive active on
+< Captive agree tac
+< Captive authentication
+< Captive auth_lic
+< Captive auth_vou
+< Captive brand color
+< Captive branding
+< Captive clients
+< Captive client session expiry time
+< Captive config
+< Captive coupon
+< Captive err doublevoucher
+< Captive expire
+< Captive expiry time
+< Captive GAIN ACCESS
+< Captive generate coupon
+< Captive genvoucher
+< Captive invalid logosize
+< Captive invalid_voucher
+< Captive ip
+< Captive issued coupons
+< Captive logo_set
+< Captive logo_upload
+< Captive logo_upload1
+< Captive logo uploaded
+< Captive mac
+< Captive menu
+< Captive noexpiretime
+< Captive nolimit
+< Captive nr
+< Captive please accept the terms and conditions
+< Captive terms
+< Captive terms short
+< Captive time
+< Captive title
+< Captive upload logo
+< Captive upload logo recommendations
+< Captive voactive
+< Captive voucher
+< Captive vouchervalid
+< Captive vout
+< Captive wrong ext
 < ccd add
 < ccd choose net
 < ccd clientip
 < drop action2
 < drop forward
 < drop outgoing
+< eight hours
 < email config
 < email empty field
 < email error
 < bit
 < block
 < capabilities
+< Captive 1day
+< Captive 1month
+< Captive 1week
+< Captive activate
+< Captive ACTIVATE
+< Captive activated
+< Captive active on
+< Captive agree tac
+< Captive authentication
+< Captive auth_lic
+< Captive auth_vou
+< Captive brand color
+< Captive branding
+< Captive clients
+< Captive client session expiry time
+< Captive config
+< Captive coupon
+< Captive err doublevoucher
+< Captive expire
+< Captive expiry time
+< Captive GAIN ACCESS
+< Captive generate coupon
+< Captive genvoucher
+< Captive invalid logosize
+< Captive invalid_voucher
+< Captive ip
+< Captive issued coupons
+< Captive logo_set
+< Captive logo_upload
+< Captive logo_upload1
+< Captive logo uploaded
+< Captive mac
+< Captive menu
+< Captive noexpiretime
+< Captive nolimit
+< Captive nr
+< Captive please accept the terms and conditions
+< Captive terms
+< Captive terms short
+< Captive time
+< Captive title
+< Captive upload logo
+< Captive upload logo recommendations
+< Captive voactive
+< Captive voucher
+< Captive vouchervalid
+< Captive vout
+< Captive wrong ext
 < ccd add
 < ccd choose net
 < ccd clientip
 < drop forward
 < drop outgoing
 < Edit an existing route
+< eight hours
 < email config
 < email empty field
 < email error
diff --git a/html/cgi-bin/captive.cgi b/html/cgi-bin/captive.cgi
new file mode 100755 (executable)
index 0000000..2f85b17
--- /dev/null
@@ -0,0 +1,666 @@
+#!/usr/bin/perl
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2016  IPFire Team  <alexander.marx@ipfire.org>                #
+#                                                                             #
+# 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 <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+#use strict;
+use HTML::Entities();
+use File::Basename;
+
+# enable only the following on debugging purpose
+#use warnings;
+#use CGI::Carp 'fatalsToBrowser';
+
+require '/var/ipfire/general-functions.pl';
+require "${General::swroot}/lang.pl";
+require "${General::swroot}/header.pl";
+
+my %selected = ();
+
+my $coupons = "${General::swroot}/captive/coupons";
+my %couponhash = ();
+
+my $logo = "${General::swroot}/captive/logo.dat";
+
+my %settings=();
+my %mainsettings;
+my %color;
+my %cgiparams=();
+my %netsettings=();
+my %checked=();
+my $errormessage='';
+my $clients="${General::swroot}/captive/clients";
+my %clientshash=();
+my $settingsfile="${General::swroot}/captive/settings";
+unless (-e $settingsfile)      { system("touch $settingsfile"); }
+
+&Header::getcgihash(\%cgiparams);
+
+&General::readhash("${General::swroot}/main/settings", \%mainsettings);
+&General::readhash("/srv/web/ipfire/html/themes/".$mainsettings{'THEME'}."/include/colors.txt", \%color);
+&General::readhash("$settingsfile", \%settings) if(-f $settingsfile);
+&General::readhash("${General::swroot}/ethernet/settings", \%netsettings);
+
+&Header::showhttpheaders();
+
+if ($cgiparams{'ACTION'} eq $Lang::tr{'save'}) {
+       my $file = $cgiparams{'logo'};
+       if ($file) {
+               # Check if the file extension is PNG/JPEG
+               chomp $file;
+
+               my ($name, $path, $ext) = fileparse($file, qr/\.[^.]*$/);
+               if ($ext ne ".png" && $ext ne ".jpg" && $ext ne ".jpeg") {
+                       $errormessage = $Lang::tr{'Captive wrong ext'};
+               }
+       }
+
+       $settings{'ENABLE_GREEN'}               = $cgiparams{'ENABLE_GREEN'};
+       $settings{'ENABLE_BLUE'}                = $cgiparams{'ENABLE_BLUE'};
+       $settings{'AUTH'}                               = $cgiparams{'AUTH'};
+       $settings{'TITLE'}                              = $cgiparams{'TITLE'};
+       $settings{'COLOR'}                      = $cgiparams{'COLOR'};
+       $settings{'SESSION_TIME'}               = $cgiparams{'SESSION_TIME'};
+
+       if (!$errormessage){
+               #Check if we need to upload a new logo
+               if ($file) {
+                       # Save logo
+                       my ($filehandle) = CGI::upload("logo");
+
+                       # XXX check filesize
+
+                       open(FILE, ">$logo");
+                       binmode $filehandle;
+                       while (<$filehandle>) {
+                               print FILE;
+                       }
+                       close(FILE);
+               }
+
+               &General::writehash("$settingsfile", \%settings);
+
+               # Save terms
+               if ($cgiparams{'TERMS'}){
+                       $cgiparams{'TERMS'} = &Header::escape($cgiparams{'TERMS'});
+                       open(FH, ">:utf8", "/var/ipfire/captive/terms.txt") or die("$!");
+                       print FH $cgiparams{'TERMS'};
+                       close(FH);
+                       $cgiparams{'TERMS'} = "";
+               }
+
+               #execute binary to reload firewall rules
+               system("/usr/local/bin/captivectrl");
+
+               if ($cgiparams{'ENABLE_BLUE'} eq 'on'){
+                               system("/usr/local/bin/wirelessctrl");
+               }
+       }
+}
+
+if ($cgiparams{'ACTION'} eq "$Lang::tr{'Captive generate coupon'}") {
+       # Check expiry time
+       if ($cgiparams{'EXP_HOUR'} + $cgiparams{'EXP_DAY'} + $cgiparams{'EXP_WEEK'} + $cgiparams{'EXP_MONTH'} == 0 && $cgiparams{'UNLIMITED'} == '') {
+               $errormessage = $Lang::tr{'Captive noexpiretime'};
+       }
+
+       #check valid remark
+       if ($cgiparams{'REMARK'} ne '' && !&validremark($cgiparams{'REMARK'})){
+               $errormessage=$Lang::tr{'fwhost err remark'};
+       }
+
+       if (!$errormessage) {
+               # Remember selected values
+               foreach my $val (("UNLIMITED", "EXP_HOUR", "EXP_DAY", "EXP_WEEK", "EXP_MONTH")) {
+                       $settings{$val} = $cgiparams{$val};
+               }
+               &General::writehash($settingsfile, \%settings);
+
+               &General::readhasharray($coupons, \%couponhash) if (-e $coupons);
+               my $now = time();
+
+               # Calculate expiry time in seconds
+               my $expires = 0;
+
+               if ($settings{'UNLIMITED'} ne 'on') {
+                       $expires += $settings{'EXP_HOUR'};
+                       $expires += $settings{'EXP_DAY'};
+                       $expires += $settings{'EXP_WEEK'};
+                       $expires += $settings{'EXP_MONTH'};
+               }
+
+               my $count = $cgiparams{'COUNT'} || 1;
+               while($count-- > 0) {
+                       # Generate a new code
+                       my $code = &gencode();
+
+                       # Check if the coupon code already exists
+                       foreach my $key (keys %couponhash) {
+                               if($couponhash{$key}[1] eq $code) {
+                                       # Code already exists, so try again
+                                       $code = "";
+                                       $count++;
+                                       last;
+                               }
+                       }
+
+                       next if ($code eq "");
+
+                       # Get a new key from hash
+                       my $key = &General::findhasharraykey(\%couponhash);
+
+                       # Initialize all fields
+                       foreach my $i (0 .. 3) { $couponhash{$key}[$i] = ""; }
+
+                       $couponhash{$key}[0] = $now;
+                       $couponhash{$key}[1] = $code;
+                       $couponhash{$key}[2] = $expires;
+                       $couponhash{$key}[3] = $cgiparams{'REMARK'};
+               }
+
+               # Save everything to disk
+               &General::writehasharray($coupons, \%couponhash);
+       }
+}
+
+if ($cgiparams{'ACTION'} eq 'delete-coupon') {
+       #deletes an already generated but unused voucher
+
+       #read all generated vouchers
+       &General::readhasharray($coupons, \%couponhash) if (-e $coupons);
+       foreach my $key (keys %couponhash) {
+               if($cgiparams{'key'} eq $couponhash{$key}[0]){
+                       #write logenty with decoded remark
+                       my $rem=HTML::Entities::decode_entities($couponhash{$key}[4]);
+                       &General::log("Captive", "Delete unused coupon $couponhash{$key}[1] $couponhash{$key}[2] hours valid expires on $couponhash{$key}[3] remark $rem");
+                       #delete line from hash
+                       delete $couponhash{$key};
+                       last;
+               }
+       }
+       #write back hash
+       &General::writehasharray($coupons, \%couponhash);
+}
+
+if ($cgiparams{'ACTION'} eq 'delete-client') {
+       #delete voucher and connection in use
+
+       #read all active clients
+       &General::readhasharray($clients, \%clientshash) if (-e $clients);
+       foreach my $key (keys %clientshash) {
+               if($cgiparams{'key'} eq $clientshash{$key}[0]){
+                       #prepare log entry with decoded remark
+                       my $rem=HTML::Entities::decode_entities($clientshash{$key}[7]);
+                       #write logentry
+                       &General::log("Captive", "Deleted client in use $clientshash{$key}[1] $clientshash{$key}[2] hours valid expires on $clientshash{$key}[3] remark $rem - Connection will be terminated");
+                       #delete line from hash
+                       delete $clientshash{$key};
+                       last;
+               }
+       }
+       #write back hash
+       &General::writehasharray("$clients", \%clientshash);
+       #reload firewallrules to kill connection of client
+       system("/usr/local/bin/captivectrl");
+}
+
+#open webpage, print header and open box
+&Header::openpage($Lang::tr{'Captive menu'}, 1, '');
+&Header::openbigbox();
+
+# If an error message exists, show a box with the error message
+if ($errormessage) {
+       &Header::openbox('100%', 'left', $Lang::tr{'error messages'});
+       print $errormessage;
+       &Header::closebox();
+}
+
+# Prints the config box on the website
+&Header::openbox('100%', 'left', $Lang::tr{'Captive config'});
+print <<END
+       <form method='post' action='$ENV{'SCRIPT_NAME'}' enctype="multipart/form-data">\n
+               <table width='100%' border="0">
+               <tr>
+END
+;
+
+#check which parameters have to be enabled (from settings file)
+$checked{'ENABLE_GREEN'}{'off'} = '';
+$checked{'ENABLE_GREEN'}{'on'} = '';
+$checked{'ENABLE_GREEN'}{$settings{'ENABLE_GREEN'}} = "checked='checked'";
+
+$checked{'ENABLE_BLUE'}{'off'} = '';
+$checked{'ENABLE_BLUE'}{'on'} = '';
+$checked{'ENABLE_BLUE'}{$settings{'ENABLE_BLUE'}} = "checked='checked'";
+
+$checked{'UNLIMITED'}{'off'} = '';
+$checked{'UNLIMITED'}{'on'} = '';
+$checked{'UNLIMITED'}{$settings{'UNLIMITED'}} = "checked='checked'";
+
+$selected{'AUTH'} = ();
+$selected{'AUTH'}{'COUPON'} = "";
+$selected{'AUTH'}{'TERMS'} = "";
+$selected{'AUTH'}{$settings{'AUTH'}} = "selected";
+
+if ($netsettings{'GREEN_DEV'}){
+       print "<td width='30%'>$Lang::tr{'Captive active on'} <font color='$Header::colourgreen'>Green</font></td><td><input type='checkbox' name='ENABLE_GREEN' $checked{'ENABLE_GREEN'}{'on'} /></td></tr>";
+}
+if ($netsettings{'BLUE_DEV'}){
+       print "<td width='30%'>$Lang::tr{'Captive active on'} <font color='$Header::colourblue'>Blue</font></td><td><input type='checkbox' name='ENABLE_BLUE' $checked{'ENABLE_BLUE'}{'on'} /></td></tr>";
+}
+
+print<<END
+       </tr>
+       <tr>
+               <td>
+                       $Lang::tr{'Captive authentication'}
+               </td>
+               <td>
+                       <select name='AUTH'>
+                               <option value="TERMS"  $selected{'AUTH'}{'TERMS'} >$Lang::tr{'Captive terms'}</option>
+                               <option value="COUPON" $selected{'AUTH'}{'COUPON'}>$Lang::tr{'Captive coupon'}</option>
+                       </select>
+               </td>
+       </tr>
+END
+;
+
+if ($settings{'AUTH'} eq 'TERMS') {
+       $selected{'SESSION_TIME'} = ();
+       $selected{'SESSION_TIME'}{'0'} = "";
+       $selected{'SESSION_TIME'}{'3600'} = "";
+       $selected{'SESSION_TIME'}{'86400'} = "";
+       $selected{'SESSION_TIME'}{'604800'} = "";
+       $selected{'SESSION_TIME'}{'18144000'} = "";
+       $selected{'SESSION_TIME'}{$settings{'SESSION_TIME'}} = "selected";
+
+       my $terms = &getterms();
+       print <<END;
+               <tr>
+                       <td></td>
+                       <td>
+                               <textarea cols="50" rows="10" name="TERMS">$terms</textarea>
+                       </td>
+               </tr>
+
+               <tr>
+                       <td>$Lang::tr{'Captive client session expiry time'}</td>
+                       <td>
+                               <select name="SESSION_TIME">
+                                       <option value="0"        $selected{'SESSION_TIME'}{'0'}>- $Lang::tr{'unlimited'} -</option>
+                                       <option value="3600"     $selected{'SESSION_TIME'}{'3600'}>$Lang::tr{'one hour'}</option>
+                                       <option value="28800"    $selected{'SESSION_TIME'}{'28800'}>$Lang::tr{'eight hours'}</option>
+                                       <option value="86400"    $selected{'SESSION_TIME'}{'86400'}>$Lang::tr{'24 hours'}</option>
+                                       <option value="604800"   $selected{'SESSION_TIME'}{'604800'}>$Lang::tr{'one week'}</option>
+                                       <option value="18144000" $selected{'SESSION_TIME'}{'18144000'}>$Lang::tr{'one month'}</option>
+                               </select>
+                       </td>
+               </tr>
+END
+}
+
+print<<END;
+       <tr>
+               <td colspan="2">
+                       <br>
+                       <strong>$Lang::tr{'Captive branding'}</strong>
+               </td>
+       </tr>
+       <tr>
+               <td>
+                       $Lang::tr{'Captive title'}
+               </td>
+               <td>
+                       <input type='text' name='TITLE' value="$settings{'TITLE'}" size='40'>
+               </td>
+       </tr>
+       <tr>
+               <td>$Lang::tr{'Captive brand color'}</td>
+               <td>
+                       <input type="color" name="COLOR" value="$settings{'COLOR'}">
+               </td>
+       </tr>
+       <tr>
+               <td>
+                       $Lang::tr{'Captive upload logo'}
+               </td>
+               <td>
+                       <input type="file" name="logo">
+                       <br>$Lang::tr{'Captive upload logo recommendations'}
+               </td>
+       </tr>
+END
+
+if (-e $logo) {
+       print <<END;
+               <tr>
+                       <td>$Lang::tr{'Captive logo uploaded'}</td>
+                       <td>$Lang::tr{'yes'}</td>
+               </tr>
+END
+}
+
+print <<END;
+       <tr>
+               <td></td>
+               <td align='right'>
+                       <input type='submit' name='ACTION' value="$Lang::tr{'save'}"/>
+               </td>
+       </tr>
+       </table></form>
+END
+
+&Header::closebox();
+
+#if settings is set to use coupons, the coupon part has to be displayed
+if ($settings{'AUTH'} eq 'COUPON') {
+       &coupons();
+}
+
+# Show active clients
+&show_clients();
+
+sub getterms() {
+       my @ret;
+
+       open(FILE, "<:utf8", "/var/ipfire/captive/terms.txt");
+       while(<FILE>) {
+               push(@ret, HTML::Entities::decode_entities($_));
+       }
+       close(FILE);
+
+       return join(/\n/, @ret);
+}
+
+sub gencode(){
+       #generate a random code only letters from A-Z except 'O'  and 0-9
+       my @chars = ("A".."N", "P".."Z", "0".."9");
+       my $randomstring;
+       $randomstring .= $chars[rand @chars] for 1..8;
+       return $randomstring;
+}
+
+sub coupons() {
+       &Header::openbox('100%', 'left', $Lang::tr{'Captive generate coupon'});
+       print <<END;
+               <form method='post' action='$ENV{'SCRIPT_NAME'}'>
+                       <table border='0' width='100%'>
+                               <tr>
+                                       <td width='30%'>
+                                               $Lang::tr{'Captive vouchervalid'}
+                                       </td>
+                                       <td width='70%'>
+                                               <table class='tbl' border='0' width='100%'>
+                                                       <tr>
+                                                               <th>$Lang::tr{'hours'}</th>
+                                                               <th>$Lang::tr{'days'}</th>
+                                                               <th>$Lang::tr{'weeks'}</th>
+                                                               <th>$Lang::tr{'months'}</th>
+                                                               <th></th>
+                                                       </tr>
+END
+
+               #print hour-dropdownbox
+               my $hrs=3600;
+               print "<tr height='40px'><td><select name='EXP_HOUR' style='width:8em;'>";
+               print "<option value='0' ";
+               print " selected='selected'" if ($settings{'EXP_HOUR'} eq '0');
+               print ">--</option>";
+               for (my $i = 1; $i<25; $i++){
+                       my $exp_sec = $i * $hrs;
+                       print "<option value='$exp_sec' ";
+                       print " selected='selected'" if ($settings{'EXP_HOUR'} eq $exp_sec);
+                       print ">$i</option>";
+               }
+               print "</td><td>";
+
+               #print day-dropdownbox
+               my $days=3600*24;
+               print "<select name='EXP_DAY' style='width:8em;'>";
+               print "<option value='0' ";
+               print " selected='selected'" if ($settings{'EXP_DAY'} eq '0');
+               print ">--</option>";
+               for (my $i = 1; $i<8; $i++){
+                       my $exp_sec = $i * $days;
+                       print "<option value='$exp_sec' ";
+                       print " selected='selected'" if ($settings{'EXP_DAY'} eq $exp_sec);
+                       print ">$i</option>";
+               }
+               print "</td><td>";
+
+               #print week-dropdownbox
+               my $week=3600*24*7;
+               print "<select name='EXP_WEEK' style='width:8em;'>";
+               print "<option value='0' ";
+               print " selected='selected'" if ($settings{'EXP_WEEK'} eq '0');
+               print ">--</option>";
+               for (my $i = 1; $i<5; $i++){
+                       my $exp_sec = $i * $week;
+                       print "<option value='$exp_sec' ";
+                       print " selected='selected'" if ($settings{'EXP_WEEK'} eq $exp_sec);
+                       print ">$i</option>";
+               }
+               print "</td><td>";
+
+               #print month-dropdownbox
+               my $month=3600*24*30;
+               print "<select name='EXP_MONTH' style='width:8em;'>";
+               print "<option value='0' ";
+               print " selected='selected'" if ($settings{'EXP_MONTH'} eq '0');
+               print ">--</option>";
+               for (my $i = 1; $i<13; $i++){
+                       my $exp_sec = $i * $month;
+                       print "<option value='$exp_sec' ";
+                       print " selected='selected'" if ($settings{'EXP_MONTH'} eq $exp_sec);
+                       print ">$i</option>";
+               }
+               print <<END;
+                                                               </td>
+                                                               <td>
+                                                                       <label>
+                                                                               <input type='checkbox' name='UNLIMITED' $checked{'UNLIMITED'}{'on'} />
+                                                                               $Lang::tr{'Captive nolimit'}
+                                                                       </label>
+                                                               </td>
+                                                       </tr>
+                                               </table>
+                                       </td>
+                               </tr>
+                               <tr>
+                                       <td>$Lang::tr{'remark'}</td>
+                                       <td>
+                                               <input type='text' style='width: 98%;' name='REMARK' align='left'>
+                                       </td>
+                               </tr>
+                       </table>
+
+                       <div align="right">
+                               <select name="COUNT">
+                                       <option value="1">1</option>
+                                       <option value="2">2</option>
+                                       <option value="3">3</option>
+                                       <option value="4">4</option>
+                                       <option value="5">5</option>
+                                       <option value="6">6</option>
+                                       <option value="7">7</option>
+                                       <option value="8">8</option>
+                                       <option value="9">9</option>
+                                       <option value="10">10</option>
+                                       <option value="20">20</option>
+                                       <option value="50">50</option>
+                                       <option value="100">100</option>
+                               </select>
+
+                               <input type="submit" name="ACTION" value="$Lang::tr{'Captive generate coupon'}">
+                       </div>
+               </form>
+END
+
+       &Header::closebox();
+
+       # Show all coupons if exist
+       if (! -z $coupons) {
+               &show_coupons();
+       }
+}
+
+sub show_coupons() {
+       &General::readhasharray($coupons, \%couponhash) if (-e $coupons);
+
+       #if there are already generated but unsused coupons, print a table
+       &Header::openbox('100%', 'left', $Lang::tr{'Captive issued coupons'});
+
+       print <<END;
+               <table class='tbl' border='0'>
+                       <tr>
+                               <th align='center' width='15%'>
+                                       $Lang::tr{'Captive coupon'}
+                               </th>
+                               <th align='center' width='15%'>$Lang::tr{'Captive expiry time'}</th>
+                               <th align='center' width='65%'>$Lang::tr{'remark'}</th>
+                               <th align='center' width='5%'>$Lang::tr{'delete'}</th>
+                       </tr>
+END
+
+       foreach my $key (keys %couponhash) {
+               my $expirytime = $Lang::tr{'Captive nolimit'};
+               if ($couponhash{$key}[2] > 0) {
+                       $expirytime = &General::format_time($couponhash{$key}[2]);
+               }
+
+               if ($count++ % 2) {
+                       $col="bgcolor='$color{'color20'}'";
+               } else {
+                       $col="bgcolor='$color{'color22'}'";
+               }
+
+               print <<END;
+                       <tr>
+                               <td $col align="center">
+                                       <b>$couponhash{$key}[1]</b>
+                               </td>
+                               <td $col align="center">
+                                       $expirytime
+                               </td>
+                               <td $col align="center">
+                                       $couponhash{$key}[3]
+                               </td>
+                               <td $col align="center">
+                                       <form method='post'>
+                                               <input type='image' src='/images/delete.gif' align='middle' alt='$Lang::tr{'delete'}' title='$Lang::tr{'delete'}' />
+                                               <input type='hidden' name='ACTION' value='delete-coupon' />
+                                               <input type='hidden' name='key' value='$couponhash{$key}[0]' />
+                                       </form>
+                               </td>
+                       </tr>
+END
+       }
+
+       print "</table>";
+
+       &Header::closebox();
+}
+
+sub show_clients() {
+       # if there are active clients which use coupons show table
+       return if ( -z $clients || ! -f $clients );
+
+       my $count=0;
+       my $col;
+
+       &Header::openbox('100%', 'left', $Lang::tr{'Captive clients'});
+
+       print <<END;
+               <table class='tbl' width='100%'>
+                       <tr>
+                               <th align='center' width='15%'>$Lang::tr{'Captive coupon'}</th>
+                               <th align='center' width='15%'>$Lang::tr{'Captive activated'}</th>
+                               <th align='center' width='15%'>$Lang::tr{'Captive expiry time'}</th>
+                               <th align='center' width='10%'>$Lang::tr{'Captive mac'}</th>
+                               <th align='center' width='43%'>$Lang::tr{'remark'}</th>
+                               <th align='center' width='5%'>$Lang::tr{'delete'}</th>
+                       </tr>
+END
+
+       &General::readhasharray($clients, \%clientshash) if (-e $clients);
+       foreach my $key (keys %clientshash) {
+               #calculate time from clientshash (starttime)
+               my $starttime = sub{sprintf '%02d.%02d.%04d %02d:%02d', $_[3], $_[4]+1, $_[5]+1900, $_[2], $_[1]  }->(localtime($clientshash{$key}[2]));
+
+               #calculate endtime from clientshash
+               my $endtime;
+               if ($clientshash{$key}[3] eq '0'){
+                       $endtime=$Lang::tr{'Captive nolimit'};
+               } else {
+                       $endtime = sub{sprintf '%02d.%02d.%04d %02d:%02d', $_[3], $_[4]+1, $_[5]+1900, $_[2], $_[1]  }->(localtime($clientshash{$key}[2]+$clientshash{$key}[3]));
+               }
+
+               if ($count++ % 2) {
+                       $col="bgcolor='$color{'color20'}'";
+               } else {
+                       $col="bgcolor='$color{'color22'}'";
+               }
+
+               my $coupon = ($clientshash{$key}[4] eq "LICENSE") ? $Lang::tr{'Captive terms short'} : $clientshash{$key}[4];
+
+               print <<END;
+                       <tr>
+                               <td $col align="center"><b>$coupon</b></td>
+                               <td $col align="center">$starttime</td>
+                               <td $col align="center">$endtime</td>
+                               <td $col align="center">$clientshash{$key}[0]</td>
+                               <td $col align="center">$clientshash{$key}[5]</td>
+                               <td $col align="center">
+                                       <form method='post'>
+                                               <input type='image' src='/images/delete.gif' align='middle' alt='$Lang::tr{'delete'}' title='$Lang::tr{'delete'}' />
+                                               <input type='hidden' name='ACTION' value='delete-client' />
+                                               <input type='hidden' name='key' value='$clientshash{$key}[0]' />
+                                       </form>
+                               </td>
+                       </tr>
+END
+       }
+
+       print "</table>";
+
+       &Header::closebox();
+}
+
+sub validremark
+{
+       # Checks a hostname against RFC1035
+        my $remark = $_[0];
+       # Each part should be at least two characters in length
+       # but no more than 63 characters
+       if (length ($remark) < 1 || length ($remark) > 255) {
+               return 0;}
+       # Only valid characters are a-z, A-Z, 0-9 and -
+       if ($remark !~ /^[a-zäöüA-ZÖÄÜ0-9-.:;\|_()\/\s]*$/) {
+               return 0;}
+       # First character can only be a letter or a digit
+       if (substr ($remark, 0, 1) !~ /^[a-zäöüA-ZÖÄÜ0-9]*$/) {
+               return 0;}
+       # Last character can only be a letter or a digit
+       if (substr ($remark, -1, 1) !~ /^[a-zöäüA-ZÖÄÜ0-9.:;_)]*$/) {
+               return 0;}
+       return 1;
+}
+
+&Header::closebigbox();
+&Header::closepage();
diff --git a/html/cgi-bin/captive/index.cgi b/html/cgi-bin/captive/index.cgi
new file mode 100755 (executable)
index 0000000..e9606b1
--- /dev/null
@@ -0,0 +1,238 @@
+#!/usr/bin/perl
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2016  Alexander Marx alexander.marx@ipfire.org                #
+#                                                                             #
+# 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 <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+use strict;
+use CGI ':standard';
+use URI::Escape;
+use HTML::Entities();
+use HTML::Template;
+
+# enable only the following on debugging purpose
+#use warnings;
+#use CGI::Carp 'fatalsToBrowser';
+
+require '/var/ipfire/general-functions.pl';
+require "${General::swroot}/lang.pl";
+
+# Load the most appropriate language from the browser configuration
+my @langs = &Lang::DetectBrowserLanguages();
+&Lang::reload(@langs);
+
+my $coupons = "${General::swroot}/captive/coupons";
+my %couponhash = ();
+
+my %clientshash=();
+my %cgiparams=();
+my %settings=();
+my $clients="${General::swroot}/captive/clients";
+my $settingsfile="${General::swroot}/captive/settings";
+my $errormessage;
+my $url=param('redirect');
+
+#Create /var/ipfire/captive/clients if not exist
+unless (-f $clients){ system("touch $clients"); }
+
+#Get GUI variables
+&getcgihash(\%cgiparams);
+
+#Read settings
+&General::readhash("$settingsfile", \%settings) if(-f $settingsfile);
+
+# Actions
+if ($cgiparams{'ACTION'} eq "SUBMIT") {
+       # Get client IP address
+       my $ip_address = $ENV{X_FORWARDED_FOR} || $ENV{REMOTE_ADDR};
+
+       # Retrieve the MAC address from the ARP table
+       my $mac_address = &Network::get_hardware_address($ip_address);
+
+       &General::readhasharray("$clients", \%clientshash);
+       my $key = &General::findhasharraykey(\%clientshash);
+
+       # Create a new client line
+       foreach my $i (0 .. 5) { $clientshash{$key}[$i] = ""; }
+
+       # MAC address of the client
+       $clientshash{$key}[0] = $mac_address;
+
+       # IP address of the client
+       $clientshash{$key}[1] = $ip_address;
+
+       # Current time
+       $clientshash{$key}[2] = time();
+
+       if ($settings{"AUTH"} eq "COUPON") {
+               &General::readhasharray($coupons, \%couponhash);
+
+               if ($cgiparams{'COUPON'}) {
+                       # Convert coupon input to uppercase
+                       $cgiparams{'COUPON'} = uc $cgiparams{'COUPON'};
+
+                       # Walk through all valid coupons and find the right one
+                       my $found = 0;
+                       foreach my $coupon (keys %couponhash) {
+                               if ($couponhash{$coupon}[1] eq $cgiparams{'COUPON'}) {
+                                       $found = 1;
+
+                                       # Copy expiry time
+                                       $clientshash{$key}[3] = $couponhash{$coupon}[2];
+
+                                       # Save coupon code
+                                       $clientshash{$key}[4] = $cgiparams{'COUPON'};
+
+                                       # Copy coupon remark
+                                       $clientshash{$key}[5] = $couponhash{$coupon}[3];
+
+                                       # Delete used coupon
+                                       delete $couponhash{$coupon};
+                                       &General::writehasharray($coupons, \%couponhash);
+
+                                       last;
+                               }
+                       }
+
+                       if ($found == 1) {
+                               &General::log("Captive", "Internet access granted via coupon ($clientshash{$key}[4]) for $ip_address until $clientshash{$key}[3]");
+                       } else {
+                               $errormessage = $Lang::tr{"Captive invalid coupon"};
+                       }
+
+               # No coupon given
+               } else {
+                       $errormessage = $Lang::tr{"Captive please enter a coupon code"};
+               }
+
+       # Terms
+       } else {
+               # Make sure that they have been accepted
+               if ($cgiparams{'TERMS'} eq "on") {
+                       # Copy session expiry time
+                       $clientshash{$key}[3] = $settings{'SESSION_TIME'} || "0";
+
+                       # No coupon code
+                       $clientshash{$key}[4] = "TERMS";
+
+                       &General::log("Captive", "Internet access granted via license agreement for $ip_address until $clientshash{$key}[3]");
+
+               # The terms have not been accepted
+               } else {
+                       $errormessage = $Lang::tr{'Captive please accept the terms and conditions'};
+               }
+       }
+
+       # If no errors were found, save configruation and reload
+       if (!$errormessage) {
+               &General::writehasharray("$clients", \%clientshash);
+
+               system("/usr/local/bin/captivectrl");
+
+               # Redirect client to the original URL
+               print "Status: 302 Moved Temporarily\n";
+               print "Location: $url\n";
+               print "Connection: close\n\n";
+               exit 0;
+       }
+}
+
+my $tmpl = HTML::Template->new(
+       filename => "/srv/web/ipfire/html/captive/template.html",
+       die_on_bad_params => 0
+);
+
+$tmpl->param(REDIRECT => $url);
+
+# Coupon
+if ($settings{'AUTH'} eq "COUPON") {
+       $tmpl->param(COUPON => 1);
+       $tmpl->param(L_HEADING => $Lang::tr{'Captive coupon'});
+} else {
+       $tmpl->param(L_HEADING => $Lang::tr{'Captive terms'});
+}
+
+$tmpl->param(TITLE => $settings{'TITLE'});
+$tmpl->param(COLOR => $settings{'COLOR'});
+$tmpl->param(ERROR => $errormessage);
+
+$tmpl->param(TERMS => &getterms());
+
+# Some translated strings
+$tmpl->param(L_ACTIVATE        => $Lang::tr{'Captive ACTIVATE'});
+$tmpl->param(L_GAIN_ACCESS     => $Lang::tr{'Captive GAIN ACCESS'});
+$tmpl->param(L_AGREE_TERMS     => $Lang::tr{'Captive agree tac'});
+
+# Print header
+print "Pragma: no-cache\n";
+print "Cache-control: no-cache\n";
+print "Connection: close\n";
+print "Content-type: text/html\n\n";
+
+# Print rendered template
+print $tmpl->output();
+
+sub getcgihash {
+       my ($hash, $params) = @_;
+       my $cgi = CGI->new ();
+       $hash->{'__CGI__'} = $cgi;
+       return if ($ENV{'REQUEST_METHOD'} ne 'POST');
+       if (!$params->{'wantfile'}) {
+               $CGI::DISABLE_UPLOADS = 1;
+               $CGI::POST_MAX        = 1024 * 1024;
+       } else {
+               $CGI::POST_MAX = 10 * 1024 * 1024;
+       }
+       $cgi->referer() =~ m/^http?\:\/\/([^\/]+)/;
+       my $referer = $1;
+       $cgi->url() =~ m/^http?\:\/\/([^\/]+)/;
+       my $servername = $1;
+       return if ($referer ne $servername);
+
+       ### Modified for getting multi-vars, split by |
+       my %temp = $cgi->Vars();
+        foreach my $key (keys %temp) {
+               $hash->{$key} = $temp{$key};
+               $hash->{$key} =~ s/\0/|/g;
+               $hash->{$key} =~ s/^\s*(.*?)\s*$/$1/;
+        }
+
+       if (($params->{'wantfile'})&&($params->{'filevar'})) {
+               $hash->{$params->{'filevar'}} = $cgi->upload
+                                               ($params->{'filevar'});
+       }
+       return;
+}
+
+sub getterms() {
+       my @terms = ();
+
+       open(my $handle, "<:utf8", "/var/ipfire/captive/terms.txt");
+       while(<$handle>) {
+               $_ = HTML::Entities::decode_entities($_);
+               push(@terms, $_);
+       }
+       close($handle);
+
+       my $terms = join("\n", @terms);
+
+       # Format paragraphs
+       $terms =~ s/\n\n/<\/p>\n<p>/g;
+
+       return $terms;
+}
diff --git a/html/cgi-bin/captive/logo.cgi b/html/cgi-bin/captive/logo.cgi
new file mode 100644 (file)
index 0000000..8f292b1
--- /dev/null
@@ -0,0 +1,44 @@
+#!/usr/bin/perl
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2016  Alexander Marx alexander.marx@ipfire.org                #
+#                                                                             #
+# 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 <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+use strict;
+use CGI;
+use File::Copy;
+
+# enable only the following on debugging purpose
+#use warnings;
+#use CGI::Carp 'fatalsToBrowser';
+
+require '/var/ipfire/general-functions.pl';
+
+my $logo = "${General::swroot}/captive/logo.dat";
+
+# Send 404 if logo was not uploaded and exit
+if (!-e $logo) {
+       print CGI::header(status => 404);
+       exit(0);
+}
+
+print "Content-Type: application/octet-stream\n\n";
+
+# Send image data
+File::Copy::copy $logo, \*STDOUT;
+exit(0);
diff --git a/html/cgi-bin/captive/redirect.cgi b/html/cgi-bin/captive/redirect.cgi
new file mode 100755 (executable)
index 0000000..7c5b5e7
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/perl
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2016 Alexander Marx alexander.marx@ipfire.org                 #
+#                                                                             #
+# 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 <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+use strict;
+use URI::Escape;
+use CGI::Carp qw(fatalsToBrowser);
+
+require '/var/ipfire/general-functions.pl';
+
+my $url = "http://$ENV{'SERVER_NAME'}$ENV{'REQUEST_URI'}";
+my $safe_url = uri_escape($url);
+
+my %settingshash = ();
+my %ethernethash = ();
+my $target;
+
+# Read settings
+&General::readhash("${General::swroot}/captive/settings", \%settingshash);
+&General::readhash("${General::swroot}/ethernet/settings", \%ethernethash);
+
+# Get the client's IP address
+my $client_address = $ENV{X_FORWARDED_FOR} || $ENV{REMOTE_ADDR} || "";
+
+if ($settingshash{'ENABLE_GREEN'} eq "on" && $ethernethash{'GREEN_ADDRESS'} ne '') {
+       if (&General::IpInSubnet($client_address, $ethernethash{'GREEN_ADDRESS'}, $ethernethash{'GREEN_NETMASK'})) {
+               $target = $ethernethash{'GREEN_ADDRESS'};
+       }
+
+} elsif($settingshash{'ENABLE_BLUE'} eq "on" && $ethernethash{'BLUE_ADDRESS'} ne '') {
+       if (&General::IpInSubnet($client_address, $ethernethash{'BLUE_ADDRESS'}, $ethernethash{'BLUE_NETMASK'})) {
+               $target = $ethernethash{'BLUE_ADDRESS'};
+       }
+
+} else {
+       exit 0;
+}
+
+print "Status: 302 Moved Temporarily\n";
+print "Location: http://$target:1013/cgi-bin/index.cgi?redirect=$safe_url\n";
+print "Connection: close\n\n";
index 5734fed18e7ee87c39c24c98b9679bcd238f8159..f241365a18a4b0fc95834a4ed0970ff459c1d55f 100644 (file)
@@ -51,6 +51,7 @@ $cgiparams{'SECTION'} = 'ipfire';
 my %sections = (
         'auth' => '(\w+\(pam_unix\)\[.*\]: )',
                'wio' => '(wio|wio\[.*\])',
+        'captive' => '(Captive:)',
         'clamav' => '(clamd\[.*\]: |freshclam\[.*\]: )',
         'collectd' => '(collectd\[.*\]: )',
         'cron' => '(fcron\[.*\]: )',
@@ -77,6 +78,7 @@ my %sections = (
 my %trsections = (
         'auth' => "$Lang::tr{'loginlogout'}",
                'wio' => 'Who Is Online?',
+        'captive' => $Lang::tr{'captive'},
         'clamav' => 'ClamAV',
         'collectd' => 'Collectd',
         'cron' => 'Cron',
diff --git a/html/html/captive/assets/captive.css b/html/html/captive/assets/captive.css
new file mode 100644 (file)
index 0000000..6f231bc
--- /dev/null
@@ -0,0 +1,194 @@
+@font-face {
+       font-family: "Ubuntu";
+       font-weight: 300;
+       src: local("Ubuntu Light"), local("Ubuntu-Light"), url("Ubuntu-L.ttf") format("truetype");
+}
+
+@font-face {
+       font-family: "Ubuntu";
+       font-weight: 400;
+       src: local("Ubuntu Regular"), local("Ubuntu-Regular"), url("Ubuntu-R.ttf") format("truetype");
+}
+
+@font-face {
+       font-family: "Ubuntu";
+       font-weight: 500;
+       src: local("Ubuntu Medium"), local("Ubuntu-Medium"), url("Ubuntu-M.ttf") format("truetype");
+}
+
+body {
+       background-image: url("../cgi-bin/logo.cgi");
+       background-size: 100%;
+       background-repeat: no-repeat;
+
+       background-color: #eceff1;
+       color: #263238;
+
+       display: flex;
+       min-height: 100vh;
+       flex-direction: column;
+}
+
+body, input {
+       font-family: "Ubuntu", sans-serif;
+       font-size: 14px;
+}
+
+.content {
+       flex: 1;
+}
+
+.box {
+       margin: 272px 0 48px 0;
+       padding: 0 30px 48px 30px;
+
+       position: relative;
+       display: flex;
+       flex-direction: column;
+       background-color: white;
+
+       border: none;
+       border-radius: 4px;
+       box-shadow:
+               0 19px 38px 0 rgba(0, 0, 0, 0.3),
+               0 15px 12px 0 rgba(0, 0, 0, 0.22);
+}
+
+.box-header {
+       display: flex;
+       align-items: center;
+       justify-content: center;
+       min-height: 128px;
+}
+
+.box-header h1 {
+       font-size: 40px;
+}
+
+@media (min-width: 1200px) {
+       .box-header {
+               min-height: none;
+       }
+
+       .box-header h1 {
+               margin-top: 24px;
+               margin-bottom: 20px;
+       }
+}
+
+.box-block {
+       padding: 24px 16px 24px 16px;
+       margin: 0 -8px 40px -8px;
+
+       text-align: center;
+       background-color: #b71c1c;
+       color: white;
+       border-radius: 4px;
+}
+
+@media (min-width: 1200px) {
+       .box-block {
+               padding: 18px 16px 18px 16px;
+               margin: 0 0 48px 0;
+       }
+}
+
+.box-block h4 {
+       font-size: 20px;
+}
+
+footer {
+       height: 64px;
+       background-color: rgba(84, 110, 122, 0.06); /* #546e7a */
+}
+
+.footer {
+       display: flex;
+       align-items: center;
+}
+
+.footer .logo {
+       width: 64px;
+       height: 64px;
+       padding: 8px;
+}
+
+.form-text {
+       display: inline-block;
+       height: 36px;
+
+       color: #263238;
+       background-color: rgba(38, 49, 56, 0,08);
+
+       border: 0;
+       border-radius: 2px;
+       box-shadow: inset 0 -2px 0 0 #546e7a;
+}
+
+.form-error {
+       box-shadow: inset 0 -2px 0 0 #ff3d00;
+}
+
+.form-submit {
+       display: inline-block;
+
+       font-weight: 500;
+       text-transform: uppercase;
+
+       height: 36px;
+       padding: 0 16px 0 16px;
+       margin: 0 8px 0 8px;
+
+       color: #263238;
+       background-color: white;
+
+       border: none;
+       border-radius: 2px;
+       box-shadow:
+               0 2px 4px 0 rgba(0, 0, 0, 0.16),
+               0 1px 2px 0 rgba(0, 0, 0, 0.23);
+}
+
+.form-submit:hover {
+       box-shadow:
+               0 3px 6px 0 rgba(0, 0, 0, 0.16),
+               0 3px 6px 0 rgba(0, 0, 0, 0.23);
+}
+
+.form-submit:active {
+       background-color: black;
+       opacity: 0.16;
+       box-shadow: none;
+}
+
+.checkbox {
+       position: relative;
+       display: block;
+
+       margin-top: 20px;
+}
+
+.checkbox .form-checkbox {
+       position: absolute;
+       margin-top: 1px;
+       margin-left: -24px;
+
+       width: 20px;
+       height: 20px;
+
+       background-color: rgba(38, 49, 56, 0.08);
+
+       border: 2px solid #546e7a;
+       border-radius: 2px;
+}
+
+.text-error {
+       position: block;
+       margin-top: 6px;
+       min-height: 23px;
+
+       color: white;
+       background-color: #ff3d00;
+
+       border-radius: 2px;
+}
diff --git a/html/html/captive/assets/favicon.ico b/html/html/captive/assets/favicon.ico
new file mode 100644 (file)
index 0000000..52da262
Binary files /dev/null and b/html/html/captive/assets/favicon.ico differ
diff --git a/html/html/captive/assets/ipfire.png b/html/html/captive/assets/ipfire.png
new file mode 100644 (file)
index 0000000..cd84ad5
Binary files /dev/null and b/html/html/captive/assets/ipfire.png differ
diff --git a/html/html/captive/template.html b/html/html/captive/template.html
new file mode 100644 (file)
index 0000000..04be2b1
--- /dev/null
@@ -0,0 +1,79 @@
+<!DOCTYPE HTML>
+<html>
+       <head>
+               <meta charset="UTF-8">
+               <title><TMPL_VAR NAME="TITLE"></title>
+
+               <link rel="stylesheet" href="../assets/bootstrap-reboot.min.css">
+               <link rel="stylesheet" href="../assets/bootstrap-grid.min.css">
+               <link rel="stylesheet" href="../assets/captive.css">
+
+               <TMPL_IF NAME="COLOR">
+                       <style>
+                               .box-block {
+                                       background-color: <TMPL_VAR NAME="COLOR">;
+                               }
+                       </style>
+               </TMPL_IF>
+
+               <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+       </head>
+
+       <body>
+               <div class="container content">
+                       <div class="row">
+                               <div class="col-12 col-lg-10 offset-lg-1 col-xl-8 offset-xl-2">
+                                       <div class="box">
+                                               <div class="box-header">
+                                                       <h1><TMPL_VAR NAME="TITLE"></h1>
+                                               </div>
+
+                                               <div class="box-block">
+                                                       <h4><TMPL_VAR NAME="L_HEADING"></h4>
+
+                                                       <form action="" method="POST">
+                                                               <input type="hidden" name="ACTION" value="SUBMIT">
+                                                               <input type="hidden" name="redirect" value="<TMPL_VAR NAME="REDIRECT">">
+
+                                                               <TMPL_IF NAME="COUPON">
+                                                                       <input class="form-text <TMPL_IF NAME="ERROR">form-error</TMPL_IF>" type="text" name="COUPON">
+                                                                       <input class="form-submit" type="submit"
+                                                                               value="<TMPL_VAR NAME="L_ACTIVATE">">
+                                                               <TMPL_ELSE>
+                                                                       <div class="checkbox">
+                                                                               <label>
+                                                                                       <input class="form-checkbox <TMPL_IF NAME="ERROR">error</TMPL_IF>" type="checkbox" name="TERMS">
+                                                                                       <TMPL_VAR NAME="L_AGREE_TERMS">
+                                                                               </label>
+                                                                       </div>
+
+                                                                       <input class="form-submit" type="submit"
+                                                                               value="<TMPL_VAR NAME="L_GAIN_ACCESS">">
+                                                               </TMPL_IF>
+
+                                                               <TMPL_IF NAME="ERROR">
+                                                                       <div class="text-error">
+                                                                               <TMPL_VAR NAME="ERROR">
+                                                                       </div>
+                                                               </TMPL_IF>
+                                                       </form>
+                                               </div>
+
+                                               <TMPL_IF NAME="TERMS">
+                                                       <TMPL_VAR NAME="TERMS">
+                                               </TMPL_IF>
+                                       </div>
+                               </div>
+                       </div>
+               </div>
+
+               <footer>
+                       <div class="container">
+                               <div class="footer">
+                                       <img class="logo" src="../assets/ipfire.png" alt="IPFire Logo">
+                                       Powered by IPFire
+                               </div>
+                       </div>
+               </footer>
+       </body>
+</html>
index 7bc1c495db57018c128c01ed12278d0ec4b3fec0..af96a6445bc9f99a11322e7543411123f369e48a 100644 (file)
@@ -7,6 +7,54 @@
 'Add Rule' => 'Regel hinzufügen',
 'Add a route' => 'Eine Route hinzufügen',
 'Async logging enabled' => 'Aktiviere asynchrones Schreiben des Syslogs',
+'Captive 1day' => '1 Tag',
+'Captive 1month' => '1 Monat',
+'Captive 1week' => '1 Woche',
+'Captive ACTIVATE' => 'AKTIVIEREN',
+'Captive GAIN ACCESS' => 'ZUGANG',
+'Captive activate' => 'Aktivieren',
+'Captive activated' => 'Aktiviert',
+'Captive active on' => 'Aktiviert auf',
+'Captive agree tac' => 'Bedingungen akzeptieren',
+'Captive auth_lic' => 'Lizenz',
+'Captive auth_vou' => 'Gutschein',
+'Captive authentication' => 'Art der Anmeldung',
+'Captive brand color' => 'Highlight-Farbe',
+'Captive branding' => 'Branding',
+'Captive client session expiry time' => 'Ablaufzeit',
+'Captive clients' => 'Clients',
+'Captive config' => 'Konfiguration',
+'Captive coupon' => 'Coupon',
+'Captive err doublevoucher' => 'Ein Gutschein mit diesem Code ist bereits im Umlauf',
+'Captive expire' => 'Ablauf',
+'Captive expiry time' => 'Ablaufzeit',
+'Captive generate coupon' => 'Coupon generieren',
+'Captive genvoucher' => 'Gutschein generieren',
+'Captive invalid logosize' => 'Die hochgeladene Datei entspricht nicht der vorgegeben Auflösung von mindestens 1280x400 und maximal 1920x800 Pixeln',
+'Captive invalid_voucher' => 'Dieser Code ist ungültig. Bitte versuchen Sie es erneut',
+'Captive ip' => 'IP-Addresse',
+'Captive issued coupons' => 'Ausgestellte Coupons',
+'Captive logo uploaded' => 'Logo hochgeladen',
+'Captive logo_set' => 'Aktuelles Logo',
+'Captive logo_upload' => 'Logo hochladen',
+'Captive logo_upload1' => '(PNG, min. 1280x400, max. 1920x800)',
+'Captive mac' => 'MAC-Adresse',
+'Captive menu' => 'Captive-Portal',
+'Captive noexpiretime' => 'Es wurde kein gültiger Verbindungszeitraum angegeben',
+'Captive nolimit' => 'Unbegrenzt',
+'Captive nr' => 'Nummer',
+'Captive please accept the terms and conditions' => 'Bitte akzeptieren Sie die Bedingungen',
+'Captive terms' => 'Bedingungen',
+'Captive terms short' => 'Bedingungen',
+'Captive time' => 'Erlaubter Nutzungszeitraum nach Aktivierung (Stunden)',
+'Captive title' => 'Titel der Anmeldeseite',
+'Captive upload logo' => 'Logo hochladen',
+'Captive upload logo recommendations' => '(PNG oder JPEG, 1280x720 Pixel empfohlen)',
+'Captive voactive' => 'Aktive Gutscheine',
+'Captive voucher' => 'Gutschein',
+'Captive vouchervalid' => 'Erlaubter Zeitraum für Gutschein',
+'Captive vout' => 'Ausgegebene Gutscheine',
+'Captive wrong ext' => 'Die hochgeladene Datei hat den falschen Dateityp',
 'Choose Rule' => 'Wählen Sie <u>eine</u> der untenstehenden Regeln aus.',
 'Class' => 'Klasse',
 'Class was deleted' => 'wurde mit eventuell vorhandenen Unterklassen gelöscht',
 'edit share' => 'Freigabe bearbeiten',
 'editor' => 'Editor',
 'eg' => 'z.B.:',
+'eight hours' => '8 Stunden',
 'email config' => 'Konfiguration',
 'email empty field' => 'Leeres Feld',
 'email error' => 'FEHLER: Test-E-Mail konnte nicht versendet werden',
index ab1f40180d562e58ca3f02bfebc9bf040fb56408..7e4f95ccfd0c2c73fc3cfbed7c7ab60e4adb9402 100644 (file)
@@ -1,12 +1,61 @@
 %tr = ( 
 %tr,
 
+'24 hours' => '24 Hours',
 'Act as' => 'Act as:',
 'Add Level7 rule' => 'Add Level7 rule',
 'Add Port Rule' => 'Add port rule',
 'Add Rule' => 'Add rule',
 'Add a route' => 'Add a route',
 'Async logging enabled' => 'Enable asynchronous writing of the syslog file',
+'Captive 1day' => '1 day',
+'Captive 1month' => '1 month',
+'Captive 1week' => '1 week',
+'Captive ACTIVATE' => 'ACTIVATE',
+'Captive GAIN ACCESS' => 'GAIN ACCESS',
+'Captive activate' => 'Activate',
+'Captive activated' => 'Activated',
+'Captive active on' => 'Activated on',
+'Captive agree tac' => 'I agree with the terms & conditions below.',
+'Captive auth_lic' => 'License',
+'Captive auth_vou' => 'Voucher',
+'Captive authentication' => 'Type of Access',
+'Captive brand color' => 'Brand Color',
+'Captive branding' => 'Branding',
+'Captive client session expiry time' => 'Session Expiry Time',
+'Captive config' => 'Settings',
+'Captive coupon' => 'Coupon',
+'Captive err doublevoucher' => 'A voucher with this code already exists',
+'Captive expire' => 'Expire',
+'Captive expiry time' => 'Expiry Time',
+'Captive generate coupon' => 'Generate Coupon',
+'Captive heading terms' => 'Terms &amp; Conditions',
+'Captive heading voucher' => 'Voucher or Access Code',
+'Captive invalid coupon' => 'You entered an invalid coupon code. Please try again.',
+'Captive invalid logosize' => 'The uploaded image file does not meet the required resolution of at least 1280x400 but not larger than 1920x800 pixels',
+'Captive invalid_voucher' => 'Invalid code. Please try again',
+'Captive ip' => 'IP Address',
+'Captive issued coupons' => 'Issued Coupons',
+'Captive logo uploaded' => 'Logo uploaded',
+'Captive logo_set' => 'Current Logo',
+'Captive mac' => 'MAC Address',
+'Captive menu' => 'Captive Portal',
+'Captive noexpiretime' => 'No valid connection time range given',
+'Captive nolimit' => 'unlimited',
+'Captive nr' => 'Number',
+'Captive please accept the terms and conditions' => 'Please accept the terms &amp; conditions',
+'Captive please enter a coupon code' => 'Please enter a coupon code',
+'Captive terms' => 'Terms &amp; Conditions',
+'Captive terms short' => 'T&Cs',
+'Captive time' => 'Access time post activation (hours)',
+'Captive title' => 'Title of Login Page',
+'Captive upload logo' => 'Upload Logo',
+'Captive upload logo recommendations' => '(PNG or JPEG, recommended 1280x720 pixels)',
+'Captive voactive' => 'Active Vouchers',
+'Captive voucher' => 'Voucher',
+'Captive vouchervalid' => 'Allowed time for this voucher',
+'Captive vout' => 'Issued Vouchers',
+'Captive wrong ext' => 'Uploaded file has wrong filetype',
 'Choose Rule' => 'Choose <u>one</u> of the following rules.',
 'Class' => 'Class',
 'Class was deleted' => 'with potential subclasses was deleted',
 'day after' => 'Day after',
 'day before' => 'Day before',
 'day-graph' => 'Day',
-'days' => 'days.',
+'days' => 'Days',
 'dbfile' => 'Dbfile',
 'ddns help dnsmadeeasy' => 'In the field for hostname enter your ID (or a list of IDs seperated by;)',
 'ddns help freedns' => 'In the fied username enter your connect string',
 'edit share' => 'Edit share',
 'editor' => 'Editor',
 'eg' => 'e.g.:',
+'eight hours' => '8 Hours',
 'email config' => 'Configuration',
 'email empty field' => 'Empty field',
 'email error' => 'ERROR: Test mail could not be sent',
 'hosts config changed' => 'Hosts config changed',
 'hour' => 'Hour',
 'hour-graph' => 'Hour',
-'hours' => 'hours',
+'hours' => 'Hours',
 'hours2' => 'Hours',
 'ibod for dual isdn only' => 'iBOD can only be used with Dual ISDN.',
 'icmp selected but no type' => 'ICMP selected for protocol, but no ICMP type specified.',
 'monthly volume' => 'Monthly volume',
 'monthly volume start day' => 'First day of monthly period',
 'monthly volume start day short' => 'First day',
-'months' => 'months',
+'months' => 'Months',
 'more' => 'more',
 'most preferred' => 'most preferred',
 'mount' => 'Mount',
 'ok' => 'OK',
 'older' => 'Older',
 'on' => 'on',
+'one hour' => 'One Hour',
+'one month' => 'One Month',
+'one week' => 'One Week',
 'online help en' => 'Online help (in english)',
 'only digits allowed in holdoff field' => 'Only digits allowed in holdoff field',
 'only digits allowed in max retries field' => 'Only digits allowed in max retries field.',
 'unix password sync' => 'Unix Password Sync',
 'unix shell' => 'UNIX Shell',
 'unknown' => 'UNKNOWN',
+'unlimited' => 'Unlimited',
 'unnamed' => 'Unnamed',
 'update' => 'Update',
 'update accelerator' => 'Update Accelerator',
 'week' => 'Week',
 'week-graph' => 'Week',
 'weekly firewallhits' => 'weekly firewallhits',
-'weeks' => 'weeks',
+'weeks' => 'Weeks',
 'wildcards' => 'Wildcards',
 'wins server' => 'Wins Server',
 'wins support' => 'Wins Support',
index b7a741614c8f47a798ea162bc04b7b2ddc2ae99d..138ede8de3121f10ceb36872514a5caa2612d3fd 100644 (file)
@@ -117,5 +117,8 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
        cp -rf $(DIR_CONF)/httpd/* /etc/httpd/conf
        ln -sf $(CONFIG_ROOT)/main/hostname.conf /etc/httpd/conf/
 
+       # Create captive logging directory
+       -mkdir -pv /var/log/httpd/captive
+
        @rm -rf $(DIR_APP)
        @$(POSTBUILD)
diff --git a/lfs/bootstrap b/lfs/bootstrap
new file mode 100644 (file)
index 0000000..15e882e
--- /dev/null
@@ -0,0 +1,79 @@
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2007-2016 IPFire Team  <info@ipfire.org>                      #
+#                                                                             #
+# 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 <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+###############################################################################
+# Definitions
+###############################################################################
+
+include Config
+
+VER        = 4.0.0-alpha.6
+
+THISAPP    = bootstrap-$(VER)
+DL_FILE    = $(THISAPP)-dist.zip
+DL_FROM    = $(URL_IPFIRE)
+DIR_APP    = $(DIR_SRC)/$(THISAPP)-dist
+TARGET     = $(DIR_INFO)/$(THISAPP)
+
+###############################################################################
+# Top-level Rules
+###############################################################################
+
+objects = $(DL_FILE)
+
+$(DL_FILE) = $(DL_FROM)/$(DL_FILE)
+
+$(DL_FILE)_MD5 = 7fed4691d4d7ca19820009dad86e1aef
+
+install : $(TARGET)
+
+check : $(patsubst %,$(DIR_CHK)/%,$(objects))
+
+download :$(patsubst %,$(DIR_DL)/%,$(objects))
+
+md5 : $(subst %,%_MD5,$(objects))
+
+###############################################################################
+# Downloading, checking, md5sum
+###############################################################################
+
+$(patsubst %,$(DIR_CHK)/%,$(objects)) :
+       @$(CHECK)
+
+$(patsubst %,$(DIR_DL)/%,$(objects)) :
+       @$(LOAD)
+
+$(subst %,%_MD5,$(objects)) :
+       @$(MD5)
+
+###############################################################################
+# Installation Details
+###############################################################################
+
+$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
+       @$(PREBUILD)
+       @rm -rf $(DIR_APP) && cd $(DIR_SRC) && unzip $(DIR_DL)/$(DL_FILE)
+
+       -mkdir -pv /usr/share/bootstrap/{css,js}
+       cd $(DIR_APP) && cp -vf css/* /usr/share/bootstrap/css
+       cd $(DIR_APP) && cp -vf js/*  /usr/share/bootstrap/js
+
+       @rm -rf $(DIR_APP)
+       @$(POSTBUILD)
index 760505e2c2e29f4988fd818925dcf07de40b7a93..ad02996cfd036d376ac016b2048a783125b29b16 100644 (file)
@@ -50,7 +50,7 @@ $(TARGET) :
        @$(PREBUILD)
 
        # Create all directories
-       for i in addon-lang auth backup ca certs connscheduler crls ddns dhcp dhcpc dns dnsforward \
+       for i in addon-lang auth backup ca captive certs connscheduler crls ddns dhcp dhcpc dns dnsforward \
                        ethernet extrahd/bin fwlogs fwhosts firewall isdn key langs logging mac main \
                        menu.d modem nfs optionsfw \
                        ovpn patches pakfire portfw ppp private proxy/advanced/cre \
@@ -62,7 +62,7 @@ $(TARGET) :
 
        # Touch empty files
        for i in auth/users backup/include.user backup/exclude.user \
-           certs/index.txt ddns/config ddns/noipsettings ddns/settings ddns/ipcache dhcp/settings \
+           captive/settings captive/agb.txt captive/clients captive/voucher_out certs/index.txt ddns/config ddns/noipsettings ddns/settings ddns/ipcache dhcp/settings \
            dhcp/fixleases dhcp/advoptions dhcp/dhcpd.conf.local dns/settings dnsforward/config ethernet/aliases ethernet/settings ethernet/known_nics ethernet/scanned_nics \
            ethernet/wireless extrahd/scan extrahd/devices extrahd/partitions extrahd/settings firewall/settings firewall/config firewall/geoipblock firewall/input firewall/outgoing \
            fwhosts/customnetworks fwhosts/customhosts fwhosts/customgroups fwhosts/customservicegrp fwhosts/customgeoipgrp fwlogs/ipsettings fwlogs/portsettings \
index b70bde78a4e273bc37444d3fc1aea2b986657470..0a5ac65c09a3f641d2f549751b8b8b07220bc61b 100644 (file)
@@ -107,6 +107,7 @@ endif
        # Move script to correct place.
        mv -vf /usr/local/bin/ovpn-ccd-convert /usr/sbin/
        mv -vf /usr/local/bin/ovpn-collectd-convert /usr/sbin/
+       mv -vf /usr/local/bin/captive-cleanup /usr/bin/
        
        # Install firewall scripts.
        mkdir -pv /usr/lib/firewall
diff --git a/lfs/ubuntu-font-family b/lfs/ubuntu-font-family
new file mode 100644 (file)
index 0000000..f817b85
--- /dev/null
@@ -0,0 +1,78 @@
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2007-2017 IPFire Team  <info@ipfire.org>                      #
+#                                                                             #
+# 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 <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+###############################################################################
+# Definitions
+###############################################################################
+
+include Config
+
+VER        = 0.83
+
+THISAPP    = ubuntu-font-family-$(VER)
+DL_FILE    = $(THISAPP).zip
+DL_FROM    = $(URL_IPFIRE)
+DIR_APP    = $(DIR_SRC)/$(THISAPP)
+TARGET     = $(DIR_INFO)/$(THISAPP)
+
+###############################################################################
+# Top-level Rules
+###############################################################################
+
+objects = $(DL_FILE)
+
+$(DL_FILE) = $(DL_FROM)/$(DL_FILE)
+
+$(DL_FILE)_MD5 = a24b8136b8f3bb93f166baf97d9328de
+
+install : $(TARGET)
+
+check : $(patsubst %,$(DIR_CHK)/%,$(objects))
+
+download :$(patsubst %,$(DIR_DL)/%,$(objects))
+
+md5 : $(subst %,%_MD5,$(objects))
+
+###############################################################################
+# Downloading, checking, md5sum
+###############################################################################
+
+$(patsubst %,$(DIR_CHK)/%,$(objects)) :
+       @$(CHECK)
+
+$(patsubst %,$(DIR_DL)/%,$(objects)) :
+       @$(LOAD)
+
+$(subst %,%_MD5,$(objects)) :
+       @$(MD5)
+
+###############################################################################
+# Installation Details
+###############################################################################
+
+$(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
+       @$(PREBUILD)
+       @rm -rf $(DIR_APP) && cd $(DIR_SRC) && unzip $(DIR_DL)/$(DL_FILE)
+
+       -mkdir -pv /usr/share/fonts
+       cd $(DIR_APP) && cp -vf *.ttf /usr/share/fonts/
+
+       @rm -rf $(DIR_APP)
+       @$(POSTBUILD)
index 919acbe76d52eabab6da92c627b6fbca0da8aec6..3e9eb9aba63f18aab1c411a93cef1f372c86e99a 100644 (file)
@@ -68,5 +68,24 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects))
        ln -svf ipfire /srv/web/ipfire/html/themes/ipfire-rounded
 
        # Reset permissions of redirect templates and theme directories
-       find /srv/web/ipfire/html/{redirect-templates,themes} -type d | xargs chmod -v 755
+       find /srv/web/ipfire/html/{captive,redirect-templates,themes} -type d | xargs chmod -v 755
+
+       # Captive Portal CSS
+       ln -svf --relative /usr/share/bootstrap/css/bootstrap-grid.min.css \
+               /srv/web/ipfire/html/captive/assets/bootstrap-grid.min.css
+       ln -svf --relative /usr/share/bootstrap/css/bootstrap-grid.min.css.map \
+               /srv/web/ipfire/html/captive/assets/bootstrap-grid.min.css.map
+       ln -svf --relative /usr/share/bootstrap/css/bootstrap-reboot.min.css \
+               /srv/web/ipfire/html/captive/assets/bootstrap-reboot.min.css
+       ln -svf --relative /usr/share/bootstrap/css/bootstrap-reboot.min.css.map \
+               /srv/web/ipfire/html/captive/assets/bootstrap-reboot.min.css.map
+
+       # Captive Portal Fonts
+       ln -svf --relative /usr/share/fonts/Ubuntu-L.ttf \
+               /srv/web/ipfire/html/captive/assets/Ubuntu-L.ttf
+       ln -svf --relative /usr/share/fonts/Ubuntu-M.ttf \
+               /srv/web/ipfire/html/captive/assets/Ubuntu-M.ttf
+       ln -svf --relative /usr/share/fonts/Ubuntu-R.ttf \
+               /srv/web/ipfire/html/captive/assets/Ubuntu-R.ttf
+
        @$(POSTBUILD)
diff --git a/make.sh b/make.sh
index 815b25aca71f6d9a46ed61097b367ecee9068e9d..790c6b81f025d550ad1e39c28276f3e2621fb9ed 100755 (executable)
--- a/make.sh
+++ b/make.sh
@@ -548,6 +548,7 @@ buildipfire() {
   lfsmake2 web-user-interface
   lfsmake2 flag-icons
   lfsmake2 jquery
+  lfsmake2 bootstrap
   lfsmake2 arping
   lfsmake2 beep
   lfsmake2 dvdrtools
@@ -626,6 +627,7 @@ buildipfire() {
   lfsmake2 openssh
   lfsmake2 fontconfig
   lfsmake2 dejavu-fonts-ttf
+  lfsmake2 ubuntu-font-family
   lfsmake2 freefont
   lfsmake2 pixman
   lfsmake2 cairo
index 7bdb292f7ddb8a2160377a45180fe58de40b93dc..c4d2fefe41d3308823b4250d15e26c998db56db3 100644 (file)
@@ -224,6 +224,13 @@ iptables_init() {
                iptables -A ${i} -j LOOPBACK
        done
 
+       # Captive portal
+       iptables -N CAPTIVE_PORTAL
+       iptables -N CAPTIVE_PORTAL_CLIENTS
+       for i in INPUT FORWARD; do
+               iptables -A ${i} -j CAPTIVE_PORTAL
+       done
+
        # Accept everything connected
        for i in INPUT FORWARD OUTPUT; do
                iptables -A ${i} -j CONNTRACK
@@ -337,6 +344,10 @@ iptables_init() {
        iptables -N UPNPFW
        iptables -A FORWARD -m conntrack --ctstate NEW -j UPNPFW
 
+       # Captive Portal
+       iptables -t nat -N CAPTIVE_PORTAL
+       iptables -t nat -A PREROUTING -j CAPTIVE_PORTAL
+
        # RED chain, used for the red interface
        iptables -N REDINPUT
        iptables -A INPUT -j REDINPUT
index 7cc70b48211c66002d06934daf11a4f09218074f..c3329b13047e8f3e300399f5067caa23dc9e0e5f 100644 (file)
@@ -31,7 +31,8 @@ SUID_PROGS = squidctrl sshctrl ipfirereboot \
        redctrl syslogdctrl extrahdctrl sambactrl upnpctrl \
        smartctrl clamavctrl addonctrl pakfire mpfirectrl wlanapctrl \
        setaliases urlfilterctrl updxlratorctrl fireinfoctrl rebuildroutes \
-       getconntracktable wirelessclient torctrl ddnsctrl unboundctrl
+       getconntracktable wirelessclient torctrl ddnsctrl unboundctrl \
+       captivectrl
 SUID_UPDX = updxsetperms
 
 OBJS = $(patsubst %,%.o,$(PROGS) $(SUID_PROGS))
diff --git a/src/misc-progs/captivectrl.c b/src/misc-progs/captivectrl.c
new file mode 100644 (file)
index 0000000..6b68f97
--- /dev/null
@@ -0,0 +1,369 @@
+/* This file is part of the IPFire Firewall.
+*
+* This program is distributed under the terms of the GNU General Public
+* Licence.  See the file COPYING for details. */
+
+#define _BSD_SOURCE
+#define _XOPEN_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "libsmooth.h"
+#include "setuid.h"
+
+#define CAPTIVE_PORTAL_SETTINGS                CONFIG_ROOT "/captive/settings"
+#define ETHERNET_SETTINGS              CONFIG_ROOT "/ethernet/settings"
+
+#define CLIENTS                                CONFIG_ROOT "/captive/clients"
+#define IPTABLES                       "/sbin/iptables --wait"
+#define HTTP_PORT                      80
+#define REDIRECT_PORT                  1013
+
+typedef struct client {
+       char etheraddr[STRING_SIZE];
+       char ipaddr[STRING_SIZE];
+       time_t time_start;
+       int expires;
+
+       struct client* next;
+} client_t;
+
+static time_t parse_time(const char* s) {
+       int t = 0;
+
+       if (sscanf(s, "%d", &t) == 1) {
+               return (time_t)t;
+       }
+
+       return -1;
+}
+
+static char* format_time(const time_t* t) {
+       char buffer[STRING_SIZE];
+
+       struct tm* tm = gmtime(t);
+       if (tm == NULL)
+               return NULL;
+
+       strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M", tm);
+
+       return strdup(buffer);
+}
+
+static client_t* read_clients(char* filename) {
+       FILE* f = NULL;
+
+       if (!(f = fopen(filename, "r"))) {
+               fprintf(stderr, "Could not open configuration file: %s\n", filename);
+               return NULL;;
+       }
+
+       char line[STRING_SIZE];
+
+       client_t* client_first = NULL;
+       client_t* client_last = NULL;
+       client_t* client_curr;
+
+       while ((fgets(line, STRING_SIZE, f) != NULL)) {
+               if (line[strlen(line) - 1] == '\n')
+                       line[strlen(line) - 1] = '\0';
+
+               // Skip all commented lines
+               if (*line == '#')
+                       continue;
+
+               client_curr = (client_t*)malloc(sizeof(client_t));
+               memset(client_curr, 0, sizeof(client_t));
+
+               if (client_first == NULL)
+                       client_first = client_curr;
+               else
+                       client_last->next = client_curr;
+               client_last = client_curr;
+
+               unsigned int count = 0;
+               char* lineptr = line;
+               while (1) {
+                       if (!*lineptr)
+                               break;
+
+                       char* word = lineptr;
+                       while (*lineptr != '\0') {
+                               if (*lineptr == ',') {
+                                       *lineptr = '\0';
+                                       lineptr++;
+                                       break;
+                               }
+                               lineptr++;
+                       }
+
+                       switch (count++) {
+                               // Ethernet address
+                               case 1:
+                                       strcpy(client_curr->etheraddr, word);
+                                       break;
+
+                               // IP address
+                               case 2:
+                                       strcpy(client_curr->ipaddr, word);
+                                       break;
+
+                               // Start time
+                               case 3:
+                                       client_curr->time_start = parse_time(word);
+                                       break;
+
+                               // Expire duration
+                               case 4:
+                                       client_curr->expires = atoi(word);
+                                       break;
+
+                               default:
+                                       break;
+                       }
+               }
+       }
+
+       if (f)
+               fclose(f);
+
+       return client_first;
+}
+
+static void flush_chains() {
+       // filter
+       safe_system(IPTABLES " -F CAPTIVE_PORTAL");
+       safe_system(IPTABLES " -F CAPTIVE_PORTAL_CLIENTS");
+
+       // nat
+       safe_system(IPTABLES " -t nat -F CAPTIVE_PORTAL");
+}
+
+static int setup_dns_filters() {
+       const char* protos[] = { "udp", "tcp", NULL };
+
+       // Limits the number of DNS requests to 3 kByte/s
+       // A burst of 1MB is permitted at the start
+       const char* limiter = "-m hashlimit --hashlimit-name dns-filter"
+               " --hashlimit-mode srcip --hashlimit-upto 3kb/sec --hashlimit-burst 1024kb";
+
+       char command[STRING_SIZE];
+
+       const char** proto = protos;
+       while (*proto) {
+               snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL_CLIENTS -p %s"
+                       " --dport 53 %s -j RETURN", *proto, limiter);
+
+               int r = safe_system(command);
+               if (r)
+                       return r;
+
+               proto++;
+       }
+
+       return 0;
+}
+
+static int add_client_rules(const client_t* clients) {
+       char command[STRING_SIZE];
+       char match[STRING_SIZE];
+
+       while (clients) {
+               size_t len = 0;
+
+               if (*clients->ipaddr && clients->expires > 0) {
+                       len += snprintf(match + len, sizeof(match) - len,
+                               "-s %s", clients->ipaddr);
+               }
+
+               len += snprintf(match + len, sizeof(match) - len,
+                       " -m mac --mac-source %s", clients->etheraddr);
+
+               if (clients->expires > 0) {
+                       time_t expires = clients->time_start + clients->expires;
+
+                       char* time_start = format_time(&clients->time_start);
+                       char* time_end = format_time(&expires);
+
+                       len += snprintf(match + len, sizeof(match) - len,
+                               " -m time --datestart %s --datestop %s",
+                               time_start, time_end);
+
+                       free(time_start);
+                       free(time_end);
+               }
+
+               // filter
+               snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL_CLIENTS"
+                       " %s -j RETURN", match);
+               safe_system(command);
+
+               // nat
+               snprintf(command, sizeof(command), IPTABLES " -t nat -A CAPTIVE_PORTAL"
+                       " %s -j RETURN", match);
+               safe_system(command);
+
+               // Move on to the next client
+               clients = clients->next;
+       }
+
+       return 0;
+}
+
+static char* get_key(struct keyvalue* settings, char* key) {
+       char value[STRING_SIZE];
+
+       if (!findkey(settings, key, value))
+               return NULL;
+
+       return strdup(value);
+}
+
+static int add_interface_rule(const char* intf, int allow_webif_access) {
+       int r;
+       char command[STRING_SIZE];
+
+       if ((intf == NULL) || (strlen(intf) == 0)) {
+               fprintf(stderr, "Empty interface given\n");
+               return -1;
+       }
+
+       snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL -i %s"
+               " -j CAPTIVE_PORTAL_CLIENTS", intf);
+       r = safe_system(command);
+       if (r)
+               return r;
+
+#if 0
+       snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL -o %s"
+               " -j CAPTIVE_PORTAL_CLIENTS", intf);
+       r = safe_system(command);
+       if (r)
+               return r;
+#endif
+
+       if (allow_webif_access) {
+               snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL_CLIENTS"
+                       " -i %s -p tcp --dport 444 -j RETURN", intf);
+               r = safe_system(command);
+               if (r)
+                       return r;
+       }
+
+       // Redirect all unauthenticated clients
+       snprintf(command, sizeof(command), IPTABLES " -t nat -A CAPTIVE_PORTAL -i %s"
+               " -p tcp --dport %d -j REDIRECT --to-ports %d", intf, HTTP_PORT, REDIRECT_PORT);
+       r = safe_system(command);
+       if (r)
+               return r;
+
+       // Allow access to captive portal site
+       snprintf(command, sizeof(command), IPTABLES " -A CAPTIVE_PORTAL_CLIENTS"
+               " -i %s -p tcp --dport %d -j RETURN", intf, REDIRECT_PORT);
+       r = safe_system(command);
+       if (r)
+               return r;
+
+       return 0;
+}
+
+static int add_interface_rules(struct keyvalue* captive_portal_settings, struct keyvalue* ethernet_settings) {
+       const char* intf;
+       char* setting;
+       int r = 0;
+
+       setting = get_key(captive_portal_settings, "ENABLE_GREEN");
+       if (setting && (strcmp(setting, "on") == 0)) {
+               free(setting);
+
+               intf = get_key(ethernet_settings, "GREEN_DEV");
+               r = add_interface_rule(intf, /* allow webif access from green */ 1);
+               if (r)
+                       return r;
+       }
+
+       setting = get_key(captive_portal_settings, "ENABLE_BLUE");
+       if (setting && (strcmp(setting, "on") == 0)) {
+               free(setting);
+
+               intf = get_key(ethernet_settings, "BLUE_DEV");
+               r = add_interface_rule(intf, /* do not allow webif access */ 0);
+               if (r)
+                       return r;
+       }
+
+       // Always pass DNS packets through all firewall rules
+       r = setup_dns_filters();
+       if (r)
+               return r;
+
+       // Add the last rule
+       r = safe_system(IPTABLES " -A CAPTIVE_PORTAL_CLIENTS -j DROP");
+       if (r)
+               return r;
+
+       return r;
+}
+
+int main(int argc, char** argv) {
+       int r = 0;
+       char* intf = NULL;
+       client_t* clients = NULL;
+
+       struct keyvalue* captive_portal_settings = NULL;
+       struct keyvalue* ethernet_settings = NULL;
+
+       if (!(initsetuid()))
+               exit(2);
+
+       ethernet_settings = initkeyvalues();
+       if (!readkeyvalues(ethernet_settings, ETHERNET_SETTINGS)) {
+               fprintf(stderr, "Could not read %s\n", ETHERNET_SETTINGS);
+               r = 1;
+               goto END;
+       }
+
+       captive_portal_settings = initkeyvalues();
+       if (!readkeyvalues(captive_portal_settings, CAPTIVE_PORTAL_SETTINGS)) {
+               fprintf(stderr, "Could not read %s\n", CAPTIVE_PORTAL_SETTINGS);
+               r = 1;
+               goto END;
+       }
+
+       clients = read_clients(CLIENTS);
+
+       // Clean up all old rules
+       flush_chains();
+
+       // Add all client rules
+       r = add_client_rules(clients);
+       if (r)
+               goto END;
+
+       // Add all interface rules
+       r = add_interface_rules(captive_portal_settings, ethernet_settings);
+       if (r)
+               goto END;
+
+END:
+       while (clients) {
+               client_t* head = clients;
+               clients = clients->next;
+
+               free(head);
+       }
+
+       if (ethernet_settings)
+               freekeyvalues(ethernet_settings);
+
+       if (captive_portal_settings)
+               freekeyvalues(captive_portal_settings);
+
+       if (intf)
+               free(intf);
+
+       return r;
+}
index 1e166eb3da160f0467641d82f49cd5e2a41d3373..7cffc89df9c224998f0de5247f51cce6ba136fc9 100644 (file)
@@ -42,6 +42,7 @@ int main(void) {
        char buffer[STRING_SIZE];
        char *index, *ipaddress, *macaddress, *enabled;
        struct keyvalue *kv = NULL;
+       struct keyvalue* captive_settings = NULL;
 
        if (!(initsetuid()))
                exit(1);
@@ -67,6 +68,13 @@ int main(void) {
                exit(1);
        }
 
+       // Read captive portal settings
+       captive_settings = initkeyvalues();
+       if (!readkeyvalues(captive_settings, CONFIG_ROOT "/captive/settings")) {
+               fprintf(stderr, "Could not read captive portal settings\n");
+               exit(1);
+       }
+
        /* Get the BLUE interface details */
        if (findkey(kv, "BLUE_DEV", blue_dev) > 0) {
                if ((strlen(blue_dev) > 0) && !VALID_DEVICE(blue_dev)) {
@@ -79,6 +87,15 @@ int main(void) {
                exit(0);
        }
 
+       // Check if the captive portal is enabled on blue. If so, we will
+       // just keep the chains flushed and do not add any rules.
+       char captive_enabled[STRING_SIZE];
+       if (findkey(captive_settings, "ENABLE_BLUE", captive_enabled) > 0) {
+               if (strcmp(captive_enabled, "on") == 0) {
+                       return 0;
+               }
+       }
+
        if ((fd = fopen(CONFIG_ROOT "/wireless/nodrop", "r")))
                return 0;
 
diff --git a/src/scripts/captive-cleanup b/src/scripts/captive-cleanup
new file mode 100755 (executable)
index 0000000..c39e488
--- /dev/null
@@ -0,0 +1,46 @@
+#!/usr/bin/perl
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2016  IPFire Team  <alexander.marx@ipfire.org>                #
+#                                                                             #
+# 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 <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+use strict;
+
+require '/var/ipfire/general-functions.pl';
+
+my %settings=();
+my %clientshash=();
+my $settingsfile="${General::swroot}/captive/settings";
+my $clients="${General::swroot}/captive/clients";
+my $time;
+my $expiretime;
+
+if (-f $settingsfile && -f $clients && ! -z $clients){
+       &General::readhash("$settingsfile", \%settings) if(-f $settingsfile);
+       &General::readhasharray("$clients", \%clientshash);
+       $time = time();
+       foreach my $key (keys %clientshash) {
+               $expiretime=($clientshash{$key}[2])+$clientshash{$key}[3];
+               if ($expiretime < $time){
+                       delete $clientshash{$key};
+                       my $exp = gmtime($expiretime);
+                       &General::log("Captive", "Delete expired voucher $clientshash{$key}[4] expired on $exp. Remark: $clientshash{$key}[5]");
+               }
+       }
+       &General::writehasharray("$clients", \%clientshash);
+}